fix types of components

This commit is contained in:
Lhcfl 2024-04-12 16:37:32 +08:00
parent ea6ef881c2
commit e4927c9b9b
38 changed files with 319 additions and 159 deletions

View file

@ -20,15 +20,10 @@ import { ref } from "vue";
import { instanceName, version } from "@/config"; import { instanceName, version } from "@/config";
import { instance as Instance } from "@/instance"; import { instance as Instance } from "@/instance";
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy"; import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
import type { entities } from "firefish-js";
const props = defineProps<{ const props = defineProps<{
instance?: { instance?: entities.InstanceLite;
faviconUrl?: string;
name: string;
themeColor?: string;
softwareName?: string;
softwareVersion?: string;
};
}>(); }>();
const ticker = ref<HTMLElement | null>(null); const ticker = ref<HTMLElement | null>(null);

View file

@ -9,7 +9,7 @@
v-vibrate="5" v-vibrate="5"
:aria-label="accessibleLabel" :aria-label="accessibleLabel"
class="tkcbzcuz note-container" class="tkcbzcuz note-container"
:tabindex="!isDeleted ? '-1' : null" :tabindex="!isDeleted ? '-1' : undefined"
:class="{ renote: isRenote }" :class="{ renote: isRenote }"
> >
<MkNoteSub <MkNoteSub
@ -112,9 +112,9 @@
:note="appearNote" :note="appearNote"
:detailed="true" :detailed="true"
:detailed-view="detailedView" :detailed-view="detailedView"
:parent-id="appearNote.parentId" :parent-id="appearNote.id"
@push="(e) => router.push(notePage(e))" @push="(e) => router.push(notePage(e))"
@focusfooter="footerEl.focus()" @focusfooter="footerEl!.focus()"
@expanded="(e) => setPostExpanded(e)" @expanded="(e) => setPostExpanded(e)"
></MkSubNoteContent> ></MkSubNoteContent>
<div v-if="translating || translation" class="translation"> <div v-if="translating || translation" class="translation">
@ -312,11 +312,17 @@ import { notePage } from "@/filters/note";
import { deepClone } from "@/scripts/clone"; import { deepClone } from "@/scripts/clone";
import { getNoteSummary } from "@/scripts/get-note-summary"; import { getNoteSummary } from "@/scripts/get-note-summary";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import type { NoteTranslation } from "@/types/note";
const router = useRouter(); const router = useRouter();
type NoteType = entities.Note & {
_featuredId_?: string;
_prId_?: string;
};
const props = defineProps<{ const props = defineProps<{
note: entities.Note; note: NoteType;
pinned?: boolean; pinned?: boolean;
detailedView?: boolean; detailedView?: boolean;
collapsedReply?: boolean; collapsedReply?: boolean;
@ -354,18 +360,18 @@ 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>();
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const starButton = ref<InstanceType<typeof XStarButton>>(); const starButton = ref<InstanceType<typeof XStarButton>>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton> | null>(null);
const renoteTime = ref<HTMLElement>(); const renoteTime = ref<HTMLElement>();
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 NoteType) : note.value,
); );
const isMyRenote = isSignedIn && me.id === note.value.userId; const isMyRenote = isSignedIn && me!.id === note.value.userId;
const showContent = ref(false); // const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref( const muted = ref(
getWordSoftMute( getWordSoftMute(
@ -375,7 +381,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 enableEmojiReactions = defaultStore.state.enableEmojiReactions; const enableEmojiReactions = defaultStore.state.enableEmojiReactions;
const expandOnNoteClick = defaultStore.state.expandOnNoteClick; const expandOnNoteClick = defaultStore.state.expandOnNoteClick;
@ -391,7 +397,7 @@ const isForeignLanguage: boolean =
return postLang !== "" && postLang !== targetLang; return postLang !== "" && postLang !== targetLang;
})(); })();
async function translate_(noteId, targetLang: string) { async function translate_(noteId: string, targetLang: string) {
return await os.api("notes/translate", { return await os.api("notes/translate", {
noteId, noteId,
targetLang, targetLang,
@ -421,12 +427,13 @@ async function translate() {
const keymap = { const keymap = {
r: () => reply(true), r: () => reply(true),
"e|a|plus": () => react(true), "e|a|plus": () => react(true),
q: () => renoteButton.value.renote(true), q: () => renoteButton.value!.renote(true),
"up|k": focusBefore, "up|k": focusBefore,
"down|j": focusAfter, "down|j": focusAfter,
esc: blur, esc: blur,
"m|o": () => menu(true), "m|o": () => menu(true),
s: () => showContent.value !== showContent.value, // FIXME: What's this?
// s: () => showContent.value !== showContent.value,
}; };
if (appearNote.value.historyId == null) { if (appearNote.value.historyId == null) {
@ -437,12 +444,12 @@ if (appearNote.value.historyId == null) {
}); });
} }
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,
}, },
() => { () => {
focus(); focus();
@ -450,11 +457,11 @@ function reply(viaKeyboard = false): void {
); );
} }
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,
@ -467,7 +474,7 @@ function react(viaKeyboard = false): void {
); );
} }
function undoReact(note): void { function undoReact(note: NoteType): void {
const oldReaction = note.myReaction; const oldReaction = note.myReaction;
if (!oldReaction) return; if (!oldReaction) return;
os.api("notes/reactions/delete", { os.api("notes/reactions/delete", {
@ -481,16 +488,17 @@ const currentClipPage = inject<Ref<entities.Clip> | null>(
); );
function onContextmenu(ev: MouseEvent): void { function onContextmenu(ev: MouseEvent): void {
const isLink = (el: HTMLElement) => { const isLink = (el: HTMLElement): boolean => {
if (el.tagName === "A") return true; if (el.tagName === "A") return true;
// The Audio element's context menu is the browser default, such as for selecting playback speed. // The Audio element's context menu is the browser default, such as for selecting playback speed.
if (el.tagName === "AUDIO") return true; if (el.tagName === "AUDIO") return true;
if (el.parentElement) { if (el.parentElement) {
return isLink(el.parentElement); return isLink(el.parentElement);
} }
return false;
}; };
if (isLink(ev.target)) return; if (isLink(ev.target as HTMLElement)) return;
if (window.getSelection().toString() !== "") return; if (window.getSelection()?.toString() !== "") return;
if (defaultStore.state.useReactionPickerForContextMenu) { if (defaultStore.state.useReactionPickerForContextMenu) {
ev.preventDefault(); ev.preventDefault();
@ -509,7 +517,7 @@ function onContextmenu(ev: MouseEvent): void {
os.pageWindow(notePage(appearNote.value)); os.pageWindow(notePage(appearNote.value));
}, },
}, },
notePage(appearNote.value) != location.pathname notePage(appearNote.value) !== location.pathname
? { ? {
icon: `${icon("ph-arrows-out-simple")}`, icon: `${icon("ph-arrows-out-simple")}`,
text: i18n.ts.showInPage, text: i18n.ts.showInPage,
@ -589,11 +597,11 @@ function showRenoteMenu(viaKeyboard = false): void {
} }
function focus() { function focus() {
el.value.focus(); el.value!.focus();
} }
function blur() { function blur() {
el.value.blur(); el.value!.blur();
} }
function focusBefore() { function focusBefore() {
@ -605,12 +613,12 @@ function focusAfter() {
} }
function scrollIntoView() { function scrollIntoView() {
el.value.scrollIntoView(); el.value!.scrollIntoView();
} }
function noteClick(e) { function noteClick(e) {
if ( if (
document.getSelection().type === "Range" || document.getSelection()?.type === "Range" ||
props.detailedView || props.detailedView ||
!expandOnNoteClick !expandOnNoteClick
) { ) {

View file

@ -6,7 +6,7 @@
v-hotkey="keymap" v-hotkey="keymap"
v-size="{ max: [500, 350, 300] }" v-size="{ max: [500, 350, 300] }"
class="lxwezrsl _block" class="lxwezrsl _block"
:tabindex="!isDeleted ? '-1' : null" :tabindex="!isDeleted ? '-1' : undefined"
:class="{ renote: isRenote }" :class="{ renote: isRenote }"
> >
<MkNoteSub <MkNoteSub
@ -64,7 +64,7 @@
) )
}} }}
</option> </option>
<option v-if="directQuotes?.length > 0" value="quotes"> <option v-if="directQuotes && directQuotes.length > 0" value="quotes">
<!-- <i :class="icon('ph-quotes')"></i> --> <!-- <i :class="icon('ph-quotes')"></i> -->
{{ {{
wordWithCount( wordWithCount(
@ -102,7 +102,7 @@
:detailed-view="true" :detailed-view="true"
:parent-id="note.id" :parent-id="note.id"
/> />
<MkLoading v-else-if="tab === 'quotes' && directQuotes.length > 0" /> <MkLoading v-else-if="tab === 'quotes' && directQuotes && directQuotes.length > 0" />
<!-- <MkPagination <!-- <MkPagination
v-if="tab === 'renotes'" v-if="tab === 'renotes'"
@ -225,12 +225,12 @@ if (noteViewInterruptors.length > 0) {
}); });
} }
const el = ref<HTMLElement>(); const el = ref<HTMLElement | null>(null);
const noteEl = ref(); const noteEl = ref();
const menuButton = ref<HTMLElement>(); const menuButton = ref<HTMLElement>();
const renoteButton = ref<InstanceType<typeof XRenoteButton>>(); const renoteButton = ref<InstanceType<typeof XRenoteButton>>();
const reactButton = ref<HTMLElement>(); const reactButton = ref<HTMLElement>();
const showContent = ref(false); // const showContent = ref(false);
const isDeleted = ref(false); const isDeleted = ref(false);
const muted = ref( const muted = ref(
getWordSoftMute( getWordSoftMute(
@ -248,7 +248,8 @@ const directReplies = ref<null | entities.Note[]>([]);
const directQuotes = ref<null | entities.Note[]>([]); const directQuotes = ref<null | entities.Note[]>([]);
const clips = ref(); const clips = ref();
const renotes = ref(); const renotes = ref();
let isScrolling; const isRenote = ref(note.value.renoteId != null);
let isScrolling: boolean;
const reactionsCount = Object.values(props.note.reactions).reduce( const reactionsCount = Object.values(props.note.reactions).reduce(
(x, y) => x + y, (x, y) => x + y,
@ -258,10 +259,10 @@ const reactionsCount = Object.values(props.note.reactions).reduce(
const keymap = { const keymap = {
r: () => reply(true), r: () => reply(true),
"e|a|plus": () => react(true), "e|a|plus": () => react(true),
q: () => renoteButton.value.renote(true), q: () => renoteButton.value!.renote(true),
esc: blur, esc: blur,
"m|o": () => menu(true), "m|o": () => menu(true),
s: () => showContent.value !== showContent.value, // s: () => showContent.value !== showContent.value,
}; };
useNoteCapture({ useNoteCapture({
@ -270,21 +271,21 @@ useNoteCapture({
isDeletedRef: isDeleted, isDeletedRef: isDeleted,
}); });
function reply(viaKeyboard = false): void { function reply(_viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
os.post({ os.post({
reply: note.value, reply: note.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: note.value.id, noteId: note.value.id,
@ -297,13 +298,13 @@ function react(viaKeyboard = false): void {
); );
} }
function undoReact(note): void { // function undoReact(note): void {
const oldReaction = note.myReaction; // const oldReaction = note.myReaction;
if (!oldReaction) return; // if (!oldReaction) return;
os.api("notes/reactions/delete", { // os.api("notes/reactions/delete", {
noteId: note.id, // noteId: note.id,
}); // });
} // }
function onContextmenu(ev: MouseEvent): void { function onContextmenu(ev: MouseEvent): void {
const isLink = (el: HTMLElement) => { const isLink = (el: HTMLElement) => {
@ -312,8 +313,8 @@ function onContextmenu(ev: MouseEvent): void {
return isLink(el.parentElement); return isLink(el.parentElement);
} }
}; };
if (isLink(ev.target)) return; if (isLink(ev.target as HTMLElement)) return;
if (window.getSelection().toString() !== "") return; if (window.getSelection()?.toString() !== "") return;
if (defaultStore.state.useReactionPickerForContextMenu) { if (defaultStore.state.useReactionPickerForContextMenu) {
ev.preventDefault(); ev.preventDefault();
@ -362,12 +363,17 @@ os.api("notes/children", {
limit: 30, limit: 30,
depth: 12, depth: 12,
}).then((res) => { }).then((res) => {
res = res.reduce((acc, resNote) => { // biome-ignore lint/style/noParameterAssign: assign it intentially
if (resNote.userId == note.value.userId) { res = res
return [...acc, resNote]; .filter((n) => n.userId !== note.value.userId)
} .reverse()
return [resNote, ...acc]; .concat(res.filter((n) => n.userId === note.value.userId));
}, []); // res = res.reduce((acc: entities.Note[], resNote) => {
// if (resNote.userId === note.value.userId) {
// return [...acc, resNote];
// }
// return [resNote, ...acc];
// }, []);
replies.value = res; replies.value = res;
directReplies.value = res directReplies.value = res
.filter((resNote) => resNote.replyId === note.value.id) .filter((resNote) => resNote.replyId === note.value.id)
@ -438,7 +444,7 @@ async function onNoteUpdated(
} }
switch (type) { switch (type) {
case "replied": case "replied": {
const { id: createdId } = body; const { id: createdId } = body;
const replyNote = await os.api("notes/show", { const replyNote = await os.api("notes/show", {
noteId: createdId, noteId: createdId,
@ -446,10 +452,10 @@ async function onNoteUpdated(
replies.value.splice(found, 0, replyNote); replies.value.splice(found, 0, replyNote);
if (found === 0) { if (found === 0) {
directReplies.value.push(replyNote); directReplies.value!.push(replyNote);
} }
break; break;
}
case "deleted": case "deleted":
if (found === 0) { if (found === 0) {
isDeleted.value = true; isDeleted.value = true;

View file

@ -1,9 +1,9 @@
<template> <template>
<div v-size="{ min: [350, 500] }" class="fefdfafb"> <div v-size="{ min: [350, 500] }" class="fefdfafb">
<MkAvatar class="avatar" :user="me" disable-link /> <MkAvatar class="avatar" :user="me!" disable-link />
<div class="main"> <div class="main">
<div class="header"> <div class="header">
<MkUserName :user="me" /> <MkUserName :user="me!" />
</div> </div>
<div class="body"> <div class="body">
<div class="content"> <div class="content">

View file

@ -40,7 +40,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import type { PagingOf } from "@/components/MkPagination.vue"; import type {
MkPaginationType,
PagingKeyOf,
PagingOf,
} from "@/components/MkPagination.vue";
import XNote from "@/components/MkNote.vue"; import XNote from "@/components/MkNote.vue";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
@ -56,10 +60,14 @@ defineProps<{
disableAutoLoad?: boolean; disableAutoLoad?: boolean;
}>(); }>();
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<
PagingKeyOf<entities.Note>
> | null>(null);
function scrollTop() { function scrollTop() {
scroll(tlEl.value, { top: 0, behavior: "smooth" }); if (tlEl.value) {
scroll(tlEl.value, { top: 0, behavior: "smooth" });
}
} }
defineExpose({ defineExpose({

View file

@ -6,7 +6,7 @@
:with-ok-button="true" :with-ok-button="true"
:ok-button-disabled="false" :ok-button-disabled="false"
@ok="ok()" @ok="ok()"
@close="dialog.close()" @close="dialog!.close()"
@closed="emit('closed')" @closed="emit('closed')"
> >
<template #header>{{ i18n.ts.notificationSetting }}</template> <template #header>{{ i18n.ts.notificationSetting }}</template>
@ -68,7 +68,7 @@ const includingTypes = computed(() => props.includingTypes || []);
const dialog = ref<InstanceType<typeof XModalWindow>>(); const dialog = ref<InstanceType<typeof XModalWindow>>();
const typesMap = ref<Record<(typeof notificationTypes)[number], boolean>>({}); const typesMap = ref({} as Record<(typeof notificationTypes)[number], boolean>);
const useGlobalSetting = ref( const useGlobalSetting = ref(
(includingTypes.value === null || includingTypes.value.length === 0) && (includingTypes.value === null || includingTypes.value.length === 0) &&
props.showGlobalToggle, props.showGlobalToggle,
@ -89,7 +89,7 @@ function ok() {
}); });
} }
dialog.value.close(); dialog.value!.close();
} }
function disableAll() { function disableAll() {

View file

@ -19,9 +19,10 @@ import { onMounted, ref } from "vue";
import XNotification from "@/components/MkNotification.vue"; import XNotification from "@/components/MkNotification.vue";
import * as os from "@/os"; import * as os from "@/os";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
import type { entities } from "firefish-js";
defineProps<{ defineProps<{
notification: any; // TODO notification: entities.Notification;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{

View file

@ -44,7 +44,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from "vue"; import { computed, onMounted, onUnmounted, ref } from "vue";
import type { StreamTypes, entities, notificationTypes } from "firefish-js"; import type { StreamTypes, entities, notificationTypes } from "firefish-js";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import XNotification from "@/components/MkNotification.vue"; import XNotification from "@/components/MkNotification.vue";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import XNote from "@/components/MkNote.vue"; import XNote from "@/components/MkNote.vue";
@ -59,7 +61,7 @@ const props = defineProps<{
const stream = useStream(); const stream = useStream();
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<"i/notifications"> | null>(null);
const pagination = { const pagination = {
endpoint: "i/notifications" as const, endpoint: "i/notifications" as const,

View file

@ -67,7 +67,7 @@
</template> </template>
<script lang="ts" setup generic="E extends PagingKey"> <script lang="ts" setup generic="E extends PagingKey">
import type { ComputedRef } from "vue"; import type { ComponentPublicInstance, ComputedRef } from "vue";
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue"; import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
import type { Endpoints, TypeUtils } from "firefish-js"; import type { Endpoints, TypeUtils } from "firefish-js";
import * as os from "@/os"; import * as os from "@/os";
@ -81,8 +81,30 @@ import MkButton from "@/components/MkButton.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { defaultStore } from "@/store"; import { defaultStore } from "@/store";
/**
* ref type of MkPagination<E>
* Due to Vue's incomplete type support for generic components,
* we have to manually maintain this type instead of
* using `InstanceType<typeof MkPagination>`
*/
export type MkPaginationType<
E extends PagingKey,
Item = Endpoints[E]["res"][number],
> = ComponentPublicInstance & {
items: Item[];
queue: Item[];
backed: boolean;
reload: () => Promise<void>;
refresh: () => Promise<void>;
prepend: (item: Item) => Promise<void>;
append: (item: Item) => Promise<void>;
removeItem: (finder: (item: Item) => boolean) => boolean;
updateItem: (id: string, replacer: (old: Item) => Item) => boolean;
};
export type PagingKeyOf<T> = TypeUtils.EndpointsOf<T[]>;
// biome-ignore lint/suspicious/noExplicitAny: Used Intentionally // biome-ignore lint/suspicious/noExplicitAny: Used Intentionally
export type PagingKey = TypeUtils.EndpointsOf<any[]>; export type PagingKey = PagingKeyOf<any>;
export interface Paging<E extends PagingKey = PagingKey> { export interface Paging<E extends PagingKey = PagingKey> {
endpoint: E; endpoint: E;

View file

@ -2,7 +2,7 @@
<MkModal <MkModal
ref="modal" ref="modal"
:prefer-type="'dialog'" :prefer-type="'dialog'"
@click="modal.close()" @click="modal!.close()"
@closed="onModalClosed()" @closed="onModalClosed()"
> >
<MkPostForm <MkPostForm
@ -12,8 +12,8 @@
autofocus autofocus
freeze-after-posted freeze-after-posted
@posted="onPosted" @posted="onPosted"
@cancel="modal.close()" @cancel="modal!.close()"
@esc="modal.close()" @esc="modal!.close()"
/> />
</MkModal> </MkModal>
</template> </template>

View file

@ -77,7 +77,7 @@ const hasRenotedBefore = ref(false);
if (isSignedIn) { if (isSignedIn) {
os.api("notes/renotes", { os.api("notes/renotes", {
noteId: props.note.id, noteId: props.note.id,
userId: me.id, userId: me!.id,
limit: 1, limit: 1,
}).then((res) => { }).then((res) => {
hasRenotedBefore.value = res.length > 0; hasRenotedBefore.value = res.length > 0;
@ -251,6 +251,10 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
os.popupMenu(buttonActions, buttonRef.value, { viaKeyboard }); os.popupMenu(buttonActions, buttonRef.value, { viaKeyboard });
}; };
defineExpose({
renote,
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -31,7 +31,6 @@
:text="note.cw" :text="note.cw"
:author="note.user" :author="note.user"
:lang="note.lang" :lang="note.lang"
:i="me"
:custom-emojis="note.emojis" :custom-emojis="note.emojis"
/> />
</p> </p>
@ -63,8 +62,8 @@
<div <div
class="body" class="body"
v-bind="{ v-bind="{
'aria-hidden': note.cw && !showContent ? 'true' : null, 'aria-hidden': note.cw && !showContent ? 'true' : undefined,
tabindex: !showContent ? '-1' : null, tabindex: !showContent ? '-1' : undefined,
}" }"
> >
<span v-if="note.deletedAt" style="opacity: 0.5" <span v-if="note.deletedAt" style="opacity: 0.5"
@ -103,7 +102,6 @@
v-if="note.text" v-if="note.text"
:text="note.text" :text="note.text"
:author="note.user" :author="note.user"
:i="me"
:lang="note.lang" :lang="note.lang"
:custom-emojis="note.emojis" :custom-emojis="note.emojis"
/> />
@ -256,7 +254,7 @@ async function toggleMfm() {
} }
function focusFooter(ev) { function focusFooter(ev) {
if (ev.key == "Tab" && !ev.getModifierState("Shift")) { if (ev.key === "Tab" && !ev.getModifierState("Shift")) {
emit("focusfooter"); emit("focusfooter");
} }
} }

View file

@ -11,10 +11,10 @@
</div> </div>
</template> </template>
<template #default="{ items: users }"> <template #default="{ items }: { items: entities.UserDetailed[] }">
<div class="efvhhmdq"> <div class="efvhhmdq">
<MkUserInfo <MkUserInfo
v-for="user in users" v-for="user in items"
:key="user.id" :key="user.id"
class="user" class="user"
:user="user" :user="user"
@ -27,16 +27,21 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import MkUserInfo from "@/components/MkUserInfo.vue"; import MkUserInfo from "@/components/MkUserInfo.vue";
import type { Paging } from "@/components/MkPagination.vue"; import type {
MkPaginationType,
PagingKeyOf,
PagingOf,
} from "@/components/MkPagination.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import type { entities } from "firefish-js";
defineProps<{ defineProps<{
pagination: Paging; pagination: PagingOf<entities.UserDetailed>;
noGap?: boolean; noGap?: boolean;
}>(); }>();
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<PagingKeyOf<entities.User>>>();
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -94,9 +94,9 @@ import { defaultStore } from "@/store";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
const emit = defineEmits<{ const emit = defineEmits<{
(ev: "ok", selected: entities.UserDetailed): void; ok: [selected: entities.UserDetailed];
(ev: "cancel"): void; cancel: [];
(ev: "closed"): void; closed: [];
}>(); }>();
const username = ref(""); const username = ref("");
@ -114,7 +114,7 @@ const search = () => {
query: username.value, query: username.value,
origin: "local", origin: "local",
limit: 10, limit: 10,
detail: false, detail: true,
}).then((_users) => { }).then((_users) => {
users.value = _users; users.value = _users;
}); });
@ -127,7 +127,7 @@ const ok = () => {
// 使 // 使
let recents = defaultStore.state.recentlyUsedUsers; let recents = defaultStore.state.recentlyUsedUsers;
recents = recents.filter((x) => x !== selected.value.id); recents = recents.filter((x) => x !== selected.value!.id);
recents.unshift(selected.value.id); recents.unshift(selected.value.id);
defaultStore.set("recentlyUsedUsers", recents.splice(0, 16)); defaultStore.set("recentlyUsedUsers", recents.splice(0, 16));
}; };

View file

@ -26,7 +26,7 @@ withDefaults(
text: string; text: string;
plain?: boolean; plain?: boolean;
nowrap?: boolean; nowrap?: boolean;
author?: entities.User; author?: entities.User | null;
customEmojis?: entities.EmojiLite[]; customEmojis?: entities.EmojiLite[];
isNote?: boolean; isNote?: boolean;
advancedMfm?: boolean; advancedMfm?: boolean;

View file

@ -30,7 +30,7 @@ export default defineComponent({
default: false, default: false,
}, },
author: { author: {
type: Object as PropType<entities.User>, type: Object as PropType<entities.User | null>,
default: null, default: null,
}, },
// TODO: This variable is not used in the code and may be removed // TODO: This variable is not used in the code and may be removed

View file

@ -13,7 +13,7 @@ export const wsUrl = `${url
export const lang = localStorage.getItem("lang"); export const lang = localStorage.getItem("lang");
export const langs = _LANGS_; export const langs = _LANGS_;
export const locale = JSON.parse(localStorage.getItem("locale") || "en-US"); export const locale = JSON.parse(localStorage.getItem("locale") || "en-US");
export const version = _VERSION_; export const version: string = _VERSION_;
export const instanceName = siteName === "Firefish" ? host : siteName; export const instanceName = siteName === "Firefish" ? host : siteName;
export const ui = localStorage.getItem("ui"); export const ui = localStorage.getItem("ui");
export const debug = localStorage.getItem("debug") === "true"; export const debug = localStorage.getItem("debug") === "true";

View file

@ -22,7 +22,7 @@ const apiClient = new firefishApi.APIClient({
export const api = (( export const api = ((
endpoint: string, endpoint: string,
data: Record<string, any> = {}, data: Record<string, unknown> = {},
token?: string | null | undefined, token?: string | null | undefined,
useToken = true, useToken = true,
) => { ) => {
@ -174,13 +174,14 @@ export function promiseDialog<T>(
} }
let popupIdCount = 0; let popupIdCount = 0;
export const popups = ref<
{ type PopupType = {
id: number; id: number;
component: Component; component: Component;
props: Record<string, unknown>; props: Record<string, unknown>;
}[] events: Record<string, unknown>;
>([]); };
export const popups = ref<PopupType[]>([]);
const zIndexes = { const zIndexes = {
low: 1000000, low: 1000000,
@ -922,18 +923,27 @@ export function contextMenu(
}); });
} }
export function post(props: Record<string, any> = {}) { export function post(
return new Promise((resolve, reject) => { props: InstanceType<typeof MkPostFormDialog>["$props"] = {},
onClosed?: () => void,
) {
return new Promise<void>((resolve, reject) => {
// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない // NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない
// NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、 // NOTE: ただ、dynamic importしない場合、MkPostFormDialogインスタンスが使いまわされ、
// Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、 // Vueが渡されたコンポーネントに内部的に__propsというプロパティを生やす影響で、
// 複数のpost formを開いたときに場合によってはエラーになる // 複数のpost formを開いたときに場合によってはエラーになる
// もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが // もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが
let dispose; // NOTE: Text area cannot be auto-focused on iOS when dynamically importing MkPostFormDialog
// NOTE: However, if you do not dynamically import, the MkPostFormDialog instance will be reused,
// Due to the effect that Vue internally creates a property called __props on the passed component,
// Sometimes an error occurs when opening multiple post forms
// Of course, opening multiple post forms is itself a bug on Misskey's side.
let dispose: () => void;
popup(MkPostFormDialog, props, { popup(MkPostFormDialog, props, {
closed: () => { closed: () => {
resolve(); resolve();
dispose(); dispose();
onClosed?.();
}, },
}).then((res) => { }).then((res) => {
dispose = res.dispose; dispose = res.dispose;

View file

@ -176,6 +176,7 @@
import { computed, onMounted, ref, watch } from "vue"; import { computed, onMounted, ref, watch } from "vue";
import { Virtual } from "swiper/modules"; import { Virtual } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import type { Swiper as SwiperType } from "swiper/types";
import XEmojis from "./about.emojis.vue"; import XEmojis from "./about.emojis.vue";
import XFederation from "./about.federation.vue"; import XFederation from "./about.federation.vue";
import { host, version } from "@/config"; import { host, version } from "@/config";
@ -294,19 +295,19 @@ watch(iconSrc, (newValue, oldValue) => {
} }
}); });
let swiperRef = null; let swiperRef: SwiperType | null = null;
function setSwiperRef(swiper) { function setSwiperRef(swiper: SwiperType) {
swiperRef = swiper; swiperRef = swiper;
syncSlide(tabs.indexOf(tab.value)); syncSlide(tabs.indexOf(tab.value));
} }
function onSlideChange() { function onSlideChange() {
tab.value = tabs[swiperRef.activeIndex]; tab.value = tabs[swiperRef!.activeIndex];
} }
function syncSlide(index) { function syncSlide(index: number) {
swiperRef.slideTo(index); swiperRef!.slideTo(index);
} }
</script> </script>

View file

@ -94,14 +94,16 @@
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import MkSelect from "@/components/form/select.vue"; import MkSelect from "@/components/form/select.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import XAbuseReport from "@/components/MkAbuseReport.vue"; import XAbuseReport from "@/components/MkAbuseReport.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import type { entities } from "firefish-js"; import type { entities } from "firefish-js";
const reports = ref<InstanceType<typeof MkPagination>>(); const reports = ref<MkPaginationType<typeof pagination.endpoint> | null>(null);
const state = ref("unresolved"); const state = ref("unresolved");
const reporterOrigin = ref<entities.OriginType>("combined"); const reporterOrigin = ref<entities.OriginType>("combined");

View file

@ -153,7 +153,9 @@
import { computed, defineAsyncComponent, ref } from "vue"; import { computed, defineAsyncComponent, ref } from "vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import MkSwitch from "@/components/form/switch.vue"; import MkSwitch from "@/components/form/switch.vue";
import FormSplit from "@/components/form/split.vue"; import FormSplit from "@/components/form/split.vue";
import { selectFile, selectFiles } from "@/scripts/select-file"; import { selectFile, selectFiles } from "@/scripts/select-file";
@ -162,7 +164,8 @@ import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const emojisPaginationComponent = ref<InstanceType<typeof MkPagination>>(); const emojisPaginationComponent =
ref<MkPaginationType<"admin/emoji/list"> | null>(null);
const tab = ref("local"); const tab = ref("local");
const query = ref(null); const query = ref(null);

View file

@ -126,7 +126,9 @@
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import MkInput from "@/components/form/input.vue"; import MkInput from "@/components/form/input.vue";
import MkSelect from "@/components/form/select.vue"; import MkSelect from "@/components/form/select.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import * as os from "@/os"; import * as os from "@/os";
import { lookupUser } from "@/scripts/lookup-user"; import { lookupUser } from "@/scripts/lookup-user";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -134,7 +136,9 @@ import { definePageMetadata } from "@/scripts/page-metadata";
import MkUserCardMini from "@/components/MkUserCardMini.vue"; import MkUserCardMini from "@/components/MkUserCardMini.vue";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const paginationComponent = ref<InstanceType<typeof MkPagination>>(); const paginationComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
const sort = ref("+createdAt"); const sort = ref("+createdAt");
const state = ref("all"); const state = ref("all");

View file

@ -54,6 +54,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import type { MkPaginationType } from "@/components/MkPagination.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -66,7 +67,7 @@ const pagination = {
limit: 10, limit: 10,
}; };
const paginationEl = ref<InstanceType<typeof MkPagination>>(); const paginationEl = ref<MkPaginationType<"announcements"> | null>(null);
function read(id: string) { function read(id: string) {
if (!paginationEl.value) return; if (!paginationEl.value) return;
paginationEl.value.updateItem(id, (announcement) => { paginationEl.value.updateItem(id, (announcement) => {

View file

@ -37,6 +37,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import type { MkPaginationType } from "@/components/MkPagination.vue";
import XNote from "@/components/MkNote.vue"; import XNote from "@/components/MkNote.vue";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -48,7 +49,9 @@ const pagination = {
limit: 10, limit: 10,
}; };
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
definePageMetadata({ definePageMetadata({
title: i18n.ts.favorites, title: i18n.ts.favorites,

View file

@ -66,14 +66,17 @@
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { acct } from "firefish-js"; import { acct } from "firefish-js";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import type { MkPaginationType } from "@/components/MkPagination.vue";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
import * as os from "@/os"; // import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import { me } from "@/me"; import { me } from "@/me";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const paginationComponent = ref<InstanceType<typeof MkPagination>>(); const paginationComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
const pagination = { const pagination = {
endpoint: "following/requests/sent" as const, endpoint: "following/requests/sent" as const,

View file

@ -85,6 +85,7 @@
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { acct } from "firefish-js"; import { acct } from "firefish-js";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import type { MkPaginationType } from "@/components/MkPagination.vue";
import { userPage } from "@/filters/user"; import { userPage } from "@/filters/user";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -92,7 +93,9 @@ import { definePageMetadata } from "@/scripts/page-metadata";
import { me } from "@/me"; import { me } from "@/me";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const paginationComponent = ref<InstanceType<typeof MkPagination>>(); const paginationComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
const pagination = { const pagination = {
endpoint: "following/requests/list" as const, endpoint: "following/requests/list" as const,
@ -102,13 +105,13 @@ const pagination = {
function accept(user) { function accept(user) {
os.api("following/requests/accept", { userId: user.id }).then(() => { os.api("following/requests/accept", { userId: user.id }).then(() => {
paginationComponent.value.reload(); paginationComponent.value!.reload();
}); });
} }
function reject(user) { function reject(user) {
os.api("following/requests/reject", { userId: user.id }).then(() => { os.api("following/requests/reject", { userId: user.id }).then(() => {
paginationComponent.value.reload(); paginationComponent.value!.reload();
}); });
} }

View file

@ -110,7 +110,7 @@ import { acct } from "firefish-js";
import XMessage from "./messaging-room.message.vue"; import XMessage from "./messaging-room.message.vue";
import XForm from "./messaging-room.form.vue"; import XForm from "./messaging-room.form.vue";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import type { Paging } from "@/components/MkPagination.vue"; import type { MkPaginationType, Paging } from "@/components/MkPagination.vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination from "@/components/MkPagination.vue";
import { import {
isBottomVisible, isBottomVisible,
@ -136,7 +136,9 @@ const stream = useStream();
const rootEl = ref<HTMLDivElement>(); const rootEl = ref<HTMLDivElement>();
const formEl = ref<InstanceType<typeof XForm>>(); const formEl = ref<InstanceType<typeof XForm>>();
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<"messaging/messages"> | null>(
null,
);
const fetching = ref(true); const fetching = ref(true);
const user = ref<entities.UserDetailed | null>(null); const user = ref<entities.UserDetailed | null>(null);

View file

@ -54,7 +54,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onActivated, onDeactivated, ref } from "vue"; import { computed, onActivated, onDeactivated, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, { MkPaginationType } from "@/components/MkPagination.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -70,7 +70,7 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []); const headerTabs = computed(() => []);
const list = ref<typeof MkPagination | null>(null); const list = ref<MkPaginationType<typeof pagination.endpoint> | null>(null);
let isCached = false; let isCached = false;
let refreshTimer: number | null = null; let refreshTimer: number | null = null;

View file

@ -40,7 +40,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
import * as os from "@/os"; import * as os from "@/os";
import { i18n } from "@/i18n"; import { i18n } from "@/i18n";
@ -52,7 +54,9 @@ const pagination = {
limit: 10, limit: 10,
}; };
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
async function create() { async function create() {
const { canceled, result } = await os.form(i18n.ts.createNewClip, { const { canceled, result } = await os.form(i18n.ts.createNewClip, {

View file

@ -44,7 +44,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import MkButton from "@/components/MkButton.vue"; import MkButton from "@/components/MkButton.vue";
import MkAvatars from "@/components/MkAvatars.vue"; import MkAvatars from "@/components/MkAvatars.vue";
import MkInfo from "@/components/MkInfo.vue"; import MkInfo from "@/components/MkInfo.vue";
@ -53,7 +55,9 @@ import { i18n } from "@/i18n";
import { definePageMetadata } from "@/scripts/page-metadata"; import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
const pagination = { const pagination = {
endpoint: "users/lists/list" as const, endpoint: "users/lists/list" as const,

View file

@ -34,7 +34,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import MkPagination from "@/components/MkPagination.vue"; import MkPagination, {
type MkPaginationType,
} from "@/components/MkPagination.vue";
import { api } from "@/os"; import { api } from "@/os";
import XList from "@/components/MkDateSeparatedList.vue"; import XList from "@/components/MkDateSeparatedList.vue";
import XNote from "@/components/MkNote.vue"; import XNote from "@/components/MkNote.vue";
@ -43,7 +45,9 @@ import { definePageMetadata } from "@/scripts/page-metadata";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import type { entities } from "firefish-js"; import type { entities } from "firefish-js";
const pagingComponent = ref<InstanceType<typeof MkPagination>>(); const pagingComponent = ref<MkPaginationType<
typeof pagination.endpoint
> | null>(null);
const props = defineProps<{ const props = defineProps<{
noteId: string; noteId: string;

View file

@ -89,8 +89,9 @@ const usersPagination = {
endpoint: "users/search" as const, endpoint: "users/search" as const,
limit: 10, limit: 10,
params: computed(() => ({ params: computed(() => ({
query: props.query, // FIXME: query is necessary for user search
origin: "combined", query: props.query!,
origin: "combined" as const,
})), })),
}; };

View file

@ -13,16 +13,17 @@ import { getUserMenu } from "@/scripts/get-user-menu";
import icon from "@/scripts/icon"; import icon from "@/scripts/icon";
import { useRouter } from "@/router"; import { useRouter } from "@/router";
import { notePage } from "@/filters/note"; import { notePage } from "@/filters/note";
import type { NoteTranslation } from "@/types/note";
const router = useRouter(); const router = useRouter();
export function getNoteMenu(props: { export function getNoteMenu(props: {
note: entities.Note; note: entities.Note;
menuButton: Ref<HTMLElement | undefined>; menuButton: Ref<HTMLElement | undefined>;
translation: Ref<any>; translation: Ref<NoteTranslation | null>;
translating: Ref<boolean>; translating: Ref<boolean>;
isDeleted: Ref<boolean>; isDeleted: Ref<boolean>;
currentClipPage?: Ref<entities.Clip>; currentClipPage?: Ref<entities.Clip> | null;
}) { }) {
const isRenote = const isRenote =
props.note.renote != null && props.note.renote != null &&

View file

@ -6,7 +6,7 @@ import { isSignedIn, me } from "@/me";
import * as os from "@/os"; import * as os from "@/os";
export function useNoteCapture(props: { export function useNoteCapture(props: {
rootEl: Ref<HTMLElement>; rootEl: Ref<HTMLElement | null>;
note: Ref<entities.Note>; note: Ref<entities.Note>;
isDeletedRef: Ref<boolean>; isDeletedRef: Ref<boolean>;
}) { }) {

View file

@ -3,11 +3,24 @@ import { isSignedIn } from "./me";
import { Storage } from "./pizzax"; import { Storage } from "./pizzax";
import type { NoteVisibility } from "@/types/note"; import type { NoteVisibility } from "@/types/note";
export const postFormActions = []; export const postFormActions: {
export const userActions = []; title: string;
export const noteActions = []; handler: (note: entities.Note) => void | Promise<void>;
export const noteViewInterruptors = []; }[] = [];
export const notePostInterruptors = []; export const userActions: {
title: string;
handler: (note: entities.Note) => void | Promise<void>;
}[] = [];
export const noteActions: {
title: string;
handler: (note: entities.Note) => void | Promise<void>;
}[] = [];
export const noteViewInterruptors: {
handler: (note: entities.Note) => Promise<entities.Note>;
}[] = [];
export const notePostInterruptors: {
handler: (note: entities.Note) => Promise<entities.Note>;
}[] = [];
const menuOptions = [ const menuOptions = [
"notifications", "notifications",
@ -453,6 +466,7 @@ 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

@ -1,3 +1,8 @@
import type { noteVisibilities } from "firefish-js"; import type { noteVisibilities } from "firefish-js";
export type NoteVisibility = (typeof noteVisibilities)[number] | "private"; export type NoteVisibility = (typeof noteVisibilities)[number] | "private";
export type NoteTranslation = {
sourceLang: string;
text: string;
};

View file

@ -36,6 +36,7 @@ import type {
UserDetailed, UserDetailed,
UserGroup, UserGroup,
UserList, UserList,
UserLite,
UserSorting, UserSorting,
} from "./entities"; } from "./entities";
@ -686,7 +687,14 @@ export type Endpoints = {
res: Note[]; res: Note[];
}; };
"notes/clips": { req: TODO; res: TODO }; "notes/clips": { req: TODO; res: TODO };
"notes/conversation": { req: TODO; res: TODO }; "notes/conversation": {
req: {
noteId: string;
limit?: number;
offset?: number;
};
res: Note[];
};
"notes/create": { "notes/create": {
req: NoteSubmitReq; req: NoteSubmitReq;
res: { createdNote: Note }; res: { createdNote: Note };
@ -789,7 +797,24 @@ export type Endpoints = {
res: Note[]; res: Note[];
}; };
"notes/search-by-tag": { req: TODO; res: TODO }; "notes/search-by-tag": { req: TODO; res: TODO };
"notes/search": { req: TODO; res: TODO }; "notes/search": {
req: {
query: string;
sinceId?: string;
untilId?: string;
sinceDate?: number;
untilDate?: number;
limit?: number;
offset?: number;
host?: string;
userId?: string;
withFiles?: boolean;
searchCwAndAlt?: boolean;
channelId?: string;
order?: "chronological" | "relevancy";
};
res: Note[];
};
"notes/show": { req: { noteId: Note["id"] }; res: Note }; "notes/show": { req: { noteId: Note["id"] }; res: Note };
"notes/state": { req: TODO; res: TODO }; "notes/state": { req: TODO; res: TODO };
"notes/timeline": { "notes/timeline": {
@ -802,6 +827,16 @@ export type Endpoints = {
}; };
res: Note[]; res: Note[];
}; };
"notes/translate": {
req: {
noteId: string;
targetLang: string;
};
res: {
sourceLang: string;
text: string;
};
};
"notes/unrenote": { req: { noteId: Note["id"] }; res: null }; "notes/unrenote": { req: { noteId: Note["id"] }; res: null };
"notes/user-list-timeline": { "notes/user-list-timeline": {
req: { req: {
@ -972,7 +1007,16 @@ export type Endpoints = {
"users/relation": { req: TODO; res: TODO }; "users/relation": { req: TODO; res: TODO };
"users/report-abuse": { req: TODO; res: TODO }; "users/report-abuse": { req: TODO; res: TODO };
"users/search-by-username-and-host": { req: TODO; res: TODO }; "users/search-by-username-and-host": { req: TODO; res: TODO };
"users/search": { req: TODO; res: TODO }; "users/search": {
req: {
query: string;
offset?: number;
limit?: number;
origin?: "local" | "remote" | "combined";
detail?: true; // FIXME: when false, returns UserLite
};
res: UserDetailed[];
};
"users/show": { "users/show": {
req: ShowUserReq | { userIds: User["id"][] }; req: ShowUserReq | { userIds: User["id"][] };
res: { res: {

View file

@ -19,14 +19,7 @@ export type UserLite = {
alsoKnownAs: string[]; alsoKnownAs: string[];
movedToUri: any; movedToUri: any;
emojis: EmojiLite[]; emojis: EmojiLite[];
instance?: { instance?: InstanceLite;
name: Instance["name"];
softwareName: Instance["softwareName"];
softwareVersion: Instance["softwareVersion"];
iconUrl: Instance["iconUrl"];
faviconUrl: Instance["faviconUrl"];
themeColor: Instance["themeColor"];
};
}; };
export type UserDetailed = UserLite & { export type UserDetailed = UserLite & {
@ -556,6 +549,15 @@ export type Blocking = {
blockee: UserDetailed; blockee: UserDetailed;
}; };
export type InstanceLite = {
name: Instance["name"];
softwareName: Instance["softwareName"];
softwareVersion: Instance["softwareVersion"];
iconUrl: Instance["iconUrl"];
faviconUrl: Instance["faviconUrl"];
themeColor: Instance["themeColor"];
};
export type Instance = { export type Instance = {
id: ID; id: ID;
caughtAt: DateString; caughtAt: DateString;