added sharing and subscriptions

This commit is contained in:
2026-06-17 23:27:37 +02:00
parent 148e441425
commit 8a614cb1d1
16 changed files with 1019 additions and 26 deletions
+74
View File
@@ -7,11 +7,31 @@ export type List = {
name: string;
emoji: string | null;
description: string | null;
share_token: string | null;
position: number;
created_at: string;
updated_at: string;
};
export type SharedView = { list: List; items: Item[] };
export type Subscription = {
id: string;
kind: "list" | "item";
created_at: string;
list_id: string | null;
item_id: string | null;
title: string;
emoji: string | null;
share_token: string | null;
url: string | null;
image_url: string | null;
current_price: number | null;
currency: string | null;
in_stock: boolean | null;
target_price: number | null;
};
export type Item = {
id: string;
list_id: string;
@@ -64,6 +84,9 @@ export const listsApi = {
update: (id: string, b: Partial<NewList> & { position?: number }) =>
api.patch<List>(`/lists/${id}`, b),
remove: (id: string) => api.del<{ deleted: string }>(`/lists/${id}`),
share: (id: string) => api.post<List>(`/lists/${id}/share`, {}),
unshare: (id: string) => api.del<List>(`/lists/${id}/share`),
shared: (token: string) => api.get<SharedView>(`/shared/${token}`),
items: (listId: string) => api.get<Item[]>(`/lists/${listId}/items`),
addItem: (listId: string, b: NewItem) =>
@@ -75,6 +98,11 @@ export const listsApi = {
removeItem: (id: string) => api.del<{ deleted: string }>(`/items/${id}`),
refetch: (id: string) => api.post<Item>(`/items/${id}/refetch`, {}),
history: (id: string) => api.get<PricePoint[]>(`/items/${id}/history`),
subscriptions: () => api.get<Subscription[]>("/subscriptions"),
subscribe: (b: { list_id?: string; item_id?: string }) =>
api.post<{ id: string }>("/subscriptions", b),
unsubscribe: (id: string) => api.del<{ deleted: string }>(`/subscriptions/${id}`),
};
/** Reactive store for the user's lists. */
@@ -97,6 +125,52 @@ class ListsStore {
await listsApi.remove(id);
this.items = this.items.filter((l) => l.id !== id);
}
/** Swap in an updated list (e.g. after share/unshare). */
replace(list: List) {
const i = this.items.findIndex((l) => l.id === list.id);
if (i >= 0) this.items[i] = list;
}
}
export const lists = new ListsStore();
/** The current user's subscriptions, with helpers to toggle by list/item. */
class SubsStore {
items = $state<Subscription[]>([]);
loaded = $state(false);
async load() {
this.items = await listsApi.subscriptions();
this.loaded = true;
}
/** Existing subscription id for a list, or null. */
forList(listId: string): string | null {
return this.items.find((s) => s.list_id === listId)?.id ?? null;
}
/** Existing subscription id for an item, or null. */
forItem(itemId: string): string | null {
return this.items.find((s) => s.item_id === itemId)?.id ?? null;
}
async subscribeList(listId: string) {
if (this.forList(listId)) return;
await listsApi.subscribe({ list_id: listId });
await this.load();
}
async subscribeItem(itemId: string) {
if (this.forItem(itemId)) return;
await listsApi.subscribe({ item_id: itemId });
await this.load();
}
async unsubscribe(id: string) {
await listsApi.unsubscribe(id);
this.items = this.items.filter((s) => s.id !== id);
}
}
export const subs = new SubsStore();