+4
-4
deno.json
+4
-4
deno.json
···
2
2
"imports": {
3
3
"$lexicon/": "./__generated__/",
4
4
"@atproto/syntax": "npm:@atproto/syntax@^0.4.0",
5
-
"@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.39",
5
+
"@bigmoves/bff": "jsr:@bigmoves/bff@0.3.0-beta.40",
6
6
"@std/http": "jsr:@std/http@^1.0.17",
7
7
"@std/path": "jsr:@std/path@^1.0.9",
8
8
"@tailwindcss/cli": "npm:@tailwindcss/cli@^4.1.4",
···
20
20
"start": "deno run -A ./src/main.tsx",
21
21
"dev": "deno run \"dev:*\"",
22
22
"build": "deno task build:static && deno task build:tailwind && deno task build:fonts",
23
-
"build:static": "deno run -A jsr:@bigmoves/bff-cli@0.3.0-beta.37 build src/static/mod.ts",
23
+
"build:static": "deno run -A jsr:@bigmoves/bff-cli@0.3.0-beta.40 build src/static/mod.ts",
24
24
"build:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --minify",
25
25
"build:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts",
26
-
"dev:build": "DEV=true deno -A --watch=src/static/ jsr:@bigmoves/bff-cli@0.3.0-beta.37 build src/static/mod.ts",
26
+
"dev:build": "DEV=true deno -A --watch=src/static/ jsr:@bigmoves/bff-cli@0.3.0-beta.40 build src/static/mod.ts",
27
27
"dev:server": "deno run -A --env-file --watch ./src/main.tsx",
28
28
"dev:tailwind": "deno run -A --node-modules-dir npm:@tailwindcss/cli -i ./src/input.css -o ./build/styles.css --watch",
29
29
"dev:fonts": "rm -rf ./build/fonts && cp -r ./static/fonts/. ./build/fonts",
30
-
"sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.37 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif --external-collections=app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile",
30
+
"sync": "deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.40 sync --collections=social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif --external-collections=app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile --collection-key-map=\"{\"social.grain.favorite\":[\"subject\"],\"social.grain.graph.follow\":[\"subject\"],\"social.grain.gallery.item\":[\"gallery\",\"item\"],\"social.grain.photo.exif\":[\"photo\"]}\"",
31
31
"codegen": "deno run -A jsr:@bigmoves/bff-cli@0.3.0-beta.37 lexgen"
32
32
},
33
33
"compilerOptions": {
+4
-4
deno.lock
+4
-4
deno.lock
···
2
2
"version": "5",
3
3
"specifiers": {
4
4
"jsr:@bigmoves/atproto-oauth-client@0.2": "0.2.0",
5
-
"jsr:@bigmoves/bff@0.3.0-beta.39": "0.3.0-beta.39",
5
+
"jsr:@bigmoves/bff@0.3.0-beta.40": "0.3.0-beta.40",
6
6
"jsr:@deno/gfm@0.10": "0.10.0",
7
7
"jsr:@denosaurs/emoji@0.3": "0.3.1",
8
8
"jsr:@luca/esbuild-deno-loader@~0.11.1": "0.11.1",
···
92
92
"npm:jose"
93
93
]
94
94
},
95
-
"@bigmoves/bff@0.3.0-beta.39": {
96
-
"integrity": "6841d6b04aa91f5c79b8530af1d2721c0f1bf5b4038270da528121f3ab57765f",
95
+
"@bigmoves/bff@0.3.0-beta.40": {
96
+
"integrity": "826055f189da5fafb53011ad393e44f605aa5bfb7bc5b016b8663e5c922d7abd",
97
97
"dependencies": [
98
98
"jsr:@bigmoves/atproto-oauth-client",
99
99
"jsr:@std/assert@^1.0.13",
···
2021
2021
},
2022
2022
"workspace": {
2023
2023
"dependencies": [
2024
-
"jsr:@bigmoves/bff@0.3.0-beta.39",
2024
+
"jsr:@bigmoves/bff@0.3.0-beta.40",
2025
2025
"jsr:@std/http@^1.0.17",
2026
2026
"jsr:@std/path@^1.0.9",
2027
2027
"npm:@atproto/syntax@0.4",
+35
-5
src/lib/gallery.ts
+35
-5
src/lib/gallery.ts
···
110
110
const labels = ctx.indexService.queryLabels({
111
111
subjects: [gallery.uri],
112
112
});
113
+
113
114
const favs = getGalleryFavs(gallery.uri, ctx);
114
-
const viewerFav = favs.find((fav) => fav.did === ctx.currentUser?.did);
115
+
116
+
let viewerFav: string | undefined = undefined;
117
+
if (ctx.currentUser?.did) {
118
+
const fav = getGalleryFav(ctx.currentUser?.did, gallery.uri, ctx);
119
+
if (fav) {
120
+
viewerFav = fav.uri;
121
+
}
122
+
}
123
+
115
124
return galleryToView({
116
125
record: gallery,
117
126
creator: profile,
118
127
items: galleryPhotosMap.get(gallery.uri) ?? [],
119
128
labels,
120
-
favCount: favs.length,
129
+
favCount: favs,
121
130
viewerState: {
122
-
fav: viewerFav ? viewerFav.uri : undefined,
131
+
fav: viewerFav,
123
132
},
124
133
});
125
134
}
···
147
156
148
157
export function getGalleryFavs(galleryUri: string, ctx: BffContext) {
149
158
const atUri = new AtUri(galleryUri);
150
-
const results = ctx.indexService.getRecords<WithBffMeta<Favorite>>(
159
+
const count = ctx.indexService.countRecords(
151
160
"social.grain.favorite",
152
161
{
153
162
where: [
···
158
167
],
159
168
},
160
169
);
161
-
return results.items;
170
+
return count;
171
+
}
172
+
173
+
export function getGalleryFav(
174
+
did: string,
175
+
galleryUri: string,
176
+
ctx: BffContext,
177
+
) {
178
+
const atUri = new AtUri(galleryUri);
179
+
const { items: favs } = ctx.indexService.getRecords<WithBffMeta<Favorite>>(
180
+
"social.grain.favorite",
181
+
{
182
+
where: [
183
+
{
184
+
field: "subject",
185
+
equals: `at://${atUri.hostname}/social.grain.gallery/${atUri.rkey}`,
186
+
},
187
+
{ field: "did", equals: did },
188
+
],
189
+
},
190
+
);
191
+
return favs[0];
162
192
}
163
193
164
194
export function galleryToView({
+13
-3
src/lib/timeline.ts
+13
-3
src/lib/timeline.ts
···
10
10
import { getActorProfile } from "./actor.ts";
11
11
import {
12
12
galleryToView,
13
+
getGalleryFav,
13
14
getGalleryFavs,
14
15
getGalleryItemsAndPhotos,
15
16
} from "./gallery.ts";
···
79
80
const labels = ctx.indexService.queryLabels({
80
81
subjects: [gallery.uri],
81
82
});
83
+
82
84
const favs = getGalleryFavs(gallery.uri, ctx);
83
-
const viewerFav = favs.find((fav) => fav.did === ctx.currentUser?.did);
85
+
86
+
let viewerFav: string | undefined = undefined;
87
+
if (ctx.currentUser?.did) {
88
+
const fav = getGalleryFav(ctx.currentUser?.did, gallery.uri, ctx);
89
+
if (fav) {
90
+
viewerFav = fav.uri;
91
+
}
92
+
}
93
+
84
94
const galleryView = galleryToView({
85
95
record: gallery,
86
96
creator: profile,
87
97
items: galleryPhotos,
88
98
labels,
89
-
favCount: favs.length,
99
+
favCount: favs,
90
100
viewerState: {
91
-
fav: viewerFav ? viewerFav.uri : undefined,
101
+
fav: viewerFav,
92
102
},
93
103
});
94
104
+6
src/main.tsx
+6
src/main.tsx
···
41
41
"sh.tangled.actor.profile",
42
42
"sh.tangled.graph.follow",
43
43
],
44
+
collectionKeyMap: {
45
+
"social.grain.favorite": ["subject"],
46
+
"social.grain.graph.follow": ["subject"],
47
+
"social.grain.gallery.item": ["gallery", "item"],
48
+
"social.grain.photo.exif": ["photo"],
49
+
},
44
50
lexicons,
45
51
rootElement: Root,
46
52
onError,
+7
-3
sync.sh
+7
-3
sync.sh
···
2
2
3
3
# Helpful when running local-infra. Specify the repos you've created on a local pds instance.
4
4
5
-
REPOS="did:plc:yyz2m2gxnbaxoru2sepbltxv"
5
+
DB="backup-2025-06-18.db"
6
+
REPOS=""
6
7
COLLECTIONS="social.grain.gallery,social.grain.actor.profile,social.grain.photo,social.grain.favorite,social.grain.gallery.item,social.grain.graph.follow,social.grain.photo.exif"
7
8
EXTERNAL_COLLECTIONS="app.bsky.actor.profile,app.bsky.graph.follow,sh.tangled.graph.follow,sh.tangled.actor.profile"
9
+
COLLECTION_KEY_MAP='{"social.grain.favorite":["subject"],"social.grain.graph.follow":["subject"],"social.grain.gallery.item":["gallery","item"],"social.grain.photo.exif":["photo"]}'
8
10
9
-
deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.37 sync \
11
+
deno run -A --env=.env jsr:@bigmoves/bff-cli@0.3.0-beta.40 sync \
12
+
--db="$DB" \
10
13
--repos="$REPOS" \
11
14
--collections="$COLLECTIONS" \
12
-
--external-collections="$EXTERNAL_COLLECTIONS"
15
+
--external-collections="$EXTERNAL_COLLECTIONS" \
16
+
--collection-key-map="$COLLECTION_KEY_MAP"