a tool for shared writing and social publishing
0

Configure Feed

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

Perf/homepage (#306)

* optimize homepage leaflets query

* seperate static landing page

* wrap legal in suspense boundary

* login tweaks

* don't show untitled for logged out users

* fix adding empty doc to home from new button

authored by

Jared Pereira and committed by
GitHub
(May 24, 2026, 8:56 PM EDT) a2ed3489 984af026

+597 -587
+4 -2
actions/getIdentityData.ts
··· 33 33 permission_tokens!inner( 34 34 id, 35 35 root_entity, 36 + title, 37 + description, 36 38 permission_token_rights(*), 37 - leaflets_to_documents(*, documents(*)), 38 - leaflets_in_publications(*, publications(*), documents(*)) 39 + leaflets_to_documents(*), 40 + leaflets_in_publications(*, publications(*)) 39 41 ) 40 42 ), 41 43 user_subscriptions(plan, status, current_period_end),
+2
actions/login.ts
··· 15 15 16 16 export async function loginWithEmailToken( 17 17 localLeaflets: { token: { id: string }; added_at: string }[], 18 + redirectRoute?: string, 18 19 ) { 19 20 const client = await pool.connect(); 20 21 const db = drizzle(client); ··· 123 124 } 124 125 } 125 126 client.release(); 127 + if (result?.identity && redirectRoute) redirect(redirectRoute); 126 128 }
+1 -1
actions/publications/subscribeEmail.tsx
··· 21 21 backfillAtprotoSubscriptionsForIdentity, 22 22 publishAtprotoSubscriptionForDid, 23 23 unsubscribeToPublication, 24 - } from "app/lish/subscribeToPublication"; 24 + } from "app/(app)/lish/subscribeToPublication"; 25 25 import type { OAuthSessionError } from "src/atproto-oauth"; 26 26 import { normalizePublicationRecord } from "src/utils/normalizeRecords"; 27 27 import { linkOrphanedEmailSubscribers } from "src/utils/linkOrphanedEmailSubscribers";
+25
app/(app)/(home-pages)/(writer)/home/LeafletList/HomeLeafletFactsProvider.tsx
··· 1 + "use client"; 2 + 3 + import useSWR from "swr"; 4 + import { create, windowScheduler } from "@yornaath/batshit"; 5 + import { callRPC } from "app/api/rpc/client"; 6 + import type { Fact } from "src/replicache"; 7 + import type { Attribute } from "src/replicache/attributes"; 8 + 9 + const leafletFactsBatcher = create({ 10 + fetcher: async (roots: string[]) => { 11 + const response = await callRPC("getFactsForRoots", { roots }); 12 + return response.result.facts as { [root: string]: Fact<Attribute>[] }; 13 + }, 14 + resolver: (results, root: string) => results[root] ?? [], 15 + scheduler: windowScheduler(10), 16 + }); 17 + 18 + export function useLeafletFacts(root: string, enabled: boolean = true) { 19 + const { data, isLoading } = useSWR( 20 + enabled ? `leaflet-facts:${root}` : null, 21 + () => leafletFactsBatcher.fetch(root), 22 + { revalidateOnFocus: false, revalidateOnReconnect: false }, 23 + ); 24 + return { facts: data ?? null, isLoading }; 25 + }
+50
app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletCardReplicache.tsx
··· 1 + "use client"; 2 + 3 + import { createContext, useCallback, useContext, useMemo, useState } from "react"; 4 + import { ReplicacheProvider, type PermissionToken } from "src/replicache"; 5 + import { useLeafletFacts } from "./HomeLeafletFactsProvider"; 6 + 7 + const CardVisibilityContext = createContext<{ 8 + notifyVisible: () => void; 9 + } | null>(null); 10 + 11 + export function useReportCardVisible() { 12 + return useContext(CardVisibilityContext); 13 + } 14 + 15 + export function LeafletCardReplicache(props: { 16 + leaflet: PermissionToken; 17 + loggedIn: boolean; 18 + eagerLoadFacts: boolean; 19 + children: React.ReactNode; 20 + }) { 21 + const [hasBeenVisible, setHasBeenVisible] = useState(props.eagerLoadFacts); 22 + const { facts } = useLeafletFacts( 23 + props.leaflet.root_entity, 24 + hasBeenVisible, 25 + ); 26 + 27 + const notifyVisible = useCallback(() => { 28 + setHasBeenVisible((v) => v || true); 29 + }, []); 30 + const contextValue = useMemo(() => ({ notifyVisible }), [notifyVisible]); 31 + 32 + const initialFacts = facts ?? []; 33 + const providerKey = `${props.leaflet.id}:${facts ? "loaded" : "loading"}`; 34 + 35 + return ( 36 + <CardVisibilityContext.Provider value={contextValue}> 37 + <ReplicacheProvider 38 + disablePull 39 + initialFactsOnly={props.loggedIn} 40 + key={providerKey} 41 + rootEntity={props.leaflet.root_entity} 42 + token={props.leaflet} 43 + name={props.leaflet.root_entity} 44 + initialFacts={initialFacts} 45 + > 46 + {props.children} 47 + </ReplicacheProvider> 48 + </CardVisibilityContext.Provider> 49 + ); 50 + }
+28
app/(app)/(home-pages)/(writer)/home/page.tsx
··· 1 + import { getIdentityData } from "actions/getIdentityData"; 2 + 3 + import { HomeContent } from "./HomeLayout"; 4 + 5 + export default async function Home() { 6 + let auth_res = await getIdentityData(); 7 + 8 + let titles = 9 + auth_res?.permission_token_on_homepage.reduce( 10 + (acc, tok) => { 11 + let title = 12 + tok.permission_tokens.leaflets_in_publications[0]?.title || 13 + tok.permission_tokens.leaflets_to_documents[0]?.title || 14 + tok.permission_tokens.title || 15 + undefined; 16 + if (title) acc[tok.permission_tokens.root_entity] = title; 17 + return acc; 18 + }, 19 + {} as { [k: string]: string }, 20 + ) ?? {}; 21 + 22 + return ( 23 + <HomeContent 24 + titles={titles} 25 + entityID={auth_res?.home_leaflet?.root_entity || null} 26 + /> 27 + ); 28 + }
+65
app/(app)/(home-pages)/(writer)/looseleafs/LooseleafsLayout.tsx
··· 1 + "use client"; 2 + import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 3 + import { useState } from "react"; 4 + import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 5 + import { PermissionToken } from "src/replicache"; 6 + import { useIdentityData } from "components/IdentityProvider"; 7 + import { Leaflet, LeafletList } from "../home/HomeLayout"; 8 + import { CreateNewLeafletButton } from "../home/Actions/CreateNewButton"; 9 + 10 + export const LooseleafsContent = (props: { 11 + entityID: string | null; 12 + titles: { [root_entity: string]: string }; 13 + }) => { 14 + let [searchValue, setSearchValue] = useState(""); 15 + let [debouncedSearchValue, setDebouncedSearchValue] = useState(""); 16 + 17 + useDebouncedEffect( 18 + () => { 19 + setDebouncedSearchValue(searchValue); 20 + }, 21 + 200, 22 + [searchValue], 23 + ); 24 + 25 + return ( 26 + <DashboardPageLayout 27 + scrollKey="dashboard-looseleafs-home" 28 + pageTitle="Looseleafs" 29 + mobileActions={<CreateNewLeafletButton compact />} 30 + showHeader={false} 31 + > 32 + <LooseleafList 33 + titles={props.titles} 34 + searchValue={debouncedSearchValue} 35 + /> 36 + </DashboardPageLayout> 37 + ); 38 + }; 39 + 40 + export const LooseleafList = (props: { 41 + titles: { [root_entity: string]: string }; 42 + searchValue: string; 43 + }) => { 44 + let { identity } = useIdentityData(); 45 + 46 + let leaflets: Leaflet[] = identity 47 + ? identity.permission_token_on_homepage 48 + .filter( 49 + (ptoh) => ptoh.permission_tokens.leaflets_to_documents.length > 0, 50 + ) 51 + .map((ptoh) => ({ 52 + added_at: ptoh.created_at, 53 + token: ptoh.permission_tokens as PermissionToken, 54 + })) 55 + : []; 56 + return ( 57 + <LeafletList 58 + defaultDisplay="list" 59 + searchValue={props.searchValue} 60 + leaflets={leaflets} 61 + titles={props.titles} 62 + showPreview 63 + /> 64 + ); 65 + };
+27
app/(app)/(home-pages)/(writer)/looseleafs/page.tsx
··· 1 + import { getIdentityData } from "actions/getIdentityData"; 2 + import { LooseleafsContent } from "./LooseleafsLayout"; 3 + 4 + export default async function Home() { 5 + let auth_res = await getIdentityData(); 6 + 7 + let titles = 8 + auth_res?.permission_token_on_homepage.reduce( 9 + (acc, tok) => { 10 + let title = 11 + tok.permission_tokens.leaflets_in_publications[0]?.title || 12 + tok.permission_tokens.leaflets_to_documents[0]?.title || 13 + tok.permission_tokens.title || 14 + undefined; 15 + if (title) acc[tok.permission_tokens.root_entity] = title; 16 + return acc; 17 + }, 18 + {} as { [k: string]: string }, 19 + ) ?? {}; 20 + 21 + return ( 22 + <LooseleafsContent 23 + entityID={auth_res?.home_leaflet?.root_entity || null} 24 + titles={titles} 25 + /> 26 + ); 27 + }
+32
app/(app)/layout.tsx
··· 1 + import { headers } from "next/headers"; 2 + import { Suspense } from "react"; 3 + import { IdentityProviderServer } from "components/IdentityProviderServer"; 4 + import { RequestHeadersProvider } from "components/Providers/RequestHeadersProvider"; 5 + import { RouteUIStateManager } from "components/RouteUIStateManger"; 6 + import { SubscriptionSuccessModal } from "components/SubscriptionSuccessModal"; 7 + 8 + export default async function AppLayout({ 9 + children, 10 + }: { 11 + children: React.ReactNode; 12 + }) { 13 + let headersList = await headers(); 14 + let ipLocation = headersList.get("X-Vercel-IP-Country"); 15 + let acceptLanguage = headersList.get("accept-language"); 16 + let ipTimezone = headersList.get("X-Vercel-IP-Timezone"); 17 + return ( 18 + <IdentityProviderServer> 19 + <RequestHeadersProvider 20 + country={ipLocation} 21 + language={acceptLanguage} 22 + timezone={ipTimezone} 23 + > 24 + {children} 25 + <Suspense> 26 + <SubscriptionSuccessModal /> 27 + </Suspense> 28 + <RouteUIStateManager /> 29 + </RequestHeadersProvider> 30 + </IdentityProviderServer> 31 + ); 32 + }
+10
app/(app)/lish/[did]/[publication]/[rkey]/l-quote/[quote]/page.tsx
··· 1 + import PostPage, { 2 + generateMetadata, 3 + } from "app/(app)/lish/[did]/[publication]/[rkey]/page"; 4 + 5 + export { generateMetadata } from "app/(app)/lish/[did]/[publication]/[rkey]/page"; 6 + export default async function Post(props: { 7 + params: Promise<{ publication: string; did: string; rkey: string }>; 8 + }) { 9 + return <PostPage {...props} />; 10 + }
+8
app/(app)/p/[didOrHandle]/[rkey]/l-quote/[quote]/page.tsx
··· 1 + import PostPage from "app/(app)/p/[didOrHandle]/[rkey]/page"; 2 + 3 + export { generateMetadata } from "app/(app)/p/[didOrHandle]/[rkey]/page"; 4 + export default async function Post(props: { 5 + params: Promise<{ didOrHandle: string; rkey: string }>; 6 + }) { 7 + return <PostPage {...props} />; 8 + }
app/(home-pages)/(writer)/WriterShell.tsx app/(app)/(home-pages)/(writer)/WriterShell.tsx
app/(home-pages)/(writer)/home/Actions/Actions.tsx app/(app)/(home-pages)/(writer)/home/Actions/Actions.tsx
+2 -2
app/(home-pages)/(writer)/home/Actions/CreateNewButton.tsx app/(app)/(home-pages)/(writer)/home/Actions/CreateNewButton.tsx
··· 13 13 let isMobile = useIsMobile(); 14 14 let openNewLeaflet = (id: string) => { 15 15 if (isMobile) { 16 - window.location.href = `/${id}?focusFirstBlock`; 16 + window.location.href = `/${id}?focusFirstBlock&addToHome`; 17 17 } else { 18 - window.open(`/${id}?focusFirstBlock`, "_blank"); 18 + window.open(`/${id}?focusFirstBlock&addToHome`, "_blank"); 19 19 } 20 20 }; 21 21 return (
app/(home-pages)/(writer)/home/Actions/HomeThemeSetter.tsx app/(app)/(home-pages)/(writer)/home/Actions/HomeThemeSetter.tsx
app/(home-pages)/(writer)/home/HomeEmpty/DiscoverIllo.tsx app/(app)/(home-pages)/(writer)/home/HomeEmpty/DiscoverIllo.tsx
+2 -2
app/(home-pages)/(writer)/home/HomeEmpty/HomeEmpty.tsx app/(app)/(home-pages)/(writer)/home/HomeEmpty/HomeEmpty.tsx
··· 63 63 onClick={async () => { 64 64 let openNewLeaflet = (id: string) => { 65 65 if (isMobile) { 66 - window.location.href = `/${id}?focusFirstBlock`; 66 + window.location.href = `/${id}?focusFirstBlock&addToHome`; 67 67 } else { 68 - window.open(`/${id}?focusFirstBlock`, "_blank"); 68 + window.open(`/${id}?focusFirstBlock&addToHome`, "_blank"); 69 69 } 70 70 }; 71 71
app/(home-pages)/(writer)/home/HomeEmpty/WelcomeToLeafletIllo.tsx app/(app)/(home-pages)/(writer)/home/HomeEmpty/WelcomeToLeafletIllo.tsx
+12 -59
app/(home-pages)/(writer)/home/HomeLayout.tsx app/(app)/(home-pages)/(writer)/home/HomeLayout.tsx
··· 2 2 3 3 import { getHomeDocs } from "./storage"; 4 4 import useSWR from "swr"; 5 - import { 6 - Fact, 7 - PermissionToken, 8 - ReplicacheProvider, 9 - useEntity, 10 - } from "src/replicache"; 5 + import { PermissionToken, useEntity } from "src/replicache"; 11 6 import { LeafletListItem } from "./LeafletList/LeafletListItem"; 12 7 import { useIdentityData } from "components/IdentityProvider"; 13 - import type { Attribute } from "src/replicache/attributes"; 14 - import { callRPC } from "app/api/rpc/client"; 15 8 import { StaticLeafletDataContext } from "components/PageSWRDataProvider"; 16 9 import { 17 10 type DashboardState, ··· 24 17 import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 25 18 import { HomeEmptyState } from "./HomeEmpty/HomeEmpty"; 26 19 import { CreateNewLeafletButton } from "./Actions/CreateNewButton"; 20 + import { LeafletCardReplicache } from "./LeafletList/LeafletCardReplicache"; 27 21 28 22 export type Leaflet = { 29 23 added_at: string; 30 24 archived?: boolean | null; 31 25 token: PermissionToken & { 26 + title?: string | null; 27 + description?: string | null; 32 28 leaflets_in_publications?: Exclude< 33 29 GetLeafletDataReturnType["result"]["data"], 34 30 null ··· 43 39 export const HomeContent = (props: { 44 40 entityID: string | null; 45 41 titles: { [root_entity: string]: string }; 46 - initialFacts: { 47 - [root_entity: string]: Fact<Attribute>[]; 48 - }; 49 42 }) => { 50 43 let hasBackgroundImage = !!useEntity( 51 44 props.entityID, ··· 92 85 > 93 86 <HomeLeafletList 94 87 titles={props.titles} 95 - initialFacts={props.initialFacts} 96 88 searchValue={debouncedSearchValue} 97 89 /> 98 90 </DashboardPageLayout> ··· 101 93 102 94 export function HomeLeafletList(props: { 103 95 titles: { [root_entity: string]: string }; 104 - initialFacts: { 105 - [root_entity: string]: Fact<Attribute>[]; 106 - }; 107 96 searchValue: string; 108 97 }) { 109 98 let { identity } = useIdentityData(); 110 - let { data: initialFacts } = useSWR( 111 - "home-leaflet-data", 112 - async () => { 113 - if (identity) { 114 - let { result } = await callRPC("getFactsFromHomeLeaflets", { 115 - tokens: identity.permission_token_on_homepage.map( 116 - (ptrh) => ptrh.permission_tokens.root_entity, 117 - ), 118 - }); 119 - let titles = { 120 - ...result.titles, 121 - ...identity.permission_token_on_homepage.reduce( 122 - (acc, tok) => { 123 - let title = 124 - tok.permission_tokens.leaflets_in_publications[0]?.title || 125 - tok.permission_tokens.leaflets_to_documents[0]?.title; 126 - if (title) acc[tok.permission_tokens.root_entity] = title; 127 - return acc; 128 - }, 129 - {} as { [k: string]: string }, 130 - ), 131 - }; 132 - return { ...result, titles }; 133 - } 134 - }, 135 - { 136 - fallbackData: { facts: props.initialFacts, titles: props.titles }, 137 - revalidateOnFocus: false, 138 - revalidateOnReconnect: false, 139 - }, 140 - ); 141 99 142 100 let { data: localLeaflets } = useSWR("leaflets", () => getHomeDocs(), { 143 101 fallbackData: [], ··· 161 119 defaultDisplay="grid" 162 120 searchValue={props.searchValue} 163 121 leaflets={leaflets} 164 - titles={initialFacts?.titles || {}} 165 - initialFacts={initialFacts?.facts || {}} 122 + titles={props.titles} 166 123 showPreview 167 124 /> 168 125 <div className="spacer sm:block hidden h-4 w-full bg-transparent shrink-0 " /> ··· 174 131 leaflets: Leaflet[]; 175 132 titles: { [root_entity: string]: string }; 176 133 defaultDisplay: Exclude<DashboardState["display"], undefined>; 177 - initialFacts: { 178 - [root_entity: string]: Fact<Attribute>[]; 179 - }; 180 134 searchValue: string; 181 135 showPreview?: boolean; 182 136 }) { ··· 199 153 ${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"} `} 200 154 > 201 155 {props.leaflets.map(({ token: leaflet, added_at, archived }, index) => ( 202 - <ReplicacheProvider 203 - disablePull 204 - initialFactsOnly={!!identity} 156 + <LeafletCardReplicache 205 157 key={leaflet.id} 206 - rootEntity={leaflet.root_entity} 207 - token={leaflet} 208 - name={leaflet.root_entity} 209 - initialFacts={props.initialFacts?.[leaflet.root_entity] || []} 158 + leaflet={leaflet} 159 + loggedIn={!!identity} 160 + eagerLoadFacts={index < 12} 210 161 > 211 162 <StaticLeafletDataContext 212 163 value={{ 213 164 ...leaflet, 165 + title: leaflet.title ?? null, 166 + description: leaflet.description ?? null, 214 167 leaflets_in_publications: leaflet.leaflets_in_publications || [], 215 168 leaflets_to_documents: leaflet.leaflets_to_documents || [], 216 169 publication_pages: [], ··· 233 186 } 234 187 /> 235 188 </StaticLeafletDataContext> 236 - </ReplicacheProvider> 189 + </LeafletCardReplicache> 237 190 ))} 238 191 </div> 239 192 );
app/(home-pages)/(writer)/home/IdentitySetter.tsx app/(app)/(home-pages)/(writer)/home/IdentitySetter.tsx
app/(home-pages)/(writer)/home/LeafletList/LeafletContent.tsx app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletContent.tsx
+8 -12
app/(home-pages)/(writer)/home/LeafletList/LeafletInfo.tsx app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletInfo.tsx
··· 1 1 "use client"; 2 - import { useEntity } from "src/replicache"; 3 2 import { LeafletOptions } from "./LeafletOptions"; 4 3 import { timeAgo } from "src/utils/timeAgo"; 5 - import { usePageTitle } from "components/utils/UpdateLeafletTitle"; 6 4 import { useLeafletPublicationStatus } from "components/PageSWRDataProvider"; 7 5 8 6 export const LeafletInfo = (props: { ··· 19 17 ? timeAgo(pubStatus.publishedAt) 20 18 : ""; 21 19 22 - // Look up root page first, like UpdateLeafletTitle does 23 - let firstPage = useEntity(pubStatus?.leafletId ?? "", "root/page")[0]; 24 - let entityID = firstPage?.data.value || pubStatus?.leafletId || ""; 25 - let titleFromDb = usePageTitle(entityID); 26 - 27 - let title = props.title ?? titleFromDb ?? "Untitled"; 20 + let showTitle = props.loggedIn || !!props.title; 21 + let title = props.title ?? "Untitled"; 28 22 29 23 return ( 30 24 <div 31 25 className={`leafletInfo w-full min-w-0 flex flex-col ${props.className}`} 32 26 > 33 27 <div className="flex justify-between items-center shrink-0 max-w-full gap-2 leading-tight overflow-hidden"> 34 - <h3 className="sm:text-lg text-base truncate w-full min-w-0"> 35 - {title} 36 - </h3> 37 - <div className="flex gap-1 shrink-0"> 28 + {showTitle && ( 29 + <h3 className="sm:text-lg text-base truncate w-full min-w-0"> 30 + {title} 31 + </h3> 32 + )} 33 + <div className="flex gap-1 shrink-0 ml-auto"> 38 34 <LeafletOptions archived={props.archived} loggedIn={props.loggedIn} /> 39 35 </div> 40 36 </div>
+4 -1
app/(home-pages)/(writer)/home/LeafletList/LeafletListItem.tsx app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletListItem.tsx
··· 5 5 import { SpeedyLink } from "components/SpeedyLink"; 6 6 import { useLeafletPublicationStatus } from "components/PageSWRDataProvider"; 7 7 import { useCardBorderHidden } from "components/Pages/useCardBorderHidden"; 8 + import { useReportCardVisible } from "./LeafletCardReplicache"; 8 9 9 10 export const LeafletListItem = (props: { 10 11 archived?: boolean | null; ··· 18 19 }) => { 19 20 const cardBorderHidden = useCardBorderHidden(); 20 21 const pubStatus = useLeafletPublicationStatus(); 22 + const visibilityReporter = useReportCardVisible(); 21 23 let [isOnScreen, setIsOnScreen] = useState(props.index < 16 ? true : false); 22 24 let previewRef = useRef<HTMLDivElement | null>(null); 23 25 ··· 28 30 entries.forEach((entry) => { 29 31 if (entry.isIntersecting) { 30 32 setIsOnScreen(true); 33 + visibilityReporter?.notifyVisible(); 31 34 } else { 32 35 setIsOnScreen(false); 33 36 } ··· 37 40 ); 38 41 observer.observe(previewRef.current); 39 42 return () => observer.disconnect(); 40 - }, [previewRef]); 43 + }, [previewRef, visibilityReporter]); 41 44 42 45 const tokenId = pubStatus?.shareLink ?? ""; 43 46
+3 -3
app/(home-pages)/(writer)/home/LeafletList/LeafletOptions.tsx app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletOptions.tsx
··· 16 16 import { 17 17 deletePost, 18 18 unpublishPost, 19 - } from "app/lish/[did]/[publication]/dashboard/deletePost"; 19 + } from "app/(app)/lish/[did]/[publication]/dashboard/deletePost"; 20 20 import { ShareSmall } from "components/Icons/ShareSmall"; 21 21 import { HideSmall } from "components/Icons/HideSmall"; 22 22 import { hideDoc } from "../storage"; ··· 28 28 import { 29 29 usePublicationData, 30 30 mutatePublicationData, 31 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 32 - import { ShareButton } from "app/[leaflet_id]/actions/ShareOptions"; 31 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 32 + import { ShareButton } from "app/(app)/[leaflet_id]/actions/ShareOptions"; 33 33 import { useLeafletPublicationStatus } from "components/PageSWRDataProvider"; 34 34 35 35 export const LeafletOptions = (props: {
app/(home-pages)/(writer)/home/LeafletList/LeafletPreview.module.css app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletPreview.module.css
app/(home-pages)/(writer)/home/LeafletList/LeafletPreview.tsx app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletPreview.tsx
app/(home-pages)/(writer)/home/icon.tsx app/(app)/(home-pages)/(writer)/home/icon.tsx
-44
app/(home-pages)/(writer)/home/page.tsx
··· 1 - import { getIdentityData } from "actions/getIdentityData"; 2 - import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets"; 3 - import { supabaseServerClient } from "supabase/serverClient"; 4 - 5 - import { HomeContent } from "./HomeLayout"; 6 - 7 - export default async function Home() { 8 - let auth_res = await getIdentityData(); 9 - 10 - let [allLeafletFacts] = await Promise.all([ 11 - auth_res 12 - ? getFactsFromHomeLeaflets.handler( 13 - { 14 - tokens: auth_res.permission_token_on_homepage.map( 15 - (r) => r.permission_tokens.root_entity, 16 - ), 17 - }, 18 - { supabase: supabaseServerClient }, 19 - ) 20 - : undefined, 21 - ]); 22 - 23 - let home_docs_initialFacts = allLeafletFacts?.result || {}; 24 - 25 - return ( 26 - <HomeContent 27 - titles={{ 28 - ...home_docs_initialFacts.titles, 29 - ...auth_res?.permission_token_on_homepage.reduce( 30 - (acc, tok) => { 31 - let title = 32 - tok.permission_tokens.leaflets_in_publications[0]?.title || 33 - tok.permission_tokens.leaflets_to_documents[0]?.title; 34 - if (title) acc[tok.permission_tokens.root_entity] = title; 35 - return acc; 36 - }, 37 - {} as { [k: string]: string }, 38 - ), 39 - }} 40 - entityID={auth_res?.home_leaflet?.root_entity || null} 41 - initialFacts={home_docs_initialFacts.facts || {}} 42 - /> 43 - ); 44 - }
app/(home-pages)/(writer)/home/storage.ts app/(app)/(home-pages)/(writer)/home/storage.ts
app/(home-pages)/(writer)/layout.tsx app/(app)/(home-pages)/(writer)/layout.tsx
-103
app/(home-pages)/(writer)/looseleafs/LooseleafsLayout.tsx
··· 1 - "use client"; 2 - import { DashboardPageLayout } from "components/PageLayouts/DashboardPageLayout"; 3 - import { useState } from "react"; 4 - import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 5 - import { Fact, PermissionToken } from "src/replicache"; 6 - import { Attribute } from "src/replicache/attributes"; 7 - import { callRPC } from "app/api/rpc/client"; 8 - import { useIdentityData } from "components/IdentityProvider"; 9 - import useSWR from "swr"; 10 - import { Leaflet, LeafletList } from "../home/HomeLayout"; 11 - import { CreateNewLeafletButton } from "../home/Actions/CreateNewButton"; 12 - 13 - export const LooseleafsContent = (props: { 14 - entityID: string | null; 15 - titles: { [root_entity: string]: string }; 16 - initialFacts: { 17 - [root_entity: string]: Fact<Attribute>[]; 18 - }; 19 - }) => { 20 - let [searchValue, setSearchValue] = useState(""); 21 - let [debouncedSearchValue, setDebouncedSearchValue] = useState(""); 22 - 23 - useDebouncedEffect( 24 - () => { 25 - setDebouncedSearchValue(searchValue); 26 - }, 27 - 200, 28 - [searchValue], 29 - ); 30 - 31 - return ( 32 - <DashboardPageLayout 33 - scrollKey="dashboard-looseleafs-home" 34 - pageTitle="Looseleafs" 35 - mobileActions={<CreateNewLeafletButton compact />} 36 - showHeader={false} 37 - > 38 - <LooseleafList 39 - titles={props.titles} 40 - initialFacts={props.initialFacts} 41 - searchValue={debouncedSearchValue} 42 - /> 43 - </DashboardPageLayout> 44 - ); 45 - }; 46 - 47 - export const LooseleafList = (props: { 48 - titles: { [root_entity: string]: string }; 49 - initialFacts: { 50 - [root_entity: string]: Fact<Attribute>[]; 51 - }; 52 - searchValue: string; 53 - }) => { 54 - let { identity } = useIdentityData(); 55 - let { data: initialFacts } = useSWR( 56 - "home-leaflet-data", 57 - async () => { 58 - if (identity) { 59 - let { result } = await callRPC("getFactsFromHomeLeaflets", { 60 - tokens: identity.permission_token_on_homepage.map( 61 - (ptrh) => ptrh.permission_tokens.root_entity, 62 - ), 63 - }); 64 - let titles = { 65 - ...result.titles, 66 - ...identity.permission_token_on_homepage.reduce( 67 - (acc, tok) => { 68 - let title = 69 - tok.permission_tokens.leaflets_in_publications[0]?.title || 70 - tok.permission_tokens.leaflets_to_documents[0]?.title; 71 - if (title) acc[tok.permission_tokens.root_entity] = title; 72 - return acc; 73 - }, 74 - {} as { [k: string]: string }, 75 - ), 76 - }; 77 - return { ...result, titles }; 78 - } 79 - }, 80 - { fallbackData: { facts: props.initialFacts, titles: props.titles } }, 81 - ); 82 - 83 - let leaflets: Leaflet[] = identity 84 - ? identity.permission_token_on_homepage 85 - .filter( 86 - (ptoh) => ptoh.permission_tokens.leaflets_to_documents.length > 0, 87 - ) 88 - .map((ptoh) => ({ 89 - added_at: ptoh.created_at, 90 - token: ptoh.permission_tokens as PermissionToken, 91 - })) 92 - : []; 93 - return ( 94 - <LeafletList 95 - defaultDisplay="list" 96 - searchValue={props.searchValue} 97 - leaflets={leaflets} 98 - titles={initialFacts?.titles || {}} 99 - initialFacts={initialFacts?.facts || {}} 100 - showPreview 101 - /> 102 - ); 103 - };
-43
app/(home-pages)/(writer)/looseleafs/page.tsx
··· 1 - import { getIdentityData } from "actions/getIdentityData"; 2 - import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets"; 3 - import { supabaseServerClient } from "supabase/serverClient"; 4 - import { LooseleafsContent } from "./LooseleafsLayout"; 5 - 6 - export default async function Home() { 7 - let auth_res = await getIdentityData(); 8 - 9 - let [allLeafletFacts] = await Promise.all([ 10 - auth_res 11 - ? getFactsFromHomeLeaflets.handler( 12 - { 13 - tokens: auth_res.permission_token_on_homepage.map( 14 - (r) => r.permission_tokens.root_entity, 15 - ), 16 - }, 17 - { supabase: supabaseServerClient }, 18 - ) 19 - : undefined, 20 - ]); 21 - 22 - let home_docs_initialFacts = allLeafletFacts?.result || {}; 23 - 24 - return ( 25 - <LooseleafsContent 26 - entityID={auth_res?.home_leaflet?.root_entity || null} 27 - titles={{ 28 - ...home_docs_initialFacts.titles, 29 - ...auth_res?.permission_token_on_homepage.reduce( 30 - (acc, tok) => { 31 - let title = 32 - tok.permission_tokens.leaflets_in_publications[0]?.title || 33 - tok.permission_tokens.leaflets_to_documents[0]?.title; 34 - if (title) acc[tok.permission_tokens.root_entity] = title; 35 - return acc; 36 - }, 37 - {} as { [k: string]: string }, 38 - ), 39 - }} 40 - initialFacts={home_docs_initialFacts.facts || {}} 41 - /> 42 - ); 43 - }
+1 -1
app/(home-pages)/(writer)/notifications/BskyPostEmbedNotification.tsx app/(app)/(home-pages)/(writer)/notifications/BskyPostEmbedNotification.tsx
··· 2 2 import { ContentLayout, Notification } from "./Notification"; 3 3 import { HydratedBskyPostEmbedNotification } from "src/notifications"; 4 4 import { AtUri } from "@atproto/api"; 5 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 5 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 6 6 7 7 export const BskyPostEmbedNotification = ( 8 8 props: HydratedBskyPostEmbedNotification,
+1 -1
app/(home-pages)/(writer)/notifications/CommentMentionNotification.tsx app/(app)/(home-pages)/(writer)/notifications/CommentMentionNotification.tsx
··· 8 8 Notification, 9 9 } from "./Notification"; 10 10 import { AtUri } from "@atproto/api"; 11 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 11 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 12 12 13 13 export const CommentMentionNotification = ( 14 14 props: HydratedCommentMentionNotification,
+2 -2
app/(home-pages)/(writer)/notifications/CommentNotication.tsx app/(app)/(home-pages)/(writer)/notifications/CommentNotication.tsx
··· 1 - import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 1 + import { BaseTextBlock } from "app/(app)/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 2 2 import { AppBskyActorProfile, PubLeafletComment } from "lexicons/api"; 3 3 import { HydratedCommentNotification } from "src/notifications"; 4 4 import { blobRefToSrc } from "src/utils/blobRefToSrc"; ··· 10 10 Notification, 11 11 } from "./Notification"; 12 12 import { AtUri } from "@atproto/api"; 13 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 13 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 14 14 15 15 export const CommentNotification = (props: HydratedCommentNotification) => { 16 16 const docRecord = props.normalizedDocument;
app/(home-pages)/(writer)/notifications/FollowNotification.tsx app/(app)/(home-pages)/(writer)/notifications/FollowNotification.tsx
+1 -1
app/(home-pages)/(writer)/notifications/MentionNotification.tsx app/(app)/(home-pages)/(writer)/notifications/MentionNotification.tsx
··· 2 2 import { ContentLayout, Notification } from "./Notification"; 3 3 import { HydratedMentionNotification } from "src/notifications"; 4 4 import { AtUri } from "@atproto/api"; 5 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 5 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 6 6 7 7 export const MentionNotification = (props: HydratedMentionNotification) => { 8 8 const docRecord = props.normalizedDocument;
+1 -1
app/(home-pages)/(writer)/notifications/Notification.tsx app/(app)/(home-pages)/(writer)/notifications/Notification.tsx
··· 1 1 "use client"; 2 2 import { Avatar } from "components/Avatar"; 3 - import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 3 + import { BaseTextBlock } from "app/(app)/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 4 4 import { PubLeafletRichtextFacet } from "lexicons/api"; 5 5 import { timeAgo } from "src/utils/timeAgo"; 6 6 import { useReplicache, useEntity } from "src/replicache";
app/(home-pages)/(writer)/notifications/NotificationList.tsx app/(app)/(home-pages)/(writer)/notifications/NotificationList.tsx
+1 -1
app/(home-pages)/(writer)/notifications/QuoteNotification.tsx app/(app)/(home-pages)/(writer)/notifications/QuoteNotification.tsx
··· 3 3 import { HydratedQuoteNotification } from "src/notifications"; 4 4 import { AtUri } from "@atproto/api"; 5 5 import { Avatar } from "components/Avatar"; 6 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 6 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 7 7 8 8 export const QuoteNotification = (props: HydratedQuoteNotification) => { 9 9 const postView = props.bskyPost.post_view as any;
+1 -1
app/(home-pages)/(writer)/notifications/RecommendNotification.tsx app/(app)/(home-pages)/(writer)/notifications/RecommendNotification.tsx
··· 5 5 import { Avatar } from "components/Avatar"; 6 6 import { AtUri } from "@atproto/api"; 7 7 import { RecommendTinyFilled } from "components/Icons/RecommendTiny"; 8 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 8 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 9 9 10 10 export const RecommendNotification = ( 11 11 props: HydratedRecommendNotification,
+2 -2
app/(home-pages)/(writer)/notifications/ReplyNotification.tsx app/(app)/(home-pages)/(writer)/notifications/ReplyNotification.tsx
··· 1 1 import { Avatar } from "components/Avatar"; 2 - import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 2 + import { BaseTextBlock } from "app/(app)/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 3 3 import { ReplyTiny } from "components/Icons/ReplyTiny"; 4 4 import { 5 5 CommentInNotification, ··· 10 10 import { PubLeafletComment } from "lexicons/api"; 11 11 import { AppBskyActorProfile, AtUri } from "@atproto/api"; 12 12 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 13 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 13 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 14 14 15 15 export const ReplyNotification = (props: HydratedCommentNotification) => { 16 16 const docRecord = props.normalizedDocument;
app/(home-pages)/(writer)/notifications/getNotifications.ts app/(app)/(home-pages)/(writer)/notifications/getNotifications.ts
app/(home-pages)/(writer)/notifications/page.tsx app/(app)/(home-pages)/(writer)/notifications/page.tsx
app/(home-pages)/layout.tsx app/(app)/(home-pages)/layout.tsx
+1 -1
app/(home-pages)/p/[didOrHandle]/PostsContent.tsx app/(app)/(home-pages)/p/[didOrHandle]/PostsContent.tsx
··· 2 2 3 3 import { EmptyState } from "components/EmptyState"; 4 4 import { PostListing } from "components/PostListing"; 5 - import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 5 + import type { Post } from "app/(app)/(home-pages)/reader/getReaderFeed"; 6 6 import type { Cursor } from "./getProfilePosts"; 7 7 import { getProfilePosts } from "./getProfilePosts"; 8 8 import useSWRInfinite from "swr/infinite";
app/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx app/(app)/(home-pages)/p/[didOrHandle]/ProfileHeader.tsx
app/(home-pages)/p/[didOrHandle]/ProfileLayout.tsx app/(app)/(home-pages)/p/[didOrHandle]/ProfileLayout.tsx
app/(home-pages)/p/[didOrHandle]/ProfileTabs.tsx app/(app)/(home-pages)/p/[didOrHandle]/ProfileTabs.tsx
+1 -1
app/(home-pages)/p/[didOrHandle]/PubListing.tsx app/(app)/(home-pages)/p/[didOrHandle]/PubListing.tsx
··· 1 1 "use client"; 2 2 import { AtUri } from "@atproto/syntax"; 3 - import { PublicationSubscription } from "app/(home-pages)/reader/getSubscriptions"; 3 + import { PublicationSubscription } from "app/(app)/(home-pages)/reader/getSubscriptions"; 4 4 import { PubIcon } from "components/ActionBar/Publications"; 5 5 import { SubscribeInput } from "components/Subscribe/SubscribeButton"; 6 6 import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider";
+2 -2
app/(home-pages)/p/[didOrHandle]/comments/CommentsContent.tsx app/(app)/(home-pages)/p/[didOrHandle]/comments/CommentsContent.tsx
··· 7 7 import { PubLeafletComment, PubLeafletDocument } from "lexicons/api"; 8 8 import { ReplyTiny } from "components/Icons/ReplyTiny"; 9 9 import { Avatar } from "components/Avatar"; 10 - import { BaseTextBlock } from "app/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 10 + import { BaseTextBlock } from "app/(app)/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock"; 11 11 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 12 12 import { 13 13 getProfileComments, ··· 15 15 type Cursor, 16 16 } from "./getProfileComments"; 17 17 import { timeAgo } from "src/utils/timeAgo"; 18 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 18 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 19 19 20 20 export const ProfileCommentsContent = (props: { 21 21 did: string;
app/(home-pages)/p/[didOrHandle]/comments/getProfileComments.ts app/(app)/(home-pages)/p/[didOrHandle]/comments/getProfileComments.ts
+1 -1
app/(home-pages)/p/[didOrHandle]/comments/page.tsx app/(app)/(home-pages)/p/[didOrHandle]/comments/page.tsx
··· 1 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 1 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 2 2 import { getProfileComments } from "./getProfileComments"; 3 3 import { ProfileCommentsContent } from "./CommentsContent"; 4 4
+2 -2
app/(home-pages)/p/[didOrHandle]/getProfilePosts.ts app/(app)/(home-pages)/p/[didOrHandle]/getProfilePosts.ts
··· 1 1 "use server"; 2 2 3 3 import { supabaseServerClient } from "supabase/serverClient"; 4 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 5 - import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 4 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 5 + import type { Post } from "app/(app)/(home-pages)/reader/getReaderFeed"; 6 6 import { 7 7 normalizeDocumentRecord, 8 8 normalizePublicationRecord,
+1 -1
app/(home-pages)/p/[didOrHandle]/layout.tsx app/(app)/(home-pages)/p/[didOrHandle]/layout.tsx
··· 1 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 1 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 2 2 import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 3 3 import { supabaseServerClient } from "supabase/serverClient"; 4 4 import { Json } from "supabase/database.types";
+1 -1
app/(home-pages)/p/[didOrHandle]/page.tsx app/(app)/(home-pages)/p/[didOrHandle]/page.tsx
··· 1 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 1 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 2 2 import { getProfilePosts } from "./getProfilePosts"; 3 3 import { ProfilePostsContent } from "./PostsContent"; 4 4
+3 -3
app/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx app/(app)/(home-pages)/p/[didOrHandle]/subscriptions/SubscriptionsContent.tsx
··· 3 3 import { EmptyState } from "components/EmptyState"; 4 4 import { useEffect, useRef } from "react"; 5 5 import useSWRInfinite from "swr/infinite"; 6 - import { PubListing } from "app/(home-pages)/p/[didOrHandle]/PubListing"; 6 + import { PubListing } from "app/(app)/(home-pages)/p/[didOrHandle]/PubListing"; 7 7 import { 8 8 getSubscriptions, 9 9 type PublicationSubscription, 10 - } from "app/(home-pages)/reader/getSubscriptions"; 11 - import { Cursor } from "app/(home-pages)/reader/getReaderFeed"; 10 + } from "app/(app)/(home-pages)/reader/getSubscriptions"; 11 + import { Cursor } from "app/(app)/(home-pages)/reader/getReaderFeed"; 12 12 13 13 export const ProfileSubscriptionsContent = (props: { 14 14 did: string;
+2 -2
app/(home-pages)/p/[didOrHandle]/subscriptions/page.tsx app/(app)/(home-pages)/p/[didOrHandle]/subscriptions/page.tsx
··· 1 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 2 - import { getSubscriptions } from "app/(home-pages)/reader/getSubscriptions"; 1 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 2 + import { getSubscriptions } from "app/(app)/(home-pages)/reader/getSubscriptions"; 3 3 import { ProfileSubscriptionsContent } from "./SubscriptionsContent"; 4 4 5 5 export default async function ProfileSubscriptionsPage(props: {
-23
app/(home-pages)/page.tsx
··· 1 - import type { Metadata } from "next"; 2 - import { cookies } from "next/headers"; 3 - import { redirect } from "next/navigation"; 4 - import { AboutPage } from "../about/AboutPage"; 5 - 6 - export const metadata: Metadata = { 7 - title: "Leaflet — social publishing on the Atmosphere", 8 - description: 9 - "A simple and powerful platform for social publishing — blogs, newsletters, and more!", 10 - }; 11 - 12 - export default async function RootPage() { 13 - const cookieStore = await cookies(); 14 - const hasAuth = 15 - cookieStore.has("auth_token") || cookieStore.has("external_auth_token"); 16 - 17 - if (!hasAuth) return <AboutPage />; 18 - 19 - const navState = cookieStore.get("nav-state")?.value; 20 - if (navState === "reader") redirect("/reader"); 21 - 22 - redirect("/home"); 23 - }
app/(home-pages)/reader/FeedLayout.tsx app/(app)/(home-pages)/reader/FeedLayout.tsx
app/(home-pages)/reader/FeedSkeleton.tsx app/(app)/(home-pages)/reader/FeedSkeleton.tsx
app/(home-pages)/reader/GlobalContent.tsx app/(app)/(home-pages)/reader/GlobalContent.tsx
app/(home-pages)/reader/InboxContent.tsx app/(app)/(home-pages)/reader/InboxContent.tsx
app/(home-pages)/reader/NewContent.tsx app/(app)/(home-pages)/reader/NewContent.tsx
app/(home-pages)/reader/PreviewDrawer.tsx app/(app)/(home-pages)/reader/PreviewDrawer.tsx
+1 -1
app/(home-pages)/reader/ReaderMentionsContent.tsx app/(app)/(home-pages)/reader/ReaderMentionsContent.tsx
··· 1 1 "use client"; 2 2 import useSWR from "swr"; 3 3 import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 4 - import { BskyPostContent } from "app/lish/[did]/[publication]/[rkey]/BskyPostContent"; 4 + import { BskyPostContent } from "app/(app)/lish/[did]/[publication]/[rkey]/BskyPostContent"; 5 5 import { EmptyState } from "components/EmptyState"; 6 6 import { DotLoader } from "components/utils/DotLoader"; 7 7
+3 -3
app/(home-pages)/reader/enrichPost.ts app/(app)/(home-pages)/reader/enrichPost.ts
··· 1 1 import { AtUri } from "@atproto/api"; 2 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 3 - import { getConstellationBacklinks } from "app/lish/[did]/[publication]/[rkey]/getPostPageData"; 4 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 2 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 3 + import { getConstellationBacklinks } from "app/(app)/lish/[did]/[publication]/[rkey]/getPostPageData"; 4 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 5 5 import { 6 6 normalizeDocumentRecord, 7 7 normalizePublicationRecord,
app/(home-pages)/reader/getHotFeed.ts app/(app)/(home-pages)/reader/getHotFeed.ts
app/(home-pages)/reader/getNewFeed.ts app/(app)/(home-pages)/reader/getNewFeed.ts
app/(home-pages)/reader/getReaderFeed.ts app/(app)/(home-pages)/reader/getReaderFeed.ts
app/(home-pages)/reader/getSubscriptions.ts app/(app)/(home-pages)/reader/getSubscriptions.ts
app/(home-pages)/reader/hot/page.tsx app/(app)/(home-pages)/reader/hot/page.tsx
app/(home-pages)/reader/idResolver.ts app/(app)/(home-pages)/reader/idResolver.ts
app/(home-pages)/reader/layout.tsx app/(app)/(home-pages)/reader/layout.tsx
app/(home-pages)/reader/new/page.tsx app/(app)/(home-pages)/reader/new/page.tsx
app/(home-pages)/reader/page.tsx app/(app)/(home-pages)/reader/page.tsx
app/(home-pages)/reader/trending/page.tsx app/(app)/(home-pages)/reader/trending/page.tsx
+3 -3
app/(home-pages)/tag/[tag]/getDocumentsByTag.ts app/(app)/(home-pages)/tag/[tag]/getDocumentsByTag.ts
··· 1 1 "use server"; 2 2 3 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 3 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 4 4 import { supabaseServerClient } from "supabase/serverClient"; 5 5 import { AtUri } from "@atproto/api"; 6 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 7 - import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 6 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 7 + import type { Post } from "app/(app)/(home-pages)/reader/getReaderFeed"; 8 8 import { 9 9 normalizeDocumentRecord, 10 10 normalizePublicationRecord,
app/(home-pages)/tag/[tag]/layout.tsx app/(app)/(home-pages)/tag/[tag]/layout.tsx
app/(home-pages)/tag/[tag]/page.tsx app/(app)/(home-pages)/tag/[tag]/page.tsx
+1 -1
app/[leaflet_id]/AddToHomeEffect.tsx app/(app)/[leaflet_id]/AddToHomeEffect.tsx
··· 3 3 import { usePathname, useRouter, useSearchParams } from "next/navigation"; 4 4 import { useEffect, useRef } from "react"; 5 5 import { mutate as swrMutate } from "swr"; 6 - import { addDocToHome } from "app/(home-pages)/(writer)/home/storage"; 6 + import { addDocToHome } from "app/(app)/(home-pages)/(writer)/home/storage"; 7 7 import { useIdentityData } from "components/IdentityProvider"; 8 8 import { useReplicache } from "src/replicache"; 9 9
+3 -3
app/[leaflet_id]/Footer.tsx app/(app)/[leaflet_id]/Footer.tsx
··· 5 5 import { ThemePopover } from "components/ThemeManager/ThemeSetter"; 6 6 import { Toolbar } from "components/Toolbar"; 7 7 import { FootnoteToolbar } from "components/Toolbar/FootnoteToolbarWrapper"; 8 - import { ShareOptions } from "app/[leaflet_id]/actions/ShareOptions"; 8 + import { ShareOptions } from "app/(app)/[leaflet_id]/actions/ShareOptions"; 9 9 import { 10 10 AddToHomeButton, 11 11 HomeButton, 12 - } from "app/[leaflet_id]/actions/HomeButton"; 12 + } from "app/(app)/[leaflet_id]/actions/HomeButton"; 13 13 import { PublishButton } from "./actions/PublishButton"; 14 14 import { useEntitySetContext } from "components/EntitySetProvider"; 15 15 import { Watermark } from "components/Watermark"; ··· 20 20 import { block } from "sharp"; 21 21 import { PostSettings } from "components/PostSettings"; 22 22 import useSWR from "swr"; 23 - import { getHomeDocs } from "app/(home-pages)/(writer)/home/storage"; 23 + import { getHomeDocs } from "app/(app)/(home-pages)/(writer)/home/storage"; 24 24 import { useAddToHomeParam } from "./AddToHomeEffect"; 25 25 26 26 export function hasBlockToolbar(blockType: string | null | undefined) {
app/[leaflet_id]/Leaflet.tsx app/(app)/[leaflet_id]/Leaflet.tsx
+4 -4
app/[leaflet_id]/Sidebar.tsx app/(app)/[leaflet_id]/Sidebar.tsx
··· 1 1 "use client"; 2 2 import { Sidebar } from "components/ActionBar/Sidebar"; 3 3 import { useEntitySetContext } from "components/EntitySetProvider"; 4 - import { HelpButton } from "app/[leaflet_id]/actions/HelpButton"; 4 + import { HelpButton } from "app/(app)/[leaflet_id]/actions/HelpButton"; 5 5 import { 6 6 AddToHomeButton, 7 7 HomeButton, 8 - } from "app/[leaflet_id]/actions/HomeButton"; 8 + } from "app/(app)/[leaflet_id]/actions/HomeButton"; 9 9 import { Media } from "components/Media"; 10 10 import { 11 11 useLeafletPublicationData, 12 12 useLeafletPublicationPage, 13 13 } from "components/PageSWRDataProvider"; 14 - import { ShareOptions } from "app/[leaflet_id]/actions/ShareOptions"; 14 + import { ShareOptions } from "app/(app)/[leaflet_id]/actions/ShareOptions"; 15 15 import { ThemePopover } from "components/ThemeManager/ThemeSetter"; 16 16 import { PublishButton } from "./actions/PublishButton"; 17 17 import { PostSettings } from "components/PostSettings"; ··· 20 20 import { useIdentityData } from "components/IdentityProvider"; 21 21 import { useReplicache } from "src/replicache"; 22 22 import useSWR from "swr"; 23 - import { getHomeDocs } from "app/(home-pages)/(writer)/home/storage"; 23 + import { getHomeDocs } from "app/(app)/(home-pages)/(writer)/home/storage"; 24 24 import { useAddToHomeParam } from "./AddToHomeEffect"; 25 25 26 26 export function LeafletSidebar() {
app/[leaflet_id]/WelcomeModal.tsx app/(app)/[leaflet_id]/WelcomeModal.tsx
+1 -1
app/[leaflet_id]/actions/BackToPubButton.tsx app/(app)/[leaflet_id]/actions/BackToPubButton.tsx
··· 1 - import { getBasePublicationURL } from "app/lish/createPub/getPublicationURL"; 1 + import { getBasePublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 2 2 import { ActionButton } from "components/ActionBar/ActionButton"; 3 3 import { GoBackSmall } from "components/Icons/GoBackSmall"; 4 4 import { SpeedyLink } from "components/SpeedyLink";
+5 -5
app/[leaflet_id]/actions/HelpButton.tsx app/(app)/[leaflet_id]/actions/HelpButton.tsx
··· 1 1 "use client"; 2 - import { ShortcutKey } from "../../../components/Layout"; 3 - import { Media } from "../../../components/Media"; 4 - import { Popover } from "../../../components/Popover"; 2 + import { ShortcutKey } from "components/Layout"; 3 + import { Media } from "components/Media"; 4 + import { Popover } from "components/Popover"; 5 5 import { metaKey } from "src/utils/metaKey"; 6 - import { useEntitySetContext } from "../../../components/EntitySetProvider"; 6 + import { useEntitySetContext } from "components/EntitySetProvider"; 7 7 import { useState } from "react"; 8 8 import { ActionButton } from "components/ActionBar/ActionButton"; 9 - import { HelpSmall } from "../../../components/Icons/HelpSmall"; 9 + import { HelpSmall } from "components/Icons/HelpSmall"; 10 10 import { isMac } from "src/utils/isDevice"; 11 11 import { useIsMobile } from "src/hooks/isMobile"; 12 12 import { HelpContent } from "components/ActionBar/DesktopNavigation";
+8 -6
app/[leaflet_id]/actions/HomeButton.tsx app/(app)/[leaflet_id]/actions/HomeButton.tsx
··· 1 1 "use client"; 2 2 import Link from "next/link"; 3 - import { useEntitySetContext } from "../../../components/EntitySetProvider"; 3 + import { useEntitySetContext } from "components/EntitySetProvider"; 4 4 import { ActionButton } from "components/ActionBar/ActionButton"; 5 5 import { useSearchParams } from "next/navigation"; 6 - import { useIdentityData } from "../../../components/IdentityProvider"; 6 + import { useIdentityData } from "components/IdentityProvider"; 7 7 import { useReplicache } from "src/replicache"; 8 8 import { addLeafletToHome } from "actions/addLeafletToHome"; 9 - import { useSmoker } from "../../../components/Toast"; 10 - import { AddToHomeSmall } from "../../../components/Icons/AddToHomeSmall"; 11 - import { HomeSmall } from "../../../components/Icons/HomeSmall"; 9 + import { useSmoker } from "components/Toast"; 10 + import { AddToHomeSmall } from "components/Icons/AddToHomeSmall"; 11 + import { HomeSmall } from "components/Icons/HomeSmall"; 12 12 import { produce } from "immer"; 13 - import { addDocToHome } from "app/(home-pages)/(writer)/home/storage"; 13 + import { addDocToHome } from "app/(app)/(home-pages)/(writer)/home/storage"; 14 14 import { mutate as swrMutate } from "swr"; 15 15 16 16 export function HomeButton() { ··· 52 52 archived: null, 53 53 permission_tokens: { 54 54 ...permission_token, 55 + title: null, 56 + description: null, 55 57 leaflets_to_documents: [], 56 58 leaflets_in_publications: [], 57 59 },
+1 -1
app/[leaflet_id]/actions/PublishButton.tsx app/(app)/[leaflet_id]/actions/PublishButton.tsx
··· 1 1 "use client"; 2 2 import { publishToPublication } from "actions/publishToPublication"; 3 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 3 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 4 4 import { ActionButton } from "components/ActionBar/ActionButton"; 5 5 import { 6 6 PubIcon,
app/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx app/(app)/[leaflet_id]/actions/ShareOptions/DomainOptions.tsx
app/[leaflet_id]/actions/ShareOptions/getShareLink.ts app/(app)/[leaflet_id]/actions/ShareOptions/getShareLink.ts
+1 -1
app/[leaflet_id]/actions/ShareOptions/index.tsx app/(app)/[leaflet_id]/actions/ShareOptions/index.tsx
··· 17 17 import { 18 18 getPublicationURL, 19 19 getDocumentURL, 20 - } from "app/lish/createPub/getPublicationURL"; 20 + } from "app/(app)/lish/createPub/getPublicationURL"; 21 21 import { AtUri } from "@atproto/syntax"; 22 22 import { useIsMobile } from "src/hooks/isMobile"; 23 23 import { LoginModal } from "components/LoginButton";
+1 -1
app/[leaflet_id]/icon.tsx app/(app)/[leaflet_id]/icon.tsx
··· 1 1 import { ImageResponse } from "next/og"; 2 2 import type { Fact } from "src/replicache"; 3 3 import type { Attribute } from "src/replicache/attributes"; 4 - import { Database } from "../../supabase/database.types"; 4 + import { Database } from "supabase/database.types"; 5 5 import { createServerClient } from "@supabase/ssr"; 6 6 import { parseHSBToRGB } from "src/utils/parseHSB"; 7 7
app/[leaflet_id]/opengraph-image.tsx app/(app)/[leaflet_id]/opengraph-image.tsx
app/[leaflet_id]/page.tsx app/(app)/[leaflet_id]/page.tsx
+1 -1
app/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx app/(app)/[leaflet_id]/publish/BskyPostEditorProsemirror.tsx
··· 9 9 import { history, undo, redo } from "prosemirror-history"; 10 10 import { inputRules, InputRule } from "prosemirror-inputrules"; 11 11 import { autolink } from "components/Blocks/TextBlock/autolink-plugin"; 12 - import { IOSBS } from "app/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox"; 12 + import { IOSBS } from "app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox"; 13 13 import { schema } from "components/Blocks/TextBlock/schema"; 14 14 import { Mention, MentionAutocomplete } from "components/Mention"; 15 15
+1 -1
app/[leaflet_id]/publish/PublishIllustration/PublishIllustration.tsx app/(app)/[leaflet_id]/publish/PublishIllustration/PublishIllustration.tsx
··· 14 14 Star2, 15 15 Star3, 16 16 } from "./Stars"; 17 - import { usePublicationData } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 17 + import { usePublicationData } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 18 18 import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 19 19 20 20 // TODO
app/[leaflet_id]/publish/PublishIllustration/Stars.tsx app/(app)/[leaflet_id]/publish/PublishIllustration/Stars.tsx
+2 -2
app/[leaflet_id]/publish/PublishPost.tsx app/(app)/[leaflet_id]/publish/PublishPost.tsx
··· 16 16 import { useSubscribe } from "src/replicache/useSubscribe"; 17 17 import { editorStateToFacetedText } from "./BskyPostEditorProsemirror"; 18 18 import { EditorState } from "prosemirror-state"; 19 - import { TagSelector } from "../../../components/Tags"; 19 + import { TagSelector } from "components/Tags"; 20 20 import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 21 21 import { PubIcon } from "components/ActionBar/Publications"; 22 22 import { OAuthErrorMessage, isOAuthSessionError } from "components/OAuthError"; ··· 34 34 PublicationBackgroundProvider, 35 35 } from "components/ThemeManager/PublicationThemeProvider"; 36 36 import { useEntity } from "src/replicache"; 37 - import { LeafletContent } from "app/(home-pages)/(writer)/home/LeafletList/LeafletContent"; 37 + import { LeafletContent } from "app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletContent"; 38 38 39 39 type Props = { 40 40 title: string;
app/[leaflet_id]/publish/ShareOptions.tsx app/(app)/[leaflet_id]/publish/ShareOptions.tsx
app/[leaflet_id]/publish/page.tsx app/(app)/[leaflet_id]/publish/page.tsx
app/[leaflet_id]/publish/publishBskyPost.ts app/(app)/[leaflet_id]/publish/publishBskyPost.ts
+2 -2
app/about/Examples.tsx
··· 1 1 import { GoToArrow } from "components/Icons/GoToArrow"; 2 2 import { supabaseServerClient } from "supabase/serverClient"; 3 3 import { normalizePublicationRecord } from "src/utils/normalizeRecords"; 4 - import { PubListing } from "app/(home-pages)/p/[didOrHandle]/PubListing"; 5 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 4 + import { PubListing } from "app/(app)/(home-pages)/p/[didOrHandle]/PubListing"; 5 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 6 6 import { SpeedyLink } from "components/SpeedyLink"; 7 7 8 8 const pubs = [
+6 -4
app/about/LandingCTA.tsx
··· 1 1 "use client"; 2 2 import Link from "next/link"; 3 - import { useState } from "react"; 3 + import { useEffect, useState } from "react"; 4 4 import { LoginModal } from "components/LoginButton"; 5 5 import { ButtonPrimary, ButtonSecondary } from "components/Buttons"; 6 6 import { Input } from "components/Input"; 7 - import { useIdentityData } from "components/IdentityProvider"; 7 + import { getIdentityData } from "actions/getIdentityData"; 8 8 9 9 export function LandingCTA() { 10 10 let [loginOpen, setLoginOpen] = useState(false); 11 - let { identity } = useIdentityData(); 12 - let signedIn = !!identity?.atp_did; 11 + let [signedIn, setSignedIn] = useState(false); 12 + useEffect(() => { 13 + getIdentityData().then((identity) => setSignedIn(!!identity?.atp_did)); 14 + }, []); 13 15 return ( 14 16 <div className="flex items-center justify-end gap-4 sm:gap-6"> 15 17 {signedIn ? (
+3 -1
app/about/page.tsx
··· 6 6 "A simple and powerful platform for social publishing — blogs, newsletters, and more!", 7 7 }; 8 8 9 - export default async function About() { 9 + export const revalidate = 3600; 10 + 11 + export default function About() { 10 12 return <AboutPage />; 11 13 }
+1 -1
app/api/inngest/functions/index_post_mention.ts
··· 8 8 pingIdentityToUpdateNotification, 9 9 } from "src/notifications"; 10 10 import { v7 } from "uuid"; 11 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 11 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 12 12 import { documentUriFilter } from "src/utils/uriHelpers"; 13 13 14 14 export const index_post_mention = inngest.createFunction(
+1 -1
app/api/inngest/functions/sync_document_metadata.ts
··· 1 1 import { inngest, events } from "../client"; 2 2 import { supabaseServerClient } from "supabase/serverClient"; 3 3 import { AtpAgent, AtUri } from "@atproto/api"; 4 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 4 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 5 5 import type { Json } from "supabase/database.types"; 6 6 7 7 // 1m, 2m, 4m, 8m, 16m, 32m, 1h, 2h, 4h, 8h, 8h, 8h (~37h total)
+1 -1
app/api/oauth/[route]/route.ts
··· 1 1 import { 2 2 backfillAtprotoSubscriptionsForIdentity, 3 3 subscribeToPublication, 4 - } from "app/lish/subscribeToPublication"; 4 + } from "app/(app)/lish/subscribeToPublication"; 5 5 import { cookies } from "next/headers"; 6 6 import { redirect } from "next/navigation"; 7 7 import { NextRequest, NextResponse } from "next/server";
+23
app/api/rpc/[command]/getFactsForRoots.ts
··· 1 + import { z } from "zod"; 2 + import type { Fact } from "src/replicache"; 3 + import type { Attribute } from "src/replicache/attributes"; 4 + import { makeRoute } from "../lib"; 5 + import type { Env } from "./route"; 6 + 7 + export const getFactsForRoots = makeRoute({ 8 + route: "getFactsForRoots", 9 + input: z.object({ 10 + roots: z.array(z.string()), 11 + }), 12 + handler: async ({ roots }, { supabase }: Pick<Env, "supabase">) => { 13 + const { data } = await supabase.rpc("get_facts_for_roots", { 14 + max_depth: 3, 15 + roots, 16 + }); 17 + const facts: { [root: string]: Fact<Attribute>[] } = {}; 18 + for (const f of data ?? []) { 19 + (facts[f.root_id] ??= []).push(f as unknown as Fact<Attribute>); 20 + } 21 + return { result: { facts } }; 22 + }, 23 + });
-91
app/api/rpc/[command]/getFactsFromHomeLeaflets.ts
··· 1 - import { z } from "zod"; 2 - import type { Fact } from "src/replicache"; 3 - import type { Attribute } from "src/replicache/attributes"; 4 - import { makeRoute } from "../lib"; 5 - import type { Env } from "./route"; 6 - import { scanIndexLocal } from "src/replicache/utils"; 7 - import * as base64 from "base64-js"; 8 - import { YJSFragmentToString } from "src/utils/yjsFragmentToString"; 9 - import { applyUpdate, Doc } from "yjs"; 10 - 11 - export const getFactsFromHomeLeaflets = makeRoute({ 12 - route: "getFactsFromHomeLeaflets", 13 - input: z.object({ 14 - tokens: z.array(z.string()), 15 - }), 16 - handler: async ({ tokens }, { supabase }: Pick<Env, "supabase">) => { 17 - let all_facts = await supabase.rpc("get_facts_for_roots", { 18 - max_depth: 3, 19 - roots: tokens, 20 - }); 21 - 22 - if (all_facts.data) { 23 - let titles = {} as { [key: string]: string }; 24 - 25 - let facts = all_facts.data.reduce( 26 - (acc, fact) => { 27 - if (!acc[fact.root_id]) acc[fact.root_id] = []; 28 - acc[fact.root_id].push(fact as unknown as Fact<Attribute>); 29 - return acc; 30 - }, 31 - {} as { [key: string]: Fact<Attribute>[] }, 32 - ); 33 - for (let token of tokens) { 34 - let scan = scanIndexLocal(facts[token] || []); 35 - let [root] = scan.eav(token, "root/page"); 36 - let rootEntity = root?.data.value || token; 37 - 38 - // Check page type to determine which blocks to look up 39 - let [pageType] = scan.eav(rootEntity, "page/type"); 40 - let isCanvas = pageType?.data.value === "canvas"; 41 - 42 - // Get blocks and sort by position 43 - let rawBlocks = isCanvas 44 - ? scan.eav(rootEntity, "canvas/block").sort((a, b) => { 45 - if (a.data.position.y === b.data.position.y) 46 - return a.data.position.x - b.data.position.x; 47 - return a.data.position.y - b.data.position.y; 48 - }) 49 - : scan.eav(rootEntity, "card/block").sort((a, b) => { 50 - if (a.data.position === b.data.position) 51 - return a.id > b.id ? 1 : -1; 52 - return a.data.position > b.data.position ? 1 : -1; 53 - }); 54 - 55 - // Map to get type and filter for text/heading 56 - let blocks = rawBlocks 57 - .map((b) => { 58 - let type = scan.eav(b.data.value, "block/type")[0]; 59 - if ( 60 - !type || 61 - (type.data.value !== "text" && type.data.value !== "heading") 62 - ) 63 - return null; 64 - return b.data; 65 - }) 66 - .filter((b): b is NonNullable<typeof b> => b !== null); 67 - 68 - let title = blocks[0]; 69 - 70 - if (!title) titles[token] = "Untitled"; 71 - else { 72 - let [content] = scan.eav(title.value, "block/text"); 73 - if (!content) titles[token] = "Untitled"; 74 - else { 75 - let doc = new Doc(); 76 - const update = base64.toByteArray(content.data.value); 77 - applyUpdate(doc, update); 78 - let nodes = doc.getXmlElement("prosemirror").toArray(); 79 - let stringValue = YJSFragmentToString(nodes[0]); 80 - titles[token] = stringValue; 81 - } 82 - } 83 - } 84 - return { 85 - result: { facts, titles }, 86 - }; 87 - } 88 - 89 - return { result: {} }; 90 - }, 91 - });
+2 -2
app/api/rpc/[command]/get_document_interactions.ts
··· 1 1 import { z } from "zod"; 2 2 import { makeRoute } from "../lib"; 3 3 import type { Env } from "./route"; 4 - import { getConstellationBacklinks } from "app/lish/[did]/[publication]/[rkey]/getPostPageData"; 5 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 4 + import { getConstellationBacklinks } from "app/(app)/lish/[did]/[publication]/[rkey]/getPostPageData"; 5 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 6 6 import { 7 7 normalizeDocumentRecord, 8 8 normalizePublicationRecord,
+2 -2
app/api/rpc/[command]/get_hot_feed.ts
··· 1 1 import { z } from "zod"; 2 2 import { makeRoute } from "../lib"; 3 3 import type { Env } from "./route"; 4 - import { getHotFeed } from "app/(home-pages)/reader/getHotFeed"; 5 - import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 4 + import { getHotFeed } from "app/(app)/(home-pages)/reader/getHotFeed"; 5 + import type { Post } from "app/(app)/(home-pages)/reader/getReaderFeed"; 6 6 7 7 export type GetHotFeedReturnType = Awaited< 8 8 ReturnType<(typeof get_hot_feed)["handler"]>
+1 -1
app/api/rpc/[command]/get_profile_data.ts
··· 1 1 import { z } from "zod"; 2 2 import { makeRoute } from "../lib"; 3 3 import type { Env } from "./route"; 4 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 4 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 5 5 import { supabaseServerClient } from "supabase/serverClient"; 6 6 import { Agent } from "@atproto/api"; 7 7 import { getIdentityData } from "actions/getIdentityData";
-16
app/api/rpc/[command]/get_publication_data.ts
··· 2 2 import { makeRoute } from "../lib"; 3 3 import type { Env } from "./route"; 4 4 import { AtUri } from "@atproto/syntax"; 5 - import { getFactsFromHomeLeaflets } from "./getFactsFromHomeLeaflets"; 6 5 import { normalizeDocumentRecord } from "src/utils/normalizeRecords"; 7 6 import { ids } from "lexicons/api/lexicons"; 8 7 ··· 69 68 .limit(1) 70 69 .single(); 71 70 72 - let leaflet_data = await getFactsFromHomeLeaflets.handler( 73 - { 74 - tokens: [ 75 - ...(publication?.leaflets_in_publications.map( 76 - (l) => l.permission_tokens?.root_entity!, 77 - ) || []), 78 - ...(publication?.publication_pages.map( 79 - (p) => p.permission_tokens?.root_entity!, 80 - ) || []), 81 - ].filter(Boolean), 82 - }, 83 - { supabase }, 84 - ); 85 - 86 71 // Pre-normalize documents from documents_in_publications 87 72 const documents = (publication?.documents_in_publications || []) 88 73 .map((dip) => { ··· 140 125 documents, 141 126 drafts, 142 127 pages, 143 - leaflet_data: leaflet_data.result, 144 128 }, 145 129 }; 146 130 },
+2 -2
app/api/rpc/[command]/route.ts
··· 3 3 import { createClient } from "@supabase/supabase-js"; 4 4 import { Database } from "supabase/database.types"; 5 5 import { pull } from "./pull"; 6 - import { getFactsFromHomeLeaflets } from "./getFactsFromHomeLeaflets"; 6 + import { getFactsForRoots } from "./getFactsForRoots"; 7 7 import { Vercel } from "@vercel/sdk"; 8 8 import { 9 9 get_domain_status, ··· 41 41 let Routes = [ 42 42 push, 43 43 pull, 44 - getFactsFromHomeLeaflets, 44 + getFactsForRoots, 45 45 get_domain_status, 46 46 get_leaflet_subdomain_status, 47 47 get_leaflet_data,
+1 -1
app/api/rpc/[command]/search_publication_documents.ts
··· 2 2 import { z } from "zod"; 3 3 import { makeRoute } from "../lib"; 4 4 import type { Env } from "./route"; 5 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 5 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 6 6 import { normalizeDocumentRecord } from "src/utils/normalizeRecords"; 7 7 8 8 export type SearchPublicationDocumentsReturnType = Awaited<
+1 -1
app/api/rpc/[command]/search_publication_names.ts
··· 1 1 import { z } from "zod"; 2 2 import { makeRoute } from "../lib"; 3 3 import type { Env } from "./route"; 4 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 4 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 5 5 import { deduplicateByUri } from "src/utils/deduplicateRecords"; 6 6 7 7 export type SearchPublicationNamesReturnType = Awaited<
app/checkout/pro/ProCheckoutLogin.tsx app/(app)/checkout/pro/ProCheckoutLogin.tsx
app/checkout/pro/ProCheckoutTrigger.tsx app/(app)/checkout/pro/ProCheckoutTrigger.tsx
app/checkout/pro/page.tsx app/(app)/checkout/pro/page.tsx
+2 -24
app/layout.tsx
··· 5 5 import "./globals.css"; 6 6 import localFont from "next/font/local"; 7 7 import { PopUpProvider } from "components/Toast"; 8 - import { IdentityProviderServer } from "components/IdentityProviderServer"; 9 - import { headers } from "next/headers"; 10 - import { RequestHeadersProvider } from "components/Providers/RequestHeadersProvider"; 11 - import { RouteUIStateManager } from "components/RouteUIStateManger"; 12 - import { SubscriptionSuccessModal } from "components/SubscriptionSuccessModal"; 13 - import { Suspense } from "react"; 14 8 15 9 export const metadata = { 16 10 title: "Leaflet", ··· 53 47 variable: "--font-quattro", 54 48 }); 55 49 56 - export default async function RootLayout({ 50 + export default function RootLayout({ 57 51 children, 58 52 }: { 59 53 children: React.ReactNode; 60 54 }) { 61 - let headersList = await headers(); 62 - let ipLocation = headersList.get("X-Vercel-IP-Country"); 63 - let acceptLanguage = headersList.get("accept-language"); 64 - let ipTimezone = headersList.get("X-Vercel-IP-Timezone"); 65 55 return ( 66 56 <html suppressHydrationWarning lang="en" className={`${quattro.variable}`}> 67 57 <body> ··· 82 72 <ServiceWorker /> 83 73 <InitialPageLoad> 84 74 <PopUpProvider> 85 - <IdentityProviderServer> 86 - <RequestHeadersProvider 87 - country={ipLocation} 88 - language={acceptLanguage} 89 - timezone={ipTimezone} 90 - > 91 - <ViewportSizeLayout>{children}</ViewportSizeLayout> 92 - <Suspense> 93 - <SubscriptionSuccessModal /> 94 - </Suspense> 95 - <RouteUIStateManager /> 96 - </RequestHeadersProvider> 97 - </IdentityProviderServer> 75 + <ViewportSizeLayout>{children}</ViewportSizeLayout> 98 76 </PopUpProvider> 99 77 </InitialPageLoad> 100 78 </body>
+4 -1
app/legal/page.tsx
··· 1 + import { Suspense } from "react"; 1 2 import { LegalContent } from "./content"; 2 3 3 4 export default function Legal() { 4 5 return ( 5 6 <div className="w-full min-h-full h-fit bg-bg-leaflet"> 6 - <LegalContent /> 7 + <Suspense> 8 + <LegalContent /> 9 + </Suspense> 7 10 </div> 8 11 ); 9 12 }
app/lish/[did]/[publication]/.well-known/site.standard.publication/route.ts app/(app)/lish/[did]/[publication]/.well-known/site.standard.publication/route.ts
app/lish/[did]/[publication]/LocalizedDate.tsx app/(app)/lish/[did]/[publication]/LocalizedDate.tsx
app/lish/[did]/[publication]/PublicationAuthor.tsx app/(app)/lish/[did]/[publication]/PublicationAuthor.tsx
+1 -1
app/lish/[did]/[publication]/PublicationContent.tsx app/(app)/lish/[did]/[publication]/PublicationContent.tsx
··· 12 12 import { FontLoader } from "components/FontLoader"; 13 13 import { SpeedyLink } from "components/SpeedyLink"; 14 14 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 15 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 15 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 16 16 import { SubscribeInput } from "components/Subscribe/SubscribeButton"; 17 17 import { 18 18 PublicationPostsList,
app/lish/[did]/[publication]/PublicationFullHeader.tsx app/(app)/lish/[did]/[publication]/PublicationFullHeader.tsx
app/lish/[did]/[publication]/PublicationHeader.tsx app/(app)/lish/[did]/[publication]/PublicationHeader.tsx
app/lish/[did]/[publication]/PublicationHomeLayout.tsx app/(app)/lish/[did]/[publication]/PublicationHomeLayout.tsx
app/lish/[did]/[publication]/PublicationNav.tsx app/(app)/lish/[did]/[publication]/PublicationNav.tsx
app/lish/[did]/[publication]/PublicationPostItem.tsx app/(app)/lish/[did]/[publication]/PublicationPostItem.tsx
+1 -1
app/lish/[did]/[publication]/PublicationPostsList.tsx app/(app)/lish/[did]/[publication]/PublicationPostsList.tsx
··· 1 1 import React from "react"; 2 2 import { AtUri } from "@atproto/api"; 3 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 3 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 4 4 import { InteractionPreview } from "components/InteractionsPreview"; 5 5 import { LocalizedDate } from "./LocalizedDate"; 6 6 import { PublicationPostItem } from "./PublicationContent";
app/lish/[did]/[publication]/PublicationStickyHeader.tsx app/(app)/lish/[did]/[publication]/PublicationStickyHeader.tsx
app/lish/[did]/[publication]/UpgradeModal.tsx app/(app)/lish/[did]/[publication]/UpgradeModal.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/BaseTextBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/PubCodeBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/PubCodeBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/PublishBskyPostBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/PublishBskyPostBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/PublishedPageBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/PublishedPageBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/PublishedPollBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/PublishedPollBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/StaticMathBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/StaticMathBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/TextBlock.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/TextBlock.tsx
app/lish/[did]/[publication]/[rkey]/Blocks/TextBlockCore.tsx app/(app)/lish/[did]/[publication]/[rkey]/Blocks/TextBlockCore.tsx
app/lish/[did]/[publication]/[rkey]/BlueskyQuotesPage.tsx app/(app)/lish/[did]/[publication]/[rkey]/BlueskyQuotesPage.tsx
app/lish/[did]/[publication]/[rkey]/BskyPostContent.tsx app/(app)/lish/[did]/[publication]/[rkey]/BskyPostContent.tsx
app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx app/(app)/lish/[did]/[publication]/[rkey]/CanvasPage.tsx
+1 -1
app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer.tsx app/(app)/lish/[did]/[publication]/[rkey]/DocumentPageRenderer.tsx
··· 18 18 import { FontLoader } from "components/FontLoader"; 19 19 import { mergePreferences } from "src/utils/mergePreferences"; 20 20 import { PublicationNav } from "../PublicationNav"; 21 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 21 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 22 22 23 23 export async function DocumentPageRenderer({ 24 24 did,
app/lish/[did]/[publication]/[rkey]/Footnotes/FootnoteCollectorContext.tsx app/(app)/lish/[did]/[publication]/[rkey]/Footnotes/FootnoteCollectorContext.tsx
app/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnotePopover.tsx app/(app)/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnotePopover.tsx
app/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnoteSideColumn.tsx app/(app)/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnoteSideColumn.tsx
app/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnotes.tsx app/(app)/lish/[did]/[publication]/[rkey]/Footnotes/PublishedFootnotes.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox.tsx app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/CommentBox.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/commentAction.ts
app/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Comments/index.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer.tsx app/(app)/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx app/(app)/lish/[did]/[publication]/[rkey]/Interactions/Quotes.tsx
app/lish/[did]/[publication]/[rkey]/Interactions/recommendAction.ts app/(app)/lish/[did]/[publication]/[rkey]/Interactions/recommendAction.ts
app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx app/(app)/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx
app/lish/[did]/[publication]/[rkey]/PostContent.tsx app/(app)/lish/[did]/[publication]/[rkey]/PostContent.tsx
+2 -2
app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx app/(app)/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx
··· 1 1 "use client"; 2 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 2 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 3 3 import { 4 4 Interactions, 5 5 getQuoteCount, ··· 11 11 import { EditTiny } from "components/Icons/EditTiny"; 12 12 import { SpeedyLink } from "components/SpeedyLink"; 13 13 import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 14 - import Post from "app/p/[didOrHandle]/[rkey]/l-quote/[quote]/page"; 14 + import Post from "app/(app)/p/[didOrHandle]/[rkey]/l-quote/[quote]/page"; 15 15 import { Separator } from "components/Layout"; 16 16 import { ProfilePopover } from "components/ProfilePopover"; 17 17
app/lish/[did]/[publication]/[rkey]/PostLinks.tsx app/(app)/lish/[did]/[publication]/[rkey]/PostLinks.tsx
app/lish/[did]/[publication]/[rkey]/PostPages.tsx app/(app)/lish/[did]/[publication]/[rkey]/PostPages.tsx
+1 -1
app/lish/[did]/[publication]/[rkey]/PostPrevNextButtons.tsx app/(app)/lish/[did]/[publication]/[rkey]/PostPrevNextButtons.tsx
··· 1 1 "use client"; 2 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 2 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 3 3 import { AtUri } from "@atproto/api"; 4 4 import { useDocument } from "contexts/DocumentContext"; 5 5 import { SpeedyLink } from "components/SpeedyLink";
+1 -1
app/lish/[did]/[publication]/[rkey]/PublicationPageRenderer.tsx app/(app)/lish/[did]/[publication]/[rkey]/PublicationPageRenderer.tsx
··· 27 27 import { PublicationHomeLayout } from "../PublicationHomeLayout"; 28 28 import { PublicationStickyHeader } from "../PublicationStickyHeader"; 29 29 import { PublicationFullHeader } from "../PublicationFullHeader"; 30 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 30 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 31 31 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 32 32 33 33 import { collectAndFetchBlockResources } from "./collectAndFetchBlockResources";
app/lish/[did]/[publication]/[rkey]/QuoteHandler.tsx app/(app)/lish/[did]/[publication]/[rkey]/QuoteHandler.tsx
app/lish/[did]/[publication]/[rkey]/StaticPostContent.tsx app/(app)/lish/[did]/[publication]/[rkey]/StaticPostContent.tsx
+1 -1
app/lish/[did]/[publication]/[rkey]/ThreadPage.tsx app/(app)/lish/[did]/[publication]/[rkey]/ThreadPage.tsx
··· 25 25 prefetchThread, 26 26 } from "./PostLinks"; 27 27 import { useDocument } from "contexts/DocumentContext"; 28 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 28 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 29 29 import { QuoteContent } from "./Interactions/Quotes"; 30 30 import { 31 31 decodeQuotePosition,
app/lish/[did]/[publication]/[rkey]/collectAndFetchBlockResources.ts app/(app)/lish/[did]/[publication]/[rkey]/collectAndFetchBlockResources.ts
app/lish/[did]/[publication]/[rkey]/extractBlocksByType.ts app/(app)/lish/[did]/[publication]/[rkey]/extractBlocksByType.ts
app/lish/[did]/[publication]/[rkey]/extractCodeBlocks.ts app/(app)/lish/[did]/[publication]/[rkey]/extractCodeBlocks.ts
app/lish/[did]/[publication]/[rkey]/fetchPollData.ts app/(app)/lish/[did]/[publication]/[rkey]/fetchPollData.ts
+1 -1
app/lish/[did]/[publication]/[rkey]/getPostPageData.ts app/(app)/lish/[did]/[publication]/[rkey]/getPostPageData.ts
··· 6 6 } from "src/utils/normalizeRecords"; 7 7 import { PubLeafletPublication, SiteStandardPublication } from "lexicons/api"; 8 8 import { documentUriFilter } from "src/utils/uriHelpers"; 9 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 9 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 10 10 11 11 export async function getPostPageData(did: string, rkey: string) { 12 12 let { data: documents } = await supabaseServerClient
+1 -1
app/lish/[did]/[publication]/[rkey]/getVoterIdentities.ts app/(app)/lish/[did]/[publication]/[rkey]/getVoterIdentities.ts
··· 1 1 "use server"; 2 2 3 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 3 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 4 4 5 5 export type VoterIdentity = { 6 6 did: string;
app/lish/[did]/[publication]/[rkey]/l-quote/[quote]/opengraph-image.ts app/(app)/lish/[did]/[publication]/[rkey]/l-quote/[quote]/opengraph-image.ts
-10
app/lish/[did]/[publication]/[rkey]/l-quote/[quote]/page.tsx
··· 1 - import PostPage, { 2 - generateMetadata, 3 - } from "app/lish/[did]/[publication]/[rkey]/page"; 4 - 5 - export { generateMetadata } from "app/lish/[did]/[publication]/[rkey]/page"; 6 - export default async function Post(props: { 7 - params: Promise<{ publication: string; did: string; rkey: string }>; 8 - }) { 9 - return <PostPage {...props} />; 10 - }
app/lish/[did]/[publication]/[rkey]/opengraph-image.ts app/(app)/lish/[did]/[publication]/[rkey]/opengraph-image.ts
app/lish/[did]/[publication]/[rkey]/page.tsx app/(app)/lish/[did]/[publication]/[rkey]/page.tsx
app/lish/[did]/[publication]/[rkey]/postPageState.ts app/(app)/lish/[did]/[publication]/[rkey]/postPageState.ts
app/lish/[did]/[publication]/[rkey]/quotePosition.ts app/(app)/lish/[did]/[publication]/[rkey]/quotePosition.ts
app/lish/[did]/[publication]/[rkey]/useHighlight.tsx app/(app)/lish/[did]/[publication]/[rkey]/useHighlight.tsx
app/lish/[did]/[publication]/[rkey]/voteOnPublishedPoll.ts app/(app)/lish/[did]/[publication]/[rkey]/voteOnPublishedPoll.ts
app/lish/[did]/[publication]/atom/route.ts app/(app)/lish/[did]/[publication]/atom/route.ts
+1 -1
app/lish/[did]/[publication]/dashboard/Actions.tsx app/(app)/lish/[did]/[publication]/dashboard/Actions.tsx
··· 4 4 import { ActionButton } from "components/ActionBar/ActionButton"; 5 5 import { ShareSmall } from "components/Icons/ShareSmall"; 6 6 import { Menu, MenuItem } from "components/Menu"; 7 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 7 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 8 8 import { usePublicationData } from "./PublicationSWRProvider"; 9 9 import { useSmoker } from "components/Toast"; 10 10 import { useIsMobile } from "src/hooks/isMobile";
+2 -3
app/lish/[did]/[publication]/dashboard/DraftList.tsx app/(app)/lish/[did]/[publication]/dashboard/DraftList.tsx
··· 5 5 usePublicationData, 6 6 useNormalizedPublicationRecord, 7 7 } from "./PublicationSWRProvider"; 8 - import { LeafletList } from "app/(home-pages)/(writer)/home/HomeLayout"; 8 + import { LeafletList } from "app/(app)/(home-pages)/(writer)/home/HomeLayout"; 9 9 10 10 export function DraftList(props: { 11 11 searchValue: string; ··· 15 15 // Normalize the publication record - skip rendering if unrecognized format 16 16 const normalizedPubRecord = useNormalizedPublicationRecord(); 17 17 if (!pub_data?.publication) return null; 18 - const { drafts, leaflet_data } = pub_data; 18 + const { drafts } = pub_data; 19 19 const { leaflets_in_publications, ...publication } = pub_data.publication; 20 20 21 21 if (!normalizedPubRecord) return null; ··· 41 41 ], 42 42 }, 43 43 }))} 44 - initialFacts={leaflet_data.facts || {}} 45 44 titles={{ 46 45 ...drafts.reduce( 47 46 (acc, draft) => {
app/lish/[did]/[publication]/dashboard/DraftsTab.tsx app/(app)/lish/[did]/[publication]/dashboard/DraftsTab.tsx
app/lish/[did]/[publication]/dashboard/EditPagesNavLink.tsx app/(app)/lish/[did]/[publication]/dashboard/EditPagesNavLink.tsx
app/lish/[did]/[publication]/dashboard/NewDraftButton.tsx app/(app)/lish/[did]/[publication]/dashboard/NewDraftButton.tsx
app/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider.tsx
+1 -1
app/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx app/(app)/lish/[did]/[publication]/dashboard/PublicationSubscribers.tsx
··· 1 1 "use client"; 2 2 import { usePublicationData } from "./PublicationSWRProvider"; 3 3 import { ButtonPrimary } from "components/Buttons"; 4 - import { getPublicationURL } from "app/lish/createPub/getPublicationURL"; 4 + import { getPublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 5 5 import { useSmoker } from "components/Toast"; 6 6 import { Menu, MenuItem } from "components/Menu"; 7 7 import { Separator } from "components/Layout";
+2 -2
app/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx app/(app)/lish/[did]/[publication]/dashboard/PublishedPostsLists.tsx
··· 13 13 import { 14 14 getPublicationURL, 15 15 getDocumentURL, 16 - } from "app/lish/createPub/getPublicationURL"; 16 + } from "app/(app)/lish/createPub/getPublicationURL"; 17 17 import { SpeedyLink } from "components/SpeedyLink"; 18 18 import { InteractionPreview } from "components/InteractionsPreview"; 19 19 import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 20 - import { LeafletOptions } from "app/(home-pages)/(writer)/home/LeafletList/LeafletOptions"; 20 + import { LeafletOptions } from "app/(app)/(home-pages)/(writer)/home/LeafletList/LeafletOptions"; 21 21 import { StaticLeafletDataContext } from "components/PageSWRDataProvider"; 22 22 23 23 export function PublishedPostsList(props: {
app/lish/[did]/[publication]/dashboard/analytics/ChartTooltip.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/ChartTooltip.tsx
app/lish/[did]/[publication]/dashboard/analytics/PublicationAnalytics.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/PublicationAnalytics.tsx
app/lish/[did]/[publication]/dashboard/analytics/Skeletons.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/Skeletons.tsx
app/lish/[did]/[publication]/dashboard/analytics/SubscribersChart.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/SubscribersChart.tsx
app/lish/[did]/[publication]/dashboard/analytics/TrafficChart.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/TrafficChart.tsx
app/lish/[did]/[publication]/dashboard/analytics/dates.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/dates.tsx
app/lish/[did]/[publication]/dashboard/analytics/page.tsx app/(app)/lish/[did]/[publication]/dashboard/analytics/page.tsx
app/lish/[did]/[publication]/dashboard/deleteDraft.ts app/(app)/lish/[did]/[publication]/dashboard/deleteDraft.ts
app/lish/[did]/[publication]/dashboard/deletePost.ts app/(app)/lish/[did]/[publication]/dashboard/deletePost.ts
app/lish/[did]/[publication]/dashboard/layout.tsx app/(app)/lish/[did]/[publication]/dashboard/layout.tsx
app/lish/[did]/[publication]/dashboard/page.tsx app/(app)/lish/[did]/[publication]/dashboard/page.tsx
app/lish/[did]/[publication]/dashboard/posts/page.tsx app/(app)/lish/[did]/[publication]/dashboard/posts/page.tsx
app/lish/[did]/[publication]/dashboard/settings/GeneralSettings.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/GeneralSettings.tsx
app/lish/[did]/[publication]/dashboard/settings/PostSettings.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/PostSettings.tsx
app/lish/[did]/[publication]/dashboard/settings/ProSettings.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/ProSettings.tsx
+1 -1
app/lish/[did]/[publication]/dashboard/settings/PubDomainSettings.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/PubDomainSettings.tsx
··· 16 16 usePublicationData, 17 17 useNormalizedPublicationRecord, 18 18 } from "../PublicationSWRProvider"; 19 - import { updatePublicationBasePath } from "app/lish/createPub/updatePublication"; 19 + import { updatePublicationBasePath } from "app/(app)/lish/createPub/updatePublication"; 20 20 import { 21 21 assignDomainToPublication, 22 22 removeDomainAssignment,
+1 -1
app/lish/[did]/[publication]/dashboard/settings/SettingsContent.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/SettingsContent.tsx
··· 9 9 usePublicationData, 10 10 useNormalizedPublicationRecord, 11 11 } from "../PublicationSWRProvider"; 12 - import { updatePublication } from "app/lish/createPub/updatePublication"; 12 + import { updatePublication } from "app/(app)/lish/createPub/updatePublication"; 13 13 import { PubDomainSettings } from "./PubDomainSettings"; 14 14 import { GeneralSettings } from "./GeneralSettings"; 15 15 import { PostSettings } from "./PostSettings";
app/lish/[did]/[publication]/dashboard/settings/ThemeSettings.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/ThemeSettings.tsx
app/lish/[did]/[publication]/dashboard/settings/deletePublication.ts app/(app)/lish/[did]/[publication]/dashboard/settings/deletePublication.ts
app/lish/[did]/[publication]/dashboard/settings/page.tsx app/(app)/lish/[did]/[publication]/dashboard/settings/page.tsx
app/lish/[did]/[publication]/dashboard/subs/page.tsx app/(app)/lish/[did]/[publication]/dashboard/subs/page.tsx
app/lish/[did]/[publication]/edit/[[...route]]/PublicationEditHeader.tsx app/(app)/lish/[did]/[publication]/edit/[[...route]]/PublicationEditHeader.tsx
app/lish/[did]/[publication]/edit/[[...route]]/PublicationPageLeaflet.tsx app/(app)/lish/[did]/[publication]/edit/[[...route]]/PublicationPageLeaflet.tsx
app/lish/[did]/[publication]/edit/[[...route]]/PublicationPagesNav.tsx app/(app)/lish/[did]/[publication]/edit/[[...route]]/PublicationPagesNav.tsx
app/lish/[did]/[publication]/edit/[[...route]]/layout.tsx app/(app)/lish/[did]/[publication]/edit/[[...route]]/layout.tsx
app/lish/[did]/[publication]/edit/[[...route]]/page.tsx app/(app)/lish/[did]/[publication]/edit/[[...route]]/page.tsx
+1 -1
app/lish/[did]/[publication]/generateFeed.ts app/(app)/lish/[did]/[publication]/generateFeed.ts
··· 11 11 hasLeafletContent, 12 12 } from "src/utils/normalizeRecords"; 13 13 import { publicationNameOrUriFilter } from "src/utils/uriHelpers"; 14 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 14 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 15 15 16 16 export async function generateFeed( 17 17 did: string,
app/lish/[did]/[publication]/icon/route.ts app/(app)/lish/[did]/[publication]/icon/route.ts
app/lish/[did]/[publication]/json/route.ts app/(app)/lish/[did]/[publication]/json/route.ts
app/lish/[did]/[publication]/layout.tsx app/(app)/lish/[did]/[publication]/layout.tsx
app/lish/[did]/[publication]/opengraph-image.ts app/(app)/lish/[did]/[publication]/opengraph-image.ts
+1 -1
app/lish/[did]/[publication]/page.tsx app/(app)/lish/[did]/[publication]/page.tsx
··· 2 2 import { 3 3 getPublicationURL, 4 4 getDocumentURL, 5 - } from "app/lish/createPub/getPublicationURL"; 5 + } from "app/(app)/lish/createPub/getPublicationURL"; 6 6 import { BskyAgent } from "@atproto/api"; 7 7 import { publicationNameOrUriFilter } from "src/utils/uriHelpers"; 8 8 import React from "react";
app/lish/[did]/[publication]/rss/route.ts app/(app)/lish/[did]/[publication]/rss/route.ts
app/lish/[did]/[publication]/sortPublicationPages.ts app/(app)/lish/[did]/[publication]/sortPublicationPages.ts
app/lish/[did]/[publication]/subscribeSuccess/page.tsx app/(app)/lish/[did]/[publication]/subscribeSuccess/page.tsx
+2 -2
app/lish/[did]/[publication]/theme-settings/PostPreview.tsx app/(app)/lish/[did]/[publication]/theme-settings/PostPreview.tsx
··· 1 1 "use client"; 2 2 3 - import { LinearDocumentPage } from "app/lish/[did]/[publication]/[rkey]/LinearDocumentPage"; 3 + import { LinearDocumentPage } from "app/(app)/lish/[did]/[publication]/[rkey]/LinearDocumentPage"; 4 4 import { LeafletContentProvider } from "contexts/LeafletContentContext"; 5 5 import { 6 6 DocumentProvider, ··· 9 9 import { useIdentityData } from "components/IdentityProvider"; 10 10 import type { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 11 11 import type { PubLeafletPagesLinearDocument } from "lexicons/api"; 12 - import type { PostPageData } from "app/lish/[did]/[publication]/[rkey]/getPostPageData"; 12 + import type { PostPageData } from "app/(app)/lish/[did]/[publication]/[rkey]/getPostPageData"; 13 13 import type { DocumentContextValue } from "contexts/DocumentContext"; 14 14 import { 15 15 usePublicationData,
+1 -1
app/lish/[did]/[publication]/theme-settings/PubPreview.tsx app/(app)/lish/[did]/[publication]/theme-settings/PubPreview.tsx
··· 3 3 import { 4 4 usePublicationData, 5 5 useNormalizedPublicationRecord, 6 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 6 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 7 7 import { useIdentityData } from "components/IdentityProvider"; 8 8 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs"; 9 9 import { PublicationContent } from "../PublicationContent";
app/lish/[did]/[publication]/theme-settings/ThemeSettingsContent.tsx app/(app)/lish/[did]/[publication]/theme-settings/ThemeSettingsContent.tsx
app/lish/[did]/[publication]/theme-settings/page.tsx app/(app)/lish/[did]/[publication]/theme-settings/page.tsx
app/lish/[did]/[publication]/theme-settings/postPreviewFakeBlocks.ts app/(app)/lish/[did]/[publication]/theme-settings/postPreviewFakeBlocks.ts
app/lish/[did]/[publication]/tryRenderPublicationPage.tsx app/(app)/lish/[did]/[publication]/tryRenderPublicationPage.tsx
app/lish/addFeed.tsx app/(app)/lish/addFeed.tsx
app/lish/createPub/CreatePubForm.tsx app/(app)/lish/createPub/CreatePubForm.tsx
app/lish/createPub/UpdatePubForm.tsx app/(app)/lish/createPub/UpdatePubForm.tsx
app/lish/createPub/createPublication.ts app/(app)/lish/createPub/createPublication.ts
app/lish/createPub/getPublicationURL.ts app/(app)/lish/createPub/getPublicationURL.ts
app/lish/createPub/page.tsx app/(app)/lish/createPub/page.tsx
app/lish/createPub/updatePublication.ts app/(app)/lish/createPub/updatePublication.ts
app/lish/feeds/[...path]/route.ts app/(app)/lish/feeds/[...path]/route.ts
app/lish/sorry-boris/fixFeeds.ts app/(app)/lish/sorry-boris/fixFeeds.ts
app/lish/sorry-boris/fixFeedsButton.tsx app/(app)/lish/sorry-boris/fixFeedsButton.tsx
app/lish/sorry-boris/page.tsx app/(app)/lish/sorry-boris/page.tsx
app/lish/subscribeToPublication.ts app/(app)/lish/subscribeToPublication.ts
app/lish/uri/[uri]/route.ts app/(app)/lish/uri/[uri]/route.ts
app/lish/url/[url]/route.tsx app/(app)/lish/url/[url]/route.tsx
app/merge-accounts/page.tsx app/(app)/merge-accounts/page.tsx
app/new/route.ts app/(app)/new/route.ts
+1 -1
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/opengraph-image.ts app/(app)/p/[didOrHandle]/[rkey]/l-quote/[quote]/opengraph-image.ts
··· 1 1 import { getMicroLinkOgImage } from "src/utils/getMicroLinkOgImage"; 2 - import { decodeQuotePosition } from "app/lish/[did]/[publication]/[rkey]/quotePosition"; 2 + import { decodeQuotePosition } from "app/(app)/lish/[did]/[publication]/[rkey]/quotePosition"; 3 3 4 4 export const runtime = "edge"; 5 5 export const revalidate = 60;
-8
app/p/[didOrHandle]/[rkey]/l-quote/[quote]/page.tsx
··· 1 - import PostPage from "app/p/[didOrHandle]/[rkey]/page"; 2 - 3 - export { generateMetadata } from "app/p/[didOrHandle]/[rkey]/page"; 4 - export default async function Post(props: { 5 - params: Promise<{ didOrHandle: string; rkey: string }>; 6 - }) { 7 - return <PostPage {...props} />; 8 - }
+1 -1
app/p/[didOrHandle]/[rkey]/opengraph-image.ts app/(app)/p/[didOrHandle]/[rkey]/opengraph-image.ts
··· 1 1 import { getMicroLinkOgImage } from "src/utils/getMicroLinkOgImage"; 2 2 import { supabaseServerClient } from "supabase/serverClient"; 3 3 import { jsonToLex } from "@atproto/lexicon"; 4 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 4 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 5 5 import { fetchAtprotoBlob } from "app/api/atproto_images/route"; 6 6 import { normalizeDocumentRecord } from "src/utils/normalizeRecords"; 7 7 import { documentUriFilter } from "src/utils/uriHelpers";
+2 -2
app/p/[didOrHandle]/[rkey]/page.tsx app/(app)/p/[didOrHandle]/[rkey]/page.tsx
··· 1 1 import { supabaseServerClient } from "supabase/serverClient"; 2 2 import { Metadata } from "next"; 3 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 4 - import { DocumentPageRenderer } from "app/lish/[did]/[publication]/[rkey]/DocumentPageRenderer"; 3 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 4 + import { DocumentPageRenderer } from "app/(app)/lish/[did]/[publication]/[rkey]/DocumentPageRenderer"; 5 5 import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 6 6 import { normalizeDocumentRecord } from "src/utils/normalizeRecords"; 7 7 import { documentUriFilter } from "src/utils/uriHelpers";
+14
app/page.tsx
··· 1 + import type { Metadata } from "next"; 2 + import { AboutPage } from "./about/AboutPage"; 3 + 4 + export const metadata: Metadata = { 5 + title: "Leaflet — social publishing on the Atmosphere", 6 + description: 7 + "A simple and powerful platform for social publishing — blogs, newsletters, and more!", 8 + }; 9 + 10 + export const revalidate = 3600; 11 + 12 + export default function RootPage() { 13 + return <AboutPage />; 14 + }
+2 -2
components/ActionBar/ProfileButton.tsx
··· 10 10 import { SpeedyLink } from "components/SpeedyLink"; 11 11 import { Popover } from "components/Popover"; 12 12 import { Modal } from "components/Modal"; 13 - import { InlineUpgradeToPro } from "app/lish/[did]/[publication]/UpgradeModal"; 14 - import { ManageProSubscription } from "app/lish/[did]/[publication]/dashboard/settings/ProSettings"; 13 + import { InlineUpgradeToPro } from "app/(app)/lish/[did]/[publication]/UpgradeModal"; 14 + import { ManageProSubscription } from "app/(app)/lish/[did]/[publication]/dashboard/settings/ProSettings"; 15 15 import { ManageDomains } from "components/Domains/ManageDomains"; 16 16 import { WebSmall } from "components/Icons/WebSmall"; 17 17 import { useIsPro, useCanSeePro } from "src/hooks/useEntitlement";
+2 -2
components/ActionBar/Publications.tsx
··· 3 3 4 4 import { useIdentityData } from "components/IdentityProvider"; 5 5 import { theme } from "tailwind.config"; 6 - import { getBasePublicationURL } from "app/lish/createPub/getPublicationURL"; 6 + import { getBasePublicationURL } from "app/(app)/lish/createPub/getPublicationURL"; 7 7 import { Json } from "supabase/database.types"; 8 8 import { AtUri } from "@atproto/syntax"; 9 9 import { ActionButton } from "./ActionButton"; ··· 16 16 import { LooseLeafSmall } from "components/Icons/LooseleafSmall"; 17 17 import { LoginModal } from "components/LoginButton"; 18 18 import useSWR from "swr"; 19 - import { getHomeDocs } from "app/(home-pages)/(writer)/home/storage"; 19 + import { getHomeDocs } from "app/(app)/(home-pages)/(writer)/home/storage"; 20 20 21 21 export const PublicationButtons = (props: { className?: string }) => { 22 22 let { identity } = useIdentityData();
+1 -1
components/Blocks/BlueskyPostBlock/BlueskyEmbed.tsx
··· 14 14 import { 15 15 OpenPage, 16 16 openPage, 17 - } from "app/lish/[did]/[publication]/[rkey]/PostPages"; 17 + } from "app/(app)/lish/[did]/[publication]/[rkey]/PostPages"; 18 18 import { BlueskyVideoPlayer } from "./BlueskyVideoPlayer"; 19 19 20 20 export const BlueskyEmbed = (props: {
+1 -1
components/Blocks/BlueskyPostBlock/index.tsx
··· 9 9 import { PostNotAvailable } from "./BlueskyEmbed"; 10 10 import { BlueskyPostEmpty } from "./BlueskyEmpty"; 11 11 12 - import { BskyPostContent } from "app/lish/[did]/[publication]/[rkey]/BskyPostContent"; 12 + import { BskyPostContent } from "app/(app)/lish/[did]/[publication]/[rkey]/BskyPostContent"; 13 13 import { PostView } from "@atproto/api/dist/client/types/app/bsky/feed/defs"; 14 14 15 15 export const BlueskyPostBlock = (props: BlockProps & { preview?: boolean }) => {
+2 -2
components/Blocks/PostsListBlock.tsx
··· 5 5 import { 6 6 usePublicationData, 7 7 useNormalizedPublicationRecord, 8 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 9 - import { PublicationPostsList } from "app/lish/[did]/[publication]/PublicationPostsList"; 8 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 9 + import { PublicationPostsList } from "app/(app)/lish/[did]/[publication]/PublicationPostsList"; 10 10 import { Popover } from "components/Popover"; 11 11 import { Toggle } from "components/Toggle"; 12 12 import { ToggleGroup } from "components/ToggleGroup";
+3 -3
components/Blocks/StandardSitePostBlock/StandardSitePostItem.tsx
··· 5 5 PublicationPostItemSmall, 6 6 PublicationPostItemMedium, 7 7 PublicationPostItemLarge, 8 - } from "app/lish/[did]/[publication]/PublicationPostItem"; 9 - import { LocalizedDate } from "app/lish/[did]/[publication]/LocalizedDate"; 8 + } from "app/(app)/lish/[did]/[publication]/PublicationPostItem"; 9 + import { LocalizedDate } from "app/(app)/lish/[did]/[publication]/LocalizedDate"; 10 10 import { 11 11 getDocumentURL, 12 12 getPublicationURL, 13 - } from "app/lish/createPub/getPublicationURL"; 13 + } from "app/(app)/lish/createPub/getPublicationURL"; 14 14 import { getFirstParagraph } from "src/utils/getFirstParagraph"; 15 15 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 16 16 import { useStandardSitePost } from "components/StandardSitePostDataProvider";
+1 -1
components/Blocks/TextBlock/index.tsx
··· 30 30 import { getAspectRatio } from "src/utils/aspectRatio"; 31 31 32 32 import { Mention, MentionAutocomplete } from "components/Mention"; 33 - import { addMentionToEditor } from "app/[leaflet_id]/publish/BskyPostEditorProsemirror"; 33 + import { addMentionToEditor } from "app/(app)/[leaflet_id]/publish/BskyPostEditorProsemirror"; 34 34 import { v7 } from "uuid"; 35 35 import { generateKeyBetween } from "fractional-indexing"; 36 36
+1 -1
components/DesktopFooter.tsx
··· 5 5 import { FootnoteToolbar } from "./Toolbar/FootnoteToolbarWrapper"; 6 6 import { useEntitySetContext } from "./EntitySetProvider"; 7 7 import { focusBlock } from "src/utils/focusBlock"; 8 - import { hasBlockToolbar } from "app/[leaflet_id]/Footer"; 8 + import { hasBlockToolbar } from "app/(app)/[leaflet_id]/Footer"; 9 9 import { useEntity } from "src/replicache"; 10 10 11 11 export function DesktopPageFooter(props: { pageID: string }) {
+2 -2
components/Domains/PublicationDomains.tsx
··· 11 11 import { 12 12 usePublicationData, 13 13 useNormalizedPublicationRecord, 14 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 15 - import { updatePublicationBasePath } from "app/lish/createPub/updatePublication"; 14 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 15 + import { updatePublicationBasePath } from "app/(app)/lish/createPub/updatePublication"; 16 16 import { 17 17 assignDomainToPublication, 18 18 removeDomainAssignment,
+5 -2
components/LoginButton.tsx
··· 19 19 confirmEmailAuthToken, 20 20 } from "actions/emailAuth"; 21 21 import { loginWithEmailToken } from "actions/login"; 22 - import { getHomeDocs } from "app/(home-pages)/(writer)/home/storage"; 22 + import { getHomeDocs } from "app/(app)/(home-pages)/(writer)/home/storage"; 23 23 import { mutate } from "swr"; 24 24 25 25 export const LoginModal = (props: { ··· 116 116 return; 117 117 } 118 118 const localLeaflets = getHomeDocs(); 119 - await loginWithEmailToken(localLeaflets.filter((l) => !l.hidden)); 119 + await loginWithEmailToken( 120 + localLeaflets.filter((l) => !l.hidden), 121 + props.redirectRoute, 122 + ); 120 123 mutate("identity"); 121 124 toaster({ 122 125 content: <div className="font-bold">Logged in! Welcome!</div>,
+2 -2
components/PageLayouts/NotificationContent.tsx
··· 1 1 "use client"; 2 2 import { useEffect, useState } from "react"; 3 3 import { DashboardPageLayout } from "./DashboardPageLayout"; 4 - import { NotificationList } from "app/(home-pages)/(writer)/notifications/NotificationList"; 5 - import { getNotifications } from "app/(home-pages)/(writer)/notifications/getNotifications"; 4 + import { NotificationList } from "app/(app)/(home-pages)/(writer)/notifications/NotificationList"; 5 + import { getNotifications } from "app/(app)/(home-pages)/(writer)/notifications/getNotifications"; 6 6 import { HydratedNotification } from "src/notifications"; 7 7 8 8 export function NotificationContent() {
+1 -1
components/PageSWRDataProvider.tsx
··· 10 10 import { 11 11 getPublicationURL, 12 12 getDocumentURL, 13 - } from "app/lish/createPub/getPublicationURL"; 13 + } from "app/(app)/lish/createPub/getPublicationURL"; 14 14 import { AtUri } from "@atproto/syntax"; 15 15 import { 16 16 normalizeDocumentRecord,
+1 -1
components/Pages/Page.tsx
··· 16 16 import { focusPage } from "src/utils/focusPage"; 17 17 import { PageOptions } from "./PageOptions"; 18 18 import { CardThemeProvider } from "components/ThemeManager/ThemeProvider"; 19 - import { useDrawerOpen } from "app/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer"; 19 + import { useDrawerOpen } from "app/(app)/lish/[did]/[publication]/[rkey]/Interactions/InteractionDrawer"; 20 20 import { usePreserveScroll } from "src/hooks/usePreserveScroll"; 21 21 import { usePageFootnotes } from "components/Footnotes/usePageFootnotes"; 22 22 import { FootnoteContext } from "components/Footnotes/FootnoteContext";
+1 -1
components/Pages/PageShareMenu.tsx
··· 2 2 import { 3 3 ShareButton, 4 4 useReadOnlyShareLink, 5 - } from "app/[leaflet_id]/actions/ShareOptions"; 5 + } from "app/(app)/[leaflet_id]/actions/ShareOptions"; 6 6 import { useEffect, useState } from "react"; 7 7 8 8 export const PageShareMenu = (props: { entityID: string }) => {
+2 -2
components/Pages/PublicationMetadata.tsx
··· 8 8 import { 9 9 getBasePublicationURL, 10 10 getPublicationURL, 11 - } from "app/lish/createPub/getPublicationURL"; 11 + } from "app/(app)/lish/createPub/getPublicationURL"; 12 12 import { useSubscribe } from "src/replicache/useSubscribe"; 13 13 import { useEntitySetContext } from "components/EntitySetProvider"; 14 14 import { timeAgo } from "src/utils/timeAgo"; ··· 18 18 import { Popover } from "components/Popover"; 19 19 import { TagSelector } from "components/Tags"; 20 20 import { useIdentityData } from "components/IdentityProvider"; 21 - import { PostHeaderLayout } from "app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader"; 21 + import { PostHeaderLayout } from "app/(app)/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader"; 22 22 import { Backdater } from "./Backdater"; 23 23 import { RecommendTinyEmpty } from "components/Icons/RecommendTiny"; 24 24 import { mergePreferences } from "src/utils/mergePreferences";
+1 -1
components/Pages/index.tsx
··· 8 8 9 9 import { useCardBorderHidden } from "./useCardBorderHidden"; 10 10 import { BookendSpacer, SandwichSpacer } from "components/LeafletLayout"; 11 - import { LeafletSidebar } from "app/[leaflet_id]/Sidebar"; 11 + import { LeafletSidebar } from "app/(app)/[leaflet_id]/Sidebar"; 12 12 import { Page } from "./Page"; 13 13 import { IframePageView } from "./IframePageView"; 14 14 import { PageOptionButton } from "./PageOptions";
+2 -2
components/PostListing.tsx
··· 9 9 NormalizedPublication, 10 10 } from "src/utils/normalizeRecords"; 11 11 import { hasLeafletContent } from "lexicons/src/normalize"; 12 - import type { Post } from "app/(home-pages)/reader/getReaderFeed"; 12 + import type { Post } from "app/(app)/(home-pages)/reader/getReaderFeed"; 13 13 14 14 import Link from "next/link"; 15 15 import { useEffect, useRef, useState } from "react"; ··· 22 22 import { useSelectedPostListing } from "src/useSelectedPostState"; 23 23 import { mergePreferences } from "src/utils/mergePreferences"; 24 24 import { ExternalLinkTiny } from "./Icons/ExternalLinkTiny"; 25 - import { getDocumentURL } from "app/lish/createPub/getPublicationURL"; 25 + import { getDocumentURL } from "app/(app)/lish/createPub/getPublicationURL"; 26 26 import { RecommendButton } from "./RecommendButton"; 27 27 import { getFirstParagraph } from "src/utils/getFirstParagraph"; 28 28
+1 -1
components/ProfilePopover.tsx
··· 3 3 import useSWR from "swr"; 4 4 import { callRPC } from "app/api/rpc/client"; 5 5 import { useRef, useState } from "react"; 6 - import { ProfileHeader } from "app/(home-pages)/p/[didOrHandle]/ProfileHeader"; 6 + import { ProfileHeader } from "app/(app)/(home-pages)/p/[didOrHandle]/ProfileHeader"; 7 7 import { SpeedyLink } from "./SpeedyLink"; 8 8 import { Tooltip } from "./Tooltip"; 9 9 import { ProfileViewDetailed } from "@atproto/api/dist/client/types/app/bsky/actor/defs";
+1 -1
components/RecommendButton.tsx
··· 7 7 import { 8 8 recommendAction, 9 9 unrecommendAction, 10 - } from "app/lish/[did]/[publication]/[rkey]/Interactions/recommendAction"; 10 + } from "app/(app)/lish/[did]/[publication]/[rkey]/Interactions/recommendAction"; 11 11 import { callRPC } from "app/api/rpc/client"; 12 12 import { useSmoker, useToaster } from "./Toast"; 13 13 import { OAuthErrorMessage, isOAuthSessionError } from "./OAuthError";
+1 -1
components/Subscribe/HandleSubscribe.tsx
··· 4 4 import Link from "next/link"; 5 5 import { useState } from "react"; 6 6 import { encodeActionToSearchParam } from "app/api/oauth/[route]/afterSignInActions"; 7 - import { subscribeToPublication } from "app/lish/subscribeToPublication"; 7 + import { subscribeToPublication } from "app/(app)/lish/subscribeToPublication"; 8 8 import { isOAuthSessionError, OAuthErrorMessage } from "components/OAuthError"; 9 9 import { useToaster } from "components/Toast"; 10 10 import { DotLoader } from "components/utils/DotLoader";
+2 -2
components/ThemeManager/PubThemeSetter.tsx
··· 1 1 import { 2 2 usePublicationData, 3 3 useNormalizedPublicationRecord, 4 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 4 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 5 5 import { useState } from "react"; 6 6 import { pickers, SectionArrow } from "./ThemeSetter"; 7 7 import { PubLeafletThemeBackgroundImage } from "lexicons/api"; ··· 9 9 import { useLocalPubTheme } from "./PublicationThemeProvider"; 10 10 import { BaseThemeProvider, CardBorderHiddenContext } from "./ThemeProvider"; 11 11 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 12 - import { updatePublicationTheme } from "app/lish/createPub/updatePublication"; 12 + import { updatePublicationTheme } from "app/(app)/lish/createPub/updatePublication"; 13 13 import { PagePickers } from "./PubPickers/PubTextPickers"; 14 14 import { BackgroundPicker } from "./PubPickers/PubBackgroundPickers"; 15 15 import { PubAccentPickers } from "./PubPickers/PubAcccentPickers";
+1 -1
components/ThemeManager/PublicationThemeProvider.tsx
··· 8 8 import { 9 9 usePublicationData, 10 10 useNormalizedPublicationRecord, 11 - } from "app/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 11 + } from "app/(app)/lish/[did]/[publication]/dashboard/PublicationSWRProvider"; 12 12 import { blobRefToSrc } from "src/utils/blobRefToSrc"; 13 13 import { PubThemeDefaults } from "./themeDefaults"; 14 14
+17 -1
components/utils/UpdateLeafletTitle.tsx
··· 1 1 "use client"; 2 2 3 - import { useEffect, useState } from "react"; 3 + import { useEffect, useRef, useState } from "react"; 4 4 import { 5 5 useBlocks, 6 6 useCanvasBlocksWithType, ··· 13 13 import { focusBlock } from "src/utils/focusBlock"; 14 14 import { useIsMobile } from "src/hooks/isMobile"; 15 15 import { useLeafletPublicationData } from "components/PageSWRDataProvider"; 16 + import { useDebouncedEffect } from "src/hooks/useDebouncedEffect"; 16 17 17 18 export function UpdateLeafletTitle(props: { entityID: string }) { 18 19 let { data: pub } = useLeafletPublicationData(); ··· 24 25 ); 25 26 let firstBlock = blocks[0]; 26 27 let title = usePageTitle(entityID); 28 + let { rep, permission_token } = useReplicache(); 29 + let hasWrite = permission_token?.permission_token_rights?.some( 30 + (r) => r.write, 31 + ); 32 + let lastSentTitleRef = useRef<string | null>(null); 33 + useDebouncedEffect( 34 + () => { 35 + if (!rep || !hasWrite || !title) return; 36 + if (lastSentTitleRef.current === title) return; 37 + lastSentTitleRef.current = title; 38 + rep.mutate.updateLeafletMetadata({ title }); 39 + }, 40 + 500, 41 + [rep, hasWrite, title], 42 + ); 27 43 useEffect(() => { 28 44 if (pub?.title) { 29 45 document.title = pub.title;
+1 -1
contexts/DocumentContext.tsx
··· 1 1 "use client"; 2 2 import { createContext, useContext } from "react"; 3 - import type { PostPageData } from "app/lish/[did]/[publication]/[rkey]/getPostPageData"; 3 + import type { PostPageData } from "app/(app)/lish/[did]/[publication]/[rkey]/getPostPageData"; 4 4 5 5 // Derive types from PostPageData 6 6 type NonNullPostPageData = NonNullable<PostPageData>;
+19
middleware.ts
··· 47 47 if (!ua) return false; 48 48 return botUserAgentRegex.test(ua); 49 49 } 50 + function isMainSiteHost(hostname: string): boolean { 51 + return ( 52 + hostname === "leaflet.pub" || 53 + hostname.startsWith("localhost") || 54 + hostname.startsWith("127.0.0.1") || 55 + hostname.endsWith(".vercel.app") 56 + ); 57 + } 58 + 50 59 export default async function middleware(req: NextRequest) { 51 60 let hostname = req.headers.get("host")!; 52 61 if (req.nextUrl.pathname === auth_callback_route) return authCallback(req); 53 62 if (req.nextUrl.pathname === receive_auth_callback_route) 54 63 return receiveAuthCallback(req); 64 + 65 + if (req.nextUrl.pathname === "/" && isMainSiteHost(hostname)) { 66 + let hasAuth = 67 + req.cookies.has("auth_token") || req.cookies.has("external_auth_token"); 68 + if (hasAuth) { 69 + let navState = req.cookies.get("nav-state")?.value; 70 + let target = navState === "reader" ? "/reader" : "/home"; 71 + return NextResponse.redirect(new URL(target, req.url)); 72 + } 73 + } 55 74 56 75 if (hostname === "leaflet.pub") return; 57 76 if (req.nextUrl.pathname === "/not-found") return;
+1 -1
src/mergeIdentity.ts
··· 11 11 } from "drizzle/schema"; 12 12 import { pool } from "supabase/pool"; 13 13 import { Err, Ok, type Result } from "src/result"; 14 - import { backfillAtprotoSubscriptionsForIdentity } from "app/lish/subscribeToPublication"; 14 + import { backfillAtprotoSubscriptionsForIdentity } from "app/(app)/lish/subscribeToPublication"; 15 15 16 16 export type MergeError = 17 17 | "merge_not_pending"
+1 -1
src/notifications.ts
··· 3 3 import { supabaseServerClient } from "supabase/serverClient"; 4 4 import { Tables, TablesInsert } from "supabase/database.types"; 5 5 import { AtUri } from "@atproto/syntax"; 6 - import { idResolver } from "app/(home-pages)/reader/idResolver"; 6 + import { idResolver } from "app/(app)/(home-pages)/reader/idResolver"; 7 7 import { 8 8 normalizeDocumentRecord, 9 9 normalizePublicationRecord,
+27
src/replicache/mutations.ts
··· 720 720 }); 721 721 }; 722 722 723 + const updateLeafletMetadata: Mutation<{ 724 + title?: string; 725 + description?: string | null; 726 + }> = async (args, ctx) => { 727 + await ctx.runOnServer(async (serverCtx) => { 728 + const updates: { title?: string; description?: string | null } = {}; 729 + if (args.title !== undefined) updates.title = args.title; 730 + if (args.description !== undefined) updates.description = args.description; 731 + if (Object.keys(updates).length === 0) return; 732 + 733 + const { data: writerToken } = await serverCtx.supabase 734 + .from("permission_tokens") 735 + .select("root_entity, permission_token_rights(write)") 736 + .eq("id", ctx.permission_token_id) 737 + .single(); 738 + if (!writerToken) return; 739 + const hasWrite = writerToken.permission_token_rights.some((r) => r.write); 740 + if (!hasWrite) return; 741 + 742 + await serverCtx.supabase 743 + .from("permission_tokens") 744 + .update(updates) 745 + .eq("root_entity", writerToken.root_entity); 746 + }); 747 + }; 748 + 723 749 const createFootnote: Mutation<{ 724 750 footnoteEntityID: string; 725 751 blockID: string; ··· 774 800 addPollOption, 775 801 removePollOption, 776 802 updatePublicationDraft, 803 + updateLeafletMetadata, 777 804 createFootnote, 778 805 deleteFootnote, 779 806 };
+53
src/utils/extractTitleFromFacts.ts
··· 1 + import * as base64 from "base64-js"; 2 + import { applyUpdate, Doc } from "yjs"; 3 + import { YJSFragmentToString } from "src/utils/yjsFragmentToString"; 4 + import type { Fact } from "src/replicache"; 5 + import type { Attribute } from "src/replicache/attributes"; 6 + import { scanIndexLocal } from "src/replicache/utils"; 7 + 8 + export function extractTitleFromFacts( 9 + rootToken: string, 10 + facts: Fact<Attribute>[], 11 + ): string { 12 + const scan = scanIndexLocal(facts); 13 + const [root] = scan.eav(rootToken, "root/page"); 14 + const rootEntity = root?.data.value || rootToken; 15 + 16 + const [pageType] = scan.eav(rootEntity, "page/type"); 17 + const isCanvas = pageType?.data.value === "canvas"; 18 + 19 + const rawBlocks = isCanvas 20 + ? scan.eav(rootEntity, "canvas/block").sort((a, b) => { 21 + if (a.data.position.y === b.data.position.y) 22 + return a.data.position.x - b.data.position.x; 23 + return a.data.position.y - b.data.position.y; 24 + }) 25 + : scan.eav(rootEntity, "card/block").sort((a, b) => { 26 + if (a.data.position === b.data.position) 27 + return a.id > b.id ? 1 : -1; 28 + return a.data.position > b.data.position ? 1 : -1; 29 + }); 30 + 31 + const blocks = rawBlocks 32 + .map((b) => { 33 + const type = scan.eav(b.data.value, "block/type")[0]; 34 + if ( 35 + !type || 36 + (type.data.value !== "text" && type.data.value !== "heading") 37 + ) 38 + return null; 39 + return b.data; 40 + }) 41 + .filter((b): b is NonNullable<typeof b> => b !== null); 42 + 43 + const firstBlock = blocks[0]; 44 + if (!firstBlock) return "Untitled"; 45 + 46 + const [content] = scan.eav(firstBlock.value, "block/text"); 47 + if (!content) return "Untitled"; 48 + 49 + const doc = new Doc(); 50 + applyUpdate(doc, base64.toByteArray(content.data.value)); 51 + const nodes = doc.getXmlElement("prosemirror").toArray(); 52 + return YJSFragmentToString(nodes[0]) || "Untitled"; 53 + }
+6
supabase/database.types.ts
··· 893 893 permission_tokens: { 894 894 Row: { 895 895 blocked_by_admin: boolean | null 896 + description: string | null 896 897 id: string 897 898 root_entity: string 899 + title: string | null 898 900 } 899 901 Insert: { 900 902 blocked_by_admin?: boolean | null 903 + description?: string | null 901 904 id?: string 902 905 root_entity: string 906 + title?: string | null 903 907 } 904 908 Update: { 905 909 blocked_by_admin?: boolean | null 910 + description?: string | null 906 911 id?: string 907 912 root_entity?: string 913 + title?: string | null 908 914 } 909 915 Relationships: [ 910 916 {