summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkPostForm.vue
diff options
context:
space:
mode:
Diffstat (limited to 'packages/frontend/src/components/MkPostForm.vue')
-rw-r--r--packages/frontend/src/components/MkPostForm.vue83
1 files changed, 43 insertions, 40 deletions
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 1e073a7de9..47904a9acb 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<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.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button>
- <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button>
+ <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button>
<button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button>
<button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button>
</div>
@@ -108,7 +108,7 @@ import { toASCII } from 'punycode/';
import MkNoteSimple from '@/components/MkNoteSimple.vue';
import MkNotePreview from '@/components/MkNotePreview.vue';
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
-import MkPollEditor from '@/components/MkPollEditor.vue';
+import MkPollEditor, { type PollEditorModelValue } from '@/components/MkPollEditor.vue';
import { host, url } from '@/config.js';
import { erase, unique } from '@/scripts/array.js';
import { extractMentions } from '@/scripts/extract-mentions.js';
@@ -139,13 +139,13 @@ const props = withDefaults(defineProps<{
renote?: Misskey.entities.Note;
channel?: Misskey.entities.Channel; // TODO
mention?: Misskey.entities.User;
- specified?: Misskey.entities.User;
+ specified?: Misskey.entities.UserDetailed;
initialText?: string;
initialCw?: string;
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
initialFiles?: Misskey.entities.DriveFile[];
initialLocalOnly?: boolean;
- initialVisibleUsers?: Misskey.entities.User[];
+ initialVisibleUsers?: Misskey.entities.UserDetailed[];
initialNote?: Misskey.entities.Note;
instant?: boolean;
fixed?: boolean;
@@ -178,12 +178,7 @@ const posting = ref(false);
const posted = ref(false);
const text = ref(props.initialText ?? '');
const files = ref(props.initialFiles ?? []);
-const poll = ref<{
- choices: string[];
- multiple: boolean;
- expiresAt: string | null;
- expiredAfter: string | null;
-} | null>(null);
+const poll = ref<PollEditorModelValue | null>(null);
const useCw = ref<boolean>(!!props.initialCw);
const showPreview = ref(defaultStore.state.showPreview);
watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
@@ -332,7 +327,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
if (visibility.value === 'specified') {
if (props.reply.visibleUserIds) {
misskeyApi('users/show', {
- userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
+ userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId),
}).then(users => {
users.forEach(pushVisibleUser);
});
@@ -534,7 +529,7 @@ async function toggleReactionAcceptance() {
reactionAcceptance.value = select.result;
}
-function pushVisibleUser(user) {
+function pushVisibleUser(user: Misskey.entities.UserDetailed) {
if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
visibleUsers.value.push(user);
}
@@ -576,10 +571,12 @@ function onCompositionEnd(ev: CompositionEvent) {
async function onPaste(ev: ClipboardEvent) {
if (props.mock) return;
+ if (!ev.clipboardData) return;
- for (const { item, i } of Array.from(ev.clipboardData.items, (item, i) => ({ item, i }))) {
+ for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) {
if (item.kind === 'file') {
const file = item.getAsFile();
+ if (!file) continue;
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
@@ -601,7 +598,7 @@ async function onPaste(ev: ClipboardEvent) {
return;
}
- quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
+ quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null;
});
}
}
@@ -632,26 +629,26 @@ function onDragover(ev) {
}
}
-function onDragenter(ev) {
+function onDragenter() {
draghover.value = true;
}
-function onDragleave(ev) {
+function onDragleave() {
draghover.value = false;
}
-function onDrop(ev): void {
+function onDrop(ev: DragEvent): void {
draghover.value = false;
// ファイルだったら
- if (ev.dataTransfer.files.length > 0) {
+ if (ev.dataTransfer && ev.dataTransfer.files.length > 0) {
ev.preventDefault();
for (const x of Array.from(ev.dataTransfer.files)) upload(x);
return;
}
//#region ドライブのファイル
- const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
+ const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_);
if (driveFile != null && driveFile !== '') {
const file = JSON.parse(driveFile);
files.value.push(file);
@@ -699,11 +696,14 @@ async function post(ev?: MouseEvent) {
}
if (ev) {
- const el = ev.currentTarget ?? ev.target;
- const rect = el.getBoundingClientRect();
- const x = rect.left + (el.offsetWidth / 2);
- const y = rect.top + (el.offsetHeight / 2);
- os.popup(MkRippleEffect, { x, y }, {}, 'end');
+ const el = (ev.currentTarget ?? ev.target) as HTMLElement | null;
+
+ if (el) {
+ const rect = el.getBoundingClientRect();
+ const x = rect.left + (el.offsetWidth / 2);
+ const y = rect.top + (el.offsetHeight / 2);
+ os.popup(MkRippleEffect, { x, y }, {}, 'end');
+ }
}
if (props.mock) return;
@@ -772,18 +772,18 @@ async function post(ev?: MouseEvent) {
if (notePostInterruptors.length > 0) {
for (const interruptor of notePostInterruptors) {
try {
- postData = await interruptor.handler(deepClone(postData));
+ postData = await interruptor.handler(deepClone(postData)) as typeof postData;
} catch (err) {
console.error(err);
}
}
}
- let token = undefined;
+ let token: string | undefined = undefined;
if (postAccount.value) {
const storedAccounts = await getAccounts();
- token = storedAccounts.find(x => x.id === postAccount.value.id)?.token;
+ token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token;
}
posting.value = true;
@@ -797,7 +797,7 @@ async function post(ev?: MouseEvent) {
deleteDraft();
emit('posted');
if (postData.text && postData.text !== '') {
- const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag);
+ const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[];
const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
}
@@ -867,9 +867,10 @@ function insertMention() {
async function insertEmoji(ev: MouseEvent) {
textAreaReadOnly.value = true;
-
+ const target = ev.currentTarget ?? ev.target;
+ if (target == null) return;
emojiPicker.show(
- ev.currentTarget ?? ev.target,
+ target as HTMLElement,
emoji => {
insertTextAtCursor(textareaEl.value, emoji);
},
@@ -881,6 +882,7 @@ async function insertEmoji(ev: MouseEvent) {
}
async function insertMfmFunction(ev: MouseEvent) {
+ if (textareaEl.value == null) return;
mfmFunctionPicker(
ev.currentTarget ?? ev.target,
textareaEl.value,
@@ -888,14 +890,15 @@ async function insertMfmFunction(ev: MouseEvent) {
);
}
-function showActions(ev) {
+function showActions(ev: MouseEvent) {
os.popupMenu(postFormActions.map(action => ({
text: action.title,
action: () => {
action.handler({
text: text.value,
cw: cw.value,
- }, (key, value) => {
+ }, (key, value: any) => {
+ if (typeof key !== 'string') return;
if (key === 'text') { text.value = value; }
if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
});
@@ -932,9 +935,9 @@ onMounted(() => {
}
// TODO: detach when unmount
- new Autocomplete(textareaEl.value, text);
- new Autocomplete(cwInputEl.value, cw);
- new Autocomplete(hashtagsInputEl.value, hashtags);
+ if (textareaEl.value) new Autocomplete(textareaEl.value, text);
+ if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw);
+ if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags);
nextTick(() => {
// 書きかけの投稿を復元
@@ -957,19 +960,19 @@ onMounted(() => {
if (props.initialNote) {
const init = props.initialNote;
text.value = init.text ? init.text : '';
- files.value = init.files;
- cw.value = init.cw;
+ files.value = init.files ?? [];
+ cw.value = init.cw ?? null;
useCw.value = init.cw != null;
if (init.poll) {
poll.value = {
choices: init.poll.choices.map(x => x.text),
multiple: init.poll.multiple,
- expiresAt: init.poll.expiresAt,
- expiredAfter: init.poll.expiredAfter,
+ expiresAt: init.poll.expiresAt ? (new Date(init.poll.expiresAt)).getTime() : null,
+ expiredAfter: null,
};
}
visibility.value = init.visibility;
- localOnly.value = init.localOnly;
+ localOnly.value = init.localOnly ?? false;
quoteId.value = init.renote ? init.renote.id : null;
}