予約投稿の一覧表示、削除をできるようにした
dbに投稿内容を保存するようにした Signed-off-by: mattyatea <mattyacocacora0@gmail.com>
This commit is contained in:
parent
14b48f87d8
commit
540f531b6d
3
locales/index.d.ts
vendored
3
locales/index.d.ts
vendored
|
@ -593,8 +593,9 @@ export interface Locale {
|
||||||
"enableInfiniteScroll": string;
|
"enableInfiniteScroll": string;
|
||||||
"visibility": string;
|
"visibility": string;
|
||||||
"poll": string;
|
"poll": string;
|
||||||
"schedule": string;
|
"schedulePost": string;
|
||||||
"useCw": string;
|
"useCw": string;
|
||||||
|
"schedulePostList": string;
|
||||||
"enablePlayer": string;
|
"enablePlayer": string;
|
||||||
"disablePlayer": string;
|
"disablePlayer": string;
|
||||||
"expandTweet": string;
|
"expandTweet": string;
|
||||||
|
|
|
@ -590,8 +590,9 @@ invisibleNote: "非公開の投稿"
|
||||||
enableInfiniteScroll: "自動でもっと見る"
|
enableInfiniteScroll: "自動でもっと見る"
|
||||||
visibility: "公開範囲"
|
visibility: "公開範囲"
|
||||||
poll: "アンケート"
|
poll: "アンケート"
|
||||||
schedule: "予約"
|
schedulePost: "予約投稿"
|
||||||
useCw: "内容を隠す"
|
useCw: "内容を隠す"
|
||||||
|
schedulePostList: "予約投稿一覧"
|
||||||
enablePlayer: "プレイヤーを開く"
|
enablePlayer: "プレイヤーを開く"
|
||||||
disablePlayer: "プレイヤーを閉じる"
|
disablePlayer: "プレイヤーを閉じる"
|
||||||
expandTweet: "ポストを展開する"
|
expandTweet: "ポストを展開する"
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
export class Schedulenote1699337454434 {
|
|
||||||
name = 'Schedulenote1699337454434'
|
|
||||||
|
|
||||||
async up(queryRunner) {
|
|
||||||
await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async down(queryRunner) {
|
|
||||||
await queryRunner.query(`DROP TABLE "note_schedule"`);
|
|
||||||
}
|
|
||||||
}
|
|
12
packages/backend/migration/1699437894737-schedulenote.js
Normal file
12
packages/backend/migration/1699437894737-schedulenote.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
export class Schedulenote1699437894737 {
|
||||||
|
name = 'Schedulenote1699437894737'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_e798958c40009bf0cdef4f28b5" ON "note_schedule" ("userId") `);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`DROP TABLE "note_schedule"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,8 +19,12 @@ export class MiNoteSchedule {
|
||||||
@Column('jsonb')
|
@Column('jsonb')
|
||||||
public note:{createdAt?: Date | undefined ; apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote };
|
public note:{createdAt?: Date | undefined ; apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote };
|
||||||
|
|
||||||
|
@Index()
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 260,
|
length: 260,
|
||||||
})
|
})
|
||||||
public userId: MiUser['id'];
|
public userId: MiUser['id'];
|
||||||
|
|
||||||
|
@Column('timestamp with time zone')
|
||||||
|
public expiresAt: Date;
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,7 +264,9 @@ import * as ep___notes_clips from './endpoints/notes/clips.js';
|
||||||
import * as ep___notes_conversation from './endpoints/notes/conversation.js';
|
import * as ep___notes_conversation from './endpoints/notes/conversation.js';
|
||||||
import * as ep___notes_create from './endpoints/notes/create.js';
|
import * as ep___notes_create from './endpoints/notes/create.js';
|
||||||
import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js';
|
import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js';
|
||||||
|
import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js';
|
||||||
import * as ep___notes_delete from './endpoints/notes/delete.js';
|
import * as ep___notes_delete from './endpoints/notes/delete.js';
|
||||||
|
import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js';
|
||||||
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
|
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
|
||||||
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
|
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
|
||||||
import * as ep___notes_featured from './endpoints/notes/featured.js';
|
import * as ep___notes_featured from './endpoints/notes/featured.js';
|
||||||
|
@ -623,7 +625,9 @@ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes
|
||||||
const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default };
|
const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default };
|
||||||
const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default };
|
const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default };
|
||||||
const $notes_schedule_create: Provider = { provide: 'ep:notes/create-schedule', useClass: ep___notes_schedule_create.default };
|
const $notes_schedule_create: Provider = { provide: 'ep:notes/create-schedule', useClass: ep___notes_schedule_create.default };
|
||||||
|
const $notes_schedule_list: Provider = { provide: 'ep:notes/list-schedule', useClass: ep___notes_schedule_list.default };
|
||||||
const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default };
|
const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default };
|
||||||
|
const $notes_schedule_delete: Provider = { provide: 'ep:notes/delete-schedule', useClass: ep___notes_schedule_delete.default };
|
||||||
const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default };
|
const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default };
|
||||||
const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default };
|
const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default };
|
||||||
const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default };
|
const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default };
|
||||||
|
@ -986,7 +990,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$notes_conversation,
|
$notes_conversation,
|
||||||
$notes_create,
|
$notes_create,
|
||||||
$notes_schedule_create,
|
$notes_schedule_create,
|
||||||
|
$notes_schedule_list,
|
||||||
$notes_delete,
|
$notes_delete,
|
||||||
|
$notes_schedule_delete,
|
||||||
$notes_favorites_create,
|
$notes_favorites_create,
|
||||||
$notes_favorites_delete,
|
$notes_favorites_delete,
|
||||||
$notes_featured,
|
$notes_featured,
|
||||||
|
@ -1343,7 +1349,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||||
$notes_conversation,
|
$notes_conversation,
|
||||||
$notes_create,
|
$notes_create,
|
||||||
$notes_schedule_create,
|
$notes_schedule_create,
|
||||||
|
$notes_schedule_list,
|
||||||
$notes_delete,
|
$notes_delete,
|
||||||
|
$notes_schedule_delete,
|
||||||
$notes_favorites_create,
|
$notes_favorites_create,
|
||||||
$notes_favorites_delete,
|
$notes_favorites_delete,
|
||||||
$notes_featured,
|
$notes_featured,
|
||||||
|
|
|
@ -264,7 +264,9 @@ import * as ep___notes_clips from './endpoints/notes/clips.js';
|
||||||
import * as ep___notes_conversation from './endpoints/notes/conversation.js';
|
import * as ep___notes_conversation from './endpoints/notes/conversation.js';
|
||||||
import * as ep___notes_create from './endpoints/notes/create.js';
|
import * as ep___notes_create from './endpoints/notes/create.js';
|
||||||
import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js';
|
import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js';
|
||||||
|
import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js';
|
||||||
import * as ep___notes_delete from './endpoints/notes/delete.js';
|
import * as ep___notes_delete from './endpoints/notes/delete.js';
|
||||||
|
import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js';
|
||||||
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
|
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
|
||||||
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
|
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
|
||||||
import * as ep___notes_featured from './endpoints/notes/featured.js';
|
import * as ep___notes_featured from './endpoints/notes/featured.js';
|
||||||
|
@ -621,7 +623,9 @@ const eps = [
|
||||||
['notes/conversation', ep___notes_conversation],
|
['notes/conversation', ep___notes_conversation],
|
||||||
['notes/create', ep___notes_create],
|
['notes/create', ep___notes_create],
|
||||||
['notes/create-schedule', ep___notes_schedule_create],
|
['notes/create-schedule', ep___notes_schedule_create],
|
||||||
|
['notes/list-schedule', ep___notes_schedule_list],
|
||||||
['notes/delete', ep___notes_delete],
|
['notes/delete', ep___notes_delete],
|
||||||
|
['notes/delete-schedule', ep___notes_schedule_delete],
|
||||||
['notes/favorites/create', ep___notes_favorites_create],
|
['notes/favorites/create', ep___notes_favorites_create],
|
||||||
['notes/favorites/delete', ep___notes_favorites_delete],
|
['notes/favorites/delete', ep___notes_favorites_delete],
|
||||||
['notes/featured', ep___notes_featured],
|
['notes/featured', ep___notes_featured],
|
||||||
|
|
|
@ -197,9 +197,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
@Inject(DI.db)
|
|
||||||
private db: DataSource,
|
|
||||||
|
|
||||||
@Inject(DI.notesRepository)
|
@Inject(DI.notesRepository)
|
||||||
private notesRepository: NotesRepository,
|
private notesRepository: NotesRepository,
|
||||||
|
|
||||||
|
@ -388,6 +385,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
id: noteId,
|
id: noteId,
|
||||||
note: note,
|
note: note,
|
||||||
userId: me.id,
|
userId: me.id,
|
||||||
|
expiresAt: new Date(ps.schedule.expiresAt),
|
||||||
});
|
});
|
||||||
|
|
||||||
const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now();
|
const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now();
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ms from 'ms';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { NoteScheduleRepository } from '@/models/_.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['notes'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 300,
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchNote: {
|
||||||
|
message: 'No such note.',
|
||||||
|
code: 'NO_SUCH_NOTE',
|
||||||
|
id: '490be23f-8c1f-4796-819f-94cb4f9d1630',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
noteId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['noteId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.noteScheduleRepository)
|
||||||
|
private noteScheduleRepository: NoteScheduleRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
await this.noteScheduleRepository.delete({ id: ps.noteId });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ms from 'ms';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { NoteScheduleRepository } from '@/models/_.js';
|
||||||
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['notes'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'Note',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 300,
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.noteScheduleRepository)
|
||||||
|
private noteScheduleRepository: NoteScheduleRepository,
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const scheduleNotes = await this.noteScheduleRepository.findBy({ userId: me.id });
|
||||||
|
const user = await this.userEntityService.pack(me, me);
|
||||||
|
scheduleNotes.forEach((item: any) => {
|
||||||
|
item.note.user = user;
|
||||||
|
item.note.createdAt = new Date(item.expiresAt);
|
||||||
|
item.note.isSchedule = true;
|
||||||
|
item.note.id = item.id;
|
||||||
|
});
|
||||||
|
return scheduleNotes;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-if="mock">
|
<div v-if="mock">
|
||||||
<MkTime :time="note.createdAt" colored/>
|
<MkTime :time="note.createdAt" colored/>
|
||||||
</div>
|
</div>
|
||||||
|
<MkTime v-else-if="note.isSchedule" mode="absolute" :time="note.createdAt" colored/>
|
||||||
<MkA v-else :to="notePage(note)">
|
<MkA v-else :to="notePage(note)">
|
||||||
<MkTime :time="note.createdAt" colored/>
|
<MkTime :time="note.createdAt" colored/>
|
||||||
</MkA>
|
</MkA>
|
||||||
|
@ -42,7 +43,8 @@ import { notePage } from '@/filters/note.js';
|
||||||
import { userPage } from '@/filters/user.js';
|
import { userPage } from '@/filters/user.js';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note & {isSchedule? : boolean};
|
||||||
|
scheduled?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const mock = inject<boolean>('mock', false);
|
const mock = inject<boolean>('mock', false);
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div v-show="!isDeleted" :class="$style.root" :tabindex="!isDeleted ? '-1' : undefined">
|
||||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||||
<div :class="$style.main">
|
<div :class="$style.main">
|
||||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||||
|
@ -16,23 +16,40 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div v-show="note.cw == null || showContent">
|
<div v-show="note.cw == null || showContent">
|
||||||
<MkSubNoteContent :class="$style.text" :note="note"/>
|
<MkSubNoteContent :class="$style.text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="note.isSchedule" style="margin-top: 10px;">
|
||||||
|
<MkButton :class="$style.button" inline @click="editScheduleNote(note.id)">{{ i18n.ts.edit }}</MkButton>
|
||||||
|
<MkButton :class="$style.button" inline danger @click="deleteScheduleNote()">{{ i18n.ts.delete }}</MkButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { } from 'vue';
|
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { i18n } from '../i18n.js';
|
||||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||||
import MkCwButton from '@/components/MkCwButton.vue';
|
import MkCwButton from '@/components/MkCwButton.vue';
|
||||||
import { $i } from '@/account.js';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
const isDeleted = ref(false);
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Misskey.entities.Note;
|
note: Misskey.entities.Note & {isSchedule? : boolean};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
async function deleteScheduleNote() {
|
||||||
|
await os.apiWithDialog('notes/delete-schedule', { noteId: props.note.id })
|
||||||
|
.then(() => {
|
||||||
|
isDeleted.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function editScheduleNote(id) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const showContent = $ref(false);
|
const showContent = $ref(false);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -42,8 +59,12 @@ const showContent = $ref(false);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 0.95em;
|
font-size: 0.95em;
|
||||||
|
border-bottom: solid 0.5px var(--divider);
|
||||||
|
}
|
||||||
|
.button{
|
||||||
|
margin-right: var(--margin);
|
||||||
|
margin-bottom: var(--margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -19,6 +19,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.headerRight">
|
<div :class="$style.headerRight">
|
||||||
|
<button v-tooltip="i18n.ts.schedulePost" class="_button" :class="[$style.headerRightItem, { [$style.headerRightButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-time"></i></button>
|
||||||
|
<button v-tooltip="i18n.ts.schedulePostList" class="_button" :class="[$style.headerRightItem]" @click="listSchedulePost"><i class="ti ti-calendar-event"></i></button>
|
||||||
<template v-if="!(channel != null && fixed)">
|
<template v-if="!(channel != null && fixed)">
|
||||||
<button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility">
|
<button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility">
|
||||||
<span v-if="visibility === 'public'"><i class="ti ti-world"></i></span>
|
<span v-if="visibility === 'public'"><i class="ti ti-world"></i></span>
|
||||||
|
@ -81,7 +83,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.footerLeft">
|
<div :class="$style.footerLeft">
|
||||||
<button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button>
|
<button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button>
|
||||||
<button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button>
|
<button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button>
|
||||||
<button v-tooltip="i18n.ts.schedule" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-event"></i></button>
|
|
||||||
<button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button>
|
<button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button>
|
||||||
<button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button>
|
<button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button>
|
||||||
<button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button>
|
<button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button>
|
||||||
|
@ -127,6 +128,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||||
import { miLocalStorage } from '@/local-storage.js';
|
import { miLocalStorage } from '@/local-storage.js';
|
||||||
import { claimAchievement } from '@/scripts/achievements.js';
|
import { claimAchievement } from '@/scripts/achievements.js';
|
||||||
import MkScheduleEditor from '@/components/MkScheduleEditor.vue';
|
import MkScheduleEditor from '@/components/MkScheduleEditor.vue';
|
||||||
|
import { listSchedulePost } from '@/os.js';
|
||||||
|
|
||||||
const modal = inject('modal');
|
const modal = inject('modal');
|
||||||
|
|
||||||
|
@ -1061,6 +1063,10 @@ defineExpose({
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.headerRightButtonActive {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
&.danger {
|
&.danger {
|
||||||
color: #ff2a2a;
|
color: #ff2a2a;
|
||||||
}
|
}
|
||||||
|
@ -1214,6 +1220,7 @@ defineExpose({
|
||||||
grid-template-columns: repeat(auto-fill, minmax(42px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(42px, 1fr));
|
||||||
grid-auto-rows: 40px;
|
grid-auto-rows: 40px;
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footerButton {
|
.footerButton {
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<MkModalWindow
|
||||||
|
ref="dialogEl"
|
||||||
|
:withOkButton="false"
|
||||||
|
@click="cancel()"
|
||||||
|
@close="cancel()"
|
||||||
|
@closed="$emit('closed')"
|
||||||
|
>
|
||||||
|
<template #header> 予約投稿一覧</template>
|
||||||
|
<div v-for="item in notes">
|
||||||
|
<MkSpacer :marginMin="14" :marginMax="16">
|
||||||
|
<MkNoteSimple scheduled="true" :note="item.note"/>
|
||||||
|
</MkSpacer>
|
||||||
|
</div>
|
||||||
|
</MkModalWindow>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||||
|
import MkSignin from '@/components/MkSignin.vue';
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(ev: 'ok', selected: Misskey.entities.UserDetailed): void;
|
||||||
|
(ev: 'cancel'): void;
|
||||||
|
(ev: 'closed'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let dialogEl = $ref();
|
||||||
|
const notes = ref([]);
|
||||||
|
const cancel = () => {
|
||||||
|
emit('cancel');
|
||||||
|
dialogEl.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
notes.value = await os.api('notes/list-schedule');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
</style>
|
|
@ -431,7 +431,13 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) {
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
export async function listSchedulePost() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
popup(defineAsyncComponent(() => import('@/components/MkSchedulePostListDialog.vue')), {
|
||||||
|
}, {
|
||||||
|
}, 'closed');
|
||||||
|
});
|
||||||
|
}
|
||||||
export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
|
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
|
||||||
|
|
Loading…
Reference in a new issue