first commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import { createSupabaseServiceRoleClient } from "@/lib/supabase/service-role";
|
||||
|
||||
export type PublicGalleryPhotoRow = {
|
||||
id: string;
|
||||
unsplash_id: string;
|
||||
slug: string | null;
|
||||
description: string | null;
|
||||
alt_description: string | null;
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
color: string | null;
|
||||
color_family: string | null;
|
||||
likes: number | null;
|
||||
urls: Record<string, unknown>;
|
||||
user_info: Record<string, unknown>;
|
||||
photo_created_at: string | null;
|
||||
picked_at: string | null;
|
||||
oss_url: string | null;
|
||||
};
|
||||
|
||||
export const publicGalleryPhotoSelect =
|
||||
"id,unsplash_id,slug,description,alt_description,width,height,color,color_family,likes,urls,user_info,photo_created_at,picked_at,oss_url";
|
||||
|
||||
export function getPublicPhotoUrl(photo: PublicGalleryPhotoRow) {
|
||||
const candidates = [
|
||||
photo.oss_url,
|
||||
photo.urls?.regular,
|
||||
photo.urls?.small,
|
||||
photo.urls?.raw,
|
||||
photo.urls?.thumb,
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (typeof candidate === "string" && candidate.length > 0) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getFavoritePhotoIds({
|
||||
photoIds,
|
||||
userId,
|
||||
}: {
|
||||
photoIds: string[];
|
||||
userId: string | null;
|
||||
}) {
|
||||
if (!userId || photoIds.length === 0) {
|
||||
return new Set<string>();
|
||||
}
|
||||
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
const { data, error } = await supabase
|
||||
.from("wg_user_favorites")
|
||||
.select("photo_id")
|
||||
.eq("user_id", userId)
|
||||
.in("photo_id", photoIds);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return new Set((data ?? []).map((favorite) => favorite.photo_id));
|
||||
}
|
||||
|
||||
export function mapPublicGalleryPhoto({
|
||||
favoritePhotoIds,
|
||||
photo,
|
||||
}: {
|
||||
favoritePhotoIds?: Set<string>;
|
||||
photo: PublicGalleryPhotoRow;
|
||||
}) {
|
||||
return {
|
||||
id: photo.id,
|
||||
unsplashId: photo.unsplash_id,
|
||||
slug: photo.slug,
|
||||
description: photo.description,
|
||||
altDescription: photo.alt_description,
|
||||
width: photo.width,
|
||||
height: photo.height,
|
||||
color: photo.color,
|
||||
colorFamily: photo.color_family,
|
||||
likes: photo.likes,
|
||||
url: getPublicPhotoUrl(photo),
|
||||
sourceUrl: getPublicPhotoUrl({ ...photo, oss_url: null }),
|
||||
user: photo.user_info,
|
||||
photoCreatedAt: photo.photo_created_at,
|
||||
pickedAt: photo.picked_at,
|
||||
isFavorited: favoritePhotoIds?.has(photo.id) ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
export async function listPickedGalleryPhotos({
|
||||
colorFamily,
|
||||
page,
|
||||
pageSize,
|
||||
userId,
|
||||
}: {
|
||||
colorFamily: string | null;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
userId: string | null;
|
||||
}) {
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
let query = supabase
|
||||
.from("wg_gallery_photos")
|
||||
.select(publicGalleryPhotoSelect, { count: "exact" })
|
||||
.eq("is_picked", true);
|
||||
|
||||
if (colorFamily && colorFamily !== "all") {
|
||||
query = query.eq("color_family", colorFamily);
|
||||
}
|
||||
|
||||
const { data, error, count } = await query
|
||||
.order("picked_at", { ascending: false, nullsFirst: false })
|
||||
.order("photo_created_at", { ascending: false })
|
||||
.range((page - 1) * pageSize, page * pageSize - 1);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const photos = (data ?? []) as PublicGalleryPhotoRow[];
|
||||
const favoritePhotoIds = await getFavoritePhotoIds({
|
||||
userId,
|
||||
photoIds: photos.map((photo) => photo.id),
|
||||
});
|
||||
|
||||
return {
|
||||
data: photos.map((photo) =>
|
||||
mapPublicGalleryPhoto({ photo, favoritePhotoIds }),
|
||||
),
|
||||
total: count ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPickedGalleryPhotoById({
|
||||
id,
|
||||
userId,
|
||||
}: {
|
||||
id: string;
|
||||
userId: string | null;
|
||||
}) {
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
const { data, error } = await supabase
|
||||
.from("wg_gallery_photos")
|
||||
.select(publicGalleryPhotoSelect)
|
||||
.eq("id", id)
|
||||
.eq("is_picked", true)
|
||||
.maybeSingle<PublicGalleryPhotoRow>();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const favoritePhotoIds = await getFavoritePhotoIds({
|
||||
userId,
|
||||
photoIds: [data.id],
|
||||
});
|
||||
|
||||
return mapPublicGalleryPhoto({ photo: data, favoritePhotoIds });
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import { createSupabaseServiceRoleClient } from "@/lib/supabase/service-role";
|
||||
import {
|
||||
getFavoritePhotoIds,
|
||||
mapPublicGalleryPhoto,
|
||||
} from "@/server/site/gallery-photos";
|
||||
import type { CollectionPhoto } from "@/types/admin/collection";
|
||||
|
||||
type PublicCollectionRow = {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
cover_photo_id: string | null;
|
||||
published_at: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
type CollectionItemRow = {
|
||||
photo_id: string;
|
||||
sort_order: number;
|
||||
};
|
||||
|
||||
const collectionSelect =
|
||||
"id,title,slug,description,cover_photo_id,published_at,created_at";
|
||||
|
||||
const photoSelect =
|
||||
"id,unsplash_id,slug,description,alt_description,width,height,color,color_family,likes,urls,user_info,photo_created_at,picked_at,oss_url";
|
||||
|
||||
function getPhotoUrl(photo: CollectionPhoto | null) {
|
||||
if (!photo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const urls = photo.urls as Record<string, string> | undefined;
|
||||
return (
|
||||
photo.oss_url ??
|
||||
urls?.regular ??
|
||||
urls?.small ??
|
||||
urls?.raw ??
|
||||
urls?.thumb ??
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
async function getPhotosById(photoIds: string[]) {
|
||||
if (photoIds.length === 0) {
|
||||
return new Map<string, CollectionPhoto>();
|
||||
}
|
||||
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
const { data, error } = await supabase
|
||||
.from("wg_gallery_photos")
|
||||
.select(photoSelect)
|
||||
.eq("is_picked", true)
|
||||
.in("id", photoIds);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return new Map(
|
||||
((data ?? []) as CollectionPhoto[]).map((photo) => [photo.id, photo]),
|
||||
);
|
||||
}
|
||||
|
||||
export async function listPublishedCollections({
|
||||
page,
|
||||
pageSize,
|
||||
userId,
|
||||
}: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
userId: string | null;
|
||||
}) {
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
const { data, error, count } = await supabase
|
||||
.from("wg_photo_collections")
|
||||
.select(collectionSelect, { count: "exact" })
|
||||
.eq("is_published", true)
|
||||
.order("published_at", { ascending: false, nullsFirst: false })
|
||||
.order("created_at", { ascending: false })
|
||||
.range((page - 1) * pageSize, page * pageSize - 1);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const collections = (data ?? []) as PublicCollectionRow[];
|
||||
const collectionIds = collections.map((collection) => collection.id);
|
||||
const coverPhotoIds = collections
|
||||
.map((collection) => collection.cover_photo_id)
|
||||
.filter((id): id is string => Boolean(id));
|
||||
|
||||
const { data: items, error: itemsError } =
|
||||
collectionIds.length > 0
|
||||
? await supabase
|
||||
.from("wg_photo_collection_items")
|
||||
.select("collection_id,photo_id")
|
||||
.in("collection_id", collectionIds)
|
||||
: { data: [], error: null };
|
||||
|
||||
if (itemsError) {
|
||||
throw itemsError;
|
||||
}
|
||||
|
||||
const photosById = await getPhotosById(coverPhotoIds);
|
||||
const favoritePhotoIds = await getFavoritePhotoIds({
|
||||
userId,
|
||||
photoIds: coverPhotoIds,
|
||||
});
|
||||
const itemRows = (items ?? []) as Array<{
|
||||
collection_id: string;
|
||||
photo_id: string;
|
||||
}>;
|
||||
const countByCollection = itemRows.reduce(
|
||||
(counts, item) => {
|
||||
counts[item.collection_id] = (counts[item.collection_id] ?? 0) + 1;
|
||||
return counts;
|
||||
},
|
||||
{} as Record<string, number>,
|
||||
);
|
||||
|
||||
return {
|
||||
data: collections.map((collection) => {
|
||||
const coverPhoto = collection.cover_photo_id
|
||||
? (photosById.get(collection.cover_photo_id) ?? null)
|
||||
: null;
|
||||
|
||||
return {
|
||||
id: collection.id,
|
||||
title: collection.title,
|
||||
slug: collection.slug,
|
||||
description: collection.description,
|
||||
coverPhoto: coverPhoto
|
||||
? mapPublicGalleryPhoto({
|
||||
photo: coverPhoto,
|
||||
favoritePhotoIds,
|
||||
})
|
||||
: null,
|
||||
coverUrl: getPhotoUrl(coverPhoto),
|
||||
photoCount: countByCollection[collection.id] ?? 0,
|
||||
publishedAt: collection.published_at,
|
||||
};
|
||||
}),
|
||||
total: count ?? 0,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPublishedCollectionBySlug({
|
||||
page,
|
||||
pageSize,
|
||||
slug,
|
||||
userId,
|
||||
}: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
slug: string;
|
||||
userId: string | null;
|
||||
}) {
|
||||
const supabase = createSupabaseServiceRoleClient();
|
||||
const { data: collection, error } = await supabase
|
||||
.from("wg_photo_collections")
|
||||
.select(collectionSelect)
|
||||
.eq("slug", slug)
|
||||
.eq("is_published", true)
|
||||
.maybeSingle<PublicCollectionRow>();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!collection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
data: items,
|
||||
error: itemsError,
|
||||
count,
|
||||
} = await supabase
|
||||
.from("wg_photo_collection_items")
|
||||
.select("photo_id,sort_order", { count: "exact" })
|
||||
.eq("collection_id", collection.id)
|
||||
.order("sort_order", { ascending: true })
|
||||
.order("created_at", { ascending: true })
|
||||
.range((page - 1) * pageSize, page * pageSize - 1);
|
||||
|
||||
if (itemsError) {
|
||||
throw itemsError;
|
||||
}
|
||||
|
||||
const itemRows = (items ?? []) as CollectionItemRow[];
|
||||
const photoIds = [
|
||||
...itemRows.map((item) => item.photo_id),
|
||||
...(collection.cover_photo_id ? [collection.cover_photo_id] : []),
|
||||
];
|
||||
const photosById = await getPhotosById(Array.from(new Set(photoIds)));
|
||||
const favoritePhotoIds = await getFavoritePhotoIds({
|
||||
userId,
|
||||
photoIds,
|
||||
});
|
||||
const coverPhoto = collection.cover_photo_id
|
||||
? (photosById.get(collection.cover_photo_id) ?? null)
|
||||
: null;
|
||||
|
||||
return {
|
||||
collection: {
|
||||
id: collection.id,
|
||||
title: collection.title,
|
||||
slug: collection.slug,
|
||||
description: collection.description,
|
||||
coverPhoto: coverPhoto
|
||||
? mapPublicGalleryPhoto({ photo: coverPhoto, favoritePhotoIds })
|
||||
: null,
|
||||
coverUrl: getPhotoUrl(coverPhoto),
|
||||
photoCount: count ?? 0,
|
||||
publishedAt: collection.published_at,
|
||||
},
|
||||
photos: itemRows
|
||||
.map((item) => photosById.get(item.photo_id))
|
||||
.filter((photo): photo is CollectionPhoto => Boolean(photo))
|
||||
.map((photo) => mapPublicGalleryPhoto({ photo, favoritePhotoIds })),
|
||||
total: count ?? 0,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user