a tool for shared writing and social publishing

interaction panel in reader gets flexbox fixes and some styling updates

+67 -44
+10 -3
app/(home-pages)/reader/GlobalContent.tsx
··· 8 8 DesktopInteractionPreviewDrawer, 9 9 MobileInteractionPreviewDrawer, 10 10 } from "./InteractionDrawers"; 11 + import { useSelectedPostListing } from "src/useSelectedPostState"; 11 12 12 13 export const GlobalContent = (props: { 13 14 promise: Promise<{ posts: Post[] }>; ··· 29 30 30 31 const posts = data?.posts ?? []; 31 32 33 + let selectedPost = useSelectedPostListing((s) => s.selectedPostListing); 34 + 32 35 if (posts.length === 0) { 33 36 return ( 34 37 <div className="flex flex-col gap-2 container bg-[rgba(var(--bg-page),.7)] sm:p-4 p-3 justify-between text-center text-tertiary"> ··· 38 41 } 39 42 40 43 return ( 41 - <div className="flex flex-row gap-6 w-full"> 42 - <div className="flex flex-col gap-8 w-full"> 44 + <div className="globalReader flex flex-row gap-6 w-full"> 45 + <div className="globalPostListings flex flex-col gap-6 min-w-0 grow w-full"> 43 46 {posts.map((p) => ( 44 - <PostListing {...p} key={p.documents.uri} /> 47 + <PostListing 48 + {...p} 49 + key={p.documents.uri} 50 + selected={selectedPost?.document_uri === p.documents.uri} 51 + /> 45 52 ))} 46 53 </div> 47 54 <DesktopInteractionPreviewDrawer />
+10 -7
app/(home-pages)/reader/InboxContent.tsx
··· 8 8 import { useEffect, useRef } from "react"; 9 9 import Link from "next/link"; 10 10 import { PostListing } from "components/PostListing"; 11 - import { useHasBackgroundImage } from "components/Pages/useHasBackgroundImage"; 12 11 import { 13 12 DesktopInteractionPreviewDrawer, 14 13 MobileInteractionPreviewDrawer, 15 14 } from "./InteractionDrawers"; 15 + import { useSelectedPostListing } from "src/useSelectedPostState"; 16 16 17 17 export const InboxContent = (props: { 18 18 promise: Promise<{ posts: Post[]; nextCursor: Cursor | null }>; 19 19 }) => { 20 20 const { posts, nextCursor } = use(props.promise); 21 - 22 21 const getKey = ( 23 22 pageIndex: number, 24 23 previousPageData: { ··· 46 45 ); 47 46 48 47 const loadMoreRef = useRef<HTMLDivElement>(null); 48 + 49 + let selectedPost = useSelectedPostListing((s) => s.selectedPostListing); 49 50 50 51 // Set up intersection observer to load more when trigger element is visible 51 52 useEffect(() => { ··· 78 79 79 80 if (allPosts.length === 0) return <ReaderEmpty />; 80 81 81 - let hasBackgroundImage = useHasBackgroundImage(); 82 - 83 82 return ( 84 - <div className="flex flex-row gap-6 w-full "> 85 - <div className="flex flex-col gap-6 w-full relative"> 83 + <div className="inboxReader flex flex-row gap-6 w-full "> 84 + <div className="inboxPostListings flex flex-col gap-6 min-w-0 grow w-full relative"> 86 85 {sortedPosts.map((p) => ( 87 - <PostListing {...p} key={p.documents.uri} /> 86 + <PostListing 87 + {...p} 88 + key={p.documents.uri} 89 + selected={selectedPost?.document_uri === p.documents.uri} 90 + /> 88 91 ))} 89 92 {/* Trigger element for loading more posts */} 90 93 <div
+26 -18
app/(home-pages)/reader/InteractionDrawers.tsx
··· 19 19 20 20 return ( 21 21 <div 22 - className={`z-20 fixed bottom-0 left-0 right-0 border border-border-light shrink-0 w-screen h-[90vh] px-3 bg-bg-leaflet rounded-t-lg overflow-auto ${selectedPost === null ? "hidden" : "block md:hidden "}`} 22 + className={`mobileInteractionPreview shrink-0 z-20 fixed bottom-0 left-0 right-0 border border-border-light w-screen h-[90vh] px-3 bg-bg-leaflet rounded-t-lg overflow-auto ${selectedPost === null ? "hidden" : "block md:hidden "}`} 23 23 > 24 24 <PreviewDrawerContent selectedPost={selectedPost} /> 25 25 </div> ··· 31 31 32 32 return ( 33 33 <div 34 - className={`hidden md:block border border-border-light shrink-0 w-96 mr-2 px-3 h-[calc(100vh-100px)] sticky top-11 bottom-4 right-0 rounded-lg overflow-auto ${selectedPost === null ? "shadow-none border-dashed bg-transparent" : "shadow-md border-border bg-bg-page "}`} 34 + className={`desktopInteractionPreview shrink-0 hidden md:block border border-border-light w-96 mr-2 px-3 h-[calc(100vh-100px)] sticky top-11 bottom-4 right-0 rounded-lg overflow-auto ${selectedPost === null ? "shadow-none border-dashed bg-transparent" : "shadow-md border-border bg-bg-page "}`} 35 35 > 36 36 <PreviewDrawerContent selectedPost={selectedPost} /> 37 37 </div> ··· 55 55 { keepPreviousData: false }, 56 56 ); 57 57 58 - if (!props.selectedPost || !props.selectedPost.document) return null; 58 + if (!props.selectedPost || !props.selectedPost.document) 59 + return ( 60 + <div className="italic text-tertiary pt-4 text-center"> 61 + Click a post's comments or mentions to preview them here! 62 + </div> 63 + ); 59 64 60 65 const postUrl = getDocumentURL( 61 66 props.selectedPost.document, ··· 70 75 71 76 return ( 72 77 <> 73 - <div className="w-full text-sm text-tertiary flex justify-between pt-3 gap-3"> 74 - <div className="truncate min-w-0 grow">{drawerTitle}</div> 75 - <button 76 - className="text-tertiary" 77 - onClick={() => 78 - useSelectedPostListing.getState().setSelectedPostListing(null) 79 - } 80 - > 81 - <CloseTiny /> 82 - </button> 78 + <div className="sticky top-0 bg-bg-page z-10"> 79 + <div className=" w-full text-sm text-tertiary flex justify-between pt-3 gap-3"> 80 + <div className="truncate min-w-0 grow">{drawerTitle}</div> 81 + <button 82 + className="text-tertiary" 83 + onClick={() => 84 + useSelectedPostListing.getState().setSelectedPostListing(null) 85 + } 86 + > 87 + <CloseTiny /> 88 + </button> 89 + </div> 90 + <SpeedyLink className="shrink-0 flex gap-1 items-center" href={postUrl}> 91 + <ButtonPrimary fullWidth compact className="text-sm! mt-1"> 92 + See Full Post <GoToArrow /> 93 + </ButtonPrimary> 94 + </SpeedyLink> 95 + <hr className="mt-2 border-border-light" /> 83 96 </div> 84 - <SpeedyLink className="shrink-0 flex gap-1 items-center" href={postUrl}> 85 - <ButtonPrimary fullWidth compact className="text-sm! mt-1"> 86 - See Full Post <GoToArrow /> 87 - </ButtonPrimary> 88 - </SpeedyLink> 89 97 {isLoading ? ( 90 98 <div className="flex items-center justify-center gap-1 text-tertiary italic text-sm mt-8"> 91 99 <span>loading</span>
+9 -2
app/(home-pages)/reader/NewContent.tsx
··· 10 10 DesktopInteractionPreviewDrawer, 11 11 MobileInteractionPreviewDrawer, 12 12 } from "./InteractionDrawers"; 13 + import { useSelectedPostListing } from "src/useSelectedPostState"; 13 14 14 15 export const NewContent = (props: { 15 16 promise: Promise<{ posts: Post[]; nextCursor: Cursor | null }>; ··· 36 37 revalidateFirstPage: false, 37 38 }, 38 39 ); 40 + 41 + let selectedPost = useSelectedPostListing((s) => s.selectedPostListing); 39 42 40 43 const loadMoreRef = useRef<HTMLDivElement>(null); 41 44 ··· 71 74 72 75 return ( 73 76 <div className="flex flex-row gap-6 w-full"> 74 - <div className="flex flex-col gap-6 w-full relative"> 77 + <div className="flex flex-col gap-6 w-full grow min-w-0 relative"> 75 78 {allPosts.map((p) => ( 76 - <PostListing {...p} key={p.documents.uri} /> 79 + <PostListing 80 + {...p} 81 + key={p.documents.uri} 82 + selected={selectedPost?.document_uri === p.documents.uri} 83 + /> 77 84 ))} 78 85 <div 79 86 ref={loadMoreRef}
+2 -3
app/(home-pages)/reader/layout.tsx
··· 12 12 13 13 const allTabs = [ 14 14 { name: "Subs", href: "/reader", requiresAuth: true }, 15 - { name: "What's Hot", href: "/reader/hot", requiresAuth: false }, 15 + { name: "Trending", href: "/reader/hot", requiresAuth: false }, 16 16 { name: "New", href: "/reader/new", requiresAuth: false }, 17 17 ]; 18 18 ··· 27 27 const tabs = allTabs.filter((tab) => !tab.requiresAuth || isLoggedIn); 28 28 29 29 const isActive = (href: string) => { 30 - if (href === "/reader") 31 - return pathname === "/reader" || pathname === "/"; 30 + if (href === "/reader") return pathname === "/reader" || pathname === "/"; 32 31 if ( 33 32 href === "/reader/hot" && 34 33 !isLoggedIn &&
+6 -7
components/PostListing.tsx
··· 13 13 14 14 import Link from "next/link"; 15 15 import { useEffect, useRef, useState } from "react"; 16 - import { InteractionPreview, TagPopover } from "./InteractionsPreview"; 16 + import { TagPopover } from "./InteractionsPreview"; 17 17 import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 18 18 import { useSmoker } from "./Toast"; 19 - import { Separator } from "./Layout"; 20 19 import { CommentTiny } from "./Icons/CommentTiny"; 21 20 import { QuoteTiny } from "./Icons/QuoteTiny"; 22 21 import { ShareTiny } from "./Icons/ShareTiny"; ··· 27 26 import { RecommendButton } from "./RecommendButton"; 28 27 import { getFirstParagraph } from "src/utils/getFirstParagraph"; 29 28 30 - export const PostListing = (props: Post) => { 29 + export const PostListing = (props: Post & { selected?: boolean }) => { 31 30 let pubRecord = props.publication?.pubRecord as 32 31 | NormalizedPublication 33 32 | undefined; ··· 101 100 className={` 102 101 relative 103 102 flex flex-col overflow-hidden 104 - selected-outline border-border-light rounded-lg w-full hover:outline-accent-contrast 105 - hover:border-accent-contrast 103 + selected-outline rounded-lg w-full 104 + ${props.selected ? "outline-2 outline-offset-1 outline-accent-contrast border-accent-contrast" : "hover:outline-accent-contrast hover:border-accent-contrast border-border-light"} 106 105 ${showPageBackground ? "bg-bg-page " : "bg-bg-leaflet"} `} 107 106 style={ 108 107 hasBackgroundImage ··· 135 134 )} 136 135 <div className="postListingInfo px-3 py-2"> 137 136 {postRecord.title && ( 138 - <h3 className="postListingTitle text-primary line-clamp-2 sm:text-lg text-base"> 137 + <h3 className="postListingTitle text-primary line-clamp-2 sm:text-lg text-base pb-0.5"> 139 138 {postRecord.title} 140 139 </h3> 141 140 )} 142 141 143 - <p className="postListingDescription text-secondary line-clamp-3 sm:text-base text-sm"> 142 + <p className="postListingDescription text-secondary line-clamp-3 leading-snug sm:text-base text-sm"> 144 143 {postRecord.description || getFirstParagraph(postRecord)} 145 144 </p> 146 145 <div className="flex flex-col-reverse gap-2 text-sm text-tertiary items-center justify-start pt-1.5 w-full">
+2 -2
components/ThemeManager/themeDefaults.ts
··· 9 9 pageBackground: "#FDFCFA", 10 10 primary: "#272727", 11 11 accentText: "#FFFFFF", 12 - accentBackground: "#0000FF", 12 + accentBackground: "#639431", 13 13 } as const; 14 14 15 15 // RGB color defaults (parsed from hex values above) 16 16 export const PubThemeDefaultsRGB = { 17 17 background: { r: 253, g: 252, b: 250 }, // #FDFCFA 18 18 foreground: { r: 39, g: 39, b: 39 }, // #272727 19 - accent: { r: 0, g: 0, b: 255 }, // #0000FF 19 + accent: { r: 99, g: 148, b: 49 }, // #639431 20 20 accentForeground: { r: 255, g: 255, b: 255 }, // #FFFFFF 21 21 } as const;
+2 -2
components/ThemeManager/themeUtils.ts
··· 12 12 //everywhere else, accent-background = accent-1 and accent-text = accent-2. 13 13 // we just need to create a migration pipeline before we can change this 14 14 "theme/accent-text": "#FFFFFF", 15 - "theme/accent-background": "#0000FF", 16 - "theme/accent-contrast": "#0000FF", 15 + "theme/accent-background": "#639431", 16 + "theme/accent-contrast": "#639431", 17 17 }; 18 18 19 19 // used to calculate the contrast between page and accent1, accent2, and determin which is higher contrast