Skip to content

Commit

Permalink
feat(frontend/draft): 下書き機能の改良・強化 (#881)
Browse files Browse the repository at this point in the history
  • Loading branch information
u1-liquid authored Jan 12, 2025
1 parent 8a0b98a commit 31d57f2
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 59 deletions.
2 changes: 2 additions & 0 deletions locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,8 @@ consentAll: "Allow All Items"
consentSelected: "Allow Selected Items"
emailAddressLogin: "Login with email address"
usernameLogin: "Login with username"
autoloadDrafts: "Automatically load drafts when opening the posting form"
drafts: "Drafts"

_bubbleGame:
howToPlay: "How to play"
Expand Down
8 changes: 8 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5330,6 +5330,14 @@ export interface Locale extends ILocale {
* ユーザー名でログイン
*/
"usernameLogin": string;
/**
* 投稿フォームを開いたときに下書きを自動で読み込む
*/
"autoloadDrafts": string;
/**
* 下書き
*/
"drafts": string;
"_bubbleGame": {
/**
* 遊び方
Expand Down
2 changes: 2 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1326,6 +1326,8 @@ consentAll: "全て許可"
consentSelected: "選択した項目のみ許可"
emailAddressLogin: "メールアドレスでログイン"
usernameLogin: "ユーザー名でログイン"
autoloadDrafts: "投稿フォームを開いたときに下書きを自動で読み込む"
drafts: "下書き"

_bubbleGame:
howToPlay: "遊び方"
Expand Down
2 changes: 2 additions & 0 deletions locales/ko-KR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,8 @@ consentAll: "모두 허용"
consentSelected: "선택한 항목만 허용"
emailAddressLogin: "이메일 주소로 로그인"
usernameLogin: "사용자명으로 로그인"
autoloadDrafts: "글 작성 시 자동으로 임시 저장된 글 불러오기"
drafts: "임시 저장"

_bubbleGame:
howToPlay: "설명"
Expand Down
183 changes: 183 additions & 0 deletions packages/frontend/src/components/MkDraftsDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<template>
<MkModalWindow
ref="dialog"
:height="500"
:width="800"
@click="done(true)"
@close="done(true)"
@closed="emit('closed')"
>
<template #header>
{{ i18n.ts.drafts }}
</template>
<div style="display: flex; flex-direction: column">
<div v-if="drafts.length === 0" class="empty">
<div class="_fullinfo">
<img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</div>
<div v-for="draft in drafts" :key="draft.id" :class="$style.draftItem">
<div :class="$style.draftNote" @click="selectDraft(draft.id)">
<div :class="$style.draftNoteHeader">
<div :class="$style.draftNoteDestination">
<span v-if="draft.channel" style="opacity: 0.7; padding-right: 0.5em">
<i class="ti ti-device-tv"></i> {{ draft.channel.name }}
</span>
<span v-if="draft.renote">
<i class="ti ti-quote"></i> <MkAcct :user="draft.renote.user" /> <span>{{ draft.renote.text }}</span>
</span>
<span v-else-if="draft.reply">
<i class="ti ti-arrow-back-up"></i> <MkAcct :user="draft.reply.user" /> <span>{{ draft.reply.text }}</span>
</span>
<span v-else>
<i class="ti ti-pencil"></i>
</span>
</div>
<div :class="$style.draftNoteInfo">
<MkTime :time="draft.createdAt" colored />
<span v-if="draft.visibility !== 'public'" :title="i18n.ts._visibility[draft.visibility]" style="margin-left: 0.5em">
<i v-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'" ref="specified" class="ti ti-mail"></i>
</span>
<span v-if="draft.localOnly" :title="i18n.ts._visibility['disableFederation']" style="margin-left: 0.5em">
<i class="ti ti-rocket-off"></i>
</span>
<span v-if="draft.channel" :title="draft.channel.name" style="margin-left: 0.5em">
<i class="ti ti-device-tv"></i>
</span>
</div>
</div>
<div>
<p v-if="!!draft.cw" :class="$style.draftNoteCw">
<Mfm :text="draft.cw" />
</p>
<MkSubNoteContent :class="$style.draftNoteText" :note="draft" />
</div>
</div>
<button :class="$style.delete" class="_button" @click="removeDraft(draft.id)">
<i class="ti ti-trash"></i>
</button>
</div>
</div>
</MkModalWindow>
</template>

<script lang="ts" setup>
import { onActivated, onMounted, ref, shallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import { miLocalStorage } from '@/local-storage.js';
import { infoImageUrl } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { $i } from '@/account.js';
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import type { NoteDraftItem } from '@/types/note-draft-item.js';
const emit = defineEmits<{
(ev: 'done', v: { canceled: true } | { canceled: false; selected: string | undefined }): void;
(ev: 'closed'): void;
}>();
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
const drafts = ref<(Misskey.entities.Note & { useCw: boolean })[]>([]);
onMounted(loadDrafts);
onActivated(loadDrafts);
function loadDrafts() {
const stored = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as Record<string, NoteDraftItem>;
drafts.value = Object.keys(stored).map((key) => ({
...(stored[key].data as Misskey.entities.Note & { useCw: boolean }),
id: key,
createdAt: stored[key].updatedAt,
channel: stored[key].channel as Misskey.entities.Channel,
renote: stored[key].renote as Misskey.entities.Note,
reply: stored[key].reply as Misskey.entities.Note,
user: $i as Misskey.entities.User,
}));
}
function selectDraft(draft: string) {
done(false, draft);
}
function removeDraft(draft: string) {
const stored = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}') as Record<string, NoteDraftItem>;
delete stored[draft];
miLocalStorage.setItem('drafts', JSON.stringify(stored));
loadDrafts();
}
function done(canceled: boolean, selected?: string): void {
emit('done', { canceled, selected } as
| { canceled: true }
| { canceled: false; selected: string | undefined });
dialog.value?.close();
}
</script>

<style lang="scss" module>
.draftItem {
display: flex;
padding: 8px 0 8px 0;
border-bottom: 1px solid var(--divider);
&:hover {
color: var(--accent);
background-color: var(--accentedBg);
}
}
.draftNote {
flex: 1;
width: calc(100% - 16px - 48px - 4px);
margin: 0 8px 0 8px;
}
.draftNoteHeader {
display: flex;
flex-wrap: nowrap;
margin-bottom: 4px;
}
.draftNoteDestination {
flex-shrink: 1;
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 4px;
}
.draftNoteInfo {
flex-shrink: 0;
margin-left: auto;
}
.draftNoteCw {
cursor: default;
display: block;
overflow-wrap: break-word;
}
.draftNoteText {
cursor: default;
}
.delete {
width: 48px;
height: 64px;
display: flex;
align-self: center;
justify-content: center;
align-items: center;
background-color: var(--buttonBg);
border-radius: 4px;
margin-right: 4px;
}
</style>
Loading

0 comments on commit 31d57f2

Please sign in to comment.