@openrails/contentkit-ui 0.85.1 → 0.87.0-rc.1
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 +28 -7
- package/dist/client/beacons.d.ts +13 -4
- package/dist/client/beacons.js +14 -7
- package/dist/client/beacons.js.map +1 -1
- package/dist/client/client.d.ts +3 -0
- package/dist/client/client.js +29 -27
- package/dist/client/client.js.map +1 -1
- package/dist/client/codes.d.ts +6 -1
- package/dist/client/codes.js +4 -1
- package/dist/client/codes.js.map +1 -1
- package/dist/client/content/types.d.ts +4 -2
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/discovery.js +3 -0
- package/dist/client/discovery.js.map +1 -1
- package/dist/client/errors.d.ts +6 -0
- package/dist/client/errors.js +6 -2
- package/dist/client/errors.js.map +1 -1
- package/dist/client/gallery.d.ts +3 -5
- package/dist/client/gallery.js +16 -20
- package/dist/client/gallery.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +9 -1
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +29 -3
- package/dist/client/generated/routes.js +44 -3
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +60 -10
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/media/queue.d.ts +4 -2
- package/dist/client/media/queue.js +18 -12
- package/dist/client/media/queue.js.map +1 -1
- package/dist/client/media/rules.d.ts +7 -1
- package/dist/client/media/rules.js +27 -21
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client/media/windows.js +1 -1
- package/dist/client/media/windows.js.map +1 -1
- package/dist/client/stats.d.ts +32 -0
- package/dist/client/stats.js +35 -0
- package/dist/client/stats.js.map +1 -0
- package/dist/client/versions.d.ts +6 -4
- package/dist/client/versions.js +24 -26
- package/dist/client/versions.js.map +1 -1
- package/dist/client.js +30 -29
- package/dist/components/admin/content-manager.js +206 -206
- package/dist/components/admin/content-manager.js.map +1 -1
- package/dist/components/admin/group-manager.js +71 -70
- package/dist/components/admin/group-manager.js.map +1 -1
- package/dist/components/admin/media-item-panel.d.ts +24 -7
- package/dist/components/admin/media-item-panel.js +209 -197
- package/dist/components/admin/media-item-panel.js.map +1 -1
- package/dist/components/admin/poll-manager.js +25 -25
- package/dist/components/admin/stats-dashboard.d.ts +22 -0
- package/dist/components/admin/stats-dashboard.js +257 -0
- package/dist/components/admin/stats-dashboard.js.map +1 -0
- package/dist/components/content/card.d.ts +5 -3
- package/dist/components/content/card.js +155 -133
- package/dist/components/content/card.js.map +1 -1
- package/dist/components/content/download-button.d.ts +26 -0
- package/dist/components/content/download-button.js +51 -0
- package/dist/components/content/download-button.js.map +1 -0
- package/dist/components/content/editors.js +455 -427
- package/dist/components/content/editors.js.map +1 -1
- package/dist/components/content/grid.d.ts +8 -1
- package/dist/components/content/grid.js +153 -104
- package/dist/components/content/grid.js.map +1 -1
- package/dist/components/content/index.d.ts +4 -2
- package/dist/components/content/listing.d.ts +3 -1
- package/dist/components/content/listing.js +34 -34
- package/dist/components/content/listing.js.map +1 -1
- package/dist/components/content/paywall.d.ts +17 -5
- package/dist/components/content/paywall.js +115 -100
- package/dist/components/content/paywall.js.map +1 -1
- package/dist/components/content/reader-stats.d.ts +12 -0
- package/dist/components/content/reader-stats.js +47 -0
- package/dist/components/content/reader-stats.js.map +1 -0
- package/dist/components/groups/banner.js +33 -33
- package/dist/components/groups/directory.js +11 -11
- package/dist/components/groups/editor.js +293 -293
- package/dist/components/groups/editor.js.map +1 -1
- package/dist/components/groups/header.js +27 -27
- package/dist/components/groups/header.js.map +1 -1
- package/dist/components/groups/memberships.js +180 -180
- package/dist/components/groups/parts.d.ts +0 -2
- package/dist/components/groups/parts.js +9 -27
- package/dist/components/groups/parts.js.map +1 -1
- package/dist/components/media-folder-editor.js +239 -189
- package/dist/components/media-folder-editor.js.map +1 -1
- package/dist/components/media-gallery.d.ts +1 -4
- package/dist/components/media-gallery.js +19 -34
- package/dist/components/media-gallery.js.map +1 -1
- package/dist/components/notifications.js +17 -17
- package/dist/components/slot-upload.d.ts +2 -2
- package/dist/components/slot-upload.js +84 -79
- package/dist/components/slot-upload.js.map +1 -1
- package/dist/components/social/bans.js +34 -34
- package/dist/components/social/comments.js +1 -1
- package/dist/components/social/comments.js.map +1 -1
- package/dist/components/social/engagement.d.ts +2 -1
- package/dist/components/social/engagement.js +5 -3
- package/dist/components/social/engagement.js.map +1 -1
- package/dist/components/social/moderation.js +52 -48
- package/dist/components/social/moderation.js.map +1 -1
- package/dist/components/video-player.d.ts +2 -0
- package/dist/components/video-player.js +318 -314
- package/dist/components/video-player.js.map +1 -1
- package/dist/i18n/context.d.ts +2 -0
- package/dist/i18n/context.js +23 -5
- package/dist/i18n/context.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +13 -10
- package/dist/locales/admin/de.js +27 -0
- package/dist/locales/admin/de.js.map +1 -1
- package/dist/locales/admin/en.d.ts +27 -0
- package/dist/locales/admin/en.js +27 -0
- package/dist/locales/admin/en.js.map +1 -1
- package/dist/locales/admin/es.js +27 -0
- package/dist/locales/admin/es.js.map +1 -1
- package/dist/locales/admin/ja.js +27 -0
- package/dist/locales/admin/ja.js.map +1 -1
- package/dist/locales/admin/ko.js +27 -0
- package/dist/locales/admin/ko.js.map +1 -1
- package/dist/locales/admin/zh.js +27 -0
- package/dist/locales/admin/zh.js.map +1 -1
- package/dist/locales/content/de.js +6 -0
- package/dist/locales/content/de.js.map +1 -1
- package/dist/locales/content/en.d.ts +6 -0
- package/dist/locales/content/en.js +6 -0
- package/dist/locales/content/en.js.map +1 -1
- package/dist/locales/content/es.js +6 -0
- package/dist/locales/content/es.js.map +1 -1
- package/dist/locales/content/ja.js +6 -0
- package/dist/locales/content/ja.js.map +1 -1
- package/dist/locales/content/ko.js +6 -0
- package/dist/locales/content/ko.js.map +1 -1
- package/dist/locales/content/zh.js +6 -0
- package/dist/locales/content/zh.js.map +1 -1
- package/dist/locales/de.js +14 -2
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/discovery/de.js +3 -0
- package/dist/locales/discovery/de.js.map +1 -1
- package/dist/locales/discovery/en.d.ts +3 -0
- package/dist/locales/discovery/en.js +3 -0
- package/dist/locales/discovery/en.js.map +1 -1
- package/dist/locales/discovery/es.js +3 -0
- package/dist/locales/discovery/es.js.map +1 -1
- package/dist/locales/discovery/ja.js +3 -0
- package/dist/locales/discovery/ja.js.map +1 -1
- package/dist/locales/discovery/ko.js +3 -0
- package/dist/locales/discovery/ko.js.map +1 -1
- package/dist/locales/discovery/zh.js +3 -0
- package/dist/locales/discovery/zh.js.map +1 -1
- package/dist/locales/en.d.ts +50 -2
- package/dist/locales/en.js +14 -2
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +14 -2
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +14 -2
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +14 -2
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +14 -2
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/admin.js +22 -23
- package/dist/react/admin.js.map +1 -1
- package/dist/react/canonical.d.ts +4 -3
- package/dist/react/canonical.js +34 -33
- package/dist/react/canonical.js.map +1 -1
- package/dist/react/comments.d.ts +1 -1
- package/dist/react/comments.js +3 -2
- package/dist/react/comments.js.map +1 -1
- package/dist/react/contents.js +144 -144
- package/dist/react/contents.js.map +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js.map +1 -1
- package/dist/react/detail.d.ts +12 -6
- package/dist/react/detail.js +31 -24
- package/dist/react/detail.js.map +1 -1
- package/dist/react/discovery.d.ts +11 -0
- package/dist/react/discovery.js +49 -30
- package/dist/react/discovery.js.map +1 -1
- package/dist/react/engagement.d.ts +6 -2
- package/dist/react/engagement.js +2 -6
- package/dist/react/engagement.js.map +1 -1
- package/dist/react/folder.d.ts +7 -2
- package/dist/react/folder.js +165 -147
- package/dist/react/folder.js.map +1 -1
- package/dist/react/groups.d.ts +0 -1
- package/dist/react/groups.js +13 -12
- package/dist/react/groups.js.map +1 -1
- package/dist/react/index.d.ts +6 -4
- package/dist/react/location.d.ts +11 -0
- package/dist/react/location.js +18 -0
- package/dist/react/location.js.map +1 -0
- package/dist/react/public.d.ts +8 -0
- package/dist/react/public.js +32 -27
- package/dist/react/public.js.map +1 -1
- package/dist/react/stats.d.ts +24 -0
- package/dist/react/stats.js +28 -0
- package/dist/react/stats.js.map +1 -0
- package/dist/react/use-resource.js +8 -7
- package/dist/react/use-resource.js.map +1 -1
- package/dist/react/versions.d.ts +25 -5
- package/dist/react/versions.js +61 -40
- package/dist/react/versions.js.map +1 -1
- package/dist/react.js +26 -24
- package/dist/styles.css +1 -1
- package/dist/urls/index.d.ts +35 -10
- package/dist/urls.js +100 -70
- package/dist/urls.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, useLanguageName, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown. */\n value: string | null | undefined;\n onChange: (version: string) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/** The shown version, the versions' languages and the shown language's variants. */\nfunction choices(versions: readonly Version[], value: string | null | undefined) {\n const current = versions.find((v) => v.id === value) ?? versions[0]!;\n const language = primary(current);\n return { current, language, languages: [...new Set(versions.map(primary))], variants: versions.filter((v) => primary(v) === language) };\n}\n\n/** Chooses among a content's versions: a language first when they differ in language, then its variants (traits, labels). */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n if (versions.length < 2) return null;\n const { current, language, languages, variants } = choices(versions, value);\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) onChange(v.id);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => onChange(v.id)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(\n versions: readonly Version[],\n value: string | null | undefined,\n onChange: (version: string) => void,\n labels?: Labels,\n): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n return useMemo(() => {\n if (versions.length < 2) return [];\n const { current, language, languages, variants } = choices(versions, value);\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) onChange(v.id);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange,\n });\n return out;\n }, [versions, value, onChange, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"paywall.js","names":[],"sources":["../../../src/components/content/paywall.tsx"],"sourcesContent":["import { LockIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport type { Decision, Sale, Terms, Version } from \"../../client/generated/wire.js\";\nimport { useLanguageName, useMessages } from \"../../i18n/context.js\";\nimport type { Translator } from \"../../i18n/messages.js\";\nimport { ContentKitContext, useContentKitClient, type ContentKitErrorHandler } from \"../../react/context.js\";\nimport { findVersion } from \"../../react/detail.js\";\nimport { useVersionParam } from \"../../react/location.js\";\nimport { useKinds } from \"../../react/groups.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"#ckui/ui/select\";\nimport { contentError, ErrorLine, formatDate, useAction } from \"../social/parts.js\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport type { PlayerMenuItem } from \"../video-player.js\";\n\n/** An offer's price and period in words: \"$2.99\", \"$4.99 / 30 days\", \"$1.99 for 48 hours\". */\nexport function priceLabel(m: Translator, terms: Terms): string {\n let price: string;\n try {\n price = new Intl.NumberFormat(m.language, { style: \"currency\", currency: terms.currency }).format(Number(terms.amount));\n } catch {\n price = `${terms.amount} ${terms.currency}`;\n }\n const period = (hours: number) => (hours % 24 === 0 ? m.plural(\"paywall.days\", hours / 24) : m.plural(\"paywall.hours\", hours));\n if (terms.renew_hours) return m.t(\"paywall.renews\", { price, period: period(terms.renew_hours) });\n if (terms.access_hours) return m.t(\"paywall.rents\", { price, period: period(terms.access_hours) });\n return price;\n}\n\nexport interface PaywallProps {\n /** The locked version's (or card's) decision. */\n decision: Decision;\n /**\n * Starts a purchase: the host creates the order through billing-ui\n * (createOrder with offer.price_id) and shows OrderCheckout; for a tier\n * (no offer), its subscription page. A free group offer joins here instead.\n */\n onCheckout: (o: { key: string; offer?: Sale }) => void;\n /** Tiers' names and prices from the host's catalog, by tier key. */\n tiers?: Readonly<Record<string, { label?: string; price?: string }>>;\n /** Asks a signed-out reader to sign in (they may hold it already); default the provider's. */\n onSignIn?: () => void;\n labels?: Labels;\n /** Below the offers: the host's own. */\n children?: ReactNode;\n client?: ContentKitClient;\n onError?: ContentKitErrorHandler;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** Why content is locked and how to open it: its offers, the tiers that include it, previews and when it turns free. */\nexport function Paywall({ decision, onCheckout, tiers, onSignIn, labels, children, client: own, onError, className, appearance }: PaywallProps) {\n const m = useMessages();\n const { t } = m;\n const ctx = useContext(ContentKitContext);\n const client = useContentKitClient(own);\n const { kinds } = useKinds({ client: own });\n const join = useAction(\"membership.join\", onError);\n if (decision.allowed) return null;\n const signIn = onSignIn ?? ctx?.onSignIn;\n const offers = decision.offers ?? [];\n const sold = new Set(offers.map((o) => o.key));\n const siteTiers = kinds?.tiers ?? Object.keys(tiers ?? {});\n const tierKeys = (decision.sell ?? []).filter((k) => !sold.has(k) && (siteTiers.includes(k) || !!tiers?.[k]));\n const required = decision.requires;\n // A required key nothing here offers gets its own button; the others wait for it.\n const requires = required && !offers.some((o) => o.key === required) ? required : undefined;\n const waits = (key: string) => !!required && key !== required;\n const preview = decision.preview;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 rounded-lg border border-border p-4 text-sm\", className)} data-ckui=\"paywall\">\n <p className=\"flex items-center gap-2 font-medium\">\n <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-4\" aria-hidden />\n {decision.unknown ? t(\"paywall.unknown\") : required ? t(\"paywall.requiresFirst\") : t(\"paywall.locked\")}\n </p>\n {preview?.pages ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewPages\", preview.pages)}</p> : null}\n {preview?.segments ? <p className=\"text-muted-foreground\">{m.plural(\"paywall.previewSegments\", preview.segments)}</p> : null}\n {decision.free_after && <p className=\"text-muted-foreground\">{t(\"paywall.freeFrom\", { date: formatDate(m, decision.free_after, false) })}</p>}\n {!decision.unknown && (\n <div className=\"flex flex-wrap gap-2\" data-ckui=\"paywall-offers\">\n {requires && (\n <Button size=\"sm\" variant=\"outline\" onClick={() => onCheckout({ key: requires })}>\n {t(\"paywall.joinFirst\")}\n </Button>\n )}\n {offers.map((o) =>\n o.target.kind === \"group\" && Number(o.terms.amount) === 0 ? (\n <Button\n key={o.key}\n size=\"sm\"\n disabled={waits(o.key) || join.pending}\n onClick={() => (ctx?.viewer === null && signIn ? signIn() : void join.run(() => client.offers.join(o.target.id)))}\n data-ckui=\"paywall-offer\"\n data-target=\"group\"\n data-free=\"\"\n >\n {t(\"paywall.joinFirst\")}\n </Button>\n ) : (\n <Button key={o.key} size=\"sm\" disabled={waits(o.key)} onClick={() => onCheckout({ key: o.key, offer: o })} data-ckui=\"paywall-offer\" data-target={o.target.kind}>\n {t(o.target.kind === \"group\" ? \"paywall.joinFor\" : o.target.kind === \"version\" ? \"paywall.unlockVersionFor\" : \"paywall.unlockFor\", { price: priceLabel(m, o.terms) })}\n </Button>\n ),\n )}\n {tierKeys.map((k) => (\n <Button key={k} size=\"sm\" variant={offers.length ? \"outline\" : \"default\"} onClick={() => onCheckout({ key: k })} data-ckui=\"paywall-tier\" data-tier={k}>\n {t(\"paywall.subscribe\", { tier: tiers?.[k]?.label ?? labelOf(labels, k) })}\n {tiers?.[k]?.price && <span className=\"ms-1 opacity-80\">{tiers[k]!.price}</span>}\n </Button>\n ))}\n </div>\n )}\n {join.error && <ErrorLine>{contentError(m, join.error)}</ErrorLine>}\n {ctx?.viewer === null && signIn && (\n <p className=\"text-muted-foreground\">\n {t(\"paywall.haveIt\")}{\" \"}\n <button type=\"button\" className=\"font-medium text-foreground underline-offset-4 hover:underline\" onClick={signIn}>\n {t(\"paywall.signIn\")}\n </button>\n </p>\n )}\n {children}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionPickerProps {\n /** The content's live versions (useContentDetail().versions). */\n versions: readonly Version[];\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n className?: string;\n appearance?: ContentKitUiAppearance;\n}\n\n/** A version's name: its label, else its traits, else its languages. */\nfunction versionName(v: Version, languageName: (l: string) => string, labels?: Labels): string {\n return v.label || (v.traits.length ? v.traits.map((x) => labelOf(labels, x)).join(\" · \") : v.languages.map(languageName).join(\", \"));\n}\n\nconst primary = (v: Version) => v.languages[0] ?? \"\";\n\n/**\n * The shown version (value, else the page's ?v=, else the reader's\n * language's default), the versions' languages, the shown language's\n * variants, and the change handler (default: write ?v=).\n */\nfunction useChoices(versions: readonly Version[], value: string | null | undefined, onChange: ((v: Version) => void) | undefined) {\n const [param, setParam] = useVersionParam();\n const reader = useContext(ContentKitContext)?.language;\n const selected = value === undefined ? param : value;\n const change = useCallback((v: Version) => (onChange ? onChange(v) : setParam(v.code)), [onChange, setParam]);\n return useMemo(() => {\n const current = findVersion(versions, selected) ?? (reader ? versions.find((v) => v.languages.includes(reader)) : undefined) ?? versions[0];\n const language = current ? primary(current) : \"\";\n return { current, language, languages: [...new Set(versions.map(primary))], variants: versions.filter((v) => primary(v) === language), change };\n }, [versions, selected, reader, change]);\n}\n\n/**\n * Chooses among a content's versions: a language first when they differ in\n * language, then its variants (traits, labels). The choice is the page's ?v=\n * unless value and onChange say otherwise.\n */\nexport function VersionPicker({ versions, value, onChange, labels, className, appearance }: VersionPickerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, value, onChange);\n if (versions.length < 2 || !current) return null;\n return (\n <ContentKitUiRoot appearance={appearance} className={cn(\"flex flex-wrap items-center gap-2 text-sm\", className)} data-ckui=\"version-picker\">\n {languages.length > 1 && (\n <Select\n value={language}\n onValueChange={(l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n }}\n >\n <SelectTrigger size=\"sm\" aria-label={t(\"versions.language\")}>\n <SelectValue>{(l: string) => languageName(l)}</SelectValue>\n </SelectTrigger>\n <SelectContent>\n {languages.map((l) => (\n <SelectItem key={l} value={l}>\n {languageName(l)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n {variants.length > 1 && (\n <div role=\"radiogroup\" aria-label={t(\"versions.version\")} className=\"flex flex-wrap gap-1\">\n {variants.map((v) => (\n <Button\n key={v.id}\n size=\"sm\"\n role=\"radio\"\n aria-checked={v.id === current.id}\n variant={v.id === current.id ? \"default\" : \"outline\"}\n onClick={() => change(v)}\n data-ckui=\"version-option\"\n data-locked={v.decision.allowed ? undefined : \"\"}\n >\n {!v.decision.allowed && <HugeiconsIcon icon={LockIcon} strokeWidth={2} className=\"size-3.5\" aria-label={t(\"versions.locked\")} />}\n {versionName(v, languageName, labels)}\n </Button>\n ))}\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n\nexport interface VersionMenuOptions {\n /** The version shown, by code or id; default the page's ?v=, else the reader's language's default. */\n value?: string | null;\n /** Receives the chosen version; default writes its code to the page's ?v=. */\n onChange?: (version: Version) => void;\n labels?: Labels;\n}\n\n/** VersionPicker as VideoPlayer settings entries (menuItems), for a watch page. */\nexport function useVersionMenuItems(versions: readonly Version[], o: VersionMenuOptions = {}): PlayerMenuItem[] {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const { current, language, languages, variants, change } = useChoices(versions, o.value, o.onChange);\n const { labels } = o;\n return useMemo(() => {\n if (versions.length < 2 || !current) return [];\n const out: PlayerMenuItem[] = [];\n if (languages.length > 1)\n out.push({\n label: t(\"versions.language\"),\n value: language,\n options: languages.map((l) => ({ value: l, label: languageName(l) })),\n onChange: (l) => {\n const v = versions.find((x) => primary(x) === l);\n if (v) change(v);\n },\n });\n if (variants.length > 1)\n out.push({\n label: t(\"versions.version\"),\n value: current.id,\n options: variants.map((v) => {\n const name = versionName(v, languageName, labels);\n return { value: v.id, label: v.decision.allowed ? name : t(\"versions.lockedOption\", { name }) };\n }),\n onChange: (id) => {\n const v = versions.find((x) => x.id === id);\n if (v) change(v);\n },\n });\n return out;\n }, [versions, current, language, languages, variants, change, labels, t, languageName]);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ContentKitUiAppearance } from '../../appearance.js';
|
|
2
|
+
import { ContentKitClient } from '../../client/client.js';
|
|
3
|
+
import { Labels } from '../groups/parts.js';
|
|
4
|
+
export interface ReaderStatsProps {
|
|
5
|
+
/** Display names of content kinds by identifier. */
|
|
6
|
+
labels?: Labels;
|
|
7
|
+
client?: ContentKitClient;
|
|
8
|
+
appearance?: ContentKitUiAppearance;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
/** The signed-in reader's activity: contents finished per kind, their comments and the reactions those received. Nothing signed out. */
|
|
12
|
+
export declare function ReaderStats({ labels, client, appearance, className }: ReaderStatsProps): import('react').JSX.Element | null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useMessages as e } from "../../i18n/context.js";
|
|
2
|
+
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
+
import { Skeleton as n } from "../../ui/skeleton.js";
|
|
4
|
+
import { contentError as r } from "../social/parts.js";
|
|
5
|
+
import { labelOf as i } from "../groups/parts.js";
|
|
6
|
+
import { useMyStats as a } from "../../react/stats.js";
|
|
7
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
8
|
+
import { cn as c } from "cn";
|
|
9
|
+
//#region src/components/content/reader-stats.tsx
|
|
10
|
+
function l({ labels: l, client: u, appearance: d, className: f }) {
|
|
11
|
+
let p = e(), { t: m } = p, h = a({ client: u }), g = (e) => /* @__PURE__ */ o(t, {
|
|
12
|
+
appearance: d,
|
|
13
|
+
className: c("grid gap-3 text-sm", f),
|
|
14
|
+
"data-ckui": "reader-stats",
|
|
15
|
+
children: e
|
|
16
|
+
});
|
|
17
|
+
if (h.error) return g(/* @__PURE__ */ o("p", {
|
|
18
|
+
role: "alert",
|
|
19
|
+
className: "text-destructive",
|
|
20
|
+
children: r(p, h.error)
|
|
21
|
+
}));
|
|
22
|
+
if (!h.data) return h.loading ? g(/* @__PURE__ */ o(n, { className: "h-16 w-full" })) : null;
|
|
23
|
+
let _ = [
|
|
24
|
+
...Object.entries(h.data.completed).map(([e, t]) => [m("myStats.completed", { kind: i(l, e) }), t]),
|
|
25
|
+
[m("myStats.comments"), h.data.comments],
|
|
26
|
+
[m("myStats.likes"), h.data.comment_likes],
|
|
27
|
+
[m("myStats.dislikes"), h.data.comment_dislikes]
|
|
28
|
+
];
|
|
29
|
+
return g(/* @__PURE__ */ o("dl", {
|
|
30
|
+
className: "grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
31
|
+
children: _.map(([e, t]) => /* @__PURE__ */ s("div", {
|
|
32
|
+
className: "grid gap-0.5 rounded-lg border border-border p-3",
|
|
33
|
+
"data-ckui": "reader-stat",
|
|
34
|
+
children: [/* @__PURE__ */ o("dt", {
|
|
35
|
+
className: "text-xs text-muted-foreground",
|
|
36
|
+
children: e
|
|
37
|
+
}), /* @__PURE__ */ o("dd", {
|
|
38
|
+
className: "text-lg font-semibold tabular-nums",
|
|
39
|
+
children: t
|
|
40
|
+
})]
|
|
41
|
+
}, e))
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { l as ReaderStats };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=reader-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reader-stats.js","names":[],"sources":["../../../src/components/content/reader-stats.tsx"],"sourcesContent":["import { cn } from \"cn\";\nimport type { ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../../appearance.js\";\nimport type { ContentKitClient } from \"../../client/client.js\";\nimport { useMessages } from \"../../i18n/context.js\";\nimport { useMyStats } from \"../../react/stats.js\";\nimport { ContentKitUiRoot } from \"../../scope.js\";\nimport { Skeleton } from \"#ckui/ui/skeleton\";\nimport { labelOf, type Labels } from \"../groups/parts.js\";\nimport { contentError } from \"../social/parts.js\";\n\nexport interface ReaderStatsProps {\n /** Display names of content kinds by identifier. */\n labels?: Labels;\n client?: ContentKitClient;\n appearance?: ContentKitUiAppearance;\n className?: string;\n}\n\n/** The signed-in reader's activity: contents finished per kind, their comments and the reactions those received. Nothing signed out. */\nexport function ReaderStats({ labels, client, appearance, className }: ReaderStatsProps) {\n const m = useMessages();\n const { t } = m;\n const s = useMyStats({ client });\n const root = (children: ReactNode) => (\n <ContentKitUiRoot appearance={appearance} className={cn(\"grid gap-3 text-sm\", className)} data-ckui=\"reader-stats\">\n {children}\n </ContentKitUiRoot>\n );\n if (s.error) return root(<p role=\"alert\" className=\"text-destructive\">{contentError(m, s.error)}</p>);\n if (!s.data) return s.loading ? root(<Skeleton className=\"h-16 w-full\" />) : null;\n const stats: [string, number][] = [\n ...Object.entries(s.data.completed).map(([kind, n]): [string, number] => [t(\"myStats.completed\", { kind: labelOf(labels, kind) }), n]),\n [t(\"myStats.comments\"), s.data.comments],\n [t(\"myStats.likes\"), s.data.comment_likes],\n [t(\"myStats.dislikes\"), s.data.comment_dislikes],\n ];\n return root(\n <dl className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n {stats.map(([label, n]) => (\n <div key={label} className=\"grid gap-0.5 rounded-lg border border-border p-3\" data-ckui=\"reader-stat\">\n <dt className=\"text-xs text-muted-foreground\">{label}</dt>\n <dd className=\"text-lg font-semibold tabular-nums\">{n}</dd>\n </div>\n ))}\n </dl>,\n );\n}\n"],"mappings":";;;;;;;;;AAoBA,SAAgB,EAAY,EAAE,WAAQ,WAAQ,eAAY,gBAA+B;CACvF,IAAM,IAAI,EAAY,GAChB,EAAE,SAAM,GACR,IAAI,EAAW,EAAE,UAAO,CAAC,GACzB,KAAQ,MACZ,kBAAC,GAAD;EAA8B;EAAY,WAAW,EAAG,sBAAsB,CAAS;EAAG,aAAU;EACjG;CACe,CAAA;CAEpB,IAAI,EAAE,OAAO,OAAO,EAAK,kBAAC,KAAD;EAAG,MAAK;EAAQ,WAAU;EAAoB,UAAA,EAAa,GAAG,EAAE,KAAK;CAAK,CAAA,CAAC;CACpG,IAAI,CAAC,EAAE,MAAM,OAAO,EAAE,UAAU,EAAK,kBAAC,GAAD,EAAU,WAAU,cAAe,CAAA,CAAC,IAAI;CAC7E,IAAM,IAA4B;EAChC,GAAG,OAAO,QAAQ,EAAE,KAAK,SAAS,CAAC,CAAC,KAAK,CAAC,GAAM,OAAyB,CAAC,EAAE,qBAAqB,EAAE,MAAM,EAAQ,GAAQ,CAAI,EAAE,CAAC,GAAG,CAAC,CAAC;EACrI,CAAC,EAAE,kBAAkB,GAAG,EAAE,KAAK,QAAQ;EACvC,CAAC,EAAE,eAAe,GAAG,EAAE,KAAK,aAAa;EACzC,CAAC,EAAE,kBAAkB,GAAG,EAAE,KAAK,gBAAgB;CACjD;CACA,OAAO,EACL,kBAAC,MAAD;EAAI,WAAU;EACX,UAAA,EAAM,KAAK,CAAC,GAAO,OAClB,kBAAC,OAAD;GAAiB,WAAU;GAAmD,aAAU;GAAxF,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;IAAiC,UAAA;GAAU,CAAA,GACzD,kBAAC,MAAD;IAAI,WAAU;IAAsC,UAAA;GAAM,CAAA,CACvD;EAHK,GAAA,CAGL,CACN;CACC,CAAA,CACN;AACF"}
|
|
@@ -2,22 +2,22 @@ import { useMessages as e } from "../../i18n/context.js";
|
|
|
2
2
|
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
3
|
import { ContentKitContext as n, useContentKitClient as r } from "../../react/context.js";
|
|
4
4
|
import { decodeImage as i } from "../../client/image.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { useGroup as a } from "../../react/groups.js";
|
|
6
|
+
import { Button as o } from "../../ui/button.js";
|
|
7
|
+
import { ImageCropDialog as s } from "../image-crop-dialog.js";
|
|
8
|
+
import { Badge as c } from "../../ui/badge.js";
|
|
9
|
+
import { Skeleton as l } from "../../ui/skeleton.js";
|
|
10
|
+
import { ErrorLine as u, contentError as d, useAction as f } from "../social/parts.js";
|
|
11
11
|
import { GroupAvatar as p, GroupCover as m } from "./parts.js";
|
|
12
12
|
import { priceLabel as h } from "../content/paywall.js";
|
|
13
13
|
import { useContext as g, useRef as _, useState as v } from "react";
|
|
14
14
|
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
15
15
|
import { cn as x } from "cn";
|
|
16
16
|
//#region src/components/groups/banner.tsx
|
|
17
|
-
function S({ group: i, onCheckout:
|
|
18
|
-
let v = e(), { t: S } = v, C = r(
|
|
17
|
+
function S({ group: i, onCheckout: a, onSignIn: s, client: l, onError: p, className: m, appearance: _ }) {
|
|
18
|
+
let v = e(), { t: S } = v, C = r(l), w = g(n), T = f("membership.join", p), E = i.access;
|
|
19
19
|
if (!E || E.editor || !E.offer && !E.member && !E.unknown) return null;
|
|
20
|
-
let D = w?.viewer === null, O =
|
|
20
|
+
let D = w?.viewer === null, O = s ?? w?.onSignIn, k = E.offer && Number(E.offer.terms.amount) > 0 ? E.offer : void 0;
|
|
21
21
|
return /* @__PURE__ */ b(t, {
|
|
22
22
|
appearance: _,
|
|
23
23
|
className: x("grid gap-2 rounded-lg border border-border p-3 text-sm", m),
|
|
@@ -28,7 +28,7 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
|
|
|
28
28
|
children: S("membership.unknown")
|
|
29
29
|
}) : E.member ? /* @__PURE__ */ b("div", {
|
|
30
30
|
className: "flex flex-wrap items-center gap-2",
|
|
31
|
-
children: [/* @__PURE__ */ y(
|
|
31
|
+
children: [/* @__PURE__ */ y(c, { children: S("membership.member") }), !k && /* @__PURE__ */ y(o, {
|
|
32
32
|
size: "sm",
|
|
33
33
|
variant: "ghost",
|
|
34
34
|
disabled: T.pending,
|
|
@@ -40,13 +40,13 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
|
|
|
40
40
|
children: [/* @__PURE__ */ y("p", {
|
|
41
41
|
className: "text-muted-foreground",
|
|
42
42
|
children: S(k ? "membership.paidPitch" : "membership.freePitch")
|
|
43
|
-
}), /* @__PURE__ */ y(
|
|
43
|
+
}), /* @__PURE__ */ y(o, {
|
|
44
44
|
size: "sm",
|
|
45
45
|
className: "justify-self-start",
|
|
46
|
-
disabled: T.pending || !!k && !
|
|
46
|
+
disabled: T.pending || !!k && !a,
|
|
47
47
|
onClick: () => {
|
|
48
48
|
if (D && O) return O();
|
|
49
|
-
if (k) return
|
|
49
|
+
if (k) return a?.({
|
|
50
50
|
key: E.key,
|
|
51
51
|
offer: k
|
|
52
52
|
});
|
|
@@ -54,69 +54,69 @@ function S({ group: i, onCheckout: o, onSignIn: c, client: f, onError: p, classN
|
|
|
54
54
|
},
|
|
55
55
|
children: k ? S("membership.joinFor", { price: h(v, k.terms) }) : S("membership.join")
|
|
56
56
|
})]
|
|
57
|
-
}), T.error && /* @__PURE__ */ y(
|
|
57
|
+
}), T.error && /* @__PURE__ */ y(u, { children: d(v, T.error) })]
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
function C(n) {
|
|
61
|
-
let r = e(), i =
|
|
61
|
+
let r = e(), i = a(n.group ? null : n.id, {
|
|
62
62
|
language: n.language,
|
|
63
63
|
client: n.client
|
|
64
|
-
}),
|
|
65
|
-
if (!
|
|
66
|
-
let
|
|
64
|
+
}), o = n.group ?? i.group;
|
|
65
|
+
if (!o) return i.error ? /* @__PURE__ */ y(u, { children: d(r, i.error) }) : /* @__PURE__ */ y(l, { className: "h-48 w-full" });
|
|
66
|
+
let s = o.text.find((e) => e.language === (n.language ?? o.name_language)) ?? o.text.find((e) => e.description), c = o.counts.filter((e) => e.language === (n.language ?? "")).reduce((e, t) => e + t.count, 0);
|
|
67
67
|
return /* @__PURE__ */ b(t, {
|
|
68
68
|
appearance: n.appearance,
|
|
69
69
|
className: x("grid gap-3 text-sm", n.className),
|
|
70
70
|
"data-ckui": "group-banner",
|
|
71
|
-
"data-kind":
|
|
71
|
+
"data-kind": o.kind,
|
|
72
72
|
children: [
|
|
73
73
|
/* @__PURE__ */ y(m, {
|
|
74
|
-
group:
|
|
74
|
+
group: o,
|
|
75
75
|
className: "aspect-[3/1] w-full"
|
|
76
76
|
}),
|
|
77
77
|
/* @__PURE__ */ b("div", {
|
|
78
78
|
className: "flex flex-wrap items-end gap-4 px-2 sm:px-4",
|
|
79
79
|
children: [
|
|
80
80
|
/* @__PURE__ */ y(p, {
|
|
81
|
-
group:
|
|
81
|
+
group: o,
|
|
82
82
|
className: "-mt-12 size-20 border-4 border-background sm:-mt-16 sm:size-28"
|
|
83
83
|
}),
|
|
84
84
|
/* @__PURE__ */ b("div", {
|
|
85
85
|
className: "grid min-w-0 flex-1 gap-1",
|
|
86
86
|
children: [/* @__PURE__ */ y("h1", {
|
|
87
87
|
className: "text-xl font-semibold break-words",
|
|
88
|
-
children:
|
|
88
|
+
children: o.name || o.slug
|
|
89
89
|
}), /* @__PURE__ */ y("p", {
|
|
90
90
|
className: "text-xs text-muted-foreground tabular-nums",
|
|
91
|
-
children: r.plural("groups.items",
|
|
91
|
+
children: r.plural("groups.items", c)
|
|
92
92
|
})]
|
|
93
93
|
}),
|
|
94
94
|
/* @__PURE__ */ b("div", {
|
|
95
95
|
className: "flex flex-wrap items-start gap-2",
|
|
96
96
|
children: [/* @__PURE__ */ y(S, {
|
|
97
|
-
group:
|
|
97
|
+
group: o,
|
|
98
98
|
onCheckout: n.onCheckout,
|
|
99
99
|
client: n.client
|
|
100
100
|
}), n.children]
|
|
101
101
|
})
|
|
102
102
|
]
|
|
103
103
|
}),
|
|
104
|
-
|
|
104
|
+
s?.description && /* @__PURE__ */ y("p", {
|
|
105
105
|
className: "px-2 break-words whitespace-pre-line sm:px-4",
|
|
106
|
-
children:
|
|
106
|
+
children: s.description
|
|
107
107
|
})
|
|
108
108
|
]
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
-
function w({ group: t, slot: n, crop: r = !0, client:
|
|
112
|
-
let { t: h } = e(), g =
|
|
111
|
+
function w({ group: t, slot: n, crop: r = !0, client: c, onError: l, className: d }) {
|
|
112
|
+
let { t: h } = e(), g = a(t.id, { client: c }), S = _(null), C = f("group.save", l), [w, T] = v(null), [E, D] = v(null), O = g.group ?? t, k = n === "cover" ? !!O.cover?.image || !!O.cover?.content_id && !O.cover.auto : !!O.avatar?.image, A = (e, t) => C.run(() => g.uploadImage(n, e, {
|
|
113
113
|
wait: !1,
|
|
114
114
|
edit: t
|
|
115
115
|
})), j = () => {
|
|
116
116
|
w?.revoke?.(), T(null), D(null);
|
|
117
117
|
};
|
|
118
118
|
return /* @__PURE__ */ b("div", {
|
|
119
|
-
className: x("flex flex-wrap items-center gap-3",
|
|
119
|
+
className: x("flex flex-wrap items-center gap-3", d),
|
|
120
120
|
"data-ckui": "group-image-editor",
|
|
121
121
|
"data-slot": n,
|
|
122
122
|
children: [
|
|
@@ -134,14 +134,14 @@ function w({ group: t, slot: n, crop: r = !0, client: s, onError: c, className:
|
|
|
134
134
|
children: [
|
|
135
135
|
/* @__PURE__ */ b("div", {
|
|
136
136
|
className: "flex flex-wrap gap-2",
|
|
137
|
-
children: [/* @__PURE__ */ y(
|
|
137
|
+
children: [/* @__PURE__ */ y(o, {
|
|
138
138
|
type: "button",
|
|
139
139
|
size: "sm",
|
|
140
140
|
variant: "outline",
|
|
141
141
|
disabled: C.pending,
|
|
142
142
|
onClick: () => S.current?.click(),
|
|
143
143
|
children: C.pending ? h("groupEditor.uploading") : h(n === "avatar" ? "groupEditor.uploadAvatar" : "groupEditor.uploadCover")
|
|
144
|
-
}), k && /* @__PURE__ */ y(
|
|
144
|
+
}), k && /* @__PURE__ */ y(o, {
|
|
145
145
|
type: "button",
|
|
146
146
|
size: "sm",
|
|
147
147
|
variant: "ghost",
|
|
@@ -166,10 +166,10 @@ function w({ group: t, slot: n, crop: r = !0, client: s, onError: c, className:
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}),
|
|
169
|
-
/* @__PURE__ */ y(
|
|
169
|
+
/* @__PURE__ */ y(u, { error: C.error })
|
|
170
170
|
]
|
|
171
171
|
}),
|
|
172
|
-
/* @__PURE__ */ y(
|
|
172
|
+
/* @__PURE__ */ y(s, {
|
|
173
173
|
open: !!w,
|
|
174
174
|
onOpenChange: (e) => !e && j(),
|
|
175
175
|
source: w,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { useMessages as e } from "../../i18n/context.js";
|
|
2
2
|
import { ContentKitUiRoot as t } from "../../scope.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { useGroups as n } from "../../react/groups.js";
|
|
4
|
+
import { Button as r } from "../../ui/button.js";
|
|
5
|
+
import { Skeleton as i } from "../../ui/skeleton.js";
|
|
6
|
+
import { contentError as a } from "../social/parts.js";
|
|
7
|
+
import { LETTERS as o } from "../../client/group-fields.js";
|
|
8
8
|
import { GroupCover as s, GroupLink as c } from "./parts.js";
|
|
9
9
|
import { useState as l } from "react";
|
|
10
10
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
11
11
|
import { cn as f } from "cn";
|
|
12
12
|
//#region src/components/groups/directory.tsx
|
|
13
13
|
function p(p) {
|
|
14
|
-
let m = e(), { t: h } = m, [g, _] = l(null), v = p.letter === void 0 ? g : p.letter, y = (e) => p.onLetterChange ? p.onLetterChange(e) : _(e), b =
|
|
14
|
+
let m = e(), { t: h } = m, [g, _] = l(null), v = p.letter === void 0 ? g : p.letter, y = (e) => p.onLetterChange ? p.onLetterChange(e) : _(e), b = n({
|
|
15
15
|
kind: p.kind,
|
|
16
16
|
language: p.language,
|
|
17
17
|
contentKind: p.contentKind,
|
|
@@ -31,7 +31,7 @@ function p(p) {
|
|
|
31
31
|
"aria-label": h("groups.byLetter"),
|
|
32
32
|
className: "flex flex-wrap gap-1",
|
|
33
33
|
"data-ckui": "letters",
|
|
34
|
-
children: [null, ...
|
|
34
|
+
children: [null, ...o].map((e) => /* @__PURE__ */ u(r, {
|
|
35
35
|
size: "xs",
|
|
36
36
|
variant: v === e ? "default" : "ghost",
|
|
37
37
|
"aria-pressed": v === e,
|
|
@@ -44,14 +44,14 @@ function p(p) {
|
|
|
44
44
|
b.loading && !b.items.length ? /* @__PURE__ */ u("div", {
|
|
45
45
|
className: x ? "grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-3" : "grid gap-2",
|
|
46
46
|
"aria-busy": !0,
|
|
47
|
-
children: Array.from({ length: 6 }, (e, t) => /* @__PURE__ */ u(
|
|
47
|
+
children: Array.from({ length: 6 }, (e, t) => /* @__PURE__ */ u(i, { className: x ? "aspect-[3/4] w-full" : "h-6 w-48" }, t))
|
|
48
48
|
}) : b.error && !b.items.length ? /* @__PURE__ */ d("div", {
|
|
49
49
|
role: "alert",
|
|
50
50
|
className: "grid justify-items-start gap-2",
|
|
51
51
|
children: [/* @__PURE__ */ u("p", {
|
|
52
52
|
className: "text-destructive",
|
|
53
|
-
children:
|
|
54
|
-
}), /* @__PURE__ */ u(
|
|
53
|
+
children: a(m, b.error)
|
|
54
|
+
}), /* @__PURE__ */ u(r, {
|
|
55
55
|
size: "sm",
|
|
56
56
|
variant: "outline",
|
|
57
57
|
onClick: b.reload,
|
|
@@ -100,7 +100,7 @@ function p(p) {
|
|
|
100
100
|
role: "status",
|
|
101
101
|
children: h("groups.empty")
|
|
102
102
|
}),
|
|
103
|
-
b.hasMore && /* @__PURE__ */ u(
|
|
103
|
+
b.hasMore && /* @__PURE__ */ u(r, {
|
|
104
104
|
variant: "outline",
|
|
105
105
|
size: "sm",
|
|
106
106
|
className: "justify-self-center",
|