fix types

This commit is contained in:
Lhcfl 2024-04-23 21:30:55 +08:00
parent 850201ff71
commit 62f5c84ca6
17 changed files with 222 additions and 131 deletions

View file

@ -15,7 +15,7 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async () => {
let release;
let release: unknown;
await fetch(
"https://firefish.dev/firefish/firefish/-/raw/develop/release.json",

View file

@ -7,6 +7,7 @@ import { alert, api, popup, popupMenu, waiting } from "@/os";
import icon from "@/scripts/icon";
import { del, get, set } from "@/scripts/idb-proxy";
import { reloadChannel, unisonReload } from "@/scripts/unison-reload";
import type { MenuButton, MenuUser } from "./types/menu";
// TODO: 他のタブと永続化されたstateを同期
@ -16,7 +17,7 @@ export async function signOut() {
waiting();
localStorage.removeItem("account");
await removeAccount(me.id);
await removeAccount(me!.id);
const accounts = await getAccounts();
@ -26,12 +27,9 @@ export async function signOut() {
const registration = await navigator.serviceWorker.ready;
const push = await registration.pushManager.getSubscription();
if (push) {
await fetch(`${apiUrl}/sw/unregister`, {
method: "POST",
body: JSON.stringify({
i: me.token,
endpoint: push.endpoint,
}),
await api("sw/unregister", {
endpoint: push.endpoint,
i: me!.token, // FIXME: This parameter seems to be removable but I didn't test it
});
}
}
@ -117,13 +115,13 @@ function showSuspendedDialog() {
export function updateAccount(accountData) {
for (const [key, value] of Object.entries(accountData)) {
me[key] = value;
me![key] = value;
}
localStorage.setItem("account", JSON.stringify(me));
}
export async function refreshAccount() {
const accountData = await fetchAccount(me.token);
const accountData = await fetchAccount(me!.token);
return updateAccount(accountData);
}
@ -186,7 +184,7 @@ export async function openAccountMenu(
async function switchAccount(account: entities.UserDetailed) {
const storedAccounts = await getAccounts();
const token = storedAccounts.find((x) => x.id === account.id).token;
const token = storedAccounts.find((x) => x.id === account.id)!.token;
switchAccountWithToken(token);
}
@ -195,15 +193,15 @@ export async function openAccountMenu(
}
const storedAccounts = await getAccounts().then((accounts) =>
accounts.filter((x) => x.id !== me.id),
accounts.filter((x) => x.id !== me!.id),
);
const accountsPromise = api("users/show", {
userIds: storedAccounts.map((x) => x.id),
});
function createItem(account: entities.UserDetailed) {
function createItem(account: entities.UserDetailed): MenuUser {
return {
type: "user",
type: "user" as const,
user: account,
active: opts.active != null ? opts.active === account.id : false,
action: () => {
@ -218,10 +216,14 @@ export async function openAccountMenu(
const accountItemPromises = storedAccounts.map(
(a) =>
new Promise((res) => {
new Promise<MenuUser>((res) => {
accountsPromise.then((accounts) => {
const account = accounts.find((x) => x.id === a.id);
if (account == null) return res(null);
if (account == null) {
// The user is deleted, remove it
removeAccount(a.id);
return res(null as unknown as MenuUser);
}
res(createItem(account));
});
}),
@ -230,74 +232,72 @@ export async function openAccountMenu(
if (opts.withExtraOperation) {
popupMenu(
[
...[
...(isMobile ?? false
? [
{
type: "parent",
icon: `${icon("ph-plus")}`,
text: i18n.ts.addAccount,
children: [
{
text: i18n.ts.existingAccount,
action: () => {
showSigninDialog();
},
...(isMobile ?? false
? [
{
type: "parent" as const,
icon: `${icon("ph-plus")}`,
text: i18n.ts.addAccount,
children: [
{
text: i18n.ts.existingAccount,
action: () => {
showSigninDialog();
},
{
text: i18n.ts.createAccount,
action: () => {
createAccount();
},
},
{
text: i18n.ts.createAccount,
action: () => {
createAccount();
},
],
},
]
: [
{
type: "link",
text: i18n.ts.profile,
to: `/@${me.username}`,
avatar: me,
},
null,
]),
...(opts.includeCurrentAccount ? [createItem(me)] : []),
...accountItemPromises,
...(isMobile ?? false
? [
null,
{
type: "link",
text: i18n.ts.profile,
to: `/@${me.username}`,
avatar: me,
},
]
: [
{
type: "parent",
icon: `${icon("ph-plus")}`,
text: i18n.ts.addAccount,
children: [
{
text: i18n.ts.existingAccount,
action: () => {
showSigninDialog();
},
},
],
},
]
: [
{
type: "link" as const,
text: i18n.ts.profile,
to: `/@${me!.username}`,
avatar: me!,
},
null,
]),
...(opts.includeCurrentAccount ? [createItem(me!)] : []),
...accountItemPromises,
...(isMobile ?? false
? [
null,
{
type: "link" as const,
text: i18n.ts.profile,
to: `/@${me!.username}`,
avatar: me!,
},
]
: [
{
type: "parent" as const,
icon: `${icon("ph-plus")}`,
text: i18n.ts.addAccount,
children: [
{
text: i18n.ts.existingAccount,
action: () => {
showSigninDialog();
},
{
text: i18n.ts.createAccount,
action: () => {
createAccount();
},
},
{
text: i18n.ts.createAccount,
action: () => {
createAccount();
},
],
},
]),
],
},
],
},
]),
],
ev.currentTarget ?? ev.target,
(ev.currentTarget ?? ev.target) as HTMLElement,
{
align: "left",
},
@ -305,10 +305,10 @@ export async function openAccountMenu(
} else {
popupMenu(
[
...(opts.includeCurrentAccount ? [createItem(me)] : []),
...(opts.includeCurrentAccount ? [createItem(me!)] : []),
...accountItemPromises,
],
ev.currentTarget ?? ev.target,
(ev.currentTarget ?? ev.target) as HTMLElement,
{
align: "left",
},

View file

@ -1,5 +1,5 @@
<template>
<MkModal ref="modal" :z-priority="'middle'" @closed="$emit('closed')">
<MkModal ref="modal" :z-priority="'middle'" @closed="emit('closed')">
<div :class="$style.root">
<div :class="$style.title">
<MkSparkle v-if="isGoodNews">{{ title }}</MkSparkle>
@ -41,6 +41,10 @@ const props = defineProps<{
announcement: entities.Announcement;
}>();
const emit = defineEmits<{
closed: [];
}>();
const { id, text, title, imageUrl, isGoodNews } = props.announcement;
const modal = shallowRef<InstanceType<typeof MkModal>>();

View file

@ -1,5 +1,5 @@
<template>
<MkModal ref="modal" :z-priority="'middle'" @closed="$emit('closed')">
<MkModal ref="modal" :z-priority="'middle'" @closed="emit('closed')">
<div :class="$style.root">
<p :class="$style.title">
{{ i18n.ts.youHaveUnreadAnnouncements }}
@ -21,6 +21,10 @@ import MkModal from "@/components/MkModal.vue";
import MkButton from "@/components/MkButton.vue";
import { i18n } from "@/i18n";
const emit = defineEmits<{
closed: [];
}>();
const modal = shallowRef<InstanceType<typeof MkModal>>();
const checkAnnouncements = () => {
modal.value!.close();

View file

@ -160,7 +160,7 @@ const hCaptchaResponse = ref(null);
const reCaptchaResponse = ref(null);
const emit = defineEmits<{
(ev: "login", v: any): void;
login: [v: { id: string; i: string }];
}>();
const props = defineProps({

View file

@ -30,7 +30,7 @@ withDefaults(
);
const emit = defineEmits<{
(ev: "done"): void;
(ev: "done", res: { id: string; i: string }): void;
(ev: "closed"): void;
(ev: "cancelled"): void;
}>();
@ -39,11 +39,11 @@ const dialog = ref<InstanceType<typeof XModalWindow>>();
function onClose() {
emit("cancelled");
dialog.value.close();
dialog.value!.close();
}
function onLogin(res) {
function onLogin(res: { id: string; i: string }) {
emit("done", res);
dialog.value.close();
dialog.value!.close();
}
</script>

View file

@ -248,7 +248,7 @@
v-model="hCaptchaResponse"
class="_formBlock captcha"
provider="hcaptcha"
:sitekey="instance.hcaptchaSiteKey"
:sitekey="instance.hcaptchaSiteKey!"
/>
<MkCaptcha
v-if="instance.enableRecaptcha"
@ -256,7 +256,7 @@
v-model="reCaptchaResponse"
class="_formBlock captcha"
provider="recaptcha"
:sitekey="instance.recaptchaSiteKey"
:sitekey="instance.recaptchaSiteKey!"
/>
<MkButton
class="_formBlock"
@ -296,7 +296,7 @@ const props = withDefaults(
);
const emit = defineEmits<{
(ev: "signup", user: Record<string, any>): void;
(ev: "signup", user: { id: string; i: string }): void;
(ev: "signupEmailPending"): void;
}>();

View file

@ -36,13 +36,13 @@ withDefaults(
);
const emit = defineEmits<{
(ev: "done"): void;
(ev: "done", res: { id: string; i: string }): void;
(ev: "closed"): void;
}>();
const dialog = ref<InstanceType<typeof XModalWindow>>();
function onSignup(res) {
function onSignup(res: { id: string; i: string }) {
emit("done", res);
dialog.value?.close();
}

View file

@ -2,8 +2,8 @@
<MkModal
ref="modal"
:z-priority="'middle'"
@click="$refs.modal.close()"
@closed="$emit('closed')"
@click="modal!.close()"
@closed="emit('closed')"
>
<div :class="$style.root">
<div :class="$style.title">
@ -11,8 +11,8 @@
</div>
<div :class="$style.version"> {{ version }} 🚀</div>
<div v-if="newRelease" :class="$style.releaseNotes">
<Mfm :text="data.notes" />
<div v-if="data.screenshots.length > 0" style="max-width: 500">
<Mfm :text="data?.notes ?? ''" />
<div v-if="data?.screenshots && data.screenshots.length > 0" style="max-width: 500">
<img
v-for="i in data.screenshots"
:key="i"
@ -25,7 +25,7 @@
:class="$style.gotIt"
primary
full
@click="$refs.modal.close()"
@click="modal!.close()"
>{{ i18n.ts.gotIt }}</MkButton
>
</div>
@ -40,11 +40,16 @@ import MkButton from "@/components/MkButton.vue";
import { version } from "@/config";
import { i18n } from "@/i18n";
import * as os from "@/os";
import type { Endpoints } from "firefish-js";
const emit = defineEmits<{
closed: [];
}>();
const modal = shallowRef<InstanceType<typeof MkModal>>();
const newRelease = ref(false);
const data = ref(Object);
const data = ref<Endpoints["release"]["res"] | null>(null);
os.api("release").then((res) => {
data.value = res;
@ -52,7 +57,7 @@ os.api("release").then((res) => {
});
console.log(`Version: ${version}`);
console.log(`Data version: ${data.value.version}`);
console.log(`Data version: ${data.value?.version}`);
console.log(newRelease.value);
console.log(data.value);
</script>

View file

@ -1,6 +1,7 @@
import { markRaw } from "vue";
import { locale } from "@/config";
// biome-ignore lint/suspicious/noExplicitAny: temporary use any
class I18n<T extends Record<string, any>> {
public ts: T;

View file

@ -245,7 +245,12 @@ function checkForSplash() {
try {
// 変なバージョン文字列来るとcompareVersionsでエラーになるため
if (lastVersion < version && defaultStore.state.showUpdates) {
// If a strange version string comes, an error will occur in compareVersions.
if (
lastVersion != null &&
lastVersion < version &&
defaultStore.state.showUpdates
) {
// ログインしてる場合だけ
if (me) {
popup(
@ -281,7 +286,7 @@ function checkForSplash() {
"closed",
);
} else {
unreadAnnouncements.forEach((item) => {
for (const item of unreadAnnouncements) {
if (item.showPopup)
popup(
defineAsyncComponent(
@ -291,7 +296,7 @@ function checkForSplash() {
{},
"closed",
);
});
}
}
})
.catch((err) => console.log(err));

View file

@ -1,7 +1,7 @@
// NIRAX --- A lightweight router
import { EventEmitter } from "eventemitter3";
import type { Component, ShallowRef } from "vue";
import type { Component } from "vue";
import { shallowRef } from "vue";
import { safeURIDecode } from "@/scripts/safe-uri-decode";
import { pleaseLogin } from "@/scripts/please-login";
@ -36,6 +36,7 @@ export interface Resolved {
function parsePath(path: string): ParsedPath {
const res = [] as ParsedPath;
// biome-ignore lint/style/noParameterAssign: assign it intentionally
path = path.substring(1);
for (const part of path.split("/")) {
@ -76,13 +77,13 @@ export class Router extends EventEmitter<{
same: () => void;
}> {
private routes: RouteDef[];
public current: Resolved;
public currentRef: ShallowRef<Resolved> = shallowRef();
public currentRoute: ShallowRef<RouteDef> = shallowRef();
public current!: Resolved; // It is assigned in this.navigate
public currentRef = shallowRef<Resolved>();
public currentRoute = shallowRef<RouteDef>();
private currentPath: string;
private currentKey = Date.now().toString();
public navHook: ((path: string, flag?: any) => boolean) | null = null;
public navHook: ((path: string, flag?: unknown) => boolean) | null = null;
constructor(routes: Router["routes"], currentPath: Router["currentPath"]) {
super();
@ -92,9 +93,10 @@ export class Router extends EventEmitter<{
this.navigate(currentPath, null, false);
}
public resolve(path: string): Resolved | null {
public resolve(_path: string): Resolved | null {
let queryString: string | null = null;
let hash: string | null = null;
let path = _path;
if (path[0] === "/") path = path.substring(1);
if (path.includes("#")) {
hash = path.substring(path.indexOf("#") + 1);
@ -168,9 +170,16 @@ export class Router extends EventEmitter<{
}
if (route.query != null && queryString != null) {
const queryObject = [
...new URLSearchParams(queryString).entries(),
].reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {});
// const queryObject = [
// ...new URLSearchParams(queryString).entries(),
// ].reduce((obj, entry) => ({ ...obj, [entry[0]]: entry[1] }), {});
const queryObject: Record<string, string> = Object.assign(
{},
...[...new URLSearchParams(queryString).entries()].map(
(entry) => ({ [entry[0]]: entry[1] }),
),
);
for (const q in route.query) {
const as = route.query[q];
@ -227,6 +236,7 @@ export class Router extends EventEmitter<{
}
const isSamePath = beforePath === path;
// biome-ignore lint/style/noParameterAssign: assign it intentionally
if (isSamePath && key == null) key = this.currentKey;
this.current = res;
this.currentRef.value = res;
@ -253,7 +263,7 @@ export class Router extends EventEmitter<{
return this.currentKey;
}
public push(path: string, flag?: any) {
public push(path: string, flag?: unknown) {
const beforePath = this.currentPath;
if (path === beforePath) {
this.emit("same");

View file

@ -893,9 +893,6 @@ export async function openEmojiPicker(
...opts,
},
{
chosen: (emoji) => {
insertTextAtCursor(activeTextarea, emoji);
},
done: (emoji) => {
insertTextAtCursor(activeTextarea, emoji);
},

View file

@ -7,7 +7,7 @@
i18n.ts._accountDelete.sendEmail
}}</FormInfo>
<FormButton
v-if="!me.isDeleted"
v-if="!me!.isDeleted"
danger
class="_formBlock"
@click="deleteAccount"
@ -27,6 +27,7 @@ import { signOut } from "@/account";
import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon";
import { me } from "@/me";
async function deleteAccount() {
{

View file

@ -42,7 +42,20 @@ export function install(plugin) {
aiscript.exec(parser.parse(plugin.src));
}
function createPluginEnv(opts) {
interface Plugin {
config?: Record<
string,
{
default: unknown;
[k: string]: unknown;
}
>;
configData: Record<string, unknown>;
token: string;
id: string;
}
function createPluginEnv(opts: { plugin: Plugin; storageKey: string }) {
const config = new Map<string, values.Value>();
for (const [k, v] of Object.entries(opts.plugin.config ?? {})) {
config.set(
@ -172,7 +185,7 @@ function registerNoteAction({ pluginId, title, handler }) {
if (!pluginContext) {
return;
}
pluginContext.execFn(handler, [utils.jsToVal(user)]);
pluginContext.execFn(handler, [utils.jsToVal(note)]);
},
});
}
@ -205,16 +218,18 @@ function registerNotePostInterruptor({ pluginId, handler }) {
});
}
// FIXME: where is pageViewInterruptors?
// This function currently can't do anything
function registerPageViewInterruptor({ pluginId, handler }): void {
pageViewInterruptors.push({
handler: async (page) => {
const pluginContext = pluginContexts.get(pluginId);
if (!pluginContext) {
return;
}
return utils.valToJs(
await pluginContext.execFn(handler, [utils.jsToVal(page)]),
);
},
});
// pageViewInterruptors.push({
// handler: async (page) => {
// const pluginContext = pluginContexts.get(pluginId);
// if (!pluginContext) {
// return;
// }
// return utils.valToJs(
// await pluginContext.execFn(handler, [utils.jsToVal(page)]),
// );
// },
// });
}

View file

@ -362,6 +362,16 @@ export type Endpoints = {
res: DriveFile[];
};
"email-address/available": {
req: {
emailAddress: string;
};
res: {
available?: boolean;
reason: string | null;
};
};
// endpoint
endpoint: {
req: { endpoint: string };
@ -893,6 +903,16 @@ export type Endpoints = {
// promo
"promo/read": { req: TODO; res: TODO };
// release
release: {
req: null;
res: {
version: string;
notes: string;
screenshots: string[];
};
};
// request-reset-password
"request-reset-password": {
req: { username: string; email: string };
@ -915,8 +935,36 @@ export type Endpoints = {
// ck specific
"latest-version": { req: NoParams; res: TODO };
// signin
signin: {
req: {
username: string;
password: string;
"hcaptcha-response"?: null | string;
"g-recaptcha-response"?: null | string;
};
res:
| {
id: User["id"];
i: string;
}
| {
challenge: string;
challengeId: string;
securityKeys: {
id: string;
}[];
};
};
// sw
"sw/register": { req: TODO; res: TODO };
"sw/unregister": {
req: {
endpoint: string;
};
res: null;
};
// username
"username/available": {

View file

@ -116,6 +116,7 @@ export type MeDetailed = UserDetailed & {
preventAiLearning: boolean;
receiveAnnouncementEmail: boolean;
usePasswordLessLogin: boolean;
token: string;
[other: string]: any;
};
@ -479,7 +480,7 @@ export type Announcement = {
imageUrl: string | null;
isRead?: boolean;
isGoodNews: boolean;
showPopUp: boolean;
showPopup: boolean;
};
export type Antenna = {