diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-09-26 15:29:52 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-26 15:29:52 +0900 |
| commit | d1446d195abb52c560c7c97177d08103a175acf7 (patch) | |
| tree | 355689adb542333f4c5abb186ab9819c29274612 /packages/frontend/src/components/MkNoteDraftsDialog.vue | |
| parent | fix(frontend): ビルド成果物のファイル名にlocalesのhashを含め... (diff) | |
| download | misskey-d1446d195abb52c560c7c97177d08103a175acf7.tar.gz misskey-d1446d195abb52c560c7c97177d08103a175acf7.tar.bz2 misskey-d1446d195abb52c560c7c97177d08103a175acf7.zip | |
feat: scheduled post (#16577)
* Update NoteDraft.ts
* Update NoteDraft.ts
* wip
* Update CHANGELOG.md
* wip
* Update PostScheduledNoteProcessorService.ts
* Update PostScheduledNoteProcessorService.ts
* Update Notification.ts
* wip
* Update NoteDraftService.ts
* Update NoteDraftService.ts
* Update NoteDraftService.ts
* wip
* Create 1758677617888-scheduled-post.js
* Update index.d.ts
* Update stats.ts
* wip
* wip
* wip
* wip
* wip
* Update MkNotification.vue
* wip
* wip
* wip
* Update NoteDraftService.ts
* Update NoteDraftService.ts
* wip
* wip
* Update NoteDraftEntityService.ts
* wip
* Update index.d.ts
* Update MkPostForm.vue
* wip
* wip
* wip
* Update NoteCreateService.ts
* wip
* wip
* wip
* Update NoteDraftEntityService.ts
* Update NoteCreateService.ts
* Update NoteDraftService.ts
* wip
* Update NoteDraftService.ts
* wip
* wip
* Update MkPostForm.vue
* wip
* Update MkPostForm.vue
* Update os.ts
* wip
* Update MkNoteDraftsDialog.vue
Diffstat (limited to 'packages/frontend/src/components/MkNoteDraftsDialog.vue')
| -rw-r--r-- | packages/frontend/src/components/MkNoteDraftsDialog.vue | 261 |
1 files changed, 173 insertions, 88 deletions
diff --git a/packages/frontend/src/components/MkNoteDraftsDialog.vue b/packages/frontend/src/components/MkNoteDraftsDialog.vue index 5b8211b715..3f0a5a5247 100644 --- a/packages/frontend/src/components/MkNoteDraftsDialog.vue +++ b/packages/frontend/src/components/MkNoteDraftsDialog.vue @@ -15,101 +15,151 @@ SPDX-License-Identifier: AGPL-3.0-only @esc="cancel()" > <template #header> - {{ i18n.ts.drafts }} ({{ currentDraftsCount }}/{{ $i?.policies.noteDraftLimit }}) + {{ i18n.ts.draftsAndScheduledNotes }} ({{ currentDraftsCount }}/{{ $i?.policies.noteDraftLimit }}) </template> - <div class="_spacer"> - <MkPagination :paginator="paginator" withControl> - <template #empty> - <MkResult type="empty" :text="i18n.ts._drafts.noDrafts"/> - </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div - v-for="draft in (items as unknown as Misskey.entities.NoteDraft[])" - :key="draft.id" - v-panel - :class="[$style.draft]" - > - <div :class="$style.draftBody" class="_gaps_s"> - <div :class="$style.draftInfo"> - <div :class="$style.draftMeta"> - <div v-if="draft.reply" class="_nowrap"> - <i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span"> - <template #user> - <Mfm v-if="draft.reply.user.name != null" :text="draft.reply.user.name" :plain="true" :nowrap="true"/> - <MkAcct v-else :user="draft.reply.user"/> - </template> - </I18n> - </div> - <div v-else-if="draft.replyId" class="_nowrap"> - <i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span"> - <template #user> - {{ i18n.ts.deletedNote }} - </template> - </I18n> - </div> - <div v-if="draft.renote && draft.text != null" class="_nowrap"> - <i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span"> - <template #user> - <Mfm v-if="draft.renote.user.name != null" :text="draft.renote.user.name" :plain="true" :nowrap="true"/> - <MkAcct v-else :user="draft.renote.user"/> - </template> - </I18n> - </div> - <div v-else-if="draft.renoteId" class="_nowrap"> - <i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span"> - <template #user> - {{ i18n.ts.deletedNote }} - </template> - </I18n> + <MkStickyContainer> + <template #header> + <MkTabs + v-model:tab="tab" + centered + :class="$style.tabs" + :tabs="[ + { + key: 'drafts', + title: i18n.ts.drafts, + icon: 'ti ti-pencil-question', + }, + { + key: 'scheduled', + title: i18n.ts.scheduled, + icon: 'ti ti-calendar-clock', + }, + ]" + /> + </template> + + <div class="_spacer"> + <MkPagination :key="tab" :paginator="tab === 'scheduled' ? scheduledPaginator : draftsPaginator" withControl> + <template #empty> + <MkResult type="empty" :text="i18n.ts._drafts.noDrafts"/> + </template> + + <template #default="{ items }"> + <div class="_gaps_s"> + <div + v-for="draft in (items as unknown as Misskey.entities.NoteDraft[])" + :key="draft.id" + v-panel + :class="[$style.draft]" + > + <div :class="$style.draftBody" class="_gaps_s"> + <MkInfo v-if="draft.scheduledAt != null && draft.isActuallyScheduled"> + <I18n :src="i18n.ts.scheduledToPostOnX" tag="span"> + <template #x> + <MkTime :time="draft.scheduledAt" :mode="'detail'" style="font-weight: bold;"/> + </template> + </I18n> + </MkInfo> + <div :class="$style.draftInfo"> + <div :class="$style.draftMeta"> + <div v-if="draft.reply" class="_nowrap"> + <i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span"> + <template #user> + <Mfm v-if="draft.reply.user.name != null" :text="draft.reply.user.name" :plain="true" :nowrap="true"/> + <MkAcct v-else :user="draft.reply.user"/> + </template> + </I18n> + </div> + <div v-else-if="draft.replyId" class="_nowrap"> + <i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span"> + <template #user> + {{ i18n.ts.deletedNote }} + </template> + </I18n> + </div> + <div v-if="draft.renote && draft.text != null" class="_nowrap"> + <i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span"> + <template #user> + <Mfm v-if="draft.renote.user.name != null" :text="draft.renote.user.name" :plain="true" :nowrap="true"/> + <MkAcct v-else :user="draft.renote.user"/> + </template> + </I18n> + </div> + <div v-else-if="draft.renoteId" class="_nowrap"> + <i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span"> + <template #user> + {{ i18n.ts.deletedNote }} + </template> + </I18n> + </div> + <div v-if="draft.channel" class="_nowrap"> + <i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }} + </div> </div> - <div v-if="draft.channel" class="_nowrap"> - <i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }} + </div> + <div :class="$style.draftContent"> + <Mfm :text="getNoteSummary(draft, { showRenote: false, showReply: false })" :plain="true" :author="draft.user"/> + </div> + <div :class="$style.draftFooter"> + <div :class="$style.draftVisibility"> + <span :title="i18n.ts._visibility[draft.visibility]"> + <i v-if="draft.visibility === 'public'" class="ti ti-world"></i> + <i v-else-if="draft.visibility === 'home'" class="ti ti-home"></i> + <i v-else-if="draft.visibility === 'followers'" class="ti ti-lock"></i> + <i v-else-if="draft.visibility === 'specified'" class="ti ti-mail"></i> + </span> + <span v-if="draft.localOnly" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> </div> + <MkTime :time="draft.createdAt" :class="$style.draftCreatedAt" mode="detail" colored/> </div> </div> - <div :class="$style.draftContent"> - <Mfm :text="getNoteSummary(draft, { showRenote: false, showReply: false })" :plain="true" :author="draft.user"/> - </div> - <div :class="$style.draftFooter"> - <div :class="$style.draftVisibility"> - <span :title="i18n.ts._visibility[draft.visibility]"> - <i v-if="draft.visibility === 'public'" class="ti ti-world"></i> - <i v-else-if="draft.visibility === 'home'" class="ti ti-home"></i> - <i v-else-if="draft.visibility === 'followers'" class="ti ti-lock"></i> - <i v-else-if="draft.visibility === 'specified'" class="ti ti-mail"></i> - </span> - <span v-if="draft.localOnly" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> - </div> - <MkTime :time="draft.createdAt" :class="$style.draftCreatedAt" mode="detail" colored/> + + <div :class="$style.draftActions" class="_buttons"> + <template v-if="draft.scheduledAt != null && draft.isActuallyScheduled"> + <MkButton + :class="$style.itemButton" + small + @click="cancelSchedule(draft)" + > + <i class="ti ti-calendar-x"></i> {{ i18n.ts._drafts.cancelSchedule }} + </MkButton> + <!-- TODO + <MkButton + :class="$style.itemButton" + small + @click="reSchedule(draft)" + > + <i class="ti ti-calendar-time"></i> {{ i18n.ts._drafts.reSchedule }} + </MkButton> + --> + </template> + <MkButton + v-else + :class="$style.itemButton" + small + @click="restoreDraft(draft)" + > + <i class="ti ti-corner-up-left"></i> {{ i18n.ts._drafts.restore }} + </MkButton> + <MkButton + v-tooltip="i18n.ts._drafts.delete" + danger + small + :iconOnly="true" + :class="$style.itemButton" + style="margin-left: auto;" + @click="deleteDraft(draft)" + > + <i class="ti ti-trash"></i> + </MkButton> </div> </div> - <div :class="$style.draftActions" class="_buttons"> - <MkButton - :class="$style.itemButton" - small - @click="restoreDraft(draft)" - > - <i class="ti ti-corner-up-left"></i> - {{ i18n.ts._drafts.restore }} - </MkButton> - <MkButton - v-tooltip="i18n.ts._drafts.delete" - danger - small - :iconOnly="true" - :class="$style.itemButton" - @click="deleteDraft(draft)" - > - <i class="ti ti-trash"></i> - </MkButton> - </div> </div> - </div> - </template> - </MkPagination> - </div> + </template> + </MkPagination> + </div> + </MkStickyContainer> </MkModalWindow> </template> @@ -125,6 +175,12 @@ import * as os from '@/os.js'; import { $i } from '@/i.js'; import { misskeyApi } from '@/utility/misskey-api'; import { Paginator } from '@/utility/paginator.js'; +import MkTabs from '@/components/MkTabs.vue'; +import MkInfo from '@/components/MkInfo.vue'; + +const props = defineProps<{ + scheduled?: boolean; +}>(); const emit = defineEmits<{ (ev: 'restore', draft: Misskey.entities.NoteDraft): void; @@ -132,8 +188,20 @@ const emit = defineEmits<{ (ev: 'closed'): void; }>(); -const paginator = markRaw(new Paginator('notes/drafts/list', { +const tab = ref<'drafts' | 'scheduled'>(props.scheduled ? 'scheduled' : 'drafts'); + +const draftsPaginator = markRaw(new Paginator('notes/drafts/list', { + limit: 10, + params: { + scheduled: false, + }, +})); + +const scheduledPaginator = markRaw(new Paginator('notes/drafts/list', { limit: 10, + params: { + scheduled: true, + }, })); const currentDraftsCount = ref(0); @@ -162,7 +230,17 @@ async function deleteDraft(draft: Misskey.entities.NoteDraft) { if (canceled) return; os.apiWithDialog('notes/drafts/delete', { draftId: draft.id }).then(() => { - paginator.reload(); + draftsPaginator.reload(); + }); +} + +async function cancelSchedule(draft: Misskey.entities.NoteDraft) { + os.apiWithDialog('notes/drafts/update', { + draftId: draft.id, + isActuallyScheduled: false, + scheduledAt: null, + }).then(() => { + scheduledPaginator.reload(); }); } </script> @@ -220,4 +298,11 @@ async function deleteDraft(draft: Misskey.entities.NoteDraft) { padding-top: 16px; border-top: solid 1px var(--MI_THEME-divider); } + +.tabs { + background: color(from var(--MI_THEME-bg) srgb r g b / 0.75); + -webkit-backdrop-filter: var(--MI-blur, blur(15px)); + backdrop-filter: var(--MI-blur, blur(15px)); + border-bottom: solid 0.5px var(--MI_THEME-divider); +} </style> |