@openrails/contentkit-ui 0.93.1 → 0.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -5
- package/dist/client/browser-reader-preferences.d.ts +38 -0
- package/dist/client/browser-reader-preferences.js +172 -0
- package/dist/client/browser-reader-preferences.js.map +1 -0
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +27 -25
- package/dist/client/client.js.map +1 -1
- package/dist/client/discovery.d.ts +14 -1
- package/dist/client/discovery.js +36 -22
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/generated/routes.d.ts +33 -7
- package/dist/client/generated/routes.js +57 -2
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +15 -0
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/http.d.ts +18 -1
- package/dist/client/http.js +67 -26
- package/dist/client/http.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/reader-preferences.d.ts +46 -0
- package/dist/client/reader-preferences.js +147 -0
- package/dist/client/reader-preferences.js.map +1 -0
- package/dist/client.js +17 -15
- package/dist/components/content/card.d.ts +21 -5
- package/dist/components/content/card.js +80 -67
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/daily-releases.d.ts +15 -0
- package/dist/components/content/daily-releases.js +167 -0
- package/dist/components/content/daily-releases.js.map +1 -0
- package/dist/components/content/filters.d.ts +1 -1
- package/dist/components/content/filters.js +1 -5
- package/dist/components/content/filters.js.map +1 -1
- package/dist/components/content/grid.d.ts +14 -1
- package/dist/components/content/grid.js +58 -32
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +4 -2
- package/dist/components/content/popular.d.ts +11 -0
- package/dist/components/content/popular.js +49 -0
- package/dist/components/content/popular.js.map +1 -0
- package/dist/components/content/reader-settings.d.ts +1 -1
- package/dist/components/content/reader-settings.js +67 -44
- package/dist/components/content/reader-settings.js.map +1 -1
- package/dist/index.js +22 -20
- package/dist/locales/discovery/de.js +6 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +6 -0
- package/dist/locales/discovery/en.js +6 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +6 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +6 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +6 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +6 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +6 -0
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/discovery.d.ts +14 -4
- package/dist/react/discovery.js +196 -149
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/index.d.ts +2 -1
- package/dist/react/provider.d.ts +4 -1
- package/dist/react/provider.js +38 -31
- package/dist/react/provider.js.map +1 -1
- package/dist/react/reader-preferences.d.ts +13 -0
- package/dist/react/reader-preferences.js +15 -0
- package/dist/react/reader-preferences.js.map +1 -0
- package/dist/react/resources.js +11 -11
- package/dist/react/resources.js.map +1 -1
- package/dist/react.js +10 -9
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-preferences.js","names":[],"sources":["../../src/client/reader-preferences.ts"],"sourcesContent":["import { ContentKitError, toContentKitError } from \"./errors.js\";\nimport type { ContentFilters, ContentFiltersUpdate } from \"./generated/wire.js\";\nimport type { Http } from \"./http.js\";\nimport { call } from \"./route.js\";\n\n/** The host's account-owned preferred language, without a route fallback. */\nexport interface LanguagePreference {\n load(accountId: string): Promise<string | null | undefined>;\n save(language: string, accountId: string): Promise<void>;\n}\n\nexport interface ReaderPreferenceState {\n language?: string;\n loading: boolean;\n saving: boolean;\n pending: boolean;\n error?: ContentKitError;\n}\n\n/** Guest storage and explicit, retryable registration inheritance. */\nexport class ReaderPreferencesClient {\n readonly browser;\n private viewer?: string | null;\n private adapter?: LanguagePreference;\n private accountLanguage?: string;\n private loading = false;\n private saving = false;\n private failure?: ContentKitError;\n private generation = 0;\n private syncing?: Promise<void>;\n private syncingGeneration = -1;\n private state: ReaderPreferenceState = Object.freeze({ loading: false, saving: false, pending: false });\n private readonly listeners = new Set<() => void>();\n private stopBrowser?: () => void;\n private guestValue: string;\n\n constructor(private readonly http: Http) {\n this.browser = http.browserPreferences;\n this.guestValue = JSON.stringify(this.browser.snapshot());\n }\n\n readonly snapshot = (): ReaderPreferenceState => this.state;\n readonly subscribe = (listener: () => void): (() => void) => {\n if (!this.listeners.size) {\n this.stopBrowser = this.browser.subscribe(this.browserChanged);\n if (typeof window !== \"undefined\") window.addEventListener(\"online\", this.retryOnline);\n }\n this.listeners.add(listener);\n this.refresh();\n return () => {\n this.listeners.delete(listener);\n if (!this.listeners.size) {\n this.stopBrowser?.();\n if (typeof window !== \"undefined\") window.removeEventListener(\"online\", this.retryOnline);\n }\n };\n };\n\n configure(viewer: string | null | undefined, adapter?: LanguagePreference): void {\n this.http.setViewer(viewer);\n if (viewer === this.viewer && adapter === this.adapter) return;\n this.viewer = viewer;\n this.adapter = adapter;\n const generation = ++this.generation;\n this.accountLanguage = undefined;\n this.failure = undefined;\n this.saving = false;\n this.loading = !!viewer && !!adapter;\n this.refresh();\n if (viewer) void this.activate(generation).catch(() => {});\n }\n\n async setLanguage(language: string): Promise<void> {\n try { language = Intl.getCanonicalLocales(language)[0]!; }\n catch { throw new ContentKitError(\"invalid_request\", \"Invalid preferred language.\"); }\n if (!language) throw new ContentKitError(\"invalid_request\", \"Invalid preferred language.\");\n const account = this.viewer;\n if (account == null) { this.browser.setLanguage(language); this.refresh(); return; }\n if (!this.adapter) throw new Error(\"contentkit: account language preference adapter is required\");\n const generation = this.generation;\n this.saving = true;\n this.failure = undefined;\n this.refresh();\n try {\n await this.adapter.save(language, account);\n if (generation !== this.generation) throw new ContentKitError(\"aborted\", \"The account changed.\");\n this.accountLanguage = language;\n this.browser.complete(account, \"language\");\n } catch (error) {\n if (generation === this.generation) this.failure = toContentKitError(error);\n throw error;\n } finally {\n if (generation === this.generation) { this.saving = false; this.refresh(); }\n }\n }\n\n /** Call only after a confirmed registration event, never on ordinary login. */\n async inheritAfterRegistration(account: string): Promise<void> {\n this.browser.register(account);\n this.refresh();\n return this.viewer === account ? this.retryInheritance() : Promise.resolve();\n }\n\n async retry(): Promise<void> {\n if (!this.viewer) return;\n const generation = this.generation;\n this.failure = undefined;\n this.loading = true;\n this.refresh();\n await this.activate(generation);\n if (generation === this.generation && this.failure) throw this.failure;\n }\n\n retryInheritance(): Promise<void> {\n if (!this.viewer || !this.browser.pending(this.viewer)) return Promise.resolve();\n if (this.syncing && this.syncingGeneration === this.generation) return this.syncing;\n const account = this.viewer, generation = this.generation;\n const task = this.inherit(account, generation).finally(() => {\n if (this.syncing === task) this.syncing = undefined;\n this.refresh();\n });\n this.syncing = task;\n this.syncingGeneration = generation;\n this.refresh();\n return this.syncing;\n }\n\n private async activate(generation: number): Promise<void> {\n try {\n await this.retryInheritance();\n if (generation !== this.generation) return;\n const language = this.viewer ? await this.adapter?.load(this.viewer) : undefined;\n if (generation === this.generation) this.accountLanguage = language ?? undefined;\n } catch (error) {\n if (generation === this.generation) this.failure = toContentKitError(error);\n } finally {\n if (generation === this.generation) { this.loading = false; this.refresh(); }\n }\n }\n\n private async inherit(account: string, generation: number): Promise<void> {\n this.failure = undefined;\n try {\n let pending = this.browser.pending(account)!;\n if (!pending.languageDone) {\n if (!this.adapter) throw new Error(\"contentkit: account language preference adapter is required\");\n const language = (await this.adapter.load(account)) ?? null;\n if (generation !== this.generation) return;\n const live = this.browser.pending(account);\n if (live && !live.languageDone) {\n if (!live.languageObserved) this.browser.observeLanguage(account, language);\n pending = this.browser.pending(account)!;\n // The initial registration default may be non-null. A later different\n // choice wins over a retry of the captured guest preference.\n const changedLater = language !== pending.initialLanguage && language !== pending.language;\n if (!changedLater && language !== pending.language && pending.language) await this.adapter.save(pending.language, account);\n if (generation !== this.generation) return;\n this.accountLanguage = changedLater ? language ?? undefined : pending.language;\n this.browser.complete(account, \"language\");\n }\n }\n pending = this.browser.pending(account)!;\n if (pending && !pending.filtersDone) {\n if (generation !== this.generation) return;\n const inventory = await this.http.request<ContentFilters>(`${this.http.mount(\"discovery\")}/content-filters`, { guestPreferences: false });\n if (generation !== this.generation) return;\n const known = new Set(inventory.filters.map((f) => f.entity.id));\n await call<\"POST\", \"/me/content-filters/initialize\", ContentFilters>(this.http, \"POST\", \"/me/content-filters/initialize\", {\n body: { account_id: account, filters: pending.filters.filter((f) => known.has(f.group_id)), include_deleted: false } satisfies ContentFiltersUpdate & { account_id: string },\n });\n if (generation !== this.generation) return;\n this.browser.complete(account, \"filters\");\n }\n if (generation === this.generation) this.http.captureChanges()({ type: \"contentFilters.changed\" });\n } catch (error) {\n if (generation === this.generation) this.failure = toContentKitError(error);\n throw error;\n }\n }\n\n private readonly retryOnline = (): void => {\n if (this.state.pending || this.state.error) void this.retry().catch(() => {});\n };\n private readonly browserChanged = (): void => {\n const value = JSON.stringify(this.browser.snapshot());\n if (this.viewer === null && value !== this.guestValue) this.http.captureChanges()({ type: \"contentFilters.changed\" });\n this.guestValue = value;\n this.refresh();\n };\n\n private refresh(): void {\n const next: ReaderPreferenceState = {\n language: this.viewer == null ? this.browser.snapshot().language : this.accountLanguage,\n loading: this.loading, saving: this.saving,\n pending: !!this.viewer && !!this.browser.pending(this.viewer), error: this.failure,\n };\n if (next.language === this.state.language && next.loading === this.state.loading && next.saving === this.state.saving && next.pending === this.state.pending && next.error === this.state.error) return;\n this.state = Object.freeze(next);\n for (const listener of this.listeners) { try { listener(); } catch { /* Observers do not own persistence. */ } }\n }\n}\n"],"mappings":";;;AAoBA,IAAa,IAAb,MAAqC;CAgBN;CAf7B;CACA;CACA;CACA;CACA,UAAkB;CAClB,SAAiB;CACjB;CACA,aAAqB;CACrB;CACA,oBAA4B;CAC5B,QAAuC,OAAO,OAAO;EAAE,SAAS;EAAO,QAAQ;EAAO,SAAS;CAAM,CAAC;CACtG,4BAA6B,IAAI,IAAgB;CACjD;CACA;CAEA,YAAY,GAA6B;EAEvC,AAF2B,KAAA,OAAA,GAC3B,KAAK,UAAU,EAAK,oBACpB,KAAK,aAAa,KAAK,UAAU,KAAK,QAAQ,SAAS,CAAC;CAC1D;CAEA,iBAAiD,KAAK;CACtD,aAAsB,OACf,KAAK,UAAU,SAClB,KAAK,cAAc,KAAK,QAAQ,UAAU,KAAK,cAAc,GACzD,OAAO,SAAW,OAAa,OAAO,iBAAiB,UAAU,KAAK,WAAW,IAEvF,KAAK,UAAU,IAAI,CAAQ,GAC3B,KAAK,QAAQ,SACA;EAEX,AADA,KAAK,UAAU,OAAO,CAAQ,GACzB,KAAK,UAAU,SAClB,KAAK,cAAc,GACf,OAAO,SAAW,OAAa,OAAO,oBAAoB,UAAU,KAAK,WAAW;CAE5F;CAGF,UAAU,GAAmC,GAAoC;EAE/E,IADA,KAAK,KAAK,UAAU,CAAM,GACtB,MAAW,KAAK,UAAU,MAAY,KAAK,SAAS;EAExD,AADA,KAAK,SAAS,GACd,KAAK,UAAU;EACf,IAAM,IAAa,EAAE,KAAK;EAM1B,AALA,KAAK,kBAAkB,KAAA,GACvB,KAAK,UAAU,KAAA,GACf,KAAK,SAAS,IACd,KAAK,UAAU,CAAC,CAAC,KAAU,CAAC,CAAC,GAC7B,KAAK,QAAQ,GACT,KAAQ,KAAU,SAAS,CAAU,CAAC,CAAC,YAAY,CAAC,CAAC;CAC3D;CAEA,MAAM,YAAY,GAAiC;EACjD,IAAI;GAAE,IAAW,KAAK,oBAAoB,CAAQ,CAAC,CAAC;EAAK,QACnD;GAAE,MAAM,IAAI,EAAgB,mBAAmB,6BAA6B;EAAG;EACrF,IAAI,CAAC,GAAU,MAAM,IAAI,EAAgB,mBAAmB,6BAA6B;EACzF,IAAM,IAAU,KAAK;EACrB,IAAI,KAAW,MAAM;GAAsC,AAApC,KAAK,QAAQ,YAAY,CAAQ,GAAG,KAAK,QAAQ;GAAG;EAAQ;EACnF,IAAI,CAAC,KAAK,SAAS,MAAU,MAAM,6DAA6D;EAChG,IAAM,IAAa,KAAK;EAGxB,AAFA,KAAK,SAAS,IACd,KAAK,UAAU,KAAA,GACf,KAAK,QAAQ;EACb,IAAI;GAEF,IADA,MAAM,KAAK,QAAQ,KAAK,GAAU,CAAO,GACrC,MAAe,KAAK,YAAY,MAAM,IAAI,EAAgB,WAAW,sBAAsB;GAE/F,AADA,KAAK,kBAAkB,GACvB,KAAK,QAAQ,SAAS,GAAS,UAAU;EAC3C,SAAS,GAAO;GAEd,MADI,MAAe,KAAK,eAAY,KAAK,UAAU,EAAkB,CAAK,IACpE;EACR,UAAU;GACR,AAAI,MAAe,KAAK,eAAc,KAAK,SAAS,IAAO,KAAK,QAAQ;EAC1E;CACF;CAGA,MAAM,yBAAyB,GAAgC;EAG7D,OAFA,KAAK,QAAQ,SAAS,CAAO,GAC7B,KAAK,QAAQ,GACN,KAAK,WAAW,IAAU,KAAK,iBAAiB,IAAI,QAAQ,QAAQ;CAC7E;CAEA,MAAM,QAAuB;EAC3B,IAAI,CAAC,KAAK,QAAQ;EAClB,IAAM,IAAa,KAAK;EAKxB,IAJA,KAAK,UAAU,KAAA,GACf,KAAK,UAAU,IACf,KAAK,QAAQ,GACb,MAAM,KAAK,SAAS,CAAU,GAC1B,MAAe,KAAK,cAAc,KAAK,SAAS,MAAM,KAAK;CACjE;CAEA,mBAAkC;EAChC,IAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QAAQ,QAAQ,KAAK,MAAM,GAAG,OAAO,QAAQ,QAAQ;EAC/E,IAAI,KAAK,WAAW,KAAK,sBAAsB,KAAK,YAAY,OAAO,KAAK;EAC5E,IAAM,IAAU,KAAK,QAAQ,IAAa,KAAK,YACzC,IAAO,KAAK,QAAQ,GAAS,CAAU,CAAC,CAAC,cAAc;GAE3D,AADI,KAAK,YAAY,MAAM,KAAK,UAAU,KAAA,IAC1C,KAAK,QAAQ;EACf,CAAC;EAID,OAHA,KAAK,UAAU,GACf,KAAK,oBAAoB,GACzB,KAAK,QAAQ,GACN,KAAK;CACd;CAEA,MAAc,SAAS,GAAmC;EACxD,IAAI;GAEF,IADA,MAAM,KAAK,iBAAiB,GACxB,MAAe,KAAK,YAAY;GACpC,IAAM,IAAW,KAAK,SAAS,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,IAAI,KAAA;GACvE,AAAI,MAAe,KAAK,eAAY,KAAK,kBAAkB,KAAY,KAAA;EACzE,SAAS,GAAO;GACd,AAAI,MAAe,KAAK,eAAY,KAAK,UAAU,EAAkB,CAAK;EAC5E,UAAU;GACR,AAAI,MAAe,KAAK,eAAc,KAAK,UAAU,IAAO,KAAK,QAAQ;EAC3E;CACF;CAEA,MAAc,QAAQ,GAAiB,GAAmC;EACxE,KAAK,UAAU,KAAA;EACf,IAAI;GACF,IAAI,IAAU,KAAK,QAAQ,QAAQ,CAAO;GAC1C,IAAI,CAAC,EAAQ,cAAc;IACzB,IAAI,CAAC,KAAK,SAAS,MAAU,MAAM,6DAA6D;IAChG,IAAM,IAAY,MAAM,KAAK,QAAQ,KAAK,CAAO,KAAM;IACvD,IAAI,MAAe,KAAK,YAAY;IACpC,IAAM,IAAO,KAAK,QAAQ,QAAQ,CAAO;IACzC,IAAI,KAAQ,CAAC,EAAK,cAAc;KAE9B,AADK,EAAK,oBAAkB,KAAK,QAAQ,gBAAgB,GAAS,CAAQ,GAC1E,IAAU,KAAK,QAAQ,QAAQ,CAAO;KAGtC,IAAM,IAAe,MAAa,EAAQ,mBAAmB,MAAa,EAAQ;KAElF,IADI,CAAC,KAAgB,MAAa,EAAQ,YAAY,EAAQ,YAAU,MAAM,KAAK,QAAQ,KAAK,EAAQ,UAAU,CAAO,GACrH,MAAe,KAAK,YAAY;KAEpC,AADA,KAAK,kBAAkB,IAAe,KAAY,KAAA,IAAY,EAAQ,UACtE,KAAK,QAAQ,SAAS,GAAS,UAAU;IAC3C;GACF;GAEA,IADA,IAAU,KAAK,QAAQ,QAAQ,CAAO,GAClC,KAAW,CAAC,EAAQ,aAAa;IACnC,IAAI,MAAe,KAAK,YAAY;IACpC,IAAM,IAAY,MAAM,KAAK,KAAK,QAAwB,GAAG,KAAK,KAAK,MAAM,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,GAAM,CAAC;IACxI,IAAI,MAAe,KAAK,YAAY;IACpC,IAAM,IAAQ,IAAI,IAAI,EAAU,QAAQ,KAAK,MAAM,EAAE,OAAO,EAAE,CAAC;IAI/D,IAHA,MAAM,EAA+D,KAAK,MAAM,QAAQ,kCAAkC,EACxH,MAAM;KAAE,YAAY;KAAS,SAAS,EAAQ,QAAQ,QAAQ,MAAM,EAAM,IAAI,EAAE,QAAQ,CAAC;KAAG,iBAAiB;IAAM,EACrH,CAAC,GACG,MAAe,KAAK,YAAY;IACpC,KAAK,QAAQ,SAAS,GAAS,SAAS;GAC1C;GACA,AAAI,MAAe,KAAK,cAAY,KAAK,KAAK,eAAe,CAAC,CAAC,EAAE,MAAM,yBAAyB,CAAC;EACnG,SAAS,GAAO;GAEd,MADI,MAAe,KAAK,eAAY,KAAK,UAAU,EAAkB,CAAK,IACpE;EACR;CACF;CAEA,oBAA2C;EACzC,CAAI,KAAK,MAAM,WAAW,KAAK,MAAM,UAAO,KAAU,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;CAC9E;CACA,uBAA8C;EAC5C,IAAM,IAAQ,KAAK,UAAU,KAAK,QAAQ,SAAS,CAAC;EAGpD,AAFI,KAAK,WAAW,QAAQ,MAAU,KAAK,cAAY,KAAK,KAAK,eAAe,CAAC,CAAC,EAAE,MAAM,yBAAyB,CAAC,GACpH,KAAK,aAAa,GAClB,KAAK,QAAQ;CACf;CAEA,UAAwB;EACtB,IAAM,IAA8B;GAClC,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ,SAAS,CAAC,CAAC,WAAW,KAAK;GACxE,SAAS,KAAK;GAAS,QAAQ,KAAK;GACpC,SAAS,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,KAAK,QAAQ,QAAQ,KAAK,MAAM;GAAG,OAAO,KAAK;EAC7E;EACI,MAAK,aAAa,KAAK,MAAM,YAAY,EAAK,YAAY,KAAK,MAAM,WAAW,EAAK,WAAW,KAAK,MAAM,UAAU,EAAK,YAAY,KAAK,MAAM,WAAW,EAAK,UAAU,KAAK,MAAM,OAC1L;QAAK,QAAQ,OAAO,OAAO,CAAI;GAC/B,KAAK,IAAM,KAAY,KAAK,WAAa,IAAI;IAAE,EAAS;GAAG,QAAQ,CAA0C;EAD9E;CAEjC;AACF"}
|
package/dist/client.js
CHANGED
|
@@ -25,18 +25,20 @@ import { CodesClient as He } from "./client/codes.js";
|
|
|
25
25
|
import { ContentsClient as Ue, IMAGE_SCHEME as We, imageRef as Ge, imageRefs as Ke } from "./client/contents.js";
|
|
26
26
|
import { BansClient as qe } from "./client/content/bans.js";
|
|
27
27
|
import { CommentsClient as Je } from "./client/content/comments.js";
|
|
28
|
-
import { DiscoveryClient as Ye } from "./client/discovery.js";
|
|
29
|
-
import { FavoritesClient as
|
|
30
|
-
import { ModerationClient as
|
|
31
|
-
import { PollsClient as
|
|
32
|
-
import { ReactionsClient as
|
|
33
|
-
import { EntitiesClient as
|
|
34
|
-
import { OffersClient as
|
|
35
|
-
import { StatsClient as
|
|
36
|
-
import { VersionsClient as
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
|
|
28
|
+
import { DiscoveryClient as Ye, popularQuery as Xe } from "./client/discovery.js";
|
|
29
|
+
import { FavoritesClient as Ze } from "./client/content/favorites.js";
|
|
30
|
+
import { ModerationClient as Qe } from "./client/content/moderation.js";
|
|
31
|
+
import { PollsClient as $e } from "./client/content/polls.js";
|
|
32
|
+
import { ReactionsClient as et } from "./client/content/reactions.js";
|
|
33
|
+
import { EntitiesClient as tt } from "./client/entities.js";
|
|
34
|
+
import { OffersClient as nt } from "./client/offers.js";
|
|
35
|
+
import { StatsClient as rt } from "./client/stats.js";
|
|
36
|
+
import { VersionsClient as it, isPending as at } from "./client/versions.js";
|
|
37
|
+
import { BrowserReaderPreferences as ot } from "./client/browser-reader-preferences.js";
|
|
38
|
+
import { NotificationsClient as st } from "./client/notifications.js";
|
|
39
|
+
import { ReaderPreferencesClient as ct } from "./client/reader-preferences.js";
|
|
40
|
+
import { createContentKitClient as lt } from "./client/client.js";
|
|
41
|
+
import { LETTERS as ut, attributeInput as dt, fold as ft, fromLocalInput as pt, humanize as mt, letterOf as ht, parseAttribute as gt, parseAttributes as _t, slugify as vt, toLocalInput as yt } from "./client/group-fields.js";
|
|
42
|
+
import { Beacons as bt, eventId as xt, readerSession as St } from "./client/beacons.js";
|
|
43
|
+
import { emptyReaderMeta as Ct, matchAudio as wt, readerMetaProblems as Tt, readerNames as Et } from "./client/reader-meta.js";
|
|
44
|
+
export { he as ABR_DEFAULTS, qe as BansClient, bt as Beacons, ot as BrowserReaderPreferences, i as CLIENT_ERROR_CODES, Ve as CatalogClient, He as CodesClient, Je as CommentsClient, a as ContentKitError, Ue as ContentsClient, e as DEFAULT_DENSITY, Ye as DiscoveryClient, o as ERROR_CODES, tt as EntitiesClient, Ze as FavoritesClient, We as IMAGE_SCHEME, ae as ItemUploadQueue, ut as LETTERS, T as MAX_PART_SIZE, E as MAX_SINGLE_PUT, D as MIN_PART_SIZE, w as MediaApi, N as MediaClient, Qe as ModerationClient, ge as NETWORK_FAILURES_BEFORE_ERROR, st as NotificationsClient, nt as OffersClient, Fe as PLAYBACK_SPEEDS, Ie as PLAYER_TRACKS_KEY, Le as PLAYER_VOLUME_KEY, k as Pacer, $e as PollsClient, ue as READ_CHUNK, _e as REFRESH_BEFORE_EXPIRY, et as ReactionsClient, ct as ReaderPreferencesClient, rt as StatsClient, it as VersionsClient, ve as abrHlsConfig, L as acceptOf, g as aspectOf, dt as attributeInput, P as backoff, v as call, ye as capRung, Y as centeredCrop, b as checkRef, de as chunkOffsets, be as classifyHlsError, xe as classifyMediaError, X as constrainCrop, lt as createContentKitClient, oe as decodeImage, R as defaultImage, A as defaultTransport, t as densityFor, Z as editOf, Q as editedSize, Ct as emptyReaderMeta, ie as encodeRemaining, xt as eventId, Se as expiryDelay, s as failureError, j as fetchTransport, z as filesFor, f as fill, ft as fold, Oe as formatDuration, pt as fromLocalInput, $ as fromRotated, ke as galleryItems, Ce as hlsConfig, mt as humanize, Ge as imageRef, Ke as imageRefs, we as initialEstimate, se as isAnimatedImage, Ae as isAudioType, x as isContentId, c as isContentKitError, je as isImageType, B as isPattern, at as isPending, fe as isProcessing, Me as isSubtitleType, Ne as isVideoType, S as itemKey, ht as letterOf, wt as matchAudio, pe as mergeReads, gt as parseAttribute, _t as parseAttributes, Re as parseSpriteVtt, n as pickRendition, ze as playerKeyAction, Xe as popularQuery, me as processing, p as publicRenditions, m as publicURL, _ as ratio, V as ratioLabel, ce as reactionVerb, l as readContentKitError, Tt as readerMetaProblems, Et as readerNames, St as readerSession, Te as refreshable, Be as resumeAt, ee as rotation, y as routeURL, H as ruleDir, U as ruleFor, te as sameEdit, C as sameItem, F as samePath, le as sameTarget, W as screenFiles, O as sha256Hex, vt as slugify, r as sortRenditions, h as srcSet, G as stableImage, Pe as stageAspect, Ee as startRung, De as statusKind, I as stem, u as toContentKitError, yt as toLocalInput, ne as toOriginal, re as toRotated, d as transportFailure, K as uniqueName, q as uploadRules, J as withMediaType, M as xhrTransport };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { Card, CardAccess } from '../../client/generated/wire.js';
|
|
2
|
+
import { Card, CardAccess, Chip as EntityChip } from '../../client/generated/wire.js';
|
|
3
3
|
import { Translator } from '../../i18n/messages.js';
|
|
4
4
|
import { GroupLike, Labels } from '../groups/parts.js';
|
|
5
5
|
export interface ContentCardProps {
|
|
@@ -10,10 +10,18 @@ export interface ContentCardProps {
|
|
|
10
10
|
onOpen?: (card: Card) => void;
|
|
11
11
|
/** Cover shape: "portrait" (3:4, readers) or "landscape" (16:9, players). Default portrait. */
|
|
12
12
|
shape?: "portrait" | "landscape";
|
|
13
|
+
/** Mount images and previews. False keeps the card's text and reserved cover space. */
|
|
14
|
+
loadMedia?: boolean;
|
|
13
15
|
/** The group kinds whose chips show, in order; default every kind the card names. */
|
|
14
16
|
groupKinds?: readonly string[];
|
|
15
17
|
/** Chips per group kind. Default 3. */
|
|
16
18
|
chips?: number;
|
|
19
|
+
/** Select which entity chips appear, before the per-kind chip limit. */
|
|
20
|
+
filterEntity?: (entity: EntityChip, kind: string) => boolean;
|
|
21
|
+
/** Metadata fields to show; omitted fields keep their defaults. */
|
|
22
|
+
metadata?: CardMetadata;
|
|
23
|
+
/** Show like and favorite totals. Default true. */
|
|
24
|
+
showCounts?: boolean;
|
|
17
25
|
/** Tiers the site sells, so a lock names them; others show as "for sale". */
|
|
18
26
|
tiers?: readonly string[];
|
|
19
27
|
/** Display names of kinds, traits and tiers by identifier. */
|
|
@@ -28,19 +36,27 @@ export interface ContentCardProps {
|
|
|
28
36
|
children?: ReactNode;
|
|
29
37
|
className?: string;
|
|
30
38
|
}
|
|
39
|
+
export interface CardMetadata {
|
|
40
|
+
languages?: boolean;
|
|
41
|
+
/** True shows all traits; an array selects specific trait identifiers. */
|
|
42
|
+
traits?: boolean | readonly string[];
|
|
43
|
+
pages?: boolean;
|
|
44
|
+
duration?: boolean;
|
|
45
|
+
}
|
|
31
46
|
/** Why a card is locked, in words (its price when it is on sale); null when it opens. */
|
|
32
47
|
export declare function lockLabel(m: Translator, a: CardAccess, tiers?: readonly string[], labels?: Labels): string | null;
|
|
33
48
|
/** One content in a listing: cover (a video the reader opens previews over it on hover), title, version, groups, counts, the reader's progress and why it is locked; what its kind's locked view hides is absent. */
|
|
34
|
-
export declare function ContentCard({ card, href, onOpen, shape, groupKinds, chips, tiers, labels, owner, ownerHref, decorate, children, className }: ContentCardProps): import('react').JSX.Element;
|
|
49
|
+
export declare function ContentCard({ card, href, onOpen, shape, loadMedia, groupKinds, chips, filterEntity, metadata, showCounts, tiers, labels, owner, ownerHref, decorate, children, className }: ContentCardProps): import('react').JSX.Element;
|
|
35
50
|
/** The group that owns a content (a channel): its avatar and name, linking to its page. */
|
|
36
|
-
export declare function OwnerChip({ group, href, className }: {
|
|
51
|
+
export declare function OwnerChip({ group, href, className, loadMedia }: {
|
|
37
52
|
group: GroupLike;
|
|
38
53
|
href?: (g: GroupLike) => string | undefined;
|
|
39
54
|
className?: string;
|
|
55
|
+
loadMedia?: boolean;
|
|
40
56
|
}): import('react').JSX.Element;
|
|
41
|
-
export interface PostCardProps extends Omit<ContentCardProps, "shape" | "chips" | "groupKinds"> {
|
|
57
|
+
export interface PostCardProps extends Omit<ContentCardProps, "shape" | "chips" | "groupKinds" | "filterEntity" | "metadata"> {
|
|
42
58
|
/** Above the title: the post's channel or author; default its owner group's chip. */
|
|
43
59
|
byline?: ReactNode;
|
|
44
60
|
}
|
|
45
61
|
/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */
|
|
46
|
-
export declare function PostCard({ card, href, onOpen, tiers, labels, byline, owner, ownerHref, decorate, children, className }: PostCardProps): import('react').JSX.Element;
|
|
62
|
+
export declare function PostCard({ card, href, onOpen, tiers, labels, byline, owner, ownerHref, loadMedia, showCounts, decorate, children, className }: PostCardProps): import('react').JSX.Element;
|
|
@@ -42,54 +42,61 @@ function T(e, t) {
|
|
|
42
42
|
t
|
|
43
43
|
]);
|
|
44
44
|
}
|
|
45
|
-
function E({ card: r, href: o, onOpen: c, shape: l = "portrait",
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
function E({ card: r, href: o, onOpen: c, shape: l = "portrait", loadMedia: d = !0, groupKinds: p, chips: E = 3, filterEntity: k, metadata: A, showCounts: j = !0, tiers: M, labels: N, owner: P = !0, ownerHref: F, decorate: I, children: L, className: R }) {
|
|
46
|
+
let z = n(), { t: B } = z, V = t(), H = o ? o(r) : r.path, U = T(H, f(() => c?.(r), [c, r])), W = w(z, r.access, M, N), G = r.version, K = r.viewer && r.viewer.progress_max > 0 ? Math.min(1, r.viewer.progress / r.viewer.progress_max) : 0, q = (p ?? Object.keys(r.entities)).flatMap((e) => (r.entities[e] ?? []).filter((t) => !k || k(t, e)).slice(0, E).map((t) => ({
|
|
47
|
+
kind: e,
|
|
48
|
+
entity: t
|
|
49
|
+
}))), J = G.duration_ms ? Math.round(G.duration_ms / 6e4) : 0, Y = A?.traits, X = Y === !1 ? [] : Array.isArray(Y) ? G.traits.filter((e) => Y.includes(e)) : G.traits, Z = [
|
|
50
|
+
...A?.languages === !1 ? [] : G.languages.map(V),
|
|
51
|
+
...X.map((e) => u(N, e)),
|
|
52
|
+
A?.pages !== !1 && G.pages ? z.plural("contents.pages", G.pages) : null,
|
|
53
|
+
A?.duration !== !1 && J ? z.plural("contents.minutes", J) : null
|
|
54
|
+
].filter(Boolean), Q = r.title || r.code, [$, ee] = m(null);
|
|
52
55
|
return /* @__PURE__ */ g("article", {
|
|
53
|
-
ref:
|
|
54
|
-
className: _("group/card grid content-start gap-2",
|
|
56
|
+
ref: ee,
|
|
57
|
+
className: _("group/card grid content-start gap-2", R),
|
|
55
58
|
"data-ckui": "content-card",
|
|
56
59
|
"data-id": r.id,
|
|
57
|
-
"data-locked":
|
|
60
|
+
"data-locked": W ? "" : void 0,
|
|
58
61
|
lang: r.language || void 0,
|
|
59
62
|
children: [/* @__PURE__ */ g("div", {
|
|
60
63
|
className: "relative overflow-hidden rounded-lg",
|
|
61
64
|
children: [/* @__PURE__ */ g("a", {
|
|
62
|
-
href:
|
|
63
|
-
onClick:
|
|
65
|
+
href: H,
|
|
66
|
+
onClick: U,
|
|
64
67
|
className: "relative block overflow-hidden rounded-lg bg-muted",
|
|
65
68
|
tabIndex: -1,
|
|
66
69
|
"aria-hidden": !0,
|
|
67
70
|
children: [
|
|
68
71
|
/* @__PURE__ */ h("div", {
|
|
69
72
|
className: l === "landscape" ? "aspect-video" : "aspect-[3/4]",
|
|
70
|
-
children: /* @__PURE__ */ h(e, {
|
|
73
|
+
children: d ? /* @__PURE__ */ h(e, {
|
|
71
74
|
outputs: r.cover,
|
|
72
75
|
alt: "",
|
|
73
76
|
fit: "cover",
|
|
74
77
|
skeleton: !0,
|
|
78
|
+
loading: "lazy",
|
|
75
79
|
className: "size-full transition-transform group-hover/card:scale-[1.02]",
|
|
76
80
|
fallback: /* @__PURE__ */ h("span", {
|
|
77
81
|
className: "grid size-full place-items-center text-2xl font-semibold text-muted-foreground",
|
|
78
|
-
children:
|
|
82
|
+
children: Q.charAt(0).toUpperCase()
|
|
79
83
|
})
|
|
84
|
+
}) : /* @__PURE__ */ h("span", {
|
|
85
|
+
className: "block size-full bg-muted",
|
|
86
|
+
"aria-hidden": !0
|
|
80
87
|
})
|
|
81
88
|
}),
|
|
82
|
-
|
|
89
|
+
d && G.hls && /* @__PURE__ */ h(i, {
|
|
83
90
|
item: {
|
|
84
91
|
kind: r.kind,
|
|
85
92
|
id: r.id,
|
|
86
|
-
version:
|
|
93
|
+
version: G.id
|
|
87
94
|
},
|
|
88
|
-
dir:
|
|
89
|
-
duration:
|
|
90
|
-
target:
|
|
95
|
+
dir: G.hls,
|
|
96
|
+
duration: G.duration_ms ? G.duration_ms / 1e3 : void 0,
|
|
97
|
+
target: $
|
|
91
98
|
}),
|
|
92
|
-
|
|
99
|
+
W && /* @__PURE__ */ g("span", {
|
|
93
100
|
className: "absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm",
|
|
94
101
|
"data-ckui": "card-lock",
|
|
95
102
|
children: [/* @__PURE__ */ h(x, {
|
|
@@ -97,72 +104,73 @@ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d,
|
|
|
97
104
|
strokeWidth: 2,
|
|
98
105
|
className: "size-3.5",
|
|
99
106
|
"aria-hidden": !0
|
|
100
|
-
}),
|
|
107
|
+
}), W]
|
|
101
108
|
}),
|
|
102
|
-
|
|
109
|
+
K > 0 && /* @__PURE__ */ h("span", {
|
|
103
110
|
className: "absolute inset-x-0 bottom-0 h-1 bg-background/60",
|
|
104
111
|
"data-ckui": "card-progress",
|
|
105
112
|
children: /* @__PURE__ */ h("span", {
|
|
106
113
|
className: "block h-full bg-primary",
|
|
107
|
-
style: { width: `${Math.round(
|
|
114
|
+
style: { width: `${Math.round(K * 100)}%` }
|
|
108
115
|
})
|
|
109
116
|
})
|
|
110
117
|
]
|
|
111
|
-
}), /* @__PURE__ */ h(S, {
|
|
118
|
+
}), d && /* @__PURE__ */ h(S, {
|
|
112
119
|
card: r,
|
|
113
|
-
decorate:
|
|
120
|
+
decorate: I
|
|
114
121
|
})]
|
|
115
122
|
}), /* @__PURE__ */ g("div", {
|
|
116
123
|
className: "grid gap-1",
|
|
117
124
|
children: [
|
|
118
125
|
/* @__PURE__ */ g("a", {
|
|
119
|
-
href:
|
|
120
|
-
onClick:
|
|
126
|
+
href: H,
|
|
127
|
+
onClick: U,
|
|
121
128
|
className: "line-clamp-2 font-medium break-words hover:underline",
|
|
122
129
|
"data-ckui": "card-title",
|
|
123
|
-
children: [
|
|
130
|
+
children: [Q, W && /* @__PURE__ */ g("span", {
|
|
124
131
|
className: "sr-only",
|
|
125
132
|
children: [
|
|
126
133
|
" (",
|
|
127
|
-
|
|
134
|
+
W,
|
|
128
135
|
")"
|
|
129
136
|
]
|
|
130
137
|
})]
|
|
131
138
|
}),
|
|
132
|
-
|
|
139
|
+
P && r.owner && /* @__PURE__ */ h(D, {
|
|
133
140
|
group: r.owner,
|
|
134
|
-
href:
|
|
141
|
+
href: F,
|
|
142
|
+
loadMedia: d
|
|
135
143
|
}),
|
|
136
|
-
|
|
144
|
+
Z.length > 0 && /* @__PURE__ */ h("p", {
|
|
137
145
|
className: "text-xs text-muted-foreground",
|
|
138
|
-
children:
|
|
146
|
+
children: Z.join(" · ")
|
|
139
147
|
}),
|
|
140
148
|
r.viewer?.completed && /* @__PURE__ */ h(a, {
|
|
141
149
|
variant: "secondary",
|
|
142
150
|
className: "justify-self-start",
|
|
143
|
-
children:
|
|
151
|
+
children: B("contents.completed")
|
|
144
152
|
}),
|
|
145
153
|
!r.access.allowed && r.access.free_after && /* @__PURE__ */ h("p", {
|
|
146
154
|
className: "text-xs text-muted-foreground",
|
|
147
|
-
children:
|
|
155
|
+
children: B("contents.freeFrom", { date: s(z, r.access.free_after, !1) })
|
|
148
156
|
}),
|
|
149
|
-
|
|
157
|
+
q.length > 0 && /* @__PURE__ */ h("ul", {
|
|
150
158
|
className: "flex flex-wrap gap-1",
|
|
151
159
|
"data-ckui": "card-groups",
|
|
152
|
-
children:
|
|
160
|
+
children: q.map(({ kind: e, entity: t }) => /* @__PURE__ */ h("li", {
|
|
153
161
|
"data-kind": e,
|
|
154
162
|
children: /* @__PURE__ */ h(O, {
|
|
155
163
|
url: t.path,
|
|
156
164
|
name: t.name || t.slug
|
|
157
165
|
})
|
|
158
|
-
}, `${e}:${t.id}`))
|
|
166
|
+
}, `${e}:${t.id}`))
|
|
159
167
|
}),
|
|
160
|
-
(r.counts.favorites !== void 0 || r.counts.likes !== void 0) && /* @__PURE__ */ g("p", {
|
|
168
|
+
j && (r.counts.favorites !== void 0 || r.counts.likes !== void 0) && /* @__PURE__ */ g("p", {
|
|
161
169
|
className: "flex items-center gap-3 text-xs text-muted-foreground tabular-nums",
|
|
162
170
|
"data-ckui": "card-counts",
|
|
163
171
|
children: [r.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
|
|
164
172
|
className: "inline-flex items-center gap-1",
|
|
165
|
-
title:
|
|
173
|
+
title: B("contents.favorites"),
|
|
166
174
|
children: [
|
|
167
175
|
/* @__PURE__ */ h(x, {
|
|
168
176
|
icon: v,
|
|
@@ -172,13 +180,13 @@ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d,
|
|
|
172
180
|
}),
|
|
173
181
|
/* @__PURE__ */ h("span", {
|
|
174
182
|
className: "sr-only",
|
|
175
|
-
children:
|
|
183
|
+
children: B("contents.favorites")
|
|
176
184
|
}),
|
|
177
|
-
C(r.counts.favorites,
|
|
185
|
+
C(r.counts.favorites, z.language)
|
|
178
186
|
]
|
|
179
187
|
}), r.counts.likes !== void 0 && /* @__PURE__ */ g("span", {
|
|
180
188
|
className: "inline-flex items-center gap-1",
|
|
181
|
-
title:
|
|
189
|
+
title: B("contents.likes"),
|
|
182
190
|
children: [
|
|
183
191
|
/* @__PURE__ */ h(x, {
|
|
184
192
|
icon: b,
|
|
@@ -188,18 +196,18 @@ function E({ card: r, href: o, onOpen: c, shape: l = "portrait", groupKinds: d,
|
|
|
188
196
|
}),
|
|
189
197
|
/* @__PURE__ */ h("span", {
|
|
190
198
|
className: "sr-only",
|
|
191
|
-
children:
|
|
199
|
+
children: B("contents.likes")
|
|
192
200
|
}),
|
|
193
|
-
C(r.counts.likes,
|
|
201
|
+
C(r.counts.likes, z.language)
|
|
194
202
|
]
|
|
195
203
|
})]
|
|
196
204
|
}),
|
|
197
|
-
|
|
205
|
+
L
|
|
198
206
|
]
|
|
199
207
|
})]
|
|
200
208
|
});
|
|
201
209
|
}
|
|
202
|
-
function D({ group: e, href: t, className: n }) {
|
|
210
|
+
function D({ group: e, href: t, className: n, loadMedia: r = !0 }) {
|
|
203
211
|
return /* @__PURE__ */ h("span", {
|
|
204
212
|
className: _("flex min-w-0 text-xs text-muted-foreground", n),
|
|
205
213
|
"data-ckui": "card-owner",
|
|
@@ -208,9 +216,12 @@ function D({ group: e, href: t, className: n }) {
|
|
|
208
216
|
group: e,
|
|
209
217
|
href: t,
|
|
210
218
|
className: "inline-flex min-w-0 items-center gap-1.5 hover:text-foreground",
|
|
211
|
-
children: [/* @__PURE__ */ h(c, {
|
|
219
|
+
children: [r ? /* @__PURE__ */ h(c, {
|
|
212
220
|
group: e,
|
|
213
221
|
className: "size-5 shrink-0 text-[0.625rem]"
|
|
222
|
+
}) : /* @__PURE__ */ h("span", {
|
|
223
|
+
className: "size-5 shrink-0 rounded-full bg-muted",
|
|
224
|
+
"aria-hidden": !0
|
|
214
225
|
}), /* @__PURE__ */ h("span", {
|
|
215
226
|
className: "truncate",
|
|
216
227
|
children: e.name || e.slug
|
|
@@ -230,43 +241,45 @@ function O({ url: e, name: t }) {
|
|
|
230
241
|
children: t
|
|
231
242
|
});
|
|
232
243
|
}
|
|
233
|
-
function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner: l = !0, ownerHref: u,
|
|
234
|
-
let
|
|
244
|
+
function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner: l = !0, ownerHref: u, loadMedia: d = !0, showCounts: p = !0, decorate: m, children: b, className: E }) {
|
|
245
|
+
let O = n(), k = r ? r(t) : t.path, A = T(k, f(() => i?.(t), [i, t])), j = w(O, t.access, a, s), M = t.title || t.code;
|
|
235
246
|
return /* @__PURE__ */ g("article", {
|
|
236
|
-
className: _("grid gap-3 rounded-lg border border-border p-3",
|
|
247
|
+
className: _("grid gap-3 rounded-lg border border-border p-3", E),
|
|
237
248
|
"data-ckui": "post-card",
|
|
238
249
|
"data-id": t.id,
|
|
239
|
-
"data-locked":
|
|
250
|
+
"data-locked": j ? "" : void 0,
|
|
240
251
|
lang: t.language || void 0,
|
|
241
252
|
children: [
|
|
242
253
|
c ?? (l && t.owner && /* @__PURE__ */ h(D, {
|
|
243
254
|
group: t.owner,
|
|
244
255
|
href: u,
|
|
256
|
+
loadMedia: d,
|
|
245
257
|
className: "text-sm"
|
|
246
258
|
})),
|
|
247
259
|
/* @__PURE__ */ g("a", {
|
|
248
|
-
href:
|
|
249
|
-
onClick:
|
|
260
|
+
href: k,
|
|
261
|
+
onClick: A,
|
|
250
262
|
className: "grid gap-2",
|
|
251
263
|
children: [/* @__PURE__ */ h("span", {
|
|
252
264
|
className: "font-medium break-words hover:underline",
|
|
253
265
|
"data-ckui": "card-title",
|
|
254
|
-
children:
|
|
266
|
+
children: M
|
|
255
267
|
}), t.cover.length > 0 && /* @__PURE__ */ g("span", {
|
|
256
268
|
className: "relative block aspect-video overflow-hidden rounded-md bg-muted",
|
|
257
269
|
children: [
|
|
258
|
-
/* @__PURE__ */ h(e, {
|
|
270
|
+
d && /* @__PURE__ */ h(e, {
|
|
259
271
|
outputs: t.cover,
|
|
260
272
|
alt: "",
|
|
261
273
|
fit: "cover",
|
|
262
274
|
skeleton: !0,
|
|
263
|
-
|
|
275
|
+
loading: "lazy",
|
|
276
|
+
className: _("size-full", j && "blur-md")
|
|
264
277
|
}),
|
|
265
|
-
/* @__PURE__ */ h(S, {
|
|
278
|
+
d && /* @__PURE__ */ h(S, {
|
|
266
279
|
card: t,
|
|
267
|
-
decorate:
|
|
280
|
+
decorate: m
|
|
268
281
|
}),
|
|
269
|
-
|
|
282
|
+
j && /* @__PURE__ */ h("span", {
|
|
270
283
|
className: "absolute inset-0 grid place-items-center",
|
|
271
284
|
children: /* @__PURE__ */ g("span", {
|
|
272
285
|
className: "inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm",
|
|
@@ -276,7 +289,7 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
|
|
|
276
289
|
strokeWidth: 2,
|
|
277
290
|
className: "size-3.5",
|
|
278
291
|
"aria-hidden": !0
|
|
279
|
-
}),
|
|
292
|
+
}), j]
|
|
280
293
|
})
|
|
281
294
|
})
|
|
282
295
|
]
|
|
@@ -286,11 +299,11 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
|
|
|
286
299
|
className: "flex flex-wrap items-center gap-3 text-xs text-muted-foreground",
|
|
287
300
|
children: [
|
|
288
301
|
t.published_at && /* @__PURE__ */ h(o, { at: t.published_at }),
|
|
289
|
-
!t.cover.length &&
|
|
302
|
+
!t.cover.length && j && /* @__PURE__ */ h("span", {
|
|
290
303
|
"data-ckui": "card-lock",
|
|
291
|
-
children:
|
|
304
|
+
children: j
|
|
292
305
|
}),
|
|
293
|
-
t.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
|
|
306
|
+
p && t.counts.favorites !== void 0 && /* @__PURE__ */ g("span", {
|
|
294
307
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
295
308
|
"data-ckui": "card-counts",
|
|
296
309
|
children: [
|
|
@@ -302,14 +315,14 @@ function k({ card: t, href: r, onOpen: i, tiers: a, labels: s, byline: c, owner:
|
|
|
302
315
|
}),
|
|
303
316
|
/* @__PURE__ */ h("span", {
|
|
304
317
|
className: "sr-only",
|
|
305
|
-
children:
|
|
318
|
+
children: O.t("contents.favorites")
|
|
306
319
|
}),
|
|
307
|
-
C(t.counts.favorites,
|
|
320
|
+
C(t.counts.favorites, O.language)
|
|
308
321
|
]
|
|
309
322
|
})
|
|
310
323
|
]
|
|
311
324
|
}),
|
|
312
|
-
|
|
325
|
+
b
|
|
313
326
|
]
|
|
314
327
|
});
|
|
315
328
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useState, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { HoverPreview } from \"../hover-preview.js\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Over the cover: the host's badges for the card, placed in the cover's box; clicks reach the card. */\n decorate?: (card: Card) => ReactNode;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\n/** The host's badges over a cover. */\nfunction Decorations({ card, decorate }: { card: Card; decorate?: (card: Card) => ReactNode }) {\n const badges = decorate?.(card);\n if (badges == null || badges === false) return null;\n return (\n <span className=\"pointer-events-none absolute inset-0\" data-ckui=\"card-decorations\">\n {badges}\n </span>\n );\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return offer.target.kind === \"group\" && Number(offer.terms.amount) === 0 ? m.t(\"contents.joinFree\") : priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover (a video the reader opens previews over it on hover), title, version, groups, counts, the reader's progress and why it is locked; what its kind's locked view hides is absent. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", groupKinds, chips = 3, tiers, labels, owner = true, ownerHref, decorate, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.entities);\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const meta = [\n ...v.languages.map(languageName),\n ...v.traits.map((x) => labelOf(labels, x)),\n v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n const [article, setArticle] = useState<HTMLElement | null>(null);\n return (\n <article ref={setArticle} className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <div className=\"relative overflow-hidden rounded-lg\">\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div className={shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\"}>\n <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n />\n </div>\n {v.hls && <HoverPreview item={{ kind: card.kind, id: card.id, version: v.id }} dir={v.hls} duration={v.duration_ms ? v.duration_ms / 1000 : undefined} target={article} />}\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className=\"absolute inset-x-0 bottom-0 h-1 bg-background/60\" data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n <Decorations card={card} decorate={decorate} />\n </div>\n <div className=\"grid gap-1\">\n <a href={url} onClick={onClick} className=\"line-clamp-2 font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {kinds.some((k) => card.entities[k]?.length) && (\n <ul className=\"flex flex-wrap gap-1\" data-ckui=\"card-groups\">\n {kinds.flatMap((k) =>\n (card.entities[k] ?? []).slice(0, chips).map((g) => (\n <li key={`${k}:${g.id}`} data-kind={k}>\n <Chip url={g.path} name={g.name || g.slug} />\n </li>\n )),\n )}\n </ul>\n )}\n {(card.counts.favorites !== undefined || card.counts.likes !== undefined) && (\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n {card.counts.likes !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n )}\n </p>\n )}\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" />\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name }: { url?: string; name: string }) {\n const onClick = useLink(url);\n const cls = \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:bg-muted hover:text-foreground\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"chips\" | \"groupKinds\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, decorate, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton className={cn(\"size-full\", lock && \"blur-md\")} />\n <Decorations card={card} decorate={decorate} />\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\" data-ckui=\"card-counts\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"card.js","names":[],"sources":["../../../src/components/content/card.tsx"],"sourcesContent":["import { FavouriteIcon, LockIcon, ThumbsUpIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useState, type MouseEvent, type ReactNode } from \"react\";\nimport type { Card, CardAccess, Chip as EntityChip } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext } from \"../../react/context.js\";\nimport { Badge } from \"#ckui/ui/badge\";\nimport { HoverPreview } from \"../hover-preview.js\";\nimport { RenditionImg } from \"../rendition-img.js\";\nimport { formatDate, RelativeTime } from \"../social/parts.js\";\nimport { GroupAvatar, GroupLink, labelOf, type GroupLike, type Labels } from \"../groups/parts.js\";\nimport { priceLabel } from \"./paywall.js\";\n\nexport interface ContentCardProps {\n card: Card;\n /** The card's link; default its canonical path. */\n href?: (card: Card) => string | undefined;\n /** Before the card's link is followed: a click signal. */\n onOpen?: (card: Card) => void;\n /** Cover shape: \"portrait\" (3:4, readers) or \"landscape\" (16:9, players). Default portrait. */\n shape?: \"portrait\" | \"landscape\";\n /** Mount images and previews. False keeps the card's text and reserved cover space. */\n loadMedia?: boolean;\n /** The group kinds whose chips show, in order; default every kind the card names. */\n groupKinds?: readonly string[];\n /** Chips per group kind. Default 3. */\n chips?: number;\n /** Select which entity chips appear, before the per-kind chip limit. */\n filterEntity?: (entity: EntityChip, kind: string) => boolean;\n /** Metadata fields to show; omitted fields keep their defaults. */\n metadata?: CardMetadata;\n /** Show like and favorite totals. Default true. */\n showCounts?: boolean;\n /** Tiers the site sells, so a lock names them; others show as \"for sale\". */\n tiers?: readonly string[];\n /** Display names of kinds, traits and tiers by identifier. */\n labels?: Labels;\n /** Show the owner group (a channel) when the card has one; default true, false on that group's own feed. */\n owner?: boolean;\n /** The owner group's link; default its canonical path. */\n ownerHref?: (g: GroupLike) => string | undefined;\n /** Over the cover: the host's badges for the card, placed in the cover's box; clicks reach the card. */\n decorate?: (card: Card) => ReactNode;\n /** Below the meta line: the host's own. */\n children?: ReactNode;\n className?: string;\n}\n\nexport interface CardMetadata {\n languages?: boolean;\n /** True shows all traits; an array selects specific trait identifiers. */\n traits?: boolean | readonly string[];\n pages?: boolean;\n duration?: boolean;\n}\n\n/** The host's badges over a cover. */\nfunction Decorations({ card, decorate }: { card: Card; decorate?: (card: Card) => ReactNode }) {\n const badges = decorate?.(card);\n if (badges == null || badges === false) return null;\n return (\n <span className=\"pointer-events-none absolute inset-0\" data-ckui=\"card-decorations\">\n {badges}\n </span>\n );\n}\n\nconst compact = (n: number, language?: string) => new Intl.NumberFormat(language, { notation: \"compact\", maximumFractionDigits: 1 }).format(n);\n\n/** Why a card is locked, in words (its price when it is on sale); null when it opens. */\nexport function lockLabel(m: Translator, a: CardAccess, tiers: readonly string[] = [], labels?: Labels): string | null {\n if (a.allowed) return null;\n if (a.unknown) return m.t(\"contents.unknownAccess\");\n const offer = a.offers?.[0];\n if (offer) return offer.target.kind === \"group\" && Number(offer.terms.amount) === 0 ? m.t(\"contents.joinFree\") : priceLabel(m, offer.terms);\n const tier = a.sell?.find((k) => tiers.includes(k));\n if (tier) return labelOf(labels, tier);\n if (a.sell?.some((k) => k.includes(\":group:\"))) return m.t(\"contents.members\");\n if (a.sell?.length) return m.t(\"contents.forSale\");\n return m.t(\"contents.locked\");\n}\n\n/** A link followed in-app through the provider's navigate. */\nfunction useLink(url: string | undefined, before?: () => void) {\n const navigate = useContext(ContentKitContext)?.navigate;\n return useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n before?.();\n if (!navigate || !url || e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;\n e.preventDefault();\n navigate(url);\n },\n [navigate, url, before],\n );\n}\n\n/** One content in a listing: cover (a video the reader opens previews over it on hover), title, version, groups, counts, the reader's progress and why it is locked; what its kind's locked view hides is absent. */\nexport function ContentCard({ card, href, onOpen, shape = \"portrait\", loadMedia = true, groupKinds, chips = 3, filterEntity, metadata, showCounts = true, tiers, labels, owner = true, ownerHref, decorate, children, className }: ContentCardProps) {\n const m = useMessages();\n const { t } = m;\n const languageName = useLanguageName();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const v = card.version;\n const progress = card.viewer && card.viewer.progress_max > 0 ? Math.min(1, card.viewer.progress / card.viewer.progress_max) : 0;\n const kinds = groupKinds ?? Object.keys(card.entities);\n const entities = kinds.flatMap((kind) => (card.entities[kind] ?? []).filter((entity) => !filterEntity || filterEntity(entity, kind)).slice(0, chips).map((entity) => ({ kind, entity })));\n const minutes = v.duration_ms ? Math.round(v.duration_ms / 60_000) : 0;\n const selectedTraits = metadata?.traits;\n const traits = selectedTraits === false ? [] : Array.isArray(selectedTraits) ? v.traits.filter((trait) => selectedTraits.includes(trait)) : v.traits;\n const meta = [\n ...(metadata?.languages === false ? [] : v.languages.map(languageName)),\n ...traits.map((x) => labelOf(labels, x)),\n metadata?.pages !== false && v.pages ? m.plural(\"contents.pages\", v.pages) : null,\n metadata?.duration !== false && minutes ? m.plural(\"contents.minutes\", minutes) : null,\n ].filter(Boolean);\n const title = card.title || card.code;\n const [article, setArticle] = useState<HTMLElement | null>(null);\n return (\n <article ref={setArticle} className={cn(\"group/card grid content-start gap-2\", className)} data-ckui=\"content-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n <div className=\"relative overflow-hidden rounded-lg\">\n <a href={url} onClick={onClick} className=\"relative block overflow-hidden rounded-lg bg-muted\" tabIndex={-1} aria-hidden>\n <div className={shape === \"landscape\" ? \"aspect-video\" : \"aspect-[3/4]\"}>\n {loadMedia ? <RenditionImg\n outputs={card.cover}\n alt=\"\"\n fit=\"cover\"\n skeleton\n loading=\"lazy\"\n className=\"size-full transition-transform group-hover/card:scale-[1.02]\"\n fallback={<span className=\"grid size-full place-items-center text-2xl font-semibold text-muted-foreground\">{title.charAt(0).toUpperCase()}</span>}\n /> : <span className=\"block size-full bg-muted\" aria-hidden />}\n </div>\n {loadMedia && v.hls && <HoverPreview item={{ kind: card.kind, id: card.id, version: v.id }} dir={v.hls} duration={v.duration_ms ? v.duration_ms / 1000 : undefined} target={article} />}\n {lock && (\n <span className=\"absolute top-2 left-2 inline-flex items-center gap-1 rounded-md bg-background/85 px-1.5 py-0.5 text-xs font-medium text-foreground shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n )}\n {progress > 0 && (\n <span className=\"absolute inset-x-0 bottom-0 h-1 bg-background/60\" data-ckui=\"card-progress\">\n <span className=\"block h-full bg-primary\" style={{ width: `${Math.round(progress * 100)}%` }} />\n </span>\n )}\n </a>\n {loadMedia && <Decorations card={card} decorate={decorate} />}\n </div>\n <div className=\"grid gap-1\">\n <a href={url} onClick={onClick} className=\"line-clamp-2 font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n {lock && <span className=\"sr-only\"> ({lock})</span>}\n </a>\n {owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} loadMedia={loadMedia} />}\n {meta.length > 0 && <p className=\"text-xs text-muted-foreground\">{meta.join(\" · \")}</p>}\n {card.viewer?.completed && (\n <Badge variant=\"secondary\" className=\"justify-self-start\">\n {t(\"contents.completed\")}\n </Badge>\n )}\n {!card.access.allowed && card.access.free_after && <p className=\"text-xs text-muted-foreground\">{t(\"contents.freeFrom\", { date: formatDate(m, card.access.free_after, false) })}</p>}\n {entities.length > 0 && (\n <ul className=\"flex flex-wrap gap-1\" data-ckui=\"card-groups\">\n {entities.map(({ kind, entity }) => <li key={`${kind}:${entity.id}`} data-kind={kind}><Chip url={entity.path} name={entity.name || entity.slug} /></li>)}\n </ul>\n )}\n {showCounts && (card.counts.favorites !== undefined || card.counts.likes !== undefined) && (\n <p className=\"flex items-center gap-3 text-xs text-muted-foreground tabular-nums\" data-ckui=\"card-counts\">\n {card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.favorites\")}>\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n {card.counts.likes !== undefined && (\n <span className=\"inline-flex items-center gap-1\" title={t(\"contents.likes\")}>\n <HugeiconsIcon icon={ThumbsUpIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n <span className=\"sr-only\">{t(\"contents.likes\")}</span>\n {compact(card.counts.likes, m.language)}\n </span>\n )}\n </p>\n )}\n {children}\n </div>\n </article>\n );\n}\n\n/** The group that owns a content (a channel): its avatar and name, linking to its page. */\nexport function OwnerChip({ group, href, className, loadMedia = true }: { group: GroupLike; href?: (g: GroupLike) => string | undefined; className?: string; loadMedia?: boolean }) {\n return (\n <span className={cn(\"flex min-w-0 text-xs text-muted-foreground\", className)} data-ckui=\"card-owner\" data-id={group.id}>\n <GroupLink group={group} href={href} className=\"inline-flex min-w-0 items-center gap-1.5 hover:text-foreground\">\n {loadMedia ? <GroupAvatar group={group} className=\"size-5 shrink-0 text-[0.625rem]\" /> : <span className=\"size-5 shrink-0 rounded-full bg-muted\" aria-hidden />}\n <span className=\"truncate\">{group.name || group.slug}</span>\n </GroupLink>\n </span>\n );\n}\n\nfunction Chip({ url, name }: { url?: string; name: string }) {\n const onClick = useLink(url);\n const cls = \"inline-block rounded-md border border-border px-1.5 py-0.5 text-xs text-muted-foreground\";\n return url ? (\n <a href={url} onClick={onClick} className={cn(cls, \"hover:bg-muted hover:text-foreground\")}>\n {name}\n </a>\n ) : (\n <span className={cls}>{name}</span>\n );\n}\n\nexport interface PostCardProps extends Omit<ContentCardProps, \"shape\" | \"chips\" | \"groupKinds\" | \"filterEntity\" | \"metadata\"> {\n /** Above the title: the post's channel or author; default its owner group's chip. */\n byline?: ReactNode;\n}\n\n/** A post in a feed: a wide cover, its title, when it was published and what unlocking it costs. */\nexport function PostCard({ card, href, onOpen, tiers, labels, byline, owner = true, ownerHref, loadMedia = true, showCounts = true, decorate, children, className }: PostCardProps) {\n const m = useMessages();\n const url = href ? href(card) : card.path;\n const open = useCallback(() => onOpen?.(card), [onOpen, card]);\n const onClick = useLink(url, open);\n const lock = lockLabel(m, card.access, tiers, labels);\n const title = card.title || card.code;\n return (\n <article className={cn(\"grid gap-3 rounded-lg border border-border p-3\", className)} data-ckui=\"post-card\" data-id={card.id} data-locked={lock ? \"\" : undefined} lang={card.language || undefined}>\n {byline ?? (owner && card.owner && <OwnerChip group={card.owner} href={ownerHref} loadMedia={loadMedia} className=\"text-sm\" />)}\n <a href={url} onClick={onClick} className=\"grid gap-2\">\n <span className=\"font-medium break-words hover:underline\" data-ckui=\"card-title\">\n {title}\n </span>\n {card.cover.length > 0 && (\n <span className=\"relative block aspect-video overflow-hidden rounded-md bg-muted\">\n {loadMedia && <RenditionImg outputs={card.cover} alt=\"\" fit=\"cover\" skeleton loading=\"lazy\" className={cn(\"size-full\", lock && \"blur-md\")} />}\n {loadMedia && <Decorations card={card} decorate={decorate} />}\n {lock && (\n <span className=\"absolute inset-0 grid place-items-center\">\n <span className=\"inline-flex items-center gap-1 rounded-md bg-background/85 px-2 py-1 text-xs font-medium shadow-sm\" data-ckui=\"card-lock\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-hidden />\n {lock}\n </span>\n </span>\n )}\n </span>\n )}\n </a>\n <p className=\"flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {card.published_at && <RelativeTime at={card.published_at} />}\n {!card.cover.length && lock && <span data-ckui=\"card-lock\">{lock}</span>}\n {showCounts && card.counts.favorites !== undefined && (\n <span className=\"inline-flex items-center gap-1 tabular-nums\" data-ckui=\"card-counts\">\n <HugeiconsIcon icon={FavouriteIcon} strokeWidth={2} className={cn(\"size-3.5\", card.viewer?.favorited && \"fill-current text-primary\")} aria-hidden />\n <span className=\"sr-only\">{m.t(\"contents.favorites\")}</span>\n {compact(card.counts.favorites, m.language)}\n </span>\n )}\n </p>\n {children}\n </article>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DailyReleasesQuery } from '../../client/discovery.js';
|
|
2
|
+
import { CardListProps } from './grid.js';
|
|
3
|
+
export interface DailyReleasesProps extends Omit<CardListProps, "infinite" | "pageSize" | "renderCard" | "loadMedia"> {
|
|
4
|
+
/** One complete calendar window. Defaults to 10 days in the browser's time zone. */
|
|
5
|
+
query?: DailyReleasesQuery;
|
|
6
|
+
/** Cards initially revealed across the whole window, allocated newest first. Default 30. */
|
|
7
|
+
initialVisible?: number;
|
|
8
|
+
/** Additional cached cards revealed by a day's Show more button. Default 30. */
|
|
9
|
+
revealStep?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* One metadata request for a calendar window, with no requests when a day is
|
|
13
|
+
* opened or expanded. Only revealed, visible cards mount images and previews.
|
|
14
|
+
*/
|
|
15
|
+
export declare function DailyReleases({ query, initialVisible, revealStep, ...p }: DailyReleasesProps): import('react').JSX.Element;
|