feat: support showing publish failed messages (#1209)

zio/stable
Alex 2023-01-17 20:56:51 +08:00 committed by GitHub
parent 0b2b9a713b
commit 85e163a0ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 17 deletions

View File

@ -0,0 +1,23 @@
<script setup lang="ts">
defineProps<{
describedBy: string
}>()
defineOptions({
inheritAttrs: false,
})
</script>
<template>
<div
role="alert"
:aria-describedby="describedBy"
flex="~ col"
gap-1 text-sm
pt-1 ps-2 pe-1 pb-2
text-red-600 dark:text-red-400
border="~ base rounded red-600 dark:red-400"
>
<slot />
</div>
</template>

View File

@ -34,7 +34,7 @@ const {
dropZoneRef, dropZoneRef,
} = $(useUploadMediaAttachment($$(draft))) } = $(useUploadMediaAttachment($$(draft)))
let { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft } = $(usePublish( let { shouldExpanded, isExpanded, isSending, isPublishDisabled, publishDraft, failedMessages } = $(usePublish(
{ {
draftState, draftState,
...$$({ expanded, isUploading, initialDraft: initial }), ...$$({ expanded, isUploading, initialDraft: initial }),
@ -160,6 +160,29 @@ defineExpose({
> >
</div> </div>
<PublishErrMessage v-if="failedMessages.length > 0" described-by="publish-failed">
<head id="publish-failed" flex justify-between>
<div flex items-center gap-x-2 font-bold>
<div aria-hidden="true" i-ri:error-warning-fill />
<p>{{ $t('state.publish_failed') }}</p>
</div>
<CommonTooltip placement="bottom" :content="$t('action.clear_publish_failed')">
<button
flex rounded-4 p1 hover:bg-active cursor-pointer transition-100 :aria-label="$t('action.clear_publish_failed')"
@click="failedMessages = []"
>
<span aria-hidden="true" w="1.75em" h="1.75em" i-ri:close-line />
</button>
</CommonTooltip>
</head>
<ol ps-2 sm:ps-1>
<li v-for="(error, i) in failedMessages" :key="i" flex="~ col sm:row" gap-y-1 sm:gap-x-2>
<strong>{{ i + 1 }}.</strong>
<span>{{ error }}</span>
</li>
</ol>
</PublishErrMessage>
<div relative flex-1 flex flex-col> <div relative flex-1 flex flex-col>
<EditorContent <EditorContent
:editor="editor" :editor="editor"
@ -174,15 +197,9 @@ defineExpose({
</div> </div>
{{ $t('state.uploading') }} {{ $t('state.uploading') }}
</div> </div>
<div <PublishErrMessage
v-else-if="failedAttachments.length > 0" v-else-if="failedAttachments.length > 0"
role="alert" :described-by="isExceedingAttachmentLimit ? 'upload-failed uploads-per-post' : 'upload-failed'"
:aria-describedby="isExceedingAttachmentLimit ? 'upload-failed uploads-per-post' : 'upload-failed'"
flex="~ col"
gap-1 text-sm
pt-1 ps-2 pe-1 pb-2
text-red-600 dark:text-red-400
border="~ base rounded red-600 dark:red-400"
> >
<head id="upload-failed" flex justify-between> <head id="upload-failed" flex justify-between>
<div flex items-center gap-x-2 font-bold> <div flex items-center gap-x-2 font-bold>
@ -191,10 +208,8 @@ defineExpose({
</div> </div>
<CommonTooltip placement="bottom" :content="$t('action.clear_upload_failed')"> <CommonTooltip placement="bottom" :content="$t('action.clear_upload_failed')">
<button <button
flex rounded-4 p1 flex rounded-4 p1 hover:bg-active cursor-pointer transition-100
hover:bg-active cursor-pointer transition-100 :aria-label="$t('action.clear_upload_failed')" @click="failedAttachments = []"
:aria-label="$t('action.clear_upload_failed')"
@click="failedAttachments = []"
> >
<span aria-hidden="true" w="1.75em" h="1.75em" i-ri:close-line /> <span aria-hidden="true" w="1.75em" h="1.75em" i-ri:close-line />
</button> </button>
@ -209,7 +224,7 @@ defineExpose({
<span>{{ error[0] }}</span> <span>{{ error[0] }}</span>
</li> </li>
</ol> </ol>
</div> </PublishErrMessage>
<div v-if="draft.attachments.length" flex="~ col gap-2" overflow-auto> <div v-if="draft.attachments.length" flex="~ col gap-2" overflow-auto>
<PublishAttachment <PublishAttachment
@ -291,7 +306,18 @@ defineExpose({
</template> </template>
</PublishVisibilityPicker> </PublishVisibilityPicker>
<CommonTooltip id="publish-tooltip" placement="top" :content="$t('tooltip.add_publishable_content')" :disabled="!isPublishDisabled"> <CommonTooltip v-if="failedMessages.length > 0" id="publish-failed-tooltip" placement="top" :content="$t('tooltip.publish_failed')">
<button
btn-danger rounded-3 text-sm w-full flex="~ gap1" items-center md:w-fit aria-describedby="publish-failed-tooltip"
>
<span block>
<div block i-carbon:face-dizzy-filled />
</span>
<span>{{ $t('state.publish_failed') }}</span>
</button>
</CommonTooltip>
<CommonTooltip v-else id="publish-tooltip" placement="top" :content="$t('tooltip.add_publishable_content')" :disabled="!isPublishDisabled">
<button <button
btn-solid rounded-3 text-sm w-full flex="~ gap1" items-center btn-solid rounded-3 text-sm w-full flex="~ gap1" items-center
md:w-fit md:w-fit
@ -303,6 +329,9 @@ defineExpose({
<span v-if="isSending" block animate-spin preserve-3d> <span v-if="isSending" block animate-spin preserve-3d>
<div block i-ri:loader-2-fill /> <div block i-ri:loader-2-fill />
</span> </span>
<span v-if="failedMessages.length" block>
<div block i-carbon:face-dizzy-filled />
</span>
<span v-if="draft.editingStatus">{{ $t('action.save_changes') }}</span> <span v-if="draft.editingStatus">{{ $t('action.save_changes') }}</span>
<span v-else-if="draft.params.inReplyToId">{{ $t('action.reply') }}</span> <span v-else-if="draft.params.inReplyToId">{{ $t('action.reply') }}</span>
<span v-else>{{ !isSending ? $t('action.publish') : $t('state.publishing') }}</span> <span v-else>{{ !isSending ? $t('action.publish') : $t('state.publishing') }}</span>

View File

@ -16,12 +16,18 @@ export const usePublish = (options: {
let isSending = $ref(false) let isSending = $ref(false)
const isExpanded = $ref(false) const isExpanded = $ref(false)
const failedMessages = $ref<string[]>([])
const shouldExpanded = $computed(() => expanded || isExpanded || !isEmpty) const shouldExpanded = $computed(() => expanded || isExpanded || !isEmpty)
const isPublishDisabled = $computed(() => { const isPublishDisabled = $computed(() => {
return isEmpty || isUploading || isSending || (draft.attachments.length === 0 && !draft.params.status) return isEmpty || isUploading || isSending || (draft.attachments.length === 0 && !draft.params.status) || failedMessages.length > 0
}) })
watch(() => draft, () => {
if (failedMessages.length > 0)
failedMessages.length = 0
}, { deep: true })
async function publishDraft() { async function publishDraft() {
let content = htmlToText(draft.params.status || '') let content = htmlToText(draft.params.status || '')
if (draft.mentions?.length) if (draft.mentions?.length)
@ -63,6 +69,7 @@ export const usePublish = (options: {
} }
catch (err) { catch (err) {
console.error(err) console.error(err)
failedMessages.push((err as Error).message)
} }
finally { finally {
isSending = false isSending = false
@ -74,6 +81,7 @@ export const usePublish = (options: {
isExpanded, isExpanded,
shouldExpanded, shouldExpanded,
isPublishDisabled, isPublishDisabled,
failedMessages,
publishDraft, publishDraft,
}) })

View File

@ -47,6 +47,7 @@
"boost": "Boost", "boost": "Boost",
"boost_count": "{0}", "boost_count": "{0}",
"boosted": "Boosted", "boosted": "Boosted",
"clear_publish_failed": "Clear publish errors",
"clear_upload_failed": "Clear file upload errors", "clear_upload_failed": "Clear file upload errors",
"close": "Close", "close": "Close",
"compose": "Compose", "compose": "Compose",
@ -400,6 +401,7 @@
"edited": "(Edited)", "edited": "(Edited)",
"editing": "Editing", "editing": "Editing",
"loading": "Loading...", "loading": "Loading...",
"publish_failed": "Publish failed",
"publishing": "Publishing", "publishing": "Publishing",
"upload_failed": "Upload failed", "upload_failed": "Upload failed",
"uploading": "Uploading..." "uploading": "Uploading..."
@ -499,6 +501,7 @@
"explore_links_intro": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "explore_links_intro": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
"explore_posts_intro": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.", "explore_posts_intro": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
"explore_tags_intro": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", "explore_tags_intro": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
"publish_failed": "Close failed messages at the top of editor to republish posts",
"toggle_code_block": "Toggle code block" "toggle_code_block": "Toggle code block"
}, },
"user": { "user": {

View File

@ -45,7 +45,8 @@
"bookmarked": "已收藏", "bookmarked": "已收藏",
"boost": "转发", "boost": "转发",
"boosted": "已转发", "boosted": "已转发",
"clear_upload_failed": "清除上传失败", "clear_publish_failed": "清除发布失败信息",
"clear_upload_failed": "清除上传失败信息",
"close": "关闭", "close": "关闭",
"compose": "撰写", "compose": "撰写",
"confirm": "确认", "confirm": "确认",
@ -374,6 +375,8 @@
"edited": "(已编辑)", "edited": "(已编辑)",
"editing": "编辑中", "editing": "编辑中",
"loading": "加载中...", "loading": "加载中...",
"publish_failed": "发布失败",
"publishing": "发布中...",
"upload_failed": "上传失败", "upload_failed": "上传失败",
"uploading": "上传中..." "uploading": "上传中..."
}, },
@ -472,6 +475,7 @@
"explore_links_intro": "这些新闻故事正被本站和分布式网络上其他站点的用户谈论。", "explore_links_intro": "这些新闻故事正被本站和分布式网络上其他站点的用户谈论。",
"explore_posts_intro": "来自本站和分布式网络上其他站点的这些嘟文正在本站引起关注。", "explore_posts_intro": "来自本站和分布式网络上其他站点的这些嘟文正在本站引起关注。",
"explore_tags_intro": "这些标签正在本站和分布式网络上其他站点的用户中引起关注。", "explore_tags_intro": "这些标签正在本站和分布式网络上其他站点的用户中引起关注。",
"publish_failed": "关闭编辑器上方的错误信息以重新发布帖文。",
"toggle_code_block": "切换代码块" "toggle_code_block": "切换代码块"
}, },
"user": { "user": {

View File

@ -1,6 +1,8 @@
:root { :root {
--c-border: #eee; --c-border: #eee;
--c-border-dark: #dccfcf; --c-border-dark: #dccfcf;
--c-danger: #FF3C1B;
--c-danger-active: #B50900;
--rgb-bg-base: 250, 250, 250; --rgb-bg-base: 250, 250, 250;
@ -32,6 +34,8 @@
--c-primary-active: var(--c-dark-primary-active); --c-primary-active: var(--c-dark-primary-active);
--c-primary-light: var(--c-dark-primary-light); --c-primary-light: var(--c-dark-primary-light);
--c-primary-fade: var(--c-dark-primary-fade); --c-primary-fade: var(--c-dark-primary-fade);
--c-danger: #FF2810;
--c-danger-active: #E02F00;
--c-border: #222; --c-border: #222;
--c-border-dark: #545251; --c-border-dark: #545251;

View File

@ -41,6 +41,7 @@ export default defineConfig({
'btn-outline': 'btn-base px-4 py-2 rounded text-$c-primary border border-$c-primary hover:bg-$c-primary hover:text-inverted', 'btn-outline': 'btn-base px-4 py-2 rounded text-$c-primary border border-$c-primary hover:bg-$c-primary hover:text-inverted',
'btn-text': 'btn-base px-4 py-2 text-$c-primary hover:text-$c-primary-active', 'btn-text': 'btn-base px-4 py-2 text-$c-primary hover:text-$c-primary-active',
'btn-action-icon': 'btn-base hover:bg-active rounded-full h9 w9 flex items-center justify-center disabled:bg-transparent disabled:text-$c-text-secondary', 'btn-action-icon': 'btn-base hover:bg-active rounded-full h9 w9 flex items-center justify-center disabled:bg-transparent disabled:text-$c-text-secondary',
'btn-danger': 'btn-base px-4 py-2 rounded text-white bg-$c-danger hover:bg-$c-danger-active',
// input // input
'input-base-focus': 'focus:outline-none focus:border-$c-primary', 'input-base-focus': 'focus:outline-none focus:border-$c-primary',
@ -95,6 +96,10 @@ export default defineConfig({
DEFAULT: 'var(--c-primary)', DEFAULT: 'var(--c-primary)',
active: 'var(--c-primary-active)', active: 'var(--c-primary-active)',
}, },
danger: {
DEFAULT: 'var(--c-danger)',
active: 'var(--c-danger-active)',
},
}, },
}, },
rules: [ rules: [