Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop

This commit is contained in:
syuilo 2022-06-10 14:56:07 +09:00
commit b9c64053e8
112 changed files with 351 additions and 111 deletions

View file

@ -18,6 +18,7 @@ You should also include the user name that made the change.
- Server: always remove completed tasks of job queue @Johann150 - Server: always remove completed tasks of job queue @Johann150
- Client: make emoji stand out more on reaction button @Johann150 - Client: make emoji stand out more on reaction button @Johann150
- Client: display URL of QR code for TOTP registration @tamaina - Client: display URL of QR code for TOTP registration @tamaina
- Client: render quote renote CWs as MFM @pixeldesu
- API: notifications/readは配列でも受け付けるように #7667 @tamaina - API: notifications/readは配列でも受け付けるように #7667 @tamaina
- API: ユーザー検索で、クエリがusernameの条件を満たす場合はusernameもLIKE検索するように @tamaina - API: ユーザー検索で、クエリがusernameの条件を満たす場合はusernameもLIKE検索するように @tamaina
- MFM: Allow speed changes in all animated MFMs @Johann150 - MFM: Allow speed changes in all animated MFMs @Johann150

View file

@ -197,7 +197,14 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
const cw = note.summary === '' ? null : note.summary; const cw = note.summary === '' ? null : note.summary;
// テキストのパース // テキストのパース
const text = typeof note._misskey_content !== 'undefined' ? note._misskey_content : (note.content ? htmlToMfm(note.content, note.tag) : null); let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') {
text = note.source.content;
} else if (typeof note._misskey_content === 'string') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = htmlToMfm(note.content, note.tag);
}
// vote // vote
if (reply && reply.hasPoll) { if (reply && reply.hasPoll) {

View file

@ -138,6 +138,10 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
summary, summary,
content, content,
_misskey_content: text, _misskey_content: text,
source: {
content: text,
mediaType: "text/x.misskeymarkdown",
},
_misskey_quote: quote, _misskey_quote: quote,
quoteUrl: quote, quoteUrl: quote,
published: note.createdAt.toISOString(), published: note.createdAt.toISOString(),

View file

@ -106,7 +106,10 @@ export const isPost = (object: IObject): object is IPost =>
export interface IPost extends IObject { export interface IPost extends IObject {
type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event'; type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
_misskey_content?: string; source?: {
content: string;
mediaType: string;
};
_misskey_quote?: string; _misskey_quote?: string;
quoteUrl?: string; quoteUrl?: string;
_misskey_talk: boolean; _misskey_talk: boolean;
@ -114,7 +117,10 @@ export interface IPost extends IObject {
export interface IQuestion extends IObject { export interface IQuestion extends IObject {
type: 'Note' | 'Question'; type: 'Note' | 'Question';
_misskey_content?: string; source?: {
content: string;
mediaType: string;
};
_misskey_quote?: string; _misskey_quote?: string;
quoteUrl?: string; quoteUrl?: string;
oneOf?: IQuestionChoice[]; oneOf?: IQuestionChoice[];

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
description: 'Find the notes to which the given file is attached.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -8,6 +8,8 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
description: 'Check if a given file exists.',
res: { res: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -20,6 +20,8 @@ export const meta = {
kind: 'write:drive', kind: 'write:drive',
description: 'Upload a new drive file.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:drive', kind: 'write:drive',
description: 'Delete an existing drive file.',
errors: { errors: {
noSuchFile: { noSuchFile: {
message: 'No such file.', message: 'No such file.',

View file

@ -8,6 +8,8 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
description: 'Search for a drive file by a hash of the contents.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
description: 'Search for a drive file by the given parameters.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -10,6 +10,8 @@ export const meta = {
kind: 'read:drive', kind: 'read:drive',
description: 'Show the properties of a drive file.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:drive', kind: 'write:drive',
description: 'Update the properties of a drive file.',
errors: { errors: {
invalidFileName: { invalidFileName: {
message: 'Invalid file name.', message: 'Invalid file name.',

View file

@ -13,6 +13,8 @@ export const meta = {
max: 60, max: 60,
}, },
description: 'Request the server to download a new drive file from the specified URL.',
requireCredential: true, requireCredential: true,
kind: 'write:drive', kind: 'write:drive',

View file

@ -10,8 +10,12 @@ import { genId } from '@/misc/gen-id.js';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
export const meta = { export const meta = {
tags: ['reset password'],
requireCredential: false, requireCredential: false,
description: 'Request a users password to be reset.',
limit: { limit: {
duration: ms('1hour'), duration: ms('1hour'),
max: 3, max: 3,

View file

@ -3,8 +3,12 @@ import { ApiError } from '../error.js';
import { resetDb } from '@/db/postgre.js'; import { resetDb } from '@/db/postgre.js';
export const meta = { export const meta = {
tags: ['non-productive'],
requireCredential: false, requireCredential: false,
description: 'Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.',
errors: { errors: {
}, },

View file

@ -5,8 +5,12 @@ import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js';
import { ApiError } from '../error.js'; import { ApiError } from '../error.js';
export const meta = { export const meta = {
tags: ['reset password'],
requireCredential: false, requireCredential: false,
description: 'Complete the password reset that was previously requested.',
errors: { errors: {
}, },

View file

@ -8,6 +8,8 @@ export const meta = {
requireCredential: true, requireCredential: true,
description: 'Register to receive push notifications.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -5,6 +5,8 @@ export const meta = {
tags: ['account'], tags: ['account'],
requireCredential: true, requireCredential: true,
description: 'Unregister from receiving push notifications.',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -1,6 +1,10 @@
import define from '../define.js'; import define from '../define.js';
export const meta = { export const meta = {
tags: ['non-productive'],
description: 'Endpoint for testing input validation.',
requireCredential: false, requireCredential: false,
} as const; } as const;

View file

@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = { export const meta = {
tags: ['users', 'clips'], tags: ['users', 'clips'],
description: 'Show all clips this user owns.',
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'Clip',
},
},
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -10,6 +10,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Show everyone that follows this user.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -10,6 +10,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Show everyone that this user is following.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = { export const meta = {
tags: ['users', 'gallery'], tags: ['users', 'gallery'],
description: 'Show all gallery posts by the given user.',
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'GalleryPost',
},
},
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -10,6 +10,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Get a list of other users that the specified user frequently replies to.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Create a new group.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Delete an existing group.',
errors: { errors: {
noSuchGroup: { noSuchGroup: {
message: 'No such group.', message: 'No such group.',

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Join a group the authenticated user has been invited to.',
errors: { errors: {
noSuchInvitation: { noSuchInvitation: {
message: 'No such invitation.', message: 'No such invitation.',

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Delete an existing group invitation for the authenticated user without joining the group.',
errors: { errors: {
noSuchInvitation: { noSuchInvitation: {
message: 'No such invitation.', message: 'No such invitation.',

View file

@ -13,6 +13,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Invite a user to an existing group.',
errors: { errors: {
noSuchGroup: { noSuchGroup: {
message: 'No such group.', message: 'No such group.',

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'read:user-groups', kind: 'read:user-groups',
description: 'List the groups that the authenticated user is a member of.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.',
errors: { errors: {
noSuchGroup: { noSuchGroup: {
message: 'No such group.', message: 'No such group.',

View file

@ -8,6 +8,8 @@ export const meta = {
kind: 'read:user-groups', kind: 'read:user-groups',
description: 'List the groups that the authenticated user is the owner of.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -10,6 +10,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Removes a specified user from a group. The owner can not be removed.',
errors: { errors: {
noSuchGroup: { noSuchGroup: {
message: 'No such group.', message: 'No such group.',

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'read:user-groups', kind: 'read:user-groups',
description: 'Show the properties of a group.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -10,6 +10,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Transfer ownership of a group from the authenticated user to another user.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:user-groups', kind: 'write:user-groups',
description: 'Update the properties of a group.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -10,6 +10,8 @@ export const meta = {
kind: 'write:account', kind: 'write:account',
description: 'Create a new list of users.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:account', kind: 'write:account',
description: 'Delete an existing list of users.',
errors: { errors: {
noSuchList: { noSuchList: {
message: 'No such list.', message: 'No such list.',

View file

@ -8,6 +8,8 @@ export const meta = {
kind: 'read:account', kind: 'read:account',
description: 'Show all lists that the authenticated user has created.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:account', kind: 'write:account',
description: 'Remove a user from a list.',
errors: { errors: {
noSuchList: { noSuchList: {
message: 'No such list.', message: 'No such list.',

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'write:account', kind: 'write:account',
description: 'Add a user to an existing list.',
errors: { errors: {
noSuchList: { noSuchList: {
message: 'No such list.', message: 'No such list.',

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'read:account', kind: 'read:account',
description: 'Show the properties of a list.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -9,6 +9,8 @@ export const meta = {
kind: 'write:account', kind: 'write:account',
description: 'Update the properties of a list.',
res: { res: {
type: 'object', type: 'object',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -12,6 +12,8 @@ import { generateMutedInstanceQuery } from '../../common/generate-muted-instance
export const meta = { export const meta = {
tags: ['users', 'notes'], tags: ['users', 'notes'],
description: 'Show all notes that this user created.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../common/make-pagination-query.js';
export const meta = { export const meta = {
tags: ['users', 'pages'], tags: ['users', 'pages'],
description: 'Show all pages this user created.',
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'Page',
},
},
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -9,6 +9,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Show all reactions this user made.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
kind: 'read:account', kind: 'read:account',
description: 'Show users that the authenticated user might be interested to follow.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -6,6 +6,8 @@ export const meta = {
requireCredential: true, requireCredential: true,
description: 'Show the different kinds of relations between the authenticated user and the specified user(s).',
res: { res: {
optional: false, nullable: false, optional: false, nullable: false,
oneOf: [ oneOf: [

View file

@ -13,6 +13,8 @@ export const meta = {
requireCredential: true, requireCredential: true,
description: 'File a report.',
errors: { errors: {
noSuchUser: { noSuchUser: {
message: 'No such user.', message: 'No such user.',

View file

@ -9,6 +9,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Search for a user by username and/or host.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -8,6 +8,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Search for users.',
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -11,6 +11,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Show the properties of a user.',
res: { res: {
optional: false, nullable: false, optional: false, nullable: false,
oneOf: [ oneOf: [

View file

@ -8,6 +8,8 @@ export const meta = {
requireCredential: false, requireCredential: false,
description: 'Show statistics about a user.',
errors: { errors: {
noSuchUser: { noSuchUser: {
message: 'No such user.', message: 'No such user.',
@ -15,6 +17,94 @@ export const meta = {
id: '9e638e45-3b25-4ef7-8f95-07e8498f1819', id: '9e638e45-3b25-4ef7-8f95-07e8498f1819',
}, },
}, },
res: {
type: 'object',
optional: false, nullable: false,
properties: {
notesCount: {
type: 'integer',
optional: false, nullable: false,
},
repliesCount: {
type: 'integer',
optional: false, nullable: false,
},
renotesCount: {
type: 'integer',
optional: false, nullable: false,
},
repliedCount: {
type: 'integer',
optional: false, nullable: false,
},
renotedCount: {
type: 'integer',
optional: false, nullable: false,
},
pollVotesCount: {
type: 'integer',
optional: false, nullable: false,
},
pollVotedCount: {
type: 'integer',
optional: false, nullable: false,
},
localFollowingCount: {
type: 'integer',
optional: false, nullable: false,
},
remoteFollowingCount: {
type: 'integer',
optional: false, nullable: false,
},
localFollowersCount: {
type: 'integer',
optional: false, nullable: false,
},
remoteFollowersCount: {
type: 'integer',
optional: false, nullable: false,
},
followingCount: {
type: 'integer',
optional: false, nullable: false,
},
followersCount: {
type: 'integer',
optional: false, nullable: false,
},
sentReactionsCount: {
type: 'integer',
optional: false, nullable: false,
},
receivedReactionsCount: {
type: 'integer',
optional: false, nullable: false,
},
noteFavoritesCount: {
type: 'integer',
optional: false, nullable: false,
},
pageLikesCount: {
type: 'integer',
optional: false, nullable: false,
},
pageLikedCount: {
type: 'integer',
optional: false, nullable: false,
},
driveFilesCount: {
type: 'integer',
optional: false, nullable: false,
},
driveUsage: {
type: 'integer',
optional: false, nullable: false,
description: 'Drive usage in bytes',
},
},
},
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -15,6 +15,12 @@ module.exports = {
'plugin:vue/vue3-recommended', 'plugin:vue/vue3-recommended',
], ],
rules: { rules: {
'@typescript-eslint/no-empty-interface': [
'error',
{
'allowSingleExtends': true,
},
],
// window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため // window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため
// data の禁止理由: 抽象的すぎるため // data の禁止理由: 抽象的すぎるため
// e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため // e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため

View file

@ -27,8 +27,7 @@ type CaptchaContainer = {
}; };
declare global { declare global {
interface Window extends CaptchaContainer { interface Window extends CaptchaContainer { }
}
} }
const props = defineProps<{ const props = defineProps<{

View file

@ -71,7 +71,7 @@ function onMouseover() {
} }
function onMouseout() { function onMouseout() {
hover.value = false hover.value = false;
} }
function onDragover(ev: DragEvent) { function onDragover(ev: DragEvent) {
@ -204,7 +204,7 @@ function deleteFolder() {
defaultStore.set('uploadFolder', null); defaultStore.set('uploadFolder', null);
} }
}).catch(err => { }).catch(err => {
switch(err.id) { switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1': case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
os.alert({ os.alert({
type: 'error', type: 'error',

View file

@ -143,7 +143,7 @@ const fetching = ref(true);
const ilFilesObserver = new IntersectionObserver( const ilFilesObserver = new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles() (entries) => entries.some((entry) => entry.isIntersecting) && !fetching.value && moreFiles.value && fetchMoreFiles()
) );
watch(folder, () => emit('cd', folder.value)); watch(folder, () => emit('cd', folder.value));
@ -332,7 +332,7 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
// //
move(folderToDelete.parentId); move(folderToDelete.parentId);
}).catch(err => { }).catch(err => {
switch(err.id) { switch (err.id) {
case 'b0fc8a17-963c-405d-bfbc-859a487295e1': case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
os.alert({ os.alert({
type: 'error', type: 'error',
@ -607,7 +607,7 @@ function onContextmenu(ev: MouseEvent) {
onMounted(() => { onMounted(() => {
if (defaultStore.state.enableInfiniteScroll && loadMoreFiles.value) { if (defaultStore.state.enableInfiniteScroll && loadMoreFiles.value) {
nextTick(() => { nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el) ilFilesObserver.observe(loadMoreFiles.value?.$el);
}); });
} }
@ -628,7 +628,7 @@ onMounted(() => {
onActivated(() => { onActivated(() => {
if (defaultStore.state.enableInfiniteScroll) { if (defaultStore.state.enableInfiniteScroll) {
nextTick(() => { nextTick(() => {
ilFilesObserver.observe(loadMoreFiles.value?.$el) ilFilesObserver.observe(loadMoreFiles.value?.$el);
}); });
} }
}); });

View file

@ -28,7 +28,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onBeforeUnmount, onMounted, ref } from 'vue'; import { onBeforeUnmount, onMounted } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as os from '@/os'; import * as os from '@/os';
import { stream } from '@/stream'; import { stream } from '@/stream';
@ -43,32 +43,30 @@ const props = withDefaults(defineProps<{
large: false, large: false,
}); });
const isFollowing = ref(props.user.isFollowing); let isFollowing = $ref(props.user.isFollowing);
const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou); let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
const wait = ref(false); let wait = $ref(false);
const connection = stream.useChannel('main'); const connection = stream.useChannel('main');
if (props.user.isFollowing == null) { if (props.user.isFollowing == null) {
os.api('users/show', { os.api('users/show', {
userId: props.user.id userId: props.user.id
}).then(u => { })
isFollowing.value = u.isFollowing; .then(onFollowChange);
hasPendingFollowRequestFromYou.value = u.hasPendingFollowRequestFromYou;
});
} }
function onFollowChange(user: Misskey.entities.UserDetailed) { function onFollowChange(user: Misskey.entities.UserDetailed) {
if (user.id === props.user.id) { if (user.id === props.user.id) {
isFollowing.value = user.isFollowing; isFollowing = user.isFollowing;
hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou; hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
} }
} }
async function onClick() { async function onClick() {
wait.value = true; wait = true;
try { try {
if (isFollowing.value) { if (isFollowing) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'warning', type: 'warning',
text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
@ -80,26 +78,22 @@ async function onClick() {
userId: props.user.id userId: props.user.id
}); });
} else { } else {
if (hasPendingFollowRequestFromYou.value) { if (hasPendingFollowRequestFromYou) {
await os.api('following/requests/cancel', { await os.api('following/requests/cancel', {
userId: props.user.id userId: props.user.id
}); });
} else if (props.user.isLocked) { hasPendingFollowRequestFromYou = false;
await os.api('following/create', {
userId: props.user.id
});
hasPendingFollowRequestFromYou.value = true;
} else { } else {
await os.api('following/create', { await os.api('following/create', {
userId: props.user.id userId: props.user.id
}); });
hasPendingFollowRequestFromYou.value = true; hasPendingFollowRequestFromYou = true;
} }
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} finally { } finally {
wait.value = false; wait = false;
} }
} }

View file

@ -24,7 +24,7 @@ const props = withDefaults(defineProps<{
defaultOpen: boolean; defaultOpen: boolean;
}>(), { }>(), {
defaultOpen: false, defaultOpen: false,
}) });
let opened = $ref(props.defaultOpen); let opened = $ref(props.defaultOpen);
let openedAtLeastOnce = $ref(props.defaultOpen); let openedAtLeastOnce = $ref(props.defaultOpen);

View file

@ -14,7 +14,7 @@ export default defineComponent({
data() { data() {
return { return {
value: this.modelValue, value: this.modelValue,
} };
}, },
watch: { watch: {
value() { value() {

View file

@ -66,7 +66,7 @@ export default defineComponent({
.then(response => response.json()) .then(response => response.json())
.then(f => { .then(f => {
ok(f); ok(f);
}) });
}); });
}); });
os.promiseDialog(promise); os.promiseDialog(promise);

View file

@ -16,7 +16,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue'; import { defineComponent, defineAsyncComponent } from 'vue';
import MkDriveFileThumbnail from './drive-file-thumbnail.vue' import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
import * as os from '@/os'; import * as os from '@/os';
export default defineComponent({ export default defineComponent({
@ -114,19 +114,19 @@ export default defineComponent({
this.menu = os.popupMenu([{ this.menu = os.popupMenu([{
text: this.$ts.renameFile, text: this.$ts.renameFile,
icon: 'fas fa-i-cursor', icon: 'fas fa-i-cursor',
action: () => { this.rename(file) } action: () => { this.rename(file); }
}, { }, {
text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive,
icon: file.isSensitive ? 'fas fa-eye-slash' : 'fas fa-eye', icon: file.isSensitive ? 'fas fa-eye-slash' : 'fas fa-eye',
action: () => { this.toggleSensitive(file) } action: () => { this.toggleSensitive(file); }
}, { }, {
text: this.$ts.describeFile, text: this.$ts.describeFile,
icon: 'fas fa-i-cursor', icon: 'fas fa-i-cursor',
action: () => { this.describe(file) } action: () => { this.describe(file); }
}, { }, {
text: this.$ts.attachCancel, text: this.$ts.attachCancel,
icon: 'fas fa-times-circle', icon: 'fas fa-times-circle',
action: () => { this.detachMedia(file.id) } action: () => { this.detachMedia(file.id); }
}], ev.currentTarget ?? ev.target).then(() => this.menu = null); }], ev.currentTarget ?? ev.target).then(() => this.menu = null);
} }
} }

View file

@ -442,7 +442,7 @@ function onCompositionEnd(ev: CompositionEvent) {
} }
async function onPaste(ev: ClipboardEvent) { async function onPaste(ev: ClipboardEvent) {
for (const { item, i } of Array.from(ev.clipboardData.items).map((item, i) => ({item, i}))) { for (const { item, i } of Array.from(ev.clipboardData.items).map((item, i) => ({ item, i }))) {
if (item.kind === 'file') { if (item.kind === 'file') {
const file = item.getAsFile(); const file = item.getAsFile();
const lio = file.name.lastIndexOf('.'); const lio = file.name.lastIndexOf('.');

View file

@ -222,7 +222,7 @@ export default defineComponent({
return { return {
chartEl, chartEl,
} };
}, },
}); });
</script> </script>

View file

@ -52,7 +52,7 @@ export default defineComponent({
flag: true, flag: true,
radio: 'misskey', radio: 'misskey',
mfm: `Hello world! This is an @example mention. BTW you are @${this.$i ? this.$i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.` mfm: `Hello world! This is an @example mention. BTW you are @${this.$i ? this.$i.username : 'guest'}.\nAlso, here is ${config.url} and [example link](${config.url}). for more details, see https://example.com.\nAs you know #misskey is open-source software.`
} };
}, },
methods: { methods: {

View file

@ -159,7 +159,7 @@ function queryKey() {
function onSubmit() { function onSubmit() {
signing = true; signing = true;
console.log('submit') console.log('submit');
if (!totpLogin && user && user.twoFactorEnabled) { if (!totpLogin && user && user.twoFactorEnabled) {
if (window.PublicKeyCredential && user.securityKeys) { if (window.PublicKeyCredential && user.securityKeys) {
os.api('signin', { os.api('signin', {
@ -222,7 +222,7 @@ function loginFailed(err) {
break; break;
} }
default: { default: {
console.log(err) console.log(err);
os.alert({ os.alert({
type: 'error', type: 'error',
title: i18n.ts.loginFailed, title: i18n.ts.loginFailed,

View file

@ -111,7 +111,7 @@ export default defineComponent({
ToSAgreement: false, ToSAgreement: false,
hCaptchaResponse: null, hCaptchaResponse: null,
reCaptchaResponse: null, reCaptchaResponse: null,
} };
}, },
computed: { computed: {

View file

@ -96,11 +96,11 @@ export default defineComponent({
} }
function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY) { function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY) {
const origin = {x: circleCenterX, y: circleCenterY}; const origin = { x: circleCenterX, y: circleCenterY };
const dist1 = distance({x: 0, y: 0}, origin); const dist1 = distance({ x: 0, y: 0 }, origin);
const dist2 = distance({x: boxW, y: 0}, origin); const dist2 = distance({ x: boxW, y: 0 }, origin);
const dist3 = distance({x: 0, y: boxH}, origin); const dist3 = distance({ x: 0, y: boxH }, origin);
const dist4 = distance({x: boxW, y: boxH }, origin); const dist4 = distance({ x: boxW, y: boxH }, origin);
return Math.max(dist1, dist2, dist3, dist4) * 2; return Math.max(dist1, dist2, dist3, dist4) * 2;
} }

View file

@ -234,7 +234,7 @@ onMounted(() => {
} }
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null); fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
await nextTick() await nextTick();
align(); align();
}, { immediate: true, }); }, { immediate: true, });

View file

@ -63,7 +63,7 @@ const setPosition = () => {
} }
return [left, top]; return [left, top];
} };
const calcPosWhenBottom = () => { const calcPosWhenBottom = () => {
let left: number; let left: number;
@ -84,7 +84,7 @@ const setPosition = () => {
} }
return [left, top]; return [left, top];
} };
const calcPosWhenLeft = () => { const calcPosWhenLeft = () => {
let left: number; let left: number;
@ -105,7 +105,7 @@ const setPosition = () => {
} }
return [left, top]; return [left, top];
} };
const calcPosWhenRight = () => { const calcPosWhenRight = () => {
let left: number; let left: number;
@ -126,7 +126,7 @@ const setPosition = () => {
} }
return [left, top]; return [left, top];
} };
const calc = (): { const calc = (): {
left: number; left: number;
@ -172,7 +172,7 @@ const setPosition = () => {
} }
return null as never; return null as never;
} };
const { left, top, transformOrigin } = calc(); const { left, top, transformOrigin } = calc();
el.value.style.transformOrigin = transformOrigin; el.value.style.transformOrigin = transformOrigin;

View file

@ -90,7 +90,7 @@ fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${requestLang}`).the
sitename = info.sitename; sitename = info.sitename;
fetching = false; fetching = false;
player = info.player; player = info.player;
}) });
}); });
function adjustTweetHeight(message: any) { function adjustTweetHeight(message: any) {

View file

@ -9,7 +9,7 @@ export default {
} else { } else {
return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
} }
} };
const parentBg = getBgColor(src.parentElement); const parentBg = getBgColor(src.parentElement);

View file

@ -25,12 +25,12 @@ function calc(src: Element) {
return; return;
} }
if (info.intersection) { if (info.intersection) {
info.intersection.disconnect() info.intersection.disconnect();
delete info.intersection; delete info.intersection;
}; }
info.fn(width, height); info.fn(width, height);
}; }
export default { export default {
mounted(src, binding, vn) { mounted(src, binding, vn) {

View file

@ -9,7 +9,7 @@ export default {
} else { } else {
return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
} }
} };
const parentBg = getBgColor(src.parentElement); const parentBg = getBgColor(src.parentElement);

View file

@ -60,9 +60,9 @@ function calc(el: Element) {
return; return;
} }
if (info.intersection) { if (info.intersection) {
info.intersection.disconnect() info.intersection.disconnect();
delete info.intersection; delete info.intersection;
}; }
mountings.set(el, Object.assign(info, { previousWidth: width })); mountings.set(el, Object.assign(info, { previousWidth: width }));

View file

@ -285,7 +285,7 @@ export function inputDate(props: {
}); });
} }
export function select<C extends any = any>(props: { export function select<C = any>(props: {
title?: string | null; title?: string | null;
text?: string | null; text?: string | null;
default?: string | null; default?: string | null;

View file

@ -159,7 +159,7 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
}, { }, {
text: i18n.ts.import, text: i18n.ts.import,
icon: 'fas fa-plus', icon: 'fas fa-plus',
action: () => { im(emoji) } action: () => { im(emoji); }
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);
}; };

View file

@ -132,7 +132,7 @@ export default defineComponent({
overviewHeight: '1fr', overviewHeight: '1fr',
queueHeight: '1fr', queueHeight: '1fr',
paused: false, paused: false,
} };
}, },
computed: { computed: {

View file

@ -20,7 +20,7 @@ import * as symbols from '@/symbols';
import * as config from '@/config'; import * as config from '@/config';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
const connection = markRaw(stream.useChannel('queueStats')) const connection = markRaw(stream.useChannel('queueStats'));
function clear() { function clear() {
os.confirm({ os.confirm({
@ -41,7 +41,7 @@ onMounted(() => {
length: 200 length: 200
}); });
}); });
}) });
onBeforeUnmount(() => { onBeforeUnmount(() => {
connection.dispose(); connection.dispose();

View file

@ -32,7 +32,7 @@ export default defineComponent({
computed: { computed: {
name(): string { name(): string {
const el = document.createElement('div'); const el = document.createElement('div');
el.textContent = this.app.name el.textContent = this.app.name;
return el.innerHTML; return el.innerHTML;
}, },
app(): any { app(): any {

View file

@ -58,7 +58,7 @@ export default defineComponent({
tags: emojiTags, tags: emojiTags,
selectedTags: new Set(), selectedTags: new Set(),
searchEmojis: null, searchEmojis: null,
} };
}, },
watch: { watch: {

View file

@ -127,7 +127,7 @@ function getStatus(instance) {
if (instance.isSuspended) return 'suspended'; if (instance.isSuspended) return 'suspended';
if (instance.isNotResponding) return 'error'; if (instance.isNotResponding) return 'error';
return 'alive'; return 'alive';
}; }
defineExpose({ defineExpose({
[symbols.PAGE_INFO]: { [symbols.PAGE_INFO]: {

View file

@ -71,7 +71,7 @@ export default defineComponent({
description: null, description: null,
title: null, title: null,
isSensitive: false, isSensitive: false,
} };
}, },
watch: { watch: {

View file

@ -123,11 +123,11 @@ export default defineComponent({
os.popupMenu([{ os.popupMenu([{
text: this.$ts.messagingWithUser, text: this.$ts.messagingWithUser,
icon: 'fas fa-user', icon: 'fas fa-user',
action: () => { this.startUser() } action: () => { this.startUser(); }
}, { }, {
text: this.$ts.messagingWithGroup, text: this.$ts.messagingWithGroup,
icon: 'fas fa-users', icon: 'fas fa-users',
action: () => { this.startGroup() } action: () => { this.startGroup(); }
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);
}, },

View file

@ -200,7 +200,7 @@ export default defineComponent({
text: this.text, text: this.text,
file: this.file file: this.file
} }
} };
localStorage.setItem('message_drafts', JSON.stringify(drafts)); localStorage.setItem('message_drafts', JSON.stringify(drafts));
}, },

View file

@ -341,7 +341,7 @@ export default defineComponent({
preview_rainbow: `$[rainbow 🍮] $[rainbow.speed=5s 🍮]`, preview_rainbow: `$[rainbow 🍮] $[rainbow.speed=5s 🍮]`,
preview_sparkle: `$[sparkle 🍮]`, preview_sparkle: `$[sparkle 🍮]`,
preview_rotate: `$[rotate 🍮]`, preview_rotate: `$[rotate 🍮]`,
} };
}, },
}); });
</script> </script>

View file

@ -32,7 +32,7 @@ defineExpose({
icon: 'fas fa-satellite', icon: 'fas fa-satellite',
bg: 'var(--bg)' bg: 'var(--bg)'
} }
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -114,7 +114,7 @@ export default defineComponent({
readonly: this.readonly, readonly: this.readonly,
getScriptBlockList: this.getScriptBlockList, getScriptBlockList: this.getScriptBlockList,
getPageBlockList: this.getPageBlockList getPageBlockList: this.getPageBlockList
} };
}, },
props: { props: {

View file

@ -100,7 +100,7 @@ async function run() {
text: error.message text: error.message
}); });
} }
}; }
function highlighter(code) { function highlighter(code) {
return highlight(code, languages.js, 'javascript'); return highlight(code, languages.js, 'javascript');

View file

@ -142,7 +142,7 @@ function registerKey() {
registration.value = null; registration.value = null;
key.lastUsed = new Date(); key.lastUsed = new Date();
os.success(); os.success();
}) });
} }
function unregisterKey(key) { function unregisterKey(key) {

View file

@ -45,7 +45,7 @@ const init = async () => {
accounts.value = response; accounts.value = response;
console.log(accounts.value); console.log(accounts.value);
}); });
} };
function menu(account, ev) { function menu(account, ev) {
os.popupMenu([{ os.popupMenu([{

View file

@ -52,7 +52,7 @@ const pagination = {
params: { params: {
sort: '+lastUsedAt' sort: '+lastUsedAt'
} }
} };
function revoke(token) { function revoke(token) {
os.api('i/revoke-token', { tokenId: token.id }).then(() => { os.api('i/revoke-token', { tokenId: token.id }).then(() => {

View file

@ -120,7 +120,7 @@ const darkThemeId = computed({
return darkTheme.value.id; return darkTheme.value.id;
}, },
set(id) { set(id) {
ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id)) ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id));
} }
}); });
const lightTheme = ColdDeviceStorage.ref('lightTheme'); const lightTheme = ColdDeviceStorage.ref('lightTheme');
@ -129,7 +129,7 @@ const lightThemeId = computed({
return lightTheme.value.id; return lightTheme.value.id;
}, },
set(id) { set(id) {
ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id)) ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id));
} }
}); });
const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));

View file

@ -75,7 +75,7 @@ async function save() {
// check each line if it is a RegExp or not // check each line if it is a RegExp or not
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
const line = lines[i] const line = lines[i];
const regexp = line.match(/^\/(.+)\/(.*)$/); const regexp = line.match(/^\/(.+)\/(.*)$/);
if (regexp) { if (regexp) {
// check that the RegExp is valid // check that the RegExp is valid

View file

@ -56,7 +56,7 @@ export default defineComponent({
localOnly: null as boolean | null, localOnly: null as boolean | null,
files: [] as Misskey.entities.DriveFile[], files: [] as Misskey.entities.DriveFile[],
visibleUsers: [] as Misskey.entities.User[], visibleUsers: [] as Misskey.entities.User[],
} };
}, },
async created() { async created() {

View file

@ -68,7 +68,7 @@
import { watch } from 'vue'; import { watch } from 'vue';
import { toUnicode } from 'punycode/'; import { toUnicode } from 'punycode/';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import { v4 as uuid} from 'uuid'; import { v4 as uuid } from 'uuid';
import JSON5 from 'json5'; import JSON5 from 'json5';
import FormButton from '@/components/ui/button.vue'; import FormButton from '@/components/ui/button.vue';

View file

@ -20,7 +20,7 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'MkTimelinePage', name: 'MkTimelinePage',
} };
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>

View file

@ -41,7 +41,7 @@ export default defineComponent({
password: '', password: '',
submitting: false, submitting: false,
host, host,
} };
}, },
methods: { methods: {

View file

@ -39,7 +39,7 @@ export default defineComponent({
return { return {
notes: [], notes: [],
isScrolling: false, isScrolling: false,
} };
}, },
created() { created() {

View file

@ -13,7 +13,7 @@ const defaultLocaleStringFormats: {[index: string]: string} = {
function formatLocaleString(date: Date, format: string): string { function formatLocaleString(date: Date, format: string): string {
return format.replace(/\{\{(\w+)(:(\w+))?\}\}/g, (match: string, kind: string, unused?, option?: string) => { return format.replace(/\{\{(\w+)(:(\w+))?\}\}/g, (match: string, kind: string, unused?, option?: string) => {
if (['weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].includes(kind)) { if (['weekday', 'era', 'year', 'month', 'day', 'hour', 'minute', 'second', 'timeZoneName'].includes(kind)) {
return date.toLocaleString(window.navigator.language, {[kind]: option ? option : defaultLocaleStringFormats[kind]}); return date.toLocaleString(window.navigator.language, { [kind]: option ? option : defaultLocaleStringFormats[kind] });
} else { } else {
return match; return match;
} }
@ -24,8 +24,8 @@ export function formatDateTimeString(date: Date, format: string): string {
return format return format
.replace(/yyyy/g, date.getFullYear().toString()) .replace(/yyyy/g, date.getFullYear().toString())
.replace(/yy/g, date.getFullYear().toString().slice(-2)) .replace(/yy/g, date.getFullYear().toString().slice(-2))
.replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long'})) .replace(/MMMM/g, date.toLocaleString(window.navigator.language, { month: 'long' }))
.replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short'})) .replace(/MMM/g, date.toLocaleString(window.navigator.language, { month: 'short' }))
.replace(/MM/g, (`0${date.getMonth() + 1}`).slice(-2)) .replace(/MM/g, (`0${date.getMonth() + 1}`).slice(-2))
.replace(/M/g, (date.getMonth() + 1).toString()) .replace(/M/g, (date.getMonth() + 1).toString())
.replace(/dd/g, (`0${date.getDate()}`).slice(-2)) .replace(/dd/g, (`0${date.getDate()}`).slice(-2))

Some files were not shown because too many files have changed in this diff Show more