fix types

This commit is contained in:
Lhcfl 2024-04-13 23:08:58 +08:00
parent 3e43819ba1
commit 16880b1231
16 changed files with 135 additions and 102 deletions

View File

@ -1,3 +1,4 @@
// biome-ignore lint/suspicious/noExplicitAny:
type FIXME = any; type FIXME = any;
declare const _LANGS_: string[][]; declare const _LANGS_: string[][];

View File

@ -1,7 +1,7 @@
<template> <template>
<article <article
v-if="!muted.muted || muted.what === 'reply'" v-if="!muted.muted || muted.what === 'reply'"
:id="detailedView ? appearNote.id : null" :id="detailedView ? appearNote.id : undefined"
ref="el" ref="el"
v-size="{ max: [450, 500] }" v-size="{ max: [450, 500] }"
class="wrpstxzv" class="wrpstxzv"
@ -35,10 +35,10 @@
:parent-id="parentId" :parent-id="parentId"
:conversation="conversation" :conversation="conversation"
:detailed-view="detailedView" :detailed-view="detailedView"
@focusfooter="footerEl.focus()" @focusfooter="footerEl!.focus()"
/> />
<div v-if="translating || translation" class="translation"> <div v-if="translating || translation" class="translation">
<MkLoading v-if="translating" mini /> <MkLoading v-if="translating || translation == null" mini />
<div v-else class="translated"> <div v-else class="translated">
<b <b
>{{ >{{
@ -217,6 +217,7 @@ import { useNoteCapture } from "@/scripts/use-note-capture";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import { deepClone } from "@/scripts/clone"; import { deepClone } from "@/scripts/clone";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import type { NoteTranslation } from "@/types/note";
const router = useRouter(); const router = useRouter();
@ -256,12 +257,12 @@ const isRenote =
note.value.fileIds.length === 0 && note.value.fileIds.length === 0 &&
note.value.poll == null; note.value.poll == null;
const el = ref<HTMLElement>(); const el = ref<HTMLElement | null>(null);
const footerEl = ref<HTMLElement>(); const footerEl = ref<HTMLElement | null>(null);
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>(); const starButton = ref<InstanceType<typeof XStarButton> | null>(null);
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton> | null>(null);
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement | null>(null);
const appearNote = computed(() => const appearNote = computed(() =>
isRenote ? (note.value.renote as entities.Note) : note.value, isRenote ? (note.value.renote as entities.Note) : note.value,
); );
@ -274,7 +275,7 @@ const muted = ref(
defaultStore.state.mutedLangs, defaultStore.state.mutedLangs,
), ),
); );
const translation = ref(null); const translation = ref<NoteTranslation | null>(null);
const translating = ref(false); const translating = ref(false);
const replies: entities.Note[] = const replies: entities.Note[] =
props.conversation props.conversation
@ -330,21 +331,21 @@ useNoteCapture({
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(_viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: appearNote.value, reply: appearNote.value,
animation: !viaKeyboard, // animation: !viaKeyboard,
}).then(() => { }).then(() => {
focus(); focus();
}); });
} }
function react(viaKeyboard = false): void { function react(_viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
blur(); blur();
reactionPicker.show( reactionPicker.show(
reactButton.value, reactButton.value!,
(reaction) => { (reaction) => {
os.api("notes/reactions/create", { os.api("notes/reactions/create", {
noteId: appearNote.value.id, noteId: appearNote.value.id,
@ -388,14 +389,15 @@ function menu(viaKeyboard = false): void {
} }
function onContextmenu(ev: MouseEvent): void { function onContextmenu(ev: MouseEvent): void {
const isLink = (el: HTMLElement) => { const isLink = (el: HTMLElement | null) => {
if (el == null) return;
if (el.tagName === "A") return true; if (el.tagName === "A") return true;
if (el.parentElement) { if (el.parentElement) {
return isLink(el.parentElement); return isLink(el.parentElement);
} }
}; };
if (isLink(ev.target)) return; if (isLink(ev.target as HTMLElement | null)) return;
if (window.getSelection().toString() !== "") return; if (window.getSelection()?.toString() !== "") return;
if (defaultStore.state.useReactionPickerForContextMenu) { if (defaultStore.state.useReactionPickerForContextMenu) {
ev.preventDefault(); ev.preventDefault();
@ -454,15 +456,15 @@ function onContextmenu(ev: MouseEvent): void {
} }
function focus() { function focus() {
el.value.focus(); el.value!.focus();
} }
function blur() { function blur() {
el.value.blur(); el.value!.blur();
} }
function noteClick(e) { function noteClick(e: MouseEvent) {
if (document.getSelection().type === "Range" || !expandOnNoteClick) { if (document.getSelection()?.type === "Range" || !expandOnNoteClick) {
e.stopPropagation(); e.stopPropagation();
} else { } else {
router.push(notePage(props.note)); router.push(notePage(props.note));

View File

@ -84,14 +84,10 @@ import { formatDateTimeString } from "@/scripts/format-time-string";
import { addTime } from "@/scripts/time"; import { addTime } from "@/scripts/time";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import type { PollType } from "@/types/post-form";
const props = defineProps<{ const props = defineProps<{
modelValue: { modelValue: PollType;
expiresAt: string;
expiredAfter: number;
choices: string[];
multiple: boolean;
};
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
"update:modelValue": [ "update:modelValue": [

View File

@ -20,7 +20,7 @@
class="account _button" class="account _button"
@click="openAccountMenu" @click="openAccountMenu"
> >
<MkAvatar :user="postAccount ?? me" class="avatar" /> <MkAvatar :user="postAccount ?? me!" class="avatar" />
</button> </button>
<div class="right"> <div class="right">
<span <span
@ -297,14 +297,22 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, nextTick, onMounted, ref, watch } from "vue"; import {
type Ref,
computed,
inject,
nextTick,
onMounted,
ref,
watch,
} from "vue";
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import autosize from "autosize"; import autosize from "autosize";
import insertTextAtCursor from "insert-text-at-cursor"; import insertTextAtCursor from "insert-text-at-cursor";
import { length } from "stringz"; import { length } from "stringz";
import { toASCII } from "punycode/"; import { toASCII } from "punycode/";
import { acct } from "firefish-js"; import { acct } from "firefish-js";
import type { entities, languages } from "firefish-js"; import type { ApiTypes, entities, languages } from "firefish-js";
import { throttle } from "throttle-debounce"; import { throttle } from "throttle-debounce";
import XNoteSimple from "@/components/MkNoteSimple.vue"; import XNoteSimple from "@/components/MkNoteSimple.vue";
import XNotePreview from "@/components/MkNotePreview.vue"; import XNotePreview from "@/components/MkNotePreview.vue";
@ -341,6 +349,7 @@ import type { MenuItem } from "@/types/menu";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import MkVisibilityPicker from "@/components/MkVisibilityPicker.vue"; import MkVisibilityPicker from "@/components/MkVisibilityPicker.vue";
import type { NoteVisibility } from "@/types/note"; import type { NoteVisibility } from "@/types/note";
import type { NoteDraft, PollType } from "@/types/post-form";
const modal = inject("modal"); const modal = inject("modal");
@ -353,11 +362,11 @@ const props = withDefaults(
specified?: entities.User; specified?: entities.User;
initialText?: string; initialText?: string;
initialVisibility?: NoteVisibility; initialVisibility?: NoteVisibility;
initialLanguage?: typeof languages; initialLanguage?: (typeof languages)[number];
initialFiles?: entities.DriveFile[]; initialFiles?: entities.DriveFile[];
initialLocalOnly?: boolean; initialLocalOnly?: boolean;
initialVisibleUsers?: entities.User[]; initialVisibleUsers?: entities.User[];
initialNote?: entities.Note; initialNote?: NoteDraft;
instant?: boolean; instant?: boolean;
fixed?: boolean; fixed?: boolean;
autofocus?: boolean; autofocus?: boolean;
@ -390,12 +399,7 @@ const showBigPostButton = defaultStore.state.showBigPostButton;
const posting = ref(false); const posting = ref(false);
const text = ref(props.initialText ?? ""); const text = ref(props.initialText ?? "");
const files = ref(props.initialFiles ?? ([] as entities.DriveFile[])); const files = ref(props.initialFiles ?? ([] as entities.DriveFile[]));
const poll = ref<{ const poll = ref<PollType | null>(null);
choices: string[];
multiple: boolean;
expiresAt: string | null;
expiredAfter: string | null;
} | null>(null);
const useCw = ref(false); const useCw = ref(false);
const showPreview = ref(defaultStore.state.showPreviewByDefault); const showPreview = ref(defaultStore.state.showPreviewByDefault);
const cw = ref<string | null>(null); const cw = ref<string | null>(null);
@ -411,12 +415,12 @@ const visibility = ref(
: defaultStore.state.defaultNoteVisibility), : defaultStore.state.defaultNoteVisibility),
); );
const visibleUsers = ref([]); const visibleUsers = ref<entities.User[]>([]);
if (props.initialVisibleUsers) { if (props.initialVisibleUsers) {
props.initialVisibleUsers.forEach(pushVisibleUser); props.initialVisibleUsers.forEach(pushVisibleUser);
} }
const draghover = ref(false); const draghover = ref(false);
const quoteId = ref(null); const quoteId = ref<string | null>(null);
const hasNotSpecifiedMentions = ref(false); const hasNotSpecifiedMentions = ref(false);
const recentHashtags = ref( const recentHashtags = ref(
JSON.parse(localStorage.getItem("hashtags") || "[]"), JSON.parse(localStorage.getItem("hashtags") || "[]"),
@ -500,7 +504,9 @@ const canPost = computed((): boolean => {
const withHashtags = computed( const withHashtags = computed(
defaultStore.makeGetterSetter("postFormWithHashtags"), defaultStore.makeGetterSetter("postFormWithHashtags"),
); );
const hashtags = computed(defaultStore.makeGetterSetter("postFormHashtags")); const hashtags = computed(
defaultStore.makeGetterSetter("postFormHashtags"),
) as Ref<string | null>;
watch(text, () => { watch(text, () => {
checkMissingMention(); checkMissingMention();
@ -525,7 +531,7 @@ if (props.mention) {
if ( if (
props.reply && props.reply &&
(props.reply.user.username !== me.username || (props.reply.user.username !== me!.username ||
(props.reply.user.host != null && props.reply.user.host !== host)) (props.reply.user.host != null && props.reply.user.host !== host))
) { ) {
text.value = `@${props.reply.user.username}${ text.value = `@${props.reply.user.username}${
@ -545,7 +551,7 @@ if (props.reply && props.reply.text != null) {
: `@${x.username}@${toASCII(otherHost)}`; : `@${x.username}@${toASCII(otherHost)}`;
// exclude me // exclude me
if (me.username === x.username && (x.host == null || x.host === host)) if (me!.username === x.username && (x.host == null || x.host === host))
continue; continue;
// remove duplicates // remove duplicates
@ -579,7 +585,7 @@ if (
if (props.reply.visibleUserIds) { if (props.reply.visibleUserIds) {
os.api("users/show", { os.api("users/show", {
userIds: props.reply.visibleUserIds.filter( userIds: props.reply.visibleUserIds.filter(
(uid) => uid !== me.id && uid !== props.reply.userId, (uid) => uid !== me!.id && uid !== props.reply!.userId,
), ),
}).then((users) => { }).then((users) => {
users.forEach(pushVisibleUser); users.forEach(pushVisibleUser);
@ -588,7 +594,7 @@ if (
visibility.value = "private"; visibility.value = "private";
} }
if (props.reply.userId !== me.id) { if (props.reply.userId !== me!.id) {
os.api("users/show", { userId: props.reply.userId }).then((user) => { os.api("users/show", { userId: props.reply.userId }).then((user) => {
pushVisibleUser(user); pushVisibleUser(user);
}); });
@ -615,7 +621,7 @@ const addRe = (s: string) => {
if (defaultStore.state.keepCw && props.reply && props.reply.cw) { if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
useCw.value = true; useCw.value = true;
cw.value = cw.value =
props.reply.user.username === me.username props.reply.user.username === me!.username
? props.reply.cw ? props.reply.cw
: addRe(props.reply.cw); : addRe(props.reply.cw);
} }
@ -894,11 +900,14 @@ function onCompositionEnd(ev: CompositionEvent) {
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
if (ev.clipboardData == null) return;
for (const { item, i } of Array.from(ev.clipboardData.items).map( for (const { item, i } of Array.from(ev.clipboardData.items).map(
(item, i) => ({ item, i }), (item, i) => ({ item, i }),
)) { )) {
if (item.kind === "file") { if (item.kind === "file") {
const file = item.getAsFile(); const file = item.getAsFile();
if (file == null) continue;
const lio = file.name.lastIndexOf("."); const lio = file.name.lastIndexOf(".");
const ext = lio >= 0 ? file.name.slice(lio) : ""; const ext = lio >= 0 ? file.name.slice(lio) : "";
const formatted = `${formatTimeString( const formatted = `${formatTimeString(
@ -911,7 +920,7 @@ async function onPaste(ev: ClipboardEvent) {
const paste = ev.clipboardData?.getData("text") ?? ""; const paste = ev.clipboardData?.getData("text") ?? "";
if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) { if (!props.renote && !quoteId.value && paste.startsWith(`${url}/notes/`)) {
ev.preventDefault(); ev.preventDefault();
os.yesno({ os.yesno({
@ -919,13 +928,13 @@ async function onPaste(ev: ClipboardEvent) {
text: i18n.ts.quoteQuestion, text: i18n.ts.quoteQuestion,
}).then(({ canceled }) => { }).then(({ canceled }) => {
if (canceled) { if (canceled) {
insertTextAtCursor(textareaEl.value, paste); insertTextAtCursor(textareaEl.value!, paste);
return; return;
} }
quoteId.value = paste quoteId.value = paste
.substring(url.length) .substring(url.length)
.match(/^\/notes\/(.+?)\/?$/)[1]; .match(/^\/notes\/(.+?)\/?$/)![1];
}); });
} }
} }
@ -956,16 +965,17 @@ function onDragover(ev) {
} }
} }
function onDragenter(ev) { function onDragenter(_ev) {
draghover.value = true; draghover.value = true;
} }
function onDragleave(ev) { function onDragleave(_ev) {
draghover.value = false; draghover.value = false;
} }
function onDrop(ev): void { function onDrop(ev: DragEvent): void {
draghover.value = false; draghover.value = false;
if (ev.dataTransfer == null) return;
// //
if (ev.dataTransfer.files.length > 0) { if (ev.dataTransfer.files.length > 0) {
@ -1064,7 +1074,7 @@ async function post() {
const processedText = preprocess(text.value); const processedText = preprocess(text.value);
let postData = { let postData: ApiTypes.NoteSubmitReq = {
editId: props.editId ? props.editId : undefined, editId: props.editId ? props.editId : undefined,
text: processedText === "" ? undefined : processedText, text: processedText === "" ? undefined : processedText,
fileIds: files.value.length > 0 ? files.value.map((f) => f.id) : undefined, fileIds: files.value.length > 0 ? files.value.map((f) => f.id) : undefined,
@ -1092,7 +1102,7 @@ async function post() {
const hashtags_ = hashtags.value const hashtags_ = hashtags.value
.trim() .trim()
.split(" ") .split(" ")
.map((x) => (x.startsWith("#") ? x : "#" + x)) .map((x) => (x.startsWith("#") ? x : `#${x}`))
.join(" "); .join(" ");
postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
} }
@ -1104,11 +1114,11 @@ async function post() {
} }
} }
let token; let token: string | undefined;
if (postAccount.value) { if (postAccount.value) {
const storedAccounts = await getAccounts(); 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; posting.value = true;
@ -1119,10 +1129,11 @@ async function post() {
deleteDraft(); deleteDraft();
emit("posted"); emit("posted");
if (postData.text && postData.text !== "") { if (postData.text && postData.text !== "") {
const hashtags_ = mfm const hashtags_ = (
.parse(postData.text) mfm
.filter((x) => x.type === "hashtag") .parse(postData.text)
.map((x) => x.props.hashtag); .filter((x) => x.type === "hashtag") as mfm.MfmHashtag[]
).map((x) => x.props.hashtag);
const history = JSON.parse( const history = JSON.parse(
localStorage.getItem("hashtags") || "[]", localStorage.getItem("hashtags") || "[]",
) as string[]; ) as string[];
@ -1133,7 +1144,7 @@ async function post() {
} }
posting.value = false; posting.value = false;
postAccount.value = null; postAccount.value = null;
nextTick(() => autosize.update(textareaEl.value)); nextTick(() => autosize.update(textareaEl.value!));
}); });
}) })
.catch((err: { message: string; id: string }) => { .catch((err: { message: string; id: string }) => {
@ -1169,19 +1180,23 @@ function cancel() {
function insertMention() { function insertMention() {
os.selectUser().then((user) => { os.selectUser().then((user) => {
insertTextAtCursor(textareaEl.value, "@" + acct.toString(user) + " "); insertTextAtCursor(textareaEl.value!, `@${acct.toString(user)} `);
}); });
} }
async function insertEmoji(ev: MouseEvent) { async function insertEmoji(ev: MouseEvent) {
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl.value); os.openEmojiPicker(
(ev.currentTarget ?? ev.target) as HTMLElement,
{},
textareaEl.value,
);
} }
async function openCheatSheet(ev: MouseEvent) { async function openCheatSheet(ev: MouseEvent) {
os.popup(XCheatSheet, {}, {}, "closed"); os.popup(XCheatSheet, {}, {}, "closed");
} }
function showActions(ev) { function showActions(ev: MouseEvent) {
os.popupMenu( os.popupMenu(
postFormActions.map((action) => ({ postFormActions.map((action) => ({
text: action.title, text: action.title,
@ -1198,7 +1213,7 @@ function showActions(ev) {
); );
}, },
})), })),
ev.currentTarget ?? ev.target, (ev.currentTarget ?? ev.target) as HTMLElement,
); );
} }
@ -1209,9 +1224,9 @@ function openAccountMenu(ev: MouseEvent) {
{ {
withExtraOperation: false, withExtraOperation: false,
includeCurrentAccount: true, includeCurrentAccount: true,
active: postAccount.value != null ? postAccount.value.id : me.id, active: postAccount.value != null ? postAccount.value.id : me!.id,
onChoose: (account) => { onChoose: (account) => {
if (account.id === me.id) { if (account.id === me!.id) {
postAccount.value = null; postAccount.value = null;
} else { } else {
postAccount.value = account; postAccount.value = account;
@ -1232,14 +1247,14 @@ onMounted(() => {
} }
// TODO: detach when unmount // TODO: detach when unmount
new Autocomplete(textareaEl.value, text); new Autocomplete(textareaEl.value!, text);
new Autocomplete(cwInputEl.value, cw); new Autocomplete(cwInputEl.value!, cw as Ref<string>);
new Autocomplete(hashtagsInputEl.value, hashtags); new Autocomplete(hashtagsInputEl.value!, hashtags as Ref<string>);
autosize(textareaEl.value); autosize(textareaEl.value!);
nextTick(() => { nextTick(() => {
autosize(textareaEl.value); autosize(textareaEl.value!);
// 稿 // 稿
if (!props.instant && !props.mention && !props.specified) { if (!props.instant && !props.mention && !props.specified) {
const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[ const draft = JSON.parse(localStorage.getItem("drafts") || "{}")[
@ -1275,8 +1290,8 @@ onMounted(() => {
}; };
} }
visibility.value = init.visibility; visibility.value = init.visibility;
localOnly.value = init.localOnly; localOnly.value = init.localOnly ?? false;
language.value = init.lang; language.value = init.lang ?? null;
quoteId.value = init.renote ? init.renote.id : null; quoteId.value = init.renote ? init.renote.id : null;
} }
@ -1289,7 +1304,7 @@ onMounted(() => {
} }
nextTick(() => watchForDraft()); nextTick(() => watchForDraft());
nextTick(() => autosize.update(textareaEl.value)); nextTick(() => autosize.update(textareaEl.value!));
}); });
}); });
</script> </script>

View File

@ -25,6 +25,7 @@ import type { entities, languages } from "firefish-js";
import MkModal from "@/components/MkModal.vue"; import MkModal from "@/components/MkModal.vue";
import MkPostForm from "@/components/MkPostForm.vue"; import MkPostForm from "@/components/MkPostForm.vue";
import type { NoteVisibility } from "@/types/note"; import type { NoteVisibility } from "@/types/note";
import type { NoteDraft } from "@/types/post-form";
const props = defineProps<{ const props = defineProps<{
reply?: entities.Note; reply?: entities.Note;
@ -34,11 +35,11 @@ const props = defineProps<{
specified?: entities.User; specified?: entities.User;
initialText?: string; initialText?: string;
initialVisibility?: NoteVisibility; initialVisibility?: NoteVisibility;
initialLanguage?: typeof languages; initialLanguage?: (typeof languages)[number];
initialFiles?: entities.DriveFile[]; initialFiles?: entities.DriveFile[];
initialLocalOnly?: boolean; initialLocalOnly?: boolean;
initialVisibleUsers?: entities.User[]; initialVisibleUsers?: entities.User[];
initialNote?: entities.Note; initialNote?: NoteDraft;
instant?: boolean; instant?: boolean;
fixed?: boolean; fixed?: boolean;
autofocus?: boolean; autofocus?: boolean;
@ -53,7 +54,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
const form = shallowRef<InstanceType<typeof MkPostForm>>(); const form = shallowRef<InstanceType<typeof MkPostForm>>();
function onPosted() { function onPosted() {
modal.value.close({ modal.value!.close({
useSendAnimation: true, useSendAnimation: true,
}); });
} }

View File

@ -319,7 +319,7 @@ const usernameState = ref<
| "invalid-format" | "invalid-format"
| "min-range" | "min-range"
| "max-range" | "max-range"
>(null); >(null);
const invitationState = ref<null | "entered">(null); const invitationState = ref<null | "entered">(null);
const emailState = ref< const emailState = ref<
| null | null
@ -331,9 +331,10 @@ const emailState = ref<
| "unavailable:mx" | "unavailable:mx"
| "unavailable:smtp" | "unavailable:smtp"
| "unavailable" | "unavailable"
| "error">(null); | "error"
>(null);
const passwordStrength = ref<"" | "low" | "medium" | "high">(""); const passwordStrength = ref<"" | "low" | "medium" | "high">("");
const passwordRetypeState = ref<null | "match" | "not-match" >(null); const passwordRetypeState = ref<null | "match" | "not-match">(null);
const submitting = ref(false); const submitting = ref(false);
const ToSAgreement = ref(false); const ToSAgreement = ref(false);
const hCaptchaResponse = ref(null); const hCaptchaResponse = ref(null);

View File

@ -76,6 +76,7 @@ onMounted(() => {
src: "/client-assets/tagcanvas.min.js", src: "/client-assets/tagcanvas.min.js",
}), }),
) )
// biome-ignore lint/suspicious/noAssignInExpressions: assign it intentially
.addEventListener("load", () => (available.value = true)); .addEventListener("load", () => (available.value = true));
} }
}); });

View File

@ -153,7 +153,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
currentVisibility: NoteVisibility; currentVisibility: NoteVisibility;
currentLocalOnly: boolean; currentLocalOnly: boolean;
src?: HTMLElement; src?: HTMLElement | null;
}>(), }>(),
{}, {},
); );

View File

@ -48,7 +48,7 @@ const id = os.getUniqueId();
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
modelValue: number; modelValue: number | null;
disabled?: boolean; disabled?: boolean;
min: number; min: number;
max: number; max: number;

View File

@ -75,29 +75,29 @@ const headerActions = computed(() =>
icon: `${icon("ph-pencil")}`, icon: `${icon("ph-pencil")}`,
text: i18n.ts.toEdit, text: i18n.ts.toEdit,
handler: async (): Promise<void> => { handler: async (): Promise<void> => {
const { canceled, result } = await os.form(clip.value.name, { const { canceled, result } = await os.form(clip.value!.name, {
name: { name: {
type: "string", type: "string",
label: i18n.ts.name, label: i18n.ts.name,
default: clip.value.name, default: clip.value!.name,
}, },
description: { description: {
type: "string", type: "string",
required: false, required: false,
multiline: true, multiline: true,
label: i18n.ts.description, label: i18n.ts.description,
default: clip.value.description, default: clip.value!.description,
}, },
isPublic: { isPublic: {
type: "boolean", type: "boolean",
label: i18n.ts.public, label: i18n.ts.public,
default: clip.value.isPublic, default: clip.value!.isPublic,
}, },
}); });
if (canceled) return; if (canceled) return;
os.apiWithDialog("clips/update", { os.apiWithDialog("clips/update", {
clipId: clip.value.id, clipId: clip.value!.id,
...result, ...result,
}); });
}, },

View File

@ -2,14 +2,15 @@ import { markRaw, ref } from "vue";
import { isSignedIn } from "./me"; import { isSignedIn } from "./me";
import { Storage } from "./pizzax"; import { Storage } from "./pizzax";
import type { NoteVisibility } from "@/types/note"; import type { NoteVisibility } from "@/types/note";
import type { entities, ApiTypes } from "firefish-js";
export const postFormActions: { export const postFormActions: {
title: string; title: string;
handler: (note: entities.Note) => void | Promise<void>; handler: (from, update) => void | Promise<void>;
}[] = []; }[] = [];
export const userActions: { export const userActions: {
title: string; title: string;
handler: (note: entities.Note) => void | Promise<void>; handler: (user: entities.User) => void | Promise<void>;
}[] = []; }[] = [];
export const noteActions: { export const noteActions: {
title: string; title: string;
@ -19,7 +20,7 @@ export const noteViewInterruptors: {
handler: (note: entities.Note) => Promise<entities.Note>; handler: (note: entities.Note) => Promise<entities.Note>;
}[] = []; }[] = [];
export const notePostInterruptors: { export const notePostInterruptors: {
handler: (note: entities.Note) => Promise<entities.Note>; handler: (note: ApiTypes.NoteSubmitReq) => Promise<ApiTypes.NoteSubmitReq>;
}[] = []; }[] = [];
const menuOptions = [ const menuOptions = [
@ -466,7 +467,6 @@ import darkTheme from "@/themes/d-rosepine.json5";
* Storage for configuration information that does not need to be constantly loaded into memory (non-reactive) * Storage for configuration information that does not need to be constantly loaded into memory (non-reactive)
*/ */
import lightTheme from "@/themes/l-rosepinedawn.json5"; import lightTheme from "@/themes/l-rosepinedawn.json5";
import { entities } from "firefish-js";
export class ColdDeviceStorage { export class ColdDeviceStorage {
public static default = { public static default = {

View File

@ -0,0 +1,12 @@
import type { entities } from "firefish-js";
export type PollType = {
choices: string[];
multiple: boolean;
expiresAt: string | null;
expiredAfter: number | null;
};
export type NoteDraft = entities.Note & {
poll?: PollType;
};

View File

@ -30,5 +30,5 @@
"jsx": "preserve" "jsx": "preserve"
}, },
"compileOnSave": false, "compileOnSave": false,
"include": ["./src/**/*.ts", "./src/**/*.vue"] "include": ["./src/**/*.ts", "./src/**/*.vue", "./@types"]
} }

View File

@ -36,7 +36,6 @@ import type {
UserDetailed, UserDetailed,
UserGroup, UserGroup,
UserList, UserList,
UserLite,
UserSorting, UserSorting,
} from "./entities"; } from "./entities";
@ -46,11 +45,13 @@ type TODO = Record<string, any> | null;
type NoParams = Record<string, never>; type NoParams = Record<string, never>;
type ShowUserReq = { username: string; host?: string } | { userId: User["id"] }; type ShowUserReq =
| { username: string; host?: string | null }
| { userId: User["id"] };
type NoteSubmitReq = { export type NoteSubmitReq = {
editId?: null | Note["id"]; editId?: null | Note["id"];
visibility?: "public" | "home" | "followers" | "specified"; visibility?: (typeof consts.noteVisibilities)[number];
visibleUserIds?: User["id"][]; visibleUserIds?: User["id"][];
text?: null | string; text?: null | string;
cw?: null | string; cw?: null | string;
@ -62,10 +63,11 @@ type NoteSubmitReq = {
channelId?: null | Channel["id"]; channelId?: null | Channel["id"];
poll?: null | { poll?: null | {
choices: string[]; choices: string[];
multiple?: boolean; multiple: boolean;
expiresAt?: null | number; expiresAt: string | null;
expiredAfter?: null | number; expiredAfter: number | null;
}; };
lang?: string;
}; };
export type Endpoints = { export type Endpoints = {

View File

@ -29,7 +29,7 @@ export type UserLite = {
isIndexable: boolean; isIndexable: boolean;
isCat?: boolean; isCat?: boolean;
speakAsCat?: boolean; speakAsCat?: boolean;
driveCapacityOverrideMb: number | null, driveCapacityOverrideMb: number | null;
}; };
export type UserDetailed = UserLite & { export type UserDetailed = UserLite & {
@ -238,8 +238,8 @@ export interface RenoteNotification extends BaseNotification {
user: User; user: User;
userId: User["id"]; userId: User["id"];
note: Note & { note: Note & {
renote: Note, renote: Note;
renoteId: string, renoteId: string;
}; };
} }
export interface QuoteNotification extends BaseNotification { export interface QuoteNotification extends BaseNotification {

View File

@ -1,6 +1,7 @@
import * as acct from "./acct"; import * as acct from "./acct";
import type { Acct } from "./acct"; import type { Acct } from "./acct";
import { Endpoints } from "./api.types"; import { Endpoints } from "./api.types";
import type * as ApiTypes from "./api.types";
import * as consts from "./consts"; import * as consts from "./consts";
import Stream, { Connection } from "./streaming"; import Stream, { Connection } from "./streaming";
import * as StreamTypes from "./streaming.types"; import * as StreamTypes from "./streaming.types";
@ -8,6 +9,7 @@ import type * as TypeUtils from "./type-utils";
export { export {
Endpoints, Endpoints,
type ApiTypes,
Stream, Stream,
Connection as ChannelConnection, Connection as ChannelConnection,
StreamTypes, StreamTypes,