a tool for shared writing and social publishing

added designs for a bunch on notification types

+300 -75
+49 -29
app/(home-pages)/notifications/CommentNotication.tsx
··· 7 7 import { HydratedCommentNotification } from "src/notifications"; 8 8 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 9 9 import { Avatar } from "components/Avatar"; 10 - import { Notification } from "./Notification"; 10 + import { 11 + CommentInNotification, 12 + ContentLayout, 13 + Notification, 14 + } from "./Notification"; 11 15 12 16 export const CommentNotification = (props: HydratedCommentNotification) => { 13 17 let docRecord = props.commentData.documents ··· 18 22 return ( 19 23 <Notification 20 24 identity={profileRecord.displayName || "Someone"} 21 - action="comment" 25 + action={{ type: "comment" }} 22 26 content={ 23 - <div className="flex flex-col gap-0.5 mt-2"> 24 - <div className="text-tertiary text-sm italic font-bold"> 25 - {docRecord.title} 26 - </div> 27 - <div className="flex gap-2 border border-border rounded-lg! p-2 text-sm w-full "> 28 - <Avatar 29 - src={ 30 - profileRecord?.avatar?.ref && 31 - blobRefToSrc( 32 - profileRecord?.avatar?.ref, 33 - props.commentData.bsky_profiles?.did || "", 34 - ) 35 - } 36 - displayName={profileRecord?.displayName} 37 - />{" "} 38 - <pre 39 - style={{ wordBreak: "break-word" }} 40 - className="whitespace-pre-wrap text-secondary pt-0.5 line-clamp-6" 41 - > 42 - <BaseTextBlock 43 - index={[]} 44 - plaintext={commentRecord.plaintext} 45 - facets={commentRecord.facets} 46 - /> 47 - </pre> 48 - </div> 49 - </div> 27 + <ContentLayout postTitle={docRecord.title}> 28 + <CommentInNotification 29 + className="" 30 + avatar={ 31 + profileRecord?.avatar?.ref && 32 + blobRefToSrc( 33 + profileRecord?.avatar?.ref, 34 + props.commentData.bsky_profiles?.did || "", 35 + ) 36 + } 37 + displayName={ 38 + profileRecord?.displayName || 39 + props.commentData.bsky_profiles?.handle || 40 + "Someone" 41 + } 42 + index={[]} 43 + plaintext={commentRecord.plaintext} 44 + facets={commentRecord.facets} 45 + /> 46 + </ContentLayout> 47 + } 48 + /> 49 + ); 50 + }; 51 + 52 + export const DummyCommentNotification = () => { 53 + return ( 54 + <Notification 55 + identity={"celine"} 56 + action={{ type: "comment" }} 57 + content={ 58 + <ContentLayout postTitle="This is the Post Title"> 59 + <CommentInNotification 60 + className="" 61 + avatar={undefined} 62 + displayName="celine" 63 + index={[]} 64 + plaintext={ 65 + "heyyyyy this is a dummt comment! I'm just gonna put this here so I know what I'm about but it really oughta be wired up at some point..." 66 + } 67 + facets={[]} 68 + /> 69 + </ContentLayout> 50 70 } 51 71 /> 52 72 );
+14
app/(home-pages)/notifications/FollowNotification.tsx
··· 1 + import { Notification } from "./Notification"; 2 + 3 + export const DummyFollowNotification = () => { 4 + return ( 5 + <Notification 6 + identity={"celine"} 7 + action={{ 8 + type: "follow", 9 + avatar: undefined, 10 + pubName: "Pub Name Here", 11 + }} 12 + /> 13 + ); 14 + };
+40
app/(home-pages)/notifications/MentionNotification.tsx
··· 1 + import { ContentLayout, Notification } from "./Notification"; 2 + 3 + export const DummyPostMentionNotification = () => { 4 + return ( 5 + <Notification 6 + identity={"celine"} 7 + action={{ type: "post-mention" }} 8 + content={ 9 + <ContentLayout postTitle={"Post Title Here"}> 10 + I'm just gonna put the description here. The surrounding context is 11 + just sort of a pain to figure out 12 + <div className="border border-border-light rounded-md p-1 my-1 text-xs text-secondary"> 13 + <div className="font-bold">Title of the Mentioned Post</div> 14 + <div className="text-tertiary"> 15 + And here is the description that follows it 16 + </div> 17 + </div> 18 + </ContentLayout> 19 + } 20 + /> 21 + ); 22 + }; 23 + 24 + export const DummyUserMentionNotification = () => { 25 + return ( 26 + <Notification 27 + identity={"celine"} 28 + action={{ type: "user-mention" }} 29 + content={ 30 + <ContentLayout postTitle={"Post Title Here"}> 31 + <div> 32 + ...llo this is the content of a post or whatever here it comes{" "} 33 + <span className="text-accent-contrast">@celine </span> and here it 34 + was! ooooh heck yeah the high is unre... 35 + </div> 36 + </ContentLayout> 37 + } 38 + /> 39 + ); 40 + };
+97 -22
app/(home-pages)/notifications/Notification.tsx
··· 1 + import { Avatar } from "components/Avatar"; 2 + import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/BaseTextBlock"; 1 3 import { CommentTiny } from "components/Icons/CommentTiny"; 2 4 import { ReplyTiny } from "components/Icons/ReplyTiny"; 5 + import { PubLeafletRichtextFacet } from "lexicons/api"; 6 + import { MentionTiny } from "components/Icons/MentionTiny"; 7 + import { PubIcon } from "components/ActionBar/Publications"; 3 8 4 9 export const Notification = (props: { 5 10 identity: string; 6 - action: "comment" | "reply" | "follow"; 7 - content: React.ReactNode; 11 + action: 12 + | { type: "comment" } 13 + | { type: "reply" } 14 + | { type: "follow"; avatar: string | undefined; pubName: string } 15 + | { type: "post-mention" } 16 + | { type: "user-mention" }; 17 + 18 + content?: React.ReactNode; 8 19 }) => { 9 20 return ( 10 - <div className="flex flex-row gap-2 w-full"> 11 - <div className="text-secondary pt-[6px] shrink-0"> 12 - {props.action === "comment" ? ( 13 - <CommentTiny /> 14 - ) : props.action === "reply" ? ( 15 - <ReplyTiny /> 16 - ) : props.action === "follow" ? ( 17 - "followed you!" 18 - ) : ( 19 - "did something!" 20 - )} 21 - </div> 22 - <div className="flex flex-col gap-0 w-full grow "> 23 - <div className="text-base text-secondary font-bold"> 21 + <div className="flex flex-col w-full"> 22 + <div className="flex flex-row gap-2 items-center"> 23 + <div className="text-secondary shrink-0"> 24 + {props.action.type === "comment" ? ( 25 + <CommentTiny /> 26 + ) : props.action.type === "reply" ? ( 27 + <ReplyTiny /> 28 + ) : props.action.type === "follow" ? ( 29 + <Avatar 30 + src={props.action.avatar} 31 + displayName={props.identity} 32 + tiny 33 + /> 34 + ) : props.action.type === "post-mention" ? ( 35 + <MentionTiny /> 36 + ) : props.action.type === "user-mention" ? ( 37 + <MentionTiny /> 38 + ) : ( 39 + "" 40 + )} 41 + </div> 42 + <div className="text-secondary font-bold"> 24 43 {props.identity}{" "} 25 - {props.action === "comment" 44 + {props.action.type === "comment" 26 45 ? "commented on your post" 27 - : props.action === "reply" 46 + : props.action.type === "reply" 28 47 ? "replied to your comment" 29 - : props.action === "follow" 30 - ? "followed you!" 31 - : "did something!"} 48 + : props.action.type === "follow" 49 + ? `followed ${props.action.pubName}!` 50 + : props.action.type === "post-mention" 51 + ? `mentioned your post` 52 + : props.action.type === "user-mention" 53 + ? "mentioned you" 54 + : "did something!"} 55 + </div> 56 + </div> 57 + {props.content && ( 58 + <div className="flex flex-row gap-2 mt-1 w-full"> 59 + <div className="w-4 shrink-0" /> 60 + {props.content} 32 61 </div> 33 - {props.content} 62 + )} 63 + </div> 64 + ); 65 + }; 66 + 67 + export const ContentLayout = (props: { 68 + children: React.ReactNode; 69 + postTitle: string; 70 + }) => { 71 + return ( 72 + <div className="border border-border-light rounded-md px-2 py-[6px] w-full"> 73 + <div className="text-tertiary text-sm italic font-bold pb-1"> 74 + {props.postTitle} 75 + </div> 76 + {props.children} 77 + <hr className="mt-2 mb-1 border-border-light" /> 78 + 79 + <div className="text-xs text-tertiary flex gap-[6px] items-center"> 80 + <div className="bg-test rounded-full w-[12px] h-[12px] " /> 81 + Pub Name Here 34 82 </div> 35 83 </div> 36 84 ); 37 85 }; 86 + 87 + type Facet = PubLeafletRichtextFacet.Main; 88 + export const CommentInNotification = (props: { 89 + avatar: string | undefined; 90 + displayName: string; 91 + plaintext: string; 92 + facets?: Facet[]; 93 + index: number[]; 94 + className?: string; 95 + }) => { 96 + return ( 97 + <div className=" flex gap-2 text-sm w-full "> 98 + <Avatar src={props.avatar} displayName={props.displayName} /> 99 + <pre 100 + style={{ wordBreak: "break-word" }} 101 + className={`whitespace-pre-wrap text-secondary line-clamp-6 ${props.className}`} 102 + > 103 + <BaseTextBlock 104 + preview 105 + index={props.index} 106 + plaintext={props.plaintext} 107 + facets={props.facets} 108 + /> 109 + </pre> 110 + </div> 111 + ); 112 + };
+41
app/(home-pages)/notifications/ReplyNotification.tsx
··· 1 + import { Avatar } from "components/Avatar"; 2 + import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/BaseTextBlock"; 3 + import { 4 + CommentInNotification, 5 + ContentLayout, 6 + Notification, 7 + } from "./Notification"; 8 + 9 + export const DummyReplyNotification = () => { 10 + return ( 11 + <Notification 12 + identity={"jared"} 13 + action={{ type: "reply" }} 14 + content={ 15 + <ContentLayout postTitle="This is the Post Title"> 16 + <CommentInNotification 17 + className="text-tertiary italic line-clamp-1!" 18 + avatar={undefined} 19 + displayName="celine" 20 + index={[]} 21 + plaintext={ 22 + "This the original comment. To make a point I'm gonna make the comment really pretty long so you can see for youself how it truncates" 23 + } 24 + facets={[]} 25 + /> 26 + <div className="h-3 -mt-[1px] ml-[10px] border-l border-border" /> 27 + <CommentInNotification 28 + className="" 29 + avatar={undefined} 30 + displayName="celine" 31 + index={[]} 32 + plaintext={ 33 + "This is a thoughful and very respectful reply. Violating the code of conduct for me is literally like water for the wicked witch of the west. EeEeEEeeK IT BURNSssSs!!!" 34 + } 35 + facets={[]} 36 + /> 37 + </ContentLayout> 38 + } 39 + /> 40 + ); 41 + };
+25 -17
components/ActionBar/Navigation.tsx
··· 21 21 import { redirect } from "next/navigation"; 22 22 import { hydrateNotifications } from "src/notifications"; 23 23 import { supabaseServerClient } from "supabase/serverClient"; 24 - import { CommentNotification } from "app/(home-pages)/notifications/CommentNotication"; 24 + import { 25 + CommentNotification, 26 + DummyCommentNotification, 27 + } from "app/(home-pages)/notifications/CommentNotication"; 25 28 import { Separator } from "components/Layout"; 26 29 import { useIsMobile } from "src/hooks/isMobile"; 30 + import { DummyReplyNotification } from "app/(home-pages)/notifications/ReplyNotification"; 31 + import { DummyFollowNotification } from "app/(home-pages)/notifications/FollowNotification"; 32 + import { 33 + DummyPostMentionNotification, 34 + DummyUserMentionNotification, 35 + } from "app/(home-pages)/notifications/MentionNotification"; 27 36 28 37 export type navPages = "home" | "reader" | "pub" | "discover" | "notifications"; 29 38 ··· 63 72 trigger={ 64 73 <div className="shrink-0 p-1 text-accent-contrast h-full flex gap-2 font-bold items-center"> 65 74 <MenuSmall /> 66 - {/*{props.currentPage !== "notifications" && ( 67 - <div className="truncate max-w-[72px]"> 68 - {props.currentPage === "home" ? ( 69 - <>Home</> 70 - ) : props.currentPage === "reader" ? ( 71 - <>Reader</> 72 - ) : props.currentPage === "discover" ? ( 73 - <>Discover</> 74 - ) : props.currentPage === "pub" ? ( 75 - thisPublication && <>{thisPublication.name}</> 76 - ) : null} 77 - </div> 78 - )}*/} 79 75 </div> 80 76 } 81 77 > ··· 180 176 asChild 181 177 side={isMobile ? "top" : "right"} 182 178 align={isMobile ? "center" : "start"} 179 + className="max-w-sm pt-3! pb-3!" 183 180 trigger={ 184 181 <ActionButton 185 182 nav ··· 192 189 ) 193 190 } 194 191 label="Notifications" 195 - className={props.current ? "bg-bg-page! border-border-light!" : ""} 192 + className={`${props.current ? "bg-bg-page! border-border-light!" : ""} ${props.unreads ? "text-accent-contrast" : ""}`} 196 193 /> 197 194 } 198 195 > 199 - <div className="flex flex-col gap-6 pt-3"></div> 200 - <SpeedyLink href={"/notifications"}>See All</SpeedyLink> 196 + <div className="flex flex-col gap-5 text-sm"> 197 + <DummyCommentNotification /> 198 + <DummyReplyNotification /> 199 + <DummyFollowNotification /> 200 + <DummyPostMentionNotification /> 201 + <DummyUserMentionNotification /> 202 + </div> 203 + <SpeedyLink 204 + className="flex justify-end pt-2 text-sm" 205 + href={"/notifications"} 206 + > 207 + See All 208 + </SpeedyLink> 201 209 </Popover> 202 210 ); 203 211 }
+11 -5
components/Avatar.tsx
··· 3 3 export const Avatar = (props: { 4 4 src: string | undefined; 5 5 displayName: string | undefined; 6 - small?: boolean; 6 + tiny?: boolean; 7 7 }) => { 8 8 if (props.src) 9 9 return ( 10 10 <img 11 - className="rounded-full w-6 h-6 shrink-0 border border-border-light " 11 + className={`${props.tiny ? "w-4 h-4" : "w-5 h-5"} rounded-full shrink-0 border border-border-light`} 12 12 src={props.src} 13 - alt={props.displayName ? `${props.displayName}'s avatar` : "avatar"} 13 + alt={ 14 + props.displayName 15 + ? `${props.displayName}'s avatar` 16 + : "someone's avatar" 17 + } 14 18 /> 15 19 ); 16 20 else 17 21 return ( 18 - <div className="bg-[var(--accent-light)] rounded-full w-6 h-6 shrink-0 border border-border-light place-items-center text-accent-1"> 19 - <AccountTiny /> 22 + <div 23 + className={`bg-[var(--accent-light)] flex rounded-full shrink-0 border border-border-light place-items-center justify-center text-accent-1 ${props.tiny ? "w-4 h-4" : "w-5 h-5"}`} 24 + > 25 + <AccountTiny className={props.tiny ? "scale-80" : "scale-90"} /> 20 26 </div> 21 27 ); 22 28 };
+21
components/Icons/MentionTiny.tsx
··· 1 + import { Props } from "./Props"; 2 + 3 + export const MentionTiny = (props: Props) => { 4 + return ( 5 + <svg 6 + width="16" 7 + height="16" 8 + viewBox="0 0 16 16" 9 + fill="none" 10 + xmlns="http://www.w3.org/2000/svg" 11 + {...props} 12 + > 13 + <path 14 + fillRule="evenodd" 15 + clipRule="evenodd" 16 + d="M8.7548 1.48131C8.37164 1.12732 7.78076 1.12733 7.39761 1.48131L6.31216 2.48412C6.11579 2.66553 5.85496 2.76077 5.58789 2.74856L4.02324 2.67702C3.42714 2.64976 2.93999 3.14727 2.97979 3.74267L3.04008 4.64469C3.06279 4.98446 2.91104 5.31244 2.63737 5.51507L1.84867 6.09903C1.30874 6.4988 1.30874 7.30663 1.84867 7.7064L2.63737 8.29036C2.91104 8.49299 3.06279 8.82097 3.04008 9.16074L2.97979 10.0628C2.93999 10.6582 3.42714 11.1557 4.02324 11.1284L5.58789 11.0569C5.85496 11.0447 6.11579 11.1399 6.31216 11.3213L7.39761 12.3241C7.78076 12.6781 8.37165 12.6781 8.7548 12.3241L9.84025 11.3213C9.8673 11.2963 9.89557 11.273 9.92492 11.2513C10.379 11.5423 10.9394 11.8764 11.3808 12.0072C12.1456 12.2339 12.9198 12.3728 13.6513 12.2853C14.4861 12.1855 14.9021 12.0899 15.3797 11.8006C14.3597 11.4989 13.8748 11.0143 13.4688 10.4865C13.2705 10.2287 13.1568 9.97205 13.0619 9.71255L12.8396 9.00919C12.8217 8.74234 13.0797 8.57625 13.3239 8.41908C13.3906 8.37613 13.4563 8.33385 13.515 8.29036L14.3037 7.7064C14.8437 7.30663 14.8437 6.4988 14.3037 6.09903L13.515 5.51507C13.2414 5.31244 13.0896 4.98447 13.1123 4.6447L13.1726 3.74267C13.2124 3.14727 12.7253 2.64976 12.1292 2.67702L10.5645 2.74856C10.2975 2.76077 10.0366 2.66553 9.84025 2.48412L8.7548 1.48131ZM0.724555 8.54935C0.893092 8.33061 1.20705 8.2899 1.42579 8.45844L1.95221 8.86403C2.17095 9.03256 2.21166 9.34652 2.04312 9.56526C1.87458 9.78401 1.56063 9.82471 1.34188 9.65618L0.815467 9.25059C0.596721 9.08206 0.556018 8.7681 0.724555 8.54935ZM2.22796 11.7918C2.40295 11.5782 2.71798 11.5469 2.9316 11.7219C3.05506 11.823 3.21912 11.8981 3.3887 11.9473C3.55945 11.9968 3.70151 12.0103 3.75507 12.0103C4.04231 12.0103 4.26845 11.9927 4.45796 11.9729C4.51258 11.9672 4.56967 11.9606 4.62708 11.954C4.75105 11.9396 4.8765 11.9251 4.98174 11.9192C5.15574 11.9095 5.34286 11.9165 5.54475 11.9825C5.74373 12.0475 5.92168 12.158 6.10009 12.3035C6.28301 12.4526 6.47827 12.6379 6.65933 12.8097C6.70174 12.8499 6.74338 12.8894 6.7839 12.9276C7.01119 13.1415 7.20968 13.3186 7.38302 13.4332C7.50037 13.5108 7.73051 13.5859 8.01215 13.6062C8.29295 13.6264 8.53643 13.5857 8.67785 13.5166C8.81401 13.4501 8.97286 13.3418 9.18171 13.1869C9.235 13.1474 9.2917 13.1048 9.35123 13.06L9.3513 13.0599L9.35133 13.0599C9.51197 12.9391 9.69328 12.8027 9.88425 12.6704C10.0346 12.5664 10.2298 12.5526 10.3932 12.6347C11.1162 12.9977 11.6692 13.1581 12.1996 13.2235C12.7423 13.2903 13.2802 13.261 14.0061 13.217C14.2817 13.2003 14.5187 13.4102 14.5354 13.6858C14.5521 13.9615 14.3422 14.1984 14.0666 14.2151C13.3566 14.2582 12.7265 14.2959 12.0773 14.216C11.492 14.1438 10.9056 13.9787 10.2184 13.6606C10.133 13.7233 10.0503 13.7855 9.96791 13.8475L9.96771 13.8477C9.90441 13.8953 9.84129 13.9428 9.77726 13.9902C9.56766 14.1456 9.34378 14.3043 9.11669 14.4152C8.76159 14.5886 8.32363 14.6312 7.94034 14.6036C7.55787 14.5761 7.14126 14.4722 6.83145 14.2673C6.57763 14.0995 6.32221 13.8663 6.09857 13.6558C6.05019 13.6103 6.00322 13.5657 5.9575 13.5224L5.95731 13.5222L5.9573 13.5222C5.77867 13.3528 5.61903 13.2015 5.46819 13.0785C5.34858 12.981 5.27842 12.9475 5.23423 12.933C5.19296 12.9196 5.14185 12.9118 5.03761 12.9176C4.96094 12.9219 4.88552 12.9308 4.78482 12.9425L4.78461 12.9426L4.78437 12.9426C4.72196 12.9499 4.64983 12.9583 4.56169 12.9675C4.34798 12.9898 4.08593 13.0103 3.75507 13.0103C3.59643 13.0103 3.36037 12.9803 3.11004 12.9077C2.85853 12.8347 2.55853 12.7089 2.29792 12.4955C2.0843 12.3205 2.05298 12.0054 2.22796 11.7918ZM7.36287 5.60901L7.868 7.84218H8.29336L8.79849 5.60901V3.81006H7.36287V5.60901ZM8.89597 9.99561V8.4182H7.25653V9.99561H8.89597Z" 17 + fill="currentColor" 18 + /> 19 + </svg> 20 + ); 21 + };
+2 -2
components/Icons/ReplyTiny.tsx
··· 10 10 xmlns="http://www.w3.org/2000/svg" 11 11 > 12 12 <path 13 - fill-rule="evenodd" 14 - clip-rule="evenodd" 13 + fillRule="evenodd" 14 + clipRule="evenodd" 15 15 d="M10.7767 3.01749C11.6289 3.39627 12.1593 3.79765 12.4801 4.2201C12.7868 4.62405 12.9578 5.12048 12.9578 5.81175C12.9578 6.45434 12.7165 7.17288 12.2111 7.72195C11.7245 8.25058 10.9456 8.67427 9.75117 8.67427L4.45638 8.67427L6.97173 6.15892C7.36226 5.7684 7.36226 5.13523 6.97173 4.74471C6.58121 4.35418 5.94804 4.35418 5.55752 4.74471L1.33513 8.9671C0.944605 9.35762 0.944605 9.99079 1.33513 10.3813L5.55752 14.6037C5.94804 14.9942 6.58121 14.9942 6.97173 14.6037C7.36226 14.2132 7.36226 13.58 6.97173 13.1895L4.45652 10.6743L9.75117 10.6743C11.4697 10.6743 12.7941 10.0416 13.6826 9.07646C14.5522 8.13173 14.9578 6.91901 14.9578 5.81175C14.9578 4.75316 14.6829 3.81405 14.073 3.01069C13.4771 2.22581 12.62 1.64809 11.589 1.18986C11.0843 0.965558 10.4933 1.19285 10.269 1.69754C10.0447 2.20222 10.272 2.79318 10.7767 3.01749Z" 16 16 fill="currentColor" 17 17 />