158 lines
3.4 KiB
TypeScript
158 lines
3.4 KiB
TypeScript
/**
|
|
* rFeeds Automerge document schemas.
|
|
*
|
|
* Granularity: one Automerge document per space.
|
|
* DocId format: {space}:rfeeds:data
|
|
*/
|
|
|
|
import type { DocSchema } from '../../shared/local-first/document';
|
|
|
|
// ── Feed source ──
|
|
|
|
export interface FeedSource {
|
|
id: string;
|
|
url: string;
|
|
name: string;
|
|
color: string;
|
|
enabled: boolean;
|
|
lastFetchedAt: number;
|
|
lastError: string | null;
|
|
itemCount: number;
|
|
intervalMs: number; // default 300_000 (5 min)
|
|
addedBy: string; // DID of who added it
|
|
addedAt: number;
|
|
}
|
|
|
|
// ── Imported feed item ──
|
|
|
|
export interface FeedItem {
|
|
id: string;
|
|
sourceId: string;
|
|
guid: string;
|
|
title: string;
|
|
url: string;
|
|
summary: string; // 500 chars max, HTML stripped
|
|
author: string;
|
|
publishedAt: number;
|
|
importedAt: number;
|
|
reshared: boolean; // included in outbound Atom feed
|
|
}
|
|
|
|
// ── Manual post ──
|
|
|
|
export interface ManualPost {
|
|
id: string;
|
|
content: string;
|
|
authorDid: string;
|
|
authorName: string;
|
|
createdAt: number;
|
|
updatedAt: number;
|
|
}
|
|
|
|
// ── Activity cache (Phase 2) ──
|
|
|
|
export interface ActivityCacheEntry {
|
|
id: string; // deterministic: `${moduleId}:${space}:${itemId}`
|
|
moduleId: string; // 'rcal' | 'rtasks' | 'rdocs' | etc
|
|
itemType: string; // 'event' | 'task' | 'note' | 'thread' | 'file' | etc
|
|
title: string;
|
|
summary: string; // 280 char max
|
|
url: string; // e.g. https://demo.rspace.online/rcal
|
|
author: string;
|
|
publishedAt: number;
|
|
reshared: boolean;
|
|
syncedAt: number;
|
|
}
|
|
|
|
export interface ActivityModuleConfig {
|
|
enabled: boolean;
|
|
label: string;
|
|
color: string;
|
|
}
|
|
|
|
// ── User feed profiles (Phase 2) ──
|
|
|
|
export interface UserFeedProfile {
|
|
did: string;
|
|
displayName: string;
|
|
bio: string;
|
|
publishModules: string[]; // which moduleIds appear in personal feed
|
|
createdAt: number;
|
|
updatedAt: number;
|
|
}
|
|
|
|
// ── Settings ──
|
|
|
|
export interface FeedsSettings {
|
|
feedTitle: string;
|
|
feedDescription: string;
|
|
activityModules: Record<string, ActivityModuleConfig>;
|
|
}
|
|
|
|
// ── Document root ──
|
|
|
|
export interface FeedsDoc {
|
|
meta: {
|
|
module: string;
|
|
collection: string;
|
|
version: number;
|
|
spaceSlug: string;
|
|
createdAt: number;
|
|
};
|
|
sources: Record<string, FeedSource>;
|
|
items: Record<string, FeedItem>;
|
|
posts: Record<string, ManualPost>;
|
|
activityCache: Record<string, ActivityCacheEntry>;
|
|
userProfiles: Record<string, UserFeedProfile>;
|
|
settings: FeedsSettings;
|
|
}
|
|
|
|
// ── Schema registration ──
|
|
|
|
export const feedsSchema: DocSchema<FeedsDoc> = {
|
|
module: 'rfeeds',
|
|
collection: 'data',
|
|
version: 2,
|
|
init: (): FeedsDoc => ({
|
|
meta: {
|
|
module: 'rfeeds',
|
|
collection: 'data',
|
|
version: 2,
|
|
spaceSlug: '',
|
|
createdAt: Date.now(),
|
|
},
|
|
sources: {},
|
|
items: {},
|
|
posts: {},
|
|
activityCache: {},
|
|
userProfiles: {},
|
|
settings: {
|
|
feedTitle: '',
|
|
feedDescription: '',
|
|
activityModules: {},
|
|
},
|
|
}),
|
|
migrate: (doc: any): any => {
|
|
if (!doc.activityCache) doc.activityCache = {};
|
|
if (!doc.userProfiles) doc.userProfiles = {};
|
|
if (!doc.settings.activityModules) doc.settings.activityModules = {};
|
|
if (doc.meta) doc.meta.version = 2;
|
|
return doc;
|
|
},
|
|
};
|
|
|
|
// ── Helpers ──
|
|
|
|
export function feedsDocId(space: string) {
|
|
return `${space}:rfeeds:data` as const;
|
|
}
|
|
|
|
/** Max items retained per source */
|
|
export const MAX_ITEMS_PER_SOURCE = 200;
|
|
|
|
/** Max activity cache entries */
|
|
export const MAX_ACTIVITY_CACHE = 500;
|
|
|
|
/** Default sync interval */
|
|
export const DEFAULT_SYNC_INTERVAL_MS = 5 * 60 * 1000;
|