|
| 1 | +<template> |
| 2 | +<MkModalWindow |
| 3 | + ref="dialog" |
| 4 | + :height="500" |
| 5 | + :width="800" |
| 6 | + @click="done(true)" |
| 7 | + @close="done(true)" |
| 8 | + @closed="emit('closed')" |
| 9 | +> |
| 10 | + <template #header> |
| 11 | + {{ i18n.ts.drafts }} |
| 12 | + </template> |
| 13 | + <div style="display: flex; flex-direction: column"> |
| 14 | + <div v-if="drafts.length === 0" class="empty"> |
| 15 | + <div class="_fullinfo"> |
| 16 | + <img :src="infoImageUrl" class="_ghost"/> |
| 17 | + <div>{{ i18n.ts.nothing }}</div> |
| 18 | + </div> |
| 19 | + </div> |
| 20 | + <div v-for="draft in drafts" :key="draft.id" :class="$style.draftItem"> |
| 21 | + <div :class="$style.draftNote" @click="selectDraft(draft.id)"> |
| 22 | + <div :class="$style.draftNoteHeader"> |
| 23 | + <div :class="$style.draftNoteDestination"> |
| 24 | + <span v-if="draft.channel" style="opacity: 0.7; padding-right: 0.5em"> |
| 25 | + <i class="ti ti-device-tv"></i> {{ draft.channel.name }} |
| 26 | + </span> |
| 27 | + <span v-if="draft.renote"> |
| 28 | + <i class="ti ti-quote"></i> <MkAcct :user="draft.renote.user" /> <span>{{ draft.renote.text }}</span> |
| 29 | + </span> |
| 30 | + <span v-else-if="draft.reply"> |
| 31 | + <i class="ti ti-arrow-back-up"></i> <MkAcct :user="draft.reply.user" /> <span>{{ draft.reply.text }}</span> |
| 32 | + </span> |
| 33 | + <span v-else> |
| 34 | + <i class="ti ti-pencil"></i> |
| 35 | + </span> |
| 36 | + </div> |
| 37 | + <div :class="$style.draftNoteInfo"> |
| 38 | + <MkTime :time="draft.createdAt" colored /> |
| 39 | + <span v-if="draft.visibility !== 'public'" :title="i18n.ts._visibility[draft.visibility]" style="margin-left: 0.5em"> |
| 40 | + <i v-if="draft.visibility === 'home'" class="ti ti-home"></i> |
| 41 | + <i v-else-if="draft.visibility === 'followers'" class="ti ti-lock"></i> |
| 42 | + <i v-else-if="draft.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> |
| 43 | + </span> |
| 44 | + <span v-if="draft.localOnly" :title="i18n.ts._visibility['disableFederation']" style="margin-left: 0.5em"> |
| 45 | + <i class="ti ti-rocket-off"></i> |
| 46 | + </span> |
| 47 | + <span v-if="draft.channel" :title="draft.channel.name" style="margin-left: 0.5em"> |
| 48 | + <i class="ti ti-device-tv"></i> |
| 49 | + </span> |
| 50 | + </div> |
| 51 | + </div> |
| 52 | + <div> |
| 53 | + <p v-if="!!draft.cw" :class="$style.draftNoteCw"> |
| 54 | + <Mfm :text="draft.cw" /> |
| 55 | + </p> |
| 56 | + <MkSubNoteContent :class="$style.draftNoteText" :note="draft" /> |
| 57 | + </div> |
| 58 | + </div> |
| 59 | + <button :class="$style.delete" class="_button" @click="removeDraft(draft.id)"> |
| 60 | + <i class="ti ti-trash"></i> |
| 61 | + </button> |
| 62 | + </div> |
| 63 | + </div> |
| 64 | +</MkModalWindow> |
| 65 | +</template> |
| 66 | + |
| 67 | +<script lang="ts" setup> |
| 68 | +import { onActivated, onMounted, ref, shallowRef } from 'vue'; |
| 69 | +import * as Misskey from 'misskey-js'; |
| 70 | +import { miLocalStorage } from '@/local-storage.js'; |
| 71 | +import { infoImageUrl } from '@/instance.js'; |
| 72 | +import { i18n } from '@/i18n.js'; |
| 73 | +import { $i } from '@/account.js'; |
| 74 | +import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; |
| 75 | +import MkModalWindow from '@/components/MkModalWindow.vue'; |
| 76 | +import type { NoteDraftItem } from '@/types/note-draft-item.js'; |
| 77 | +
|
| 78 | +const emit = defineEmits<{ |
| 79 | + (ev: 'done', v: { canceled: true } | { canceled: false; selected: string | undefined }): void; |
| 80 | + (ev: 'closed'): void; |
| 81 | +}>(); |
| 82 | +
|
| 83 | +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); |
| 84 | +
|
| 85 | +const drafts = ref<(Misskey.entities.Note & { useCw: boolean })[]>([]); |
| 86 | +
|
| 87 | +onMounted(loadDrafts); |
| 88 | +onActivated(loadDrafts); |
| 89 | +
|
| 90 | +function loadDrafts() { |
| 91 | + const stored = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as Record<string, NoteDraftItem>; |
| 92 | + drafts.value = Object.keys(stored).map((key) => ({ |
| 93 | + ...(stored[key].data as Misskey.entities.Note & { useCw: boolean }), |
| 94 | + id: key, |
| 95 | + createdAt: stored[key].updatedAt, |
| 96 | + channel: stored[key].channel as Misskey.entities.Channel, |
| 97 | + renote: stored[key].renote as Misskey.entities.Note, |
| 98 | + reply: stored[key].reply as Misskey.entities.Note, |
| 99 | + user: $i as Misskey.entities.User, |
| 100 | + })); |
| 101 | +} |
| 102 | +
|
| 103 | +function selectDraft(draft: string) { |
| 104 | + done(false, draft); |
| 105 | +} |
| 106 | +
|
| 107 | +function removeDraft(draft: string) { |
| 108 | + const stored = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as Record<string, NoteDraftItem>; |
| 109 | +
|
| 110 | + delete stored[draft]; |
| 111 | + miLocalStorage.setItem('drafts', JSON.stringify(stored)); |
| 112 | +
|
| 113 | + loadDrafts(); |
| 114 | +} |
| 115 | +
|
| 116 | +function done(canceled: boolean, selected?: string): void { |
| 117 | + emit('done', { canceled, selected } as |
| 118 | + | { canceled: true } |
| 119 | + | { canceled: false; selected: string | undefined }); |
| 120 | + dialog.value?.close(); |
| 121 | +} |
| 122 | +</script> |
| 123 | + |
| 124 | +<style lang="scss" module> |
| 125 | +.draftItem { |
| 126 | + display: flex; |
| 127 | + padding: 8px 0 8px 0; |
| 128 | + border-bottom: 1px solid var(--divider); |
| 129 | +
|
| 130 | + &:hover { |
| 131 | + color: var(--accent); |
| 132 | + background-color: var(--accentedBg); |
| 133 | + } |
| 134 | +} |
| 135 | +
|
| 136 | +.draftNote { |
| 137 | + flex: 1; |
| 138 | + width: calc(100% - 16px - 48px - 4px); |
| 139 | + margin: 0 8px 0 8px; |
| 140 | +} |
| 141 | +
|
| 142 | +.draftNoteHeader { |
| 143 | + display: flex; |
| 144 | + flex-wrap: nowrap; |
| 145 | + margin-bottom: 4px; |
| 146 | +} |
| 147 | +
|
| 148 | +.draftNoteDestination { |
| 149 | + flex-shrink: 1; |
| 150 | + flex-grow: 1; |
| 151 | + overflow: hidden; |
| 152 | + text-overflow: ellipsis; |
| 153 | + white-space: nowrap; |
| 154 | + margin-right: 4px; |
| 155 | +} |
| 156 | +
|
| 157 | +.draftNoteInfo { |
| 158 | + flex-shrink: 0; |
| 159 | + margin-left: auto; |
| 160 | +} |
| 161 | +
|
| 162 | +.draftNoteCw { |
| 163 | + cursor: default; |
| 164 | + display: block; |
| 165 | + overflow-wrap: break-word; |
| 166 | +} |
| 167 | +
|
| 168 | +.draftNoteText { |
| 169 | + cursor: default; |
| 170 | +} |
| 171 | +
|
| 172 | +.delete { |
| 173 | + width: 48px; |
| 174 | + height: 64px; |
| 175 | + display: flex; |
| 176 | + align-self: center; |
| 177 | + justify-content: center; |
| 178 | + align-items: center; |
| 179 | + background-color: var(--buttonBg); |
| 180 | + border-radius: 4px; |
| 181 | + margin-right: 4px; |
| 182 | +} |
| 183 | +</style> |
0 commit comments