diff options
| author | taichan <40626578+tai-cha@users.noreply.github.com> | 2025-06-25 17:09:23 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-25 17:09:23 +0900 |
| commit | b752dc72e531f6c63f09876a1c68a87a77c03b49 (patch) | |
| tree | d9bd25825a9b1b06c8db07a1888594ffc9db45c8 /packages/frontend/src/components/MkNoteDraftsDialog.vue | |
| parent | fix(frontend): ファイルがドライブの既定アップロード先に... (diff) | |
| download | misskey-b752dc72e531f6c63f09876a1c68a87a77c03b49.tar.gz misskey-b752dc72e531f6c63f09876a1c68a87a77c03b49.tar.bz2 misskey-b752dc72e531f6c63f09876a1c68a87a77c03b49.zip | |
feat: ノートの下書き(draft of note) (#15298)
* WIp (backend)
* Remove unused
* 下書きbackend 続き
* fix(backedn): visibilityが下書きに反映されない
* Update packages/backend/src/postgres.ts
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
* Fix : import order
* fix(backend) : createでcwが効かない
* FIX FOREGIN KEY
* wip: frontend(既存の下書きを挿入)
まだ:チャンネル表示、下書きの作成、削除
* WIP: ノート選択ダイアログ
投稿時に下書きを削除
* Promiseに変更
* 連合なし、チャンネルも表示
* Hashtagの値抜け漏れ
* hasthagを0文字でも作成可能に
* 下書きの保存機構
* chore(misskey-js): build types
* localOnly抜け漏れ
* チャンネル情報の書き換え
* enhance(frontend): ヘッダ部の表示改善
* fix(frontend): ファイル添付できない
* fix: no file
* fix(frontend): 投票が反映されない
* ハッシュタグの展開(コメントアウト外し忘れ)
* fix: visibleUserIdsが反映されない
* enhance: APIの型を整備
* refactor: 型が整備できたのでasを削除
* Add userhost
* fix
* enhance: paginationを使う
* fix
* fix: 自分のアカウントでの投稿でしか下書きを利用できないように
完全に塞ぐことはできないが一応
* :art:
* APIのエラーIDを追加
* enhance: スタイル調整
* remove unused code
* :art:
* fix: ロールポリシーの型
* ロールの編集画面
* ダイアログの挙動改善
* 下書き機能が利用できない場合は表示しないように
* refactor
* fix: ダブルクリックが効かない問題を修正
* add comments
* fix
* fix: 保存時のエラーの種別にかかわらずmodalを閉じないように
* fix()backend: NoteDraftのreply, renoteの型が間違ってたので修正 (migtrationはあってた)
* fix: 投稿フォームを空白にして通常リノートできるやつは下書きとしては弾くように
* fix(backend): テキストが0文字でも下書きは保存できるように
* Fix(backend): replyIdの型定義がミスっているのを修正
* chore(misskey-js): update types
* Add CHANGELOG
* lint
* 常にサーバー下書きに保存し、上限を超えた場合のみ尋ねるように
* NoteDraftServiceにcreate, updateの処理を移譲
* Fix typeerror
* remove tooltip
* Remove Mkbutton:short and use iconOnly
* 不要なコメントの削除
* Remove Short Completely
* wip
* escキーまわりの挙動を改善
* 下書き選択時に下書き可能数と現在の量が分かるように
* cleanUp
* wip
* wi
* wip
* Update MkPostForm.vue
---------
Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/frontend/src/components/MkNoteDraftsDialog.vue')
| -rw-r--r-- | packages/frontend/src/components/MkNoteDraftsDialog.vue | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/packages/frontend/src/components/MkNoteDraftsDialog.vue b/packages/frontend/src/components/MkNoteDraftsDialog.vue new file mode 100644 index 0000000000..b4aff8d16f --- /dev/null +++ b/packages/frontend/src/components/MkNoteDraftsDialog.vue @@ -0,0 +1,218 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModalWindow + ref="dialogEl" + :width="600" + :height="650" + :withOkButton="false" + @click="cancel()" + @close="cancel()" + @closed="emit('closed')" + @esc="cancel()" +> + <template #header> + {{ i18n.ts.drafts }} ({{ currentDraftsCount }}/{{ $i?.policies.noteDraftLimit }}) + </template> + <div :class="$style.drafts" class="_gaps"> + <MkPagination ref="pagingEl" :pagination="paging"> + <template #empty> + <MkResult type="empty" :text="i18n.ts._drafts.noDrafts"/> + </template> + + <template #default="{ items }"> + <div class="_spacer _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-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-if="draft.channel" class="_nowrap"> + <i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }} + </div> + </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> + </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> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { ref, shallowRef, useTemplateRef } from 'vue'; +import * as Misskey from 'misskey-js'; +import type { PagingCtx } from '@/composables/use-pagination.js'; +import MkButton from '@/components/MkButton.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import { getNoteSummary } from '@/utility/get-note-summary.js'; +import { i18n } from '@/i18n.js'; +import * as os from '@/os.js'; +import { $i } from '@/i.js'; +import { misskeyApi } from '@/utility/misskey-api'; + +const emit = defineEmits<{ + (ev: 'restore', draft: Misskey.entities.NoteDraft): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; +}>(); + +const paging = { + endpoint: 'notes/drafts/list', + limit: 10, +} satisfies PagingCtx; + +const pagingComponent = useTemplateRef('pagingEl'); + +const currentDraftsCount = ref(0); +misskeyApi('notes/drafts/count').then((count) => { + currentDraftsCount.value = count; +}); + +const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); + +function cancel() { + emit('cancel'); + dialogEl.value?.close(); +} + +function restoreDraft(draft: Misskey.entities.NoteDraft) { + emit('restore', draft); + dialogEl.value?.close(); +} + +async function deleteDraft(draft: Misskey.entities.NoteDraft) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts._drafts.deleteAreYouSure, + }); + + if (canceled) return; + + os.apiWithDialog('notes/drafts/delete', { draftId: draft.id }).then(() => { + pagingComponent.value?.paginator.reload(); + }); +} +</script> + +<style lang="scss" module> +.drafts { + overflow-x: hidden; + overflow-x: clip; + overflow-y: auto; +} + +.draft { + padding: 16px; + gap: 16px; + border-radius: 10px; +} + +.draftBody { + width: 100%; + min-width: 0; +} + +.draftInfo { + display: flex; + width: 100%; + font-size: 0.85em; + opacity: 0.7; +} + +.draftMeta { + flex-grow: 1; + min-width: 0; +} + +.draftContent { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + font-size: 0.9em; +} + +.draftFooter { + display: flex; + align-items: center; + gap: 8px; +} + +.draftVisibility { + flex-shrink: 0; +} + +.draftCreatedAt { + font-size: 85%; + opacity: 0.7; +} + +.draftActions { + margin-top: 16px; + padding-top: 16px; + border-top: solid 1px var(--MI_THEME-divider); +} +</style> |