@openrails/contentkit-ui 0.68.1 → 0.70.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 +17 -13
- package/dist/client/content/comments.js +3 -3
- package/dist/client/content/comments.js.map +1 -1
- package/dist/client/content/posts.d.ts +16 -8
- package/dist/client/content/posts.js +5 -13
- package/dist/client/content/posts.js.map +1 -1
- package/dist/client/content/reactions.js +4 -4
- package/dist/client/content/reactions.js.map +1 -1
- package/dist/client/content/types.d.ts +2 -2
- package/dist/client/content/types.js +1 -1
- package/dist/client/content/types.js.map +1 -1
- package/dist/client/generated/error-codes.d.ts +1 -1
- package/dist/client/generated/error-codes.js +4 -0
- package/dist/client/generated/error-codes.js.map +1 -1
- package/dist/client/generated/routes.d.ts +2 -54
- package/dist/client/generated/routes.js +2 -74
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/generated/wire.d.ts +5 -1
- package/dist/client/generated/wire.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/media/client.d.ts +7 -5
- package/dist/client/media/client.js +1 -1
- package/dist/client/media/client.js.map +1 -1
- package/dist/client/media/rules.d.ts +2 -2
- package/dist/client/media/rules.js +1 -5
- package/dist/client/media/rules.js.map +1 -1
- package/dist/client.js +5 -5
- package/dist/locales/de.js +1 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/locales/en.d.ts +1 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +1 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/locales/ja.js +1 -0
- package/dist/locales/ja.js.map +1 -1
- package/dist/locales/ko.js +1 -0
- package/dist/locales/ko.js.map +1 -1
- package/dist/locales/zh.js +1 -0
- package/dist/locales/zh.js.map +1 -1
- package/dist/react/posts.d.ts +9 -7
- package/dist/react/posts.js +132 -99
- package/dist/react/posts.js.map +1 -1
- package/dist/react/public.d.ts +2 -2
- package/dist/react/public.js +7 -4
- package/dist/react/public.js.map +1 -1
- package/package.json +1 -1
package/dist/locales/zh.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { social } from \"./social/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n lockedOne: \"还有 1 项已锁定\",\n lockedMany: \"还有 {count} 项已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n errors: {\n generic: \"我们这边出了问题,请重试。\",\n network: \"连接已断开,请检查网络后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"图片已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"zh.js","names":[],"sources":["../../src/locales/zh.ts"],"sourcesContent":["import type { ContentKitUiMessageBundle } from \"../i18n/messages.js\";\nimport { social } from \"./social/zh.js\";\n\nexport const zh: ContentKitUiMessageBundle = {\n ...social,\n common: {\n cancel: \"取消\",\n close: \"关闭\",\n save: \"保存\",\n saving: \"正在保存…\",\n retry: \"重试\",\n change: \"更换\",\n upload: \"上传\",\n editCrop: \"编辑裁剪\",\n remove: \"移除\",\n rename: \"重命名\",\n replace: \"替换\",\n },\n crop: {\n title: \"裁剪图片\",\n avatarTitle: \"裁剪头像\",\n coverTitle: \"裁剪封面\",\n description: \"拖动调整位置。滚动、双指缩放或使用滑块来缩放。\",\n area: \"裁剪区域。使用方向键移动图片,按 + 或 − 缩放,按 R 旋转。\",\n zoom: \"缩放\",\n zoomIn: \"放大\",\n zoomOut: \"缩小\",\n rotateLeft: \"向左旋转\",\n rotateRight: \"向右旋转\",\n reset: \"重置\",\n undersized: \"此裁剪宽 {width} 像素;在高分辨率屏幕上需要 {target} 像素或以上才清晰。\",\n tooSmall: \"此图片最宽 {width} 像素;至少需要 {min} 像素。请使用更大的图片。\",\n shape: \"比例\",\n original: \"原始比例\",\n preview: \"效果预览\",\n size: \"{width} × {height} 像素\",\n },\n sortable: {\n handle: \"调整 {name} 的顺序\",\n instructions: \"要调整顺序,请按空格键或回车键,用方向键移动,再按空格键或回车键放下。按 Esc 取消。\",\n pickedUp: \"已拿起 {name},位于第 {position} 位,共 {total} 项。\",\n over: \"{name} 将移到第 {position} 位,共 {total} 项。\",\n dropped: \"{name} 已放在第 {position} 位,共 {total} 项。\",\n droppedAway: \"{name} 已放下。\",\n canceled: \"已取消移动 {name}。\",\n },\n folder: {\n label: \"媒体\",\n drop: \"将文件拖放到此处,或点击选择\",\n add: \"添加文件\",\n commit: \"添加 {count} 个\",\n commitAgain: \"重新添加\",\n adding: \"正在添加…\",\n queued: \"等待中\",\n uploaded: \"已上传\",\n processing: \"处理中…\",\n processingPercent: \"处理中 {percent}%\",\n edited: \"已编辑\",\n failed: \"未处理\",\n failedVideo: \"无法播放\",\n crop: \"裁剪和旋转\",\n select: \"选择 {name}\",\n selected: \"已选择 {count} 个\",\n removeSelected: \"移除所选\",\n clearSelection: \"取消选择\",\n confirmRemove: \"要移除 {count} 个文件吗?此操作无法撤销。\",\n renameLabel: \"{name} 的新名称\",\n paused: \"上传已暂停\",\n resume: \"继续\",\n dismiss: \"关闭\",\n limitSize: \"{what}最大 {size}\",\n limitMax: \"最多 {max} 个\",\n limitAspect: \"视频比例为 {min} 至 {max}\",\n kinds: {\n image: \"图片\",\n video: \"视频\",\n audio: \"音频\",\n subtitle: \"字幕\",\n file: \"文件\",\n },\n },\n readiness: {\n processing: \"正在处理媒体\",\n processingHint: \"所有文件处理完成后将自动发布。\",\n failedOne: \"无法处理 {name}\",\n failedMany: \"无法处理 {count} 个文件:{names}\",\n failedHint: \"移除这些文件后将自动发布。\",\n },\n avatar: {\n label: \"头像\",\n empty: \"暂无头像\",\n hint: \"正方形图片,至少 {size} 像素。\",\n },\n cover: {\n label: \"封面图片\",\n empty: \"暂无封面\",\n hint: \"横向图片,至少 {width} × {height} 像素。\",\n drop: \"将图片拖放到此处\",\n },\n poster: {\n title: \"设置封面\",\n description: \"从视频中选取一帧,或上传一张图片。封面与视频比例相同。\",\n frame: \"视频画面\",\n upload: \"上传图片\",\n time: \"时间\",\n previousFrame: \"上一帧\",\n nextFrame: \"下一帧\",\n jumpTo: \"跳到 {time}\",\n useFrame: \"使用此帧\",\n crop: \"裁剪…\",\n cropTitle: \"裁剪封面\",\n auto: \"自动\",\n autoHint: \"在你选择之前,将使用自动选取的画面。\",\n drop: \"将图片拖到这里,或点击选择\",\n processing: \"视频仍在处理中。编码完成后即可选择封面。\",\n rendering: \"正在生成封面…\",\n },\n progress: {\n decoding: \"正在打开图片…\",\n hashing: \"正在准备…\",\n uploading: \"正在上传… {percent}%\",\n completing: \"正在完成上传…\",\n rendering: \"正在生成各尺寸…\",\n },\n encode: {\n processing: \"正在处理视频\",\n queued: \"等待处理\",\n queuedPosition: \"排队中 · 第 {position} 位\",\n stalled: \"处理已暂停,正在等待服务器…\",\n segments: \"片段 {done} / {total}\",\n higherQualities: \"更高画质\",\n remainingSeconds: \"剩余约 {seconds} 秒\",\n remainingMinutes: \"剩余约 {minutes} 分钟\",\n remainingHours: \"剩余约 {hours} 小时 {minutes} 分钟\",\n almostDone: \"即将完成\",\n estimating: \"正在估算…\",\n phase: {\n queued: \"排队中\",\n downloading: \"准备中\",\n probing: \"分析中\",\n encoding: \"编码中\",\n muxing: \"封装中\",\n uploading: \"保存中\",\n publishing: \"发布中\",\n images: \"正在生成封面\",\n },\n },\n gallery: {\n label: \"媒体\",\n view: \"布局\",\n carousel: \"轮播\",\n grid: \"网格\",\n counter: \"{current} / {total}\",\n slide: \"第 {current} 项,共 {total} 项\",\n previous: \"上一个\",\n next: \"下一个\",\n goTo: \"转到第 {index} 项\",\n open: \"打开第 {index} 项,共 {total} 项\",\n lightbox: \"媒体查看器\",\n lockedOne: \"还有 1 项已锁定\",\n lockedMany: \"还有 {count} 项已锁定\",\n processingImage: \"正在处理图片…\",\n image: \"图片 {index}\",\n video: \"视频 {index}\",\n audio: \"音频 {index}\",\n processingAudio: \"正在处理音频…\",\n download: \"下载\",\n failedAudio: \"无法处理此音频。\",\n failedImage: \"无法处理此图片。\",\n },\n player: {\n play: \"播放视频\",\n loading: \"正在加载视频…\",\n retry: \"重试\",\n code: \"代码 {code}\",\n quality: \"画质\",\n auto: \"自动\",\n autoCurrent: \"自动({quality})\",\n label: \"视频播放器\",\n resume: \"播放\",\n pause: \"暂停\",\n replay: \"重新播放\",\n seek: \"跳转\",\n time: \"{current} / {duration}\",\n seekBack: \"后退 {seconds} 秒\",\n seekForward: \"前进 {seconds} 秒\",\n seconds: \"{seconds} 秒\",\n mute: \"静音\",\n unmute: \"取消静音\",\n volume: \"音量\",\n settings: \"设置\",\n speed: \"播放速度\",\n normal: \"正常\",\n audio: \"音轨\",\n subtitles: \"字幕\",\n subtitlesOff: \"关闭\",\n forced: \"{label}(强制)\",\n track: \"轨道 {number}\",\n fullscreen: \"全屏\",\n exitFullscreen: \"退出全屏\",\n pip: \"画中画\",\n exitPip: \"退出画中画\",\n theater: \"影院模式\",\n miniPlayer: \"迷你播放器\",\n expand: \"展开\",\n close: \"关闭\",\n failed: \"无法处理此视频。\",\n errors: {\n network: \"无法连接到视频服务器。\",\n access: \"你已无权访问此视频。\",\n not_found: \"此视频不可用。\",\n rate_limited: \"请求过多,请稍后重试。\",\n unsupported: \"此浏览器无法播放此视频。\",\n stalled: \"播放已停滞。请检查网络连接后重试。\",\n unknown: \"目前无法播放此视频。\",\n },\n },\n errors: {\n generic: \"我们这边出了问题,请重试。\",\n network: \"连接已断开,请检查网络后重试。\",\n storage: \"上传中断,请重试。\",\n decode: \"无法将此文件作为图片打开。\",\n invalid_request: \"无法保存图片。\",\n unauthorized: \"会话已过期,请重新登录后上传。\",\n forbidden: \"你没有权限更改此图片。\",\n not_found: \"此项目已不存在。\",\n conflict: \"图片已在其他地方被更改,请刷新后重试。\",\n incomplete: \"上传未完成,请重试。\",\n not_uploaded: \"上传已过期,请重新选择图片。\",\n too_large: \"文件过大。\",\n quota_exceeded: \"已达到存储空间上限。\",\n type_not_allowed: \"此处不支持该文件类型。\",\n checksum_mismatch: \"上传过程中文件发生了变化,请重试。\",\n rate_limited: \"上传次数过多,请在 {seconds} 秒后重试。\",\n unavailable: \"媒体存储暂时不可用,请稍后重试。\",\n upgrade_required: \"此项目正在升级,请稍后重试。\",\n internal_error: \"我们这边出了问题,请重试。\",\n allowedTypes: \"请使用 {allowed}。\",\n tooLargeBy: \"此文件为 {size};上限为 {max}。\",\n image_too_small: \"裁剪后图片宽 {width} 像素;至少需要 {min} 像素。请选择更大的区域或使用更大的图片。\",\n image_too_large: \"此图片为 {width}×{height} 像素;图片最多 {megapixels} 百万像素。\",\n image_unreadable: \"此文件不是有效的 {format} 图片。\",\n render_timeout: \"服务器仍在处理,完成后会显示;请稍后再查看。\",\n animation_not_allowed: \"此处不允许动图。请上传静态图片。\",\n animation_unsupported: \"不支持动态 {format} 图片。请使用动态 WebP 或 GIF。\",\n animationFrames: \"动图最多 {maxFrames} 帧;此图有 {frames} 帧。\",\n animationSeconds: \"动图最长 {maxSeconds} 秒。\",\n animationTooLarge: \"此动图过大:{frames} 帧 {width}×{height} 像素。请减少帧数或缩小尺寸。\",\n too_many_files: \"此项目的文件数已达上限。请先删除一些。\",\n animation_too_long: \"此动图过长。\",\n video_too_long: \"此文件时长超出此处允许的范围。\",\n video_too_large: \"此视频的画面尺寸超出此处允许的范围。\",\n videoTooLongBy: \"此文件时长 {duration} 秒;此处上限为 {maxSeconds} 秒。\",\n videoTooLargeBy: \"此视频为 {width}×{height} 像素;视频最多 {megapixels} 百万像素。\",\n video_over_budget: \"处理此视频耗时过长。请使用更短或更小的视频。\",\n video_aspect_unsupported: \"此视频过宽或过高,无法在此使用。\",\n videoAspectBy: \"此视频的比例为 {shape};此处的视频比例须在 {minAspect} 至 {maxAspect} 之间。\",\n tooManyFilesBy: \"此项目最多可包含 {maxFiles} 个此类文件。请先移除一些。\",\n aborted: \"已取消。\",\n resume_mismatch: \"保存的上传属于另一个文件。请重新选择文件。\",\n moderation_rejected: \"已被审核拒绝。\",\n comment_banned: \"你无法在此评论。\",\n comment_too_long: \"评论太长了。\",\n commentTooLongBy: \"评论最多 {maxChars} 个字符。\",\n not_configured: \"此网站未提供该功能。\",\n tenant_mismatch: \"我们这边出了点问题,请重试。\",\n gone: \"此内容已不可用。\",\n },\n};\n"],"mappings":"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"}
|
package/dist/react/posts.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface UsePost {
|
|
|
29
29
|
saving: boolean;
|
|
30
30
|
/** Creates the post; the hook then edits it. */
|
|
31
31
|
create: (input: PostInput) => Promise<Post>;
|
|
32
|
-
/** Updates the given fields; a body from the editor is stored with
|
|
32
|
+
/** Updates the given fields; a body from the editor is stored with image references (storedBody). */
|
|
33
33
|
update: (patch: PostInput) => Promise<Post>;
|
|
34
34
|
remove: () => Promise<void>;
|
|
35
35
|
/** Restores the deleted post (PostWrite). */
|
|
@@ -39,15 +39,17 @@ export interface UsePost {
|
|
|
39
39
|
/** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */
|
|
40
40
|
uploadImage: (file: Blob) => Promise<string>;
|
|
41
41
|
/**
|
|
42
|
-
* A URL that shows one of the post's images now
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
42
|
+
* A URL that shows one of the post's images now, by name ("i-{uuid}") or
|
|
43
|
+
* reference: a fresh upload from its file, else its public file
|
|
44
|
+
* (`post.images`), else, while it has none (an unpublished post's), its
|
|
45
|
+
* signed editor view from an editor read. Anything else is returned as is.
|
|
46
46
|
*/
|
|
47
|
-
imageSrc: (
|
|
47
|
+
imageSrc: (image: string | null | undefined) => string | undefined;
|
|
48
|
+
/** The cover through imageSrc. */
|
|
49
|
+
coverSrc: string | undefined;
|
|
48
50
|
/** post.body with its images through imageSrc: what a rich-text editor shows. */
|
|
49
51
|
editorBody: string | undefined;
|
|
50
|
-
/** HTML from the editor with
|
|
52
|
+
/** HTML from the editor with every image URL this hook showed turned back into the image's reference. */
|
|
51
53
|
storedBody: (html: string) => string;
|
|
52
54
|
}
|
|
53
55
|
/** Whether readers see the post, and so its images' public files exist. */
|
package/dist/react/posts.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { useMediaRead as e } from "./read.js";
|
|
2
2
|
import { keyOf as t, offsetPages as n, useContentScope as r, useList as i, useResource as a } from "./use-resource.js";
|
|
3
|
-
import {
|
|
3
|
+
import { IMAGE_SCHEME as o, imageRef as s, imageRefs as c } from "../client/content/posts.js";
|
|
4
|
+
import { useCallback as l, useEffect as u, useMemo as d, useRef as f, useState as p } from "react";
|
|
4
5
|
//#region src/react/posts.ts
|
|
5
|
-
function
|
|
6
|
+
function m(e = {}, a = {}) {
|
|
6
7
|
let { client: o, store: s, scope: c } = r(a.client), l = e.pageSize ?? 20;
|
|
7
8
|
return i(s, t("posts", c, !!e.admin, e.language, e.sort, e.draft, e.deleted, e.q, l), {
|
|
8
9
|
type: "posts",
|
|
@@ -19,131 +20,163 @@ function d(e = {}, a = {}) {
|
|
|
19
20
|
sort: e.sort
|
|
20
21
|
}, n)));
|
|
21
22
|
}
|
|
22
|
-
var
|
|
23
|
-
|
|
23
|
+
var h = (e) => [...new Set([...(e ?? "").matchAll(c())].map((e) => e[1]))], g = (e) => {
|
|
24
|
+
let t = e.slice(e.lastIndexOf("/") + 1), n = t.lastIndexOf(".");
|
|
25
|
+
return n > 0 ? t.slice(0, n) : t;
|
|
26
|
+
};
|
|
27
|
+
function _(e, t = Date.now()) {
|
|
24
28
|
return !e.is_draft && !e.moderation && (!e.live_at || Date.parse(e.live_at) <= t);
|
|
25
29
|
}
|
|
26
|
-
function
|
|
27
|
-
let { client:
|
|
30
|
+
function v(n, i = {}) {
|
|
31
|
+
let { client: m, store: _, scope: v } = r(i.client), [y, b] = p(null), x = n ?? y, S = x ? t("post", v, x) : null, C = a(_, S, {
|
|
28
32
|
type: "post",
|
|
29
|
-
id:
|
|
30
|
-
}, (e) =>
|
|
31
|
-
|
|
33
|
+
id: x ?? void 0
|
|
34
|
+
}, (e) => m.posts.get(x, e), { initial: i.initial }), w = C.data ?? null, [T, E] = p(0), D = l(async (e) => {
|
|
35
|
+
E((e) => e + 1);
|
|
32
36
|
try {
|
|
33
37
|
return await e();
|
|
34
38
|
} finally {
|
|
35
|
-
|
|
39
|
+
E((e) => e - 1);
|
|
36
40
|
}
|
|
37
|
-
}, []),
|
|
38
|
-
if (!
|
|
39
|
-
return
|
|
40
|
-
}, [
|
|
41
|
-
|
|
42
|
-
let e =
|
|
41
|
+
}, []), O = l(() => {
|
|
42
|
+
if (!x) throw Error("contentkit-ui: create the post first");
|
|
43
|
+
return x;
|
|
44
|
+
}, [x]), [k, A] = p(/* @__PURE__ */ new Map()), j = f([]);
|
|
45
|
+
u(() => {
|
|
46
|
+
let e = j.current;
|
|
43
47
|
return () => {
|
|
44
|
-
for (let t of e
|
|
48
|
+
for (let t of e) URL.revokeObjectURL(t);
|
|
45
49
|
};
|
|
46
50
|
}, []);
|
|
47
|
-
let
|
|
48
|
-
let e =
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
let M = d(() => new Map(Object.entries(w?.images ?? {})), [w?.images]), N = d(() => {
|
|
52
|
+
let e = h(w?.body);
|
|
53
|
+
return w?.cover && !w.cover_url && !e.includes(w.cover) && e.push(w.cover), e.filter((e) => !k.has(e));
|
|
54
|
+
}, [
|
|
55
|
+
w?.body,
|
|
56
|
+
w?.cover,
|
|
57
|
+
w?.cover_url,
|
|
58
|
+
k
|
|
59
|
+
]), P = d(() => m.media.postRef(x ?? "none"), [m, x]), F = e(P, {
|
|
55
60
|
editor: !0,
|
|
56
|
-
read:
|
|
57
|
-
client:
|
|
58
|
-
}),
|
|
61
|
+
read: N.length ? void 0 : null,
|
|
62
|
+
client: m
|
|
63
|
+
}), I = d(() => {
|
|
59
64
|
let e = /* @__PURE__ */ new Map();
|
|
60
|
-
for (let t of
|
|
61
|
-
let n = t.upload && t.editor_url ? p(t.path) : void 0;
|
|
62
|
-
n && e.set(n, t.editor_url);
|
|
63
|
-
}
|
|
65
|
+
for (let t of F.read?.files ?? []) t.upload && t.editor_url && e.set(g(t.path), t.editor_url);
|
|
64
66
|
return e;
|
|
65
|
-
}, [
|
|
66
|
-
|
|
67
|
-
if (!
|
|
68
|
-
|
|
67
|
+
}, [F.read]), L = (e) => !!F.read?.files.some((t) => t.failed && g(t.path) === e), R = !!F.read && N.some((e) => !I.has(e) && !L(e)), z = f(0), { reload: B } = F;
|
|
68
|
+
u(() => {
|
|
69
|
+
if (!R) {
|
|
70
|
+
z.current = 0;
|
|
69
71
|
return;
|
|
70
72
|
}
|
|
71
|
-
if (
|
|
73
|
+
if (z.current >= 6) return;
|
|
72
74
|
let e = setTimeout(() => {
|
|
73
|
-
|
|
74
|
-
}, 1e3 * 2 **
|
|
75
|
+
z.current++, B();
|
|
76
|
+
}, 1e3 * 2 ** z.current);
|
|
75
77
|
return () => clearTimeout(e);
|
|
76
78
|
}, [
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
R,
|
|
80
|
+
F.read,
|
|
81
|
+
B
|
|
80
82
|
]);
|
|
81
|
-
let
|
|
83
|
+
let V = f(/* @__PURE__ */ new Map());
|
|
84
|
+
u(() => {
|
|
85
|
+
for (let e of [
|
|
86
|
+
k,
|
|
87
|
+
M,
|
|
88
|
+
I
|
|
89
|
+
]) for (let [t, n] of e) V.current.set(n, t);
|
|
90
|
+
}, [
|
|
91
|
+
k,
|
|
92
|
+
M,
|
|
93
|
+
I
|
|
94
|
+
]);
|
|
95
|
+
let H = l((e) => {
|
|
82
96
|
if (!e) return;
|
|
83
|
-
let t =
|
|
84
|
-
return
|
|
85
|
-
}, [
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let t =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
let t = e.startsWith("contentkit:") ? e.slice(o.length) : e;
|
|
98
|
+
return k.get(t) ?? M.get(t) ?? I.get(t) ?? e;
|
|
99
|
+
}, [
|
|
100
|
+
k,
|
|
101
|
+
M,
|
|
102
|
+
I
|
|
103
|
+
]), U = d(() => w?.body.replace(c(), (e, t) => k.get(t) ?? I.get(t) ?? e), [
|
|
104
|
+
w?.body,
|
|
105
|
+
k,
|
|
106
|
+
I
|
|
107
|
+
]), W = l((e) => {
|
|
108
|
+
let t = new Map(V.current);
|
|
109
|
+
for (let e of [
|
|
110
|
+
k,
|
|
111
|
+
M,
|
|
112
|
+
I
|
|
113
|
+
]) for (let [n, r] of e) t.set(r, n);
|
|
114
|
+
let n = e;
|
|
115
|
+
for (let [e, r] of t) n = n.split(e).join(s(r)).split(e.replaceAll("&", "&")).join(s(r));
|
|
116
|
+
return n;
|
|
117
|
+
}, [
|
|
118
|
+
k,
|
|
119
|
+
M,
|
|
120
|
+
I
|
|
121
|
+
]), G = l((e, t) => {
|
|
122
|
+
if (typeof URL.createObjectURL != "function") return;
|
|
123
|
+
let n = URL.createObjectURL(t);
|
|
124
|
+
return j.current.push(n), A((t) => new Map(t).set(e, n)), n;
|
|
125
|
+
}, []), K = l((e) => D(async () => {
|
|
126
|
+
let t = await m.posts.create(e);
|
|
127
|
+
return b(t.id), t;
|
|
128
|
+
}), [m, D]), q = l((e) => D(() => m.posts.update(O(), e.body === void 0 ? e : {
|
|
97
129
|
...e,
|
|
98
|
-
body:
|
|
130
|
+
body: W(e.body)
|
|
99
131
|
})), [
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
]),
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
]),
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
]),
|
|
113
|
-
if (!e) return
|
|
114
|
-
let t = await
|
|
115
|
-
return
|
|
132
|
+
m,
|
|
133
|
+
D,
|
|
134
|
+
O,
|
|
135
|
+
W
|
|
136
|
+
]), J = l(() => D(() => m.posts.delete(O())), [
|
|
137
|
+
m,
|
|
138
|
+
D,
|
|
139
|
+
O
|
|
140
|
+
]), Y = l(() => D(() => m.posts.restore(O())), [
|
|
141
|
+
m,
|
|
142
|
+
D,
|
|
143
|
+
O
|
|
144
|
+
]), X = l((e) => D(async () => {
|
|
145
|
+
if (!e) return m.posts.setCover(O(), null);
|
|
146
|
+
let t = await m.media.uploadNamed(m.media.postRef(O()), e), n = await m.posts.setCover(O(), t.name), r = G(t.name, e) ?? n;
|
|
147
|
+
return r && V.current.set(r, t.name), r;
|
|
116
148
|
}), [
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
]),
|
|
122
|
-
let t = await
|
|
123
|
-
return V(
|
|
149
|
+
m,
|
|
150
|
+
D,
|
|
151
|
+
O,
|
|
152
|
+
G
|
|
153
|
+
]), Z = l((e) => D(async () => {
|
|
154
|
+
let t = await m.media.uploadInline(O(), e), n = G(t.name, e) ?? t.url ?? t.ref;
|
|
155
|
+
return V.current.set(n, t.name), n;
|
|
124
156
|
}), [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
m,
|
|
158
|
+
D,
|
|
159
|
+
O,
|
|
160
|
+
G
|
|
129
161
|
]);
|
|
130
162
|
return {
|
|
131
|
-
post:
|
|
132
|
-
loading: !!
|
|
133
|
-
error:
|
|
134
|
-
saving:
|
|
135
|
-
create:
|
|
136
|
-
update:
|
|
137
|
-
remove:
|
|
138
|
-
restore:
|
|
139
|
-
setCover:
|
|
140
|
-
uploadImage:
|
|
141
|
-
imageSrc:
|
|
142
|
-
|
|
143
|
-
|
|
163
|
+
post: w,
|
|
164
|
+
loading: !!S && (C.loading || !C.loaded && i.initial === void 0),
|
|
165
|
+
error: C.error,
|
|
166
|
+
saving: T > 0,
|
|
167
|
+
create: K,
|
|
168
|
+
update: q,
|
|
169
|
+
remove: J,
|
|
170
|
+
restore: Y,
|
|
171
|
+
setCover: X,
|
|
172
|
+
uploadImage: Z,
|
|
173
|
+
imageSrc: H,
|
|
174
|
+
coverSrc: H(w?.cover),
|
|
175
|
+
editorBody: U,
|
|
176
|
+
storedBody: W
|
|
144
177
|
};
|
|
145
178
|
}
|
|
146
179
|
//#endregion
|
|
147
|
-
export {
|
|
180
|
+
export { _ as isPublished, v as usePost, m as usePosts };
|
|
148
181
|
|
|
149
182
|
//# sourceMappingURL=posts.js.map
|
package/dist/react/posts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"posts.js","names":[],"sources":["../../src/react/posts.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Sort } from \"../client/content/types.js\";\nimport type { Post, PostInput } from \"../client/generated/wire.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface PostFilter {\n language?: string;\n sort?: Sort;\n /** Staff: every post, drafts and scheduled ones included (PostWrite). */\n admin?: boolean;\n /** With admin: true only drafts, false no drafts. */\n draft?: boolean;\n /** With admin: the deleted posts instead of the live ones. */\n deleted?: boolean;\n /** With admin: only posts whose title, excerpt or body contains this text. */\n q?: string;\n pageSize?: number;\n}\n\n/** Posts, a page at a time: the published ones, or every one for staff. */\nexport function usePosts(f: PostFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Post> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n return useList(\n store,\n keyOf(\"posts\", scope, !!f.admin, f.language, f.sort, f.draft, f.deleted, f.q, size),\n { type: \"posts\", scope: f.admin && f.deleted ? \"deleted\" : undefined },\n offsetPages(size, (q, signal) =>\n f.admin\n ? client.posts.adminList({ ...q, language: f.language, draft: f.draft, deleted: f.deleted, q: f.q }, signal)\n : client.posts.list({ ...q, language: f.language, sort: f.sort }, signal),\n ),\n );\n}\n\nexport interface UsePost {\n /** null before create (or while loading). */\n post: Post | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the post; the hook then edits it. */\n create: (input: PostInput) => Promise<Post>;\n /** Updates the given fields; a body from the editor is stored with its images' public URLs (storedBody). */\n update: (patch: PostInput) => Promise<Post>;\n remove: () => Promise<void>;\n /** Restores the deleted post (PostWrite). */\n restore: () => Promise<Post>;\n /** Uploads the cover (null clears it); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the post's images now (the cover, a body image):\n * an unpublished post's images are not public yet, so its editors see them\n * through signed URLs of an editor read; a fresh upload shows from the\n * file itself. Any other URL is returned as is.\n */\n imageSrc: (url: string | null | undefined) => string | undefined;\n /** post.body with its images through imageSrc: what a rich-text editor shows. */\n editorBody: string | undefined;\n /** HTML from the editor with the URLs imageSrc gave back to the images' public URLs. */\n storedBody: (html: string) => string;\n}\n\nconst NAME = \"i-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\";\nconst nameIn = (url: string) => url.match(new RegExp(NAME))?.[0];\n// An image URL in HTML: everything around a name up to a quote, a space or a bracket.\nconst imageURLs = new RegExp(`[^\\\\s\"'()<>]*${NAME}[^\\\\s\"'()<>]*`, \"g\");\n\n/** Whether readers see the post, and so its images' public files exist. */\nexport function isPublished(post: Post, now = Date.now()): boolean {\n return !post.is_draft && !post.moderation && (!post.live_at || Date.parse(post.live_at) <= now);\n}\n\n/** One post: read, and for staff (PostWrite) create, update, delete, restore, cover and body images. null creates a new post. */\nexport function usePost(id: string | null | undefined, o: { initial?: Post; client?: ContentKitClient } = {}): UsePost {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"post\", scope, pid) : null;\n const r = useResource<Post | null>(store, key, { type: \"post\", id: pid ?? undefined }, (s) => client.posts.get(pid!, s), { initial: o.initial });\n const post = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the post first\");\n return pid;\n }, [pid]);\n\n // Images: fresh uploads show from their file; an unpublished post's from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const given = useRef(new Map<string, string>()); // each URL imageSrc gave -> the image's public URL\n useEffect(() => {\n const urls = given.current;\n return () => {\n for (const u of urls.keys()) if (u.startsWith(\"blob:\")) URL.revokeObjectURL(u);\n };\n }, []);\n const names = useMemo(() => {\n const out = new Map<string, string>();\n for (const u of [...(post?.body.match(imageURLs) ?? []), post?.cover_url ?? \"\"]) {\n const n = nameIn(u);\n if (n && !out.has(n)) out.set(n, u);\n }\n return out;\n }, [post?.body, post?.cover_url]);\n const unread = !!post && !isPublished(post) && [...names.keys()].some((n) => !previews.has(n));\n const folder = useMemo(() => client.media.postRef(pid ?? \"none\"), [client, pid]);\n const read = useMediaRead(folder, { editor: true, read: unread ? undefined : null, client });\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of read.read?.files ?? []) {\n const n = f.upload && f.editor_url ? nameIn(f.path) : undefined;\n if (n) out.set(n, f.editor_url!);\n }\n return out;\n }, [read.read]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const missing = unread && !!read.read && [...names.keys()].some((n) => !previews.has(n) && !views.has(n) && !read.read!.files.some((f) => f.failed && nameIn(f.path) === n));\n const tries = useRef(0);\n const { reload } = read;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, read.read, reload]);\n\n const imageSrc = useCallback(\n (url: string | null | undefined) => {\n if (!url) return undefined;\n const n = nameIn(url);\n const shown = n ? (previews.get(n) ?? views.get(n)) : undefined;\n if (!shown) return url;\n given.current.set(shown, url);\n return shown;\n },\n [previews, views],\n );\n const editorBody = useMemo(() => post?.body.replace(imageURLs, (u) => imageSrc(u) ?? u), [post?.body, imageSrc]);\n const storedBody = useCallback((html: string) => {\n let out = html;\n for (const [shown, url] of given.current) out = out.split(shown).join(url).split(shown.replaceAll(\"&\", \"&\")).join(url);\n return out;\n }, []);\n const preview = useCallback((name: string, url: string, file: Blob) => {\n if (typeof URL.createObjectURL !== \"function\") return url;\n const shown = URL.createObjectURL(file);\n given.current.set(shown, url);\n setPreviews((m) => new Map(m).set(name, shown));\n return shown;\n }, []);\n\n const create = useCallback(\n (input: PostInput) =>\n run(async () => {\n const post = await client.posts.create(input);\n setCreated(post.id);\n return post;\n }),\n [client, run],\n );\n const update = useCallback(\n (patch: PostInput) => run(() => client.posts.update(need(), patch.body === undefined ? patch : { ...patch, body: storedBody(patch.body) })),\n [client, run, need, storedBody],\n );\n const remove = useCallback(() => run(() => client.posts.delete(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.posts.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) return client.posts.setCover(need(), null);\n const up = await client.media.uploadNamed(client.media.postRef(need()), file);\n const url = await client.posts.setCover(need(), up.name);\n return url && preview(up.name, url, file);\n }),\n [client, run, need, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline(need(), file);\n return preview(up.name, up.url, file);\n }),\n [client, run, need, preview],\n );\n return {\n post,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n restore,\n setCover,\n uploadImage,\n imageSrc,\n editorBody,\n storedBody,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"posts.js","names":[],"sources":["../../src/react/posts.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { Sort } from \"../client/content/types.js\";\nimport type { Post, PostInput } from \"../client/generated/wire.js\";\nimport { IMAGE_SCHEME, imageRef, imageRefs } from \"../client/content/posts.js\";\nimport { useMediaRead } from \"./read.js\";\nimport { keyOf, offsetPages, useContentScope, useList, useResource, type UseList } from \"./use-resource.js\";\n\nexport interface PostFilter {\n language?: string;\n sort?: Sort;\n /** Staff: every post, drafts and scheduled ones included (PostWrite). */\n admin?: boolean;\n /** With admin: true only drafts, false no drafts. */\n draft?: boolean;\n /** With admin: the deleted posts instead of the live ones. */\n deleted?: boolean;\n /** With admin: only posts whose title, excerpt or body contains this text. */\n q?: string;\n pageSize?: number;\n}\n\n/** Posts, a page at a time: the published ones, or every one for staff. */\nexport function usePosts(f: PostFilter = {}, o: { client?: ContentKitClient } = {}): UseList<Post> {\n const { client, store, scope } = useContentScope(o.client);\n const size = f.pageSize ?? 20;\n return useList(\n store,\n keyOf(\"posts\", scope, !!f.admin, f.language, f.sort, f.draft, f.deleted, f.q, size),\n { type: \"posts\", scope: f.admin && f.deleted ? \"deleted\" : undefined },\n offsetPages(size, (q, signal) =>\n f.admin\n ? client.posts.adminList({ ...q, language: f.language, draft: f.draft, deleted: f.deleted, q: f.q }, signal)\n : client.posts.list({ ...q, language: f.language, sort: f.sort }, signal),\n ),\n );\n}\n\nexport interface UsePost {\n /** null before create (or while loading). */\n post: Post | null;\n loading: boolean;\n error?: ContentKitError;\n /** Writes in flight. */\n saving: boolean;\n /** Creates the post; the hook then edits it. */\n create: (input: PostInput) => Promise<Post>;\n /** Updates the given fields; a body from the editor is stored with image references (storedBody). */\n update: (patch: PostInput) => Promise<Post>;\n remove: () => Promise<void>;\n /** Restores the deleted post (PostWrite). */\n restore: () => Promise<Post>;\n /** Uploads the cover (null clears it); resolves with a URL that shows it now. */\n setCover: (file: Blob | null) => Promise<string | null>;\n /** Uploads an image for the body; resolves with a URL that shows it now, to place in the editor. */\n uploadImage: (file: Blob) => Promise<string>;\n /**\n * A URL that shows one of the post's images now, by name (\"i-{uuid}\") or\n * reference: a fresh upload from its file, else its public file\n * (`post.images`), else, while it has none (an unpublished post's), its\n * signed editor view from an editor read. Anything else is returned as is.\n */\n imageSrc: (image: string | null | undefined) => string | undefined;\n /** The cover through imageSrc. */\n coverSrc: string | undefined;\n /** post.body with its images through imageSrc: what a rich-text editor shows. */\n editorBody: string | undefined;\n /** HTML from the editor with every image URL this hook showed turned back into the image's reference. */\n storedBody: (html: string) => string;\n}\n\n/** The names of the images a body still references, once each. */\nconst refNames = (body: string | undefined) => [...new Set([...(body ?? \"\").matchAll(imageRefs())].map((m) => m[1]!))];\nconst uploadName = (path: string) => {\n const base = path.slice(path.lastIndexOf(\"/\") + 1);\n const dot = base.lastIndexOf(\".\");\n return dot > 0 ? base.slice(0, dot) : base;\n};\n\n/** Whether readers see the post, and so its images' public files exist. */\nexport function isPublished(post: Post, now = Date.now()): boolean {\n return !post.is_draft && !post.moderation && (!post.live_at || Date.parse(post.live_at) <= now);\n}\n\n/** One post: read, and for staff (PostWrite) create, update, delete, restore, cover and body images. null creates a new post. */\nexport function usePost(id: string | null | undefined, o: { initial?: Post; client?: ContentKitClient } = {}): UsePost {\n const { client, store, scope } = useContentScope(o.client);\n const [created, setCreated] = useState<string | null>(null);\n const pid = id ?? created;\n const key = pid ? keyOf(\"post\", scope, pid) : null;\n const r = useResource<Post | null>(store, key, { type: \"post\", id: pid ?? undefined }, (s) => client.posts.get(pid!, s), { initial: o.initial });\n const post = r.data ?? null;\n const [inflight, setInflight] = useState(0);\n const run = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {\n setInflight((n) => n + 1);\n try {\n return await fn();\n } finally {\n setInflight((n) => n - 1);\n }\n }, []);\n const need = useCallback(() => {\n if (!pid) throw new Error(\"contentkit-ui: create the post first\");\n return pid;\n }, [pid]);\n\n // Images: fresh uploads show from their file, published ones from post.images,\n // the rest (an unpublished post's) from an editor read.\n const [previews, setPreviews] = useState<ReadonlyMap<string, string>>(new Map());\n const blobs = useRef<string[]>([]);\n useEffect(() => {\n const urls = blobs.current;\n return () => {\n for (const u of urls) URL.revokeObjectURL(u);\n };\n }, []);\n const published = useMemo(() => new Map(Object.entries(post?.images ?? {})), [post?.images]);\n const pending = useMemo(() => {\n const names = refNames(post?.body);\n if (post?.cover && !post.cover_url && !names.includes(post.cover)) names.push(post.cover);\n return names.filter((n) => !previews.has(n));\n }, [post?.body, post?.cover, post?.cover_url, previews]);\n const folder = useMemo(() => client.media.postRef(pid ?? \"none\"), [client, pid]);\n const read = useMediaRead(folder, { editor: true, read: pending.length ? undefined : null, client });\n const views = useMemo(() => {\n const out = new Map<string, string>();\n for (const f of read.read?.files ?? []) if (f.upload && f.editor_url) out.set(uploadName(f.path), f.editor_url);\n return out;\n }, [read.read]);\n // Editor views render on the first editor read that misses them: read again, backing off.\n const failed = (n: string) => !!read.read?.files.some((f) => f.failed && uploadName(f.path) === n);\n const missing = !!read.read && pending.some((n) => !views.has(n) && !failed(n));\n const tries = useRef(0);\n const { reload } = read;\n useEffect(() => {\n if (!missing) {\n tries.current = 0;\n return;\n }\n if (tries.current >= 6) return;\n const t = setTimeout(() => {\n tries.current++;\n reload();\n }, 1000 * 2 ** tries.current);\n return () => clearTimeout(t);\n }, [missing, read.read, reload]);\n\n // Every URL shown for an image, of this and earlier reads, back to its name.\n const shown = useRef(new Map<string, string>());\n useEffect(() => {\n for (const m of [previews, published, views]) for (const [n, u] of m) shown.current.set(u, n);\n }, [previews, published, views]);\n\n const imageSrc = useCallback(\n (image: string | null | undefined) => {\n if (!image) return undefined;\n const n = image.startsWith(IMAGE_SCHEME) ? image.slice(IMAGE_SCHEME.length) : image;\n return previews.get(n) ?? published.get(n) ?? views.get(n) ?? image;\n },\n [previews, published, views],\n );\n const editorBody = useMemo(() => post?.body.replace(imageRefs(), (ref, n: string) => previews.get(n) ?? views.get(n) ?? ref), [post?.body, previews, views]);\n const storedBody = useCallback(\n (html: string) => {\n const back = new Map(shown.current);\n for (const m of [previews, published, views]) for (const [n, u] of m) back.set(u, n);\n let out = html;\n for (const [u, n] of back) out = out.split(u).join(imageRef(n)).split(u.replaceAll(\"&\", \"&\")).join(imageRef(n));\n return out;\n },\n [previews, published, views],\n );\n const preview = useCallback((name: string, file: Blob): string | undefined => {\n if (typeof URL.createObjectURL !== \"function\") return undefined;\n const url = URL.createObjectURL(file);\n blobs.current.push(url);\n setPreviews((m) => new Map(m).set(name, url));\n return url;\n }, []);\n\n const create = useCallback(\n (input: PostInput) =>\n run(async () => {\n const post = await client.posts.create(input);\n setCreated(post.id);\n return post;\n }),\n [client, run],\n );\n const update = useCallback(\n (patch: PostInput) => run(() => client.posts.update(need(), patch.body === undefined ? patch : { ...patch, body: storedBody(patch.body) })),\n [client, run, need, storedBody],\n );\n const remove = useCallback(() => run(() => client.posts.delete(need())), [client, run, need]);\n const restore = useCallback(() => run(() => client.posts.restore(need())), [client, run, need]);\n const setCover = useCallback(\n (file: Blob | null) =>\n run(async () => {\n if (!file) return client.posts.setCover(need(), null);\n const up = await client.media.uploadNamed(client.media.postRef(need()), file);\n const cover = await client.posts.setCover(need(), up.name);\n const url = preview(up.name, file) ?? cover;\n if (url) shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n const uploadImage = useCallback(\n (file: Blob) =>\n run(async () => {\n const up = await client.media.uploadInline(need(), file);\n const url = preview(up.name, file) ?? up.url ?? up.ref;\n shown.current.set(url, up.name);\n return url;\n }),\n [client, run, need, preview],\n );\n return {\n post,\n loading: !!key && (r.loading || (!r.loaded && o.initial === undefined)),\n error: r.error,\n saving: inflight > 0,\n create,\n update,\n remove,\n restore,\n setCover,\n uploadImage,\n imageSrc,\n coverSrc: imageSrc(post?.cover),\n editorBody,\n storedBody,\n };\n}\n"],"mappings":"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"}
|
package/dist/react/public.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface PublicImageOptions {
|
|
|
21
21
|
client?: ContentKitClient | null;
|
|
22
22
|
}
|
|
23
23
|
export interface UsePublicImage {
|
|
24
|
-
/** The item's current renditions; the kind's default image when
|
|
24
|
+
/** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */
|
|
25
25
|
image: PublicPreset | null;
|
|
26
26
|
/** The preset's rules (aspect, widths, min_width) once the presets load. */
|
|
27
27
|
rule?: PresetRule;
|
|
@@ -34,6 +34,6 @@ export interface UsePublicImage {
|
|
|
34
34
|
/**
|
|
35
35
|
* An item's public image for a preset ("avatar", "cover"): the exact
|
|
36
36
|
* renditions from a read's `public` (or the host's listing), else the kind's
|
|
37
|
-
* default image. Reads refresh after commits through the same client.
|
|
37
|
+
* default image, else none. Reads refresh after commits through the same client.
|
|
38
38
|
*/
|
|
39
39
|
export declare function usePublicImage(kind: string, id: string, preset: string, o?: PublicImageOptions): UsePublicImage;
|
package/dist/react/public.js
CHANGED
|
@@ -33,18 +33,21 @@ function d(a, s, c, l = {}) {
|
|
|
33
33
|
return {
|
|
34
34
|
...o(() => {
|
|
35
35
|
let t = h ? l.image : _.read?.public?.find((e) => e.preset === c);
|
|
36
|
-
|
|
36
|
+
if (t && t.renditions.length) return {
|
|
37
37
|
image: {
|
|
38
38
|
preset: c,
|
|
39
39
|
renditions: t.renditions,
|
|
40
40
|
aspect: "aspect" in t && t.aspect || m?.aspect
|
|
41
41
|
},
|
|
42
42
|
isDefault: !1
|
|
43
|
-
}
|
|
43
|
+
};
|
|
44
|
+
if (!h && !_.read) return {
|
|
44
45
|
image: null,
|
|
45
46
|
isDefault: !1
|
|
46
|
-
}
|
|
47
|
-
|
|
47
|
+
};
|
|
48
|
+
let n = m && e(m, s);
|
|
49
|
+
return n ? {
|
|
50
|
+
image: n,
|
|
48
51
|
isDefault: !0
|
|
49
52
|
} : {
|
|
50
53
|
image: null,
|
package/dist/react/public.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult } from \"../client/generated/wire.js\";\nimport type { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; the kind's default image when
|
|
1
|
+
{"version":3,"file":"public.js","names":[],"sources":["../../src/react/public.ts"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport type { ContentKitClient } from \"../client/client.js\";\nimport type { ContentKitError } from \"../client/errors.js\";\nimport type { ReadResult } from \"../client/generated/wire.js\";\nimport type { PresetRule } from \"../client/generated/wire.js\";\nimport { defaultImage, isPattern, ruleDir } from \"../client/media/rules.js\";\nimport type { PublicPreset } from \"../client/public.js\";\nimport { useOptionalContentKitClient } from \"./context.js\";\nimport { useMediaRead } from \"./read.js\";\n\nexport interface UsePresets {\n /** Every kind's public presets; empty until they load (or when the server has none). */\n presets: PresetRule[];\n loaded: boolean;\n error?: ContentKitError;\n}\n\nconst settled = new WeakMap<ContentKitClient, { presets: PresetRule[]; error?: ContentKitError }>();\nconst inflight = new WeakMap<ContentKitClient, Promise<void>>();\n\n/** The public presets (`GET /media/presets`), fetched once per client. */\nexport function usePresets(o: { client?: ContentKitClient | null } = {}): UsePresets {\n const client = useOptionalContentKitClient(o.client);\n const [, rerender] = useState(0);\n const done = client ? settled.get(client) : undefined;\n useEffect(() => {\n if (!client || settled.has(client)) return;\n let live = true;\n let load = inflight.get(client);\n if (!load) {\n load = client.media.presets().then(\n (presets) => void settled.set(client, { presets }),\n (error: ContentKitError) => void settled.set(client, { presets: [], error }),\n ).finally(() => inflight.delete(client));\n inflight.set(client, load);\n }\n void load.then(() => live && rerender((n) => n + 1));\n return () => {\n live = false;\n };\n }, [client]);\n return { presets: done?.presets ?? [], loaded: !!done || !client, error: done?.error };\n}\n\nexport interface PublicImageOptions {\n /** The host's listing of the item's image (its API's): shown without a read. */\n image?: PublicPreset | null;\n /** A read of the item the host already has. */\n read?: ReadResult | null;\n /** Overrides the provider's client. */\n client?: ContentKitClient | null;\n}\n\nexport interface UsePublicImage {\n /** The item's current renditions; else the kind's default image (when the preset has one); null while unknown or when there is neither. */\n image: PublicPreset | null;\n /** The preset's rules (aspect, widths, min_width) once the presets load. */\n rule?: PresetRule;\n /** image is the kind's default, not the item's own. */\n isDefault: boolean;\n loading: boolean;\n error?: ContentKitError;\n reload: () => void;\n}\n\n/**\n * An item's public image for a preset (\"avatar\", \"cover\"): the exact\n * renditions from a read's `public` (or the host's listing), else the kind's\n * default image, else none. Reads refresh after commits through the same client.\n */\nexport function usePublicImage(kind: string, id: string, preset: string, o: PublicImageOptions = {}): UsePublicImage {\n const client = useOptionalContentKitClient(o.client);\n const { presets, loaded } = usePresets({ client });\n const rule = presets.find((p) => p.kind === kind && p.name === preset);\n const listed = o.image !== undefined;\n // The read waits for the presets, so it lists only the preset's upload.\n const prefix = rule ? (isPattern(rule.from) ? ruleDir(rule.from) : rule.from) : undefined;\n const r = useMediaRead({ kind, id }, { prefix, read: listed || !loaded ? null : o.read, client });\n const image = useMemo((): { image: PublicPreset | null; isDefault: boolean } => {\n const own = listed ? o.image : r.read?.public?.find((p) => p.preset === preset);\n if (own && own.renditions.length) return { image: { preset, renditions: own.renditions, aspect: (\"aspect\" in own && own.aspect) || rule?.aspect }, isDefault: false };\n if (!listed && !r.read) return { image: null, isDefault: false };\n const def = rule && defaultImage(rule, id);\n return def ? { image: def, isDefault: true } : { image: null, isDefault: false };\n }, [listed, o.image, r.read, preset, rule, id]);\n return { ...image, rule, loading: !loaded || r.loading, error: r.error, reload: r.reload };\n}\n"],"mappings":";;;;;AAiBA,IAAM,oBAAU,IAAI,QAA8E,GAC5F,oBAAW,IAAI,QAAyC;AAG9D,SAAgB,EAAW,IAA0C,CAAC,GAAe;CACnF,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,GAAG,KAAY,EAAS,CAAC,GACzB,IAAO,IAAS,EAAQ,IAAI,CAAM,IAAI,KAAA;CAiB5C,OAhBA,QAAgB;EACd,IAAI,CAAC,KAAU,EAAQ,IAAI,CAAM,GAAG;EACpC,IAAI,IAAO,IACP,IAAO,EAAS,IAAI,CAAM;EAS9B,OARK,MACH,IAAO,EAAO,MAAM,QAAQ,CAAC,CAAC,MAC3B,MAAY,KAAK,EAAQ,IAAI,GAAQ,EAAE,WAAQ,CAAC,IAChD,MAA2B,KAAK,EAAQ,IAAI,GAAQ;GAAE,SAAS,CAAC;GAAG;EAAM,CAAC,CAC7E,CAAC,CAAC,cAAc,EAAS,OAAO,CAAM,CAAC,GACvC,EAAS,IAAI,GAAQ,CAAI,IAE3B,EAAU,WAAW,KAAQ,GAAU,MAAM,IAAI,CAAC,CAAC,SACtC;GACX,IAAO;EACT;CACF,GAAG,CAAC,CAAM,CAAC,GACJ;EAAE,SAAS,GAAM,WAAW,CAAC;EAAG,QAAQ,CAAC,CAAC,KAAQ,CAAC;EAAQ,OAAO,GAAM;CAAM;AACvF;AA4BA,SAAgB,EAAe,GAAc,GAAY,GAAgB,IAAwB,CAAC,GAAmB;CACnH,IAAM,IAAS,EAA4B,EAAE,MAAM,GAC7C,EAAE,YAAS,cAAW,EAAW,EAAE,UAAO,CAAC,GAC3C,IAAO,EAAQ,MAAM,MAAM,EAAE,SAAS,KAAQ,EAAE,SAAS,CAAM,GAC/D,IAAS,EAAE,UAAU,KAAA,GAErB,IAAS,IAAQ,EAAU,EAAK,IAAI,IAAI,EAAQ,EAAK,IAAI,IAAI,EAAK,OAAQ,KAAA,GAC1E,IAAI,EAAa;EAAE;EAAM;CAAG,GAAG;EAAE;EAAQ,MAAM,KAAU,CAAC,IAAS,OAAO,EAAE;EAAM;CAAO,CAAC;CAQhG,OAAO;EAAE,GAPK,QAAkE;GAC9E,IAAM,IAAM,IAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,EAAE,WAAW,CAAM;GAC9E,IAAI,KAAO,EAAI,WAAW,QAAQ,OAAO;IAAE,OAAO;KAAE;KAAQ,YAAY,EAAI;KAAY,QAAS,YAAY,KAAO,EAAI,UAAW,GAAM;IAAO;IAAG,WAAW;GAAM;GACpK,IAAI,CAAC,KAAU,CAAC,EAAE,MAAM,OAAO;IAAE,OAAO;IAAM,WAAW;GAAM;GAC/D,IAAM,IAAM,KAAQ,EAAa,GAAM,CAAE;GACzC,OAAO,IAAM;IAAE,OAAO;IAAK,WAAW;GAAK,IAAI;IAAE,OAAO;IAAM,WAAW;GAAM;EACjF,GAAG;GAAC;GAAQ,EAAE;GAAO,EAAE;GAAM;GAAQ;GAAM;EAAE,CACjC;EAAO;EAAM,SAAS,CAAC,KAAU,EAAE;EAAS,OAAO,EAAE;EAAO,QAAQ,EAAE;CAAO;AAC3F"}
|
package/package.json
CHANGED