[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

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

likes, v0.1

+174 -2
+14
scripts/author-dids.json
··· 1 + [ 2 + "did:plc:257wekqxg4hyapkq6k47igmp", 3 + "did:plc:wfml3smwyvohotnywklys5uq", 4 + "did:plc:myrtyr2rsjiivzlp5m6qevv5", 5 + "did:plc:xv2x4dimpe3zukzuynblaiyt", 6 + "did:plc:haldrwdq4aea5klterbxzsfs", 7 + "did:plc:izttpdp3l6vss5crelt5kcux", 8 + "did:plc:c764uyv2vgzswy5gpc4jgknf", 9 + "did:plc:2huehxznewvgzh3vknb36x5z", 10 + "did:plc:46c3ur7daizungsgji73qfky", 11 + "did:plc:sjxrdgjiotxnk6wapajdbn55", 12 + "did:plc:6x3sa4qcuhtc4yky2f6eu2ki", 13 + "did:plc:gysemcjbbnjru4f7reznddpk" 14 + ]
+26
scripts/backfill-authors.ts
··· 1 + import fs from 'fs'; 2 + 3 + async function main() { 4 + // const backendUrl = 'http://127.0.0.1:3001'; 5 + const backendUrl = 'https://skywatched-jetstream.fly.dev'; 6 + 7 + // get all authors from "author-dids.json" 8 + const authorDids = JSON.parse(fs.readFileSync('author-dids.json', 'utf8')); 9 + 10 + console.log(authorDids); 11 + 12 + for (const authorDid of authorDids) { 13 + try { 14 + const response = await fetch(`${backendUrl}/api/refresh-user?did=${authorDid}`); 15 + const data = await response.json(); 16 + console.log(data); 17 + } catch (error) { 18 + console.error(`Error refreshing user ${authorDid}:`, error); 19 + } 20 + 21 + // wait 1 second 22 + await new Promise((resolve) => setTimeout(resolve, 1000)); 23 + } 24 + } 25 + 26 + main();
+15
scripts/get-all-authors.ts
··· 1 + import fs from 'fs'; 2 + 3 + async function main() { 4 + const backendUrl = 'https://skywatched-jetstream.fly.dev'; 5 + 6 + const response = await fetch(`${backendUrl}/api/author-dids`); 7 + const authorDids = await response.json(); 8 + 9 + // save to file 10 + fs.writeFileSync('author-dids.json', JSON.stringify(authorDids, null, 2)); 11 + 12 + console.log(authorDids); 13 + } 14 + 15 + main();
+73 -1
src/lib/Components/ReviewCard.svelte
··· 5 5 6 6 let { data, showMovieDetails = true }: { data: MainRecord; showMovieDetails?: boolean } = 7 7 $props(); 8 + 9 + let isLiked = $state(false); 8 10 </script> 9 11 10 - <div class="relative w-full max-w-2xl p-6 backdrop-blur-sm"> 12 + <div class="relative w-full max-w-2xl p-4 backdrop-blur-sm"> 11 13 <div class="flex items-center gap-4 max-w-full overflow-hidden"> 12 14 {#if showMovieDetails} 13 15 <a ··· 67 69 {@html data.record.note?.value?.replace('\n', '<br /><br />')} 68 70 </div> 69 71 {/if} 72 + 73 + 74 + <div class="text-base-500 dark:text-base-400 mt-6 flex justify-between gap-2"> 75 + <!-- <button class="group inline-flex items-center gap-2 text-sm"> 76 + <svg 77 + xmlns="http://www.w3.org/2000/svg" 78 + fill="none" 79 + viewBox="0 0 24 24" 80 + stroke-width="1.5" 81 + stroke="currentColor" 82 + class="-m-1.5 size-7 rounded-full p-1.5 transition-all duration-100 group-hover:bg-amber-500/10 group-hover:text-amber-700 dark:group-hover:text-amber-400" 83 + > 84 + <path 85 + stroke-linecap="round" 86 + stroke-linejoin="round" 87 + d="M12 20.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 7.444 3 12c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 0 1-.923 1.785A5.969 5.969 0 0 0 6 21c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337Z" 88 + /> 89 + </svg> 90 + {#if data.post.replyCount} 91 + {numberToHumanReadable(data.post.replyCount)} 92 + {/if} 93 + </button> --> 94 + 95 + <!-- this is kind of how the bookmark button should look like too 96 + (but it should work without login too and just use the local db) --> 97 + <button 98 + class="group inline-flex items-center gap-2 text-sm" 99 + onclick={() => { 100 + if(isLiked) return; 101 + 102 + isLiked = true; 103 + 104 + fetch('/api/like', { 105 + method: 'POST', 106 + body: JSON.stringify({ cid: data.cid, uri: data.uri }) 107 + }); 108 + }} 109 + > 110 + {#if isLiked} 111 + <svg 112 + xmlns="http://www.w3.org/2000/svg" 113 + viewBox="0 0 24 24" 114 + fill="currentColor" 115 + class="-m-1.5 size-7 rounded-full p-1.5 transition-all duration-100 group-hover:bg-accent-500/10 group-hover:text-accent-700 dark:group-hover:text-accent-400" 116 + > 117 + <path 118 + d="m11.645 20.91-.007-.003-.022-.012a15.247 15.247 0 0 1-.383-.218 25.18 25.18 0 0 1-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0 1 12 5.052 5.5 5.5 0 0 1 16.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 0 1-4.244 3.17 15.247 15.247 0 0 1-.383.219l-.022.012-.007.004-.003.001a.752.752 0 0 1-.704 0l-.003-.001Z" 119 + /> 120 + </svg> 121 + {:else} 122 + <svg 123 + xmlns="http://www.w3.org/2000/svg" 124 + fill="none" 125 + viewBox="0 0 24 24" 126 + stroke-width="1.5" 127 + stroke="currentColor" 128 + class="-m-1.5 size-7 rounded-full p-1.5 transition-all duration-100 group-hover:bg-accent-500/10 group-hover:text-accent-700 dark:group-hover:text-accent-400" 129 + > 130 + <path 131 + stroke-linecap="round" 132 + stroke-linejoin="round" 133 + d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z" 134 + /> 135 + </svg> 136 + {/if} 137 + {(data.record.likes ?? 0) + (isLiked ? 1 : 0)} 138 + </button> 139 + 140 + 141 + </div> 70 142 71 143 <!-- <a href={`/review/${encodeURIComponent(data.uri)}`}> 72 144 <span class="absolute inset-0 z-10"></span>
+6 -1
src/lib/db.ts
··· 41 41 }; 42 42 crosspost?: { 43 43 uri: string; 44 + likes?: number; 45 + reposts?: number; 46 + replies?: number; 44 47 }; 48 + 49 + likes?: number; 45 50 }; 46 - }; 51 + }; 47 52 48 53 export async function getRecentRecordOfUser({ did }: { did: string }): Promise<MainRecord[]> { 49 54 const response = await fetch(`${env.BACKEND_URL}/api/recent-records-by-user?did=${did}`);
+40
src/routes/api/like/+server.ts
··· 1 + import { error, json, type RequestHandler } from '@sveltejs/kit'; 2 + import { AtpBaseClient } from '@atproto/api'; 3 + import { TID } from '@atproto/common'; 4 + 5 + export const POST: RequestHandler = async ({ request, locals }) => { 6 + const user = locals.user; 7 + const agent = locals.agent; 8 + if (!user || !agent || agent instanceof AtpBaseClient) { 9 + return error(401, 'Unauthorized API call'); 10 + } 11 + const body = await request.json(); 12 + 13 + const cid = body.cid; 14 + const uri = body.uri; 15 + 16 + const did = user.did; 17 + 18 + const rkey = TID.nextStr(); 19 + 20 + const record: { 21 + repo: string; 22 + collection: string; 23 + rkey: string; 24 + record: { 25 + subject: { cid: string; uri: string }; 26 + createdAt: string; 27 + }; 28 + } = { 29 + repo: did, 30 + collection: 'community.lexicon.interaction.like', 31 + rkey, 32 + record: { 33 + subject: { cid, uri }, 34 + createdAt: new Date().toISOString() 35 + } 36 + }; 37 + await agent.com.atproto.repo.putRecord(record); 38 + 39 + return json({ status: 'liked' }); 40 + };