diff --git a/locales/en-US.yml b/locales/en-US.yml index f44c3d1842..a70eb2406f 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1227,6 +1227,8 @@ publishTimelinesDescription: "If enabled, the Local and Global timelines will be on {url} even when signed out." noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?" showNoAltTextWarning: "Show a warning if you attempt to post files without a description" +showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description when you + attempt to post files without a description" _emojiModPerm: unauthorized: "None" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c8579a4c1a..1be460f7f0 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2054,6 +2054,7 @@ searchRangeDescription: "如果您要过滤时间段,请按以下格式输入 messagingUnencryptedInfo: "Firefish 上的聊天没有经过端到端加密,请不要在聊天中分享您的敏感信息。" noAltTextWarning: 有些附件没有描述。您是否忘记写描述了? showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告 +showAddFileDescriptionAtFirstPost: 当您首次尝试发布没有描述的帖子附件时自动弹出添加描述页面 autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告 incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?" noteEditHistory: "帖子编辑历史" diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue index 582da6c6d4..5850fdf498 100644 --- a/packages/client/src/components/MkPostForm.vue +++ b/packages/client/src/components/MkPostForm.vue @@ -318,6 +318,8 @@ import XNoteSimple from "@/components/MkNoteSimple.vue"; import XNotePreview from "@/components/MkNotePreview.vue"; import XPostFormAttaches from "@/components/MkPostFormAttaches.vue"; import XPollEditor from "@/components/MkPollEditor.vue"; +import XCheatSheet from "@/components/MkCheatSheetDialog.vue"; +import XMediaCaption from "@/components/MkMediaCaption.vue"; import { host, url } from "@/config"; import { erase, unique } from "@/scripts/array"; import { extractMentions } from "@/scripts/extract-mentions"; @@ -334,7 +336,6 @@ import { getAccounts, openAccountMenu as openAccountMenu_ } from "@/account"; import { me } from "@/me"; import { uploadFile } from "@/scripts/upload"; import { deepClone } from "@/scripts/clone"; -import XCheatSheet from "@/components/MkCheatSheetDialog.vue"; import preprocess from "@/scripts/preprocess"; import { vibrate } from "@/scripts/vibrate"; import { langmap } from "@/scripts/langmap"; @@ -508,6 +509,8 @@ const hashtags = computed( defaultStore.makeGetterSetter("postFormHashtags"), ) as Ref; +let isFirstPostAttempt = true; + watch(text, () => { checkMissingMention(); }); @@ -1022,6 +1025,46 @@ function deleteDraft() { localStorage.setItem("drafts", JSON.stringify(draftData)); } +/** + * @returns whether the file is described + */ +function openFileDescriptionWindow(file: entities.DriveFile) { + return new Promise((resolve, reject) => { + os.popup( + XMediaCaption, + { + title: i18n.ts.describeFile, + input: { + placeholder: i18n.ts.inputNewDescription, + default: file.comment !== null ? file.comment : "", + }, + image: file, + }, + { + done: (result) => { + if (!result || result.canceled) { + resolve(false); + return; + } + const comment = result.result?.length === 0 ? null : result.result; + os.api("drive/files/update", { + fileId: file.id, + comment, + }) + .then(() => { + resolve(true); + file.comment = comment ?? null; + }) + .catch((err: unknown) => { + reject(err); + }); + }, + }, + "closed", + ); + }); +} + async function post() { // For text that is too short, the false positive rate may be too high, so we don't show alarm. if (defaultStore.state.autocorrectNoteLanguage && text.value.length > 10) { @@ -1056,6 +1099,24 @@ async function post() { } } + if ( + defaultStore.state.showAddFileDescriptionAtFirstPost && + files.value.some((f) => f.comment == null || f.comment.length === 0) + ) { + if (isFirstPostAttempt) { + for (const file of files.value) { + if (file.comment == null || file.comment.length === 0) { + const described = await openFileDescriptionWindow(file); + if (!described) { + return; + } + } + } + isFirstPostAttempt = false; + return; + } + } + if ( defaultStore.state.showNoAltTextWarning && files.value.some((f) => f.comment == null || f.comment.length === 0) @@ -1064,12 +1125,22 @@ async function post() { const { canceled } = await os.confirm({ type: "warning", text: i18n.ts.noAltTextWarning, - okText: i18n.ts.goBack, + okText: i18n.ts.describeFile, cancelText: i18n.ts.toPost, isPlaintext: true, }); - if (!canceled) return; + if (!canceled) { + for (const file of files.value) { + if (file.comment == null || file.comment.length === 0) { + const described = await openFileDescriptionWindow(file); + if (!described) { + return; + } + } + } + return; + } } const processedText = preprocess(text.value); diff --git a/packages/client/src/pages/settings/general.vue b/packages/client/src/pages/settings/general.vue index 15e1172169..bc21efc132 100644 --- a/packages/client/src/pages/settings/general.vue +++ b/packages/client/src/pages/settings/general.vue @@ -124,6 +124,9 @@ {{ i18n.ts.showNoAltTextWarning }} + {{ + i18n.ts.showAddFileDescriptionAtFirstPost + }} {{ i18n.ts.autocorrectNoteLanguage }} @@ -533,6 +536,9 @@ const pullToRefreshThreshold = computed( const showNoAltTextWarning = computed( defaultStore.makeGetterSetter("showNoAltTextWarning"), ); +const showAddFileDescriptionAtFirstPost = computed( + defaultStore.makeGetterSetter("showAddFileDescriptionAtFirstPost"), +); const autocorrectNoteLanguage = computed( defaultStore.makeGetterSetter("autocorrectNoteLanguage"), ); diff --git a/packages/client/src/pages/settings/preferences-backups.vue b/packages/client/src/pages/settings/preferences-backups.vue index 73e098c4ea..15ff5de402 100644 --- a/packages/client/src/pages/settings/preferences-backups.vue +++ b/packages/client/src/pages/settings/preferences-backups.vue @@ -125,6 +125,7 @@ const defaultStoreSaveKeys: (keyof (typeof defaultStore)["state"])[] = [ "enablePullToRefresh", "pullToRefreshThreshold", "showNoAltTextWarning", + "showAddFileDescriptionAtFirstPost", "autocorrectNoteLanguage", ]; const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [ diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 29393123c0..0adc4e76db 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -442,6 +442,10 @@ export const defaultStore = markRaw( where: "account", default: true, }, + showAddFileDescriptionAtFirstPost: { + where: "account", + default: false, + }, autocorrectNoteLanguage: { where: "account", default: true,