your personal website on atproto - mirror
0
fork

Configure Feed

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

add recently updated blentos

Florian 83332418 80861920

+172 -22
+40 -7
src/lib/EditableWebsite.svelte
··· 5 5 import { BlueskyLogin } from '@foxui/social'; 6 6 7 7 import { margin, mobileMargin } from '$lib'; 8 - import { cardsEqual, clamp, fixCollisions, setCanEdit, setIsMobile, setPositionOfNewItem } from './helper'; 8 + import { 9 + cardsEqual, 10 + clamp, 11 + fixCollisions, 12 + setCanEdit, 13 + setIsMobile, 14 + setPositionOfNewItem 15 + } from './helper'; 9 16 import Profile from './Profile.svelte'; 10 17 import type { Item } from './types'; 11 18 import { deleteRecord, putRecord } from './oauth/atproto'; ··· 55 62 56 63 setIsMobile(() => isMobile); 57 64 58 - setCanEdit(() => client.isLoggedIn && client.profile?.did === did) 65 + setCanEdit(() => client.isLoggedIn && client.profile?.did === did); 59 66 60 67 // svelte-ignore state_referenced_locally 61 68 setDidContext(did); ··· 67 74 68 75 let maxHeight = $derived(items.reduce((max, item) => Math.max(max, getY(item) + getH(item)), 0)); 69 76 70 - function newCard(type: 'text' | 'image' | 'link' = 'link') { 77 + function newCard(type: string = 'link') { 71 78 let item: Item = { 72 79 id: TID.nextStr(), 73 80 x: 0, ··· 148 155 const item = items.find((i) => i.id === originalItem.id); 149 156 if (!item) { 150 157 console.log('deleting item', originalItem); 151 - promises.push( 152 - deleteRecord({ collection: 'app.blento.card', rkey: originalItem.id, did }) 153 - ); 158 + promises.push(deleteRecord({ collection: 'app.blento.card', rkey: originalItem.id, did })); 154 159 } 155 160 } 156 161 ··· 173 178 {/if} 174 179 175 180 {#if showingMobileView} 176 - <div class="bg-base-200 dark:bg-base-900 pointer-events-none fixed inset-0 -z-10 h-full w-full"></div> 181 + <div 182 + class="bg-base-200 dark:bg-base-900 pointer-events-none fixed inset-0 -z-10 h-full w-full" 183 + ></div> 177 184 {/if} 178 185 179 186 {#if newItem.modal && newItem.item} ··· 382 389 /> 383 390 </svg> 384 391 </Button> 392 + 393 + <!-- for special stuff --> 394 + {#if handle === 'blento.app'} 395 + <Button 396 + size="iconLg" 397 + variant="ghost" 398 + class="backdrop-blur-none" 399 + onclick={() => { 400 + newCard('updatedBlentos'); 401 + }} 402 + > 403 + <svg 404 + xmlns="http://www.w3.org/2000/svg" 405 + fill="none" 406 + viewBox="0 0 24 24" 407 + stroke-width="1.5" 408 + stroke="currentColor" 409 + > 410 + <path 411 + stroke-linecap="round" 412 + stroke-linejoin="round" 413 + d="M9.813 15.904 9 18.75l-.813-2.846a4.5 4.5 0 0 0-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 0 0 3.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 0 0 3.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 0 0-3.09 3.09ZM18.259 8.715 18 9.75l-.259-1.035a3.375 3.375 0 0 0-2.455-2.456L14.25 6l1.036-.259a3.375 3.375 0 0 0 2.455-2.456L18 2.25l.259 1.035a3.375 3.375 0 0 0 2.456 2.456L21.75 6l-1.035.259a3.375 3.375 0 0 0-2.456 2.456ZM16.894 20.567 16.5 21.75l-.394-1.183a2.25 2.25 0 0 0-1.423-1.423L13.5 18.75l1.183-.394a2.25 2.25 0 0 0 1.423-1.423l.394-1.183.394 1.183a2.25 2.25 0 0 0 1.423 1.423l1.183.394-1.183.394a2.25 2.25 0 0 0-1.423 1.423Z" 414 + /> 415 + </svg> 416 + </Button> 417 + {/if} 385 418 </div> 386 419 <div class="flex items-center gap-2"> 387 420 <Toggle
+12 -7
src/lib/Profile.svelte
··· 3 3 4 4 import { marked } from 'marked'; 5 5 import { client, login } from './oauth'; 6 - import { Button } from '@foxui/core'; 6 + import { Button, Subheading } from '@foxui/core'; 7 7 import { BlueskyLogin } from '@foxui/social'; 8 8 import { env } from '$env/dynamic/public'; 9 9 let { ··· 70 70 71 71 {#if !env.PUBLIC_IS_SELFHOSTED && handle === 'blento.app' && client.profile?.handle !== handle} 72 72 {#if !client.isInitializing && !client.isLoggedIn} 73 - <BlueskyLogin 74 - login={async (handle) => { 75 - await login(handle); 76 - return true; 77 - }} 78 - /> 73 + <div> 74 + <div class="my-4 text-sm"> 75 + To create your own blento, sign in with your bluesky account 76 + </div> 77 + <BlueskyLogin 78 + login={async (handle) => { 79 + await login(handle); 80 + return true; 81 + }} 82 + /> 83 + </div> 79 84 {:else if client.isLoggedIn} 80 85 <div> 81 86 <Button href={client.profile?.handle} class="mt-2">
+13
src/lib/cards/SpecialCards/UpdatedBlentos/EditingUpdatedBlentosCard.svelte
··· 1 + <script lang="ts"> 2 + import type { Item } from '$lib/types'; 3 + import BaseEditingCard, { 4 + type BaseEditingCardProps 5 + } from '../../BaseCard/BaseEditingCard.svelte'; 6 + import MainUpdatedBlentosCards from './MainUpdatedBlentosCards.svelte'; 7 + 8 + let { item = $bindable<Item>(), ...rest }: BaseEditingCardProps = $props(); 9 + </script> 10 + 11 + <BaseEditingCard {item} {...rest}> 12 + <MainUpdatedBlentosCards /> 13 + </BaseEditingCard>
+46
src/lib/cards/SpecialCards/UpdatedBlentos/MainUpdatedBlentosCards.svelte
··· 1 + <script lang="ts"> 2 + import { getRecentRecords } from '$lib/helper'; 3 + import { getProfile } from '$lib/oauth/atproto'; 4 + import type { ProfileViewDetailed } from '@atproto/api/dist/client/types/app/bsky/actor/defs'; 5 + import { onMount } from 'svelte'; 6 + 7 + const recentRecords = getRecentRecords(); 8 + 9 + let profiles: ProfileViewDetailed[] = $state([]); 10 + 11 + onMount(async () => { 12 + console.log(recentRecords); 13 + let uniqueDids = new Set<string>(); 14 + for (let record of recentRecords as { did: string }[]) { 15 + uniqueDids.add(record.did); 16 + } 17 + console.log(uniqueDids, Array.from(uniqueDids)); 18 + 19 + for (let did of Array.from(uniqueDids)) { 20 + console.log(did); 21 + const profile = await getProfile({ did }); 22 + profiles.push(profile); 23 + 24 + if (profiles.length > 9) return; 25 + } 26 + }); 27 + </script> 28 + 29 + <div class="pointer-events-none"> 30 + <div 31 + class="from-base-50 dark:from-base-950 absolute inset-0 bg-gradient-to-t to-transparent" 32 + ></div> 33 + <div class="absolute bottom-3 left-4 text-sm font-semibold">recently updated blentos</div> 34 + </div> 35 + <div class="flex h-full max-w-full items-center gap-4 overflow-x-scroll px-8"> 36 + {#each profiles as profile} 37 + <a 38 + href="/{profile.handle}" 39 + class=" hover:bg-base-200 dark:hover:bg-base-800 mb-4 flex h-fit min-w-24 flex-col items-center justify-center gap-2 rounded-xl p-2" 40 + target="_blank" 41 + > 42 + <div class="font-semibold">{profile.displayName || profile.handle}</div> 43 + <img src={profile.avatar} class="aspect-square size-20 rounded-full" alt="" /> 44 + </a> 45 + {/each} 46 + </div>
+15
src/lib/cards/SpecialCards/UpdatedBlentos/UpdatedBlentosCard.svelte
··· 1 + <script lang="ts"> 2 + import { marked } from 'marked'; 3 + import BaseCard, { type BaseCardProps } from '../../BaseCard/BaseCard.svelte'; 4 + import MainUpdatedBlentosCards from './MainUpdatedBlentosCards.svelte'; 5 + 6 + let { item, ...rest }: BaseCardProps = $props(); 7 + 8 + const renderer = new marked.Renderer(); 9 + renderer.link = ({ href, title, text }) => 10 + `<a target="_blank" href="${href}" title="${title}">${text}</a>`; 11 + </script> 12 + 13 + <BaseCard {item} {...rest}> 14 + <MainUpdatedBlentosCards /> 15 + </BaseCard>
+9
src/lib/cards/SpecialCards/UpdatedBlentos/index.ts
··· 1 + import type { CardDefinition } from '../../types'; 2 + import EditingUpdatedBlentosCard from './EditingUpdatedBlentosCard.svelte'; 3 + import UpdatedBlentosCard from './UpdatedBlentosCard.svelte'; 4 + 5 + export const UpdatedBlentosCardDefitition = { 6 + type: 'updatedBlentos', 7 + cardComponent: UpdatedBlentosCard, 8 + editingCardComponent: EditingUpdatedBlentosCard 9 + } as CardDefinition & { type: 'updatedBlentos' };
+3 -1
src/lib/cards/index.ts
··· 1 1 import { ImageCardDefinition } from './ImageCard'; 2 2 import { LinkCardDefinition } from './LinkCard'; 3 + import { UpdatedBlentosCardDefitition } from './SpecialCards/UpdatedBlentos'; 3 4 import { TextCardDefinition } from './TextCard'; 4 5 import type { CardDefinition } from './types'; 5 6 6 7 export const AllCardDefinitions = [ 7 8 ImageCardDefinition, 8 9 TextCardDefinition, 9 - LinkCardDefinition 10 + LinkCardDefinition, 11 + UpdatedBlentosCardDefitition 10 12 ] as const; 11 13 12 14 export const CardDefinitionsByType = AllCardDefinitions.reduce(
+3 -1
src/lib/helper.ts
··· 145 145 146 146 export const [getIsMobile, setIsMobile] = createContext<() => boolean>(); 147 147 148 - export const [getCanEdit, setCanEdit] = createContext<() => boolean>(); 148 + export const [getCanEdit, setCanEdit] = createContext<() => boolean>(); 149 + 150 + export const [getRecentRecords, setRecentRecords] = createContext();
+15 -2
src/lib/website/utils.ts
··· 69 69 } 70 70 } 71 71 72 - return { did, data: JSON.parse(JSON.stringify(downloadedData)) as DownloadedData }; 72 + let recentRecords; 73 + if (handle === 'blento.app') { 74 + try { 75 + // https://ufos-api.microcosm.blue/records?collection=app.blento.card 76 + const response = await fetch( 77 + 'https://ufos-api.microcosm.blue/records?collection=app.blento.card' 78 + ); 79 + recentRecords = await response.json(); 80 + } catch (error) { 81 + console.error('failed to fetch recent records', error); 82 + } 83 + } 84 + 85 + return { did, data: JSON.parse(JSON.stringify(downloadedData)) as DownloadedData, recentRecords }; 73 86 } 74 87 75 88 export async function uploadImage(image: Blob) { ··· 97 110 98 111 export function getImageBlobUrl({ did, link }: { did: string; link: string }) { 99 112 return `https://cdn.bsky.app/img/feed_thumbnail/plain/${did}/${link}@jpeg`; 100 - } 113 + }
+4 -2
src/routes/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { page } from '$app/state'; 2 + import { setRecentRecords } from '$lib/helper.js'; 3 3 import { type Item } from '$lib/types.js'; 4 4 import Website from '$lib/Website.svelte'; 5 5 6 6 let { data } = $props(); 7 - $inspect(data); 7 + 8 + // svelte-ignore state_referenced_locally 9 + setRecentRecords(data.recentRecords); 8 10 </script> 9 11 10 12 <Website
+4 -1
src/routes/[handle]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { page } from '$app/state'; 3 + import { setRecentRecords } from '$lib/helper.js'; 3 4 import { type Item } from '$lib/types.js'; 4 5 import Website from '$lib/Website.svelte'; 5 6 6 7 let { data } = $props(); 7 - $inspect(data); 8 + 9 + // svelte-ignore state_referenced_locally 10 + setRecentRecords(data.recentRecords); 8 11 </script> 9 12 10 13 <Website
+4
src/routes/[handle]/edit/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import { page } from '$app/state'; 3 3 import EditableWebsite from '$lib/EditableWebsite.svelte'; 4 + import { setRecentRecords } from '$lib/helper.js'; 4 5 import { type Item } from '$lib/types.js'; 5 6 6 7 let { data } = $props(); 8 + 9 + // svelte-ignore state_referenced_locally 10 + setRecentRecords(data.recentRecords); 7 11 </script> 8 12 9 13 <EditableWebsite
+4 -1
src/routes/edit/+page.svelte
··· 1 1 <script lang="ts"> 2 - import { page } from '$app/state'; 3 2 import EditableWebsite from '$lib/EditableWebsite.svelte'; 3 + import { setRecentRecords } from '$lib/helper.js'; 4 4 import { type Item } from '$lib/types.js'; 5 5 6 6 let { data } = $props(); 7 + 8 + // svelte-ignore state_referenced_locally 9 + setRecentRecords(data.recentRecords); 7 10 </script> 8 11 9 12 <EditableWebsite