refactor
This commit is contained in:
parent
eb9915baf8
commit
d6a1046361
|
@ -49,15 +49,12 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||||
public async pack(
|
public async pack(
|
||||||
src: MiNoteReaction['id'] | MiNoteReaction,
|
src: MiNoteReaction['id'] | MiNoteReaction,
|
||||||
me?: { id: MiUser['id'] } | null | undefined,
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
options?: {
|
options?: object,
|
||||||
withNote: boolean;
|
|
||||||
},
|
|
||||||
hints?: {
|
hints?: {
|
||||||
packedUser?: Packed<'UserLite'>
|
packedUser?: Packed<'UserLite'>
|
||||||
},
|
},
|
||||||
): Promise<Packed<'NoteReaction'>> {
|
): Promise<Packed<'NoteReaction'>> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
withNote: false,
|
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
|
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
|
||||||
|
@ -67,9 +64,6 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||||
createdAt: this.idService.parse(reaction.id).date.toISOString(),
|
createdAt: this.idService.parse(reaction.id).date.toISOString(),
|
||||||
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
|
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
|
||||||
type: this.reactionService.convertLegacyReaction(reaction.reaction),
|
type: this.reactionService.convertLegacyReaction(reaction.reaction),
|
||||||
...(opts.withNote ? {
|
|
||||||
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
|
|
||||||
} : {}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,16 +71,50 @@ export class NoteReactionEntityService implements OnModuleInit {
|
||||||
public async packMany(
|
public async packMany(
|
||||||
reactions: MiNoteReaction[],
|
reactions: MiNoteReaction[],
|
||||||
me?: { id: MiUser['id'] } | null | undefined,
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
options?: {
|
options?: object,
|
||||||
withNote: boolean;
|
|
||||||
},
|
|
||||||
): Promise<Packed<'NoteReaction'>[]> {
|
): Promise<Packed<'NoteReaction'>[]> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
withNote: false,
|
|
||||||
}, options);
|
}, options);
|
||||||
const _users = reactions.map(({ user, userId }) => user ?? userId);
|
const _users = reactions.map(({ user, userId }) => user ?? userId);
|
||||||
const _userMap = await this.userEntityService.packMany(_users, me)
|
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||||
.then(users => new Map(users.map(u => [u.id, u])));
|
.then(users => new Map(users.map(u => [u.id, u])));
|
||||||
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
|
return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packWithNote(
|
||||||
|
src: MiNoteReaction['id'] | MiNoteReaction,
|
||||||
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
|
options?: object,
|
||||||
|
hints?: {
|
||||||
|
packedUser?: Packed<'UserLite'>
|
||||||
|
},
|
||||||
|
): Promise<Packed<'NoteReactionWithNote'>> {
|
||||||
|
const opts = Object.assign({
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: reaction.id,
|
||||||
|
createdAt: this.idService.parse(reaction.id).date.toISOString(),
|
||||||
|
user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
|
||||||
|
type: this.reactionService.convertLegacyReaction(reaction.reaction),
|
||||||
|
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async packManyWithNote(
|
||||||
|
reactions: MiNoteReaction[],
|
||||||
|
me?: { id: MiUser['id'] } | null | undefined,
|
||||||
|
options?: object,
|
||||||
|
): Promise<Packed<'NoteReactionWithNote'>[]> {
|
||||||
|
const opts = Object.assign({
|
||||||
|
}, options);
|
||||||
|
const _users = reactions.map(({ user, userId }) => user ?? userId);
|
||||||
|
const _userMap = await this.userEntityService.packMany(_users, me)
|
||||||
|
.then(users => new Map(users.map(u => [u.id, u])));
|
||||||
|
return Promise.all(reactions.map(reaction => this.packWithNote(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { packedFollowingSchema } from '@/models/json-schema/following.js';
|
||||||
import { packedMutingSchema } from '@/models/json-schema/muting.js';
|
import { packedMutingSchema } from '@/models/json-schema/muting.js';
|
||||||
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
|
import { packedRenoteMutingSchema } from '@/models/json-schema/renote-muting.js';
|
||||||
import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
|
import { packedBlockingSchema } from '@/models/json-schema/blocking.js';
|
||||||
import { packedNoteReactionSchema } from '@/models/json-schema/note-reaction.js';
|
import { packedNoteReactionSchema, packedNoteReactionWithNoteSchema } from '@/models/json-schema/note-reaction.js';
|
||||||
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
|
import { packedHashtagSchema } from '@/models/json-schema/hashtag.js';
|
||||||
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
|
import { packedInviteCodeSchema } from '@/models/json-schema/invite-code.js';
|
||||||
import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js';
|
import { packedPageBlockSchema, packedPageSchema } from '@/models/json-schema/page.js';
|
||||||
|
@ -92,6 +92,7 @@ export const refs = {
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
NoteDraft: packedNoteDraftSchema,
|
NoteDraft: packedNoteDraftSchema,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
|
NoteReactionWithNote: packedNoteReactionWithNoteSchema,
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
NoteFavorite: packedNoteFavoriteSchema,
|
||||||
Notification: packedNotificationSchema,
|
Notification: packedNotificationSchema,
|
||||||
DriveFile: packedDriveFileSchema,
|
DriveFile: packedDriveFileSchema,
|
||||||
|
|
|
@ -10,7 +10,6 @@ export const packedNoteReactionSchema = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
format: 'id',
|
format: 'id',
|
||||||
example: 'xxxxxxxxxx',
|
|
||||||
},
|
},
|
||||||
createdAt: {
|
createdAt: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -28,3 +27,33 @@ export const packedNoteReactionSchema = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export const packedNoteReactionWithNoteSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserLite',
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
note: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'Note',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
|
@ -49,6 +49,34 @@ export const meta = {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
icon: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
|
display: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
isActive: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
forExistingUsers: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
silence: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
needConfirmationToRead: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
},
|
||||||
imageUrl: {
|
imageUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
|
|
@ -23,6 +23,16 @@ export const meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'UserList',
|
ref: 'UserList',
|
||||||
|
properties: {
|
||||||
|
likedCount: {
|
||||||
|
type: 'number',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
isLiked: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: true, nullable: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|
|
@ -28,7 +28,7 @@ export const meta = {
|
||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
ref: 'NoteReaction',
|
ref: 'NoteReactionWithNote',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
return await this.noteReactionEntityService.packMany(reactions, me, { withNote: true });
|
return await this.noteReactionEntityService.packManyWithNote(reactions, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,11 +368,6 @@ export async function mainBoot() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
main.on('unreadAntenna', () => {
|
|
||||||
updateCurrentAccountPartial({ hasUnreadAntenna: true });
|
|
||||||
sound.playMisskeySfx('antenna');
|
|
||||||
});
|
|
||||||
|
|
||||||
main.on('newChatMessage', () => {
|
main.on('newChatMessage', () => {
|
||||||
updateCurrentAccountPartial({ hasUnreadChatMessages: true });
|
updateCurrentAccountPartial({ hasUnreadChatMessages: true });
|
||||||
sound.playMisskeySfx('chatMessage');
|
sound.playMisskeySfx('chatMessage');
|
||||||
|
|
|
@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
|
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
|
||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
<div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
|
<div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg ?? '' }">
|
||||||
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
|
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'In',
|
name: 'In',
|
||||||
|
@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Users',
|
name: 'Users',
|
||||||
|
@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Notes',
|
name: 'Notes',
|
||||||
|
@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Following',
|
name: 'Following',
|
||||||
|
@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
bytes: true,
|
bytes: true,
|
||||||
series: [{
|
series: [{
|
||||||
|
@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
|
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
|
const host = props.args?.host;
|
||||||
|
if (host == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Drive files',
|
name: 'Drive files',
|
||||||
|
@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
const userId = props.args?.user?.id;
|
||||||
|
if (userId == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [...(props.args?.withoutAll ? [] : [{
|
series: [...(props.args?.withoutAll ? [] : [{
|
||||||
name: 'All',
|
name: 'All',
|
||||||
|
@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
const userId = props.args?.user?.id;
|
||||||
|
if (userId == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Unique PV (user)',
|
name: 'Unique PV (user)',
|
||||||
|
@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
const userId = props.args?.user?.id;
|
||||||
|
if (userId == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Local',
|
name: 'Local',
|
||||||
|
@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
const userId = props.args?.user?.id;
|
||||||
|
if (userId == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
series: [{
|
series: [{
|
||||||
name: 'Local',
|
name: 'Local',
|
||||||
|
@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
|
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
|
||||||
const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
|
const userId = props.args?.user?.id;
|
||||||
|
if (userId == null) return { series: [] };
|
||||||
|
|
||||||
|
const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span });
|
||||||
return {
|
return {
|
||||||
bytes: true,
|
bytes: true,
|
||||||
series: [{
|
series: [{
|
||||||
|
|
|
@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, useTemplateRef, ref } from 'vue';
|
import { onMounted, useTemplateRef, ref, onUnmounted } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import Cropper from 'cropperjs';
|
import Cropper from 'cropperjs';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
@ -55,17 +55,19 @@ const imgEl = useTemplateRef('imgEl');
|
||||||
let cropper: Cropper | null = null;
|
let cropper: Cropper | null = null;
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
|
||||||
const ok = async () => {
|
async function ok() {
|
||||||
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
|
const promise = new Promise<Blob>(async (res) => {
|
||||||
const croppedImage = await cropper?.getCropperImage();
|
if (cropper == null) throw new Error('Cropper is not initialized');
|
||||||
const croppedSection = await cropper?.getCropperSelection();
|
|
||||||
|
const croppedImage = await cropper.getCropperImage()!;
|
||||||
|
const croppedSection = await cropper.getCropperSelection()!;
|
||||||
|
|
||||||
// 拡大率を計算し、(ほぼ)元の大きさに戻す
|
// 拡大率を計算し、(ほぼ)元の大きさに戻す
|
||||||
const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
|
const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
|
||||||
const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
|
const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
|
||||||
|
|
||||||
const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender });
|
const croppedCanvas = await croppedSection.$toCanvas({ width: widthToRender });
|
||||||
croppedCanvas?.toBlob(blob => {
|
croppedCanvas.toBlob(blob => {
|
||||||
if (!blob) return;
|
if (!blob) return;
|
||||||
res(blob);
|
res(blob);
|
||||||
});
|
});
|
||||||
|
@ -74,25 +76,27 @@ const ok = async () => {
|
||||||
const f = await promise;
|
const f = await promise;
|
||||||
|
|
||||||
emit('ok', f);
|
emit('ok', f);
|
||||||
dialogEl.value!.close();
|
if (dialogEl.value != null) dialogEl.value.close();
|
||||||
};
|
}
|
||||||
|
|
||||||
const cancel = () => {
|
function cancel() {
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
dialogEl.value!.close();
|
if (dialogEl.value != null) dialogEl.value.close();
|
||||||
};
|
}
|
||||||
|
|
||||||
const onImageLoad = () => {
|
function onImageLoad() {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
||||||
if (cropper) {
|
if (cropper) {
|
||||||
cropper.getCropperImage()!.$center('contain');
|
cropper.getCropperImage()!.$center('contain');
|
||||||
cropper.getCropperSelection()!.$center();
|
cropper.getCropperSelection()!.$center();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
cropper = new Cropper(imgEl.value!, {
|
if (imgEl.value == null) return; // TSを黙らすため
|
||||||
|
|
||||||
|
cropper = new Cropper(imgEl.value, {
|
||||||
});
|
});
|
||||||
|
|
||||||
const computedStyle = getComputedStyle(window.document.documentElement);
|
const computedStyle = getComputedStyle(window.document.documentElement);
|
||||||
|
@ -104,16 +108,22 @@ onMounted(() => {
|
||||||
selection.outlined = true;
|
selection.outlined = true;
|
||||||
|
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
cropper!.getCropperImage()!.$center('contain');
|
if (cropper == null) return;
|
||||||
|
cropper.getCropperImage()!.$center('contain');
|
||||||
selection.$center();
|
selection.$center();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
// モーダルオープンアニメーションが終わったあとで再度調整
|
// モーダルオープンアニメーションが終わったあとで再度調整
|
||||||
window.setTimeout(() => {
|
window.setTimeout(() => {
|
||||||
cropper!.getCropperImage()!.$center('contain');
|
if (cropper == null) return;
|
||||||
|
cropper.getCropperImage()!.$center('contain');
|
||||||
selection.$center();
|
selection.$center();
|
||||||
}, 500);
|
}, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
URL.revokeObjectURL(imgUrl);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -152,7 +152,7 @@ const props = withDefaults(defineProps<{
|
||||||
asDrawer?: boolean;
|
asDrawer?: boolean;
|
||||||
asWindow?: boolean;
|
asWindow?: boolean;
|
||||||
asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう
|
asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう
|
||||||
targetNote?: Misskey.entities.Note;
|
targetNote?: Misskey.entities.Note | null;
|
||||||
}>(), {
|
}>(), {
|
||||||
showPinned: true,
|
showPinned: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,11 +44,11 @@ import { prefer } from '@/preferences.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
manualShowing?: boolean | null;
|
manualShowing?: boolean | null;
|
||||||
anchorElement?: HTMLElement;
|
anchorElement?: HTMLElement | null;
|
||||||
showPinned?: boolean;
|
showPinned?: boolean;
|
||||||
pinnedEmojis?: string[],
|
pinnedEmojis?: string[],
|
||||||
asReactionPicker?: boolean;
|
asReactionPicker?: boolean;
|
||||||
targetNote?: Misskey.entities.Note;
|
targetNote?: Misskey.entities.Note | null;
|
||||||
choseAndClose?: boolean;
|
choseAndClose?: boolean;
|
||||||
}>(), {
|
}>(), {
|
||||||
manualShowing: null,
|
manualShowing: null,
|
||||||
|
|
|
@ -91,7 +91,7 @@ const emit = defineEmits<{
|
||||||
(ev: 'opened'): void;
|
(ev: 'opened'): void;
|
||||||
(ev: 'click'): void;
|
(ev: 'click'): void;
|
||||||
(ev: 'esc'): void;
|
(ev: 'esc'): void;
|
||||||
(ev: 'close'): void;
|
(ev: 'close'): void; // TODO: (refactor) closing に改名する
|
||||||
(ev: 'closed'): void;
|
(ev: 'closed'): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -148,7 +148,6 @@ function close(opts: { useSendAnimation?: boolean } = {}) {
|
||||||
useSendAnime.value = true;
|
useSendAnime.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
|
||||||
if (props.anchorElement) props.anchorElement.style.pointerEvents = 'auto';
|
if (props.anchorElement) props.anchorElement.style.pointerEvents = 'auto';
|
||||||
showing.value = false;
|
showing.value = false;
|
||||||
emit('close');
|
emit('close');
|
||||||
|
@ -319,7 +318,6 @@ const alignObserver = new ResizeObserver((entries, observer) => {
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
watch(() => props.anchorElement, async () => {
|
watch(() => props.anchorElement, async () => {
|
||||||
if (props.anchorElement) {
|
if (props.anchorElement) {
|
||||||
// eslint-disable-next-line vue/no-mutating-props
|
|
||||||
props.anchorElement.style.pointerEvents = 'none';
|
props.anchorElement.style.pointerEvents = 'none';
|
||||||
}
|
}
|
||||||
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.anchorElement) != null);
|
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.anchorElement) != null);
|
||||||
|
|
|
@ -58,18 +58,22 @@ const emit = defineEmits<{
|
||||||
const buttonEl = useTemplateRef('buttonEl');
|
const buttonEl = useTemplateRef('buttonEl');
|
||||||
|
|
||||||
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
|
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
|
||||||
const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));
|
|
||||||
|
|
||||||
const canToggle = computed(() => {
|
const canToggle = computed(() => {
|
||||||
|
const emoji = customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
//return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);
|
//return !props.reaction.match(/@\w/) && $i && emoji && checkReactionPermissions($i, props.note, emoji);
|
||||||
return !props.reaction.match(/@\w/) && $i && emoji.value;
|
return !props.reaction.match(/@\w/) && $i && emoji;
|
||||||
});
|
});
|
||||||
const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
|
const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
|
||||||
const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.');
|
const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.');
|
||||||
|
|
||||||
async function toggleReaction() {
|
async function toggleReaction() {
|
||||||
if (!canToggle.value) return;
|
if (!canToggle.value) return;
|
||||||
|
if ($i == null) return;
|
||||||
|
|
||||||
|
const me = $i;
|
||||||
|
|
||||||
const oldReaction = props.myReaction;
|
const oldReaction = props.myReaction;
|
||||||
if (oldReaction) {
|
if (oldReaction) {
|
||||||
|
@ -93,7 +97,7 @@ async function toggleReaction() {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
noteEvents.emit(`unreacted:${props.noteId}`, {
|
noteEvents.emit(`unreacted:${props.noteId}`, {
|
||||||
userId: $i!.id,
|
userId: me.id,
|
||||||
reaction: oldReaction,
|
reaction: oldReaction,
|
||||||
});
|
});
|
||||||
if (oldReaction !== props.reaction) {
|
if (oldReaction !== props.reaction) {
|
||||||
|
@ -101,10 +105,12 @@ async function toggleReaction() {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
reaction: props.reaction,
|
reaction: props.reaction,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
const emoji = customEmojisMap.get(emojiName.value);
|
||||||
|
if (emoji == null) return;
|
||||||
noteEvents.emit(`reacted:${props.noteId}`, {
|
noteEvents.emit(`reacted:${props.noteId}`, {
|
||||||
userId: $i!.id,
|
userId: me.id,
|
||||||
reaction: props.reaction,
|
reaction: props.reaction,
|
||||||
emoji: emoji.value,
|
emoji: emoji,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -131,10 +137,13 @@ async function toggleReaction() {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
reaction: props.reaction,
|
reaction: props.reaction,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
const emoji = customEmojisMap.get(emojiName.value);
|
||||||
|
if (emoji == null) return;
|
||||||
|
|
||||||
noteEvents.emit(`reacted:${props.noteId}`, {
|
noteEvents.emit(`reacted:${props.noteId}`, {
|
||||||
userId: $i!.id,
|
userId: me.id,
|
||||||
reaction: props.reaction,
|
reaction: props.reaction,
|
||||||
emoji: emoji.value,
|
emoji: emoji,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// TODO: 上位コンポーネントでやる
|
// TODO: 上位コンポーネントでやる
|
||||||
|
@ -217,6 +226,8 @@ onMounted(() => {
|
||||||
|
|
||||||
if (!mock) {
|
if (!mock) {
|
||||||
useTooltip(buttonEl, async (showing) => {
|
useTooltip(buttonEl, async (showing) => {
|
||||||
|
if (buttonEl.value == null) return;
|
||||||
|
|
||||||
const reactions = await misskeyApiGet('notes/reactions', {
|
const reactions = await misskeyApiGet('notes/reactions', {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
type: props.reaction,
|
type: props.reaction,
|
||||||
|
|
|
@ -59,7 +59,7 @@ import { prefer } from '@/preferences.js';
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
tabs?: Tab[];
|
tabs?: Tab[];
|
||||||
tab?: string;
|
tab?: string;
|
||||||
rootEl?: HTMLElement;
|
rootEl?: HTMLElement | null;
|
||||||
}>(), {
|
}>(), {
|
||||||
tabs: () => ([] as Tab[]),
|
tabs: () => ([] as Tab[]),
|
||||||
});
|
});
|
||||||
|
|
|
@ -143,8 +143,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
|
<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedRoleIds.includes(role.id)" :class="$style.roleItemSub">
|
<div v-if="expandedRoleIds.includes(role.id)" :class="$style.roleItemSub">
|
||||||
<div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id).createdAt" mode="detail"/></div>
|
<div>Assigned: <MkTime :time="info.roleAssigns.find(a => a.roleId === role.id)!.createdAt" mode="detail"/></div>
|
||||||
<div v-if="info.roleAssigns.find(a => a.roleId === role.id).expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id).expiresAt).toLocaleString() }}</div>
|
<div v-if="info.roleAssigns.find(a => a.roleId === role.id)!.expiresAt">Period: {{ new Date(info.roleAssigns.find(a => a.roleId === role.id)!.expiresAt!).toLocaleString() }}</div>
|
||||||
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
|
<div v-else>Period: {{ i18n.ts.indefinitely }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -119,7 +119,7 @@ async function _fetch_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function postThis() {
|
function postThis() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
os.post({
|
os.post({
|
||||||
initialFiles: [file.value],
|
initialFiles: [file.value],
|
||||||
|
@ -127,11 +127,13 @@ function postThis() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function move() {
|
function move() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
|
const f = file.value;
|
||||||
|
|
||||||
selectDriveFolder(null).then(folder => {
|
selectDriveFolder(null).then(folder => {
|
||||||
misskeyApi('drive/files/update', {
|
misskeyApi('drive/files/update', {
|
||||||
fileId: file.value.id,
|
fileId: f.id,
|
||||||
folderId: folder[0] ? folder[0].id : null,
|
folderId: folder[0] ? folder[0].id : null,
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
await _fetch_();
|
await _fetch_();
|
||||||
|
@ -140,7 +142,7 @@ function move() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSensitive() {
|
function toggleSensitive() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
os.apiWithDialog('drive/files/update', {
|
os.apiWithDialog('drive/files/update', {
|
||||||
fileId: file.value.id,
|
fileId: file.value.id,
|
||||||
|
@ -157,7 +159,9 @@ function toggleSensitive() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function rename() {
|
function rename() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
|
const f = file.value;
|
||||||
|
|
||||||
os.inputText({
|
os.inputText({
|
||||||
title: i18n.ts.renameFile,
|
title: i18n.ts.renameFile,
|
||||||
|
@ -166,7 +170,7 @@ function rename() {
|
||||||
}).then(({ canceled, result: name }) => {
|
}).then(({ canceled, result: name }) => {
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
os.apiWithDialog('drive/files/update', {
|
os.apiWithDialog('drive/files/update', {
|
||||||
fileId: file.value.id,
|
fileId: f.id,
|
||||||
name: name,
|
name: name,
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
await _fetch_();
|
await _fetch_();
|
||||||
|
@ -175,7 +179,9 @@ function rename() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function describe() {
|
async function describe() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
|
const f = file.value;
|
||||||
|
|
||||||
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
|
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkFileCaptionEditWindow.vue').then(x => x.default), {
|
||||||
default: file.value.comment ?? '',
|
default: file.value.comment ?? '',
|
||||||
|
@ -183,7 +189,7 @@ async function describe() {
|
||||||
}, {
|
}, {
|
||||||
done: caption => {
|
done: caption => {
|
||||||
os.apiWithDialog('drive/files/update', {
|
os.apiWithDialog('drive/files/update', {
|
||||||
fileId: file.value.id,
|
fileId: f.id,
|
||||||
comment: caption.length === 0 ? null : caption,
|
comment: caption.length === 0 ? null : caption,
|
||||||
}).then(async () => {
|
}).then(async () => {
|
||||||
await _fetch_();
|
await _fetch_();
|
||||||
|
@ -194,7 +200,7 @@ async function describe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteFile() {
|
async function deleteFile() {
|
||||||
if (!file.value) return;
|
if (file.value == null) return;
|
||||||
|
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
|
|
|
@ -26,18 +26,12 @@ import { definePage } from '@/page.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
tag?: string;
|
|
||||||
initialTab?: string;
|
initialTab?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
initialTab: 'featured',
|
initialTab: 'featured',
|
||||||
});
|
});
|
||||||
|
|
||||||
const tab = ref(props.initialTab);
|
const tab = ref(props.initialTab);
|
||||||
const tagsEl = useTemplateRef('tagsEl');
|
|
||||||
|
|
||||||
watch(() => props.tag, () => {
|
|
||||||
if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
|
|
||||||
});
|
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ definePage(() => ({
|
||||||
icon: 'ti ti-list',
|
icon: 'ti ti-list',
|
||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.userItem {
|
.userItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -126,7 +126,7 @@ function fetchNote() {
|
||||||
noteId: props.noteId,
|
noteId: props.noteId,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
note.value = res;
|
note.value = res;
|
||||||
const appearNote = getAppearNote(res);
|
const appearNote = getAppearNote(res) ?? res;
|
||||||
// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
|
// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
|
||||||
if ((appearNote.clippedCount ?? 0) > 0 || new Date(appearNote.createdAt).getTime() < new Date('2023-10-01').getTime()) {
|
if ((appearNote.clippedCount ?? 0) > 0 || new Date(appearNote.createdAt).getTime() < new Date('2023-10-01').getTime()) {
|
||||||
misskeyApi('notes/clips', {
|
misskeyApi('notes/clips', {
|
||||||
|
|
|
@ -62,10 +62,10 @@ const props = defineProps<{
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const scope = computed(() => props.path.split('/').slice(0, -1));
|
const scope = computed(() => props.path.split('/').slice(0, -1));
|
||||||
const key = computed(() => props.path.split('/').at(-1));
|
const key = computed(() => props.path.split('/').at(-1)!);
|
||||||
|
|
||||||
const value = ref<any>(null);
|
const value = ref<any>(null);
|
||||||
const valueForEditor = ref<string | null>(null);
|
const valueForEditor = ref<string>('');
|
||||||
|
|
||||||
function fetchValue() {
|
function fetchValue() {
|
||||||
misskeyApi('i/registry/get-detail', {
|
misskeyApi('i/registry/get-detail', {
|
||||||
|
|
|
@ -108,7 +108,7 @@ async function openDecoration(avatarDecoration: {
|
||||||
offsetY: payload.offsetY,
|
offsetY: payload.offsetY,
|
||||||
};
|
};
|
||||||
const update = [...$i.avatarDecorations];
|
const update = [...$i.avatarDecorations];
|
||||||
update[index] = decoration;
|
update[index!] = decoration;
|
||||||
await os.apiWithDialog('i/update', {
|
await os.apiWithDialog('i/update', {
|
||||||
avatarDecorations: update,
|
avatarDecorations: update,
|
||||||
});
|
});
|
||||||
|
@ -116,7 +116,7 @@ async function openDecoration(avatarDecoration: {
|
||||||
},
|
},
|
||||||
'detach': async () => {
|
'detach': async () => {
|
||||||
const update = [...$i.avatarDecorations];
|
const update = [...$i.avatarDecorations];
|
||||||
update.splice(index, 1);
|
update.splice(index!, 1);
|
||||||
await os.apiWithDialog('i/update', {
|
await os.apiWithDialog('i/update', {
|
||||||
avatarDecorations: update,
|
avatarDecorations: update,
|
||||||
});
|
});
|
||||||
|
|
|
@ -43,9 +43,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</FormSection>
|
</FormSection>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink>
|
<MkButton @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</MkButton>
|
||||||
<FormLink @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
|
<MkButton @click="testNotification">{{ i18n.ts._notification.sendTestNotification }}</MkButton>
|
||||||
<FormLink @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</FormLink>
|
<MkButton @click="flushNotification">{{ i18n.ts._notification.flushNotification }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -76,6 +76,7 @@ import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
import MkFolder from '@/components/MkFolder.vue';
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { ensureSignin } from '@/i.js';
|
import { ensureSignin } from '@/i.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
@ -96,7 +97,7 @@ const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadM
|
||||||
const userLists = await misskeyApi('users/lists/list');
|
const userLists = await misskeyApi('users/lists/list');
|
||||||
|
|
||||||
async function readAllNotifications() {
|
async function readAllNotifications() {
|
||||||
await os.apiWithDialog('notifications/mark-all-as-read');
|
await os.apiWithDialog('notifications/mark-all-as-read', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateReceiveConfig(type: typeof notificationTypes[number], value: NotificationConfig) {
|
async function updateReceiveConfig(type: typeof notificationTypes[number], value: NotificationConfig) {
|
||||||
|
@ -134,7 +135,7 @@ async function flushNotification() {
|
||||||
|
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
||||||
os.apiWithDialog('notifications/flush');
|
os.apiWithDialog('notifications/flush', {});
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
|
@ -80,14 +80,14 @@ async function change() {
|
||||||
type: 'password',
|
type: 'password',
|
||||||
autocomplete: 'new-password',
|
autocomplete: 'new-password',
|
||||||
});
|
});
|
||||||
if (canceled2) return;
|
if (canceled2 || newPassword == null) return;
|
||||||
|
|
||||||
const { canceled: canceled3, result: newPassword2 } = await os.inputText({
|
const { canceled: canceled3, result: newPassword2 } = await os.inputText({
|
||||||
title: i18n.ts.newPasswordRetype,
|
title: i18n.ts.newPasswordRetype,
|
||||||
type: 'password',
|
type: 'password',
|
||||||
autocomplete: 'new-password',
|
autocomplete: 'new-password',
|
||||||
});
|
});
|
||||||
if (canceled3) return;
|
if (canceled3 || newPassword2 == null) return;
|
||||||
|
|
||||||
if (newPassword !== newPassword2) {
|
if (newPassword !== newPassword2) {
|
||||||
os.alert({
|
os.alert({
|
||||||
|
|
|
@ -205,8 +205,8 @@ import { computed, ref, watch } from 'vue';
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import defaultLightTheme from '@@/themes/l-light.json5';
|
import defaultLightTheme from '@@/themes/l-light.json5';
|
||||||
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
|
import defaultDarkTheme from '@@/themes/d-green-lime.json5';
|
||||||
import type { Theme } from '@/theme.js';
|
|
||||||
import { isSafeMode } from '@@/js/config.js';
|
import { isSafeMode } from '@@/js/config.js';
|
||||||
|
import type { Theme } from '@/theme.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import MkSwitch from '@/components/MkSwitch.vue';
|
import MkSwitch from '@/components/MkSwitch.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
@ -275,6 +275,7 @@ async function toggleDarkMode() {
|
||||||
const value = !store.r.darkMode.value;
|
const value = !store.r.darkMode.value;
|
||||||
if (syncDeviceDarkMode.value) {
|
if (syncDeviceDarkMode.value) {
|
||||||
const { canceled } = await os.confirm({
|
const { canceled } = await os.confirm({
|
||||||
|
type: 'question',
|
||||||
text: i18n.tsx.switchDarkModeManuallyWhenSyncEnabledConfirm({ x: i18n.ts.syncDeviceDarkMode }),
|
text: i18n.tsx.switchDarkModeManuallyWhenSyncEnabledConfirm({ x: i18n.ts.syncDeviceDarkMode }),
|
||||||
});
|
});
|
||||||
if (canceled) return;
|
if (canceled) return;
|
||||||
|
|
|
@ -149,10 +149,8 @@ async function test(type: Misskey.entities.UserWebhook['on'][number]): Promise<v
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => []);
|
||||||
|
|
||||||
definePage(() => ({
|
definePage(() => ({
|
||||||
|
|
|
@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
ref="tlComponent"
|
ref="tlComponent"
|
||||||
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
:key="src + withRenotes + withReplies + onlyFiles + withSensitive"
|
||||||
:class="$style.tl"
|
:class="$style.tl"
|
||||||
:src="src.split(':')[0]"
|
:src="(src.split(':')[0] as (BasicTimelineType | 'list'))"
|
||||||
:list="src.split(':')[1]"
|
:list="src.split(':')[1]"
|
||||||
:withRenotes="withRenotes"
|
:withRenotes="withRenotes"
|
||||||
:withReplies="withReplies"
|
:withReplies="withReplies"
|
||||||
|
|
|
@ -43,6 +43,8 @@ const fetching = ref(true);
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
async function renderChart() {
|
async function renderChart() {
|
||||||
|
if (chartEl.value == null) return;
|
||||||
|
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,15 @@ const props = defineProps<{
|
||||||
const chartEl = useTemplateRef('chartEl');
|
const chartEl = useTemplateRef('chartEl');
|
||||||
const legendEl = useTemplateRef('legendEl');
|
const legendEl = useTemplateRef('legendEl');
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart | null = null;
|
||||||
const chartLimit = 50;
|
const chartLimit = 50;
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
async function renderChart() {
|
async function renderChart() {
|
||||||
|
if (chartEl.value == null) return;
|
||||||
|
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,13 +36,15 @@ const props = defineProps<{
|
||||||
const chartEl = useTemplateRef('chartEl');
|
const chartEl = useTemplateRef('chartEl');
|
||||||
const legendEl = useTemplateRef('legendEl');
|
const legendEl = useTemplateRef('legendEl');
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
let chartInstance: Chart = null;
|
let chartInstance: Chart | null = null;
|
||||||
const chartLimit = 30;
|
const chartLimit = 30;
|
||||||
const fetching = ref(true);
|
const fetching = ref(true);
|
||||||
|
|
||||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||||
|
|
||||||
async function renderChart() {
|
async function renderChart() {
|
||||||
|
if (chartEl.value == null) return;
|
||||||
|
|
||||||
if (chartInstance) {
|
if (chartInstance) {
|
||||||
chartInstance.destroy();
|
chartInstance.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,8 @@ export type SoundStore = {
|
||||||
volume: number;
|
volume: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type OmitStrict<T, K extends keyof T> = T extends any ? Pick<T, Exclude<keyof T, K>> : never;
|
||||||
|
|
||||||
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
|
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
|
||||||
|
|
||||||
export const PREF_DEF = definePreferences({
|
export const PREF_DEF = definePreferences({
|
||||||
|
@ -385,7 +387,7 @@ export const PREF_DEF = definePreferences({
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
default: [] as Plugin[],
|
default: [] as (OmitStrict<Plugin, 'config'> & { config: Record<string, any> })[],
|
||||||
mergeStrategy: (a, b) => {
|
mergeStrategy: (a, b) => {
|
||||||
const sameIdExists = a.some(x => b.some(y => x.installId === y.installId));
|
const sameIdExists = a.some(x => b.some(y => x.installId === y.installId));
|
||||||
if (sameIdExists) throw new Error();
|
if (sameIdExists) throw new Error();
|
||||||
|
|
|
@ -109,10 +109,11 @@ export function definePreferences<T extends Record<string, unknown>>(x: {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
|
export function getInitialPrefValue<K extends keyof PREF>(k: K): ValueOf<K> {
|
||||||
if (typeof PREF_DEF[k].default === 'function') { // factory
|
const _default = PREF_DEF[k as string].default;
|
||||||
return PREF_DEF[k].default();
|
if (typeof _default === 'function') { // factory
|
||||||
|
return _default();
|
||||||
} else {
|
} else {
|
||||||
return PREF_DEF[k].default;
|
return _default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root">
|
<div v-if="$i" :class="$style.root">
|
||||||
<MkA
|
<MkA
|
||||||
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
|
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
|
||||||
:key="announcement.id"
|
:key="announcement.id"
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
|
|
||||||
function toolsMenuItems(): MenuItem[] {
|
function toolsMenuItems(): MenuItem[] {
|
||||||
return [{
|
const items: MenuItem[] = [{
|
||||||
type: 'link',
|
type: 'link',
|
||||||
to: '/scratchpad',
|
to: '/scratchpad',
|
||||||
text: i18n.ts.scratchpad,
|
text: i18n.ts.scratchpad,
|
||||||
|
@ -27,17 +27,27 @@ function toolsMenuItems(): MenuItem[] {
|
||||||
to: '/clicker',
|
to: '/clicker',
|
||||||
text: '🍪👈',
|
text: '🍪👈',
|
||||||
icon: 'ti ti-cookie',
|
icon: 'ti ti-cookie',
|
||||||
}, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? {
|
}];
|
||||||
type: 'link',
|
|
||||||
to: '/custom-emojis-manager',
|
if ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) {
|
||||||
text: i18n.ts.manageCustomEmojis,
|
items.push({
|
||||||
icon: 'ti ti-icons',
|
type: 'link',
|
||||||
} : undefined, ($i && ($i.isAdmin || $i.policies.canManageAvatarDecorations)) ? {
|
to: '/custom-emojis-manager',
|
||||||
type: 'link',
|
text: i18n.ts.manageCustomEmojis,
|
||||||
to: '/avatar-decorations',
|
icon: 'ti ti-icons',
|
||||||
text: i18n.ts.manageAvatarDecorations,
|
});
|
||||||
icon: 'ti ti-sparkles',
|
}
|
||||||
} : undefined];
|
|
||||||
|
if ($i && ($i.isAdmin || $i.policies.canManageAvatarDecorations)) {
|
||||||
|
items.push({
|
||||||
|
type: 'link' as const,
|
||||||
|
to: '/avatar-decorations',
|
||||||
|
text: i18n.ts.manageAvatarDecorations,
|
||||||
|
icon: 'ti ti-sparkles',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openInstanceMenu(ev: MouseEvent) {
|
export function openInstanceMenu(ev: MouseEvent) {
|
||||||
|
|
|
@ -29,18 +29,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkMarqueeText from '@/components/MkMarqueeText.vue';
|
|
||||||
import { useInterval } from '@@/js/use-interval.js';
|
import { useInterval } from '@@/js/use-interval.js';
|
||||||
|
import MkMarqueeText from '@/components/MkMarqueeText.vue';
|
||||||
import { shuffle } from '@/utility/shuffle.js';
|
import { shuffle } from '@/utility/shuffle.js';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
url?: string;
|
url: string;
|
||||||
shuffle?: boolean;
|
shuffle?: boolean;
|
||||||
display?: 'marquee' | 'oneByOne';
|
display?: 'marquee' | 'oneByOne';
|
||||||
marqueeDuration?: number;
|
marqueeDuration?: number;
|
||||||
marqueeReverse?: boolean;
|
marqueeReverse?: boolean;
|
||||||
oneByOneInterval?: number;
|
oneByOneInterval?: number;
|
||||||
refreshIntervalSec?: number;
|
refreshIntervalSec: number;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const items = ref<Misskey.entities.FetchRssResponse['items']>([]);
|
const items = ref<Misskey.entities.FetchRssResponse['items']>([]);
|
||||||
|
|
|
@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div :class="$style.nonTitlebarArea">
|
<div :class="$style.nonTitlebarArea">
|
||||||
<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
|
<XSidebar v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'left'"/>
|
||||||
|
|
||||||
<div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : null }">
|
<div :class="[$style.main, { [$style.withWallpaper]: withWallpaper, [$style.withSidebarAndTitlebar]: !isMobile && prefer.r['deck.navbarPosition'].value === 'left' && prefer.r.showTitlebar.value }]" :style="{ backgroundImage: prefer.s['deck.wallpaper'] != null ? `url(${ prefer.s['deck.wallpaper'] })` : '' }">
|
||||||
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
|
<XNavbarH v-if="!isMobile && prefer.r['deck.navbarPosition'].value === 'top'"/>
|
||||||
|
|
||||||
<XReloadSuggestion v-if="shouldSuggestReload"/>
|
<XReloadSuggestion v-if="shouldSuggestReload"/>
|
||||||
|
|
|
@ -52,7 +52,7 @@ export async function lookupUserByEmail() {
|
||||||
const user = await os.apiWithDialog('admin/accounts/find-by-email', { email: result });
|
const user = await os.apiWithDialog('admin/accounts/find-by-email', { email: result });
|
||||||
|
|
||||||
os.pageWindow(`/admin/user/${user.id}`);
|
os.pageWindow(`/admin/user/${user.id}`);
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (err.code === 'USER_NOT_FOUND') {
|
if (err.code === 'USER_NOT_FOUND') {
|
||||||
os.alert({
|
os.alert({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|
|
@ -10,7 +10,7 @@ export const chartLegend = (legend: InstanceType<typeof MkChartLegend>) => ({
|
||||||
id: 'htmlLegend',
|
id: 'htmlLegend',
|
||||||
afterUpdate(chart, args, options) {
|
afterUpdate(chart, args, options) {
|
||||||
// Reuse the built-in legendItems generator
|
// Reuse the built-in legendItems generator
|
||||||
const items = chart.options.plugins.legend.labels.generateLabels(chart);
|
const items = chart.options.plugins!.legend!.labels!.generateLabels!(chart);
|
||||||
|
|
||||||
legend.update(chart, items);
|
legend.update(chart, items);
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,7 +27,7 @@ export async function load() {
|
||||||
scope: ['clickerGame'],
|
scope: ['clickerGame'],
|
||||||
key: 'saveData',
|
key: 'saveData',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
if (err.code === 'NO_SUCH_KEY') {
|
if (err.code === 'NO_SUCH_KEY') {
|
||||||
saveData.value = {
|
saveData.value = {
|
||||||
gameVersion: 2,
|
gameVersion: 2,
|
||||||
|
@ -43,20 +43,6 @@ export async function load() {
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// migration
|
|
||||||
if (saveData.value.gameVersion === 1) {
|
|
||||||
saveData.value = {
|
|
||||||
gameVersion: 2,
|
|
||||||
cookies: saveData.value.cookies,
|
|
||||||
totalCookies: saveData.value.cookies,
|
|
||||||
totalHandmadeCookies: saveData.value.cookies,
|
|
||||||
clicked: saveData.value.clicked,
|
|
||||||
achievements: [],
|
|
||||||
facilities: [],
|
|
||||||
};
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save() {
|
export async function save() {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export async function getNoteClipMenu(props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const appearNote = getAppearNote(props.note);
|
const appearNote = getAppearNote(props.note) ?? props.note;
|
||||||
|
|
||||||
const clips = await clipsCache.fetch();
|
const clips = await clipsCache.fetch();
|
||||||
const menu: MenuItem[] = [...clips.map(clip => ({
|
const menu: MenuItem[] = [...clips.map(clip => ({
|
||||||
|
|
|
@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div class="_panel">
|
<div class="_panel">
|
||||||
<div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ instance.bannerUrl })` : undefined }">
|
<div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ instance.bannerUrl })` : undefined }">
|
||||||
<div :class="$style.iconContainer">
|
<div :class="$style.iconContainer">
|
||||||
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/>
|
<img :src="instance.iconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.bodyContainer">
|
<div :class="$style.bodyContainer">
|
||||||
<div :class="$style.body">
|
<div :class="$style.body">
|
||||||
|
@ -20,10 +20,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { host } from '@@/js/config.js';
|
||||||
import { useWidgetPropsManager } from './widget.js';
|
import { useWidgetPropsManager } from './widget.js';
|
||||||
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
|
||||||
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
|
||||||
import { host } from '@@/js/config.js';
|
|
||||||
import { instance } from '@/instance.js';
|
import { instance } from '@/instance.js';
|
||||||
|
|
||||||
const name = 'instanceInfo';
|
const name = 'instanceInfo';
|
||||||
|
|
Loading…
Reference in New Issue