From bd63206790ebe7397ee53d2596234dfd466481c0 Mon Sep 17 00:00:00 2001 From: celine Date: Wed, 28 Jan 2026 19:41:09 -0500 Subject: [PATCH] add user preference to show/hide recommends --- .claude/settings.local.json | 3 +- .../[did]/[publication]/[rkey]/CanvasPage.tsx | 2 + .../[rkey]/Interactions/Interactions.tsx | 30 +- .../[rkey]/LinearDocumentPage.tsx | 1 + .../[rkey]/PostHeader/PostHeader.tsx | 7 +- .../[did]/[publication]/[rkey]/PostPages.tsx | 2 + .../dashboard/PublishedPostsLists.tsx | 1 + .../dashboard/settings/PostOptions.tsx | 22 +- app/lish/[did]/[publication]/page.tsx | 3 + app/lish/createPub/CreatePubForm.tsx | 1 + app/lish/createPub/UpdatePubForm.tsx | 3 +- app/lish/createPub/createPublication.ts | 32 ++- app/lish/createPub/updatePublication.ts | 265 +++++++++++------- components/InteractionsPreview.tsx | 14 +- components/PostListing.tsx | 3 + lexicons/api/types/pub/leaflet/publication.ts | 83 +++--- .../api/types/site/standard/publication.ts | 61 ++-- lexicons/pub/leaflet/publication.json | 4 + lexicons/site/standard/publication.json | 4 + lexicons/src/normalize.ts | 27 +- lexicons/src/publication.ts | 1 + 21 files changed, 363 insertions(+), 206 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 457d562b..9371392c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -8,7 +8,8 @@ "mcp__primitive__pending_delegations", "mcp__primitive__claim_delegation", "mcp__primitive__tasks_update", - "mcp__primitive__contexts_update" + "mcp__primitive__contexts_update", + "mcp__primitive__contexts_list" ] } } diff --git a/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx b/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx index 05c9e1c1..12b30135 100644 --- a/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/CanvasPage.tsx @@ -206,6 +206,7 @@ const CanvasMetadata = (props: { preferences: { showComments?: boolean; showMentions?: boolean; + showRecommends?: boolean; showPrevNext?: boolean; }; quotesCount: number | undefined; @@ -221,6 +222,7 @@ const CanvasMetadata = (props: { recommendsCount={props.recommendsCount} showComments={props.preferences.showComments !== false} showMentions={props.preferences.showMentions !== false} + showRecommends={props.preferences.showRecommends !== false} pageId={props.pageId} /> {!props.isSubpage && ( diff --git a/app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx b/app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx index 4b90df68..f2645ef9 100644 --- a/app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx +++ b/app/lish/[did]/[publication]/[rkey]/Interactions/Interactions.tsx @@ -112,6 +112,7 @@ export const Interactions = (props: { className?: string; showComments: boolean; showMentions: boolean; + showRecommends: boolean; pageId?: string; }) => { const { @@ -132,7 +133,8 @@ export const Interactions = (props: { const tags = normalizedDocument.tags; const tagCount = tags?.length || 0; - let interactionsAvailable = props.showComments || props.showMentions; + let interactionsAvailable = + props.showComments || props.showMentions || props.showRecommends; return (
{props.quotesCount} )} - + {props.showRecommends === false ? null : ( + + )} {tagCount > 0 && }
@@ -186,6 +190,7 @@ export const ExpandedInteractions = (props: { className?: string; showComments: boolean; showMentions: boolean; + showRecommends: boolean; pageId?: string; }) => { const { @@ -208,7 +213,8 @@ export const ExpandedInteractions = (props: { const tags = normalizedDocument.tags; const tagCount = tags?.length || 0; - let noInteractions = !props.showComments && !props.showMentions; + let noInteractions = + !props.showComments && !props.showMentions && !props.showRecommends; let subscribed = identity?.atp_did && @@ -237,11 +243,13 @@ export const ExpandedInteractions = (props: { ) : ( <>
- + {props.showRecommends === false ? null : ( + + )} {props.quotesCount === 0 || !props.showMentions ? null : ( { diff --git a/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx b/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx index 32ada105..04b9d7d9 100644 --- a/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx +++ b/app/lish/[did]/[publication]/[rkey]/LinearDocumentPage.tsx @@ -87,6 +87,7 @@ export function LinearDocumentPage({ pageId={pageId} showComments={preferences.showComments !== false} showMentions={preferences.showMentions !== false} + showRecommends={preferences.showRecommends !== false} commentsCount={ getCommentCount(document.comments_on_documents, pageId) || 0 } diff --git a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx index 8db38856..7a0f8ab0 100644 --- a/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx +++ b/app/lish/[did]/[publication]/[rkey]/PostHeader/PostHeader.tsx @@ -18,7 +18,11 @@ import { ProfilePopover } from "components/ProfilePopover"; export function PostHeader(props: { data: PostPageData; profile: ProfileViewDetailed; - preferences: { showComments?: boolean; showMentions?: boolean }; + preferences: { + showComments?: boolean; + showMentions?: boolean; + showRecommends?: boolean; + }; }) { let { identity } = useIdentityData(); let document = props.data; @@ -87,6 +91,7 @@ export function PostHeader(props: {
diff --git a/app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx b/app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx index 076568ef..70ae3d75 100644 --- a/app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx +++ b/app/lish/[did]/[publication]/dashboard/settings/PostOptions.tsx @@ -29,6 +29,11 @@ export const PostOptions = (props: { ? true : record.preferences.showMentions, ); + let [showRecommends, setShowRecommends] = useState( + record?.preferences?.showRecommends === undefined + ? true + : record.preferences.showRecommends, + ); let [showPrevNext, setShowPrevNext] = useState( record?.preferences?.showPrevNext === undefined ? true @@ -53,6 +58,7 @@ export const PostOptions = (props: { showComments: showComments, showMentions: showMentions, showPrevNext: showPrevNext, + showRecommends: showRecommends, }, }); toast({ type: "success", content: Posts Updated! }); @@ -99,7 +105,21 @@ export const PostOptions = (props: {
Show Mentions
- Display a list of posts on Bluesky that mention your post + Display a list Bluesky mentions about your post +
+
+ + + { + setShowRecommends(!showRecommends); + }} + > +
+
Show Recommends
+
+ Allow readers to recommend/like your post
diff --git a/app/lish/[did]/[publication]/page.tsx b/app/lish/[did]/[publication]/page.tsx index 4a369f00..457f38b2 100644 --- a/app/lish/[did]/[publication]/page.tsx +++ b/app/lish/[did]/[publication]/page.tsx @@ -175,6 +175,9 @@ export default async function Publication(props: { showMentions={ record?.preferences?.showMentions !== false } + showRecommends={ + record?.preferences?.showRecommends !== false + } /> diff --git a/app/lish/createPub/CreatePubForm.tsx b/app/lish/createPub/CreatePubForm.tsx index 2dbe924e..749b4397 100644 --- a/app/lish/createPub/CreatePubForm.tsx +++ b/app/lish/createPub/CreatePubForm.tsx @@ -58,6 +58,7 @@ export const CreatePubForm = () => { showComments: true, showMentions: true, showPrevNext: true, + showRecommends: true, }, }); diff --git a/app/lish/createPub/UpdatePubForm.tsx b/app/lish/createPub/UpdatePubForm.tsx index 3672c5f8..f9844796 100644 --- a/app/lish/createPub/UpdatePubForm.tsx +++ b/app/lish/createPub/UpdatePubForm.tsx @@ -88,6 +88,7 @@ export const EditPubForm = (props: { showComments: showComments, showMentions: showMentions, showPrevNext: showPrevNext, + showRecommends: record?.preferences?.showRecommends ?? true, }, }); toast({ type: "success", content: "Updated!" }); @@ -194,8 +195,6 @@ export const EditPubForm = (props: {

- - ); diff --git a/app/lish/createPub/createPublication.ts b/app/lish/createPub/createPublication.ts index a705a0e6..5adcc7ee 100644 --- a/app/lish/createPub/createPublication.ts +++ b/app/lish/createPub/createPublication.ts @@ -5,10 +5,7 @@ import { PubLeafletPublication, SiteStandardPublication, } from "lexicons/api"; -import { - restoreOAuthSession, - OAuthSessionError, -} from "src/atproto-oauth"; +import { restoreOAuthSession, OAuthSessionError } from "src/atproto-oauth"; import { getIdentityData } from "actions/getIdentityData"; import { supabaseServerClient } from "supabase/serverClient"; import { Json } from "supabase/database.types"; @@ -76,7 +73,11 @@ export async function createPublication({ // Build record based on publication type let record: SiteStandardPublication.Record | PubLeafletPublication.Record; - let iconBlob: Awaited>["data"]["blob"] | undefined; + let iconBlob: + | Awaited< + ReturnType + >["data"]["blob"] + | undefined; // Upload the icon if provided if (iconFile && iconFile.size > 0) { @@ -97,16 +98,29 @@ export async function createPublication({ ...(iconBlob && { icon: iconBlob }), basicTheme: { $type: "site.standard.theme.basic", - background: { $type: "site.standard.theme.color#rgb", ...PubThemeDefaultsRGB.background }, - foreground: { $type: "site.standard.theme.color#rgb", ...PubThemeDefaultsRGB.foreground }, - accent: { $type: "site.standard.theme.color#rgb", ...PubThemeDefaultsRGB.accent }, - accentForeground: { $type: "site.standard.theme.color#rgb", ...PubThemeDefaultsRGB.accentForeground }, + background: { + $type: "site.standard.theme.color#rgb", + ...PubThemeDefaultsRGB.background, + }, + foreground: { + $type: "site.standard.theme.color#rgb", + ...PubThemeDefaultsRGB.foreground, + }, + accent: { + $type: "site.standard.theme.color#rgb", + ...PubThemeDefaultsRGB.accent, + }, + accentForeground: { + $type: "site.standard.theme.color#rgb", + ...PubThemeDefaultsRGB.accentForeground, + }, }, preferences: { showInDiscover: preferences.showInDiscover, showComments: preferences.showComments, showMentions: preferences.showMentions, showPrevNext: preferences.showPrevNext, + showRecommends: preferences.showRecommends, }, } satisfies SiteStandardPublication.Record; } else { diff --git a/app/lish/createPub/updatePublication.ts b/app/lish/createPub/updatePublication.ts index a9bd3e56..350b3f88 100644 --- a/app/lish/createPub/updatePublication.ts +++ b/app/lish/createPub/updatePublication.ts @@ -77,7 +77,9 @@ async function withPublicationUpdate( } const aturi = new AtUri(existingPub.uri); - const publicationType = getPublicationType(aturi.collection) as PublicationType; + const publicationType = getPublicationType( + aturi.collection, + ) as PublicationType; // Normalize existing record const normalizedPub = normalizePublicationRecord(existingPub.record); @@ -128,7 +130,11 @@ interface RecordOverrides { } /** Merges override with existing value, respecting explicit undefined */ -function resolveField(override: T | undefined, existing: T | undefined, hasOverride: boolean): T | undefined { +function resolveField( + override: T | undefined, + existing: T | undefined, + hasOverride: boolean, +): T | undefined { return hasOverride ? override : existing; } @@ -146,17 +152,32 @@ function buildLeafletRecord( return { $type: "pub.leaflet.publication", name: overrides.name ?? normalizedPub?.name ?? "", - description: resolveField(overrides.description, normalizedPub?.description, "description" in overrides), - icon: resolveField(overrides.icon, normalizedPub?.icon, "icon" in overrides), - theme: resolveField(overrides.theme, normalizedPub?.theme, "theme" in overrides), + description: resolveField( + overrides.description, + normalizedPub?.description, + "description" in overrides, + ), + icon: resolveField( + overrides.icon, + normalizedPub?.icon, + "icon" in overrides, + ), + theme: resolveField( + overrides.theme, + normalizedPub?.theme, + "theme" in overrides, + ), base_path: overrides.basePath ?? existingBasePath, - preferences: preferences ? { - $type: "pub.leaflet.publication#preferences", - showInDiscover: preferences.showInDiscover, - showComments: preferences.showComments, - showMentions: preferences.showMentions, - showPrevNext: preferences.showPrevNext, - } : undefined, + preferences: preferences + ? { + $type: "pub.leaflet.publication#preferences", + showInDiscover: preferences.showInDiscover, + showComments: preferences.showComments, + showMentions: preferences.showMentions, + showPrevNext: preferences.showPrevNext, + showRecommends: preferences.showRecommends, + } + : undefined, }; } @@ -175,17 +196,36 @@ function buildStandardRecord( return { $type: "site.standard.publication", name: overrides.name ?? normalizedPub?.name ?? "", - description: resolveField(overrides.description, normalizedPub?.description, "description" in overrides), - icon: resolveField(overrides.icon, normalizedPub?.icon, "icon" in overrides), - theme: resolveField(overrides.theme, normalizedPub?.theme, "theme" in overrides), - basicTheme: resolveField(overrides.basicTheme, normalizedPub?.basicTheme, "basicTheme" in overrides), + description: resolveField( + overrides.description, + normalizedPub?.description, + "description" in overrides, + ), + icon: resolveField( + overrides.icon, + normalizedPub?.icon, + "icon" in overrides, + ), + theme: resolveField( + overrides.theme, + normalizedPub?.theme, + "theme" in overrides, + ), + basicTheme: resolveField( + overrides.basicTheme, + normalizedPub?.basicTheme, + "basicTheme" in overrides, + ), url: basePath ? `https://${basePath}` : normalizedPub?.url || "", - preferences: preferences ? { - showInDiscover: preferences.showInDiscover, - showComments: preferences.showComments, - showMentions: preferences.showMentions, - showPrevNext: preferences.showPrevNext, - } : undefined, + preferences: preferences + ? { + showInDiscover: preferences.showInDiscover, + showComments: preferences.showComments, + showMentions: preferences.showMentions, + showPrevNext: preferences.showPrevNext, + showRecommends: preferences.showRecommends, + } + : undefined, }; } @@ -217,27 +257,30 @@ export async function updatePublication({ iconFile?: File | null; preferences?: Omit; }): Promise { - return withPublicationUpdate(uri, async ({ normalizedPub, existingBasePath, publicationType, agent }) => { - // Upload icon if provided - let iconBlob = normalizedPub?.icon; - if (iconFile && iconFile.size > 0) { - const buffer = await iconFile.arrayBuffer(); - const uploadResult = await agent.com.atproto.repo.uploadBlob( - new Uint8Array(buffer), - { encoding: iconFile.type }, - ); - if (uploadResult.data.blob) { - iconBlob = uploadResult.data.blob; + return withPublicationUpdate( + uri, + async ({ normalizedPub, existingBasePath, publicationType, agent }) => { + // Upload icon if provided + let iconBlob = normalizedPub?.icon; + if (iconFile && iconFile.size > 0) { + const buffer = await iconFile.arrayBuffer(); + const uploadResult = await agent.com.atproto.repo.uploadBlob( + new Uint8Array(buffer), + { encoding: iconFile.type }, + ); + if (uploadResult.data.blob) { + iconBlob = uploadResult.data.blob; + } } - } - return buildRecord(normalizedPub, existingBasePath, publicationType, { - name, - description, - icon: iconBlob, - preferences, - }); - }); + return buildRecord(normalizedPub, existingBasePath, publicationType, { + name, + description, + icon: iconBlob, + preferences, + }); + }, + ); } export async function updatePublicationBasePath({ @@ -247,11 +290,14 @@ export async function updatePublicationBasePath({ uri: string; base_path: string; }): Promise { - return withPublicationUpdate(uri, async ({ normalizedPub, existingBasePath, publicationType }) => { - return buildRecord(normalizedPub, existingBasePath, publicationType, { - basePath: base_path, - }); - }); + return withPublicationUpdate( + uri, + async ({ normalizedPub, existingBasePath, publicationType }) => { + return buildRecord(normalizedPub, existingBasePath, publicationType, { + basePath: base_path, + }); + }, + ); } type Color = @@ -275,58 +321,81 @@ export async function updatePublicationTheme({ accentText: Color; }; }): Promise { - return withPublicationUpdate(uri, async ({ normalizedPub, existingBasePath, publicationType, agent }) => { - // Build theme object - const themeData = { - $type: "pub.leaflet.publication#theme" as const, - backgroundImage: theme.backgroundImage - ? { - $type: "pub.leaflet.theme.backgroundImage", - image: ( - await agent.com.atproto.repo.uploadBlob( - new Uint8Array(await theme.backgroundImage.arrayBuffer()), - { encoding: theme.backgroundImage.type }, - ) - )?.data.blob, - width: theme.backgroundRepeat || undefined, - repeat: !!theme.backgroundRepeat, - } - : theme.backgroundImage === null - ? undefined - : normalizedPub?.theme?.backgroundImage, - backgroundColor: theme.backgroundColor - ? { - ...theme.backgroundColor, - } - : undefined, - pageWidth: theme.pageWidth, - primary: { - ...theme.primary, - }, - pageBackground: { - ...theme.pageBackground, - }, - showPageBackground: theme.showPageBackground, - accentBackground: { - ...theme.accentBackground, - }, - accentText: { - ...theme.accentText, - }, - }; + return withPublicationUpdate( + uri, + async ({ normalizedPub, existingBasePath, publicationType, agent }) => { + // Build theme object + const themeData = { + $type: "pub.leaflet.publication#theme" as const, + backgroundImage: theme.backgroundImage + ? { + $type: "pub.leaflet.theme.backgroundImage", + image: ( + await agent.com.atproto.repo.uploadBlob( + new Uint8Array(await theme.backgroundImage.arrayBuffer()), + { encoding: theme.backgroundImage.type }, + ) + )?.data.blob, + width: theme.backgroundRepeat || undefined, + repeat: !!theme.backgroundRepeat, + } + : theme.backgroundImage === null + ? undefined + : normalizedPub?.theme?.backgroundImage, + backgroundColor: theme.backgroundColor + ? { + ...theme.backgroundColor, + } + : undefined, + pageWidth: theme.pageWidth, + primary: { + ...theme.primary, + }, + pageBackground: { + ...theme.pageBackground, + }, + showPageBackground: theme.showPageBackground, + accentBackground: { + ...theme.accentBackground, + }, + accentText: { + ...theme.accentText, + }, + }; - // Derive basicTheme from the theme colors for site.standard.publication - const basicTheme: NormalizedPublication["basicTheme"] = { - $type: "site.standard.theme.basic", - background: { $type: "site.standard.theme.color#rgb", r: theme.backgroundColor.r, g: theme.backgroundColor.g, b: theme.backgroundColor.b }, - foreground: { $type: "site.standard.theme.color#rgb", r: theme.primary.r, g: theme.primary.g, b: theme.primary.b }, - accent: { $type: "site.standard.theme.color#rgb", r: theme.accentBackground.r, g: theme.accentBackground.g, b: theme.accentBackground.b }, - accentForeground: { $type: "site.standard.theme.color#rgb", r: theme.accentText.r, g: theme.accentText.g, b: theme.accentText.b }, - }; + // Derive basicTheme from the theme colors for site.standard.publication + const basicTheme: NormalizedPublication["basicTheme"] = { + $type: "site.standard.theme.basic", + background: { + $type: "site.standard.theme.color#rgb", + r: theme.backgroundColor.r, + g: theme.backgroundColor.g, + b: theme.backgroundColor.b, + }, + foreground: { + $type: "site.standard.theme.color#rgb", + r: theme.primary.r, + g: theme.primary.g, + b: theme.primary.b, + }, + accent: { + $type: "site.standard.theme.color#rgb", + r: theme.accentBackground.r, + g: theme.accentBackground.g, + b: theme.accentBackground.b, + }, + accentForeground: { + $type: "site.standard.theme.color#rgb", + r: theme.accentText.r, + g: theme.accentText.g, + b: theme.accentText.b, + }, + }; - return buildRecord(normalizedPub, existingBasePath, publicationType, { - theme: themeData, - basicTheme, - }); - }); + return buildRecord(normalizedPub, existingBasePath, publicationType, { + theme: themeData, + basicTheme, + }); + }, + ); } diff --git a/components/InteractionsPreview.tsx b/components/InteractionsPreview.tsx index 46f91183..9f393f86 100644 --- a/components/InteractionsPreview.tsx +++ b/components/InteractionsPreview.tsx @@ -18,22 +18,26 @@ export const InteractionPreview = (props: { postUrl: string; showComments: boolean; showMentions: boolean; + showRecommends: boolean; share?: boolean; }) => { let smoker = useSmoker(); let interactionsAvailable = (props.quotesCount > 0 && props.showMentions) || - (props.showComments !== false && props.commentsCount > 0); + (props.showComments !== false && props.commentsCount > 0) || + (props.showRecommends !== false && props.recommendsCount > 0); const tagsCount = props.tags?.length || 0; return (
- + {props.showRecommends === false ? null : ( + + )} {!props.showMentions || props.quotesCount === 0 ? null : ( { tags={tags} showComments={pubRecord?.preferences?.showComments !== false} showMentions={pubRecord?.preferences?.showMentions !== false} + showRecommends={ + pubRecord?.preferences?.showRecommends !== false + } share />
diff --git a/lexicons/api/types/pub/leaflet/publication.ts b/lexicons/api/types/pub/leaflet/publication.ts index 88314a28..51143ee0 100644 --- a/lexicons/api/types/pub/leaflet/publication.ts +++ b/lexicons/api/types/pub/leaflet/publication.ts @@ -1,89 +1,94 @@ /** * GENERATED CODE - DO NOT MODIFY */ -import { type ValidationResult, BlobRef } from '@atproto/lexicon' -import { CID } from 'multiformats/cid' -import { validate as _validate } from '../../../lexicons' -import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' -import type * as PubLeafletThemeColor from './theme/color' -import type * as PubLeafletThemeBackgroundImage from './theme/backgroundImage' +import { type ValidationResult, BlobRef } from "@atproto/lexicon"; +import { CID } from "multiformats/cid"; +import { validate as _validate } from "../../../lexicons"; +import { + type $Typed, + is$typed as _is$typed, + type OmitKey, +} from "../../../util"; +import type * as PubLeafletThemeColor from "./theme/color"; +import type * as PubLeafletThemeBackgroundImage from "./theme/backgroundImage"; const is$typed = _is$typed, - validate = _validate -const id = 'pub.leaflet.publication' + validate = _validate; +const id = "pub.leaflet.publication"; export interface Record { - $type: 'pub.leaflet.publication' - name: string - base_path?: string - description?: string - icon?: BlobRef - theme?: Theme - preferences?: Preferences - [k: string]: unknown + $type: "pub.leaflet.publication"; + name: string; + base_path?: string; + description?: string; + icon?: BlobRef; + theme?: Theme; + preferences?: Preferences; + [k: string]: unknown; } -const hashRecord = 'main' +const hashRecord = "main"; export function isRecord(v: V) { - return is$typed(v, id, hashRecord) + return is$typed(v, id, hashRecord); } export function validateRecord(v: V) { - return validate(v, id, hashRecord, true) + return validate(v, id, hashRecord, true); } export interface Preferences { - $type?: 'pub.leaflet.publication#preferences' - showInDiscover: boolean - showComments: boolean - showMentions: boolean - showPrevNext: boolean + $type?: "pub.leaflet.publication#preferences"; + showInDiscover: boolean; + showComments: boolean; + showMentions: boolean; + showPrevNext: boolean; + showRecommends: boolean; } -const hashPreferences = 'preferences' +const hashPreferences = "preferences"; export function isPreferences(v: V) { - return is$typed(v, id, hashPreferences) + return is$typed(v, id, hashPreferences); } export function validatePreferences(v: V) { - return validate(v, id, hashPreferences) + return validate(v, id, hashPreferences); } export interface Theme { - $type?: 'pub.leaflet.publication#theme' + $type?: "pub.leaflet.publication#theme"; backgroundColor?: | $Typed | $Typed - | { $type: string } - backgroundImage?: PubLeafletThemeBackgroundImage.Main - pageWidth?: number + | { $type: string }; + backgroundImage?: PubLeafletThemeBackgroundImage.Main; + pageWidth?: number; primary?: | $Typed | $Typed - | { $type: string } + | { $type: string }; pageBackground?: | $Typed | $Typed - | { $type: string } - showPageBackground: boolean + | { $type: string }; + showPageBackground: boolean; accentBackground?: | $Typed | $Typed - | { $type: string } + | { $type: string }; accentText?: | $Typed | $Typed - | { $type: string } + | { $type: string }; } -const hashTheme = 'theme' +const hashTheme = "theme"; export function isTheme(v: V) { - return is$typed(v, id, hashTheme) + return is$typed(v, id, hashTheme); } export function validateTheme(v: V) { - return validate(v, id, hashTheme) + return validate(v, id, hashTheme); } diff --git a/lexicons/api/types/site/standard/publication.ts b/lexicons/api/types/site/standard/publication.ts index 2daf12b6..08f80d9c 100644 --- a/lexicons/api/types/site/standard/publication.ts +++ b/lexicons/api/types/site/standard/publication.ts @@ -1,53 +1,58 @@ /** * GENERATED CODE - DO NOT MODIFY */ -import { type ValidationResult, BlobRef } from '@atproto/lexicon' -import { CID } from 'multiformats/cid' -import { validate as _validate } from '../../../lexicons' -import { type $Typed, is$typed as _is$typed, type OmitKey } from '../../../util' -import type * as SiteStandardThemeBasic from './theme/basic' -import type * as PubLeafletPublication from '../../pub/leaflet/publication' +import { type ValidationResult, BlobRef } from "@atproto/lexicon"; +import { CID } from "multiformats/cid"; +import { validate as _validate } from "../../../lexicons"; +import { + type $Typed, + is$typed as _is$typed, + type OmitKey, +} from "../../../util"; +import type * as SiteStandardThemeBasic from "./theme/basic"; +import type * as PubLeafletPublication from "../../pub/leaflet/publication"; const is$typed = _is$typed, - validate = _validate -const id = 'site.standard.publication' + validate = _validate; +const id = "site.standard.publication"; export interface Record { - $type: 'site.standard.publication' - basicTheme?: SiteStandardThemeBasic.Main - theme?: $Typed | { $type: string } - description?: string - icon?: BlobRef - name: string - preferences?: Preferences - url: string - [k: string]: unknown + $type: "site.standard.publication"; + basicTheme?: SiteStandardThemeBasic.Main; + theme?: $Typed | { $type: string }; + description?: string; + icon?: BlobRef; + name: string; + preferences?: Preferences; + url: string; + [k: string]: unknown; } -const hashRecord = 'main' +const hashRecord = "main"; export function isRecord(v: V) { - return is$typed(v, id, hashRecord) + return is$typed(v, id, hashRecord); } export function validateRecord(v: V) { - return validate(v, id, hashRecord, true) + return validate(v, id, hashRecord, true); } export interface Preferences { - $type?: 'site.standard.publication#preferences' - showInDiscover: boolean - showComments: boolean - showMentions: boolean - showPrevNext: boolean + $type?: "site.standard.publication#preferences"; + showInDiscover: boolean; + showComments: boolean; + showMentions: boolean; + showPrevNext: boolean; + showRecommends: boolean; } -const hashPreferences = 'preferences' +const hashPreferences = "preferences"; export function isPreferences(v: V) { - return is$typed(v, id, hashPreferences) + return is$typed(v, id, hashPreferences); } export function validatePreferences(v: V) { - return validate(v, id, hashPreferences) + return validate(v, id, hashPreferences); } diff --git a/lexicons/pub/leaflet/publication.json b/lexicons/pub/leaflet/publication.json index 0627addf..c98b1eb6 100644 --- a/lexicons/pub/leaflet/publication.json +++ b/lexicons/pub/leaflet/publication.json @@ -59,6 +59,10 @@ "showPrevNext": { "type": "boolean", "default": true + }, + "showRecommends": { + "type": "boolean", + "default": true } } }, diff --git a/lexicons/site/standard/publication.json b/lexicons/site/standard/publication.json index b1449052..7fdbcc22 100644 --- a/lexicons/site/standard/publication.json +++ b/lexicons/site/standard/publication.json @@ -58,6 +58,10 @@ "showPrevNext": { "default": false, "type": "boolean" + }, + "showRecommends": { + "default": true, + "type": "boolean" } }, "type": "object" diff --git a/lexicons/src/normalize.ts b/lexicons/src/normalize.ts index d4890e07..4f7294c7 100644 --- a/lexicons/src/normalize.ts +++ b/lexicons/src/normalize.ts @@ -50,7 +50,7 @@ export type NormalizedPublication = { * Checks if the record is a pub.leaflet.document */ export function isLeafletDocument( - record: unknown + record: unknown, ): record is PubLeafletDocument.Record { if (!record || typeof record !== "object") return false; const r = record as Record; @@ -65,7 +65,7 @@ export function isLeafletDocument( * Checks if the record is a site.standard.document */ export function isStandardDocument( - record: unknown + record: unknown, ): record is SiteStandardDocument.Record { if (!record || typeof record !== "object") return false; const r = record as Record; @@ -76,7 +76,7 @@ export function isStandardDocument( * Checks if the record is a pub.leaflet.publication */ export function isLeafletPublication( - record: unknown + record: unknown, ): record is PubLeafletPublication.Record { if (!record || typeof record !== "object") return false; const r = record as Record; @@ -91,7 +91,7 @@ export function isLeafletPublication( * Checks if the record is a site.standard.publication */ export function isStandardPublication( - record: unknown + record: unknown, ): record is SiteStandardPublication.Record { if (!record || typeof record !== "object") return false; const r = record as Record; @@ -106,7 +106,7 @@ function extractRgb( | $Typed | $Typed | { $type: string } - | undefined + | undefined, ): { r: number; g: number; b: number } | undefined { if (!color || typeof color !== "object") return undefined; const c = color as Record; @@ -124,12 +124,13 @@ function extractRgb( * Converts a pub.leaflet theme to a site.standard.theme.basic format */ export function leafletThemeToBasicTheme( - theme: PubLeafletPublication.Theme | undefined + theme: PubLeafletPublication.Theme | undefined, ): SiteStandardThemeBasic.Main | undefined { if (!theme) return undefined; const background = extractRgb(theme.backgroundColor); - const accent = extractRgb(theme.accentBackground) || extractRgb(theme.primary); + const accent = + extractRgb(theme.accentBackground) || extractRgb(theme.primary); const accentForeground = extractRgb(theme.accentText); // If we don't have the required colors, return undefined @@ -160,7 +161,10 @@ export function leafletThemeToBasicTheme( * @param uri - Optional document URI, used to extract the rkey for the path field when normalizing pub.leaflet records * @returns A normalized document in site.standard format, or null if invalid/unrecognized */ -export function normalizeDocument(record: unknown, uri?: string): NormalizedDocument | null { +export function normalizeDocument( + record: unknown, + uri?: string, +): NormalizedDocument | null { if (!record || typeof record !== "object") return null; // Pass through site.standard records directly (theme is already in correct format if present) @@ -219,7 +223,7 @@ export function normalizeDocument(record: unknown, uri?: string): NormalizedDocu * @returns A normalized publication in site.standard format, or null if invalid/unrecognized */ export function normalizePublication( - record: unknown + record: unknown, ): NormalizedPublication | null { if (!record || typeof record !== "object") return null; @@ -268,6 +272,7 @@ export function normalizePublication( showComments: record.preferences.showComments, showMentions: record.preferences.showMentions, showPrevNext: record.preferences.showPrevNext, + showRecommends: record.preferences.showRecommends, } : undefined; @@ -290,7 +295,7 @@ export function normalizePublication( * Type guard to check if a normalized document has leaflet content */ export function hasLeafletContent( - doc: NormalizedDocument + doc: NormalizedDocument, ): doc is NormalizedDocument & { content: $Typed; } { @@ -304,7 +309,7 @@ export function hasLeafletContent( * Gets the pages array from a normalized document, handling both formats */ export function getDocumentPages( - doc: NormalizedDocument + doc: NormalizedDocument, ): PubLeafletContent.Main["pages"] | undefined { if (!doc.content) return undefined; diff --git a/lexicons/src/publication.ts b/lexicons/src/publication.ts index 57bbbef9..cf5904d2 100644 --- a/lexicons/src/publication.ts +++ b/lexicons/src/publication.ts @@ -29,6 +29,7 @@ export const PubLeafletPublication: LexiconDoc = { showComments: { type: "boolean", default: true }, showMentions: { type: "boolean", default: true }, showPrevNext: { type: "boolean", default: true }, + showRecommends: { type: "boolean", default: true }, }, }, theme: { -- 2.51.2