-import { defineComponent } from "vue";
+
diff --git a/packages/client/src/components/MkContextMenu.vue b/packages/client/src/components/MkContextMenu.vue
index 319f7fd0fe..86ca3b3f3e 100644
--- a/packages/client/src/components/MkContextMenu.vue
+++ b/packages/client/src/components/MkContextMenu.vue
@@ -28,7 +28,7 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
-const rootEl = ref
();
+const rootEl = ref(null);
const zIndex = ref(os.claimZIndex("high"));
@@ -36,8 +36,8 @@ onMounted(() => {
let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
- const width = rootEl.value.offsetWidth;
- const height = rootEl.value.offsetHeight;
+ const width = rootEl.value!.offsetWidth;
+ const height = rootEl.value!.offsetHeight;
if (left + width - window.scrollX > window.innerWidth) {
left = window.innerWidth - width + window.scrollX;
@@ -55,8 +55,8 @@ onMounted(() => {
left = 0;
}
- rootEl.value.style.top = `${top}px`;
- rootEl.value.style.left = `${left}px`;
+ rootEl.value!.style.top = `${top}px`;
+ rootEl.value!.style.left = `${left}px`;
document.body.addEventListener("mousedown", onMousedown);
});
diff --git a/packages/client/src/components/MkCropperDialog.vue b/packages/client/src/components/MkCropperDialog.vue
index 16b42c2f2a..955481bdad 100644
--- a/packages/client/src/components/MkCropperDialog.vue
+++ b/packages/client/src/components/MkCropperDialog.vue
@@ -68,40 +68,48 @@ let cropper: Cropper | null = null;
const loading = ref(true);
const ok = async () => {
- const promise = new Promise(async (res) => {
+ async function UploadCroppedImg(): Promise {
const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas();
- croppedCanvas.toBlob((blob) => {
- const formData = new FormData();
- formData.append("file", blob);
- if (defaultStore.state.uploadFolder) {
- formData.append("folderId", defaultStore.state.uploadFolder);
- }
- fetch(apiUrl + "/drive/files/create", {
- method: "POST",
- body: formData,
- headers: {
- authorization: `Bearer ${me.token}`,
- },
- })
- .then((response) => response.json())
- .then((f) => {
- res(f);
- });
+ const blob = await new Promise((resolve) =>
+ croppedCanvas!.toBlob((blob) => resolve(blob)),
+ );
+
+ // MDN says `null` may be passed if the image cannot be created for any reason.
+ // But I don't think this is reachable for normal case.
+ if (blob == null) {
+ throw "Cropping image failed.";
+ }
+
+ const formData = new FormData();
+ formData.append("file", blob);
+ if (defaultStore.state.uploadFolder) {
+ formData.append("folderId", defaultStore.state.uploadFolder);
+ }
+
+ const response = await fetch(`${apiUrl}/drive/files/create`, {
+ method: "POST",
+ body: formData,
+ headers: {
+ authorization: `Bearer ${me!.token}`,
+ },
});
- });
+ return await response.json();
+ }
+
+ const promise = UploadCroppedImg();
os.promiseDialog(promise);
const f = await promise;
emit("ok", f);
- dialogEl.value.close();
+ dialogEl.value!.close();
};
const cancel = () => {
emit("cancel");
- dialogEl.value.close();
+ dialogEl.value!.close();
};
const onImageLoad = () => {
@@ -114,7 +122,7 @@ const onImageLoad = () => {
};
onMounted(() => {
- cropper = new Cropper(imgEl.value, {});
+ cropper = new Cropper(imgEl.value!, {});
const computedStyle = getComputedStyle(document.documentElement);
@@ -127,13 +135,13 @@ onMounted(() => {
selection.outlined = true;
window.setTimeout(() => {
- cropper.getCropperImage()!.$center("contain");
+ cropper!.getCropperImage()!.$center("contain");
selection.$center();
}, 100);
// モーダルオープンアニメーションが終わったあとで再度調整
window.setTimeout(() => {
- cropper.getCropperImage()!.$center("contain");
+ cropper!.getCropperImage()!.$center("contain");
selection.$center();
}, 500);
});
diff --git a/packages/client/src/components/MkCwButton.vue b/packages/client/src/components/MkCwButton.vue
index e7a2d3d77a..ccdea3ec90 100644
--- a/packages/client/src/components/MkCwButton.vue
+++ b/packages/client/src/components/MkCwButton.vue
@@ -48,7 +48,7 @@ const toggle = () => {
};
function focus() {
- el.value.focus();
+ el.value?.focus();
}
defineExpose({
diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue
index 0b9d2cac36..a345f7dcea 100644
--- a/packages/client/src/components/MkDialog.vue
+++ b/packages/client/src/components/MkDialog.vue
@@ -104,7 +104,7 @@
void;
+ callback: () => void;
}[];
showOkButton?: boolean;
showCancelButton?: boolean;
@@ -268,7 +284,10 @@ const props = withDefaults(
);
const emit = defineEmits<{
- (ev: "done", v: { canceled: boolean; result: any }): void;
+ (
+ ev: "done",
+ v: { canceled: boolean; result?: string | number | boolean | null },
+ ): void;
(ev: "closed"): void;
}>();
@@ -306,7 +325,7 @@ const okButtonDisabled = computed(() => {
const inputEl = ref();
-function done(canceled: boolean, result?) {
+function done(canceled: boolean, result?: string | number | boolean | null) {
emit("done", { canceled, result });
modal.value?.close(null);
}
@@ -342,12 +361,12 @@ function onInputKeydown(evt: KeyboardEvent) {
}
}
-function formatDateToYYYYMMDD(date) {
- const year = date.getFullYear();
- const month = ("0" + (date.getMonth() + 1)).slice(-2);
- const day = ("0" + (date.getDate() + 1)).slice(-2);
- return `${year}-${month}-${day}`;
-}
+// function formatDateToYYYYMMDD(date) {
+// const year = date.getFullYear();
+// const month = ("0" + (date.getMonth() + 1)).slice(-2);
+// const day = ("0" + (date.getDate() + 1)).slice(-2);
+// return `${year}-${month}-${day}`;
+// }
/**
* Appends a new search parameter to the value in the input field.
@@ -355,18 +374,18 @@ function formatDateToYYYYMMDD(date) {
* begin typing a new criteria.
* @param value The value to append.
*/
-function appendFilter(value: string) {
- return (
- [
- typeof inputValue.value === "string"
- ? inputValue.value.trim()
- : inputValue.value,
- value,
- ]
- .join(" ")
- .trim() + " "
- );
-}
+// function appendFilter(value: string) {
+// return (
+// [
+// typeof inputValue.value === "string"
+// ? inputValue.value.trim()
+// : inputValue.value,
+// value,
+// ]
+// .join(" ")
+// .trim() + " "
+// );
+// }
onMounted(() => {
document.addEventListener("keydown", onKeydown);
diff --git a/packages/client/src/components/MkDigitalClock.vue b/packages/client/src/components/MkDigitalClock.vue
index b42ecf19eb..42eabdf749 100644
--- a/packages/client/src/components/MkDigitalClock.vue
+++ b/packages/client/src/components/MkDigitalClock.vue
@@ -26,7 +26,7 @@ const props = withDefaults(
},
);
-let intervalId;
+let intervalId: number;
const hh = ref("");
const mm = ref("");
const ss = ref("");
diff --git a/packages/client/src/components/MkDonation.vue b/packages/client/src/components/MkDonation.vue
index 1c13754c13..ad9df629f4 100644
--- a/packages/client/src/components/MkDonation.vue
+++ b/packages/client/src/components/MkDonation.vue
@@ -29,7 +29,7 @@
{{
i18n.t("_aboutFirefish.donateHost", {
host: hostname,
@@ -73,7 +73,8 @@ const emit = defineEmits<{
(ev: "closed"): void;
}>();
-const hostname = instance.name?.length < 38 ? instance.name : host;
+const hostname =
+ instance.name?.length && instance.name?.length < 38 ? instance.name : host;
const zIndex = os.claimZIndex("low");
@@ -97,7 +98,7 @@ function neverShow() {
close();
}
-function openExternal(link) {
+function openExternal(link: string) {
window.open(link, "_blank");
}
diff --git a/packages/client/src/components/MkDrive.file.vue b/packages/client/src/components/MkDrive.file.vue
index 5e8a50eec2..6d348a33e7 100644
--- a/packages/client/src/components/MkDrive.file.vue
+++ b/packages/client/src/components/MkDrive.file.vue
@@ -47,6 +47,7 @@ import * as os from "@/os";
import { i18n } from "@/i18n";
import { me } from "@/me";
import icon from "@/scripts/icon";
+import type { MenuItem } from "@/types/menu";
const props = withDefaults(
defineProps<{
@@ -72,7 +73,7 @@ const title = computed(
() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`,
);
-function getMenu() {
+function getMenu(): MenuItem[] {
return [
{
text: i18n.ts.rename,
@@ -180,12 +181,15 @@ function describe() {
image: props.file,
},
{
- done: (result) => {
+ done: (result: {
+ canceled: boolean;
+ result?: string | null;
+ }) => {
if (!result || result.canceled) return;
const comment = result.result;
os.api("drive/files/update", {
fileId: props.file.id,
- comment: comment.length === 0 ? null : comment,
+ comment: comment || null,
});
},
},
diff --git a/packages/client/src/components/MkDrive.vue b/packages/client/src/components/MkDrive.vue
index 0273e0b40e..ad2f620f6c 100644
--- a/packages/client/src/components/MkDrive.vue
+++ b/packages/client/src/components/MkDrive.vue
@@ -253,7 +253,7 @@ function onStreamDriveFolderDeleted(folderId: string) {
removeFolder(folderId);
}
-function onDragover(ev: DragEvent): any {
+function onDragover(ev: DragEvent) {
if (!ev.dataTransfer) return;
// ドラッグ元が自分自身の所有するアイテムだったら
@@ -285,7 +285,7 @@ function onDragleave() {
draghover.value = false;
}
-function onDrop(ev: DragEvent): any {
+function onDrop(ev: DragEvent) {
draghover.value = false;
if (!ev.dataTransfer) return;
@@ -493,14 +493,12 @@ function move(target?: entities.DriveFolder) {
if (!target) {
goRoot();
return;
- } else if (typeof target === "object") {
- target = target.id;
}
fetching.value = true;
os.api("drive/folders/show", {
- folderId: target,
+ folderId: target.id,
}).then((folderToMove) => {
folder.value = folderToMove;
hierarchyFolders.value = [];
diff --git a/packages/client/src/components/MkEmojiPicker.section.vue b/packages/client/src/components/MkEmojiPicker.section.vue
index b91195fe65..9fcd5d363d 100644
--- a/packages/client/src/components/MkEmojiPicker.section.vue
+++ b/packages/client/src/components/MkEmojiPicker.section.vue
@@ -14,7 +14,7 @@
class="_button"
@click.stop="
applyUnicodeSkinTone(
- props.skinTones.indexOf(skinTone) + 1,
+ props.skinTones!.indexOf(skinTone) + 1,
)
"
>
diff --git a/packages/client/src/components/MkEmojiPicker.vue b/packages/client/src/components/MkEmojiPicker.vue
index ac2e1a8111..c731fe9b0e 100644
--- a/packages/client/src/components/MkEmojiPicker.vue
+++ b/packages/client/src/components/MkEmojiPicker.vue
@@ -180,6 +180,11 @@ import { i18n } from "@/i18n";
import { defaultStore } from "@/store";
import icon from "@/scripts/icon";
+// FIXME: This variable doesn't seem to be used at all. I don't know why it was here.
+const isActive = ref();
+
+type EmojiDef = string | entities.CustomEmoji | UnicodeEmojiDef;
+
const props = withDefaults(
defineProps<{
showPinned?: boolean;
@@ -193,7 +198,7 @@ const props = withDefaults(
);
const emit = defineEmits<{
- (ev: "chosen", v: string, ev: MouseEvent): void;
+ chosen: [v: string, ev?: MouseEvent];
}>();
const search = ref();
@@ -226,15 +231,9 @@ const unicodeEmojiSkinToneLabels = [
i18n.ts._skinTones?.dark ?? "Dark",
];
-const size = computed(() =>
- props.asReactionPicker ? reactionPickerSize.value : 1,
-);
-const width = computed(() =>
- props.asReactionPicker ? reactionPickerWidth.value : 3,
-);
-const height = computed(() =>
- props.asReactionPicker ? reactionPickerHeight.value : 2,
-);
+const size = reactionPickerSize;
+const width = reactionPickerWidth;
+const height = reactionPickerHeight;
const customEmojiCategories = emojiCategories;
const customEmojis = instance.emojis;
const q = ref(null);
@@ -410,13 +409,17 @@ function reset() {
q.value = "";
}
-function getKey(
- emoji: string | entities.CustomEmoji | UnicodeEmojiDef,
-): string {
- return typeof emoji === "string" ? emoji : emoji.emoji || `:${emoji.name}:`;
+function getKey(emoji: EmojiDef): string {
+ if (typeof emoji === "string") {
+ return emoji;
+ }
+ if ("emoji" in emoji) {
+ return emoji.emoji;
+ }
+ return `:${emoji.name}:`;
}
-function chosen(emoji: any, ev?: MouseEvent) {
+function chosen(emoji: EmojiDef, ev?: MouseEvent) {
const el =
ev && ((ev.currentTarget ?? ev.target) as HTMLElement | null | undefined);
if (el) {
@@ -432,22 +435,33 @@ function chosen(emoji: any, ev?: MouseEvent) {
// 最近使った絵文字更新
if (!pinned.value.includes(key)) {
let recents = defaultStore.state.recentlyUsedEmojis;
- recents = recents.filter((emoji: any) => emoji !== key);
+ recents = recents.filter((emoji) => emoji !== key);
recents.unshift(key);
defaultStore.set("recentlyUsedEmojis", recents.splice(0, 32));
}
}
-function paste(event: ClipboardEvent) {
- const paste = (event.clipboardData || window.clipboardData).getData("text");
- if (done(paste)) {
+async function paste(event: ClipboardEvent) {
+ let pasteStr: string | null = null;
+ if (event.clipboardData) {
+ pasteStr = event.clipboardData.getData("text");
+ } else {
+ // Use native api
+ try {
+ pasteStr = await window.navigator.clipboard.readText();
+ } catch (_err) {
+ // Reading the clipboard requires permission, and the user did not give it
+ }
+ }
+ if (done(pasteStr)) {
event.preventDefault();
}
}
-function done(query?: any): boolean | void {
+function done(query?: string | null): boolean {
+ // biome-ignore lint/style/noParameterAssign: assign it intentially
if (query == null) query = q.value;
- if (query == null || typeof query !== "string") return;
+ if (query == null || typeof query !== "string") return false;
const q2 = query.replaceAll(":", "");
const exactMatchCustom = customEmojis.find((emoji) => emoji.name === q2);
@@ -470,6 +484,7 @@ function done(query?: any): boolean | void {
chosen(searchResultUnicode.value[0]);
return true;
}
+ return false;
}
onMounted(() => {
diff --git a/packages/client/src/components/MkEmojiPickerDialog.vue b/packages/client/src/components/MkEmojiPickerDialog.vue
index 7c3319fb9f..decf49fd38 100644
--- a/packages/client/src/components/MkEmojiPickerDialog.vue
+++ b/packages/client/src/components/MkEmojiPickerDialog.vue
@@ -39,7 +39,7 @@ import { defaultStore } from "@/store";
withDefaults(
defineProps<{
manualShowing?: boolean | null;
- src?: HTMLElement;
+ src?: HTMLElement | null;
showPinned?: boolean;
asReactionPicker?: boolean;
}>(),
@@ -51,7 +51,7 @@ withDefaults(
);
const emit = defineEmits<{
- (ev: "done", v: any): void;
+ (ev: "done", v: string): void;
(ev: "close"): void;
(ev: "closed"): void;
}>();
@@ -64,7 +64,7 @@ function checkForShift(ev?: MouseEvent) {
modal.value?.close(ev);
}
-function chosen(emoji: any, ev: MouseEvent) {
+function chosen(emoji: string, ev?: MouseEvent) {
emit("done", emoji);
checkForShift(ev);
}
diff --git a/packages/client/src/components/MkFolder.vue b/packages/client/src/components/MkFolder.vue
index 8f545406d1..1e69996054 100644
--- a/packages/client/src/components/MkFolder.vue
+++ b/packages/client/src/components/MkFolder.vue
@@ -31,72 +31,76 @@
-
diff --git a/packages/client/src/components/MkFollowButton.vue b/packages/client/src/components/MkFollowButton.vue
index ffe1de72af..0920001c7b 100644
--- a/packages/client/src/components/MkFollowButton.vue
+++ b/packages/client/src/components/MkFollowButton.vue
@@ -8,7 +8,7 @@