a tool for shared writing and social publishing

move publicationbuttons into separate compoennt, added state to handle jsut one pub or looseleaf

+158 -130
+1 -1
components/ActionBar/Footer.tsx
··· 8 8 actionFooter touch-none shrink-0 9 9 w-full z-10 10 10 px-2 pt-1 pwa-padding-bottom 11 - flex justify-between gap-1 11 + flex justify-between 12 12 h-[calc(38px+var(--safe-padding-bottom))] 13 13 bg-[rgba(var(--bg-page),0.5)] border-top border-bg-page`} 14 14 >
+3 -5
components/ActionBar/MobileNavigation.tsx
··· 6 6 ReaderButton, 7 7 WriterButton, 8 8 } from "./NavigationButtons"; 9 - import { PublicationNavigation } from "./Publications"; 9 + import { PublicationNavigation } from "./PublicationNavigation"; 10 10 import { LoginActionButton } from "components/LoginButton"; 11 11 12 12 export const MobileNavigation = (props: { ··· 23 23 24 24 return ( 25 25 <div 26 - className={`mobileFooter flex gap-2 px-1 text-secondary grow items-center justify-between`} 26 + className={`mobileFooter w-full flex gap-4 px-1 text-secondary grow items-center justify-between`} 27 27 > 28 - <div className="mobileNav flex gap-2 items-center justify-start"> 28 + <div className="mobileNav flex gap-2 items-center justify-start min-w-0"> 29 29 <ReaderButton 30 30 compactOnMobile={compactOnMobile} 31 31 current={props.currentPage === "reader"} ··· 42 42 43 43 {compactOnMobile && ( 44 44 <> 45 - {identity && <Separator classname="h-6!" />} 46 45 <PublicationNavigation 47 46 currentPage={props.currentPage} 48 47 currentPubUri={props.currentPublicationUri} ··· 52 51 </div> 53 52 {identity?.atp_did ? ( 54 53 <> 55 - {compactOnMobile && <Separator classname="h-6!" />} 56 54 <NotificationButton /> 57 55 </> 58 56 ) : (
+1 -1
components/ActionBar/NavigationButtons.tsx
··· 30 30 nav 31 31 icon={<HomeSmall />} 32 32 label="Write" 33 - className={`${props.current ? "bg-bg-page! border-border-light!" : ""} ${props.className}`} 33 + className={`${props.current ? "bg-bg-page! border-border-light!" : ""} w-full! ${props.className}`} 34 34 /> 35 35 </SpeedyLink> 36 36 );
+148
components/ActionBar/PublicationNavigation.tsx
··· 1 + "use client"; 2 + import { useIdentityData } from "components/IdentityProvider"; 3 + import { getBasePublicationURL } from "app/lish/createPub/getPublicationURL"; 4 + import { 5 + normalizePublicationRecord, 6 + type NormalizedPublication, 7 + } from "src/utils/normalizeRecords"; 8 + import { SpeedyLink } from "components/SpeedyLink"; 9 + import { Popover } from "components/Popover"; 10 + import { ButtonPrimary } from "components/Buttons"; 11 + import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 12 + import { HomeButton, type navPages } from "./NavigationButtons"; 13 + import { HomeSmall } from "components/Icons/HomeSmall"; 14 + import { MoreOptionsVerticalTiny } from "components/Icons/MoreOptionsVerticalTiny"; 15 + import { PubIcon, PublicationButtons } from "./Publications"; 16 + import { HomeTiny } from "components/Icons/HomeTiny"; 17 + import { LooseleafTiny } from "components/Icons/LooseleafTiny"; 18 + import { Separator } from "components/Layout"; 19 + import { Menu, MenuItem } from "components/Menu"; 20 + import { AddTiny } from "components/Icons/AddTiny"; 21 + 22 + export const PublicationNavigation = (props: { 23 + currentPage: navPages; 24 + currentPubUri?: string; 25 + }) => { 26 + let { identity } = useIdentityData(); 27 + 28 + if (!identity) return; 29 + 30 + let hasLooseleafs = !!identity?.permission_token_on_homepage.find( 31 + (f) => 32 + f.permission_tokens.leaflets_to_documents && 33 + f.permission_tokens.leaflets_to_documents[0]?.document, 34 + ); 35 + 36 + let pubCount = identity?.publications.length ?? 0; 37 + let onlyOnePub = pubCount === 1 && hasLooseleafs; 38 + let onlyLooseleafs = pubCount === 0 && hasLooseleafs; 39 + let className = 40 + "font-bold text-secondary flex gap-2 items-center grow min-w-0 text-sm h-[34px] px-2 accent-container"; 41 + 42 + // if not publications or looseleafs 43 + if (!identity.publications && !hasLooseleafs) { 44 + return ( 45 + <SpeedyLink href="/lish/createPub"> 46 + <ButtonPrimary compact className="text-sm!"> 47 + Create a Publication! 48 + </ButtonPrimary> 49 + </SpeedyLink> 50 + ); 51 + } 52 + 53 + switch (props.currentPage) { 54 + case "looseleafs": 55 + case "pub": 56 + if (onlyLooseleafs || onlyOnePub) 57 + return ( 58 + <> 59 + <SpeedyLink href={`/home`} className={className}> 60 + <HomeTiny className="shrink-0" /> 61 + Home 62 + </SpeedyLink> 63 + </> 64 + ); 65 + break; 66 + case "home": { 67 + if (onlyLooseleafs || onlyOnePub) { 68 + let pub = identity.publications[0]; 69 + return ( 70 + <div className={className}> 71 + <Menu trigger={<MoreOptionsVerticalTiny className="shrink-0" />}> 72 + <SpeedyLink href="/createPub"> 73 + <MenuItem className="items-center! text-sm" onSelect={() => {}}> 74 + <AddTiny /> 75 + Create New Publication 76 + </MenuItem> 77 + </SpeedyLink> 78 + </Menu> 79 + <Separator classname="h-6!" /> 80 + {onlyLooseleafs ? ( 81 + <SpeedyLink 82 + href="/looseleafs" 83 + className="hover:no-underline! text-inherit flex gap-2 items-center pr-2 w-full min-w-0" 84 + > 85 + <LooseleafTiny className="shrink-0" /> Looseleafs 86 + </SpeedyLink> 87 + ) : ( 88 + <SpeedyLink 89 + href={`${getBasePublicationURL(pub)}/dashboard`} 90 + className="hover:no-underline! text-inherit flex gap-2 items-center pr-2 w0ull min-w-0" 91 + > 92 + <PubIcon 93 + small 94 + record={normalizePublicationRecord(pub.record)} 95 + uri={pub.uri} 96 + /> 97 + <div className="truncate min-w-0">{pub.name}</div> 98 + </SpeedyLink> 99 + )} 100 + </div> 101 + ); 102 + } 103 + break; 104 + } 105 + } 106 + 107 + return ( 108 + <Popover 109 + trigger={ 110 + <div className={className}> 111 + <PubIcons 112 + publications={identity.publications.map((pub) => ({ 113 + record: normalizePublicationRecord(pub.record), 114 + uri: pub.uri, 115 + }))} 116 + />{" "} 117 + Publications 118 + </div> 119 + } 120 + className="pt-1 px-2!" 121 + > 122 + <HomeButton current={props.currentPage === "home"} /> 123 + <hr className="my-1 border-border-light" /> 124 + <PublicationButtons 125 + currentPage={props.currentPage} 126 + currentPubUri={props.currentPubUri} 127 + /> 128 + </Popover> 129 + ); 130 + }; 131 + 132 + function PubIcons(props: { 133 + publications: { record: NormalizedPublication | null; uri: string }[]; 134 + }) { 135 + if (props.publications.length < 1) return null; 136 + return ( 137 + <div className="flex"> 138 + {props.publications.map((pub, index) => { 139 + if (index <= 2) 140 + return ( 141 + <div className="-ml-[6px] first:ml-0" key={pub.uri}> 142 + <PubIcon small record={pub.record} uri={pub.uri} /> 143 + </div> 144 + ); 145 + })} 146 + </div> 147 + ); 148 + }
+4 -123
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 { ButtonPrimary, ButtonSecondary } from "components/Buttons"; 18 + import { 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 { HomeButton, type navPages } from "./NavigationButtons"; 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"; 22 + import { type navPages } from "./NavigationButtons"; 27 23 28 24 export const PublicationButtons = (props: { 29 25 currentPage: navPages; ··· 59 55 label="Looseleafs" 60 56 icon={<LooseLeafSmall />} 61 57 nav 62 - className={`${ 58 + className={`w-full! ${ 63 59 props.currentPage === "looseleafs" 64 60 ? "bg-bg-page! border-border!" 65 61 : "" ··· 122 118 label={record.name} 123 119 icon={<PubIcon record={record} uri={props.uri} />} 124 120 nav 125 - className={`${props.current ? "bg-bg-page! border-border!" : ""} ${props.className}`} 121 + className={`w-full! ${props.current ? "bg-bg-page! border-border!" : ""} ${props.className}`} 126 122 /> 127 123 </SpeedyLink> 128 124 ); ··· 236 232 {props.record?.name.slice(0, 1).toUpperCase()} 237 233 </div> 238 234 </div> 239 - ); 240 - }; 241 - 242 - export const PublicationNavigation = (props: { 243 - currentPage: navPages; 244 - currentPubUri?: string; 245 - }) => { 246 - let { identity } = useIdentityData(); 247 - 248 - if (!identity) return; 249 - 250 - let hasLooseleafs = !!identity?.permission_token_on_homepage.find( 251 - (f) => 252 - f.permission_tokens.leaflets_to_documents && 253 - f.permission_tokens.leaflets_to_documents[0]?.document, 254 - ); 255 - 256 - let pubCount = identity?.publications.length ?? 0; 257 - let onlyOnePub = pubCount === 1 && !hasLooseleafs; 258 - let onlyLooseleafs = pubCount === 0 && hasLooseleafs; 259 - 260 - function getTriggerIcons() { 261 - if (identity && pubCount >= 1) { 262 - return ( 263 - <div className="flex gap-1"> 264 - {identity.publications.map((pub, index) => { 265 - if (index <= 2) 266 - return ( 267 - <PubIcon 268 - key={pub.uri} 269 - record={normalizePublicationRecord(pub.record)} 270 - uri={pub.uri} 271 - /> 272 - ); 273 - })} 274 - </div> 275 - ); 276 - } 277 - if (identity && hasLooseleafs) { 278 - return ( 279 - <div className="bg-bg-leaflet rounded-full"> 280 - <LooseLeafSmall className="scale-[75%]" /> 281 - </div> 282 - ); 283 - } else 284 - return ( 285 - <SpeedyLink href="/lish/createPub"> 286 - <ButtonPrimary compact className="text-sm!"> 287 - Create a Publication! 288 - </ButtonPrimary> 289 - </SpeedyLink> 290 - ); 291 - } 292 - 293 - // Single pub, no looseleafs - just link to that pub 294 - if (onlyOnePub && identity) { 295 - let pub = identity.publications[0]; 296 - if (props.currentPage === "pub") 297 - return ( 298 - <SpeedyLink 299 - href={`/home`} 300 - 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" 301 - > 302 - Home <HomeTiny /> 303 - </SpeedyLink> 304 - ); 305 - return ( 306 - <SpeedyLink 307 - href={`${getBasePublicationURL(pub)}/dashboard`} 308 - 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 " 309 - > 310 - <div className="truncate">{pub.name}</div> 311 - <PubIcon 312 - small 313 - record={normalizePublicationRecord(pub.record)} 314 - uri={pub.uri} 315 - /> 316 - </SpeedyLink> 317 - ); 318 - } 319 - 320 - // Only looseleafs, no pubs - just link to looseleafs 321 - if (onlyLooseleafs) { 322 - if (props.currentPage === "looseleafs") 323 - return ( 324 - <SpeedyLink 325 - href={`/home`} 326 - 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" 327 - > 328 - Home <HomeTiny /> 329 - </SpeedyLink> 330 - ); 331 - return ( 332 - <SpeedyLink href="/looseleafs" className="hover:no-underline!"> 333 - <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 "> 334 - Looseleafs <LooseleafTiny /> 335 - </div> 336 - </SpeedyLink> 337 - ); 338 - } 339 - 340 - return ( 341 - <Popover trigger={<div>{getTriggerIcons()}</div>} className="pt-1 px-2!"> 342 - <HomeButton 343 - current={props.currentPage === "home"} 344 - className="flex-row-reverse! justify-end!" 345 - /> 346 - <hr className="my-1 border-border-light" /> 347 - <PublicationButtons 348 - currentPage={props.currentPage} 349 - currentPubUri={props.currentPubUri} 350 - className="justify-end!" 351 - optionClassName=" flex-row-reverse!" 352 - /> 353 - </Popover> 354 235 ); 355 236 }; 356 237
+1
components/Icons/MoreOptionsVerticalTiny.tsx
··· 8 8 viewBox="0 0 12 24" 9 9 fill="none" 10 10 xmlns="http://www.w3.org/2000/svg" 11 + {...props} 11 12 > 12 13 <path 13 14 d="M6 15.5C6.82843 15.5 7.5 16.1716 7.5 17C7.5 17.8284 6.82843 18.5 6 18.5C5.17157 18.5 4.5 17.8284 4.5 17C4.5 16.1716 5.17157 15.5 6 15.5ZM6 10.5C6.82843 10.5 7.5 11.1716 7.5 12C7.5 12.8284 6.82843 13.5 6 13.5C5.17157 13.5 4.5 12.8284 4.5 12C4.5 11.1716 5.17157 10.5 6 10.5ZM6 5.5C6.82843 5.5 7.5 6.17157 7.5 7C7.5 7.82843 6.82843 8.5 6 8.5C5.17157 8.5 4.5 7.82843 4.5 7C4.5 6.17157 5.17157 5.5 6 5.5Z"