diff options
author | polwex <polwex@sortug.com> | 2025-09-17 21:45:18 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-09-17 21:45:18 +0700 |
commit | 985fa2f7c99832cdf3c3351d2273c8fd05402b78 (patch) | |
tree | bc727486a89ad05e588754f8de8b1096400a3d31 /front/src/pages/User.tsx | |
parent | f0df4c7297a05bd592d8717b8997284c80fd0500 (diff) |
basic comms working
Diffstat (limited to 'front/src/pages/User.tsx')
-rw-r--r-- | front/src/pages/User.tsx | 138 |
1 files changed, 128 insertions, 10 deletions
diff --git a/front/src/pages/User.tsx b/front/src/pages/User.tsx index a1e26f1..e209bb3 100644 --- a/front/src/pages/User.tsx +++ b/front/src/pages/User.tsx @@ -1,20 +1,138 @@ // import spinner from "@/assets/icons/spinner.svg"; import Composer from "@/components/composer/Composer"; import PostList from "@/components/feed/PostList"; -import useLocalState from "@/state/state"; +import ProfileEditor from "@/components/ProfileEditor"; +import useLocalState, { useStore } from "@/state/state"; import type { Ship } from "@/types/urbit"; +import "@/styles/ProfileEditor.css"; +import Icon from "@/components/Icon"; +import toast from "react-hot-toast"; +import { useState } from "react"; +import type { FC } from "@/types/trill"; function UserFeed({ p }: { p: Ship }) { - const { api, following } = useLocalState(); - const feed = following.get(api!.airlock.our!); + const { api } = useLocalState((s) => ({ + api: s.api, + })); + // auto updating on SSE doesn't work if we do shallow + const { following } = useStore(); + const feed = following.get(p); const refetch = () => feed; - if (p === api!.airlock.our) - return ( - <div id="feed-proper"> - <Composer /> - <PostList data={feed!} refetch={refetch} /> - </div> - ); + const isOwnProfile = p === api?.airlock.our; + const isFollowing = following.has(p); + + const [isFollowLoading, setIsFollowLoading] = useState(false); + const [isAccessLoading, setIsAccessLoading] = useState(false); + const [fc, setFC] = useState<FC>(); + + const handleFollow = async () => { + if (!api) return; + + setIsFollowLoading(true); + try { + if (isFollowing) { + await api.unfollow(p); + toast.success(`Unfollowed ${p}`); + } else { + await api.follow(p); + toast.success(`Now following ${p}`); + } + } catch (error) { + toast.error(`Failed to ${isFollowing ? "unfollow" : "follow"} ${p}`); + console.error("Follow error:", error); + } finally { + setIsFollowLoading(false); + } + }; + + const handleRequestAccess = async () => { + if (!api) return; + + setIsAccessLoading(true); + try { + const res = await api.peekFeed(p); + toast.success(`Access request sent to ${p}`); + if ("error" in res) toast.error(res.error); + else setFC(res.ok); + } catch (error) { + toast.error(`Failed to request access from ${p}`); + console.error("Access request error:", error); + } finally { + setIsAccessLoading(false); + } + }; + + return ( + <div id="user-page"> + <ProfileEditor ship={p} /> + + {!isOwnProfile && ( + <div className="user-actions"> + <button + onClick={handleFollow} + disabled={isFollowLoading} + className={`action-btn ${isFollowing ? "following" : "follow"}`} + > + {isFollowLoading ? ( + <> + <Icon name="settings" size={16} /> + {isFollowing ? "Unfollowing..." : "Following..."} + </> + ) : ( + <> + <Icon name={isFollowing ? "bell" : "pals"} size={16} /> + {isFollowing ? "Unfollow" : "Follow"} + </> + )} + </button> + + <button + onClick={handleRequestAccess} + disabled={isAccessLoading} + className="action-btn access" + > + {isAccessLoading ? ( + <> + <Icon name="settings" size={16} /> + Requesting... + </> + ) : ( + <> + <Icon name="key" size={16} /> + Request Access + </> + )} + </button> + </div> + )} + + {feed ? ( + <div id="feed-proper"> + <Composer /> + <PostList data={feed} refetch={refetch} /> + </div> + ) : fc ? ( + <div id="feed-proper"> + <Composer /> + <PostList data={fc} refetch={refetch} /> + </div> + ) : null} + + {!isOwnProfile && !feed && !fc && ( + <div id="other-user-feed"> + <div className="empty-feed-message"> + <Icon name="messages" size={48} color="textMuted" /> + <h3>No Posts Available</h3> + <p> + This user's posts are not publicly visible. + {!isFollowing && " Try following them"} or request temporary + access to see their content. + </p> + </div> + </div> + )} + </div> + ); } export default UserFeed; |