your personal website on atproto - mirror blento.app
25
fork

Configure Feed

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

profile stuff

Florian 12d5f948 1fd59d31

+181 -192
+1 -1
src/lib/cards/BaseCard/BaseEditingCard.svelte
··· 182 182 {#if cardDef.canHaveLabel} 183 183 <div 184 184 class={cn( 185 - 'bg-base-200/30 dark:bg-base-900/30 absolute top-2 left-2 z-100 w-fit max-w-[calc(100%-1rem)] rounded-xl p-1 px-2 backdrop-blur-md', 185 + 'bg-base-200/50 dark:bg-base-900/50 absolute top-2 left-2 z-100 w-fit max-w-[calc(100%-1rem)] rounded-xl p-1 px-2 backdrop-blur-md', 186 186 !item.cardData.label && 'hidden group-hover/card:block' 187 187 )} 188 188 >
+6 -2
src/lib/components/MarkdownTextEditor.svelte
··· 19 19 key = 'text', 20 20 placeholder = '', 21 21 defaultContent = '', 22 - class: className 22 + class: className, 23 + onupdate 23 24 }: { 24 - editor: Editor | null; 25 + editor?: Editor | null; 25 26 contentDict: Record<string, any>; 26 27 key: string; 27 28 placeholder?: string; 28 29 defaultContent?: string; 29 30 class?: string; 31 + onupdate?: (content: string) => void; 30 32 } = $props(); 31 33 32 34 const update = async () => { ··· 41 43 const markdown = turndownService.turndown(html); 42 44 43 45 contentDict[key] = markdown; 46 + 47 + onupdate?.(markdown); 44 48 }; 45 49 46 50 onMount(async () => {
+8 -2
src/lib/components/PlainTextEditor.svelte
··· 15 15 key, 16 16 class: className, 17 17 placeholder = '', 18 - defaultContent = '' 18 + defaultContent = '', 19 + onupdate 19 20 }: { 20 21 contentDict: Record<string, any>; 21 22 key: string; 22 23 class?: string; 23 24 placeholder?: string; 24 25 defaultContent?: string; 26 + onupdate?: (content: string) => void; 25 27 } = $props(); 26 28 27 29 const update = async () => { 28 30 if (!editor) return; 29 31 30 - contentDict[key] = editor.getText(); 32 + const text = editor.getText(); 33 + 34 + contentDict[key] = text; 35 + 36 + onupdate?.(text); 31 37 }; 32 38 33 39 onMount(async () => {
+22 -8
src/lib/helper.ts
··· 285 285 } 286 286 287 287 export function getName(data: WebsiteData): string { 288 - return (data.publication?.name ?? data.profile.displayName) || data.handle; 288 + return data.publication?.name || data.profile.displayName || data.handle; 289 289 } 290 290 291 291 export function getDescription(data: WebsiteData): string { ··· 300 300 return data?.publication?.preferences?.hideProfile; 301 301 302 302 return data.page !== 'blento.self'; 303 + } 304 + 305 + export function getProfilePosition(data: WebsiteData): 'side' | 'top' { 306 + return data?.publication?.preferences?.profilePosition ?? 'side'; 303 307 } 304 308 305 309 export function isTyping() { ··· 475 479 data.publication.url += '/' + data.page.replace('blento.', ''); 476 480 } 477 481 } 478 - promises.push( 479 - putRecord({ 480 - collection: 'site.standard.publication', 481 - rkey: data.page, 482 - record: data.publication 483 - }) 484 - ); 482 + if (data.page !== 'blento.self') { 483 + promises.push( 484 + putRecord({ 485 + collection: 'app.blento.page', 486 + rkey: data.page, 487 + record: data.publication 488 + }) 489 + ); 490 + } else { 491 + promises.push( 492 + putRecord({ 493 + collection: 'site.standard.publication', 494 + rkey: data.page, 495 + record: data.publication 496 + }) 497 + ); 498 + } 485 499 486 500 console.log('updating or adding publication', data.publication); 487 501 }
+19 -18
src/lib/types.ts
··· 33 33 handle: string; 34 34 35 35 cards: Item[]; 36 - publication: 37 - | { 38 - url?: string; 39 - name?: string; 40 - description?: string; 41 - icon?: Blob; 42 - preferences?: { 43 - /** 44 - * @deprecated 45 - * 46 - * use hideProfileSection instead 47 - */ 48 - hideProfile?: boolean; 36 + publication: { 37 + url?: string; 38 + name?: string; 39 + description?: string; 40 + icon?: Blob; 41 + preferences?: { 42 + /** 43 + * @deprecated 44 + * 45 + * use hideProfileSection instead 46 + */ 47 + hideProfile?: boolean; 48 + 49 + // use this instead 50 + hideProfileSection?: boolean; 49 51 50 - // use this instead 51 - hideProfileSection?: boolean; 52 - }; 53 - } 54 - | undefined; 52 + // 'side' (default on desktop) or 'top' (always top like mobile view) 53 + profilePosition?: 'side' | 'top'; 54 + }; 55 + }; 55 56 profile: AppBskyActorDefs.ProfileViewDetailed; 56 57 57 58 additionalData: Record<string, unknown>;
+1 -1
src/lib/website/Account.svelte
··· 14 14 </script> 15 15 16 16 {#if user.isLoggedIn && user.profile} 17 - <div class="fixed top-4 right-4 z-20"> 17 + <div class="fixed bottom-4 right-4 z-20"> 18 18 <Popover sideOffset={8} bind:open={settingsPopoverOpen} class="bg-base-100 dark:bg-base-900"> 19 19 {#snippet child({ props })} 20 20 <button {...props}>
-30
src/lib/website/EditBar.svelte
··· 9 9 linkValue = $bindable(), 10 10 newCard, 11 11 addLink, 12 - showSettings = $bindable(), 13 12 14 13 showingMobileView = $bindable(), 15 14 isSaving = $bindable(), ··· 23 22 linkValue: string; 24 23 newCard: (type: string) => void; 25 24 addLink: (url: string) => void; 26 - 27 - showSettings: boolean; 28 25 29 26 showingMobileView: boolean; 30 27 ··· 225 222 </Button> 226 223 </div> 227 224 <div class="flex items-center gap-2"> 228 - <Button 229 - size="iconLg" 230 - variant="ghost" 231 - class="backdrop-blur-none" 232 - onclick={() => { 233 - showSettings = true; 234 - }} 235 - > 236 - <svg 237 - xmlns="http://www.w3.org/2000/svg" 238 - fill="none" 239 - viewBox="0 0 24 24" 240 - stroke-width="1.5" 241 - stroke="currentColor" 242 - > 243 - <path 244 - stroke-linecap="round" 245 - stroke-linejoin="round" 246 - d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" 247 - /> 248 - <path 249 - stroke-linecap="round" 250 - stroke-linejoin="round" 251 - d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" 252 - /> 253 - </svg> 254 - </Button> 255 225 <Toggle 256 226 class="hidden bg-transparent backdrop-blur-none lg:block dark:bg-transparent" 257 227 bind:pressed={showingMobileView}
+55 -30
src/lib/website/EditableProfile.svelte
··· 1 1 <script lang="ts"> 2 2 import type { WebsiteData } from '$lib/types'; 3 - import { getDescription, getName, getImage, compressImage } from '$lib/helper'; 3 + import { getImage, compressImage, getProfilePosition } from '$lib/helper'; 4 4 import PlainTextEditor from '$lib/components/PlainTextEditor.svelte'; 5 5 import MarkdownTextEditor from '$lib/components/MarkdownTextEditor.svelte'; 6 - import type { Editor } from '@tiptap/core'; 6 + import { Button } from '@foxui/core'; 7 + import { getIsMobile } from './context'; 7 8 8 9 let { data = $bindable() }: { data: WebsiteData } = $props(); 9 10 11 + let profilePosition = $derived(getProfilePosition(data)); 12 + 13 + function toggleProfilePosition() { 14 + data.publication.preferences ??= {}; 15 + data.publication.preferences.profilePosition = profilePosition === 'side' ? 'top' : 'side'; 16 + data = { ...data }; 17 + } 18 + 10 19 let fileInput: HTMLInputElement; 11 20 let isHoveringAvatar = $state(false); 12 - let descriptionEditor: Editor | null = $state(null); 13 - 14 - // Initialize publication if needed 15 - $effect(() => { 16 - if (!data.publication) { 17 - data.publication = { 18 - name: getName(data), 19 - description: getDescription(data) 20 - }; 21 - } else { 22 - if (data.publication.name === undefined) { 23 - data.publication.name = getName(data); 24 - } 25 - if (data.publication.description === undefined) { 26 - data.publication.description = getDescription(data); 27 - } 28 - } 29 - }); 30 21 31 22 async function handleAvatarChange(event: Event) { 32 23 const target = event.target as HTMLInputElement; ··· 37 28 const compressedBlob = await compressImage(file); 38 29 const objectUrl = URL.createObjectURL(compressedBlob); 39 30 40 - data.publication ??= {}; 41 31 data.publication.icon = { 42 32 blob: compressedBlob, 43 33 objectUrl ··· 50 40 } 51 41 52 42 function getAvatarUrl(): string | undefined { 53 - const customIcon = getImage(data.publication ?? {}, data.did, 'icon'); 43 + const customIcon = getImage(data.publication, data.did, 'icon'); 54 44 if (customIcon) return customIcon; 55 45 return data.profile.avatar; 56 46 } ··· 58 48 function handleFileInputClick() { 59 49 fileInput.click(); 60 50 } 51 + 52 + let isMobile = getIsMobile(); 61 53 </script> 62 54 63 55 <div 64 - class="mx-auto flex max-w-lg flex-col justify-between px-8 @5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12" 56 + class={[ 57 + 'relative mx-auto flex max-w-lg flex-col justify-between px-8', 58 + profilePosition === 'side' 59 + ? '@5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12' 60 + : '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12' 61 + ]} 65 62 > 66 - <div class="flex flex-col gap-4 pt-16 pb-8 @5xl/wrapper:h-screen @5xl/wrapper:pt-24"> 63 + <div class={['absolute left-2 flex gap-2', profilePosition === 'side' ? 'top-12' : 'top-4']}> 64 + <!-- Position toggle button (desktop only) --> 65 + {#if !isMobile()} 66 + <Button size="sm" type="button" onclick={toggleProfilePosition} variant="ghost"> 67 + {profilePosition === 'side' ? 'Move to top' : 'Move to side'} 68 + </Button> 69 + {/if} 70 + 71 + <Button 72 + size="sm" 73 + onclick={() => { 74 + data.publication.preferences ??= {}; 75 + data.publication.preferences.hideProfileSection = true; 76 + data = { ...data }; 77 + }} 78 + variant="ghost" 79 + > 80 + hide profile 81 + </Button> 82 + </div> 83 + 84 + <div 85 + class={[ 86 + 'flex flex-col gap-4 pt-16 pb-8', 87 + profilePosition === 'side' && '@5xl/wrapper:h-screen @5xl/wrapper:pt-24' 88 + ]} 89 + > 67 90 <!-- Avatar with edit capability --> 68 91 <button 69 92 type="button" 70 - class="group relative size-32 cursor-pointer overflow-hidden rounded-full @5xl/wrapper:size-44" 93 + class={[ 94 + 'group relative size-32 cursor-pointer overflow-hidden rounded-full', 95 + profilePosition === 'side' && '@5xl/wrapper:size-44' 96 + ]} 71 97 onmouseenter={() => (isHoveringAvatar = true)} 72 98 onmouseleave={() => (isHoveringAvatar = false)} 73 99 onclick={handleFileInputClick} ··· 109 135 d="M16.5 12.75a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0ZM18.75 10.5h.008v.008h-.008V10.5Z" 110 136 /> 111 137 </svg> 112 - <span>Click to change</span> 138 + <span class="font-medium">Click to change</span> 113 139 </div> 114 140 </div> 115 141 </button> ··· 133 159 <div class="scrollbar -mx-4 grow overflow-x-hidden overflow-y-scroll px-4"> 134 160 {#if data.publication} 135 161 <MarkdownTextEditor 136 - bind:editor={descriptionEditor} 137 162 bind:contentDict={data.publication} 138 163 key="description" 139 - placeholder="Add a description... (supports markdown)" 164 + placeholder="Something about me..." 140 165 class="" 141 166 /> 142 167 {/if} 143 168 </div> 144 169 145 - <div class="h-10.5 w-1 @5xl/wrapper:hidden"></div> 170 + <div class={['h-10.5 w-1', profilePosition === 'side' && '@5xl/wrapper:hidden']}></div> 146 171 147 - <div class="hidden text-xs font-light @5xl/wrapper:block"> 172 + <div class={['hidden text-xs font-light', profilePosition === 'side' && '@5xl/wrapper:block']}> 148 173 made with <a 149 174 href="https://blento.app" 150 175 target="_blank"
+17 -8
src/lib/website/EditableWebsite.svelte
··· 8 8 createEmptyCard, 9 9 fixCollisions, 10 10 getHideProfileSection, 11 + getProfilePosition, 11 12 getName, 12 13 isTyping, 13 14 savePage, ··· 26 27 import { setIsMobile } from './context'; 27 28 import BaseEditingCard from '../cards/BaseCard/BaseEditingCard.svelte'; 28 29 import Context from './Context.svelte'; 29 - import Settings from './Settings.svelte'; 30 30 import Head from './Head.svelte'; 31 31 import { compressImage } from '../helper'; 32 32 import Account from './Account.svelte'; ··· 149 149 } 150 150 151 151 const sidebarItems = AllCardDefinitions.filter((cardDef) => cardDef.sidebarButtonText); 152 - 153 - let showSettings = $state(false); 154 152 155 153 let debugPoint = $state({ x: 0, y: 0 }); 156 154 ··· 525 523 image={'/' + data.handle + '/og.png'} 526 524 /> 527 525 528 - <Settings bind:open={showSettings} bind:data /> 529 - 530 526 <Account {data} /> 531 527 532 528 <Context {data}> ··· 538 534 </div> 539 535 {/if} 540 536 537 + {#if getHideProfileSection(data)} 538 + <Button 539 + size="sm" 540 + variant="ghost" 541 + onclick={() => { 542 + data.publication.preferences ??= {}; 543 + data.publication.preferences.hideProfileSection = false; 544 + data = { ...data }; 545 + }} 546 + class="absolute top-14 left-4 z-20" 547 + > 548 + show profile 549 + </Button> 550 + {/if} 541 551 {#if showingMobileView} 542 552 <div 543 553 class="bg-base-200 dark:bg-base-950 pointer-events-none fixed inset-0 -z-10 h-full w-full" ··· 560 570 class={[ 561 571 '@container/wrapper relative w-full', 562 572 showingMobileView 563 - ? 'bg-base-50 dark:bg-base-900 my-4 min-h-[calc(100dhv-2em)] rounded-2xl lg:mx-auto lg:w-[375px]' 573 + ? 'bg-base-50 dark:bg-base-900 my-4 min-h-[calc(100dhv-2em)] rounded-2xl lg:mx-auto lg:w-90' 564 574 : '' 565 575 ]} 566 576 > ··· 571 581 <div 572 582 class={[ 573 583 'mx-auto max-w-lg', 574 - !getHideProfileSection(data) 584 + !getHideProfileSection(data) && getProfilePosition(data) === 'side' 575 585 ? '@5xl/wrapper:grid @5xl/wrapper:max-w-7xl @5xl/wrapper:grid-cols-4' 576 586 : '@5xl/wrapper:max-w-4xl' 577 587 ]} ··· 749 759 bind:linkValue 750 760 bind:isSaving 751 761 bind:showingMobileView 752 - bind:showSettings 753 762 {newCard} 754 763 {addLink} 755 764 {save}
+27 -8
src/lib/website/Profile.svelte
··· 5 5 import { BlueskyLogin } from '@foxui/social'; 6 6 import { env } from '$env/dynamic/public'; 7 7 import type { WebsiteData } from '$lib/types'; 8 - import { getDescription, getImage, getName } from '$lib/helper'; 8 + import { getDescription, getImage, getName, getProfilePosition } from '$lib/helper'; 9 9 import { page } from '$app/state'; 10 10 import type { ActorIdentifier } from '@atcute/lexicons'; 11 11 import { qrOverlay } from '$lib/components/qr/qrOverlay.svelte'; ··· 23 23 `<a target="_blank" href="${href}" title="${title}">${text}</a>`; 24 24 25 25 const profileUrl = $derived(`${page.url.origin}/${data.handle}`); 26 + const profilePosition = $derived(getProfilePosition(data)); 26 27 </script> 27 28 28 29 <!-- lg:fixed lg:h-screen lg:w-1/4 lg:max-w-none lg:px-12 lg:pt-24 xl:w-1/3 --> 29 30 <div 30 - class="mx-auto flex max-w-lg flex-col justify-between px-8 @5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12" 31 + class={[ 32 + 'mx-auto flex max-w-lg flex-col justify-between px-8', 33 + profilePosition === 'side' 34 + ? '@5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12' 35 + : '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12' 36 + ]} 31 37 > 32 - <div class="flex flex-col gap-4 pt-16 pb-8 @5xl/wrapper:h-screen @5xl/wrapper:pt-24"> 38 + <div 39 + class={[ 40 + 'flex flex-col gap-4 pt-16 pb-8', 41 + profilePosition === 'side' && '@5xl/wrapper:h-screen @5xl/wrapper:pt-24' 42 + ]} 43 + > 33 44 <a 34 45 href={profileUrl} 35 46 class="w-fit" ··· 39 50 } 40 51 }} 41 52 > 42 - {#if data.profile.avatar} 53 + {#if data.publication?.icon || data.profile.avatar} 43 54 <img 44 - class="border-base-400 dark:border-base-800 size-32 rounded-full border @5xl/wrapper:size-44" 55 + class={[ 56 + 'border-base-400 dark:border-base-800 size-32 rounded-full border object-cover', 57 + profilePosition === 'side' && '@5xl/wrapper:size-44' 58 + ]} 45 59 src={getImage(data.publication, data.did, 'icon') || data.profile.avatar} 46 60 alt="" 47 61 /> 48 62 {:else} 49 - <div class="bg-base-300 dark:bg-base-700 size-32 rounded-full @5xl/wrapper:size-44"></div> 63 + <div 64 + class={[ 65 + 'bg-base-300 dark:bg-base-700 size-32 rounded-full', 66 + profilePosition === 'side' && '@5xl/wrapper:size-44' 67 + ]} 68 + ></div> 50 69 {/if} 51 70 </a> 52 71 ··· 85 104 > 86 105 </div> 87 106 {:else} 88 - <div class="h-10.5 w-1 @5xl/wrapper:hidden"></div> 107 + <div class={['h-10.5 w-1', profilePosition === 'side' && '@5xl/wrapper:hidden']}></div> 89 108 {/if} 90 109 91 110 {#if !env.PUBLIC_IS_SELFHOSTED && data.handle === 'blento.app' && user.profile?.handle !== data.handle} ··· 123 142 </div> 124 143 {/if} 125 144 {/if} 126 - <div class="hidden text-xs font-light @5xl/wrapper:block"> 145 + <div class={['hidden text-xs font-light', profilePosition === 'side' && '@5xl/wrapper:block']}> 127 146 made with <a 128 147 href="https://blento.app" 129 148 target="_blank"
-74
src/lib/website/Settings.svelte
··· 1 - <script lang="ts"> 2 - import { getDescription, getHideProfileSection, getName } from '$lib/helper'; 3 - import type { WebsiteData } from '$lib/types'; 4 - import { Button, Checkbox, Heading, Input, Label, Modal, Textarea } from '@foxui/core'; 5 - 6 - export type Settings = { 7 - title: string; 8 - }; 9 - 10 - let { open = $bindable(), data = $bindable() }: { open: boolean; data: WebsiteData } = $props(); 11 - 12 - let name = $state(getName(data)); 13 - 14 - $effect(() => { 15 - if (!open && name && name !== getName(data)) { 16 - data.publication ??= {}; 17 - data.publication.name = name; 18 - 19 - data = { ...data }; 20 - } 21 - }); 22 - </script> 23 - 24 - <Modal bind:open class="dark:bg-base-900"> 25 - <Heading>Settings</Heading> 26 - <Label>Name</Label> 27 - <Input bind:value={name} /> 28 - <Label class="mt-4">Description</Label> 29 - <Textarea 30 - rows={5} 31 - bind:value={ 32 - () => { 33 - return getDescription(data); 34 - }, 35 - (value) => { 36 - data.publication ??= {}; 37 - data.publication.description = value; 38 - 39 - data = { ...data }; 40 - } 41 - } 42 - /> 43 - 44 - <div class="flex items-center space-x-2"> 45 - <Checkbox 46 - bind:checked={ 47 - () => { 48 - return getHideProfileSection(data); 49 - }, 50 - (value) => { 51 - data.publication ??= {}; 52 - data.publication.preferences ??= {}; 53 - data.publication.preferences.hideProfileSection = value; 54 - 55 - data = { ...data }; 56 - } 57 - } 58 - id="hide-profile" 59 - aria-labelledby="hide-profile-label" 60 - variant="secondary" 61 - /> 62 - <Label 63 - id="hide-profile-label" 64 - for="hide-profile" 65 - class="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 66 - > 67 - Hide Profile Section 68 - </Label> 69 - </div> 70 - 71 - <div class="flex w-full justify-end space-x-2"> 72 - <Button onclick={() => (open = false)}>Close</Button> 73 - </div> 74 - </Modal>
+8 -2
src/lib/website/Website.svelte
··· 1 1 <script lang="ts"> 2 2 import Card from '../cards/Card/Card.svelte'; 3 3 import Profile from './Profile.svelte'; 4 - import { getDescription, getHideProfileSection, getName, sortItems } from '../helper'; 4 + import { 5 + getDescription, 6 + getHideProfileSection, 7 + getProfilePosition, 8 + getName, 9 + sortItems 10 + } from '../helper'; 5 11 import { innerWidth } from 'svelte/reactivity/window'; 6 12 import { setDidContext, setHandleContext, setIsMobile } from './context'; 7 13 import BaseCard from '../cards/BaseCard/BaseCard.svelte'; ··· 48 54 <div 49 55 class={[ 50 56 'mx-auto max-w-lg', 51 - !getHideProfileSection(data) 57 + !getHideProfileSection(data) && getProfilePosition(data) === 'side' 52 58 ? '@5xl/wrapper:grid @5xl/wrapper:max-w-7xl @5xl/wrapper:grid-cols-4' 53 59 : '@5xl/wrapper:max-w-4xl' 54 60 ]}
+17 -8
src/lib/website/load.ts
··· 1 - import { getDetailedProfile, listRecords, resolveHandle, parseUri } from '$lib/atproto'; 1 + import { getDetailedProfile, listRecords, resolveHandle, parseUri, getRecord } from '$lib/atproto'; 2 2 import { CardDefinitionsByType } from '$lib/cards'; 3 3 import type { Item, UserCache, WebsiteData } from '$lib/types'; 4 4 import { compactItems, fixAllCollisions } from '$lib/helper'; ··· 33 33 result.publication = (result.publications as Awaited<ReturnType<typeof listRecords>>).find( 34 34 (v) => parseUri(v.uri).rkey === result.page 35 35 )?.value; 36 + result.publication ??= {}; 36 37 37 38 delete result['publications']; 38 39 ··· 67 68 return [] as Awaited<ReturnType<typeof listRecords>>; 68 69 }); 69 70 70 - const publications = await listRecords({ did, collection: 'site.standard.publication' }).catch( 71 - () => { 72 - console.error('error getting records for collection site.standard.publication'); 73 - return [] as Awaited<ReturnType<typeof listRecords>>; 74 - } 75 - ); 71 + const mainPublication = await getRecord({ 72 + did, 73 + collection: 'site.standard.publication', 74 + rkey: 'blento.self' 75 + }).catch(() => { 76 + console.error('error getting record for collection site.standard.publication'); 77 + return [] as Awaited<ReturnType<typeof listRecords>>; 78 + }); 79 + 80 + const pages = await listRecords({ did, collection: 'app.blento.page' }).catch(() => { 81 + console.error('error getting records for collection app.blento.page'); 82 + return [] as Awaited<ReturnType<typeof listRecords>>; 83 + }); 76 84 77 85 const profile = await getDetailedProfile({ did }); 78 86 ··· 116 124 cards: (cards.map((v) => { 117 125 return { ...v.value }; 118 126 }) ?? []) as Item[], 119 - publications: publications, 127 + publications: [mainPublication, ...pages], 120 128 additionalData, 121 129 profile, 122 130 updatedAt: Date.now(), ··· 130 138 parsedResult.publication = ( 131 139 parsedResult.publications as Awaited<ReturnType<typeof listRecords>> 132 140 ).find((v) => parseUri(v.uri).rkey === parsedResult.page)?.value; 141 + parsedResult.publication ??= {}; 133 142 134 143 delete parsedResult['publications']; 135 144