From 406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 7 Dec 2023 14:42:09 +0900
Subject: refactor(frontend): 非推奨となったReactivity
 Transformを使わないように (#12539)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): 非推奨となったReactivity Transformを使わないように

* refactor: 不要な括弧を除去

* fix: 不要なアノテーションを除去

* fix: Refの配列をrefしている部分の対応

* refactor: 不要な括弧を除去

* fix: lint

* refactor: Ref、ShallowRef、ComputedRefの変数の宣言をletからconstに置換

* fix: type error

* chore: drop reactivity transform from eslint configuration

* refactor: remove unnecessary import

* fix: 対応漏れ
---
 packages/frontend/src/components/MkPostForm.vue | 368 ++++++++++++------------
 1 file changed, 184 insertions(+), 184 deletions(-)

(limited to 'packages/frontend/src/components/MkPostForm.vue')

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 13b615d104..9e5c4ca3f1 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, ref } from 'vue';
+import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import insertTextAtCursor from 'insert-text-at-cursor';
@@ -162,42 +162,42 @@ const emit = defineEmits<{
 	(ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
 }>();
 
-const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null);
-const cwInputEl = $shallowRef<HTMLInputElement | null>(null);
-const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null);
-const visibilityButton = $shallowRef<HTMLElement | null>(null);
+const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
+const cwInputEl = shallowRef<HTMLInputElement | null>(null);
+const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
+const visibilityButton = shallowRef<HTMLElement | null>(null);
 
-let posting = $ref(false);
-let posted = $ref(false);
-let text = $ref(props.initialText ?? '');
-let files = $ref(props.initialFiles ?? []);
-let poll = $ref<{
+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);
-let useCw = $ref(false);
-let showPreview = $ref(defaultStore.state.showPreview);
-watch($$(showPreview), () => defaultStore.set('showPreview', showPreview));
-let cw = $ref<string | null>(null);
-let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
-let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
-let visibleUsers = $ref([]);
+const useCw = ref(false);
+const showPreview = ref(defaultStore.state.showPreview);
+watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
+const cw = ref<string | null>(null);
+const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
+const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
+const visibleUsers = ref([]);
 if (props.initialVisibleUsers) {
 	props.initialVisibleUsers.forEach(pushVisibleUser);
 }
-let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance);
-let autocomplete = $ref(null);
-let draghover = $ref(false);
-let quoteId = $ref(null);
-let hasNotSpecifiedMentions = $ref(false);
-let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
-let imeText = $ref('');
-let showingOptions = $ref(false);
+const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
+const autocomplete = ref(null);
+const draghover = ref(false);
+const quoteId = ref(null);
+const hasNotSpecifiedMentions = ref(false);
+const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
+const imeText = ref('');
+const showingOptions = ref(false);
 const textAreaReadOnly = ref(false);
 
