your personal website on atproto - mirror blento.app

Merge pull request #40 from flo-bit/profile-stuff

profile stuff

authored by Florian and committed by GitHub be093765 7c0181de

+181 -191
+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 -29
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 { Button } from '@foxui/core'; 7 + import { getIsMobile } from './context'; 6 8 import type { Editor } from '@tiptap/core'; 7 9 import MadeWithBlento from './MadeWithBlento.svelte'; 8 10 9 11 let { data = $bindable() }: { data: WebsiteData } = $props(); 10 12 13 + let profilePosition = $derived(getProfilePosition(data)); 14 + 15 + function toggleProfilePosition() { 16 + data.publication.preferences ??= {}; 17 + data.publication.preferences.profilePosition = profilePosition === 'side' ? 'top' : 'side'; 18 + data = { ...data }; 19 + } 20 + 11 21 let fileInput: HTMLInputElement; 12 22 let isHoveringAvatar = $state(false); 13 - let descriptionEditor: Editor | null = $state(null); 14 - 15 - // Initialize publication if needed 16 - $effect(() => { 17 - if (!data.publication) { 18 - data.publication = { 19 - name: getName(data), 20 - description: getDescription(data) 21 - }; 22 - } else { 23 - if (data.publication.name === undefined) { 24 - data.publication.name = getName(data); 25 - } 26 - if (data.publication.description === undefined) { 27 - data.publication.description = getDescription(data); 28 - } 29 - } 30 - }); 31 23 32 24 async function handleAvatarChange(event: Event) { 33 25 const target = event.target as HTMLInputElement; ··· 38 30 const compressedBlob = await compressImage(file); 39 31 const objectUrl = URL.createObjectURL(compressedBlob); 40 32 41 - data.publication ??= {}; 42 33 data.publication.icon = { 43 34 blob: compressedBlob, 44 35 objectUrl ··· 51 42 } 52 43 53 44 function getAvatarUrl(): string | undefined { 54 - const customIcon = getImage(data.publication ?? {}, data.did, 'icon'); 45 + const customIcon = getImage(data.publication, data.did, 'icon'); 55 46 if (customIcon) return customIcon; 56 47 return data.profile.avatar; 57 48 } ··· 59 50 function handleFileInputClick() { 60 51 fileInput.click(); 61 52 } 53 + 54 + let isMobile = getIsMobile(); 62 55 </script> 63 56 64 57 <div 65 - 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" 58 + class={[ 59 + 'relative mx-auto flex max-w-lg flex-col justify-between px-8', 60 + profilePosition === 'side' 61 + ? '@5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12' 62 + : '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12' 63 + ]} 66 64 > 67 - <div class="flex flex-col gap-4 pt-16 pb-8 @5xl/wrapper:h-screen @5xl/wrapper:pt-24"> 65 + <div class={['absolute left-2 flex gap-2', profilePosition === 'side' ? 'top-12' : 'top-4']}> 66 + <!-- Position toggle button (desktop only) --> 67 + {#if !isMobile()} 68 + <Button size="sm" type="button" onclick={toggleProfilePosition} variant="ghost"> 69 + {profilePosition === 'side' ? 'Move to top' : 'Move to side'} 70 + </Button> 71 + {/if} 72 + 73 + <Button 74 + size="sm" 75 + onclick={() => { 76 + data.publication.preferences ??= {}; 77 + data.publication.preferences.hideProfileSection = true; 78 + data = { ...data }; 79 + }} 80 + variant="ghost" 81 + > 82 + hide profile 83 + </Button> 84 + </div> 85 + 86 + <div 87 + class={[ 88 + 'flex flex-col gap-4 pt-16 pb-8', 89 + profilePosition === 'side' && '@5xl/wrapper:h-screen @5xl/wrapper:pt-24' 90 + ]} 91 + > 68 92 <!-- Avatar with edit capability --> 69 93 <button 70 94 type="button" 71 - class="group relative size-32 cursor-pointer overflow-hidden rounded-full @5xl/wrapper:size-44" 95 + class={[ 96 + 'group relative size-32 cursor-pointer overflow-hidden rounded-full', 97 + profilePosition === 'side' && '@5xl/wrapper:size-44' 98 + ]} 72 99 onmouseenter={() => (isHoveringAvatar = true)} 73 100 onmouseleave={() => (isHoveringAvatar = false)} 74 101 onclick={handleFileInputClick} ··· 110 137 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" 111 138 /> 112 139 </svg> 113 - <span>Click to change</span> 140 + <span class="font-medium">Click to change</span> 114 141 </div> 115 142 </div> 116 143 </button> ··· 134 161 <div class="scrollbar -mx-4 grow overflow-x-hidden overflow-y-scroll px-4"> 135 162 {#if data.publication} 136 163 <MarkdownTextEditor 137 - bind:editor={descriptionEditor} 138 164 bind:contentDict={data.publication} 139 165 key="description" 140 - placeholder="Add a description... (supports markdown)" 166 + placeholder="Something about me..." 141 167 class="" 142 168 /> 143 169 {/if} 144 170 </div> 145 171 146 - <div class="h-10.5 w-1 @5xl/wrapper:hidden"></div> 172 + <div class={['h-10.5 w-1', profilePosition === 'side' && '@5xl/wrapper:hidden']}></div> 147 173 148 - <MadeWithBlento class="hidden @5xl/wrapper:block" /> 174 + <MadeWithBlento class="hidden @5xl/wrapper:block {profilePosition === 'side' && '@5xl/wrapper:block'}" /> 149 175 </div> 150 176 </div>
+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'; ··· 150 150 } 151 151 152 152 const sidebarItems = AllCardDefinitions.filter((cardDef) => cardDef.sidebarButtonText); 153 - 154 - let showSettings = $state(false); 155 153 156 154 let debugPoint = $state({ x: 0, y: 0 }); 157 155 ··· 526 524 image={'/' + data.handle + '/og.png'} 527 525 /> 528 526 529 - <Settings bind:open={showSettings} bind:data /> 530 - 531 527 <Account {data} /> 532 528 533 529 <Context {data}> ··· 539 535 </div> 540 536 {/if} 541 537 538 + {#if getHideProfileSection(data)} 539 + <Button 540 + size="sm" 541 + variant="ghost" 542 + onclick={() => { 543 + data.publication.preferences ??= {}; 544 + data.publication.preferences.hideProfileSection = false; 545 + data = { ...data }; 546 + }} 547 + class="absolute top-14 left-4 z-20" 548 + > 549 + show profile 550 + </Button> 551 + {/if} 542 552 {#if showingMobileView} 543 553 <div 544 554 class="bg-base-200 dark:bg-base-950 pointer-events-none fixed inset-0 -z-10 h-full w-full" ··· 561 571 class={[ 562 572 '@container/wrapper relative w-full', 563 573 showingMobileView 564 - ? 'bg-base-50 dark:bg-base-900 my-4 min-h-[calc(100dhv-2em)] rounded-2xl lg:mx-auto lg:w-[375px]' 574 + ? 'bg-base-50 dark:bg-base-900 my-4 min-h-[calc(100dhv-2em)] rounded-2xl lg:mx-auto lg:w-90' 565 575 : '' 566 576 ]} 567 577 > ··· 572 582 <div 573 583 class={[ 574 584 'mx-auto max-w-lg', 575 - !getHideProfileSection(data) 585 + !getHideProfileSection(data) && getProfilePosition(data) === 'side' 576 586 ? '@5xl/wrapper:grid @5xl/wrapper:max-w-7xl @5xl/wrapper:grid-cols-4' 577 587 : '@5xl/wrapper:max-w-4xl' 578 588 ]} ··· 751 761 bind:linkValue 752 762 bind:isSaving 753 763 bind:showingMobileView 754 - bind:showSettings 755 764 {newCard} 756 765 {addLink} 757 766 {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'; ··· 24 24 `<a target="_blank" href="${href}" title="${title}">${text}</a>`; 25 25 26 26 const profileUrl = $derived(`${page.url.origin}/${data.handle}`); 27 + const profilePosition = $derived(getProfilePosition(data)); 27 28 </script> 28 29 29 30 <!-- lg:fixed lg:h-screen lg:w-1/4 lg:max-w-none lg:px-12 lg:pt-24 xl:w-1/3 --> 30 31 <div 31 - 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" 32 + class={[ 33 + 'mx-auto flex max-w-lg flex-col justify-between px-8', 34 + profilePosition === 'side' 35 + ? '@5xl/wrapper:fixed @5xl/wrapper:h-screen @5xl/wrapper:w-1/4 @5xl/wrapper:max-w-none @5xl/wrapper:px-12' 36 + : '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12' 37 + ]} 32 38 > 33 - <div class="flex flex-col gap-4 pt-16 pb-8 @5xl/wrapper:h-screen @5xl/wrapper:pt-24"> 39 + <div 40 + class={[ 41 + 'flex flex-col gap-4 pt-16 pb-8', 42 + profilePosition === 'side' && '@5xl/wrapper:h-screen @5xl/wrapper:pt-24' 43 + ]} 44 + > 34 45 <a 35 46 href={profileUrl} 36 47 class="w-fit" ··· 40 51 } 41 52 }} 42 53 > 43 - {#if data.profile.avatar} 54 + {#if data.publication?.icon || data.profile.avatar} 44 55 <img 45 - class="border-base-400 dark:border-base-800 size-32 rounded-full border @5xl/wrapper:size-44" 56 + class={[ 57 + 'border-base-400 dark:border-base-800 size-32 rounded-full border object-cover', 58 + profilePosition === 'side' && '@5xl/wrapper:size-44' 59 + ]} 46 60 src={getImage(data.publication, data.did, 'icon') || data.profile.avatar} 47 61 alt="" 48 62 /> 49 63 {:else} 50 - <div class="bg-base-300 dark:bg-base-700 size-32 rounded-full @5xl/wrapper:size-44"></div> 64 + <div 65 + class={[ 66 + 'bg-base-300 dark:bg-base-700 size-32 rounded-full', 67 + profilePosition === 'side' && '@5xl/wrapper:size-44' 68 + ]} 69 + ></div> 51 70 {/if} 52 71 </a> 53 72 ··· 86 105 > 87 106 </div> 88 107 {:else} 89 - <div class="h-10.5 w-1 @5xl/wrapper:hidden"></div> 108 + <div class={['h-10.5 w-1', profilePosition === 'side' && '@5xl/wrapper:hidden']}></div> 90 109 {/if} 91 110 92 111 {#if !env.PUBLIC_IS_SELFHOSTED && data.handle === 'blento.app' && user.profile?.handle !== data.handle} ··· 124 143 </div> 125 144 {/if} 126 145 {/if} 127 - <MadeWithBlento class="hidden @5xl/wrapper:block" /> 146 + <MadeWithBlento class="hidden {profilePosition === 'side' && '@5xl/wrapper:block'}" /> 128 147 </div> 129 148 </div>
-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'; ··· 50 56 <div 51 57 class={[ 52 58 'mx-auto max-w-lg', 53 - !getHideProfileSection(data) 59 + !getHideProfileSection(data) && getProfilePosition(data) === 'side' 54 60 ? '@5xl/wrapper:grid @5xl/wrapper:max-w-7xl @5xl/wrapper:grid-cols-4' 55 61 : '@5xl/wrapper:max-w-4xl' 56 62 ]}
+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