summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkNoteDraftsDialog.vue
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-09-26 15:29:52 +0900
committerGitHub <noreply@github.com>2025-09-26 15:29:52 +0900
commitd1446d195abb52c560c7c97177d08103a175acf7 (patch)
tree355689adb542333f4c5abb186ab9819c29274612 /packages/frontend/src/components/MkNoteDraftsDialog.vue
parentfix(frontend): ビルド成果物のファイル名にlocalesのhashを含め... (diff)
downloadmisskey-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.vue261
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>