a tool for shared writing and social publishing

refactoring to add notification list in popover as well, added timestamp

+42 -58
+3 -9
app/(home-pages)/notifications/CommentNotication.tsx
··· 16 16 } from "./Notification"; 17 17 import { AtUri } from "@atproto/api"; 18 18 19 - export const CommentNotification = ( 20 - props: { cardBorderHidden: boolean } & HydratedCommentNotification, 21 - ) => { 19 + export const CommentNotification = (props: HydratedCommentNotification) => { 22 20 let docRecord = props.commentData.documents 23 21 ?.data as PubLeafletDocument.Record; 24 22 let commentRecord = props.commentData.record as PubLeafletComment.Record; ··· 34 32 35 33 return ( 36 34 <Notification 35 + timestamp="" 37 36 href={`https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`} 38 - cardBorderHidden={props.cardBorderHidden} 39 37 icon={<CommentTiny />} 40 38 actionText={<>{displayName} commented on your post</>} 41 39 content={ 42 - <ContentLayout 43 - cardBorderHidden={props.cardBorderHidden} 44 - postTitle={docRecord.title} 45 - pubRecord={pubRecord} 46 - > 40 + <ContentLayout postTitle={docRecord.title} pubRecord={pubRecord}> 47 41 <CommentInNotification 48 42 className="" 49 43 avatar={
+2 -4
app/(home-pages)/notifications/FollowNotification.tsx
··· 1 1 import { Avatar } from "components/Avatar"; 2 2 import { Notification } from "./Notification"; 3 3 4 - export const DummyFollowNotification = (props: { 5 - cardBorderHidden: boolean; 6 - }) => { 4 + export const DummyFollowNotification = (props: {}) => { 7 5 const identity = "celine"; 8 6 const pubName = "Pub Name Here"; 9 7 return ( 10 8 <Notification 9 + timestamp={""} 11 10 href="/" 12 11 icon={<Avatar src={undefined} displayName={identity} tiny />} 13 - cardBorderHidden={props.cardBorderHidden} 14 12 actionText={ 15 13 <> 16 14 {identity} followed {pubName}!
+3 -6
app/(home-pages)/notifications/MentionNotification.tsx
··· 1 1 import { MentionTiny } from "components/Icons/MentionTiny"; 2 2 import { ContentLayout, Notification } from "./Notification"; 3 3 4 - export const DummyPostMentionNotification = (props: { 5 - cardBorderHidden: boolean; 6 - }) => { 4 + export const DummyPostMentionNotification = (props: {}) => { 7 5 return ( 8 6 <Notification 7 + timestamp={""} 9 8 href="/" 10 9 icon={<MentionTiny />} 11 - cardBorderHidden={props.cardBorderHidden} 12 10 actionText={<>celine mentioned your post</>} 13 11 content={ 14 12 <ContentLayout 15 13 postTitle={"Post Title Here"} 16 - cardBorderHidden={props.cardBorderHidden} 17 14 pubRecord={{ name: "My Publication" } as any} 18 15 > 19 16 I'm just gonna put the description here. The surrounding context is ··· 35 32 }) => { 36 33 return ( 37 34 <Notification 35 + timestamp={""} 38 36 href="/" 39 37 icon={<MentionTiny />} 40 38 actionText={<>celine mentioned you</>} ··· 42 40 <ContentLayout 43 41 postTitle={"Post Title Here"} 44 42 pubRecord={{ name: "My Publication" } as any} 45 - cardBorderHidden={props.cardBorderHidden} 46 43 > 47 44 <div> 48 45 ...llo this is the content of a post or whatever here it comes{" "}
+28 -11
app/(home-pages)/notifications/Notification.tsx
··· 1 + "use client"; 1 2 import { Avatar } from "components/Avatar"; 2 3 import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/BaseTextBlock"; 3 4 import { PubLeafletPublication, PubLeafletRichtextFacet } from "lexicons/api"; 5 + import { timeAgo } from "src/utils/timeAgo"; 6 + import { useReplicache, useEntity } from "src/replicache"; 4 7 5 8 export const Notification = (props: { 6 9 icon: React.ReactNode; 7 10 actionText: React.ReactNode; 8 11 content?: React.ReactNode; 9 - cardBorderHidden?: boolean; 12 + timestamp: string; 10 13 href: string; 11 14 }) => { 15 + let { rootEntity } = useReplicache(); 16 + let cardBorderHidden = useEntity(rootEntity, "theme/card-border-hidden")?.data 17 + .value; 18 + 12 19 return ( 13 20 <div 14 - className={`relative flex flex-col w-full ${ 15 - props.cardBorderHidden 16 - ? "" 17 - : " block-border border-border! hover:outline-border sm:p-4 px-3 pl-2 sm:pl-3 pt-2 sm:pt-3!" 21 + className={`relative flex flex-col w-full py-3 sm:py-4 pt-2 sm:pt-3! ${ 22 + cardBorderHidden 23 + ? " first:pt-0! " 24 + : " block-border border-border! hover:outline-border sm:px-4 px-3 pl-2 sm:pl-3 " 18 25 }`} 19 26 style={{ 20 - backgroundColor: props.cardBorderHidden 27 + backgroundColor: cardBorderHidden 21 28 ? "transparent" 22 29 : "rgba(var(--bg-page), var(--bg-page-alpha))", 23 30 }} ··· 26 33 href={props.href} 27 34 className=" absolute top-0 bottom-0 left-0 right-0" 28 35 /> 29 - <div className={`flex flex-row gap-2 items-center`}> 30 - <div className="text-secondary shrink-0">{props.icon}</div> 31 - <div className="text-secondary font-bold">{props.actionText}</div> 36 + <div className="flex justify-between items-center gap-3 w-full "> 37 + <div className={`flex flex-row gap-2 items-center grow w-full min-w-0`}> 38 + <div className="text-secondary shrink-0">{props.icon}</div> 39 + <div className="text-secondary font-bold grow truncate min-w-0"> 40 + {props.actionText} 41 + </div> 42 + </div> 43 + <div className="text-sm text-tertiary shrink-0 min-w-8"> 44 + {timeAgo(props.timestamp)} 45 + </div> 32 46 </div> 33 47 {props.content && ( 34 48 <div className="flex flex-row gap-2 mt-1 w-full"> ··· 44 58 children: React.ReactNode; 45 59 postTitle: string; 46 60 pubRecord?: PubLeafletPublication.Record; 47 - cardBorderHidden: boolean; 48 61 }) => { 62 + let { rootEntity } = useReplicache(); 63 + let cardBorderHidden = useEntity(rootEntity, "theme/card-border-hidden")?.data 64 + .value; 65 + 49 66 return ( 50 67 <div 51 - className={`border border-border-light rounded-md px-2 py-[6px] w-full ${props.cardBorderHidden ? "transparent" : "bg-bg-page"}`} 68 + className={`border border-border-light rounded-md px-2 py-[6px] w-full ${cardBorderHidden ? "transparent" : "bg-bg-page"}`} 52 69 > 53 70 <div className="text-tertiary text-sm italic font-bold pb-1"> 54 71 {props.postTitle}
+3 -19
app/(home-pages)/notifications/NotificationList.tsx
··· 17 17 markAsRead(); 18 18 }, 500); 19 19 }, []); 20 - let { rootEntity } = useReplicache(); 21 - let cardBorderHidden = useEntity(rootEntity, "theme/card-border-hidden")?.data 22 - .value; 23 20 24 21 if (notifications.length === 0) 25 22 return ( ··· 29 26 ); 30 27 return ( 31 28 <div className="max-w-prose mx-auto w-full"> 32 - <div className={`flex flex-col ${cardBorderHidden ? "gap-6" : "gap-2"}`}> 29 + <div className={`flex flex-col gap-2`}> 33 30 {notifications.map((n) => { 34 31 if (n.type === "comment") { 35 - if (n.parentData) 36 - return ( 37 - <ReplyNotification 38 - cardBorderHidden={!!cardBorderHidden} 39 - key={n.id} 40 - {...n} 41 - /> 42 - ); 43 - return ( 44 - <CommentNotification 45 - cardBorderHidden={!!cardBorderHidden} 46 - key={n.id} 47 - {...n} 48 - /> 49 - ); 32 + if (n.parentData) return <ReplyNotification key={n.id} {...n} />; 33 + return <CommentNotification key={n.id} {...n} />; 50 34 } 51 35 })} 52 36 </div>
+3 -9
app/(home-pages)/notifications/ReplyNotification.tsx
··· 15 15 import { AppBskyActorProfile, AtUri } from "@atproto/api"; 16 16 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 17 17 18 - export const ReplyNotification = ( 19 - props: { cardBorderHidden: boolean } & HydratedCommentNotification, 20 - ) => { 18 + export const ReplyNotification = (props: HydratedCommentNotification) => { 21 19 let docRecord = props.commentData.documents 22 20 ?.data as PubLeafletDocument.Record; 23 21 let commentRecord = props.commentData.record as PubLeafletComment.Record; ··· 42 40 43 41 return ( 44 42 <Notification 43 + timestamp={commentRecord.createdAt} 45 44 href={`https://${pubRecord.base_path}/${rkey}?interactionDrawer=comments`} 46 45 icon={<ReplyTiny />} 47 46 actionText={`${displayName} replied to your comment`} 48 - cardBorderHidden={props.cardBorderHidden} 49 47 content={ 50 - <ContentLayout 51 - cardBorderHidden={props.cardBorderHidden} 52 - postTitle={docRecord.title} 53 - pubRecord={pubRecord} 54 - > 48 + <ContentLayout postTitle={docRecord.title} pubRecord={pubRecord}> 55 49 <CommentInNotification 56 50 className="" 57 51 avatar={