diff --git a/locales/index.d.ts b/locales/index.d.ts index 9592e94839..714e73cecb 100755 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5213,6 +5213,22 @@ export interface Locale extends ILocale { * 高度なMFMのピッカーを表示する */ "enableQuickAddMfmFunction": string; + /** + * ノートの自己消滅の初期値 + */ + "defaultScheduledNoteDeleteTime": string; + /** + * ノートの自己消滅が有効になっています + */ + "scheduledNoteDeleteEnabled": string; + /** + * 1年以上先の日時を指定することはできません + */ + "cannotScheduleLaterThanOneYear": string; + /** + * デフォルトでノートが自己消滅するように + */ + "defaultScheduledNoteDelete": string; /** * バブルゲーム */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 201a49cd5e..21ef77277a 100755 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1344,6 +1344,10 @@ _delivery: autoSuspendedForNotResponding: "サーバー応答なしのため停止中" scheduledNoteDelete: "時限爆弾" noteDeletationAt: "このノートは{time}に削除されます" +defaultScheduledNoteDeleteTime: "ノートの自己消滅の初期値" +scheduledNoteDeleteEnabled: "ノートの自己消滅が有効になっています" +cannotScheduleLaterThanOneYear: "1年以上先の日時を指定することはできません" +defaultScheduledNoteDelete: "デフォルトでノートが自己消滅するように" _bubbleGame: howToPlay: "遊び方" diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 1d55cfe646..4122d4dfff 100755 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -110,6 +110,12 @@ export const meta = { id: '9576c3c8-d8f3-11ee-ac15-00155d19d35d', }, + cannotScheduleDeleteLaterThanOneYear: { + message: 'Scheduled delete time is later than one year.', + code: 'CANNOT_SCHEDULE_DELETE_LATER_THAN_ONE_YEAR', + id: 'b02b5edb-2741-4841-b692-d9893f1e6515', + }, + noSuchChannel: { message: 'No such channel.', code: 'NO_SUCH_CHANNEL', @@ -387,6 +393,10 @@ export default class extends Endpoint { // eslint- } else if (typeof ps.scheduledDelete.deleteAfter === 'number') { ps.scheduledDelete.deleteAt = Date.now() + ps.scheduledDelete.deleteAfter; } + + if (ps.scheduledDelete.deleteAt && ps.scheduledDelete.deleteAt > Date.now() + ms('1year')) { + throw new ApiError(meta.errors.cannotScheduleDeleteLaterThanOneYear); + } } let channel: MiChannel | null = null; diff --git a/packages/frontend/src/components/MkDeleteScheduleEditor.vue b/packages/frontend/src/components/MkDeleteScheduleEditor.vue index 352b02145c..f401c92150 100755 --- a/packages/frontend/src/components/MkDeleteScheduleEditor.vue +++ b/packages/frontend/src/components/MkDeleteScheduleEditor.vue @@ -1,74 +1,108 @@ - - - - diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 66a683b65c..79e6af35ce 100755 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -185,7 +185,7 @@ const posted = ref(false); const text = ref(props.initialText ?? ''); const files = ref(props.initialFiles ?? []); const poll = ref(null); -const scheduledNoteDelete = ref(null); +const scheduledNoteDelete = ref(defaultStore.state.defaultScheduledNoteDelete ? { deleteAt: null, deleteAfter: defaultStore.state.defaultScheduledNoteDeleteTime, isValid: true } : null); const useCw = ref(!!props.initialCw); const showPreview = ref(defaultStore.state.showPreview); watch(showPreview, () => defaultStore.set('showPreview', showPreview.value)); @@ -260,6 +260,7 @@ const maxTextLength = computed((): number => { const canPost = computed((): boolean => { return !props.mock && !posting.value && !posted.value && + (scheduledNoteDelete.value ? scheduledNoteDelete.value.isValid : true) && ( 1 <= textLength.value || 1 <= files.value.length || @@ -433,6 +434,7 @@ function toggleScheduledNoteDelete() { scheduledNoteDelete.value = { deleteAt: null, deleteAfter: null, + isValid: true, }; } } @@ -1074,6 +1076,7 @@ onMounted(() => { scheduledNoteDelete.value = { deleteAt: init.deleteAt ? (new Date(init.deleteAt)).getTime() : null, deleteAfter: null, + isValid: true, }; } if (init.visibleUserIds) { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index e4a798e72b..e2a4137105 100755 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -36,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.showFixedPostForm }} {{ i18n.ts.showFixedPostFormInChannel }} + {{ i18n.ts.defaultScheduledNoteDelete }} diff --git a/packages/frontend/src/pages/settings/post-form.vue b/packages/frontend/src/pages/settings/post-form.vue new file mode 100644 index 0000000000..91ad3d0c12 --- /dev/null +++ b/packages/frontend/src/pages/settings/post-form.vue @@ -0,0 +1,62 @@ + + + diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index a4d4612b5b..585aaba50d 100755 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -116,6 +116,10 @@ const routes: RouteDef[] = [{ path: '/navbar', name: 'navbar', component: page(() => import('@/pages/settings/navbar.vue')), + }, { + path: '/post-form', + name: 'post-form', + component: page(() => import('@/pages/settings/post-form.vue')), }, { path: '/statusbar', name: 'statusbar', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index bef6829f40..fd6e0b0af0 100755 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -117,6 +117,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: true, }, + defaultScheduledNoteDelete: { + where: 'account', + default: false, + }, + defaultScheduledNoteDeleteTime: { + where: 'account', + default: 86400000, + }, uploadFolder: { where: 'account', default: null as string | null, @@ -569,10 +577,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore, }, - searchEngine: { - where: 'device', - default: 'https://google.com/search?q=', - }, reactionChecksMuting: { where: 'device', default: true,