a tool for shared writing and social publishing

Refactor/shared home layout (#230)

* group home pages with shared layout

* tweak discover button styles

* make discover dynamic

* add lazy loading for pub icon and bg images

authored by awarm.space and committed by GitHub f32014ca bac31849

+6 -3
actions/getIdentityData.ts
··· 2 2 3 3 import { cookies } from "next/headers"; 4 4 import { supabaseServerClient } from "supabase/serverClient"; 5 - 6 - export async function getIdentityData() { 5 + import { cache } from "react"; 6 + export const getIdentityData = cache(uncachedGetIdentityData); 7 + export async function uncachedGetIdentityData() { 7 8 let cookieStore = await cookies(); 8 9 let auth_token = 9 10 cookieStore.get("auth_token")?.value || ··· 18 19 bsky_profiles(*), 19 20 publication_subscriptions(*), 20 21 custom_domains!custom_domains_identity_id_fkey(publication_domains(*), *), 21 - home_leaflet:permission_tokens!identities_home_page_fkey(*, permission_token_rights(*)), 22 + home_leaflet:permission_tokens!identities_home_page_fkey(*, permission_token_rights(*, 23 + entity_sets(entities(facts(*))) 24 + )), 22 25 permission_token_on_homepage( 23 26 created_at, 24 27 permission_tokens!inner(
+43
app/(home-pages)/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 { HomeLayout } 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 + <HomeLayout 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 + if (title) acc[tok.permission_tokens.root_entity] = title; 34 + return acc; 35 + }, 36 + {} as { [k: string]: string }, 37 + ), 38 + }} 39 + entityID={auth_res?.home_leaflet?.root_entity || null} 40 + initialFacts={home_docs_initialFacts.facts || {}} 41 + /> 42 + ); 43 + }
+38
app/(home-pages)/layout.tsx
··· 1 + import { getIdentityData } from "actions/getIdentityData"; 2 + import { EntitySetProvider } from "components/EntitySetProvider"; 3 + import { 4 + ThemeProvider, 5 + ThemeBackgroundProvider, 6 + } from "components/ThemeManager/ThemeProvider"; 7 + import { ReplicacheProvider, type Fact } from "src/replicache"; 8 + 9 + export default async function HomePagesLayout(props: { 10 + children: React.ReactNode; 11 + }) { 12 + let identityData = await getIdentityData(); 13 + if (!identityData?.home_leaflet) return <>{props.children}</>; 14 + let facts = 15 + (identityData?.home_leaflet?.permission_token_rights[0].entity_sets?.entities.flatMap( 16 + (e) => e.facts, 17 + ) || []) as Fact<any>[]; 18 + 19 + let root_entity = identityData.home_leaflet.root_entity; 20 + return ( 21 + <ReplicacheProvider 22 + rootEntity={identityData.home_leaflet.root_entity} 23 + token={identityData.home_leaflet} 24 + name={identityData.home_leaflet.root_entity} 25 + initialFacts={facts} 26 + > 27 + <EntitySetProvider 28 + set={identityData.home_leaflet.permission_token_rights[0].entity_set} 29 + > 30 + <ThemeProvider entityID={root_entity}> 31 + <ThemeBackgroundProvider entityID={root_entity}> 32 + {props.children} 33 + </ThemeBackgroundProvider> 34 + </ThemeProvider> 35 + </EntitySetProvider> 36 + </ReplicacheProvider> 37 + ); 38 + }
+3
app/(home-pages)/notifications/page.tsx
··· 1 + export default async function Notifications() { 2 + return <div>Notifications</div>; 3 + }
+38
app/(home-pages)/reader/page.tsx
··· 1 + import { getIdentityData } from "actions/getIdentityData"; 2 + 3 + import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 4 + import { ReaderContent } from "./ReaderContent"; 5 + import { SubscriptionsContent } from "./SubscriptionsContent"; 6 + import { getReaderFeed } from "./getReaderFeed"; 7 + import { getSubscriptions } from "./getSubscriptions"; 8 + 9 + export default async function Reader(props: {}) { 10 + let posts = await getReaderFeed(); 11 + let publications = await getSubscriptions(); 12 + return ( 13 + <DashboardLayout 14 + id="reader" 15 + cardBorderHidden={false} 16 + currentPage="reader" 17 + defaultTab="Read" 18 + actions={null} 19 + tabs={{ 20 + Read: { 21 + controls: null, 22 + content: ( 23 + <ReaderContent nextCursor={posts.nextCursor} posts={posts.posts} /> 24 + ), 25 + }, 26 + Subscriptions: { 27 + controls: null, 28 + content: ( 29 + <SubscriptionsContent 30 + publications={publications.subscriptions} 31 + nextCursor={publications.nextCursor} 32 + /> 33 + ), 34 + }, 35 + }} 36 + /> 37 + ); 38 + }
+19 -8
app/discover/PubListing.tsx app/(home-pages)/discover/PubListing.tsx
··· 1 1 "use client"; 2 2 import { AtUri } from "@atproto/syntax"; 3 - import { PublicationSubscription } from "app/reader/getSubscriptions"; 3 + import { PublicationSubscription } from "app/(home-pages)/reader/getSubscriptions"; 4 4 import { PubIcon } from "components/ActionBar/Publications"; 5 5 import { Separator } from "components/Layout"; 6 6 import { usePubTheme } from "components/ThemeManager/PublicationThemeProvider"; ··· 31 31 <BaseThemeProvider {...theme} local> 32 32 <a 33 33 href={`https://${record.base_path}`} 34 - style={{ 35 - backgroundImage: `url(${backgroundImage})`, 36 - backgroundRepeat: backgroundImageRepeat ? "repeat" : "no-repeat", 37 - backgroundSize: `${backgroundImageRepeat ? `${backgroundImageSize}px` : "cover"}`, 38 - }} 39 34 className={`no-underline! flex flex-row gap-2 40 35 bg-bg-leaflet 41 36 border border-border-light rounded-lg 42 37 px-3 py-3 selected-outline 43 - hover:outline-accent-contrast hover:border-accent-contrast`} 38 + hover:outline-accent-contrast hover:border-accent-contrast 39 + relative overflow-hidden`} 44 40 > 41 + {backgroundImage && ( 42 + <img 43 + src={backgroundImage} 44 + alt="" 45 + loading="lazy" 46 + fetchPriority="low" 47 + className="absolute inset-0 pointer-events-none" 48 + style={{ 49 + width: backgroundImageRepeat ? `${backgroundImageSize}px` : "100%", 50 + height: backgroundImageRepeat ? "auto" : "100%", 51 + objectFit: backgroundImageRepeat ? "none" : "cover", 52 + objectPosition: "center", 53 + }} 54 + /> 55 + )} 45 56 <div 46 - className={`flex w-full flex-col justify-center text-center max-h-48 pt-4 pb-3 px-3 rounded-lg ${props.resizeHeight ? "" : "sm:h-48 h-full"} ${record.theme?.showPageBackground ? "bg-[rgba(var(--bg-page),var(--bg-page-alpha))] " : ""}`} 57 + className={`flex w-full flex-col justify-center text-center max-h-48 pt-4 pb-3 px-3 rounded-lg relative z-10 ${props.resizeHeight ? "" : "sm:h-48 h-full"} ${record.theme?.showPageBackground ? "bg-[rgba(var(--bg-page),var(--bg-page-alpha))] " : ""}`} 47 58 > 48 59 <div className="mx-auto pb-1"> 49 60 <PubIcon record={record} uri={props.uri} large />
app/discover/SortButtons.tsx app/(home-pages)/discover/SortButtons.tsx
+1 -6
app/discover/SortedPublicationList.tsx app/(home-pages)/discover/SortedPublicationList.tsx
··· 81 81 <div className="relative"> 82 82 <button 83 83 onClick={props.onClick} 84 - style={ 85 - props.selected 86 - ? { backgroundColor: `rgba(var(--accent-1), 0.2)` } 87 - : {} 88 - } 89 - className={`text-sm rounded-md px-[8px] py-0.5 border ${props.selected ? "border-accent-contrast text-accent-1 font-bold" : "text-tertiary border-border-light"}`} 84 + className={`text-sm bg-accent-1 text-accent-2 rounded-md px-[8px] py-0.5 border ${props.selected ? "border-accent-contrast font-bold" : "border-border-light"}`} 90 85 > 91 86 {props.children} 92 87 </button>
+13 -19
app/discover/page.tsx app/(home-pages)/discover/page.tsx
··· 4 4 import { Metadata } from "next"; 5 5 import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 6 6 7 - export const dynamic = "force-static"; 8 - export const revalidate = 60; 9 - 10 7 export type PublicationsList = Awaited<ReturnType<typeof getPublications>>; 11 8 async function getPublications() { 12 9 let { data: publications, error } = await supabaseServerClient ··· 34 31 searchParams: Promise<{ [key: string]: string | string[] | undefined }>; 35 32 }) { 36 33 let order = ((await props.searchParams).order as string) || "recentlyUpdated"; 37 - let publications = await getPublications(); 38 34 39 35 return ( 40 - <div className="w-full h-full mx-auto bg-[#FDFCFA]"> 41 - <DashboardLayout 42 - id="discover" 43 - cardBorderHidden={false} 44 - currentPage="discover" 45 - defaultTab="default" 46 - actions={null} 47 - tabs={{ 48 - default: { 49 - controls: null, 50 - content: <DiscoverContent order={order} />, 51 - }, 52 - }} 53 - /> 54 - </div> 36 + <DashboardLayout 37 + id="discover" 38 + cardBorderHidden={false} 39 + currentPage="discover" 40 + defaultTab="default" 41 + actions={null} 42 + tabs={{ 43 + default: { 44 + controls: null, 45 + content: <DiscoverContent order={order} />, 46 + }, 47 + }} 48 + /> 55 49 ); 56 50 } 57 51
app/home/Actions/AccountSettings.tsx app/(home-pages)/home/Actions/AccountSettings.tsx
app/home/Actions/Actions.tsx app/(home-pages)/home/Actions/Actions.tsx
app/home/Actions/CreateNewButton.tsx app/(home-pages)/home/Actions/CreateNewButton.tsx
app/home/Actions/HomeHelp.tsx app/(home-pages)/home/Actions/HomeHelp.tsx
app/home/HomeEmpty/DiscoverIllo.tsx app/(home-pages)/home/HomeEmpty/DiscoverIllo.tsx
app/home/HomeEmpty/HomeEmpty.tsx app/(home-pages)/home/HomeEmpty/HomeEmpty.tsx
app/home/HomeEmpty/WelcomeToLeafletIllo.tsx app/(home-pages)/home/HomeEmpty/WelcomeToLeafletIllo.tsx
+1 -1
app/home/HomeLayout.tsx app/(home-pages)/home/HomeLayout.tsx
··· 63 63 }; 64 64 65 65 export const HomeLayout = (props: { 66 - entityID: string; 66 + entityID: string | null; 67 67 titles: { [root_entity: string]: string }; 68 68 initialFacts: { 69 69 [root_entity: string]: Fact<Attribute>[];
app/home/IdentitySetter.tsx app/(home-pages)/home/IdentitySetter.tsx
app/home/LeafletList/LeafletContent.tsx app/(home-pages)/home/LeafletList/LeafletContent.tsx
app/home/LeafletList/LeafletInfo.tsx app/(home-pages)/home/LeafletList/LeafletInfo.tsx
app/home/LeafletList/LeafletListItem.tsx app/(home-pages)/home/LeafletList/LeafletListItem.tsx
app/home/LeafletList/LeafletOptions.tsx app/(home-pages)/home/LeafletList/LeafletOptions.tsx
app/home/LeafletList/LeafletPreview.module.css app/(home-pages)/home/LeafletList/LeafletPreview.module.css
app/home/LeafletList/LeafletPreview.tsx app/(home-pages)/home/LeafletList/LeafletPreview.tsx
app/home/LoggedOutWarning.tsx app/(home-pages)/home/LoggedOutWarning.tsx
+1 -1
app/home/icon.tsx app/(home-pages)/home/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 import { cookies } from "next/headers";
-124
app/home/page.tsx
··· 1 - import { cookies } from "next/headers"; 2 - import { Fact, ReplicacheProvider, useEntity } from "src/replicache"; 3 - import type { Attribute } from "src/replicache/attributes"; 4 - import { 5 - ThemeBackgroundProvider, 6 - ThemeProvider, 7 - } from "components/ThemeManager/ThemeProvider"; 8 - import { EntitySetProvider } from "components/EntitySetProvider"; 9 - import { createIdentity } from "actions/createIdentity"; 10 - import { drizzle } from "drizzle-orm/node-postgres"; 11 - import { IdentitySetter } from "./IdentitySetter"; 12 - 13 - import { getIdentityData } from "actions/getIdentityData"; 14 - import { getFactsFromHomeLeaflets } from "app/api/rpc/[command]/getFactsFromHomeLeaflets"; 15 - import { supabaseServerClient } from "supabase/serverClient"; 16 - import { pool } from "supabase/pool"; 17 - 18 - import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 19 - import { HomeLayout } from "./HomeLayout"; 20 - 21 - export default async function Home() { 22 - let cookieStore = await cookies(); 23 - let auth_res = await getIdentityData(); 24 - let identity: string | undefined; 25 - if (auth_res) identity = auth_res.id; 26 - else identity = cookieStore.get("identity")?.value; 27 - let needstosetcookie = false; 28 - if (!identity) { 29 - const client = await pool.connect(); 30 - const db = drizzle(client); 31 - let newIdentity = await createIdentity(db); 32 - client.release(); 33 - identity = newIdentity.id; 34 - needstosetcookie = true; 35 - } 36 - 37 - async function setCookie() { 38 - "use server"; 39 - 40 - (await cookies()).set("identity", identity as string, { 41 - sameSite: "strict", 42 - }); 43 - } 44 - 45 - let permission_token = auth_res?.home_leaflet; 46 - if (!permission_token) { 47 - let res = await supabaseServerClient 48 - .from("identities") 49 - .select( 50 - `*, 51 - permission_tokens!identities_home_page_fkey(*, permission_token_rights(*)) 52 - `, 53 - ) 54 - .eq("id", identity) 55 - .single(); 56 - permission_token = res.data?.permission_tokens; 57 - } 58 - 59 - if (!permission_token) 60 - return ( 61 - <NotFoundLayout> 62 - <p className="font-bold">Sorry, we can't find this home!</p> 63 - <p> 64 - This may be a glitch on our end. If the issue persists please{" "} 65 - <a href="mailto:contact@leaflet.pub">send us a note</a>. 66 - </p> 67 - </NotFoundLayout> 68 - ); 69 - let [homeLeafletFacts, allLeafletFacts] = await Promise.all([ 70 - supabaseServerClient.rpc("get_facts", { 71 - root: permission_token.root_entity, 72 - }), 73 - auth_res 74 - ? getFactsFromHomeLeaflets.handler( 75 - { 76 - tokens: auth_res.permission_token_on_homepage.map( 77 - (r) => r.permission_tokens.root_entity, 78 - ), 79 - }, 80 - { supabase: supabaseServerClient }, 81 - ) 82 - : undefined, 83 - ]); 84 - let initialFacts = 85 - (homeLeafletFacts.data as unknown as Fact<Attribute>[]) || []; 86 - 87 - let root_entity = permission_token.root_entity; 88 - let home_docs_initialFacts = allLeafletFacts?.result || {}; 89 - 90 - return ( 91 - <ReplicacheProvider 92 - rootEntity={root_entity} 93 - token={permission_token} 94 - name={root_entity} 95 - initialFacts={initialFacts} 96 - > 97 - <IdentitySetter cb={setCookie} call={needstosetcookie} /> 98 - <EntitySetProvider 99 - set={permission_token.permission_token_rights[0].entity_set} 100 - > 101 - <ThemeProvider entityID={root_entity}> 102 - <ThemeBackgroundProvider entityID={root_entity}> 103 - <HomeLayout 104 - titles={{ 105 - ...home_docs_initialFacts.titles, 106 - ...auth_res?.permission_token_on_homepage.reduce( 107 - (acc, tok) => { 108 - let title = 109 - tok.permission_tokens.leaflets_in_publications[0]?.title; 110 - if (title) acc[tok.permission_tokens.root_entity] = title; 111 - return acc; 112 - }, 113 - {} as { [k: string]: string }, 114 - ), 115 - }} 116 - entityID={root_entity} 117 - initialFacts={home_docs_initialFacts.facts || {}} 118 - /> 119 - </ThemeBackgroundProvider> 120 - </ThemeProvider> 121 - </EntitySetProvider> 122 - </ReplicacheProvider> 123 - ); 124 - }
app/home/storage.ts app/(home-pages)/home/storage.ts
+1 -1
app/lish/[did]/[publication]/dashboard/DraftList.tsx
··· 3 3 import { NewDraftSecondaryButton } from "./NewDraftButton"; 4 4 import React from "react"; 5 5 import { usePublicationData } from "./PublicationSWRProvider"; 6 - import { LeafletList } from "app/home/HomeLayout"; 6 + import { LeafletList } from "app/(home-pages)/home/HomeLayout"; 7 7 8 8 export function DraftList(props: { 9 9 searchValue: string;
+1 -1
app/login/LoginForm.tsx
··· 5 5 } from "actions/emailAuth"; 6 6 import { loginWithEmailToken } from "actions/login"; 7 7 import { ActionAfterSignIn } from "app/api/oauth/[route]/afterSignInActions"; 8 - import { getHomeDocs } from "app/home/storage"; 8 + import { getHomeDocs } from "app/(home-pages)/home/storage"; 9 9 import { ButtonPrimary } from "components/Buttons"; 10 10 import { ArrowRightTiny } from "components/Icons/ArrowRightTiny"; 11 11 import { BlueskySmall } from "components/Icons/BlueskySmall";
+1 -2
app/reader/ReaderContent.tsx app/(home-pages)/reader/ReaderContent.tsx
··· 23 23 import { useLocalizedDate } from "src/hooks/useLocalizedDate"; 24 24 25 25 export const ReaderContent = (props: { 26 - root_entity: string; 27 26 posts: Post[]; 28 27 nextCursor: Cursor | null; 29 28 }) => { ··· 205 204 year: "numeric", 206 205 month: "short", 207 206 day: "numeric", 208 - } 207 + }, 209 208 ); 210 209 211 210 return (
+1 -1
app/reader/SubscriptionsContent.tsx app/(home-pages)/reader/SubscriptionsContent.tsx
··· 1 1 "use client"; 2 - import { PubListing } from "app/discover/PubListing"; 2 + import { PubListing } from "app/(home-pages)/discover/PubListing"; 3 3 import { ButtonPrimary } from "components/Buttons"; 4 4 import { DiscoverSmall } from "components/Icons/DiscoverSmall"; 5 5 import { Json } from "supabase/database.types";
app/reader/getReaderFeed.ts app/(home-pages)/reader/getReaderFeed.ts
app/reader/getSubscriptions.ts app/(home-pages)/reader/getSubscriptions.ts
app/reader/idResolver.ts app/(home-pages)/reader/idResolver.ts
-104
app/reader/page.tsx
··· 1 - import { cookies } from "next/headers"; 2 - import { Fact, ReplicacheProvider } from "src/replicache"; 3 - import type { Attribute } from "src/replicache/attributes"; 4 - import { 5 - ThemeBackgroundProvider, 6 - ThemeProvider, 7 - } from "components/ThemeManager/ThemeProvider"; 8 - import { EntitySetProvider } from "components/EntitySetProvider"; 9 - import { getIdentityData } from "actions/getIdentityData"; 10 - import { supabaseServerClient } from "supabase/serverClient"; 11 - 12 - import { NotFoundLayout } from "components/PageLayouts/NotFoundLayout"; 13 - import { DashboardLayout } from "components/PageLayouts/DashboardLayout"; 14 - import { ReaderContent, ReaderEmpty } from "./ReaderContent"; 15 - import { 16 - SubscriptionsContent, 17 - SubscriptionsEmpty, 18 - } from "./SubscriptionsContent"; 19 - import { getReaderFeed } from "./getReaderFeed"; 20 - import { getSubscriptions } from "./getSubscriptions"; 21 - 22 - export default async function Reader(props: {}) { 23 - let cookieStore = await cookies(); 24 - let auth_res = await getIdentityData(); 25 - let identity: string | undefined; 26 - let permission_token = auth_res?.home_leaflet; 27 - if (!permission_token) 28 - return ( 29 - <DashboardLayout 30 - id="reader" 31 - cardBorderHidden={false} 32 - currentPage="reader" 33 - defaultTab="Read" 34 - actions={null} 35 - tabs={{ 36 - Read: { 37 - controls: null, 38 - content: <ReaderEmpty />, 39 - }, 40 - Subscriptions: { 41 - controls: null, 42 - content: <SubscriptionsEmpty />, 43 - }, 44 - }} 45 - /> 46 - ); 47 - let [homeLeafletFacts] = await Promise.all([ 48 - supabaseServerClient.rpc("get_facts", { 49 - root: permission_token.root_entity, 50 - }), 51 - ]); 52 - let initialFacts = 53 - (homeLeafletFacts.data as unknown as Fact<Attribute>[]) || []; 54 - let root_entity = permission_token.root_entity; 55 - 56 - if (!auth_res?.atp_did) return; 57 - let posts = await getReaderFeed(); 58 - let publications = await getSubscriptions(); 59 - return ( 60 - <ReplicacheProvider 61 - rootEntity={root_entity} 62 - token={permission_token} 63 - name={root_entity} 64 - initialFacts={initialFacts} 65 - > 66 - <EntitySetProvider 67 - set={permission_token.permission_token_rights[0].entity_set} 68 - > 69 - <ThemeProvider entityID={root_entity}> 70 - <ThemeBackgroundProvider entityID={root_entity}> 71 - <DashboardLayout 72 - id="reader" 73 - cardBorderHidden={false} 74 - currentPage="reader" 75 - defaultTab="Read" 76 - actions={null} 77 - tabs={{ 78 - Read: { 79 - controls: null, 80 - content: ( 81 - <ReaderContent 82 - root_entity={root_entity} 83 - nextCursor={posts.nextCursor} 84 - posts={posts.posts} 85 - /> 86 - ), 87 - }, 88 - Subscriptions: { 89 - controls: null, 90 - content: ( 91 - <SubscriptionsContent 92 - publications={publications.subscriptions} 93 - nextCursor={publications.nextCursor} 94 - /> 95 - ), 96 - }, 97 - }} 98 - /> 99 - </ThemeBackgroundProvider> 100 - </ThemeProvider> 101 - </EntitySetProvider> 102 - </ReplicacheProvider> 103 - ); 104 - }
+9 -9
components/ActionBar/Publications.tsx
··· 102 102 let iconSizeClassName = `${props.small ? "w-4 h-4" : props.large ? "w-12 h-12" : "w-6 h-6"} rounded-full`; 103 103 104 104 return props.record.icon ? ( 105 - <div 106 - style={{ 107 - backgroundRepeat: "no-repeat", 108 - backgroundPosition: "center", 109 - backgroundSize: "cover", 110 - backgroundImage: `url(/api/atproto_images?did=${new AtUri(props.uri).host}&cid=${(props.record.icon?.ref as unknown as { $link: string })["$link"]})`, 111 - }} 112 - className={`${iconSizeClassName} ${props.className}`} 113 - /> 105 + <div className={`${iconSizeClassName} ${props.className} relative overflow-hidden`}> 106 + <img 107 + src={`/api/atproto_images?did=${new AtUri(props.uri).host}&cid=${(props.record.icon?.ref as unknown as { $link: string })["$link"]}`} 108 + alt={`${props.record.name} icon`} 109 + loading="lazy" 110 + fetchPriority="low" 111 + className="absolute inset-0 w-full h-full object-cover object-center" 112 + /> 113 + </div> 114 114 ) : ( 115 115 <div className={`${iconSizeClassName} bg-accent-1 relative`}> 116 116 <div
+1 -1
components/Pages/useCardBorderHidden.ts
··· 2 2 import { PubLeafletPublication } from "lexicons/api"; 3 3 import { useEntity, useReplicache } from "src/replicache"; 4 4 5 - export function useCardBorderHidden(entityID: string) { 5 + export function useCardBorderHidden(entityID: string | null) { 6 6 let { rootEntity } = useReplicache(); 7 7 let { data: pub } = useLeafletPublicationData(); 8 8 let rootCardBorderHidden = useEntity(rootEntity, "theme/card-border-hidden");
+1 -1
components/ShareOptions/index.tsx
··· 6 6 import { Menu, MenuItem } from "components/Layout"; 7 7 import { ActionButton } from "components/ActionBar/ActionButton"; 8 8 import useSWR from "swr"; 9 - import { useTemplateState } from "app/home/Actions/CreateNewButton"; 9 + import { useTemplateState } from "app/(home-pages)/home/Actions/CreateNewButton"; 10 10 import LoginForm from "app/login/LoginForm"; 11 11 import { CustomDomainMenu } from "./DomainOptions"; 12 12 import { useIdentityData } from "components/IdentityProvider";
+1 -1
components/ThemeManager/ThemeProvider.tsx
··· 38 38 39 39 // define the color defaults for everything 40 40 export const ThemeDefaults = { 41 - "theme/page-background": "#F0F7FA", 41 + "theme/page-background": "#FDFCFA", 42 42 "theme/card-background": "#FFFFFF", 43 43 "theme/primary": "#272727", 44 44 "theme/highlight-1": "#FFFFFF",
+1 -1
components/utils/AddLeafletToHomepage.tsx
··· 1 1 "use client"; 2 2 3 - import { addDocToHome } from "app/home/storage"; 3 + import { addDocToHome } from "app/(home-pages)/home/storage"; 4 4 import { useIdentityData } from "components/IdentityProvider"; 5 5 import { useEffect } from "react"; 6 6 import { useReplicache } from "src/replicache";