55622d0a1a
The bookmarks list can now render as 1 (default), 2, or 3 columns instead of always being a single-column list, admin-configurable via a pill picker on the Widgets tab. Column count lives in widget_kv (same idiom as weather's config) and rides along in the public GET /api/widget/bookmarks response so the sidebar picks it up without a separate request.
68 lines
2.5 KiB
TypeScript
68 lines
2.5 KiB
TypeScript
import { getBackendUrl } from './config';
|
|
import type { MergedArticle, Tag, TrackedEventPublic, Category, Weather, StockTicker, BookmarksFeed, Poe2Data, WidgetsEnabled } from './types';
|
|
|
|
async function get<T>(path: string, fetchFn: typeof fetch = fetch): Promise<T> {
|
|
// credentials: 'include' so the private-access cookie (see lib/privateAccess.ts)
|
|
// is sent cross-origin to the backend, revealing private categories/articles to
|
|
// anyone who's logged in — without it every request would look unauthenticated.
|
|
const res = await fetchFn(`${getBackendUrl()}${path}`, { credentials: 'include' });
|
|
if (!res.ok) throw new Error(`Request failed: ${path} (${res.status})`);
|
|
return res.json();
|
|
}
|
|
|
|
export interface FeedParams {
|
|
category?: string;
|
|
geo?: string;
|
|
eventId?: string;
|
|
tag?: string;
|
|
before?: string;
|
|
limit?: number;
|
|
}
|
|
|
|
export function getFeed(params: FeedParams = {}, fetchFn?: typeof fetch): Promise<MergedArticle[]> {
|
|
const qs = new URLSearchParams(
|
|
Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined).map(([k, v]) => [k, String(v)]))
|
|
).toString();
|
|
return get<MergedArticle[]>(`/api/feed${qs ? `?${qs}` : ''}`, fetchFn);
|
|
}
|
|
|
|
export function getArticle(id: string, fetchFn?: typeof fetch): Promise<MergedArticle> {
|
|
return get<MergedArticle>(`/api/article/${id}`, fetchFn);
|
|
}
|
|
|
|
export function getTags(fetchFn?: typeof fetch): Promise<Tag[]> {
|
|
return get<Tag[]>('/api/tags', fetchFn);
|
|
}
|
|
|
|
export function getTagBySlug(slug: string, fetchFn?: typeof fetch): Promise<Tag> {
|
|
return get<Tag>(`/api/tag/${slug}`, fetchFn);
|
|
}
|
|
|
|
export function getEvents(fetchFn?: typeof fetch): Promise<TrackedEventPublic[]> {
|
|
return get<TrackedEventPublic[]>('/api/events', fetchFn);
|
|
}
|
|
|
|
export function getCategories(fetchFn?: typeof fetch): Promise<Category[]> {
|
|
return get<Category[]>('/api/categories', fetchFn);
|
|
}
|
|
|
|
export function getWeather(fetchFn?: typeof fetch): Promise<Weather> {
|
|
return get<Weather>('/api/widget/weather', fetchFn);
|
|
}
|
|
|
|
export function getStocks(fetchFn?: typeof fetch): Promise<StockTicker[]> {
|
|
return get<StockTicker[]>('/api/widget/stocks', fetchFn);
|
|
}
|
|
|
|
export function getBookmarks(fetchFn?: typeof fetch): Promise<BookmarksFeed> {
|
|
return get<BookmarksFeed>('/api/widget/bookmarks', fetchFn);
|
|
}
|
|
|
|
export function getPoe2(fetchFn?: typeof fetch): Promise<Poe2Data> {
|
|
return get<Poe2Data>('/api/widget/poe2', fetchFn);
|
|
}
|
|
|
|
export function getWidgetsEnabled(fetchFn?: typeof fetch): Promise<WidgetsEnabled> {
|
|
return get<WidgetsEnabled>('/api/widgets', fetchFn);
|
|
}
|