a tool for shared writing and social publishing

init page profile stuff

+186 -8
+89
app/p/[didOrHandle]/ProfilePageLayout.tsx
··· 1 + "use client"; 2 + import { Actions } from "app/(home-pages)/home/Actions/Actions"; 3 + import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 4 + import { useState } from "react"; 5 + import { ProfileTabs, TabContent } from "./ProfileTabs/Tabs"; 6 + import { Json } from "supabase/database.types"; 7 + import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 8 + 9 + export const ProfilePageLayout = (props: { 10 + profile: { 11 + did: string; 12 + handle: string | null; 13 + indexed_at: string; 14 + record: Json; 15 + } | null; 16 + }) => { 17 + if (!props.profile) return null; 18 + 19 + let profileRecord = props.profile.record as unknown as ProfileViewDetailed; 20 + 21 + console.log(profileRecord); 22 + return ( 23 + <DashboardLayout 24 + id={props.profile.did} 25 + cardBorderHidden={false} 26 + defaultTab="home" 27 + tabs={{ 28 + home: { 29 + content: <ProfilePageContent profile={props.profile} />, 30 + controls: null, 31 + }, 32 + }} 33 + actions={<Actions />} 34 + currentPage="home" 35 + /> 36 + ); 37 + }; 38 + 39 + export type profileTabsType = 40 + | "activity" 41 + | "posts" 42 + | "comments" 43 + | "subscriptions"; 44 + const ProfilePageContent = (props: { 45 + profile: { 46 + did: string; 47 + handle: string | null; 48 + indexed_at: string; 49 + record: Json; 50 + } | null; 51 + }) => { 52 + let [tab, setTab] = useState<profileTabsType>("activity"); 53 + 54 + let profileRecord = props.profile?.record as unknown as ProfileViewDetailed; 55 + console.log(profileRecord); 56 + 57 + return ( 58 + <div 59 + className={` 60 + max-w-prose mx-auto w-full h-full 61 + flex flex-col 62 + border border-border rounded-md 63 + text-center px-3 sm:px-4 mt-8`} 64 + > 65 + <div className="bg-test rounded-full h-16 w-16 mx-auto -mt-8" /> 66 + <h3 className="pt-2"> 67 + {profileRecord.displayName 68 + ? profileRecord.displayName 69 + : `@${props.profile?.handle}`} 70 + </h3> 71 + {profileRecord.displayName && ( 72 + <div className="text-tertiary text-sm pb-1"> 73 + @{props.profile?.handle} 74 + </div> 75 + )} 76 + <div className="text-secondary">{profileRecord.description}</div> 77 + <div className="flex flex-row gap-2 mx-auto my-3"> 78 + <div>pub 1</div> 79 + <div>pub 2</div> 80 + </div> 81 + <ProfileTabs tab={tab} setTab={setTab} /> 82 + <TabContent tab={tab} /> 83 + </div> 84 + ); 85 + }; 86 + 87 + const PubListingCompact = () => { 88 + return <div></div>; 89 + };
+14
app/p/[didOrHandle]/ProfileTabs/Activity/Activity.tsx
··· 1 + export const Activity = (props: { 2 + icon: React.ReactNode; 3 + activity: React.ReactNode; 4 + content: React.ReactNode; 5 + }) => { 6 + return ( 7 + <div className="flex flex-col"> 8 + <div className="flex gap-2 items-center text-tertiary font-bold"> 9 + {props.icon} {props.activity} 10 + </div> 11 + <div>{props.content}</div> 12 + </div> 13 + ); 14 + };
+12
app/p/[didOrHandle]/ProfileTabs/Activity/Comment.tsx
··· 1 + import { CommentTiny } from "components/Icons/CommentTiny"; 2 + import { Activity } from "./Activity"; 3 + 4 + export const CommentActivty = () => { 5 + return ( 6 + <Activity 7 + icon={<CommentTiny />} 8 + activity={"celine commented"} 9 + content={<div>hello</div>} 10 + /> 11 + ); 12 + };
app/p/[didOrHandle]/ProfileTabs/Activity/Post.tsx

This is a binary file and will not be displayed.

app/p/[didOrHandle]/ProfileTabs/Activity/Publication.tsx

This is a binary file and will not be displayed.

app/p/[didOrHandle]/ProfileTabs/Activity/Subscription.tsx

This is a binary file and will not be displayed.

+62
app/p/[didOrHandle]/ProfileTabs/Tabs.tsx
··· 1 + import { profileTabsType } from "../ProfilePageLayout"; 2 + 3 + export const ProfileTabs = (props: { 4 + tab: profileTabsType; 5 + setTab: (t: profileTabsType) => void; 6 + }) => { 7 + let buttonStyle = "text-secondary"; 8 + return ( 9 + <div className="flex flex-col w-full"> 10 + <div className="flex gap-2 justify-between"> 11 + <div className="flex gap-2"> 12 + <button 13 + className={buttonStyle} 14 + onClick={() => { 15 + props.setTab("activity"); 16 + }} 17 + > 18 + Activity 19 + </button> 20 + <button 21 + className={buttonStyle} 22 + onClick={() => { 23 + props.setTab("posts"); 24 + }} 25 + > 26 + Posts 27 + </button> 28 + <button 29 + className={buttonStyle} 30 + onClick={() => { 31 + props.setTab("comments"); 32 + }} 33 + > 34 + Comments 35 + </button> 36 + </div> 37 + <button 38 + className={buttonStyle} 39 + onClick={() => { 40 + props.setTab("subscriptions"); 41 + }} 42 + > 43 + Subscriptions 44 + </button> 45 + </div> 46 + <hr className="border-border-light mb-2 mt-1" /> 47 + </div> 48 + ); 49 + }; 50 + 51 + export const TabContent = (props: { tab: profileTabsType }) => { 52 + switch (props.tab) { 53 + case "activity": 54 + return <div>activty stuff here!</div>; 55 + case "posts": 56 + return <div>posts here!</div>; 57 + case "comments": 58 + return <div>comments here!</div>; 59 + case "subscriptions": 60 + return <div>subscriptions here!</div>; 61 + } 62 + };
+9 -8
app/p/[didOrHandle]/page.tsx
··· 1 - import { supabaseServerClient } from "supabase/serverClient"; 2 - import { AtUri } from "@atproto/syntax"; 3 - import { ids } from "lexicons/api/lexicons"; 4 - import { PubLeafletDocument } from "lexicons/api"; 5 - import { Metadata } from "next"; 6 1 import { idResolver } from "app/(home-pages)/reader/idResolver"; 7 - import { DocumentPageRenderer } from "app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer"; 8 2 import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 9 - 3 + import { ProfilePageLayout } from "./ProfilePageLayout"; 4 + import { supabaseServerClient } from "supabase/serverClient"; 10 5 export default async function ProfilePage(props: { 11 6 params: Promise<{ didOrHandle: string }>; 12 7 }) { ··· 15 10 16 11 // Resolve handle to DID if necessary 17 12 let did = didOrHandle; 13 + 18 14 if (!didOrHandle.startsWith("did:")) { 19 15 let resolved = await idResolver.handle.resolve(didOrHandle); 20 16 if (!resolved) { ··· 30 26 } 31 27 did = resolved; 32 28 } 29 + let { data: profile } = await supabaseServerClient 30 + .from("bsky_profiles") 31 + .select(`*`) 32 + .eq("did", did) 33 + .single(); 33 34 34 - return <DocumentPageRenderer did={did} rkey={params.rkey} />; 35 + return <ProfilePageLayout profile={profile} />; 35 36 }