Merge branch 'refactor/lists' into 'develop'
refactor: Fix types of components Co-authored-by: Lhcfl <Lhcfl@outlook.com> See merge request firefish/firefish!10730
This commit is contained in:
commit
cb7e9ab449
45 changed files with 608 additions and 420 deletions
24
biome.json
24
biome.json
|
@ -1,12 +1,30 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://biomejs.dev/schemas/1.0.0/schema.json",
|
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true
|
"recommended": true,
|
||||||
|
"style": {
|
||||||
|
"noUselessElse": "off"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"include": ["*.vue"],
|
||||||
|
"linter": {
|
||||||
|
"rules": {
|
||||||
|
"style": {
|
||||||
|
"useImportType": "warn",
|
||||||
|
"useShorthandFunctionType": "warn",
|
||||||
|
"useTemplate": "warn",
|
||||||
|
"noNonNullAssertion": "off"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"caughtErrorsIgnorePattern": "^_",
|
"caughtErrorsIgnorePattern": "^_",
|
||||||
"destructuredArrayIgnorePattern": "^_"
|
"destructuredArrayIgnorePattern": "^_"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"vue/no-setup-props-destructure": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"build:debug": "pnpm run build",
|
"build:debug": "pnpm run build",
|
||||||
"lint": "pnpm biome check **/*.ts --apply ; pnpm run lint:vue",
|
"lint": "pnpm biome check **/*.ts --apply ; pnpm run lint:vue",
|
||||||
"lint:vue": "pnpm eslint src --fix '**/*.vue' --cache ; pnpm run format",
|
"lint:vue": "pnpm eslint src --fix '**/*.vue' --cache ; pnpm run format",
|
||||||
|
"types:check": "pnpm vue-tsc --noEmit",
|
||||||
"format": "pnpm biome format * --write"
|
"format": "pnpm biome format * --write"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -86,6 +87,7 @@
|
||||||
"vue": "3.4.21",
|
"vue": "3.4.21",
|
||||||
"vue-draggable-plus": "^0.3.5",
|
"vue-draggable-plus": "^0.3.5",
|
||||||
"vue-plyr": "^7.0.0",
|
"vue-plyr": "^7.0.0",
|
||||||
"vue-prism-editor": "2.0.0-alpha.2"
|
"vue-prism-editor": "2.0.0-alpha.2",
|
||||||
|
"vue-tsc": "2.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,14 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import MkChannelPreview from "@/components/MkChannelPreview.vue";
|
import MkChannelPreview from "@/components/MkChannelPreview.vue";
|
||||||
import type { Paging } from "@/components/MkPagination.vue";
|
import type { 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";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pagination: Paging;
|
pagination: PagingOf<entities.Channel>;
|
||||||
noGap?: boolean;
|
noGap?: boolean;
|
||||||
extractor?: (item: any) => any;
|
extractor?: (item: any) => any;
|
||||||
}>(),
|
}>(),
|
||||||
|
|
|
@ -1,130 +1,83 @@
|
||||||
<script lang="ts">
|
<template>
|
||||||
import type { PropType } from "vue";
|
<component
|
||||||
import { TransitionGroup, defineComponent, h } from "vue";
|
:is="defaultStore.state.animation? TransitionGroup : 'div'"
|
||||||
|
tag="div"
|
||||||
|
class="sqadhkmv"
|
||||||
|
name="list"
|
||||||
|
:class="{ noGap }"
|
||||||
|
:data-direction = "props.direction"
|
||||||
|
:data-reversed = "props.reversed ? 'true' : 'false'"
|
||||||
|
>
|
||||||
|
<template v-for="(item, index) in items" :key="item.id">
|
||||||
|
<slot :item="item"> </slot>
|
||||||
|
<div
|
||||||
|
v-if="index !== items.length - 1 &&
|
||||||
|
new Date(item.createdAt).getDate() !==
|
||||||
|
new Date(items[index + 1].createdAt).getDate()"
|
||||||
|
class="separator"
|
||||||
|
>
|
||||||
|
<p class="date">
|
||||||
|
<span>
|
||||||
|
<i class="icon" :class="icon('ph-caret-up')"></i>
|
||||||
|
{{ getDateText(item.createdAt) }}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{{ getDateText(items[index + 1].createdAt) }}
|
||||||
|
<i class="icon" :class="icon('ph-caret-down')"></i>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<!-- class="a" means advertise -->
|
||||||
|
<MkAd
|
||||||
|
v-else-if="ad && item._shouldInsertAd_"
|
||||||
|
class="a"
|
||||||
|
:prefer="['inline', 'inline-big']"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup generic="T extends Item">
|
||||||
|
import { TransitionGroup } from "vue";
|
||||||
import MkAd from "@/components/global/MkAd.vue";
|
import MkAd from "@/components/global/MkAd.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
|
||||||
export default defineComponent({
|
export interface Item {
|
||||||
props: {
|
id: string;
|
||||||
items: {
|
createdAt: string;
|
||||||
type: Array as PropType<
|
_shouldInsertAd_?: boolean;
|
||||||
{ id: string; createdAt: string; _shouldInsertAd_?: boolean }[]
|
}
|
||||||
>,
|
|
||||||
required: true,
|
const props = withDefaults(
|
||||||
},
|
defineProps<{
|
||||||
direction: {
|
items: T[];
|
||||||
type: String,
|
direction?: string;
|
||||||
required: false,
|
reversed?: boolean;
|
||||||
default: "down",
|
noGap?: boolean;
|
||||||
},
|
ad?: boolean;
|
||||||
reversed: {
|
}>(),
|
||||||
type: Boolean,
|
{
|
||||||
required: false,
|
direction: "down",
|
||||||
default: false,
|
reversed: false,
|
||||||
},
|
noGap: false,
|
||||||
noGap: {
|
ad: false,
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
ad: {
|
|
||||||
type: Boolean,
|
|
||||||
required: false,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
|
||||||
setup(props, { slots, expose }) {
|
const slots = defineSlots<{
|
||||||
function getDateText(time: string) {
|
default(props: { item: T }): unknown;
|
||||||
const date = new Date(time).getDate();
|
}>();
|
||||||
const month = new Date(time).getMonth() + 1;
|
|
||||||
return i18n.t("monthAndDay", {
|
|
||||||
month: month.toString(),
|
|
||||||
day: date.toString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.items.length === 0) return;
|
function getDateText(time: string) {
|
||||||
|
const date = new Date(time).getDate();
|
||||||
const renderChildren = () =>
|
const month = new Date(time).getMonth() + 1;
|
||||||
props.items.map((item, i) => {
|
return i18n.t("monthAndDay", {
|
||||||
if (!slots || !slots.default) return;
|
month: month.toString(),
|
||||||
|
day: date.toString(),
|
||||||
const el = slots.default({
|
});
|
||||||
item,
|
}
|
||||||
})[0];
|
|
||||||
if (el.key == null && item.id) el.key = item.id;
|
|
||||||
|
|
||||||
if (
|
|
||||||
i !== props.items.length - 1 &&
|
|
||||||
new Date(item.createdAt).getDate() !==
|
|
||||||
new Date(props.items[i + 1].createdAt).getDate()
|
|
||||||
) {
|
|
||||||
const separator = h(
|
|
||||||
"div",
|
|
||||||
{
|
|
||||||
class: "separator",
|
|
||||||
key: item.id + ":separator",
|
|
||||||
},
|
|
||||||
h(
|
|
||||||
"p",
|
|
||||||
{
|
|
||||||
class: "date",
|
|
||||||
},
|
|
||||||
[
|
|
||||||
h("span", [
|
|
||||||
h("i", {
|
|
||||||
class: `${icon("ph-caret-up")} icon`,
|
|
||||||
}),
|
|
||||||
getDateText(item.createdAt),
|
|
||||||
]),
|
|
||||||
h("span", [
|
|
||||||
getDateText(props.items[i + 1].createdAt),
|
|
||||||
h("i", {
|
|
||||||
class: `${icon("ph-caret-down")} icon`,
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return [el, separator];
|
|
||||||
} else {
|
|
||||||
if (props.ad && item._shouldInsertAd_) {
|
|
||||||
return [
|
|
||||||
h(MkAd, {
|
|
||||||
class: "a", // advertiseの意(ブロッカー対策)
|
|
||||||
key: item.id + ":ad",
|
|
||||||
prefer: ["inline", "inline-big"],
|
|
||||||
}),
|
|
||||||
el,
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return el;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () =>
|
|
||||||
h(
|
|
||||||
defaultStore.state.animation ? TransitionGroup : "div",
|
|
||||||
defaultStore.state.animation
|
|
||||||
? {
|
|
||||||
class: "sqadhkmv" + (props.noGap ? " noGap" : ""),
|
|
||||||
name: "list",
|
|
||||||
tag: "div",
|
|
||||||
"data-direction": props.direction,
|
|
||||||
"data-reversed": props.reversed ? "true" : "false",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
class: "sqadhkmv" + (props.noGap ? " noGap" : ""),
|
|
||||||
},
|
|
||||||
{ default: renderChildren },
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MkPagination
|
<MkPagination
|
||||||
v-slot="{ items }"
|
v-slot="{ items }: { items: entities.DriveFile[]}"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
class="urempief"
|
class="urempief"
|
||||||
:class="{ grid: viewMode === 'grid' }"
|
:class="{ grid: viewMode === 'grid' }"
|
||||||
|
@ -53,13 +53,15 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { acct } from "firefish-js";
|
import { acct } from "firefish-js";
|
||||||
|
import type { entities } from "firefish-js";
|
||||||
import MkPagination from "@/components/MkPagination.vue";
|
import MkPagination from "@/components/MkPagination.vue";
|
||||||
|
import type { PagingOf } from "@/components/MkPagination.vue";
|
||||||
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
|
import MkDriveFileThumbnail from "@/components/MkDriveFileThumbnail.vue";
|
||||||
import bytes from "@/filters/bytes";
|
import bytes from "@/filters/bytes";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pagination: any;
|
pagination: PagingOf<entities.DriveFile>;
|
||||||
viewMode: "grid" | "list";
|
viewMode: "grid" | "list";
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -40,17 +40,18 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import type { Paging } from "@/components/MkPagination.vue";
|
import type { 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";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { scroll } from "@/scripts/scroll";
|
import { scroll } from "@/scripts/scroll";
|
||||||
|
import type { entities } from "firefish-js";
|
||||||
|
|
||||||
const tlEl = ref<HTMLElement>();
|
const tlEl = ref<HTMLElement>();
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pagination: Paging;
|
pagination: PagingOf<entities.Note>;
|
||||||
noGap?: boolean;
|
noGap?: boolean;
|
||||||
disableAutoLoad?: boolean;
|
disableAutoLoad?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
|
@ -19,12 +19,8 @@
|
||||||
:no-gap="true"
|
:no-gap="true"
|
||||||
>
|
>
|
||||||
<XNote
|
<XNote
|
||||||
v-if="
|
v-if="isNoteNotification(notification)"
|
||||||
['reply', 'quote', 'mention'].includes(
|
:key="'nn-' + notification.id"
|
||||||
notification.type,
|
|
||||||
)
|
|
||||||
"
|
|
||||||
:key="notification.id"
|
|
||||||
:note="notification.note"
|
:note="notification.note"
|
||||||
:collapsed-reply="
|
:collapsed-reply="
|
||||||
notification.type === 'reply' ||
|
notification.type === 'reply' ||
|
||||||
|
@ -34,7 +30,7 @@
|
||||||
/>
|
/>
|
||||||
<XNotification
|
<XNotification
|
||||||
v-else
|
v-else
|
||||||
:key="notification.id"
|
:key="'n-' + notification.id"
|
||||||
:notification="notification"
|
:notification="notification"
|
||||||
:with-time="true"
|
:with-time="true"
|
||||||
:full="true"
|
:full="true"
|
||||||
|
@ -47,8 +43,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||||
import type { notificationTypes } from "firefish-js";
|
import type { StreamTypes, entities, notificationTypes } from "firefish-js";
|
||||||
import type { Paging } from "@/components/MkPagination.vue";
|
|
||||||
import MkPagination from "@/components/MkPagination.vue";
|
import MkPagination 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";
|
||||||
|
@ -66,20 +61,29 @@ const stream = useStream();
|
||||||
|
|
||||||
const pagingComponent = ref<InstanceType<typeof MkPagination>>();
|
const pagingComponent = ref<InstanceType<typeof MkPagination>>();
|
||||||
|
|
||||||
const pagination: Paging = {
|
const pagination = {
|
||||||
endpoint: "i/notifications" as const,
|
endpoint: "i/notifications" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: computed(() => ({
|
params: computed(() => ({
|
||||||
includeTypes: props.includeTypes ?? undefined,
|
includeTypes: props.includeTypes ?? undefined,
|
||||||
excludeTypes: props.includeTypes ? undefined : me.mutingNotificationTypes,
|
excludeTypes: props.includeTypes ? undefined : me?.mutingNotificationTypes,
|
||||||
unreadOnly: props.unreadOnly,
|
unreadOnly: props.unreadOnly,
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const onNotification = (notification) => {
|
function isNoteNotification(
|
||||||
|
n: entities.Notification,
|
||||||
|
): n is
|
||||||
|
| entities.ReplyNotification
|
||||||
|
| entities.QuoteNotification
|
||||||
|
| entities.MentionNotification {
|
||||||
|
return n.type === "reply" || n.type === "quote" || n.type === "mention";
|
||||||
|
}
|
||||||
|
|
||||||
|
const onNotification = (notification: entities.Notification) => {
|
||||||
const isMuted = props.includeTypes
|
const isMuted = props.includeTypes
|
||||||
? !props.includeTypes.includes(notification.type)
|
? !props.includeTypes.includes(notification.type)
|
||||||
: me.mutingNotificationTypes.includes(notification.type);
|
: me?.mutingNotificationTypes.includes(notification.type);
|
||||||
if (isMuted || document.visibilityState === "visible") {
|
if (isMuted || document.visibilityState === "visible") {
|
||||||
stream.send("readNotification", {
|
stream.send("readNotification", {
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
|
@ -94,7 +98,7 @@ const onNotification = (notification) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let connection;
|
let connection: StreamTypes.ChannelOf<"main"> | undefined;
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
connection = stream.useChannel("main");
|
connection = stream.useChannel("main");
|
||||||
|
|
|
@ -66,10 +66,10 @@
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup generic="E extends PagingKey">
|
||||||
import type { ComputedRef } from "vue";
|
import type { ComputedRef } from "vue";
|
||||||
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
|
import { computed, isRef, onActivated, onDeactivated, ref, watch } from "vue";
|
||||||
import type { Endpoints } from "firefish-js";
|
import type { Endpoints, TypeUtils } from "firefish-js";
|
||||||
import * as os from "@/os";
|
import * as os from "@/os";
|
||||||
import {
|
import {
|
||||||
getScrollContainer,
|
getScrollContainer,
|
||||||
|
@ -81,7 +81,10 @@ import MkButton from "@/components/MkButton.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
|
||||||
export interface Paging<E extends keyof Endpoints = keyof Endpoints> {
|
// biome-ignore lint/suspicious/noExplicitAny: Used Intentionally
|
||||||
|
export type PagingKey = TypeUtils.EndpointsOf<any[]>;
|
||||||
|
|
||||||
|
export interface Paging<E extends PagingKey = PagingKey> {
|
||||||
endpoint: E;
|
endpoint: E;
|
||||||
limit: number;
|
limit: number;
|
||||||
params?: Endpoints[E]["req"] | ComputedRef<Endpoints[E]["req"]>;
|
params?: Endpoints[E]["req"] | ComputedRef<Endpoints[E]["req"]>;
|
||||||
|
@ -100,11 +103,13 @@ export interface Paging<E extends keyof Endpoints = keyof Endpoints> {
|
||||||
offsetMode?: boolean;
|
offsetMode?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type PagingOf<T> = Paging<TypeUtils.EndpointsOf<T[]>>;
|
||||||
|
|
||||||
const SECOND_FETCH_LIMIT = 30;
|
const SECOND_FETCH_LIMIT = 30;
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
pagination: Paging;
|
pagination: Paging<E>;
|
||||||
disableAutoLoad?: boolean;
|
disableAutoLoad?: boolean;
|
||||||
displayLimit?: number;
|
displayLimit?: number;
|
||||||
}>(),
|
}>(),
|
||||||
|
@ -113,14 +118,18 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const slots = defineSlots<{
|
||||||
(ev: "queue", count: number): void;
|
default(props: { items: Item[] }): unknown;
|
||||||
(ev: "status", error: boolean): void;
|
empty(props: Record<string, never>): never;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
type Item = Endpoints[typeof props.pagination.endpoint]["res"] & {
|
const emit = defineEmits<{
|
||||||
id: string;
|
(ev: "queue", count: number): void;
|
||||||
};
|
(ev: "status", hasError: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
type Param = Endpoints[E]["req"] | Record<string, never>;
|
||||||
|
type Item = Endpoints[E]["res"][number];
|
||||||
|
|
||||||
const rootEl = ref<HTMLElement>();
|
const rootEl = ref<HTMLElement>();
|
||||||
const items = ref<Item[]>([]);
|
const items = ref<Item[]>([]);
|
||||||
|
@ -137,8 +146,9 @@ const error = ref(false);
|
||||||
const init = async (): Promise<void> => {
|
const init = async (): Promise<void> => {
|
||||||
queue.value = [];
|
queue.value = [];
|
||||||
fetching.value = true;
|
fetching.value = true;
|
||||||
|
|
||||||
const params = props.pagination.params
|
const params = props.pagination.params
|
||||||
? isRef(props.pagination.params)
|
? isRef<Param>(props.pagination.params)
|
||||||
? props.pagination.params.value
|
? props.pagination.params.value
|
||||||
: props.pagination.params
|
: props.pagination.params
|
||||||
: {};
|
: {};
|
||||||
|
@ -150,7 +160,7 @@ const init = async (): Promise<void> => {
|
||||||
: (props.pagination.limit || 10) + 1,
|
: (props.pagination.limit || 10) + 1,
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res: Item[]) => {
|
||||||
for (let i = 0; i < res.length; i++) {
|
for (let i = 0; i < res.length; i++) {
|
||||||
const item = res[i];
|
const item = res[i];
|
||||||
if (props.pagination.reversed) {
|
if (props.pagination.reversed) {
|
||||||
|
@ -174,7 +184,7 @@ const init = async (): Promise<void> => {
|
||||||
error.value = false;
|
error.value = false;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
},
|
},
|
||||||
(err) => {
|
(_err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
},
|
},
|
||||||
|
@ -188,7 +198,7 @@ const reload = (): Promise<void> => {
|
||||||
|
|
||||||
const refresh = async (): Promise<void> => {
|
const refresh = async (): Promise<void> => {
|
||||||
const params = props.pagination.params
|
const params = props.pagination.params
|
||||||
? isRef(props.pagination.params)
|
? isRef<Param>(props.pagination.params)
|
||||||
? props.pagination.params.value
|
? props.pagination.params.value
|
||||||
: props.pagination.params
|
: props.pagination.params
|
||||||
: {};
|
: {};
|
||||||
|
@ -199,7 +209,7 @@ const refresh = async (): Promise<void> => {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res: Item[]) => {
|
||||||
const ids = items.value.reduce(
|
const ids = items.value.reduce(
|
||||||
(a, b) => {
|
(a, b) => {
|
||||||
a[b.id] = true;
|
a[b.id] = true;
|
||||||
|
@ -210,7 +220,7 @@ const refresh = async (): Promise<void> => {
|
||||||
|
|
||||||
for (let i = 0; i < res.length; i++) {
|
for (let i = 0; i < res.length; i++) {
|
||||||
const item = res[i];
|
const item = res[i];
|
||||||
if (!updateItem(item.id, (old) => item)) {
|
if (!updateItem(item.id, (_old) => item)) {
|
||||||
append(item);
|
append(item);
|
||||||
}
|
}
|
||||||
delete ids[item.id];
|
delete ids[item.id];
|
||||||
|
@ -220,7 +230,7 @@ const refresh = async (): Promise<void> => {
|
||||||
removeItem((i) => i.id === id);
|
removeItem((i) => i.id === id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err) => {
|
(_err) => {
|
||||||
error.value = true;
|
error.value = true;
|
||||||
fetching.value = false;
|
fetching.value = false;
|
||||||
},
|
},
|
||||||
|
@ -238,7 +248,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
backed.value = true;
|
backed.value = true;
|
||||||
const params = props.pagination.params
|
const params = props.pagination.params
|
||||||
? isRef(props.pagination.params)
|
? isRef<Param>(props.pagination.params)
|
||||||
? props.pagination.params.value
|
? props.pagination.params.value
|
||||||
: props.pagination.params
|
: props.pagination.params
|
||||||
: {};
|
: {};
|
||||||
|
@ -259,7 +269,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res: Item[]) => {
|
||||||
for (let i = 0; i < res.length; i++) {
|
for (let i = 0; i < res.length; i++) {
|
||||||
const item = res[i];
|
const item = res[i];
|
||||||
if (props.pagination.reversed) {
|
if (props.pagination.reversed) {
|
||||||
|
@ -283,7 +293,7 @@ const fetchMore = async (): Promise<void> => {
|
||||||
offset.value += res.length;
|
offset.value += res.length;
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
},
|
},
|
||||||
(err) => {
|
(_err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -299,7 +309,7 @@ const fetchMoreAhead = async (): Promise<void> => {
|
||||||
return;
|
return;
|
||||||
moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
const params = props.pagination.params
|
const params = props.pagination.params
|
||||||
? isRef(props.pagination.params)
|
? isRef<Param>(props.pagination.params)
|
||||||
? props.pagination.params.value
|
? props.pagination.params.value
|
||||||
: props.pagination.params
|
: props.pagination.params
|
||||||
: {};
|
: {};
|
||||||
|
@ -320,7 +330,7 @@ const fetchMoreAhead = async (): Promise<void> => {
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(
|
.then(
|
||||||
(res) => {
|
(res: Item[]) => {
|
||||||
if (res.length > SECOND_FETCH_LIMIT) {
|
if (res.length > SECOND_FETCH_LIMIT) {
|
||||||
res.pop();
|
res.pop();
|
||||||
items.value = props.pagination.reversed
|
items.value = props.pagination.reversed
|
||||||
|
@ -336,7 +346,7 @@ const fetchMoreAhead = async (): Promise<void> => {
|
||||||
offset.value += res.length;
|
offset.value += res.length;
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
},
|
},
|
||||||
(err) => {
|
(_err) => {
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -428,7 +438,7 @@ const updateItem = (id: Item["id"], replacer: (old: Item) => Item): boolean => {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (props.pagination.params && isRef(props.pagination.params)) {
|
if (props.pagination.params && isRef<Param>(props.pagination.params)) {
|
||||||
watch(props.pagination.params, init, { deep: true });
|
watch(props.pagination.params, init, { deep: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onUnmounted, provide, ref } from "vue";
|
import { computed, onUnmounted, provide, ref } from "vue";
|
||||||
import type { Endpoints } from "firefish-js";
|
import type { entities, StreamTypes } from "firefish-js";
|
||||||
import MkPullToRefresh from "@/components/MkPullToRefresh.vue";
|
import MkPullToRefresh from "@/components/MkPullToRefresh.vue";
|
||||||
import XNotes from "@/components/MkNotes.vue";
|
import XNotes from "@/components/MkNotes.vue";
|
||||||
import MkInfo from "@/components/MkInfo.vue";
|
import MkInfo from "@/components/MkInfo.vue";
|
||||||
|
@ -54,10 +54,23 @@ import { isSignedIn, me } from "@/me";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
import type { Paging } from "@/components/MkPagination.vue";
|
import type { EndpointsOf } from "@/components/MkPagination.vue";
|
||||||
|
|
||||||
|
export type TimelineSource =
|
||||||
|
| "antenna"
|
||||||
|
| "home"
|
||||||
|
| "local"
|
||||||
|
| "recommended"
|
||||||
|
| "social"
|
||||||
|
| "global"
|
||||||
|
| "mentions"
|
||||||
|
| "directs"
|
||||||
|
| "list"
|
||||||
|
| "channel"
|
||||||
|
| "file";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
src: string;
|
src: TimelineSource;
|
||||||
list?: string;
|
list?: string;
|
||||||
antenna?: string;
|
antenna?: string;
|
||||||
channel?: string;
|
channel?: string;
|
||||||
|
@ -73,7 +86,7 @@ const emit = defineEmits<{
|
||||||
const tlComponent = ref<InstanceType<typeof XNotes>>();
|
const tlComponent = ref<InstanceType<typeof XNotes>>();
|
||||||
const pullToRefreshComponent = ref<InstanceType<typeof MkPullToRefresh>>();
|
const pullToRefreshComponent = ref<InstanceType<typeof MkPullToRefresh>>();
|
||||||
|
|
||||||
let endpoint = ""; // keyof Endpoints
|
let endpoint: EndpointsOf<entities.Note[]>; // keyof Endpoints
|
||||||
let query: {
|
let query: {
|
||||||
antennaId?: string | undefined;
|
antennaId?: string | undefined;
|
||||||
withReplies?: boolean;
|
withReplies?: boolean;
|
||||||
|
@ -81,14 +94,19 @@ let query: {
|
||||||
listId?: string | undefined;
|
listId?: string | undefined;
|
||||||
channelId?: string | undefined;
|
channelId?: string | undefined;
|
||||||
fileId?: string | undefined;
|
fileId?: string | undefined;
|
||||||
};
|
} = {};
|
||||||
let connection: {
|
|
||||||
on: (
|
// FIXME: The type defination is wrong here, need fix
|
||||||
arg0: string,
|
let connection:
|
||||||
arg1: { (note: any): void; (note: any): void; (note: any): void },
|
| StreamTypes.ChannelOf<"antenna">
|
||||||
) => void;
|
| StreamTypes.ChannelOf<"homeTimeline">
|
||||||
dispose: () => void;
|
| StreamTypes.ChannelOf<"recommendedTimeline">
|
||||||
};
|
| StreamTypes.ChannelOf<"hybridTimeline">
|
||||||
|
| StreamTypes.ChannelOf<"globalTimeline">
|
||||||
|
| StreamTypes.ChannelOf<"main">
|
||||||
|
| StreamTypes.ChannelOf<"userList">
|
||||||
|
| StreamTypes.ChannelOf<"channel">;
|
||||||
|
|
||||||
let connection2: { dispose: () => void } | null;
|
let connection2: { dispose: () => void } | null;
|
||||||
|
|
||||||
let tlHint: string;
|
let tlHint: string;
|
||||||
|
@ -96,14 +114,14 @@ let tlHintClosed: boolean;
|
||||||
let tlNotesCount = 0;
|
let tlNotesCount = 0;
|
||||||
const queue = ref(0);
|
const queue = ref(0);
|
||||||
|
|
||||||
const prepend = (note) => {
|
const prepend = (note: entities.Note) => {
|
||||||
tlNotesCount++;
|
tlNotesCount++;
|
||||||
tlComponent.value?.pagingComponent?.prepend(note);
|
tlComponent.value?.pagingComponent?.prepend(note);
|
||||||
|
|
||||||
emit("note");
|
emit("note");
|
||||||
|
|
||||||
if (props.sound) {
|
if (props.sound) {
|
||||||
sound.play(isSignedIn && note.userId === me.id ? "noteMy" : "note");
|
sound.play(isSignedIn && note.userId === me?.id ? "noteMy" : "note");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,6 +187,8 @@ if (props.src === "antenna") {
|
||||||
query = {
|
query = {
|
||||||
fileId: props.fileId,
|
fileId: props.fileId,
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
throw "NoEndpointError";
|
||||||
}
|
}
|
||||||
|
|
||||||
const stream = useStream();
|
const stream = useStream();
|
||||||
|
@ -194,8 +214,9 @@ function connectChannel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.src === "antenna") {
|
if (props.src === "antenna") {
|
||||||
|
if (!props.antenna) throw "NoAntennaProvided";
|
||||||
connection = stream.useChannel("antenna", {
|
connection = stream.useChannel("antenna", {
|
||||||
antennaId: props.antenna!,
|
antennaId: props.antenna,
|
||||||
});
|
});
|
||||||
} else if (props.src === "home") {
|
} else if (props.src === "home") {
|
||||||
connection = stream.useChannel("homeTimeline", {
|
connection = stream.useChannel("homeTimeline", {
|
||||||
|
@ -272,8 +293,8 @@ function reloadTimeline() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const pagination: Paging = {
|
const pagination = {
|
||||||
endpoint: endpoint as keyof Endpoints,
|
endpoint,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: query,
|
params: query,
|
||||||
};
|
};
|
||||||
|
|
|
@ -36,7 +36,7 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", v: boolean): void;
|
"update:modelValue": [v: boolean];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const button = ref<HTMLElement>();
|
const button = ref<HTMLElement>();
|
||||||
|
@ -46,9 +46,9 @@ const toggle = () => {
|
||||||
emit("update:modelValue", !checked.value);
|
emit("update:modelValue", !checked.value);
|
||||||
|
|
||||||
if (!checked.value) {
|
if (!checked.value) {
|
||||||
const rect = button.value.getBoundingClientRect();
|
const rect = button.value!.getBoundingClientRect();
|
||||||
const x = rect.left + button.value.offsetWidth / 2;
|
const x = rect.left + button.value!.offsetWidth / 2;
|
||||||
const y = rect.top + button.value.offsetHeight / 2;
|
const y = rect.top + button.value!.offsetHeight / 2;
|
||||||
os.popup(Ripple, { x, y, particle: false }, {}, "end");
|
os.popup(Ripple, { x, y, particle: false }, {}, "end");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
/>
|
/>
|
||||||
<datalist v-if="datalist" :id="id">
|
<datalist v-if="datalist" :id="id">
|
||||||
<option v-for="data in datalist" :value="data" />
|
<option v-for="data in datalist" :key="data" :value="data" />
|
||||||
</datalist>
|
</datalist>
|
||||||
<div ref="suffixEl" class="suffix">
|
<div ref="suffixEl" class="suffix">
|
||||||
<slot name="suffix"></slot>
|
<slot name="suffix"></slot>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { nextTick, onMounted, ref, toRefs, watch } from "vue";
|
import { nextTick, onMounted, ref, toRefs, watch } from "vue";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce as Debounce } from "throttle-debounce";
|
||||||
import MkButton from "@/components/MkButton.vue";
|
import MkButton from "@/components/MkButton.vue";
|
||||||
import { useInterval } from "@/scripts/use-interval";
|
import { useInterval } from "@/scripts/use-interval";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
|
@ -72,7 +72,7 @@ const props = defineProps<{
|
||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
autocomplete?: string;
|
autocomplete?: string;
|
||||||
spellcheck?: boolean;
|
spellcheck?: boolean;
|
||||||
step?: any;
|
step?: number | string;
|
||||||
datalist?: string[];
|
datalist?: string[];
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
debounce?: boolean;
|
debounce?: boolean;
|
||||||
|
@ -82,10 +82,10 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "change", _ev: KeyboardEvent): void;
|
(ev: "change", _ev: Event): void;
|
||||||
(ev: "keydown", _ev: KeyboardEvent): void;
|
(ev: "keydown", _ev: KeyboardEvent): void;
|
||||||
(ev: "enter"): void;
|
(ev: "enter"): void;
|
||||||
(ev: "update:modelValue", value: string | number): void;
|
(ev: "update:modelValue", value: string | number | null): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { modelValue, type, autofocus } = toRefs(props);
|
const { modelValue, type, autofocus } = toRefs(props);
|
||||||
|
@ -94,14 +94,15 @@ const id = Math.random().toString(); // TODO: uuid?
|
||||||
const focused = ref(false);
|
const focused = ref(false);
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
const invalid = ref(false);
|
const invalid = ref(false);
|
||||||
const inputEl = ref<HTMLElement>();
|
const inputEl = ref<HTMLInputElement>();
|
||||||
const prefixEl = ref<HTMLElement>();
|
const prefixEl = ref<HTMLElement>();
|
||||||
const suffixEl = ref<HTMLElement>();
|
const suffixEl = ref<HTMLElement>();
|
||||||
const height = props.small ? 36 : props.large ? 40 : 38;
|
const height = props.small ? 36 : props.large ? 40 : 38;
|
||||||
|
|
||||||
const focus = () => inputEl.value.focus();
|
const focus = () => inputEl.value!.focus();
|
||||||
const selectRange = (start, end) => inputEl.value.setSelectionRange(start, end);
|
const selectRange = (start, end) =>
|
||||||
const onInput = (ev: KeyboardEvent) => {
|
inputEl.value!.setSelectionRange(start, end);
|
||||||
|
const onInput = (ev: Event) => {
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
emit("change", ev);
|
emit("change", ev);
|
||||||
};
|
};
|
||||||
|
@ -116,13 +117,13 @@ const onKeydown = (ev: KeyboardEvent) => {
|
||||||
const updated = () => {
|
const updated = () => {
|
||||||
changed.value = false;
|
changed.value = false;
|
||||||
if (type.value === "number") {
|
if (type.value === "number") {
|
||||||
emit("update:modelValue", parseFloat(v.value));
|
emit("update:modelValue", Number.parseFloat(v.value as string));
|
||||||
} else {
|
} else {
|
||||||
emit("update:modelValue", v.value);
|
emit("update:modelValue", v.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const debouncedUpdated = debounce(1000, updated);
|
const debouncedUpdated = Debounce(1000, updated);
|
||||||
|
|
||||||
watch(modelValue, (newValue) => {
|
watch(modelValue, (newValue) => {
|
||||||
v.value = newValue;
|
v.value = newValue;
|
||||||
|
@ -137,7 +138,7 @@ watch(v, (_) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value!.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
// このコンポーネントが作成された時、非表示状態である場合がある
|
// このコンポーネントが作成された時、非表示状態である場合がある
|
||||||
|
@ -146,12 +147,12 @@ useInterval(
|
||||||
() => {
|
() => {
|
||||||
if (prefixEl.value) {
|
if (prefixEl.value) {
|
||||||
if (prefixEl.value.offsetWidth) {
|
if (prefixEl.value.offsetWidth) {
|
||||||
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + "px";
|
inputEl.value!.style.paddingLeft = `${prefixEl.value.offsetWidth}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (suffixEl.value) {
|
if (suffixEl.value) {
|
||||||
if (suffixEl.value.offsetWidth) {
|
if (suffixEl.value.offsetWidth) {
|
||||||
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + "px";
|
inputEl.value!.style.paddingRight = `${suffixEl.value.offsetWidth}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,19 +16,22 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||||
|
type ValueType = any;
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: any;
|
modelValue: ValueType;
|
||||||
value: any;
|
value: ValueType;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", value: any): void;
|
"update:modelValue": [value: ValueType];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const checked = computed(() => props.modelValue === props.value);
|
const checked = computed(() => props.modelValue === props.value);
|
||||||
|
|
||||||
function toggle(x) {
|
function toggle(_ev: Event) {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
emit("update:modelValue", props.value);
|
emit("update:modelValue", props.value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, h } from "vue";
|
import { type VNode, defineComponent, h } from "vue";
|
||||||
import MkRadio from "./radio.vue";
|
import MkRadio from "./radio.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -22,13 +22,17 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render() {
|
render() {
|
||||||
let options = this.$slots.default();
|
let options = this.$slots.default!();
|
||||||
const label = this.$slots.label && this.$slots.label();
|
const label = this.$slots.label && this.$slots.label!();
|
||||||
const caption = this.$slots.caption && this.$slots.caption();
|
const caption = this.$slots.caption && this.$slots.caption!();
|
||||||
|
|
||||||
// なぜかFragmentになることがあるため
|
// なぜかFragmentになることがあるため
|
||||||
if (options.length === 1 && options[0].props == null)
|
if (
|
||||||
options = options[0].children;
|
options.length === 1 &&
|
||||||
|
options[0].props == null &&
|
||||||
|
Array.isArray(options[0].children)
|
||||||
|
)
|
||||||
|
options = options[0].children as VNode[];
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
"fieldset",
|
"fieldset",
|
||||||
|
@ -56,11 +60,15 @@ export default defineComponent({
|
||||||
h(
|
h(
|
||||||
MkRadio,
|
MkRadio,
|
||||||
{
|
{
|
||||||
|
// FIXME: It seems that there is a type error
|
||||||
key: option.key,
|
key: option.key,
|
||||||
value: option.props?.value,
|
value: option.props?.value,
|
||||||
disabled: option.props?.disabled,
|
disabled: option.props?.disabled,
|
||||||
modelValue: this.value,
|
modelValue: this.value,
|
||||||
"onUpdate:modelValue": (value) => (this.value = value),
|
"onUpdate:modelValue": (value) => {
|
||||||
|
this.value = value;
|
||||||
|
return value;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
option.children,
|
option.children,
|
||||||
),
|
),
|
||||||
|
@ -83,7 +91,7 @@ export default defineComponent({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss" scoped>
|
||||||
.novjtcto {
|
.novjtcto {
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
@mouseleave="tooltipHide"
|
@mouseleave="tooltipHide"
|
||||||
@input="
|
@input="
|
||||||
(x) => {
|
(x) => {
|
||||||
inputVal = x.target.value;
|
inputVal = Number((x.target as HTMLInputElement).value);
|
||||||
if (instant) onChange(x);
|
if (instant) onChange(x);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
@ -29,6 +29,7 @@
|
||||||
<datalist v-if="showTicks && steps" :id="id">
|
<datalist v-if="showTicks && steps" :id="id">
|
||||||
<option
|
<option
|
||||||
v-for="i in steps"
|
v-for="i in steps"
|
||||||
|
:key="`step-${i}`"
|
||||||
:value="i + min"
|
:value="i + min"
|
||||||
:label="(i + min).toString()"
|
:label="(i + min).toString()"
|
||||||
></option>
|
></option>
|
||||||
|
@ -69,11 +70,11 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputEl = ref<HTMLElement>();
|
const inputEl = ref<HTMLInputElement>();
|
||||||
const inputVal = ref(props.modelValue);
|
const inputVal = ref(props.modelValue);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", value: number): void;
|
"update:modelValue": [value: number];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const steps = computed(() => {
|
const steps = computed(() => {
|
||||||
|
@ -84,7 +85,7 @@ const steps = computed(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function onChange(x) {
|
function onChange(_x) {
|
||||||
emit("update:modelValue", inputVal.value);
|
emit("update:modelValue", inputVal.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ import * as os from "@/os";
|
||||||
import { useInterval } from "@/scripts/use-interval";
|
import { useInterval } from "@/scripts/use-interval";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
import type { MenuItem } from "@/types/menu";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: string | null;
|
modelValue: string | null;
|
||||||
|
@ -85,13 +86,13 @@ const focused = ref(false);
|
||||||
const opening = ref(false);
|
const opening = ref(false);
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
const invalid = ref(false);
|
const invalid = ref(false);
|
||||||
const inputEl = ref(null);
|
const inputEl = ref<HTMLInputElement | null>(null);
|
||||||
const prefixEl = ref(null);
|
const prefixEl = ref<HTMLElement | null>(null);
|
||||||
const suffixEl = ref(null);
|
const suffixEl = ref<HTMLElement | null>(null);
|
||||||
const container = ref(null);
|
const container = ref<HTMLElement | null>(null);
|
||||||
const height = props.small ? 33 : props.large ? 39 : 36;
|
const height = props.small ? 33 : props.large ? 39 : 36;
|
||||||
|
|
||||||
const focus = () => inputEl.value.focus();
|
const focus = () => inputEl.value!.focus();
|
||||||
const onInput = (ev) => {
|
const onInput = (ev) => {
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
emit("change", ev);
|
emit("change", ev);
|
||||||
|
@ -106,26 +107,27 @@ watch(modelValue, (newValue) => {
|
||||||
v.value = newValue;
|
v.value = newValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(v, (newValue) => {
|
watch(v, (_newValue) => {
|
||||||
if (!props.manualSave) {
|
if (!props.manualSave) {
|
||||||
updated();
|
updated();
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value!.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
// このコンポーネントが作成された時、非表示状態である場合がある
|
// このコンポーネントが作成された時、非表示状態である場合がある
|
||||||
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
// 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する
|
||||||
useInterval(
|
useInterval(
|
||||||
() => {
|
() => {
|
||||||
|
if (inputEl.value == null) return;
|
||||||
if (prefixEl.value) {
|
if (prefixEl.value) {
|
||||||
if (prefixEl.value.offsetWidth) {
|
if (prefixEl.value.offsetWidth) {
|
||||||
inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + "px";
|
inputEl.value.style.paddingLeft = `${prefixEl.value.offsetWidth}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (suffixEl.value) {
|
if (suffixEl.value) {
|
||||||
if (suffixEl.value.offsetWidth) {
|
if (suffixEl.value.offsetWidth) {
|
||||||
inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + "px";
|
inputEl.value.style.paddingRight = `${suffixEl.value.offsetWidth}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -144,19 +146,19 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function show(ev: MouseEvent) {
|
function show(_ev: MouseEvent) {
|
||||||
focused.value = true;
|
focused.value = true;
|
||||||
opening.value = true;
|
opening.value = true;
|
||||||
|
|
||||||
const menu = [];
|
const menu: MenuItem[] = [];
|
||||||
const options = slots.default!();
|
const options = slots.default!();
|
||||||
|
|
||||||
const pushOption = (option: VNode) => {
|
const pushOption = (option: VNode) => {
|
||||||
menu.push({
|
menu.push({
|
||||||
text: option.children,
|
text: option.children as string,
|
||||||
active: computed(() => v.value === option.props.value),
|
active: computed(() => v.value === option.props?.value).value,
|
||||||
action: () => {
|
action: () => {
|
||||||
v.value = option.props.value;
|
v.value = option.props?.value;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -167,13 +169,13 @@ function show(ev: MouseEvent) {
|
||||||
const optgroup = vnode;
|
const optgroup = vnode;
|
||||||
menu.push({
|
menu.push({
|
||||||
type: "label",
|
type: "label",
|
||||||
text: optgroup.props.label,
|
text: optgroup.props?.label,
|
||||||
});
|
});
|
||||||
scanOptions(optgroup.children);
|
scanOptions(optgroup.children as VNode[]);
|
||||||
} else if (Array.isArray(vnode.children)) {
|
} else if (Array.isArray(vnode.children)) {
|
||||||
// 何故かフラグメントになってくることがある
|
// 何故かフラグメントになってくることがある
|
||||||
const fragment = vnode;
|
const fragment = vnode;
|
||||||
scanOptions(fragment.children);
|
scanOptions(fragment.children as VNode[]);
|
||||||
} else if (vnode.props == null) {
|
} else if (vnode.props == null) {
|
||||||
// v-if で条件が false のときにこうなる
|
// v-if で条件が false のときにこうなる
|
||||||
// nop?
|
// nop?
|
||||||
|
@ -186,12 +188,13 @@ function show(ev: MouseEvent) {
|
||||||
|
|
||||||
scanOptions(options);
|
scanOptions(options);
|
||||||
|
|
||||||
os.popupMenu(menu, container.value, {
|
os.popupMenu(menu, container.value!, {
|
||||||
width: container.value.offsetWidth,
|
width: container.value!.offsetWidth,
|
||||||
onClosing: () => {
|
// onClosing: () => {
|
||||||
opening.value = false;
|
// opening.value = false;
|
||||||
},
|
// },
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
opening.value = false;
|
||||||
focused.value = false;
|
focused.value = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ const props = withDefaults(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const minWidth = props.minWidth + "px";
|
const minWidth = `${props.minWidth}px`;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -39,12 +39,13 @@ export default defineComponent({
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
p: {
|
p: {
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: FIXME
|
||||||
type: Function as PropType<() => Promise<any>>,
|
type: Function as PropType<() => Promise<any>>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
setup(props, context) {
|
setup(props, _context) {
|
||||||
const pending = ref(true);
|
const pending = ref(true);
|
||||||
const resolved = ref(false);
|
const resolved = ref(false);
|
||||||
const rejected = ref(false);
|
const rejected = ref(false);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<label class="ziffeomt">
|
<label class="ziffeomt">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="modelValue"
|
:checked="toValue(modelValue)"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@change="(x) => toggle(x)"
|
@change="(x) => toggle(x)"
|
||||||
/>
|
/>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { Ref } from "vue";
|
import { type Ref, toValue } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean | Ref<boolean>;
|
modelValue: boolean | Ref<boolean>;
|
||||||
|
@ -26,12 +26,12 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:modelValue", v: boolean): void;
|
"update:modelValue": [v: boolean];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
function toggle(x) {
|
function toggle(x: Event) {
|
||||||
if (props.disabled) return;
|
if (props.disabled) return;
|
||||||
emit("update:modelValue", x.target.checked);
|
emit("update:modelValue", (x.target as HTMLInputElement).checked);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@ export default defineComponent({
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
|
@ -92,9 +93,11 @@ export default defineComponent({
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
autocomplete: {
|
autocomplete: {
|
||||||
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
spellcheck: {
|
spellcheck: {
|
||||||
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
code: {
|
code: {
|
||||||
|
@ -132,9 +135,9 @@ export default defineComponent({
|
||||||
const changed = ref(false);
|
const changed = ref(false);
|
||||||
const invalid = ref(false);
|
const invalid = ref(false);
|
||||||
const filled = computed(() => v.value !== "" && v.value != null);
|
const filled = computed(() => v.value !== "" && v.value != null);
|
||||||
const inputEl = ref(null);
|
const inputEl = ref<HTMLTextAreaElement | null>(null);
|
||||||
|
|
||||||
const focus = () => inputEl.value.focus();
|
const focus = () => inputEl.value!.focus();
|
||||||
const onInput = (ev) => {
|
const onInput = (ev) => {
|
||||||
changed.value = true;
|
changed.value = true;
|
||||||
context.emit("change", ev);
|
context.emit("change", ev);
|
||||||
|
@ -167,7 +170,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invalid.value = inputEl.value.validity.badInput;
|
invalid.value = inputEl.value!.validity.badInput;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-for="chosenItem in chosen"
|
v-for="chosenItem in chosen"
|
||||||
v-if="chosen && chosen.length > 0 && defaultStore.state.showAds"
|
v-if="chosen && Array.isArray(chosen) && chosen.length > 0 && defaultStore.state.showAds"
|
||||||
class="qiivuoyo"
|
class="qiivuoyo"
|
||||||
>
|
>
|
||||||
<div v-if="!showMenu" class="main" :class="chosenItem.place">
|
<div v-if="!showMenu" class="main" :class="chosenItem.place">
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="chosen && defaultStore.state.showAds" class="qiivuoyo">
|
<div v-else-if="chosen && !Array.isArray(chosen) && defaultStore.state.showAds" class="qiivuoyo">
|
||||||
<div v-if="!showMenu" class="main" :class="chosen.place">
|
<div v-if="!showMenu" class="main" :class="chosen.place">
|
||||||
<a :href="chosen.url" target="_blank">
|
<a :href="chosen.url" target="_blank">
|
||||||
<img :src="chosen.imageUrl" />
|
<img :src="chosen.imageUrl" />
|
||||||
|
@ -60,7 +60,7 @@ const toggleMenu = (): void => {
|
||||||
showMenu.value = !showMenu.value;
|
showMenu.value = !showMenu.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const choseAd = (): Ad | null => {
|
const choseAd = (): Ad | Ad[] | null => {
|
||||||
if (props.specify) {
|
if (props.specify) {
|
||||||
return props.specify;
|
return props.specify;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,7 @@ const chosen = ref(choseAd());
|
||||||
|
|
||||||
function reduceFrequency(): void {
|
function reduceFrequency(): void {
|
||||||
if (chosen.value == null) return;
|
if (chosen.value == null) return;
|
||||||
|
if (Array.isArray(chosen.value)) return;
|
||||||
if (defaultStore.state.mutedAds.includes(chosen.value.id)) return;
|
if (defaultStore.state.mutedAds.includes(chosen.value.id)) return;
|
||||||
defaultStore.push("mutedAds", chosen.value.id);
|
defaultStore.push("mutedAds", chosen.value.id);
|
||||||
os.success();
|
os.success();
|
||||||
|
|
|
@ -98,7 +98,7 @@ const props = withDefaults(
|
||||||
);
|
);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "click", v: MouseEvent): void;
|
click: [v: MouseEvent];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const url = computed(() =>
|
const url = computed(() =>
|
||||||
|
@ -123,18 +123,18 @@ watch(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const gallery = ref(null);
|
const gallery = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const lightbox = new PhotoSwipeLightbox({
|
const lightbox = new PhotoSwipeLightbox({
|
||||||
dataSource: [
|
dataSource: [
|
||||||
{
|
{
|
||||||
src: url,
|
src: url.value,
|
||||||
w: 300,
|
w: 300,
|
||||||
h: 300,
|
h: 300,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
gallery: gallery.value,
|
gallery: gallery.value || undefined,
|
||||||
children: ".avatar",
|
children: ".avatar",
|
||||||
thumbSelector: ".avatar",
|
thumbSelector: ".avatar",
|
||||||
loop: false,
|
loop: false,
|
||||||
|
@ -174,7 +174,7 @@ onMounted(() => {
|
||||||
history.pushState(null, "", location.href);
|
history.pushState(null, "", location.href);
|
||||||
addEventListener("popstate", close);
|
addEventListener("popstate", close);
|
||||||
// This is a workaround. Not sure why, but when clicking to open, it doesn't move focus to the photoswipe. Preventing using esc to close. However when using keyboard to open it already focuses the lightbox fine.
|
// This is a workaround. Not sure why, but when clicking to open, it doesn't move focus to the photoswipe. Preventing using esc to close. However when using keyboard to open it already focuses the lightbox fine.
|
||||||
lightbox.pswp.element.focus();
|
lightbox.pswp?.element?.focus();
|
||||||
});
|
});
|
||||||
lightbox.on("close", () => {
|
lightbox.on("close", () => {
|
||||||
removeEventListener("popstate", close);
|
removeEventListener("popstate", close);
|
||||||
|
@ -186,7 +186,7 @@ onMounted(() => {
|
||||||
function close() {
|
function close() {
|
||||||
removeEventListener("popstate", close);
|
removeEventListener("popstate", close);
|
||||||
history.forward();
|
history.forward();
|
||||||
lightbox.pswp.close();
|
lightbox.pswp?.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -4,16 +4,16 @@
|
||||||
class="mk-emoji custom"
|
class="mk-emoji custom"
|
||||||
:class="{ normal, noStyle }"
|
:class="{ normal, noStyle }"
|
||||||
:src="url"
|
:src="url"
|
||||||
:alt="alt"
|
:alt="alt || undefined"
|
||||||
:title="alt"
|
:title="alt || undefined"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
v-else-if="char && !useOsNativeEmojis"
|
v-else-if="char && !useOsNativeEmojis"
|
||||||
class="mk-emoji"
|
class="mk-emoji"
|
||||||
:src="url"
|
:src="url"
|
||||||
:alt="alt"
|
:alt="alt || undefined"
|
||||||
:title="alt"
|
:title="alt || undefined"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
|
<span v-else-if="char && useOsNativeEmojis">{{ char }}</span>
|
||||||
|
@ -32,7 +32,7 @@ const props = defineProps<{
|
||||||
emoji: string;
|
emoji: string;
|
||||||
normal?: boolean;
|
normal?: boolean;
|
||||||
noStyle?: boolean;
|
noStyle?: boolean;
|
||||||
customEmojis?: entities.CustomEmoji[];
|
customEmojis?: entities.EmojiLite[];
|
||||||
isReaction?: boolean;
|
isReaction?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ const customEmoji = computed(() =>
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
const url = computed(() => {
|
const url = computed(() => {
|
||||||
|
if (!customEmoji.value) return undefined;
|
||||||
if (char.value) {
|
if (char.value) {
|
||||||
return char2filePath(char.value);
|
return char2filePath(char.value);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const props = withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
inline?: boolean;
|
inline?: boolean;
|
||||||
colored?: boolean;
|
colored?: boolean;
|
||||||
|
|
|
@ -19,22 +19,22 @@
|
||||||
import {} from "vue";
|
import {} from "vue";
|
||||||
import MfmCore from "@/components/mfm";
|
import MfmCore from "@/components/mfm";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
import type { entities } from "firefish-js";
|
||||||
|
|
||||||
const props = withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
text: string;
|
text: string;
|
||||||
plain?: boolean;
|
plain?: boolean;
|
||||||
nowrap?: boolean;
|
nowrap?: boolean;
|
||||||
author?: any;
|
author?: entities.User;
|
||||||
customEmojis?: any;
|
customEmojis?: entities.EmojiLite[];
|
||||||
isNote?: boolean;
|
isNote?: boolean;
|
||||||
advancedMfm: boolean;
|
advancedMfm?: boolean;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
plain: false,
|
plain: false,
|
||||||
nowrap: false,
|
nowrap: false,
|
||||||
author: null,
|
|
||||||
isNote: true,
|
isNote: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
v-if="show"
|
v-if="show"
|
||||||
ref="el"
|
ref="el"
|
||||||
class="fdidabkb"
|
class="fdidabkb"
|
||||||
:class="{ thin: thin_, tabs: tabs?.length > 0 }"
|
:class="{ thin: thin_, tabs: isTabs(tabs)}"
|
||||||
:style="{ background: bg }"
|
:style="{ background: bg || undefined }"
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
>
|
>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
|
@ -70,14 +70,14 @@
|
||||||
</div>
|
</div>
|
||||||
<template v-if="metadata">
|
<template v-if="metadata">
|
||||||
<nav
|
<nav
|
||||||
v-if="hasTabs"
|
v-if="isTabs(tabs)"
|
||||||
ref="tabsEl"
|
ref="tabsEl"
|
||||||
class="tabs"
|
class="tabs"
|
||||||
:class="{ collapse: hasTabs && tabs.length > 3 }"
|
:class="{ collapse: tabs.length > 3 }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
v-for="tab in tabs"
|
v-for="tab in tabs"
|
||||||
:ref="(el) => (tabRefs[tab.key] = el)"
|
:ref="(el) => (tab.key && (tabRefs[tab.key] = el))"
|
||||||
v-tooltip.noDelay="tab.title"
|
v-tooltip.noDelay="tab.title"
|
||||||
v-vibrate="5"
|
v-vibrate="5"
|
||||||
class="tab _button"
|
class="tab _button"
|
||||||
|
@ -157,6 +157,7 @@ const props = defineProps<{
|
||||||
actions?: {
|
actions?: {
|
||||||
text: string;
|
text: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
highlighted?: boolean;
|
||||||
handler: (ev: MouseEvent) => void;
|
handler: (ev: MouseEvent) => void;
|
||||||
}[];
|
}[];
|
||||||
thin?: boolean;
|
thin?: boolean;
|
||||||
|
@ -171,7 +172,7 @@ const displayBackButton =
|
||||||
inject("shouldBackButton", true);
|
inject("shouldBackButton", true);
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: "update:tab", key: string);
|
"update:tab": [key: string];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const metadata = injectPageMetadata();
|
const metadata = injectPageMetadata();
|
||||||
|
@ -183,9 +184,14 @@ const el = ref<HTMLElement | null>(null);
|
||||||
const tabRefs = {};
|
const tabRefs = {};
|
||||||
const tabHighlightEl = ref<HTMLElement | null>(null);
|
const tabHighlightEl = ref<HTMLElement | null>(null);
|
||||||
const tabsEl = ref<HTMLElement | null>(null);
|
const tabsEl = ref<HTMLElement | null>(null);
|
||||||
const bg = ref(null);
|
const bg = ref<string | null | number>(null);
|
||||||
const narrow = ref(false);
|
const narrow = ref(false);
|
||||||
const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
|
const hasTabs = computed(() => props.tabs && props.tabs.length > 0);
|
||||||
|
|
||||||
|
function isTabs(t: Tab[] | undefined): t is Tab[] {
|
||||||
|
return t != null && t.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
const hasActions = computed(() => props.actions && props.actions.length > 0);
|
const hasActions = computed(() => props.actions && props.actions.length > 0);
|
||||||
const show = computed(() => {
|
const show = computed(() => {
|
||||||
return !hideTitle || hasTabs.value || hasActions.value;
|
return !hideTitle || hasTabs.value || hasActions.value;
|
||||||
|
@ -201,7 +207,7 @@ const openAccountMenu = (ev: MouseEvent) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const showTabsPopup = (ev: MouseEvent) => {
|
const showTabsPopup = (ev: MouseEvent) => {
|
||||||
if (!hasTabs.value) return;
|
if (!isTabs(props.tabs)) return;
|
||||||
if (!narrow.value) return;
|
if (!narrow.value) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
@ -213,7 +219,7 @@ const showTabsPopup = (ev: MouseEvent) => {
|
||||||
onTabClick(tab, ev);
|
onTabClick(tab, ev);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
popupMenu(menu, ev.currentTarget ?? ev.target);
|
popupMenu(menu, (ev.currentTarget ?? ev.target) as HTMLElement);
|
||||||
};
|
};
|
||||||
|
|
||||||
const preventDrag = (ev: TouchEvent) => {
|
const preventDrag = (ev: TouchEvent) => {
|
||||||
|
@ -224,7 +230,9 @@ const onClick = () => {
|
||||||
if (props.to) {
|
if (props.to) {
|
||||||
location.href = props.to;
|
location.href = props.to;
|
||||||
} else {
|
} else {
|
||||||
scrollToTop(el.value, { behavior: "smooth" });
|
if (el.value) {
|
||||||
|
scrollToTop(el.value, { behavior: "smooth" });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,6 +265,8 @@ onMounted(() => {
|
||||||
() => [props.tab, props.tabs],
|
() => [props.tab, props.tabs],
|
||||||
() => {
|
() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
|
if (props.tab == null) return;
|
||||||
|
if (!isTabs(props.tabs)) return;
|
||||||
const tabEl = tabRefs[props.tab];
|
const tabEl = tabRefs[props.tab];
|
||||||
if (tabEl && tabHighlightEl.value) {
|
if (tabEl && tabHighlightEl.value) {
|
||||||
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
|
||||||
|
@ -266,7 +276,8 @@ onMounted(() => {
|
||||||
tabEl.style = `--width: ${tabSizeX}px`;
|
tabEl.style = `--width: ${tabSizeX}px`;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tabHighlightEl.value.style.width = tabSizeX + "px";
|
if (tabHighlightEl.value == null) return;
|
||||||
|
tabHighlightEl.value.style.width = `${tabSizeX}px`;
|
||||||
tabHighlightEl.value.style.transform = `translateX(${tabEl.offsetLeft}px)`;
|
tabHighlightEl.value.style.transform = `translateX(${tabEl.offsetLeft}px)`;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
tabsEl.value?.scrollTo({
|
tabsEl.value?.scrollTo({
|
||||||
|
@ -283,10 +294,10 @@ onMounted(() => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (el.value && el.value.parentElement) {
|
if (el.value?.parentElement) {
|
||||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||||
ro = new ResizeObserver((entries, observer) => {
|
ro = new ResizeObserver((_entries, _observer) => {
|
||||||
if (el.value.parentElement && document.body.contains(el.value)) {
|
if (el.value?.parentElement && document.body.contains(el.value)) {
|
||||||
narrow.value = el.value.parentElement.offsetWidth < 500;
|
narrow.value = el.value.parentElement.offsetWidth < 500;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,8 +28,8 @@ const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
|
||||||
provide(CURRENT_STICKY_TOP, childStickyTop);
|
provide(CURRENT_STICKY_TOP, childStickyTop);
|
||||||
|
|
||||||
const calc = () => {
|
const calc = () => {
|
||||||
childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
|
childStickyTop.value = parentStickyTop.value + headerEl.value!.offsetHeight;
|
||||||
headerHeight.value = headerEl.value.offsetHeight.toString();
|
headerHeight.value = headerEl.value!.offsetHeight.toString();
|
||||||
};
|
};
|
||||||
|
|
||||||
const observer = new ResizeObserver(() => {
|
const observer = new ResizeObserver(() => {
|
||||||
|
@ -46,7 +46,7 @@ onMounted(() => {
|
||||||
watch(
|
watch(
|
||||||
childStickyTop,
|
childStickyTop,
|
||||||
() => {
|
() => {
|
||||||
bodyEl.value.style.setProperty(
|
bodyEl.value!.style.setProperty(
|
||||||
"--stickyTop",
|
"--stickyTop",
|
||||||
`${childStickyTop.value}px`,
|
`${childStickyTop.value}px`,
|
||||||
);
|
);
|
||||||
|
@ -56,7 +56,7 @@ onMounted(() => {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
observer.observe(headerEl.value);
|
observer.observe(headerEl.value!);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
|
|
@ -27,7 +27,7 @@ const props = withDefaults(
|
||||||
|
|
||||||
const _time =
|
const _time =
|
||||||
props.time == null
|
props.time == null
|
||||||
? NaN
|
? Number.NaN
|
||||||
: typeof props.time === "number"
|
: typeof props.time === "number"
|
||||||
? props.time
|
? props.time
|
||||||
: (props.time instanceof Date
|
: (props.time instanceof Date
|
||||||
|
|
|
@ -38,13 +38,14 @@ export default defineComponent({
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
this.tag,
|
this.tag,
|
||||||
parsed.map((x) =>
|
parsed.map((x) => {
|
||||||
typeof x === "string"
|
if (typeof x === "string") {
|
||||||
? this.textTag
|
return this.textTag ? h(this.textTag, x) : x;
|
||||||
? h(this.textTag, x)
|
} else {
|
||||||
: x
|
const t = this.$slots[x.arg];
|
||||||
: this.$slots[x.arg](),
|
return t ? t() : `I18n[${x.arg}]`;
|
||||||
),
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineComponent, h } from "vue";
|
import { defineComponent, h } from "vue";
|
||||||
import * as mfm from "mfm-js";
|
import * as mfm from "mfm-js";
|
||||||
import type { VNode } from "vue";
|
import type { VNode, PropType } from "vue";
|
||||||
import MkUrl from "@/components/global/MkUrl.vue";
|
import MkUrl from "@/components/global/MkUrl.vue";
|
||||||
import MkLink from "@/components/MkLink.vue";
|
import MkLink from "@/components/MkLink.vue";
|
||||||
import MkMention from "@/components/MkMention.vue";
|
import MkMention from "@/components/MkMention.vue";
|
||||||
|
@ -14,6 +14,7 @@ import MkA from "@/components/global/MkA.vue";
|
||||||
import { host } from "@/config";
|
import { host } from "@/config";
|
||||||
import { reducedMotion } from "@/scripts/reduced-motion";
|
import { reducedMotion } from "@/scripts/reduced-motion";
|
||||||
import { defaultStore } from "@/store";
|
import { defaultStore } from "@/store";
|
||||||
|
import type { entities } from "firefish-js";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -38,6 +39,7 @@ export default defineComponent({
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
customEmojis: {
|
customEmojis: {
|
||||||
|
type: Array as PropType<entities.EmojiLite[]>,
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
isNote: {
|
isNote: {
|
||||||
|
|
|
@ -855,8 +855,8 @@ export function popupMenu(
|
||||||
noReturnFocus?: boolean;
|
noReturnFocus?: boolean;
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, _reject) => {
|
||||||
let dispose;
|
let dispose: () => void;
|
||||||
popup(
|
popup(
|
||||||
defineAsyncComponent({
|
defineAsyncComponent({
|
||||||
loader: () => import("@/components/MkPopupMenu.vue"),
|
loader: () => import("@/components/MkPopupMenu.vue"),
|
||||||
|
|
|
@ -113,10 +113,11 @@ import MkInstanceCardMini from "@/components/MkInstanceCardMini.vue";
|
||||||
import FormSplit from "@/components/form/split.vue";
|
import FormSplit from "@/components/form/split.vue";
|
||||||
import { i18n } from "@/i18n";
|
import { i18n } from "@/i18n";
|
||||||
import icon from "@/scripts/icon";
|
import icon from "@/scripts/icon";
|
||||||
|
import type { instanceSortParam } from "firefish-js";
|
||||||
|
|
||||||
const host = ref("");
|
const host = ref("");
|
||||||
const state = ref("federating");
|
const state = ref("federating");
|
||||||
const sort = ref("+pubSub");
|
const sort = ref<(typeof instanceSortParam)[number]>("+pubSub");
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: "federation/instances" as const,
|
endpoint: "federation/instances" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
|
|
@ -94,7 +94,7 @@ const origin = ref("local");
|
||||||
const type = ref(null);
|
const type = ref(null);
|
||||||
const searchHost = ref("");
|
const searchHost = ref("");
|
||||||
const userId = ref("");
|
const userId = ref("");
|
||||||
const viewMode = ref("grid");
|
const viewMode = ref<"list" | "grid">("grid");
|
||||||
const pagination = {
|
const pagination = {
|
||||||
endpoint: "admin/drive/files" as const,
|
endpoint: "admin/drive/files" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
|
|
@ -313,7 +313,7 @@ const isSilenced = ref(false);
|
||||||
const faviconUrl = ref<string | null>(null);
|
const faviconUrl = ref<string | null>(null);
|
||||||
|
|
||||||
const usersPagination = {
|
const usersPagination = {
|
||||||
endpoint: isAdmin ? "admin/show-users" : ("users" as const),
|
endpoint: isAdmin ? ("admin/show-users" as const) : ("users" as const),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
params: {
|
params: {
|
||||||
sort: "+updatedAt",
|
sort: "+updatedAt",
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
<MkPagination
|
<MkPagination
|
||||||
v-else
|
v-else
|
||||||
ref="pagingComponent"
|
ref="pagingComponent"
|
||||||
v-slot="{ items }: { items: entities.NoteEdit[] }"
|
v-slot="{ items }"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
>
|
>
|
||||||
<div ref="tlEl" class="giivymft noGap">
|
<div ref="tlEl" class="giivymft noGap">
|
||||||
<XList
|
<XList
|
||||||
v-slot="{ item }: { item: entities.Note }"
|
v-slot="{ item }"
|
||||||
:items="convertNoteEditsToNotes(items)"
|
:items="convertNoteEditsToNotes(items)"
|
||||||
class="notes"
|
class="notes"
|
||||||
:no-gap="true"
|
:no-gap="true"
|
||||||
|
@ -35,7 +35,6 @@
|
||||||
<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 from "@/components/MkPagination.vue";
|
||||||
import type { Paging } 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";
|
||||||
|
@ -50,7 +49,7 @@ const props = defineProps<{
|
||||||
noteId: string;
|
noteId: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const pagination: Paging = {
|
const pagination = {
|
||||||
endpoint: "notes/history" as const,
|
endpoint: "notes/history" as const,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offsetMode: true,
|
offsetMode: true,
|
||||||
|
|
|
@ -9,7 +9,7 @@ export interface PageMetadata {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
icon?: string | null;
|
icon?: string | null;
|
||||||
avatar?: entities.User | null;
|
avatar?: entities.UserDetailed | null;
|
||||||
userName?: entities.User | null;
|
userName?: entities.User | null;
|
||||||
bg?: string;
|
bg?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,9 @@
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"typeRoots": ["node_modules/@types", "@types"],
|
"typeRoots": ["node_modules/@types", "@types"],
|
||||||
"types": ["vite/client"],
|
|
||||||
"lib": ["esnext", "dom"],
|
"lib": ["esnext", "dom"],
|
||||||
"jsx": "preserve"
|
"jsx": "preserve"
|
||||||
},
|
},
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"include": ["./**/*.ts", "./**/*.vue"]
|
"include": ["./src/**/*.ts", "./src/**/*.vue"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ import type {
|
||||||
UserSorting,
|
UserSorting,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
|
|
||||||
|
import type * as consts from "./consts";
|
||||||
|
|
||||||
type TODO = Record<string, any> | null;
|
type TODO = Record<string, any> | null;
|
||||||
|
|
||||||
type NoParams = Record<string, never>;
|
type NoParams = Record<string, never>;
|
||||||
|
@ -84,7 +86,7 @@ export type Endpoints = {
|
||||||
"admin/server-info": { req: TODO; res: TODO };
|
"admin/server-info": { req: TODO; res: TODO };
|
||||||
"admin/show-moderation-logs": { req: TODO; res: TODO };
|
"admin/show-moderation-logs": { req: TODO; res: TODO };
|
||||||
"admin/show-user": { req: TODO; res: TODO };
|
"admin/show-user": { req: TODO; res: TODO };
|
||||||
"admin/show-users": { req: TODO; res: TODO };
|
"admin/show-users": { req: TODO; res: User[] };
|
||||||
"admin/silence-user": { req: TODO; res: TODO };
|
"admin/silence-user": { req: TODO; res: TODO };
|
||||||
"admin/suspend-user": { req: TODO; res: TODO };
|
"admin/suspend-user": { req: TODO; res: TODO };
|
||||||
"admin/unsilence-user": { req: TODO; res: TODO };
|
"admin/unsilence-user": { req: TODO; res: TODO };
|
||||||
|
@ -101,7 +103,18 @@ export type Endpoints = {
|
||||||
"admin/announcements/update": { req: TODO; res: TODO };
|
"admin/announcements/update": { req: TODO; res: TODO };
|
||||||
"admin/drive/clean-remote-files": { req: TODO; res: TODO };
|
"admin/drive/clean-remote-files": { req: TODO; res: TODO };
|
||||||
"admin/drive/cleanup": { req: TODO; res: TODO };
|
"admin/drive/cleanup": { req: TODO; res: TODO };
|
||||||
"admin/drive/files": { req: TODO; res: TODO };
|
"admin/drive/files": {
|
||||||
|
req: {
|
||||||
|
limit?: number;
|
||||||
|
sinceId?: DriveFile["id"];
|
||||||
|
untilId?: DriveFile["id"];
|
||||||
|
userId?: User["id"];
|
||||||
|
type?: string;
|
||||||
|
origin?: "combined" | "local" | "remote";
|
||||||
|
hostname?: string;
|
||||||
|
};
|
||||||
|
res: DriveFile[];
|
||||||
|
};
|
||||||
"admin/drive/show-file": { req: TODO; res: TODO };
|
"admin/drive/show-file": { req: TODO; res: TODO };
|
||||||
"admin/emoji/add": { req: TODO; res: TODO };
|
"admin/emoji/add": { req: TODO; res: TODO };
|
||||||
"admin/emoji/copy": { req: TODO; res: TODO };
|
"admin/emoji/copy": { req: TODO; res: TODO };
|
||||||
|
@ -200,7 +213,7 @@ export type Endpoints = {
|
||||||
"channels/owned": { req: TODO; res: TODO };
|
"channels/owned": { req: TODO; res: TODO };
|
||||||
"channels/pin-note": { req: TODO; res: TODO };
|
"channels/pin-note": { req: TODO; res: TODO };
|
||||||
"channels/show": { req: TODO; res: TODO };
|
"channels/show": { req: TODO; res: TODO };
|
||||||
"channels/timeline": { req: TODO; res: TODO };
|
"channels/timeline": { req: TODO; res: Note[] };
|
||||||
"channels/unfollow": { req: TODO; res: TODO };
|
"channels/unfollow": { req: TODO; res: TODO };
|
||||||
"channels/update": { req: TODO; res: TODO };
|
"channels/update": { req: TODO; res: TODO };
|
||||||
|
|
||||||
|
@ -238,7 +251,7 @@ export type Endpoints = {
|
||||||
};
|
};
|
||||||
res: DriveFile[];
|
res: DriveFile[];
|
||||||
};
|
};
|
||||||
"drive/files/attached-notes": { req: TODO; res: TODO };
|
"drive/files/attached-notes": { req: TODO; res: Note[] };
|
||||||
"drive/files/check-existence": { req: TODO; res: TODO };
|
"drive/files/check-existence": { req: TODO; res: TODO };
|
||||||
"drive/files/create": { req: TODO; res: TODO };
|
"drive/files/create": { req: TODO; res: TODO };
|
||||||
"drive/files/delete": { req: { fileId: DriveFile["id"] }; res: null };
|
"drive/files/delete": { req: { fileId: DriveFile["id"] }; res: null };
|
||||||
|
@ -360,25 +373,7 @@ export type Endpoints = {
|
||||||
publishing?: boolean | null;
|
publishing?: boolean | null;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
sort?:
|
sort?: (typeof consts.instanceSortParam)[number];
|
||||||
| "+pubSub"
|
|
||||||
| "-pubSub"
|
|
||||||
| "+notes"
|
|
||||||
| "-notes"
|
|
||||||
| "+users"
|
|
||||||
| "-users"
|
|
||||||
| "+following"
|
|
||||||
| "-following"
|
|
||||||
| "+followers"
|
|
||||||
| "-followers"
|
|
||||||
| "+caughtAt"
|
|
||||||
| "-caughtAt"
|
|
||||||
| "+lastCommunicatedAt"
|
|
||||||
| "-lastCommunicatedAt"
|
|
||||||
| "+driveUsage"
|
|
||||||
| "-driveUsage"
|
|
||||||
| "+driveFiles"
|
|
||||||
| "-driveFiles";
|
|
||||||
};
|
};
|
||||||
res: Instance[];
|
res: Instance[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -151,3 +151,24 @@ export const languages = [
|
||||||
"yi",
|
"yi",
|
||||||
"zh",
|
"zh",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
|
export const instanceSortParam = [
|
||||||
|
"+pubSub",
|
||||||
|
"-pubSub",
|
||||||
|
"+notes",
|
||||||
|
"-notes",
|
||||||
|
"+users",
|
||||||
|
"-users",
|
||||||
|
"+following",
|
||||||
|
"-following",
|
||||||
|
"+followers",
|
||||||
|
"-followers",
|
||||||
|
"+caughtAt",
|
||||||
|
"-caughtAt",
|
||||||
|
"+lastCommunicatedAt",
|
||||||
|
"-lastCommunicatedAt",
|
||||||
|
"+driveUsage",
|
||||||
|
"-driveUsage",
|
||||||
|
"+driveFiles",
|
||||||
|
"-driveFiles",
|
||||||
|
] as const;
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
|
import type * as consts from "./consts";
|
||||||
|
|
||||||
export type ID = string;
|
export type ID = string;
|
||||||
export type DateString = string;
|
export type DateString = string;
|
||||||
|
|
||||||
type TODO = Record<string, any>;
|
type TODO = Record<string, any>;
|
||||||
|
|
||||||
// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように
|
// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように
|
||||||
export type User = UserLite | UserDetailed;
|
export type User = UserLite & Partial<UserDetailed>;
|
||||||
|
|
||||||
export type UserLite = {
|
export type UserLite = {
|
||||||
id: ID;
|
id: ID;
|
||||||
|
@ -108,7 +110,7 @@ export type MeDetailed = UserDetailed & {
|
||||||
isExplorable: boolean;
|
isExplorable: boolean;
|
||||||
mutedWords: string[][];
|
mutedWords: string[][];
|
||||||
mutedPatterns: string[];
|
mutedPatterns: string[];
|
||||||
mutingNotificationTypes: string[];
|
mutingNotificationTypes: (typeof consts.notificationTypes)[number][];
|
||||||
noCrawle: boolean;
|
noCrawle: boolean;
|
||||||
preventAiLearning: boolean;
|
preventAiLearning: boolean;
|
||||||
receiveAnnouncementEmail: boolean;
|
receiveAnnouncementEmail: boolean;
|
||||||
|
@ -129,6 +131,8 @@ export type DriveFile = {
|
||||||
blurhash: string;
|
blurhash: string;
|
||||||
comment: string | null;
|
comment: string | null;
|
||||||
properties: Record<string, any>;
|
properties: Record<string, any>;
|
||||||
|
userId?: User["id"];
|
||||||
|
user?: User;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DriveFolder = TODO;
|
export type DriveFolder = TODO;
|
||||||
|
@ -152,7 +156,8 @@ export type Note = {
|
||||||
visibleUserIds?: User["id"][];
|
visibleUserIds?: User["id"][];
|
||||||
lang?: string;
|
lang?: string;
|
||||||
localOnly?: boolean;
|
localOnly?: boolean;
|
||||||
channel?: Channel["id"];
|
channelId?: Channel["id"];
|
||||||
|
channel?: Channel;
|
||||||
myReaction?: string;
|
myReaction?: string;
|
||||||
reactions: Record<string, number>;
|
reactions: Record<string, number>;
|
||||||
renoteCount: number;
|
renoteCount: number;
|
||||||
|
@ -199,82 +204,98 @@ export type NoteReaction = {
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Notification = {
|
interface BaseNotification {
|
||||||
id: ID;
|
id: ID;
|
||||||
createdAt: DateString;
|
createdAt: DateString;
|
||||||
isRead: boolean;
|
isRead: boolean;
|
||||||
} & (
|
type: (typeof consts.notificationTypes)[number];
|
||||||
| {
|
}
|
||||||
type: "reaction";
|
|
||||||
reaction: string;
|
export interface ReactionNotification extends BaseNotification {
|
||||||
user: User;
|
type: "reaction";
|
||||||
userId: User["id"];
|
reaction: string;
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "reply";
|
}
|
||||||
user: User;
|
export interface ReplyNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "reply";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "renote";
|
}
|
||||||
user: User;
|
export interface RenoteNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "renote";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "quote";
|
}
|
||||||
user: User;
|
export interface QuoteNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "quote";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "mention";
|
}
|
||||||
user: User;
|
export interface MentionNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "mention";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "pollVote";
|
}
|
||||||
user: User;
|
export interface PollVoteNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "pollVote";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "pollEnded";
|
}
|
||||||
user: User;
|
export interface PollEndedNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "pollEnded";
|
||||||
note: Note;
|
user: User;
|
||||||
}
|
userId: User["id"];
|
||||||
| {
|
note: Note;
|
||||||
type: "follow";
|
}
|
||||||
user: User;
|
export interface FollowNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "follow";
|
||||||
}
|
user: User;
|
||||||
| {
|
userId: User["id"];
|
||||||
type: "followRequestAccepted";
|
}
|
||||||
user: User;
|
|
||||||
userId: User["id"];
|
export interface FollowRequestAcceptedNotification extends BaseNotification {
|
||||||
}
|
type: "followRequestAccepted";
|
||||||
| {
|
user: User;
|
||||||
type: "receiveFollowRequest";
|
userId: User["id"];
|
||||||
user: User;
|
}
|
||||||
userId: User["id"];
|
export interface ReceiveFollowRequestNotification extends BaseNotification {
|
||||||
}
|
type: "receiveFollowRequest";
|
||||||
| {
|
user: User;
|
||||||
type: "groupInvited";
|
userId: User["id"];
|
||||||
invitation: UserGroup;
|
}
|
||||||
user: User;
|
export interface GroupInvitedNotification extends BaseNotification {
|
||||||
userId: User["id"];
|
type: "groupInvited";
|
||||||
}
|
invitation: UserGroup;
|
||||||
| {
|
user: User;
|
||||||
type: "app";
|
userId: User["id"];
|
||||||
header?: string | null;
|
}
|
||||||
body: string;
|
export interface AppNotification extends BaseNotification {
|
||||||
icon?: string | null;
|
type: "app";
|
||||||
}
|
header?: string | null;
|
||||||
);
|
body: string;
|
||||||
|
icon?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Notification =
|
||||||
|
| ReactionNotification
|
||||||
|
| ReplyNotification
|
||||||
|
| RenoteNotification
|
||||||
|
| QuoteNotification
|
||||||
|
| MentionNotification
|
||||||
|
| PollVoteNotification
|
||||||
|
| PollEndedNotification
|
||||||
|
| FollowNotification
|
||||||
|
| FollowRequestAcceptedNotification
|
||||||
|
| ReceiveFollowRequestNotification
|
||||||
|
| GroupInvitedNotification
|
||||||
|
| AppNotification;
|
||||||
|
|
||||||
export type MessagingMessage = {
|
export type MessagingMessage = {
|
||||||
id: ID;
|
id: ID;
|
||||||
|
@ -300,6 +321,11 @@ export type CustomEmoji = {
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EmojiLite = {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type LiteInstanceMetadata = {
|
export type LiteInstanceMetadata = {
|
||||||
maintainerName: string | null;
|
maintainerName: string | null;
|
||||||
maintainerEmail: string | null;
|
maintainerEmail: string | null;
|
||||||
|
@ -449,6 +475,7 @@ export type FollowRequest = {
|
||||||
|
|
||||||
export type Channel = {
|
export type Channel = {
|
||||||
id: ID;
|
id: ID;
|
||||||
|
name: string;
|
||||||
// TODO
|
// TODO
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Endpoints } 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";
|
||||||
|
import type * as TypeUtils from "./type-utils";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Endpoints,
|
Endpoints,
|
||||||
|
@ -12,6 +13,7 @@ export {
|
||||||
StreamTypes,
|
StreamTypes,
|
||||||
acct,
|
acct,
|
||||||
type Acct,
|
type Acct,
|
||||||
|
type TypeUtils,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const permissions = consts.permissions;
|
export const permissions = consts.permissions;
|
||||||
|
@ -20,6 +22,7 @@ export const noteVisibilities = consts.noteVisibilities;
|
||||||
export const mutedNoteReasons = consts.mutedNoteReasons;
|
export const mutedNoteReasons = consts.mutedNoteReasons;
|
||||||
export const languages = consts.languages;
|
export const languages = consts.languages;
|
||||||
export const ffVisibility = consts.ffVisibility;
|
export const ffVisibility = consts.ffVisibility;
|
||||||
|
export const instanceSortParam = consts.instanceSortParam;
|
||||||
|
|
||||||
// api extractor not supported yet
|
// api extractor not supported yet
|
||||||
//export * as api from './api';
|
//export * as api from './api';
|
||||||
|
|
|
@ -236,14 +236,14 @@ export default class Stream extends EventEmitter<StreamEvents> {
|
||||||
// TODO: これらのクラスを Stream クラスの内部クラスにすれば余計なメンバをpublicにしないで済むかも?
|
// TODO: これらのクラスを Stream クラスの内部クラスにすれば余計なメンバをpublicにしないで済むかも?
|
||||||
// もしくは @internal を使う? https://www.typescriptlang.org/tsconfig#stripInternal
|
// もしくは @internal を使う? https://www.typescriptlang.org/tsconfig#stripInternal
|
||||||
class Pool {
|
class Pool {
|
||||||
public channel: string;
|
public channel: keyof Channels;
|
||||||
public id: string;
|
public id: string;
|
||||||
protected stream: Stream;
|
protected stream: Stream;
|
||||||
public users = 0;
|
public users = 0;
|
||||||
private disposeTimerId: any;
|
private disposeTimerId: any;
|
||||||
private isConnected = false;
|
private isConnected = false;
|
||||||
|
|
||||||
constructor(stream: Stream, channel: string, id: string) {
|
constructor(stream: Stream, channel: keyof Channels, id: string) {
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -301,7 +301,7 @@ class Pool {
|
||||||
export abstract class Connection<
|
export abstract class Connection<
|
||||||
Channel extends AnyOf<Channels> = any,
|
Channel extends AnyOf<Channels> = any,
|
||||||
> extends EventEmitter<Channel["events"]> {
|
> extends EventEmitter<Channel["events"]> {
|
||||||
public channel: string;
|
public channel: keyof Channels;
|
||||||
protected stream: Stream;
|
protected stream: Stream;
|
||||||
public abstract id: string;
|
public abstract id: string;
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ export abstract class Connection<
|
||||||
public inCount = 0; // for debug
|
public inCount = 0; // for debug
|
||||||
public outCount = 0; // for debug
|
public outCount = 0; // for debug
|
||||||
|
|
||||||
constructor(stream: Stream, channel: string, name?: string) {
|
constructor(stream: Stream, channel: keyof Channels, name?: string) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
|
@ -342,7 +342,12 @@ class SharedConnection<
|
||||||
return this.pool.id;
|
return this.pool.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(stream: Stream, channel: string, pool: Pool, name?: string) {
|
constructor(
|
||||||
|
stream: Stream,
|
||||||
|
channel: keyof Channels,
|
||||||
|
pool: Pool,
|
||||||
|
name?: string,
|
||||||
|
) {
|
||||||
super(stream, channel, name);
|
super(stream, channel, name);
|
||||||
|
|
||||||
this.pool = pool;
|
this.pool = pool;
|
||||||
|
@ -364,7 +369,7 @@ class NonSharedConnection<
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
stream: Stream,
|
stream: Stream,
|
||||||
channel: string,
|
channel: keyof Channels,
|
||||||
id: string,
|
id: string,
|
||||||
params: Channel["params"],
|
params: Channel["params"],
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -10,9 +10,14 @@ import type {
|
||||||
User,
|
User,
|
||||||
UserGroup,
|
UserGroup,
|
||||||
} from "./entities";
|
} from "./entities";
|
||||||
|
import type { Connection } from "./streaming";
|
||||||
|
|
||||||
type FIXME = any;
|
type FIXME = any;
|
||||||
|
|
||||||
|
type TimelineParams = {
|
||||||
|
withReplies?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type Channels = {
|
export type Channels = {
|
||||||
main: {
|
main: {
|
||||||
params: null;
|
params: null;
|
||||||
|
@ -56,35 +61,35 @@ export type Channels = {
|
||||||
receives: null;
|
receives: null;
|
||||||
};
|
};
|
||||||
homeTimeline: {
|
homeTimeline: {
|
||||||
params: null;
|
params?: TimelineParams;
|
||||||
events: {
|
events: {
|
||||||
note: (payload: Note) => void;
|
note: (payload: Note) => void;
|
||||||
};
|
};
|
||||||
receives: null;
|
receives: null;
|
||||||
};
|
};
|
||||||
localTimeline: {
|
localTimeline: {
|
||||||
params: null;
|
params: TimelineParams;
|
||||||
events: {
|
events: {
|
||||||
note: (payload: Note) => void;
|
note: (payload: Note) => void;
|
||||||
};
|
};
|
||||||
receives: null;
|
receives: null;
|
||||||
};
|
};
|
||||||
hybridTimeline: {
|
hybridTimeline: {
|
||||||
params: null;
|
params: TimelineParams;
|
||||||
events: {
|
events: {
|
||||||
note: (payload: Note) => void;
|
note: (payload: Note) => void;
|
||||||
};
|
};
|
||||||
receives: null;
|
receives: null;
|
||||||
};
|
};
|
||||||
recommendedTimeline: {
|
recommendedTimeline: {
|
||||||
params: null;
|
params: TimelineParams;
|
||||||
events: {
|
events: {
|
||||||
note: (payload: Note) => void;
|
note: (payload: Note) => void;
|
||||||
};
|
};
|
||||||
receives: null;
|
receives: null;
|
||||||
};
|
};
|
||||||
globalTimeline: {
|
globalTimeline: {
|
||||||
params: null;
|
params: TimelineParams;
|
||||||
events: {
|
events: {
|
||||||
note: (payload: Note) => void;
|
note: (payload: Note) => void;
|
||||||
};
|
};
|
||||||
|
@ -195,3 +200,5 @@ export type BroadcastEvents = {
|
||||||
emoji: CustomEmoji;
|
emoji: CustomEmoji;
|
||||||
}) => void;
|
}) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChannelOf<C extends keyof Channels> = Connection<Channels[C]>;
|
||||||
|
|
7
packages/firefish-js/src/type-utils.ts
Normal file
7
packages/firefish-js/src/type-utils.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import type { Endpoints } from "./api.types";
|
||||||
|
|
||||||
|
type PropertyOfType<Type, U> = {
|
||||||
|
[K in keyof Type]: Type[K] extends U ? K : never;
|
||||||
|
}[keyof Type];
|
||||||
|
|
||||||
|
export type EndpointsOf<T> = PropertyOfType<Endpoints, { res: T }>;
|
|
@ -785,6 +785,9 @@ importers:
|
||||||
vue-prism-editor:
|
vue-prism-editor:
|
||||||
specifier: 2.0.0-alpha.2
|
specifier: 2.0.0-alpha.2
|
||||||
version: 2.0.0-alpha.2(vue@3.4.21)
|
version: 2.0.0-alpha.2(vue@3.4.21)
|
||||||
|
vue-tsc:
|
||||||
|
specifier: 2.0.6
|
||||||
|
version: 2.0.6(typescript@5.4.3)
|
||||||
|
|
||||||
packages/firefish-js:
|
packages/firefish-js:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4805,6 +4808,25 @@ packages:
|
||||||
vue: 3.4.21(typescript@5.4.3)
|
vue: 3.4.21(typescript@5.4.3)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@volar/language-core@2.1.6:
|
||||||
|
resolution: {integrity: sha512-pAlMCGX/HatBSiDFMdMyqUshkbwWbLxpN/RL7HCQDOo2gYBE+uS+nanosLc1qR6pTQ/U8q00xt8bdrrAFPSC0A==}
|
||||||
|
dependencies:
|
||||||
|
'@volar/source-map': 2.1.6
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@volar/source-map@2.1.6:
|
||||||
|
resolution: {integrity: sha512-TeyH8pHHonRCHYI91J7fWUoxi0zWV8whZTVRlsWHSYfjm58Blalkf9LrZ+pj6OiverPTmrHRkBsG17ScQyWECw==}
|
||||||
|
dependencies:
|
||||||
|
muggle-string: 0.4.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@volar/typescript@2.1.6:
|
||||||
|
resolution: {integrity: sha512-JgPGhORHqXuyC3r6skPmPHIZj4LoMmGlYErFTuPNBq9Nhc9VTv7ctHY7A3jMN3ngKEfRrfnUcwXHztvdSQqNfw==}
|
||||||
|
dependencies:
|
||||||
|
'@volar/language-core': 2.1.6
|
||||||
|
path-browserify: 1.0.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/compiler-core@3.4.21:
|
/@vue/compiler-core@3.4.21:
|
||||||
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
|
resolution: {integrity: sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4851,6 +4873,24 @@ packages:
|
||||||
'@vue/shared': 3.4.21
|
'@vue/shared': 3.4.21
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@vue/language-core@2.0.6(typescript@5.4.3):
|
||||||
|
resolution: {integrity: sha512-UzqU12tzf9XLqRO3TiWPwRNpP4fyUzE6MAfOQWQNZ4jy6a30ARRUpmODDKq6O8C4goMc2AlPqTmjOHPjHkilSg==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@volar/language-core': 2.1.6
|
||||||
|
'@vue/compiler-dom': 3.4.21
|
||||||
|
'@vue/shared': 3.4.21
|
||||||
|
computeds: 0.0.1
|
||||||
|
minimatch: 9.0.3
|
||||||
|
path-browserify: 1.0.1
|
||||||
|
typescript: 5.4.3
|
||||||
|
vue-template-compiler: 2.7.16
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@vue/reactivity@3.4.21:
|
/@vue/reactivity@3.4.21:
|
||||||
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
resolution: {integrity: sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6795,6 +6835,10 @@ packages:
|
||||||
readable-stream: 4.5.2
|
readable-stream: 4.5.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/computeds@0.0.1:
|
||||||
|
resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/concat-map@0.0.1:
|
/concat-map@0.0.1:
|
||||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||||
|
|
||||||
|
@ -7317,6 +7361,10 @@ packages:
|
||||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/de-indent@1.0.2:
|
||||||
|
resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/debug@2.6.9:
|
/debug@2.6.9:
|
||||||
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -12896,6 +12944,10 @@ packages:
|
||||||
msgpackr-extract: 3.0.2
|
msgpackr-extract: 3.0.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/muggle-string@0.4.1:
|
||||||
|
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/multer@1.4.5-lts.1:
|
/multer@1.4.5-lts.1:
|
||||||
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==}
|
resolution: {integrity: sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==}
|
||||||
engines: {node: '>= 6.0.0'}
|
engines: {node: '>= 6.0.0'}
|
||||||
|
@ -13676,6 +13728,10 @@ packages:
|
||||||
resolution: {integrity: sha512-Wy8PXTLqPAN0oEgBrlnsXPMww3SYJ44tQ8aVrGAI4h4JZYCS0oYqsPqtPR8OhJpv6qFbpbB7XAn0liKV7EXubA==}
|
resolution: {integrity: sha512-Wy8PXTLqPAN0oEgBrlnsXPMww3SYJ44tQ8aVrGAI4h4JZYCS0oYqsPqtPR8OhJpv6qFbpbB7XAn0liKV7EXubA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/path-browserify@1.0.1:
|
||||||
|
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/path-dirname@1.0.2:
|
/path-dirname@1.0.2:
|
||||||
resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
|
resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
@ -17233,6 +17289,25 @@ packages:
|
||||||
vue: 3.4.21(typescript@5.4.3)
|
vue: 3.4.21(typescript@5.4.3)
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vue-template-compiler@2.7.16:
|
||||||
|
resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==}
|
||||||
|
dependencies:
|
||||||
|
de-indent: 1.0.2
|
||||||
|
he: 1.2.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/vue-tsc@2.0.6(typescript@5.4.3):
|
||||||
|
resolution: {integrity: sha512-kK50W4XqQL34vHRkxlRWLicrT6+F9xfgCgJ4KSmCHcytKzc1u3c94XXgI+CjmhOSxyw0krpExF7Obo7y4+0dVQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
typescript: '*'
|
||||||
|
dependencies:
|
||||||
|
'@volar/typescript': 2.1.6
|
||||||
|
'@vue/language-core': 2.0.6(typescript@5.4.3)
|
||||||
|
semver: 7.6.0
|
||||||
|
typescript: 5.4.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vue@2.7.14:
|
/vue@2.7.14:
|
||||||
resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
|
resolution: {integrity: sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==}
|
||||||
deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
|
deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.
|
||||||
|
|
Loading…
Reference in a new issue