+12
__generated__/lexicons.ts
+12
__generated__/lexicons.ts
···
2816
2816
format: 'at-uri',
2817
2817
description: 'Unique identifier of the gallery to delete',
2818
2818
},
2819
+
cascade: {
2820
+
type: 'boolean',
2821
+
default: true,
2822
+
description:
2823
+
'If true, will also delete any associated items in the gallery.',
2824
+
},
2819
2825
},
2820
2826
},
2821
2827
},
···
4404
4410
type: 'string',
4405
4411
format: 'at-uri',
4406
4412
description: 'AT URI of the photo to delete.',
4413
+
},
4414
+
cascade: {
4415
+
type: 'boolean',
4416
+
default: true,
4417
+
description:
4418
+
'If true, will also delete any associated EXIF data and gallery items.',
4407
4419
},
4408
4420
},
4409
4421
},
+15
-13
src/api/mod.ts
+15
-13
src/api/mod.ts
···
138
138
getFollowingWithProfiles,
139
139
} from "../lib/graph.ts";
140
140
import { getNotificationsDetailed } from "../lib/notifications.ts";
141
-
import { applyAlts, createExif, createPhoto } from "../lib/photo.ts";
141
+
import {
142
+
applyAlts,
143
+
createExif,
144
+
createPhoto,
145
+
deletePhoto,
146
+
} from "../lib/photo.ts";
142
147
import { getTimeline } from "../lib/timeline.ts";
143
148
import { createComment, getGalleryComments } from "../modules/comments.tsx";
144
149
···
181
186
["POST"],
182
187
async (req, _params, ctx) => {
183
188
ctx.requireAuth();
184
-
const { uri } = await parseDeleteGalleryInputs(
189
+
const { uri, cascade = true } = await parseDeleteGalleryInputs(
185
190
req,
186
191
);
187
-
const success = await deleteGallery(uri, ctx);
192
+
const success = await deleteGallery(uri, cascade, ctx);
188
193
return ctx.json({ success } satisfies DeleteGalleryOutputSchema);
189
194
},
190
195
),
···
298
303
["POST"],
299
304
async (req, _params, ctx) => {
300
305
ctx.requireAuth();
301
-
const { uri } = await parseDeletePhotoInputs(req);
302
-
try {
303
-
await ctx.deleteRecord(uri);
304
-
return ctx.json({ success: true } satisfies DeletePhotoOutputSchema);
305
-
} catch (error) {
306
-
console.error("Error deleting photo:", error);
307
-
return ctx.json({ success: false } satisfies DeletePhotoOutputSchema);
308
-
}
306
+
const { uri, cascade = true } = await parseDeletePhotoInputs(req);
307
+
const success = await deletePhoto(uri, cascade, ctx);
308
+
return ctx.json({ success } satisfies DeletePhotoOutputSchema);
309
309
},
310
310
),
311
311
route(
···
772
772
if (!uri) {
773
773
throw new XRPCError("InvalidRequest", "Missing uri input");
774
774
}
775
-
return { uri };
775
+
const cascade = typeof body.cascade === "boolean" ? body.cascade : undefined;
776
+
return { uri, cascade };
776
777
}
777
778
778
779
async function parseCreateGalleryItemInputs(
···
819
820
if (!uri) {
820
821
throw new XRPCError("InvalidRequest", "Missing uri input");
821
822
}
822
-
return { uri };
823
+
const cascade = typeof body.cascade === "boolean" ? body.cascade : undefined;
824
+
return { uri, cascade };
823
825
}
824
826
825
827
async function parseCreateFollowInputs(
+6
-1
src/lib/gallery.ts
+6
-1
src/lib/gallery.ts
···
145
145
});
146
146
}
147
147
148
-
export async function deleteGallery(uri: string, ctx: BffContext) {
148
+
export async function deleteGallery(
149
+
uri: string,
150
+
cascade: boolean,
151
+
ctx: BffContext,
152
+
) {
149
153
try {
150
154
await ctx.deleteRecord(uri);
155
+
if (!cascade) return true;
151
156
const { items: galleryItems } = ctx.indexService.getRecords<
152
157
WithBffMeta<GalleryItem>
153
158
>("social.grain.gallery.item", {
+70
src/lib/photo.ts
+70
src/lib/photo.ts
···
1
+
import { Record as Favorite } from "$lexicon/types/social/grain/favorite.ts";
1
2
import { Record as Gallery } from "$lexicon/types/social/grain/gallery.ts";
2
3
import { GalleryView } from "$lexicon/types/social/grain/gallery/defs.ts";
3
4
import { Record as GalleryItem } from "$lexicon/types/social/grain/gallery/item.ts";
···
351
352
352
353
return true;
353
354
}
355
+
356
+
export async function deletePhoto(
357
+
uri: string,
358
+
cascade: boolean,
359
+
ctx: BffContext,
360
+
): Promise<boolean> {
361
+
const deleteUris: string[] = [];
362
+
try {
363
+
await ctx.deleteRecord(
364
+
uri,
365
+
);
366
+
if (!cascade) return true;
367
+
const { items: galleryItems } = ctx.indexService.getRecords<
368
+
WithBffMeta<GalleryItem>
369
+
>(
370
+
"social.grain.gallery.item",
371
+
{
372
+
where: [
373
+
{
374
+
field: "item",
375
+
equals: uri,
376
+
},
377
+
],
378
+
},
379
+
);
380
+
for (const item of galleryItems) {
381
+
deleteUris.push(item.uri);
382
+
}
383
+
const { items: favorites } = ctx.indexService.getRecords<
384
+
WithBffMeta<Favorite>
385
+
>(
386
+
"social.grain.favorite",
387
+
{
388
+
where: [
389
+
{
390
+
field: "subject",
391
+
equals: uri,
392
+
},
393
+
],
394
+
},
395
+
);
396
+
for (const favorite of favorites) {
397
+
deleteUris.push(favorite.uri);
398
+
}
399
+
const { items: exifItems } = ctx.indexService.getRecords<
400
+
WithBffMeta<PhotoExif>
401
+
>(
402
+
"social.grain.photo.exif",
403
+
{
404
+
where: [
405
+
{
406
+
field: "photo",
407
+
equals: uri,
408
+
},
409
+
],
410
+
},
411
+
);
412
+
for (const item of exifItems) {
413
+
deleteUris.push(item.uri);
414
+
}
415
+
for (const uri of deleteUris) {
416
+
await ctx.deleteRecord(uri);
417
+
}
418
+
} catch (error) {
419
+
console.error("Failed to delete photo:", error);
420
+
return false;
421
+
}
422
+
return true;
423
+
}
+9
-58
src/routes/actions.tsx
+9
-58
src/routes/actions.tsx
···
5
5
import { Record as GalleryItem } from "$lexicon/types/social/grain/gallery/item.ts";
6
6
import { Record as Photo } from "$lexicon/types/social/grain/photo.ts";
7
7
import { isPhotoView } from "$lexicon/types/social/grain/photo/defs.ts";
8
-
import { Record as PhotoExif } from "$lexicon/types/social/grain/photo/exif.ts";
9
8
import { AtUri } from "@atproto/syntax";
10
9
import { BffContext, RouteHandler, WithBffMeta } from "@bigmoves/bff";
11
10
import {
···
28
27
updateGallery,
29
28
} from "../lib/gallery.ts";
30
29
import { getFollowers } from "../lib/graph.ts";
31
-
import { createExif, createPhoto, getPhoto } from "../lib/photo.ts";
30
+
import {
31
+
createExif,
32
+
createPhoto,
33
+
deletePhoto,
34
+
getPhoto,
35
+
} from "../lib/photo.ts";
32
36
import type { State } from "../state.ts";
33
37
import { galleryLink, profileLink, uploadPageLink } from "../utils.ts";
34
38
···
131
135
const { handle } = ctx.requireAuth();
132
136
const formData = await req.formData();
133
137
const uri = formData.get("uri") as string;
134
-
await deleteGallery(uri, ctx);
138
+
await deleteGallery(uri, true, ctx);
135
139
return ctx.redirect(profileLink(handle));
136
140
};
137
141
···
331
335
const selectedGalleryRkey = selectedGallery
332
336
? new AtUri(selectedGallery).rkey
333
337
: undefined;
334
-
const deleteUris: string[] = [];
335
-
await ctx.deleteRecord(
336
-
`at://${did}/social.grain.photo/${params.rkey}`,
337
-
);
338
-
const { items: galleryItems } = ctx.indexService.getRecords<
339
-
WithBffMeta<GalleryItem>
340
-
>(
341
-
"social.grain.gallery.item",
342
-
{
343
-
where: [
344
-
{
345
-
field: "item",
346
-
equals: `at://${did}/social.grain.photo/${params.rkey}`,
347
-
},
348
-
],
349
-
},
350
-
);
351
-
for (const item of galleryItems) {
352
-
deleteUris.push(item.uri);
353
-
}
354
-
const { items: favorites } = ctx.indexService.getRecords<
355
-
WithBffMeta<Favorite>
356
-
>(
357
-
"social.grain.favorite",
358
-
{
359
-
where: [
360
-
{
361
-
field: "subject",
362
-
equals: `at://${did}/social.grain.photo/${params.rkey}`,
363
-
},
364
-
],
365
-
},
366
-
);
367
-
for (const favorite of favorites) {
368
-
deleteUris.push(favorite.uri);
369
-
}
370
-
const { items: exifItems } = ctx.indexService.getRecords<
371
-
WithBffMeta<PhotoExif>
372
-
>(
373
-
"social.grain.photo.exif",
374
-
{
375
-
where: [
376
-
{
377
-
field: "photo",
378
-
equals: `at://${did}/social.grain.photo/${params.rkey}`,
379
-
},
380
-
],
381
-
},
382
-
);
383
-
for (const item of exifItems) {
384
-
deleteUris.push(item.uri);
385
-
}
386
-
for (const uri of deleteUris) {
387
-
await ctx.deleteRecord(uri);
388
-
}
338
+
const photoUri = `at://${did}/social.grain.photo/${params.rkey}`;
339
+
await deletePhoto(photoUri, true, ctx);
389
340
return ctx.redirect(uploadPageLink(selectedGalleryRkey));
390
341
};
391
342