6 changed files with 215 additions and 3 deletions
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
import { ofetch } from "ofetch"; |
||||
|
||||
import { getAuthHeaders } from "@/backend/accounts/auth"; |
||||
import { BookmarkResponse } from "@/backend/accounts/user"; |
||||
import { AccountWithToken } from "@/stores/auth"; |
||||
|
||||
export interface BookmarkInput { |
||||
title: string; |
||||
year: number; |
||||
poster?: string; |
||||
type: string; |
||||
tmdbId: string; |
||||
} |
||||
|
||||
export async function addBookmark( |
||||
url: string, |
||||
account: AccountWithToken, |
||||
input: BookmarkInput |
||||
) { |
||||
return ofetch<BookmarkResponse>( |
||||
`/users/${account.userId}/bookmarks/${input.tmdbId}`, |
||||
{ |
||||
method: "POST", |
||||
headers: getAuthHeaders(account.token), |
||||
baseURL: url, |
||||
body: { |
||||
meta: input, |
||||
}, |
||||
} |
||||
); |
||||
} |
||||
|
||||
export async function removeBookmark( |
||||
url: string, |
||||
account: AccountWithToken, |
||||
id: string |
||||
) { |
||||
return ofetch<{ tmdbId: string }>( |
||||
`/users/${account.userId}/bookmarks/${id}`, |
||||
{ |
||||
method: "DELETE", |
||||
headers: getAuthHeaders(account.token), |
||||
baseURL: url, |
||||
} |
||||
); |
||||
} |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
import { useEffect } from "react"; |
||||
|
||||
import { addBookmark, removeBookmark } from "@/backend/accounts/bookmarks"; |
||||
import { useBackendUrl } from "@/hooks/auth/useBackendUrl"; |
||||
import { AccountWithToken, useAuthStore } from "@/stores/auth"; |
||||
import { BookmarkUpdateItem, useBookmarkStore } from "@/stores/bookmarks"; |
||||
|
||||
const syncIntervalMs = 5 * 1000; |
||||
|
||||
async function syncBookmarks( |
||||
items: BookmarkUpdateItem[], |
||||
finish: (id: string) => void, |
||||
url: string, |
||||
account: AccountWithToken | null |
||||
) { |
||||
for (const item of items) { |
||||
// complete it beforehand so it doesn't get handled while in progress
|
||||
finish(item.id); |
||||
|
||||
if (!account) return; // not logged in, dont sync to server
|
||||
|
||||
try { |
||||
if (item.action === "delete") { |
||||
await removeBookmark(url, account, item.tmdbId); |
||||
continue; |
||||
} |
||||
|
||||
if (item.action === "add") { |
||||
await addBookmark(url, account, { |
||||
poster: item.poster, |
||||
title: item.title ?? "", |
||||
tmdbId: item.tmdbId, |
||||
type: item.type ?? "", |
||||
year: item.year ?? NaN, |
||||
}); |
||||
continue; |
||||
} |
||||
} catch (err) { |
||||
console.error( |
||||
`Failed to sync bookmark: ${item.tmdbId} - ${item.action}`, |
||||
err |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
export function BookmarkSyncer() { |
||||
const clearUpdateQueue = useBookmarkStore((s) => s.clearUpdateQueue); |
||||
const removeUpdateItem = useBookmarkStore((s) => s.removeUpdateItem); |
||||
const url = useBackendUrl(); |
||||
|
||||
// when booting for the first time, clear update queue.
|
||||
// we dont want to process persisted update items
|
||||
useEffect(() => { |
||||
clearUpdateQueue(); |
||||
}, [clearUpdateQueue]); |
||||
|
||||
useEffect(() => { |
||||
const interval = setInterval(() => { |
||||
(async () => { |
||||
const state = useBookmarkStore.getState(); |
||||
const user = useAuthStore.getState(); |
||||
await syncBookmarks( |
||||
state.updateQueue, |
||||
removeUpdateItem, |
||||
url, |
||||
user.account |
||||
); |
||||
})(); |
||||
}, syncIntervalMs); |
||||
|
||||
return () => { |
||||
clearInterval(interval); |
||||
}; |
||||
}, [removeUpdateItem, url]); |
||||
|
||||
return null; |
||||
} |
Loading…
Reference in new issue