|
| 1 | +export interface ExploreClip { |
| 2 | + id: string; |
| 3 | + userId: string; |
| 4 | + creatorUsername: string; |
| 5 | + title: string; |
| 6 | + thumbnail: string; |
| 7 | + score: number; |
| 8 | + style: string; |
| 9 | + duration: string; |
| 10 | + videoUrl: string; |
| 11 | + isPublic: boolean; |
| 12 | + shareId: string; |
| 13 | + createdAt: string; |
| 14 | +} |
| 15 | + |
| 16 | +export interface UserPrivacySettings { |
| 17 | + userId: string; |
| 18 | + exploreOptIn: boolean; |
| 19 | + showUsername: boolean; |
| 20 | +} |
| 21 | + |
| 22 | +class PrivacyStore { |
| 23 | + private settings = new Map<string, UserPrivacySettings>(); |
| 24 | + |
| 25 | + get(userId: string): UserPrivacySettings { |
| 26 | + return ( |
| 27 | + this.settings.get(userId) ?? { |
| 28 | + userId, |
| 29 | + exploreOptIn: false, |
| 30 | + showUsername: true, |
| 31 | + } |
| 32 | + ); |
| 33 | + } |
| 34 | + |
| 35 | + update(userId: string, update: Partial<Pick<UserPrivacySettings, "exploreOptIn" | "showUsername">>): UserPrivacySettings { |
| 36 | + const current = this.get(userId); |
| 37 | + const next = { ...current, ...update, userId }; |
| 38 | + this.settings.set(userId, next); |
| 39 | + return next; |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +export const privacyStore = new PrivacyStore(); |
| 44 | + |
| 45 | +class ExploreStore { |
| 46 | + private clips: ExploreClip[] = []; |
| 47 | + |
| 48 | + constructor() { |
| 49 | + this.seed(); |
| 50 | + } |
| 51 | + |
| 52 | + private seed() { |
| 53 | + const creators = ["viralVibes", "clipMaster", "trendSetter", "contentKing", "shortFormPro"]; |
| 54 | + const styles = ["Bold & Dynamic", "Minimalist", "Emoji-Rich", "Subtitles Only"]; |
| 55 | + const thumbs = ["/projects/thumb1.png", "/projects/thumb2.png", "/projects/thumb3.png"]; |
| 56 | + |
| 57 | + this.clips = Array.from({ length: 40 }, (_, i) => ({ |
| 58 | + id: `explore-clip-${i + 1}`, |
| 59 | + userId: `creator-${(i % 5) + 1}`, |
| 60 | + creatorUsername: creators[i % creators.length], |
| 61 | + title: `Trending Clip #${String(i + 1).padStart(2, "0")}`, |
| 62 | + thumbnail: thumbs[i % thumbs.length], |
| 63 | + score: 95 - (i % 30), |
| 64 | + style: styles[i % styles.length], |
| 65 | + duration: `00:${String(30 + (i % 30)).padStart(2, "0")}`, |
| 66 | + videoUrl: "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4", |
| 67 | + isPublic: true, |
| 68 | + shareId: `explore-clip-${i + 1}-share`, |
| 69 | + createdAt: new Date(Date.now() - i * 3600000).toISOString(), |
| 70 | + })); |
| 71 | + } |
| 72 | + |
| 73 | + getTrending(options: { |
| 74 | + cursor?: string; |
| 75 | + limit?: number; |
| 76 | + privacyFilter?: (clip: ExploreClip) => ExploreClip | null; |
| 77 | + }): { clips: ExploreClip[]; nextCursor: string | null } { |
| 78 | + const limit = options.limit ?? 20; |
| 79 | + let filtered = this.clips.filter((c) => c.isPublic); |
| 80 | + |
| 81 | + if (options.privacyFilter) { |
| 82 | + filtered = filtered |
| 83 | + .map(options.privacyFilter) |
| 84 | + .filter((c): c is ExploreClip => c !== null); |
| 85 | + } |
| 86 | + |
| 87 | + filtered.sort((a, b) => b.score - a.score); |
| 88 | + |
| 89 | + let startIndex = 0; |
| 90 | + if (options.cursor) { |
| 91 | + const cursorIndex = filtered.findIndex((c) => c.id === options.cursor); |
| 92 | + startIndex = cursorIndex >= 0 ? cursorIndex + 1 : 0; |
| 93 | + } |
| 94 | + |
| 95 | + const page = filtered.slice(startIndex, startIndex + limit); |
| 96 | + const nextCursor = |
| 97 | + startIndex + limit < filtered.length ? page[page.length - 1]?.id ?? null : null; |
| 98 | + |
| 99 | + return { clips: page, nextCursor }; |
| 100 | + } |
| 101 | + |
| 102 | + getByShareId(shareId: string): ExploreClip | undefined { |
| 103 | + return this.clips.find((c) => c.shareId === shareId && c.isPublic); |
| 104 | + } |
| 105 | + |
| 106 | + getById(id: string): ExploreClip | undefined { |
| 107 | + return this.clips.find((c) => c.id === id && c.isPublic); |
| 108 | + } |
| 109 | +} |
| 110 | + |
| 111 | +export const exploreStore = new ExploreStore(); |
0 commit comments