This commit is contained in:
syuilo 2025-09-24 17:53:36 +09:00
parent 4cef192011
commit 4a698165cc
5 changed files with 158 additions and 98 deletions

4
locales/index.d.ts vendored
View File

@ -5569,6 +5569,10 @@ export interface Locale extends ILocale {
* *
*/ */
"schedule": string; "schedule": string;
/**
*
*/
"scheduled": string;
"_compression": { "_compression": {
"_quality": { "_quality": {
/** /**

View File

@ -1387,6 +1387,7 @@ createUserSpecifiedNote: "ユーザー指定ノートを作成"
schedulePost: "投稿を予約" schedulePost: "投稿を予約"
scheduleToPostOnX: "{x}に投稿を予約します" scheduleToPostOnX: "{x}に投稿を予約します"
schedule: "予約" schedule: "予約"
scheduled: "予約"
_compression: _compression:
_quality: _quality:

View File

@ -41,6 +41,7 @@ export const paramDef = {
untilId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' }, sinceDate: { type: 'integer' },
untilDate: { type: 'integer' }, untilDate: { type: 'integer' },
scheduled: { type: 'boolean', nullable: true },
}, },
required: [], required: [],
} as const; } as const;
@ -58,6 +59,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const query = this.queryService.makePaginationQuery<MiNoteDraft>(this.noteDraftsRepository.createQueryBuilder('drafts'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) const query = this.queryService.makePaginationQuery<MiNoteDraft>(this.noteDraftsRepository.createQueryBuilder('drafts'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere('drafts.userId = :meId', { meId: me.id }); .andWhere('drafts.userId = :meId', { meId: me.id });
if (ps.scheduled === true) {
query.andWhere('drafts.isActuallyScheduled = true');
} else if (ps.scheduled === false) {
query.andWhere('drafts.isActuallyScheduled = false');
}
const drafts = await query const drafts = await query
.limit(ps.limit) .limit(ps.limit)
.getMany(); .getMany();

View File

@ -15,110 +15,133 @@ SPDX-License-Identifier: AGPL-3.0-only
@esc="cancel()" @esc="cancel()"
> >
<template #header> <template #header>
{{ i18n.ts.drafts }} ({{ currentDraftsCount }}/{{ $i?.policies.noteDraftLimit }}) {{ i18n.ts.draftsAndScheduledNotes }} ({{ currentDraftsCount }}/{{ $i?.policies.noteDraftLimit }})
</template> </template>
<div class="_spacer">
<MkPagination :paginator="paginator" withControl>
<template #empty>
<MkResult type="empty" :text="i18n.ts._drafts.noDrafts"/>
</template>
<template #default="{ items }"> <MkStickyContainer>
<div class="_gaps_s"> <template #header>
<div <MkTabs
v-for="draft in (items as unknown as Misskey.entities.NoteDraft[])" v-model:tab="tab"
:key="draft.id" centered
v-panel :class="$style.tabs"
:class="[$style.draft]" :tabs="[
> {
<div :class="$style.draftBody" class="_gaps_s"> key: 'drafts',
<div :class="$style.draftInfo"> title: i18n.ts.drafts,
<div :class="$style.draftMeta"> icon: 'ti ti-pencil-question',
<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> key: 'scheduled',
<Mfm v-if="draft.reply.user.name != null" :text="draft.reply.user.name" :plain="true" :nowrap="true"/> title: i18n.ts.scheduled,
<MkAcct v-else :user="draft.reply.user"/> icon: 'ti ti-calendar-clock',
</template> },
</I18n> ]"
</div> />
<div v-else-if="draft.replyId" class="_nowrap"> </template>
<i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span">
<template #user> <div class="_spacer">
{{ i18n.ts.deletedNote }} <MkPagination :key="tab" :paginator="tab === 'scheduled' ? scheduledPaginator : draftsPaginator" withControl>
</template> <template #empty>
</I18n> <MkResult type="empty" :text="i18n.ts._drafts.noDrafts"/>
</div> </template>
<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 #default="{ items }">
<template #user> <div class="_gaps_s">
<Mfm v-if="draft.renote.user.name != null" :text="draft.renote.user.name" :plain="true" :nowrap="true"/> <div
<MkAcct v-else :user="draft.renote.user"/> v-for="draft in (items as unknown as Misskey.entities.NoteDraft[])"
</template> :key="draft.id"
</I18n> v-panel
</div> :class="[$style.draft]"
<div v-else-if="draft.renoteId" class="_nowrap"> >
<i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span"> <div :class="$style.draftBody" class="_gaps_s">
<template #user> <div :class="$style.draftInfo">
{{ i18n.ts.deletedNote }} <div :class="$style.draftMeta">
</template> <div v-if="draft.reply" class="_nowrap">
</I18n> <i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span">
</div> <template #user>
<div v-if="draft.channel" class="_nowrap"> <Mfm v-if="draft.reply.user.name != null" :text="draft.reply.user.name" :plain="true" :nowrap="true"/>
<i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }} <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>
</div> </div>
</div> <div :class="$style.draftContent">
<div :class="$style.draftContent"> <Mfm :text="getNoteSummary(draft, { showRenote: false, showReply: false })" :plain="true" :author="draft.user"/>
<Mfm :text="getNoteSummary(draft, { showRenote: false, showReply: false })" :plain="true" :author="draft.user"/> </div>
</div> <div :class="$style.draftFooter">
<div :class="$style.draftFooter"> <div :class="$style.draftVisibility">
<div :class="$style.draftVisibility"> <span :title="i18n.ts._visibility[draft.visibility]">
<span :title="i18n.ts._visibility[draft.visibility]"> <i v-if="draft.visibility === 'public'" class="ti ti-world"></i>
<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 === 'home'" class="ti ti-home"></i> <i v-else-if="draft.visibility === 'followers'" class="ti ti-lock"></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>
<i v-else-if="draft.visibility === 'specified'" class="ti ti-mail"></i> </span>
</span> <span v-if="draft.localOnly" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></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>
<MkTime :time="draft.createdAt" :class="$style.draftCreatedAt" mode="detail" colored/>
</div> </div>
</div> <div :class="$style.draftActions" class="_buttons">
<div :class="$style.draftActions" class="_buttons"> <MkButton
<MkButton :class="$style.itemButton"
:class="$style.itemButton" small
small @click="restoreDraft(draft)"
@click="restoreDraft(draft)" >
> <i class="ti ti-corner-up-left"></i>
<i class="ti ti-corner-up-left"></i> {{ i18n.ts._drafts.restore }}
{{ i18n.ts._drafts.restore }} </MkButton>
</MkButton> <MkButton
<MkButton :class="$style.itemButton"
:class="$style.itemButton" small
small @click="schedule(draft)"
@click="schedule(draft)" >
> <i class="ti ti-calendar-time"></i>
<i class="ti ti-calendar-time"></i> {{ i18n.ts._drafts.schedule }}
{{ i18n.ts._drafts.schedule }} </MkButton>
</MkButton> <MkButton
<MkButton v-tooltip="i18n.ts._drafts.delete"
v-tooltip="i18n.ts._drafts.delete" danger
danger small
small :iconOnly="true"
:iconOnly="true" :class="$style.itemButton"
:class="$style.itemButton" style="margin-left: auto;"
style="margin-left: auto;" @click="deleteDraft(draft)"
@click="deleteDraft(draft)" >
> <i class="ti ti-trash"></i>
<i class="ti ti-trash"></i> </MkButton>
</MkButton> </div>
</div> </div>
</div> </div>
</div> </template>
</template> </MkPagination>
</MkPagination> </div>
</div> </MkStickyContainer>
</MkModalWindow> </MkModalWindow>
</template> </template>
@ -134,6 +157,11 @@ import * as os from '@/os.js';
import { $i } from '@/i.js'; import { $i } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api'; import { misskeyApi } from '@/utility/misskey-api';
import { Paginator } from '@/utility/paginator.js'; import { Paginator } from '@/utility/paginator.js';
import MkTabs from '@/components/MkTabs.vue';
const props = defineProps<{
scheduled?: boolean;
}>();
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'restore', draft: Misskey.entities.NoteDraft): void; (ev: 'restore', draft: Misskey.entities.NoteDraft): void;
@ -141,8 +169,20 @@ const emit = defineEmits<{
(ev: 'closed'): void; (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, limit: 10,
params: {
scheduled: false,
},
}));
const scheduledPaginator = markRaw(new Paginator('notes/drafts/list', {
limit: 10,
params: {
scheduled: true,
},
})); }));
const currentDraftsCount = ref(0); const currentDraftsCount = ref(0);
@ -171,7 +211,7 @@ async function deleteDraft(draft: Misskey.entities.NoteDraft) {
if (canceled) return; if (canceled) return;
os.apiWithDialog('notes/drafts/delete', { draftId: draft.id }).then(() => { os.apiWithDialog('notes/drafts/delete', { draftId: draft.id }).then(() => {
paginator.reload(); draftsPaginator.reload();
}); });
} }
</script> </script>
@ -229,4 +269,11 @@ async function deleteDraft(draft: Misskey.entities.NoteDraft) {
padding-top: 16px; padding-top: 16px;
border-top: solid 1px var(--MI_THEME-divider); 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> </style>

View File

@ -29354,6 +29354,7 @@ export interface operations {
untilId?: string; untilId?: string;
sinceDate?: number; sinceDate?: number;
untilDate?: number; untilDate?: number;
scheduled?: boolean | null;
}; };
}; };
}; };