a tool for shared writing and social publishing
0

Configure Feed

Select the types of activity you want to include in your feed.

added pagetitle to looseleaf and pub, added shortcut if you only have one pub or looseleaf

celine (Jan 31, 2026, 12:52 AM EST) 76b1e08a 2cb5eff0

+136 -73
+3 -59
app/(home-pages)/home/HomeLayout.tsx
··· 29 29 HomeEmptyState, 30 30 PublicationBanner, 31 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"; 32 + import { PublicationNavigation } from "components/ActionBar/Publications"; 38 33 39 34 export type Leaflet = { 40 35 added_at: string; ··· 83 78 (leaflet) => leaflet.archived === true, 84 79 ).length > 0; 85 80 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 - 123 81 return ( 124 82 <DashboardLayout 125 83 id="home" ··· 150 108 pageTitle={ 151 109 <PageTitle 152 110 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 - } 111 + controls={<PublicationNavigation currentPage="home" />} 168 112 /> 169 113 } 170 114 /> ··· 268 212 className={` 269 213 leafletList 270 214 w-full 271 - ${display === "grid" ? "grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-4 gap-x-4 sm:gap-x-6 sm:gap-y-5 grow" : "flex flex-col gap-2 pt-2"} `} 215 + ${display === "grid" ? "grid auto-rows-max md:grid-cols-4 sm:grid-cols-3 grid-cols-2 gap-y-4 gap-x-4 sm:gap-x-6 sm:gap-y-5 grow" : "flex flex-col gap-2"} `} 272 216 > 273 217 {props.leaflets.map(({ token: leaflet, added_at, archived }, index) => ( 274 218 <ReplicacheProvider
+11 -1
app/(home-pages)/looseleafs/LooseleafsLayout.tsx
··· 1 1 "use client"; 2 - import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 2 + import { 3 + DashboardLayout, 4 + PageTitle, 5 + } from "components/PageLayouts/DashboardLayout"; 3 6 import { useState } from "react"; 4 7 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 5 8 import { Fact, PermissionToken } from "src/replicache"; ··· 10 13 import useSWR from "swr"; 11 14 import { getHomeDocs } from "../home/storage"; 12 15 import { Leaflet, LeafletList } from "../home/HomeLayout"; 16 + import { PublicationNavigation } from "components/ActionBar/Publications"; 13 17 14 18 export const LooseleafsLayout = (props: { 15 19 entityID: string | null; ··· 47 51 ), 48 52 }, 49 53 }} 54 + pageTitle={ 55 + <PageTitle 56 + pageTitle={"Looseleafs"} 57 + controls={<PublicationNavigation currentPage="looseleafs" />} 58 + /> 59 + } 50 60 /> 51 61 ); 52 62 };
+5 -10
app/lish/[did]/[publication]/dashboard/PublicationDashboard.tsx
··· 13 13 } from "components/PageLayouts/DashboardLayout"; 14 14 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 15 15 import { type NormalizedPublication } from "src/utils/normalizeRecords"; 16 - import { PublicationButtons } from "components/ActionBar/Publications"; 17 - import { Popover } from "components/Popover"; 16 + import { PublicationNavigation } from "components/ActionBar/Publications"; 18 17 19 18 export default function PublicationDashboard({ 20 19 publication, ··· 83 82 <PageTitle 84 83 pageTitle={record.name} 85 84 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> 85 + <PublicationNavigation 86 + currentPage="pub" 87 + currentPubUri={publication.uri} 88 + /> 94 89 } 95 90 /> 96 91 }
+116 -2
components/ActionBar/Publications.tsx
··· 15 15 import { PublishSmall } from "components/Icons/PublishSmall"; 16 16 import { Popover } from "components/Popover"; 17 17 import { BlueskyLogin } from "app/login/LoginForm"; 18 - import { ButtonSecondary } from "components/Buttons"; 18 + import { ButtonPrimary, ButtonSecondary } from "components/Buttons"; 19 19 import { useIsMobile } from "src/hooks/isMobile"; 20 20 import { useState } from "react"; 21 21 import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 22 - import type { navPages } from "./NavigationButtons"; 22 + import { HomeButton, type navPages } from "./NavigationButtons"; 23 23 import { AddTiny } from "components/Icons/AddTiny"; 24 + import { HomeSmall } from "components/Icons/HomeSmall"; 25 + import { HomeTiny } from "components/Icons/HomeTiny"; 26 + import { LooseleafTiny } from "components/Icons/LooseleafTiny"; 24 27 25 28 export const PublicationButtons = (props: { 26 29 currentPage: navPages; ··· 222 225 {props.record?.name.slice(0, 1).toUpperCase()} 223 226 </div> 224 227 </div> 228 + ); 229 + }; 230 + 231 + export const PublicationNavigation = (props: { 232 + currentPage: navPages; 233 + currentPubUri?: string; 234 + }) => { 235 + let { identity } = useIdentityData(); 236 + 237 + let hasLooseleafs = !!identity?.permission_token_on_homepage.find( 238 + (f) => 239 + f.permission_tokens.leaflets_to_documents && 240 + f.permission_tokens.leaflets_to_documents[0]?.document, 241 + ); 242 + 243 + let pubCount = identity?.publications.length ?? 0; 244 + let onlyOnePub = pubCount === 1 && !hasLooseleafs; 245 + let onlyLooseleafs = pubCount === 0 && hasLooseleafs; 246 + 247 + function getTriggerIcons() { 248 + if (identity && pubCount >= 1) { 249 + return ( 250 + <div className="flex gap-1"> 251 + {identity.publications.map((pub, index) => { 252 + if (index <= 2) 253 + return ( 254 + <PubIcon 255 + key={pub.uri} 256 + record={normalizePublicationRecord(pub.record)} 257 + uri={pub.uri} 258 + /> 259 + ); 260 + })} 261 + </div> 262 + ); 263 + } 264 + if (identity && hasLooseleafs) { 265 + return ( 266 + <div className="bg-bg-leaflet rounded-full"> 267 + <LooseLeafSmall className="scale-[75%]" /> 268 + </div> 269 + ); 270 + } else 271 + return ( 272 + <ButtonPrimary compact className="text-sm!"> 273 + Create a Publication! 274 + </ButtonPrimary> 275 + ); 276 + } 277 + 278 + // Single pub, no looseleafs - just link to that pub 279 + if (onlyOnePub && identity) { 280 + let pub = identity.publications[0]; 281 + if (props.currentPage === "pub") 282 + return ( 283 + <SpeedyLink 284 + href={`/home`} 285 + className="hover:no-underline! text-tertiary text-bold flex gap-2 text-sm border border-border-light rounded-md px-1 py-[px] items-center" 286 + > 287 + Home <HomeTiny /> 288 + </SpeedyLink> 289 + ); 290 + return ( 291 + <SpeedyLink 292 + href={`${getBasePublicationURL(pub)}/dashboard`} 293 + className="hover:no-underline! text-tertiary text-bold flex gap-2 text-sm border border-border-light rounded-md px-1 py-[px] items-center max-w-32 " 294 + > 295 + <div className="truncate">{pub.name}</div> 296 + <PubIcon 297 + small 298 + record={normalizePublicationRecord(pub.record)} 299 + uri={pub.uri} 300 + /> 301 + </SpeedyLink> 302 + ); 303 + } 304 + 305 + // Only looseleafs, no pubs - just link to looseleafs 306 + if (!onlyLooseleafs) { 307 + if (props.currentPage === "looseleafs") 308 + return ( 309 + <SpeedyLink 310 + href={`/home`} 311 + className="hover:no-underline! text-tertiary text-bold flex gap-2 text-sm border border-border-light rounded-md px-1 py-[px] items-center" 312 + > 313 + Home <HomeTiny /> 314 + </SpeedyLink> 315 + ); 316 + return ( 317 + <SpeedyLink href="/looseleafs" className="hover:no-underline!"> 318 + <div className="hover:no-underline! text-tertiary text-bold flex gap-2 text-sm border border-border-light rounded-md px-1 py-[px] items-center max-w-32 "> 319 + Looseleafs <LooseleafTiny /> 320 + </div> 321 + </SpeedyLink> 322 + ); 323 + } 324 + 325 + return ( 326 + <Popover trigger={<div>{getTriggerIcons()}</div>} className="pt-1 px-2!"> 327 + <HomeButton 328 + current={props.currentPage === "home"} 329 + className="flex-row-reverse! justify-end!" 330 + /> 331 + <hr className="my-1 border-border-light" /> 332 + <PublicationButtons 333 + currentPage={props.currentPage} 334 + currentPubUri={props.currentPubUri} 335 + className="justify-end!" 336 + optionClassName=" flex-row-reverse!" 337 + /> 338 + </Popover> 225 339 ); 226 340 }; 227 341
+1 -1
components/PageHeader.tsx
··· 29 29 <div 30 30 className={` 31 31 headerWrapper 32 - sticky top-0 z-10 32 + sticky top-0 z-20 33 33 w-full bg-transparent 34 34 `} 35 35 >