-const draftKey = $computed((): string => {
+const draftKey = computed((): string => {
 	let key = props.channel ? `channel:${props.channel.id}` : '';
 
 	if (props.renote) {
@@ -211,7 +211,7 @@ const draftKey = $computed((): string => {
 	return key;
 });
 
-const placeholder = $computed((): string => {
+const placeholder = computed((): string => {
 	if (props.renote) {
 		return i18n.ts._postForm.quotePlaceholder;
 	} else if (props.reply) {
@@ -231,7 +231,7 @@ const placeholder = $computed((): string => {
 	}
 });
 
-const submitText = $computed((): string => {
+const submitText = computed((): string => {
 	return props.renote
 		? i18n.ts.quote
 		: props.reply
@@ -239,45 +239,45 @@ const submitText = $computed((): string => {
 			: i18n.ts.note;
 });
 
-const textLength = $computed((): number => {
-	return (text + imeText).trim().length;
+const textLength = computed((): number => {
+	return (text.value + imeText.value).trim().length;
 });
 
-const maxTextLength = $computed((): number => {
+const maxTextLength = computed((): number => {
 	return instance ? instance.maxNoteTextLength : 1000;
 });
 
-const canPost = $computed((): boolean => {
-	return !props.mock && !posting && !posted &&
-		(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
-		(textLength <= maxTextLength) &&
-		(!poll || poll.choices.length >= 2);
+const canPost = computed((): boolean => {
+	return !props.mock && !posting.value && !posted.value &&
+		(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
+		(textLength.value <= maxTextLength.value) &&
+		(!poll.value || poll.value.choices.length >= 2);
 });
 
-const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
-const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags'));
+const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
+const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
 
-watch($$(text), () => {
+watch(text, () => {
 	checkMissingMention();
 }, { immediate: true });
 
-watch($$(visibility), () => {
+watch(visibility, () => {
 	checkMissingMention();
 }, { immediate: true });
 
-watch($$(visibleUsers), () => {
+watch(visibleUsers, () => {
 	checkMissingMention();
 }, {
 	deep: true,
 });
 
 if (props.mention) {
-	text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
-	text += ' ';
+	text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
+	text.value += ' ';
 }
 
 if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
-	text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
+	text.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
 }
 
 if (props.reply && props.reply.text != null) {
@@ -295,32 +295,32 @@ if (props.reply && props.reply.text != null) {
 		if ($i.username === x.username && (x.host == null || x.host === host)) continue;
 
 		// 重複は除外
-		if (text.includes(`${mention} `)) continue;
+		if (text.value.includes(`${mention} `)) continue;
 
-		text += `${mention} `;
+		text.value += `${mention} `;
 	}
 }
 
-if ($i?.isSilenced && visibility === 'public') {
-	visibility = 'home';
+if ($i?.isSilenced && visibility.value === 'public') {
+	visibility.value = 'home';
 }
 
 if (props.channel) {
-	visibility = 'public';
-	localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+	visibility.value = 'public';
+	localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 }
 
 // 公開以外へのリプライ時は元の公開範囲を引き継ぐ
 if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
-	if (props.reply.visibility === 'home' && visibility === 'followers') {
-		visibility = 'followers';
-	} else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') {
-		visibility = 'specified';
+	if (props.reply.visibility === 'home' && visibility.value === 'followers') {
+		visibility.value = 'followers';
+	} else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') {
+		visibility.value = 'specified';
 	} else {
-		visibility = props.reply.visibility;
+		visibility.value = props.reply.visibility;
 	}
 
-	if (visibility === 'specified') {
+	if (visibility.value === 'specified') {
 		if (props.reply.visibleUserIds) {
 			os.api('users/show', {
 				userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
@@ -338,57 +338,57 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
 }
 
 if (props.specified) {
-	visibility = 'specified';
+	visibility.value = 'specified';
 	pushVisibleUser(props.specified);
 }
 
 // keep cw when reply
 if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
-	useCw = true;
-	cw = props.reply.cw;
+	useCw.value = true;
+	cw.value = props.reply.cw;
 }
 
 function watchForDraft() {
-	watch($$(text), () => saveDraft());
-	watch($$(useCw), () => saveDraft());
-	watch($$(cw), () => saveDraft());
-	watch($$(poll), () => saveDraft());
-	watch($$(files), () => saveDraft(), { deep: true });
-	watch($$(visibility), () => saveDraft());
-	watch($$(localOnly), () => saveDraft());
+	watch(text, () => saveDraft());
+	watch(useCw, () => saveDraft());
+	watch(cw, () => saveDraft());
+	watch(poll, () => saveDraft());
+	watch(files, () => saveDraft(), { deep: true });
+	watch(visibility, () => saveDraft());
+	watch(localOnly, () => saveDraft());
 }
 
 function checkMissingMention() {
-	if (visibility === 'specified') {
-		const ast = mfm.parse(text);
+	if (visibility.value === 'specified') {
+		const ast = mfm.parse(text.value);
 
 		for (const x of extractMentions(ast)) {
-			if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
-				hasNotSpecifiedMentions = true;
+			if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
+				hasNotSpecifiedMentions.value = true;
 				return;
 			}
 		}
 	}
-	hasNotSpecifiedMentions = false;
+	hasNotSpecifiedMentions.value = false;
 }
 
 function addMissingMention() {
-	const ast = mfm.parse(text);
+	const ast = mfm.parse(text.value);
 
 	for (const x of extractMentions(ast)) {
-		if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
+		if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
 			os.api('users/show', { username: x.username, host: x.host }).then(user => {
-				visibleUsers.push(user);
+				visibleUsers.value.push(user);
 			});
 		}
 	}
 }
 
 function togglePoll() {
-	if (poll) {
-		poll = null;
+	if (poll.value) {
+		poll.value = null;
 	} else {
-		poll = {
+		poll.value = {
 			choices: ['', ''],
 			multiple: false,
 			expiresAt: null,
@@ -398,13 +398,13 @@ function togglePoll() {
 }
 
 function addTag(tag: string) {
-	insertTextAtCursor(textareaEl, ` #${tag} `);
+	insertTextAtCursor(textareaEl.value, ` #${tag} `);
 }
 
 function focus() {
-	if (textareaEl) {
-		textareaEl.focus();
-		textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length);
+	if (textareaEl.value) {
+		textareaEl.value.focus();
+		textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length);
 	}
 }
 
@@ -413,55 +413,55 @@ function chooseFileFrom(ev) {
 
 	selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
 		for (const file of files_) {
-			files.push(file);
+			files.value.push(file);
 		}
 	});
 }
 
 function detachFile(id) {
-	files = files.filter(x => x.id !== id);
+	files.value = files.value.filter(x => x.id !== id);
 }
 
 function updateFileSensitive(file, sensitive) {
 	if (props.mock) {
 		emit('fileChangeSensitive', file.id, sensitive);
 	}
-	files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive;
+	files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive;
 }
 
 function updateFileName(file, name) {
-	files[files.findIndex(x => x.id === file.id)].name = name;
+	files.value[files.value.findIndex(x => x.id === file.id)].name = name;
 }
 
 function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
-	files[files.findIndex(x => x.id === file.id)] = newFile;
+	files.value[files.value.findIndex(x => x.id === file.id)] = newFile;
 }
 
 function upload(file: File, name?: string): void {
 	if (props.mock) return;
 
 	uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
-		files.push(res);
+		files.value.push(res);
 	});
 }
 
 function setVisibility() {
 	if (props.channel) {
-		visibility = 'public';
-		localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+		visibility.value = 'public';
+		localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 		return;
 	}
 
 	os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
-		currentVisibility: visibility,
+		currentVisibility: visibility.value,
 		isSilenced: $i?.isSilenced,
-		localOnly: localOnly,
-		src: visibilityButton,
+		localOnly: localOnly.value,
+		src: visibilityButton.value,
 	}, {
 		changeVisibility: v => {
-			visibility = v;
+			visibility.value = v;
 			if (defaultStore.state.rememberNoteVisibility) {
-				defaultStore.set('visibility', visibility);
+				defaultStore.set('visibility', visibility.value);
 			}
 		},
 	}, 'closed');
@@ -469,14 +469,14 @@ function setVisibility() {
 
 async function toggleLocalOnly() {
 	if (props.channel) {
-		visibility = 'public';
-		localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+		visibility.value = 'public';
+		localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 		return;
 	}
 
 	const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
 
-	if (!localOnly && neverShowInfo !== 'true') {
+	if (!localOnly.value && neverShowInfo !== 'true') {
 		const confirm = await os.actions({
 			type: 'question',
 			title: i18n.ts.disableFederationConfirm,
@@ -506,7 +506,7 @@ async function toggleLocalOnly() {
 		}
 	}
 
-	localOnly = !localOnly;
+	localOnly.value = !localOnly.value;
 }
 
 async function toggleReactionAcceptance() {
@@ -519,15 +519,15 @@ async function toggleReactionAcceptance() {
 			{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
 			{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
 		],
-		default: reactionAcceptance,
+		default: reactionAcceptance.value,
 	});
 	if (select.canceled) return;
-	reactionAcceptance = select.result;
+	reactionAcceptance.value = select.result;
 }
 
 function pushVisibleUser(user) {
-	if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) {
-		visibleUsers.push(user);
+	if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
+		visibleUsers.value.push(user);
 	}
 }
 
@@ -535,34 +535,34 @@ function addVisibleUser() {
 	os.selectUser().then(user => {
 		pushVisibleUser(user);
 
-		if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
-			text = `@${Misskey.acct.toString(user)} ${text}`;
+		if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
+			text.value = `@${Misskey.acct.toString(user)} ${text.value}`;
 		}
 	});
 }
 
 function removeVisibleUser(user) {
-	visibleUsers = erase(user, visibleUsers);
+	visibleUsers.value = erase(user, visibleUsers.value);
 }
 
 function clear() {
-	text = '';
-	files = [];
-	poll = null;
-	quoteId = null;
+	text.value = '';
+	files.value = [];
+	poll.value = null;
+	quoteId.value = null;
 }
 
 function onKeydown(ev: KeyboardEvent) {
-	if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post();
+	if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
 	if (ev.key === 'Escape') emit('esc');
 }
 
 function onCompositionUpdate(ev: CompositionEvent) {
-	imeText = ev.data;
+	imeText.value = ev.data;
 }
 
 function onCompositionEnd(ev: CompositionEvent) {
-	imeText = '';
+	imeText.value = '';
 }
 
 async function onPaste(ev: ClipboardEvent) {
@@ -580,7 +580,7 @@ async function onPaste(ev: ClipboardEvent) {
 
 	const paste = ev.clipboardData.getData('text');
 
-	if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) {
+	if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) {
 		ev.preventDefault();
 
 		os.confirm({
@@ -588,11 +588,11 @@ async function onPaste(ev: ClipboardEvent) {
 			text: i18n.ts.quoteQuestion,
 		}).then(({ canceled }) => {
 			if (canceled) {
-				insertTextAtCursor(textareaEl, paste);
+				insertTextAtCursor(textareaEl.value, paste);
 				return;
 			}
 
-			quoteId = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
+			quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
 		});
 	}
 }
@@ -603,7 +603,7 @@ function onDragover(ev) {
 	const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
 	if (isFile || isDriveFile) {
 		ev.preventDefault();
-		draghover = true;
+		draghover.value = true;
 		switch (ev.dataTransfer.effectAllowed) {
 			case 'all':
 			case 'uninitialized':
@@ -624,15 +624,15 @@ function onDragover(ev) {
 }
 
 function onDragenter(ev) {
-	draghover = true;
+	draghover.value = true;
 }
 
 function onDragleave(ev) {
-	draghover = false;
+	draghover.value = false;
 }
 
 function onDrop(ev): void {
-	draghover = false;
+	draghover.value = false;
 
 	// ファイルだったら
 	if (ev.dataTransfer.files.length > 0) {
@@ -645,7 +645,7 @@ function onDrop(ev): void {
 	const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
 	if (driveFile != null && driveFile !== '') {
 		const file = JSON.parse(driveFile);
-		files.push(file);
+		files.value.push(file);
 		ev.preventDefault();
 	}
 	//#endregion
@@ -656,16 +656,16 @@ function saveDraft() {
 
 	const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
 
-	draftData[draftKey] = {
+	draftData[draftKey.value] = {
 		updatedAt: new Date(),
 		data: {
-			text: text,
-			useCw: useCw,
-			cw: cw,
-			visibility: visibility,
-			localOnly: localOnly,
-			files: files,
-			poll: poll,
+			text: text.value,
+			useCw: useCw.value,
+			cw: cw.value,
+			visibility: visibility.value,
+			localOnly: localOnly.value,
+			files: files.value,
+			poll: poll.value,
 		},
 	};
 
@@ -675,13 +675,13 @@ function saveDraft() {
 function deleteDraft() {
 	const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
 
-	delete draftData[draftKey];
+	delete draftData[draftKey.value];
 
 	miLocalStorage.setItem('drafts', JSON.stringify(draftData));
 }
 
 async function post(ev?: MouseEvent) {
-	if (useCw && (cw == null || cw.trim() === '')) {
+	if (useCw.value && (cw.value == null || cw.value.trim() === '')) {
 		os.alert({
 			type: 'error',
 			text: i18n.ts.cwNotationRequired,
@@ -700,13 +700,13 @@ async function post(ev?: MouseEvent) {
 	if (props.mock) return;
 
 	const annoying =
-		text.includes('$[x2') ||
-		text.includes('$[x3') ||
-		text.includes('$[x4') ||
-		text.includes('$[scale') ||
-		text.includes('$[position');
+		text.value.includes('$[x2') ||
+		text.value.includes('$[x3') ||
+		text.value.includes('$[x4') ||
+		text.value.includes('$[scale') ||
+		text.value.includes('$[position');
 
-	if (annoying && visibility === 'public') {
+	if (annoying && visibility.value === 'public') {
 		const { canceled, result } = await os.actions({
 			type: 'warning',
 			text: i18n.ts.thisPostMayBeAnnoying,
@@ -726,26 +726,26 @@ async function post(ev?: MouseEvent) {
 		if (canceled) return;
 		if (result === 'cancel') return;
 		if (result === 'home') {
-			visibility = 'home';
+			visibility.value = 'home';
 		}
 	}
 
 	let postData = {
-		text: text === '' ? null : text,
-		fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
+		text: text.value === '' ? null : text.value,
+		fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
 		replyId: props.reply ? props.reply.id : undefined,
-		renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
+		renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
 		channelId: props.channel ? props.channel.id : undefined,
-		poll: poll,
-		cw: useCw ? cw ?? '' : null,
-		localOnly: localOnly,
-		visibility: visibility,
-		visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
-		reactionAcceptance,
+		poll: poll.value,
+		cw: useCw.value ? cw.value ?? '' : null,
+		localOnly: localOnly.value,
+		visibility: visibility.value,
+		visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
+		reactionAcceptance: reactionAcceptance.value,
 	};
 
-	if (withHashtags && hashtags && hashtags.trim() !== '') {
-		const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
+	if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
+		const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
 		postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
 	}
 
@@ -762,15 +762,15 @@ async function post(ev?: MouseEvent) {
 
 	let token = undefined;
 
-	if (postAccount) {
+	if (postAccount.value) {
 		const storedAccounts = await getAccounts();
-		token = storedAccounts.find(x => x.id === postAccount.id)?.token;
+		token = storedAccounts.find(x => x.id === postAccount.value.id)?.token;
 	}
 
-	posting = true;
+	posting.value = true;
 	os.api('notes/create', postData, token).then(() => {
 		if (props.freezeAfterPosted) {
-			posted = true;
+			posted.value = true;
 		} else {
 			clear();
 		}
@@ -782,8 +782,8 @@ async function post(ev?: MouseEvent) {
 				const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
 				miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
 			}
-			posting = false;
-			postAccount = null;
+			posting.value = false;
+			postAccount.value = null;
 
 			incNotesCount();
 			if (notesCount === 1) {
@@ -828,7 +828,7 @@ async function post(ev?: MouseEvent) {
 			}
 		});
 	}).catch(err => {
-		posting = false;
+		posting.value = false;
 		os.alert({
 			type: 'error',
 			text: err.message + '\n' + (err as any).id,
@@ -842,7 +842,7 @@ function cancel() {
 
 function insertMention() {
 	os.selectUser().then(user => {
-		insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' ');
+		insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' ');
 	});
 }
 
@@ -852,7 +852,7 @@ async function insertEmoji(ev: MouseEvent) {
 	emojiPicker.show(
 		ev.currentTarget ?? ev.target,
 		emoji => {
-			insertTextAtCursor(textareaEl, emoji);
+			insertTextAtCursor(textareaEl.value, emoji);
 		},
 		() => {
 			textAreaReadOnly.value = false;
@@ -866,17 +866,17 @@ function showActions(ev) {
 		text: action.title,
 		action: () => {
 			action.handler({
-				text: text,
-				cw: cw,
+				text: text.value,
+				cw: cw.value,
 			}, (key, value) => {
-				if (key === 'text') { text = value; }
-				if (key === 'cw') { useCw = value !== null; cw = value; }
+				if (key === 'text') { text.value = value; }
+				if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
 			});
 		},
 	})), ev.currentTarget ?? ev.target);
 }
 
-let postAccount = $ref<Misskey.entities.UserDetailed | null>(null);
+const postAccount = ref<Misskey.entities.UserDetailed | null>(null);
 
 function openAccountMenu(ev: MouseEvent) {
 	if (props.mock) return;
@@ -884,12 +884,12 @@ function openAccountMenu(ev: MouseEvent) {
 	openAccountMenu_({
 		withExtraOperation: false,
 		includeCurrentAccount: true,
-		active: postAccount != null ? postAccount.id : $i.id,
+		active: postAccount.value != null ? postAccount.value.id : $i.id,
 		onChoose: (account) => {
 			if (account.id === $i.id) {
-				postAccount = null;
+				postAccount.value = null;
 			} else {
-				postAccount = account;
+				postAccount.value = account;
 			}
 		},
 	}, ev);
@@ -905,23 +905,23 @@ onMounted(() => {
 	}
 
 	// TODO: detach when unmount
-	new Autocomplete(textareaEl, $$(text));
-	new Autocomplete(cwInputEl, $$(cw));
-	new Autocomplete(hashtagsInputEl, $$(hashtags));
+	new Autocomplete(textareaEl.value, text);
+	new Autocomplete(cwInputEl.value, cw);
+	new Autocomplete(hashtagsInputEl.value, hashtags);
 
 	nextTick(() => {
 		// 書きかけの投稿を復元
 		if (!props.instant && !props.mention && !props.specified && !props.mock) {
-			const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey];
+			const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value];
 			if (draft) {
-				text = draft.data.text;
-				useCw = draft.data.useCw;
-				cw = draft.data.cw;
-				visibility = draft.data.visibility;
-				localOnly = draft.data.localOnly;
-				files = (draft.data.files || []).filter(draftFile => draftFile);
+				text.value = draft.data.text;
+				useCw.value = draft.data.useCw;
+				cw.value = draft.data.cw;
+				visibility.value = draft.data.visibility;
+				localOnly.value = draft.data.localOnly;
+				files.value = (draft.data.files || []).filter(draftFile => draftFile);
 				if (draft.data.poll) {
-					poll = draft.data.poll;
+					poll.value = draft.data.poll;
 				}
 			}
 		}
@@ -929,21 +929,21 @@ onMounted(() => {
 		// 削除して編集
 		if (props.initialNote) {
 			const init = props.initialNote;
-			text = init.text ? init.text : '';
-			files = init.files;
-			cw = init.cw;
-			useCw = init.cw != null;
+			text.value = init.text ? init.text : '';
+			files.value = init.files;
+			cw.value = init.cw;
+			useCw.value = init.cw != null;
 			if (init.poll) {
-				poll = {
+				poll.value = {
 					choices: init.poll.choices.map(x => x.text),
 					multiple: init.poll.multiple,
 					expiresAt: init.poll.expiresAt,
 					expiredAfter: init.poll.expiredAfter,
 				};
 			}
-			visibility = init.visibility;
-			localOnly = init.localOnly;
-			quoteId = init.renote ? init.renote.id : null;
+			visibility.value = init.visibility;
+			localOnly.value = init.localOnly;
+			quoteId.value = init.renote ? init.renote.id : null;
 		}
 
 		nextTick(() => watchForDraft());
-- 
cgit v1.2.3-freya

