diff --git a/locales/index.d.ts b/locales/index.d.ts index 5877cc8432..ddbb9e3335 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5553,6 +5553,10 @@ export interface Locale extends ILocale { * ユーザー指定ノートを作成 */ "createUserSpecifiedNote": string; + /** + * 投稿を予約 + */ + "schedulePost": string; "_compression": { "_quality": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1381c1c65b..514181b925 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1383,6 +1383,7 @@ customCssIsDisabledBecauseSafeMode: "セーフモードが有効なため、カ themeIsDefaultBecauseSafeMode: "セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。" thankYouForTestingBeta: "ベータ版の検証にご協力いただきありがとうございます!" createUserSpecifiedNote: "ユーザー指定ノートを作成" +schedulePost: "投稿を予約" _compression: _quality: diff --git a/packages/backend/src/core/entities/NoteDraftEntityService.ts b/packages/backend/src/core/entities/NoteDraftEntityService.ts index 926c526e87..cdbe2cf643 100644 --- a/packages/backend/src/core/entities/NoteDraftEntityService.ts +++ b/packages/backend/src/core/entities/NoteDraftEntityService.ts @@ -105,7 +105,7 @@ export class NoteDraftEntityService implements OnModuleInit { const packed: Packed<'NoteDraft'> = await awaitAll({ id: noteDraft.id, createdAt: this.idService.parse(noteDraft.id).date.toISOString(), - scheduledAt: noteDraft.scheduledAt?.toISOString() ?? undefined, + scheduledAt: noteDraft.scheduledAt?.getTime() ?? null, userId: noteDraft.userId, user: packedUsers?.get(noteDraft.userId) ?? this.userEntityService.pack(noteDraft.user ?? noteDraft.userId, me), text: text, diff --git a/packages/backend/src/models/json-schema/note-draft.ts b/packages/backend/src/models/json-schema/note-draft.ts index 00622fa588..03c32b6548 100644 --- a/packages/backend/src/models/json-schema/note-draft.ts +++ b/packages/backend/src/models/json-schema/note-draft.ts @@ -168,9 +168,8 @@ export const packedNoteDraftSchema = { enum: ['likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote', null], }, scheduledAt: { - type: 'string', - optional: true, nullable: true, - format: 'date-time', + type: 'number', + optional: false, nullable: true, }, }, } as const; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 17f93a4ec8..197530d260 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@@ -199,6 +199,7 @@ if (props.initialVisibleUsers) { props.initialVisibleUsers.forEach(u => pushVisibleUser(u)); } const reactionAcceptance = ref(store.s.reactionAcceptance); +const scheduledAt = ref(null); const draghover = ref(false); const quoteId = ref(null); const hasNotSpecifiedMentions = ref(false); @@ -414,6 +415,7 @@ function watchForDraft() { watch(localOnly, () => saveDraft()); watch(quoteId, () => saveDraft()); watch(reactionAcceptance, () => saveDraft()); + watch(scheduledAt, () => saveDraft()); } function checkMissingMention() { @@ -605,6 +607,12 @@ function showOtherSettings() { action: () => { toggleReactionAcceptance(); }, + }, { + icon: 'ti ti-calendar-time', + text: i18n.ts.schedulePost + '...', + action: () => { + schedule(); + }, }, { type: 'divider' }, { type: 'switch', icon: 'ti ti-eye', @@ -809,6 +817,7 @@ function saveDraft() { ...( visibleUsers.value.length > 0 ? { visibleUserIds: visibleUsers.value.map(x => x.id) } : {}), quoteId: quoteId.value, reactionAcceptance: reactionAcceptance.value, + scheduledAt: scheduledAt.value, }, }; @@ -838,6 +847,7 @@ async function saveServerDraft(clearLocal = false) { replyId: replyTargetNote.value ? replyTargetNote.value.id : undefined, channelId: targetChannel.value ? targetChannel.value.id : undefined, reactionAcceptance: reactionAcceptance.value, + scheduledAt: scheduledAt.value, }).then(() => { if (clearLocal) { clear(); @@ -1175,6 +1185,7 @@ function showDraftMenu(ev: MouseEvent) { renoteTargetNote.value = draft.renote; replyTargetNote.value = draft.reply; reactionAcceptance.value = draft.reactionAcceptance; + scheduledAt.value = draft.scheduledAt ?? null; if (draft.channel) targetChannel.value = draft.channel as unknown as Misskey.entities.Channel; visibleUsers.value = []; @@ -1220,6 +1231,13 @@ function showDraftMenu(ev: MouseEvent) { }], (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); } +async function schedule() { + const { canceled, result } = await os.inputDate({ + title: i18n.ts.schedulePost, + }); + if (canceled) return; +} + onMounted(() => { if (props.autofocus) { focus(); @@ -1255,6 +1273,7 @@ onMounted(() => { } quoteId.value = draft.data.quoteId; reactionAcceptance.value = draft.data.reactionAcceptance; + scheduledAt.value = draft.data.scheduledAt ?? null; } } diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index afff4c9301..74ea5fbe5f 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -4462,8 +4462,7 @@ export type components = { localOnly?: boolean; /** @enum {string|null} */ reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null; - /** Format: date-time */ - scheduledAt?: string | null; + scheduledAt: number | null; }; NoteReaction: { /** Format: id */ @@ -29205,6 +29204,7 @@ export interface operations { expiresAt?: number | null; expiredAfter?: number | null; } | null; + scheduledAt?: number | null; }; }; }; @@ -29446,6 +29446,7 @@ export interface operations { expiresAt?: number | null; expiredAfter?: number | null; } | null; + scheduledAt?: number | null; }; }; };