a tool for shared writing and social publishing

Added a page title to the home layout

+139 -32
+64
app/(home-pages)/home/HomeLayout.tsx
··· 18 18 DashboardLayout, 19 19 DashboardState, 20 20 useDashboardState, 21 + PageTitle, 21 22 } from "components/PageLayouts/DashboardLayout"; 22 23 import { Actions } from "./Actions/Actions"; 23 24 import { GetLeafletDataReturnType } from "app/api/rpc/[command]/get_leaflet_data"; ··· 28 29 HomeEmptyState, 29 30 PublicationBanner, 30 31 } from "./HomeEmpty/HomeEmpty"; 32 + import { Popover } from "components/Popover"; 33 + import { PubIcon, PublicationButtons } from "components/ActionBar/Publications"; 34 + import { normalizePublicationRecord } from "src/utils/normalizeRecords"; 35 + import { ButtonPrimary } from "components/Buttons"; 36 + import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 37 + import { HomeButton } from "components/ActionBar/NavigationButtons"; 31 38 32 39 export type Leaflet = { 33 40 added_at: string; ··· 76 83 (leaflet) => leaflet.archived === true, 77 84 ).length > 0; 78 85 86 + function getPubIcons() { 87 + let hasLooseleafs = !!identity?.permission_token_on_homepage.find( 88 + (f) => 89 + f.permission_tokens.leaflets_to_documents && 90 + f.permission_tokens.leaflets_to_documents[0]?.document, 91 + ); 92 + 93 + if (identity && identity.publications.length >= 1) { 94 + return ( 95 + <div className="flex gap-1"> 96 + {identity.publications.map((pub, index) => { 97 + if (index <= 3) 98 + return ( 99 + <PubIcon 100 + key={pub.uri} 101 + record={normalizePublicationRecord(pub.record)} 102 + uri={pub.uri} 103 + /> 104 + ); 105 + })} 106 + </div> 107 + ); 108 + } 109 + if (identity && hasLooseleafs) { 110 + return ( 111 + <div className="bg-bg-leaflet rounded-full "> 112 + <LooseLeafSmall className="scale-[75%]" /> 113 + </div> 114 + ); 115 + } else 116 + return ( 117 + <ButtonPrimary compact className="text-sm!"> 118 + Create a Publication! 119 + </ButtonPrimary> 120 + ); 121 + } 122 + 79 123 return ( 80 124 <DashboardLayout 81 125 id="home" ··· 103 147 ), 104 148 }, 105 149 }} 150 + pageTitle={ 151 + <PageTitle 152 + pageTitle={"Home"} 153 + controls={ 154 + <Popover 155 + trigger={<div>{getPubIcons()}</div>} 156 + className="pt-1 px-2!" 157 + > 158 + <HomeButton current className="flex-row-reverse! justify-end!" /> 159 + <hr className="my-1 border-border-light" /> 160 + <PublicationButtons 161 + currentPage={"home"} 162 + currentPubUri={undefined} 163 + className="justify-end!" 164 + optionClassName=" flex-row-reverse!" 165 + /> 166 + </Popover> 167 + } 168 + /> 169 + } 106 170 /> 107 171 ); 108 172 };
+18
app/lish/[did]/[publication]/dashboard/PublicationDashboard.tsx
··· 8 8 import { PublicationSubscribers } from "./PublicationSubscribers"; 9 9 import { 10 10 DashboardLayout, 11 + PageTitle, 11 12 PublicationDashboardControls, 12 13 } from "components/PageLayouts/DashboardLayout"; 13 14 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 14 15 import { type NormalizedPublication } from "src/utils/normalizeRecords"; 16 + import { PublicationButtons } from "components/ActionBar/Publications"; 17 + import { Popover } from "components/Popover"; 15 18 16 19 export default function PublicationDashboard({ 17 20 publication, ··· 76 79 actions={<Actions publication={publication.uri} />} 77 80 currentPage="pub" 78 81 publication={publication.uri} 82 + pageTitle={ 83 + <PageTitle 84 + pageTitle={record.name} 85 + controls={ 86 + <Popover trigger={<div>pubs</div>} className="pt-1 px-2!"> 87 + <PublicationButtons 88 + currentPage={"pub"} 89 + currentPubUri={publication.uri} 90 + className="justify-end!" 91 + optionClassName=" flex-row-reverse!" 92 + /> 93 + </Popover> 94 + } 95 + /> 96 + } 79 97 /> 80 98 ); 81 99 }
+1 -1
components/ActionBar/ActionButton.tsx
··· 71 71 > 72 72 <div className="shrink-0 flex flex-row gap-0.5">{icon}</div> 73 73 <div 74 - className={`flex flex-col pr-1 ${subtext && "leading-snug"} max-w-full min-w-0 ${sidebar.open ? "block" : showLabelOnMobile ? "sm:hidden block" : "hidden"}`} 74 + className={`flex flex-col ${subtext && "leading-snug"} max-w-full min-w-0 ${sidebar.open ? "block" : showLabelOnMobile ? "sm:hidden block" : "hidden"}`} 75 75 > 76 76 <div className="truncate text-left">{label}</div> 77 77 {subtext && (
+8 -4
components/ActionBar/DesktopNavigation.tsx
··· 8 8 } from "./NavigationButtons"; 9 9 import { PublicationButtons } from "./Publications"; 10 10 import { Sidebar } from "./Sidebar"; 11 + import { LoginActionButton, LoginButton } from "components/LoginButton"; 11 12 12 13 export const DesktopNavigation = (props: { 13 14 currentPage: navPages; ··· 24 25 props.currentPage === "pub"; 25 26 return ( 26 27 <div className="flex flex-col gap-3"> 27 - {identity?.atp_did && ( 28 - <Sidebar alwaysOpen> 28 + <Sidebar alwaysOpen> 29 + {identity?.atp_did ? ( 29 30 <NotificationButton current={props.currentPage === "notifications"} /> 30 - </Sidebar> 31 - )} 31 + ) : ( 32 + <LoginActionButton /> 33 + )} 34 + </Sidebar> 35 + 32 36 <Sidebar alwaysOpen> 33 37 <ReaderButton 34 38 current={props.currentPage === "reader"}
+11 -19
components/ActionBar/NavigationButtons.tsx
··· 23 23 | "notifications" 24 24 | "looseleafs" 25 25 | "tag" 26 - | "profile"; 26 + | "profile" 27 + | "discover"; 27 28 28 - export const HomeButton = (props: { current?: boolean }) => { 29 + export const HomeButton = (props: { 30 + current?: boolean; 31 + className?: string; 32 + }) => { 29 33 return ( 30 34 <SpeedyLink href={"/home"} className="hover:!no-underline"> 31 35 <ActionButton 32 36 nav 33 37 icon={<HomeSmall />} 34 - label="Writer Home" 35 - className={props.current ? "bg-bg-page! border-border-light!" : ""} 38 + label="Write" 39 + className={`${props.current ? "bg-bg-page! border-border-light!" : ""} ${props.className}`} 36 40 /> 37 41 </SpeedyLink> 38 42 ); ··· 82 86 nav 83 87 labelOnMobile={true} 84 88 icon={<WriterSmall />} 85 - label=<div className="flex flex-row gap-1"> 86 - Writer 87 - {current && ( 88 - <> 89 - <Divider /> {currentIcon} 90 - </> 91 - )} 92 - </div> 89 + label=<div className="flex flex-row gap-1">Write</div> 93 90 className={current ? "bg-bg-page! border-border-light!" : ""} 94 91 /> 95 92 ) : ( ··· 99 96 icon={ 100 97 <> 101 98 <WriterSmall /> 102 - {current && ( 103 - <> 104 - <Divider /> {currentIcon} 105 - </> 106 - )} 107 99 </> 108 100 } 109 101 label=<div className="flex flex-row gap-1">Writer</div> ··· 116 108 <ActionButton 117 109 nav 118 110 icon={<HomeSmall />} 119 - label="Writer Home" 111 + label="Write" 120 112 className={ 121 113 props.currentPage === "home" 122 114 ? "bg-bg-page! border-border-light!" ··· 145 137 nav 146 138 labelOnMobile={props.compactOnMobile} 147 139 icon={<ReaderUnreadSmall />} 148 - label="Reader" 140 + label="Read" 149 141 className={props.current ? "bg-bg-page! border-border-light!" : ""} 150 142 /> 151 143 </SpeedyLink>
+16 -7
components/ActionBar/Publications.tsx
··· 20 20 import { useState } from "react"; 21 21 import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 22 22 import type { navPages } from "./NavigationButtons"; 23 + import { AddTiny } from "components/Icons/AddTiny"; 23 24 24 25 export const PublicationButtons = (props: { 25 26 currentPage: navPages; 26 27 currentPubUri: string | undefined; 28 + className?: string; 29 + optionClassName?: string; 27 30 }) => { 28 31 let { identity } = useIdentityData(); 29 32 let hasLooseleafs = !!identity?.permission_token_on_homepage.find( ··· 38 41 return <PubListEmpty />; 39 42 40 43 return ( 41 - <div className="pubListWrapper w-full flex flex-col sm:bg-transparent sm:border-0"> 44 + <div 45 + className={`pubListWrapper w-full flex flex-col sm:bg-transparent sm:border-0 ${props.className}`} 46 + > 42 47 {hasLooseleafs && ( 43 48 <> 44 49 <SpeedyLink 45 50 href={`/looseleafs`} 46 - className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full" 51 + className={`flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full `} 47 52 > 48 53 {/*TODO How should i get if this is the current page or not? 49 54 theres not "pub" to check the uri for. Do i need to add it as an option to NavPages? thats kinda annoying*/} ··· 51 56 label="Looseleafs" 52 57 icon={<LooseLeafSmall />} 53 58 nav 54 - className={ 59 + className={`${ 55 60 props.currentPage === "looseleafs" 56 61 ? "bg-bg-page! border-border!" 57 62 : "" 58 63 } 64 + ${props.optionClassName}`} 59 65 /> 60 66 </SpeedyLink> 61 67 </> ··· 68 74 key={d.uri} 69 75 record={d.record} 70 76 current={d.uri === props.currentPubUri} 77 + className={props.optionClassName} 71 78 /> 72 79 ); 73 80 })} 74 81 <Link 75 82 href={"/lish/createPub"} 76 - className="pubListCreateNew text-accent-contrast text-sm place-self-end hover:text-accent-contrast" 83 + className={`pubListCreateNew group/new-pub text-tertiary hover:text-accent-contrast flex gap-2 items-center p-1 no-underline! ${props.optionClassName}`} 77 84 > 78 - New 85 + <div className="group-hover/new-pub:border-accent-contrast w-6 h-6 border-border-light border-2 border-dashed rounded-full" /> 86 + New Publication 79 87 </Link> 80 88 </div> 81 89 ); ··· 86 94 name: string; 87 95 record: Json; 88 96 current?: boolean; 97 + className?: string; 89 98 }) => { 90 99 let record = normalizePublicationRecord(props.record); 91 100 if (!record) return; ··· 93 102 return ( 94 103 <SpeedyLink 95 104 href={`${getBasePublicationURL(props)}/dashboard`} 96 - className="flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full" 105 + className={`flex gap-2 items-start text-secondary font-bold hover:no-underline! hover:text-accent-contrast w-full `} 97 106 > 98 107 <ActionButton 99 108 label={record.name} 100 109 icon={<PubIcon record={record} uri={props.uri} />} 101 110 nav 102 - className={props.current ? "bg-bg-page! border-border!" : ""} 111 + className={`${props.current ? "bg-bg-page! border-border!" : ""} ${props.className}`} 103 112 /> 104 113 </SpeedyLink> 105 114 );
+21 -1
components/PageLayouts/DashboardLayout.tsx
··· 27 27 import { ExternalLinkTiny } from "components/Icons/ExternalLinkTiny"; 28 28 import { usePreserveScroll } from "src/hooks/usePreserveScroll"; 29 29 import { Tab } from "components/Tab"; 30 + import { PubIcon, PublicationButtons } from "components/ActionBar/Publications"; 30 31 31 32 export type DashboardState = { 32 33 display?: "grid" | "list"; ··· 141 142 publication?: string; 142 143 profileDid?: string; 143 144 actions: React.ReactNode; 145 + pageTitle?: React.ReactNode; 144 146 }) { 145 147 const searchParams = useSearchParams(); 146 148 const tabParam = searchParams.get("tab"); ··· 167 169 let [headerState, setHeaderState] = useState<"default" | "controls">( 168 170 "default", 169 171 ); 172 + 170 173 return ( 171 174 <DashboardIdContext.Provider value={props.id}> 172 175 <div ··· 186 189 ref={ref} 187 190 id="home-content" 188 191 > 192 + {props.pageTitle} 193 + 189 194 {Object.keys(props.tabs).length <= 1 && !controls ? null : ( 190 195 <> 191 196 <Header> ··· 256 261 ); 257 262 } 258 263 264 + export const PageTitle = (props: { 265 + pageTitle: string; 266 + controls: React.ReactNode; 267 + }) => { 268 + return ( 269 + <MediaContents 270 + mobile={true} 271 + className="flex justify-between items-center px-1 mt-1 -mb-1 w-full " 272 + > 273 + <h4 className="grow truncate">{props.pageTitle}</h4> 274 + <div className="shrink-0 h-6">{props.controls}</div> 275 + </MediaContents> 276 + ); 277 + }; 278 + 259 279 export const HomeDashboardControls = (props: { 260 280 searchValue: string; 261 281 setSearchValueAction: (searchValue: string) => void; ··· 449 469 className={`dashboardSearchInput 450 470 appearance-none! outline-hidden! 451 471 w-full min-w-0 text-primary relative pl-7 pr-1 -my-px 452 - border rounded-md border-transparent focus-within:border-border 472 + border rounded-md border-border-light focus-within:border-border 453 473 bg-transparent ${props.hasBackgroundImage ? "focus-within:bg-bg-page" : "focus-within:bg-bg-leaflet"} `} 454 474 type="text" 455 475 id="pubName"