···
1
1
+
[
2
2
+
"did:plc:257wekqxg4hyapkq6k47igmp",
3
3
+
"did:plc:wfml3smwyvohotnywklys5uq",
4
4
+
"did:plc:myrtyr2rsjiivzlp5m6qevv5",
5
5
+
"did:plc:xv2x4dimpe3zukzuynblaiyt",
6
6
+
"did:plc:haldrwdq4aea5klterbxzsfs",
7
7
+
"did:plc:izttpdp3l6vss5crelt5kcux",
8
8
+
"did:plc:c764uyv2vgzswy5gpc4jgknf",
9
9
+
"did:plc:2huehxznewvgzh3vknb36x5z",
10
10
+
"did:plc:46c3ur7daizungsgji73qfky",
11
11
+
"did:plc:sjxrdgjiotxnk6wapajdbn55",
12
12
+
"did:plc:6x3sa4qcuhtc4yky2f6eu2ki",
13
13
+
"did:plc:gysemcjbbnjru4f7reznddpk"
14
14
+
]
···
1
1
+
import fs from 'fs';
2
2
+
3
3
+
async function main() {
4
4
+
// const backendUrl = 'http://127.0.0.1:3001';
5
5
+
const backendUrl = 'https://skywatched-jetstream.fly.dev';
6
6
+
7
7
+
// get all authors from "author-dids.json"
8
8
+
const authorDids = JSON.parse(fs.readFileSync('author-dids.json', 'utf8'));
9
9
+
10
10
+
console.log(authorDids);
11
11
+
12
12
+
for (const authorDid of authorDids) {
13
13
+
try {
14
14
+
const response = await fetch(`${backendUrl}/api/refresh-user?did=${authorDid}`);
15
15
+
const data = await response.json();
16
16
+
console.log(data);
17
17
+
} catch (error) {
18
18
+
console.error(`Error refreshing user ${authorDid}:`, error);
19
19
+
}
20
20
+
21
21
+
// wait 1 second
22
22
+
await new Promise((resolve) => setTimeout(resolve, 1000));
23
23
+
}
24
24
+
}
25
25
+
26
26
+
main();
···
1
1
+
import fs from 'fs';
2
2
+
3
3
+
async function main() {
4
4
+
const backendUrl = 'https://skywatched-jetstream.fly.dev';
5
5
+
6
6
+
const response = await fetch(`${backendUrl}/api/author-dids`);
7
7
+
const authorDids = await response.json();
8
8
+
9
9
+
// save to file
10
10
+
fs.writeFileSync('author-dids.json', JSON.stringify(authorDids, null, 2));
11
11
+
12
12
+
console.log(authorDids);
13
13
+
}
14
14
+
15
15
+
main();
···
5
5
6
6
let { data, showMovieDetails = true }: { data: MainRecord; showMovieDetails?: boolean } =
7
7
$props();
8
8
+
9
9
+
let isLiked = $state(false);
8
10
</script>
9
11
10
10
-
<div class="relative w-full max-w-2xl p-6 backdrop-blur-sm">
12
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
72
+
73
73
+
74
74
+
<div class="text-base-500 dark:text-base-400 mt-6 flex justify-between gap-2">
75
75
+
<!-- <button class="group inline-flex items-center gap-2 text-sm">
76
76
+
<svg
77
77
+
xmlns="http://www.w3.org/2000/svg"
78
78
+
fill="none"
79
79
+
viewBox="0 0 24 24"
80
80
+
stroke-width="1.5"
81
81
+
stroke="currentColor"
82
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
83
+
>
84
84
+
<path
85
85
+
stroke-linecap="round"
86
86
+
stroke-linejoin="round"
87
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
88
+
/>
89
89
+
</svg>
90
90
+
{#if data.post.replyCount}
91
91
+
{numberToHumanReadable(data.post.replyCount)}
92
92
+
{/if}
93
93
+
</button> -->
94
94
+
95
95
+
<!-- this is kind of how the bookmark button should look like too
96
96
+
(but it should work without login too and just use the local db) -->
97
97
+
<button
98
98
+
class="group inline-flex items-center gap-2 text-sm"
99
99
+
onclick={() => {
100
100
+
if(isLiked) return;
101
101
+
102
102
+
isLiked = true;
103
103
+
104
104
+
fetch('/api/like', {
105
105
+
method: 'POST',
106
106
+
body: JSON.stringify({ cid: data.cid, uri: data.uri })
107
107
+
});
108
108
+
}}
109
109
+
>
110
110
+
{#if isLiked}
111
111
+
<svg
112
112
+
xmlns="http://www.w3.org/2000/svg"
113
113
+
viewBox="0 0 24 24"
114
114
+
fill="currentColor"
115
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
116
+
>
117
117
+
<path
118
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
119
+
/>
120
120
+
</svg>
121
121
+
{:else}
122
122
+
<svg
123
123
+
xmlns="http://www.w3.org/2000/svg"
124
124
+
fill="none"
125
125
+
viewBox="0 0 24 24"
126
126
+
stroke-width="1.5"
127
127
+
stroke="currentColor"
128
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
129
+
>
130
130
+
<path
131
131
+
stroke-linecap="round"
132
132
+
stroke-linejoin="round"
133
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
134
+
/>
135
135
+
</svg>
136
136
+
{/if}
137
137
+
{(data.record.likes ?? 0) + (isLiked ? 1 : 0)}
138
138
+
</button>
139
139
+
140
140
+
141
141
+
</div>
70
142
71
143
<!-- <a href={`/review/${encodeURIComponent(data.uri)}`}>
72
144
<span class="absolute inset-0 z-10"></span>
···
41
41
};
42
42
crosspost?: {
43
43
uri: string;
44
44
+
likes?: number;
45
45
+
reposts?: number;
46
46
+
replies?: number;
44
47
};
48
48
+
49
49
+
likes?: number;
45
50
};
46
46
-
};
51
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}`);
···
1
1
+
import { error, json, type RequestHandler } from '@sveltejs/kit';
2
2
+
import { AtpBaseClient } from '@atproto/api';
3
3
+
import { TID } from '@atproto/common';
4
4
+
5
5
+
export const POST: RequestHandler = async ({ request, locals }) => {
6
6
+
const user = locals.user;
7
7
+
const agent = locals.agent;
8
8
+
if (!user || !agent || agent instanceof AtpBaseClient) {
9
9
+
return error(401, 'Unauthorized API call');
10
10
+
}
11
11
+
const body = await request.json();
12
12
+
13
13
+
const cid = body.cid;
14
14
+
const uri = body.uri;
15
15
+
16
16
+
const did = user.did;
17
17
+
18
18
+
const rkey = TID.nextStr();
19
19
+
20
20
+
const record: {
21
21
+
repo: string;
22
22
+
collection: string;
23
23
+
rkey: string;
24
24
+
record: {
25
25
+
subject: { cid: string; uri: string };
26
26
+
createdAt: string;
27
27
+
};
28
28
+
} = {
29
29
+
repo: did,
30
30
+
collection: 'community.lexicon.interaction.like',
31
31
+
rkey,
32
32
+
record: {
33
33
+
subject: { cid, uri },
34
34
+
createdAt: new Date().toISOString()
35
35
+
}
36
36
+
};
37
37
+
await agent.com.atproto.repo.putRecord(record);
38
38
+
39
39
+
return json({ status: 'liked' });
40
40
+
};