diff --git a/locales/en-US.yml b/locales/en-US.yml index 3f04adb676..333e987811 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1298,6 +1298,7 @@ showDetailTimeWhenHover: "Hover the timestamp of the note to expand the detailed noteClickToOpen: "Click to open note details" enableFallbackReactButton: "Enable fallback reaction button" enableMFMCheatsheet: "Enable MFM Cheatsheet in post form" +enableUndoClearPostForm: "Enable undo & clear button in post form" disableReactionsViewer: "Disable emoji reactions viewer" autoSpacing: "Auto Spacing" autoSpacingDescription: "Adding spaces between CJK and English characters" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 76e5e6bdab..3769107fd0 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1305,6 +1305,7 @@ showDetailTimeWhenHover: "悬浮/长按帖文时间戳时,展开详细时间" noteClickToOpen: "点击展开帖文详情" enableFallbackReactButton: "开启Fallback回应按钮" enableMFMCheatsheet: "在帖文编辑框中启用MFM Cheatsheet" +enableUndoClearPostForm: "在帖文编辑框中启用撤销/清空按钮" disableReactionsViewer: "禁用帖文表情回应显示" autoSpacing: "自动空格" autoSpacingDescription: "在CJK字符和英文字符中添加空格" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 58f5535a30..a8db1d17b0 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1305,6 +1305,7 @@ showDetailTimeWhenHover: "長按貼文時間戳記時展開詳細時間" noteClickToOpen: "點擊展開貼文詳情" enableFallbackReactButton: "啓用Fallback回應鍵" enableMFMCheatsheet: "在貼文編輯框中啓用MFM Cheatsheet" +enableUndoClearPostForm: "在貼文編輯框中啓用撤回/清除按鈕" disableReactionsViewer: "禁用貼文表情回應顯示" autoSpacing: "自動間距" autoSpacingDescription: "在CJK字符和英文字符中添加間距" diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 435372f6a2..b2d80c1000 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -87,11 +87,11 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> + <button v-if="enableMFMCheatsheet" v-tooltip="'MFM Cheatsheet'" class="_button" :class="$style.footerButton" @click="MFMWindow"><i class="ti ti-note"></i></button> </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> - <button v-if="showTextManageButton" v-tooltip="currentHistoryIndex >= 0 ? i18n.ts.undoPostForm : i18n.ts.clearPostForm" class="_button" :class="$style.footerButton" @click="handleTextManageClick"><i :class="textManageButtonIcon"></i></button> - <button v-if="defaultStore.state.enableMFMCheatsheet" v-tooltip="'MFM Cheatsheet'" class="_button" :class="$style.footerButton" @click="MFMWindow"><i class="ti ti-note"></i></button> + <button v-if="showTextManageButton && enableUndoClearPostForm" v-tooltip="currentHistoryIndex >= 0 ? i18n.ts.undoPostForm : i18n.ts.clearPostForm" class="_button" :class="$style.footerButton" @click="handleTextManageClick"><i :class="textManageButtonIcon"></i></button> <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ti ti-dots"></i></button>--> </div> </footer> @@ -195,6 +195,9 @@ const showingOptions = ref(false); const textAreaReadOnly = ref(false); const justEndedComposition = ref(false); +const enableMFMCheatsheet = ref(defaultStore.state.enableMFMCheatsheet); +const enableUndoClearPostForm = ref(defaultStore.state.enableUndoClearPostForm); + const draftKey = computed((): string => { let key = props.channel ? `channel:${props.channel.id}` : ''; @@ -268,6 +271,8 @@ const textManageButtonIcon = computed(() => { if (currentHistoryIndex.value >= 0) return 'ti ti-arrow-back-up'; return text.value !== '' ? 'ti ti-trash' : ''; }); +let lastSaveTime = 0; +const SAVE_INTERVAL = 300; function clearText() { if (text.value !== '') { @@ -278,9 +283,22 @@ function clearText() { } function saveToHistory() { - textHistory.value = textHistory.value.slice(0, currentHistoryIndex.value + 1); - textHistory.value.push(text.value); - currentHistoryIndex.value = textHistory.value.length - 1; + const now = Date.now(); + if ( + (now - lastSaveTime > SAVE_INTERVAL) && + (textHistory.value[currentHistoryIndex.value] !== text.value) && + (text.value.length > 0) + ) { + textHistory.value = textHistory.value.slice(0, currentHistoryIndex.value + 1); + textHistory.value.push(text.value); + currentHistoryIndex.value = textHistory.value.length - 1; + lastSaveTime = now; + + if (textHistory.value.length > 50) { + textHistory.value = textHistory.value.slice(-50); + currentHistoryIndex.value = textHistory.value.length - 1; + } + } } function undoTextChange() { @@ -615,7 +633,9 @@ function clear() { function onKeydown(ev: KeyboardEvent) { if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post(); - if (!ev.ctrlKey && !ev.metaKey && !ev.altKey && !justEndedComposition.value && !ev.isComposing) { + if (enableUndoClearPostForm.value && !ev.ctrlKey && !ev.metaKey && !ev.altKey && + !justEndedComposition.value && !ev.isComposing && + !['Shift', 'Alt', 'Control', 'Meta', 'CapsLock', 'Tab'].includes(ev.key)) { saveToHistory(); } diff --git a/packages/frontend/src/pages/settings/pari.vue b/packages/frontend/src/pages/settings/pari.vue index 93ba5234bc..14fa18594c 100644 --- a/packages/frontend/src/pages/settings/pari.vue +++ b/packages/frontend/src/pages/settings/pari.vue @@ -69,6 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="noteClickToOpen">{{ i18n.ts.noteClickToOpen }}</MkSwitch> <MkSwitch v-model="enableFallbackReactButton">{{ i18n.ts.enableFallbackReactButton }}</MkSwitch> <MkSwitch v-model="enableMFMCheatsheet">{{ i18n.ts.enableMFMCheatsheet }}</MkSwitch> + <MkSwitch v-model="enableUndoClearPostForm">{{ i18n.ts.enableUndoClearPostForm }}</MkSwitch> <MkSwitch v-model="collapseNotesRepliedTo">{{ i18n.ts.collapseNotesRepliedTo }}</MkSwitch> <MkSwitch v-model="disableReactionsViewer">{{ i18n.ts.disableReactionsViewer }}</MkSwitch> <MkSelect v-model="autoSpacingBehaviour"> @@ -121,6 +122,7 @@ const showDetailTimeWhenHover = computed(defaultStore.makeGetterSetter('showDeta const noteClickToOpen = computed(defaultStore.makeGetterSetter('noteClickToOpen')); const enableFallbackReactButton = computed(defaultStore.makeGetterSetter('enableFallbackReactButton')); const enableMFMCheatsheet = computed(defaultStore.makeGetterSetter('enableMFMCheatsheet')); +const enableUndoClearPostForm = computed(defaultStore.makeGetterSetter('enableUndoClearPostForm')); const autoSpacingBehaviour = computed(defaultStore.makeGetterSetter('autoSpacingBehaviour')); const collapseNotesRepliedTo = computed(defaultStore.makeGetterSetter('collapseNotesRepliedTo')); const disableReactionsViewer = computed(defaultStore.makeGetterSetter('disableReactionsViewer')); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 587ab43c44..b28f461b84 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -536,6 +536,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, + enableUndoClearPostForm: { + where: 'device', + default: true, + }, autoSpacingBehaviour: { where: 'device', default: null as 'all' | 'special' | null,