From 1f2dab0a839f9ee6d754243881d949988af7df55 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 13 Jan 2022 00:47:05 +0900 Subject: [PATCH 01/35] feat: multiple emojis editing --- .../endpoints/admin/emoji/add-aliases-bulk.ts | 39 ++ .../api/endpoints/admin/emoji/delete-bulk.ts | 37 ++ .../admin/emoji/{remove.ts => delete.ts} | 2 +- .../api/endpoints/admin/emoji/import-zip.ts | 21 ++ .../admin/emoji/remove-aliases-bulk.ts | 39 ++ .../endpoints/admin/emoji/set-aliases-bulk.ts | 35 ++ .../admin/emoji/set-category-bulk.ts | 35 ++ .../src/pages/admin/emoji-edit-dialog.vue | 2 +- packages/client/src/pages/admin/emojis.vue | 349 +++++++++++------- packages/client/src/pages/admin/index.vue | 8 +- 10 files changed, 428 insertions(+), 139 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts rename packages/backend/src/server/api/endpoints/admin/emoji/{remove.ts => delete.ts} (95%) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts new file mode 100644 index 0000000000..ef0f315022 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -0,0 +1,39 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ID } from '@/misc/cafy-id'; +import { Emojis } from '@/models/index'; +import { getConnection, In } from 'typeorm'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + ids: { + validator: $.arr($.type(ID)), + }, + + aliases: { + validator: $.arr($.str), + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps) => { + const emojis = await Emojis.find({ + id: In(ps.ids), + }); + + for (const emoji of emojis) { + await Emojis.update(emoji.id, { + updatedAt: new Date(), + aliases: [...new Set(emoji.aliases.concat(ps.aliases))], + }); + } + + await getConnection().queryResultCache!.remove(['meta_emojis']); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts new file mode 100644 index 0000000000..a99cd3c978 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -0,0 +1,37 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ID } from '@/misc/cafy-id'; +import { Emojis } from '@/models/index'; +import { getConnection, In } from 'typeorm'; +import { insertModerationLog } from '@/services/insert-moderation-log'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + ids: { + validator: $.arr($.type(ID)), + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps, me) => { + const emojis = await Emojis.find({ + id: In(ps.ids), + }); + + for (const emoji of emojis) { + await Emojis.delete(emoji.id); + + await getConnection().queryResultCache!.remove(['meta_emojis']); + + insertModerationLog(me, 'deleteEmoji', { + emoji: emoji, + }); + } +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts similarity index 95% rename from packages/backend/src/server/api/endpoints/admin/emoji/remove.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 440c1008c7..870245ac92 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -37,7 +37,7 @@ export default define(meta, async (ps, me) => { await getConnection().queryResultCache!.remove(['meta_emojis']); - insertModerationLog(me, 'removeEmoji', { + insertModerationLog(me, 'deleteEmoji', { emoji: emoji, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts new file mode 100644 index 0000000000..04895b8f20 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -0,0 +1,21 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { createImportCustomEmojisJob } from '@/queue/index'; +import ms from 'ms'; +import { ID } from '@/misc/cafy-id'; + +export const meta = { + secure: true, + requireCredential: true as const, + requireModerator: true, + params: { + fileId: { + validator: $.type(ID), + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps, user) => { + createImportCustomEmojisJob(user, ps.fileId); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts new file mode 100644 index 0000000000..4c771b4e42 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -0,0 +1,39 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ID } from '@/misc/cafy-id'; +import { Emojis } from '@/models/index'; +import { getConnection, In } from 'typeorm'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + ids: { + validator: $.arr($.type(ID)), + }, + + aliases: { + validator: $.arr($.str), + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps) => { + const emojis = await Emojis.find({ + id: In(ps.ids), + }); + + for (const emoji of emojis) { + await Emojis.update(emoji.id, { + updatedAt: new Date(), + aliases: emoji.aliases.filter(x => !ps.aliases.includes(x)), + }); + } + + await getConnection().queryResultCache!.remove(['meta_emojis']); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts new file mode 100644 index 0000000000..33dccbc642 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -0,0 +1,35 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ID } from '@/misc/cafy-id'; +import { Emojis } from '@/models/index'; +import { getConnection, In } from 'typeorm'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + ids: { + validator: $.arr($.type(ID)), + }, + + aliases: { + validator: $.arr($.str), + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps) => { + await Emojis.update({ + id: In(ps.ids), + }, { + updatedAt: new Date(), + aliases: ps.aliases, + }); + + await getConnection().queryResultCache!.remove(['meta_emojis']); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts new file mode 100644 index 0000000000..d40ed52da7 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -0,0 +1,35 @@ +import $ from 'cafy'; +import define from '../../../define'; +import { ID } from '@/misc/cafy-id'; +import { Emojis } from '@/models/index'; +import { getConnection, In } from 'typeorm'; +import { ApiError } from '../../../error'; + +export const meta = { + tags: ['admin'], + + requireCredential: true as const, + requireModerator: true, + + params: { + ids: { + validator: $.arr($.type(ID)), + }, + + category: { + validator: $.optional.nullable.str, + }, + }, +}; + +// eslint-disable-next-line import/no-default-export +export default define(meta, async (ps) => { + await Emojis.update({ + id: In(ps.ids), + }, { + updatedAt: new Date(), + category: ps.category, + }); + + await getConnection().queryResultCache!.remove(['meta_emojis']); +}); diff --git a/packages/client/src/pages/admin/emoji-edit-dialog.vue b/packages/client/src/pages/admin/emoji-edit-dialog.vue index a45d92fa16..2e3903426e 100644 --- a/packages/client/src/pages/admin/emoji-edit-dialog.vue +++ b/packages/client/src/pages/admin/emoji-edit-dialog.vue @@ -95,7 +95,7 @@ export default defineComponent({ }); if (canceled) return; - os.api('admin/emoji/remove', { + os.api('admin/emoji/delete', { id: this.emoji.id }).then(() => { this.$emit('done', { diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index df5d234d6f..f995460f11 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -6,11 +6,22 @@ - + + + +
+ Select all + Set category + Add tag + Remove tag + Set tag + Delete +
+ - diff --git a/packages/client/src/pages/tag.vue b/packages/client/src/pages/tag.vue index a9497ae801..debee2606d 100644 --- a/packages/client/src/pages/tag.vue +++ b/packages/client/src/pages/tag.vue @@ -4,37 +4,28 @@ - diff --git a/packages/client/src/router.ts b/packages/client/src/router.ts index 312374cdb9..499afefbfe 100644 --- a/packages/client/src/router.ts +++ b/packages/client/src/router.ts @@ -33,7 +33,7 @@ const defaultRoutes = [ { path: '/explore/tags/:tag', props: true, component: page('explore') }, { path: '/federation', component: page('federation') }, { path: '/emojis', component: page('emojis') }, - { path: '/search', component: page('search') }, + { path: '/search', component: page('search'), props: route => ({ query: route.query.q, channel: route.query.channel }) }, { path: '/pages', name: 'pages', component: page('pages') }, { path: '/pages/new', component: page('page-editor/page-editor') }, { path: '/pages/edit/:pageId', component: page('page-editor/page-editor'), props: route => ({ initPageId: route.params.pageId }) }, From 861d028d0971b026c98f4a68881b3c37749760ee Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 13 Jan 2022 02:26:10 +0900 Subject: [PATCH 08/35] refactor --- packages/client/src/pages/admin/abuses.vue | 2 +- packages/client/src/pages/admin/emojis.vue | 4 ++-- packages/client/src/pages/admin/files.vue | 2 +- packages/client/src/pages/admin/users.vue | 2 +- packages/client/src/pages/announcements.vue | 2 +- packages/client/src/pages/channel.vue | 2 +- packages/client/src/pages/channels.vue | 6 +++--- packages/client/src/pages/clip.vue | 2 +- packages/client/src/pages/explore.vue | 4 ++-- packages/client/src/pages/featured.vue | 2 +- packages/client/src/pages/federation.vue | 2 +- packages/client/src/pages/follow-requests.vue | 2 +- packages/client/src/pages/gallery/index.vue | 10 +++++----- packages/client/src/pages/gallery/post.vue | 2 +- packages/client/src/pages/mentions.vue | 2 +- packages/client/src/pages/messages.vue | 2 +- packages/client/src/pages/my-antennas/index.vue | 2 +- packages/client/src/pages/my-clips/index.vue | 2 +- packages/client/src/pages/my-groups/index.vue | 6 +++--- packages/client/src/pages/my-lists/index.vue | 2 +- packages/client/src/pages/note.vue | 4 ++-- packages/client/src/pages/page.vue | 2 +- packages/client/src/pages/pages.vue | 6 +++--- packages/client/src/pages/search.vue | 2 +- packages/client/src/pages/settings/apps.vue | 2 +- packages/client/src/pages/settings/mute-block.vue | 4 ++-- packages/client/src/pages/settings/security.vue | 2 +- packages/client/src/pages/tag.vue | 2 +- packages/client/src/pages/user/clips.vue | 2 +- packages/client/src/pages/user/follow-list.vue | 4 ++-- packages/client/src/pages/user/gallery.vue | 2 +- packages/client/src/pages/user/pages.vue | 2 +- packages/client/src/pages/user/reactions.vue | 2 +- packages/client/src/ui/deck/direct-column.vue | 2 +- packages/client/src/ui/deck/mentions-column.vue | 2 +- 35 files changed, 50 insertions(+), 50 deletions(-) diff --git a/packages/client/src/pages/admin/abuses.vue b/packages/client/src/pages/admin/abuses.vue index 0f19f8dbe9..31cdef492a 100644 --- a/packages/client/src/pages/admin/abuses.vue +++ b/packages/client/src/pages/admin/abuses.vue @@ -95,7 +95,7 @@ export default defineComponent({ reporterOrigin: 'combined', targetUserOrigin: 'combined', pagination: { - endpoint: 'admin/abuse-user-reports', + endpoint: 'admin/abuse-user-reports' as const, limit: 10, params: computed(() => ({ state: this.state, diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index 38182e5be7..5b1dfe565a 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -85,7 +85,7 @@ const selectMode = ref(false); const selectedEmojis = ref([]); const pagination = { - endpoint: 'admin/emoji/list', + endpoint: 'admin/emoji/list' as const, limit: 30, params: computed(() => ({ query: (query.value && query.value !== '') ? query.value : null, @@ -93,7 +93,7 @@ const pagination = { }; const remotePagination = { - endpoint: 'admin/emoji/list-remote', + endpoint: 'admin/emoji/list-remote' as const, limit: 30, params: computed(() => ({ query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null, diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue index 10341e7e36..87dd12f489 100644 --- a/packages/client/src/pages/admin/files.vue +++ b/packages/client/src/pages/admin/files.vue @@ -95,7 +95,7 @@ export default defineComponent({ type: null, searchHost: '', pagination: { - endpoint: 'admin/drive/files', + endpoint: 'admin/drive/files' as const, limit: 10, params: computed(() => ({ type: (this.type && this.type !== '') ? this.type : null, diff --git a/packages/client/src/pages/admin/users.vue b/packages/client/src/pages/admin/users.vue index 30220a2a29..03e155ddcf 100644 --- a/packages/client/src/pages/admin/users.vue +++ b/packages/client/src/pages/admin/users.vue @@ -110,7 +110,7 @@ export default defineComponent({ searchUsername: '', searchHost: '', pagination: { - endpoint: 'admin/show-users', + endpoint: 'admin/show-users' as const, limit: 10, params: computed(() => ({ sort: this.sort, diff --git a/packages/client/src/pages/announcements.vue b/packages/client/src/pages/announcements.vue index ca94640dda..53727823a4 100644 --- a/packages/client/src/pages/announcements.vue +++ b/packages/client/src/pages/announcements.vue @@ -36,7 +36,7 @@ export default defineComponent({ bg: 'var(--bg)', }, pagination: { - endpoint: 'announcements', + endpoint: 'announcements' as const, limit: 10, }, }; diff --git a/packages/client/src/pages/channel.vue b/packages/client/src/pages/channel.vue index 598e173d81..c9a8f36844 100644 --- a/packages/client/src/pages/channel.vue +++ b/packages/client/src/pages/channel.vue @@ -67,7 +67,7 @@ export default defineComponent({ channel: null, showBanner: true, pagination: { - endpoint: 'channels/timeline', + endpoint: 'channels/timeline' as const, limit: 10, params: computed(() => ({ channelId: this.channelId, diff --git a/packages/client/src/pages/channels.vue b/packages/client/src/pages/channels.vue index 48877ab3ec..4e538a6da3 100644 --- a/packages/client/src/pages/channels.vue +++ b/packages/client/src/pages/channels.vue @@ -60,15 +60,15 @@ export default defineComponent({ })), tab: 'featured', featuredPagination: { - endpoint: 'channels/featured', + endpoint: 'channels/featured' as const, noPaging: true, }, followingPagination: { - endpoint: 'channels/followed', + endpoint: 'channels/followed' as const, limit: 5, }, ownedPagination: { - endpoint: 'channels/owned', + endpoint: 'channels/owned' as const, limit: 5, }, }; diff --git a/packages/client/src/pages/clip.vue b/packages/client/src/pages/clip.vue index b375856803..6b49221d32 100644 --- a/packages/client/src/pages/clip.vue +++ b/packages/client/src/pages/clip.vue @@ -50,7 +50,7 @@ export default defineComponent({ } : null), clip: null, pagination: { - endpoint: 'clips/notes', + endpoint: 'clips/notes' as const, limit: 10, params: computed(() => ({ clipId: this.clipId, diff --git a/packages/client/src/pages/explore.vue b/packages/client/src/pages/explore.vue index a3c3b771f2..04cc3662a7 100644 --- a/packages/client/src/pages/explore.vue +++ b/packages/client/src/pages/explore.vue @@ -156,7 +156,7 @@ export default defineComponent({ sort: '+createdAt', } }, searchPagination: { - endpoint: 'users/search', + endpoint: 'users/search' as const, limit: 10, params: computed(() => (this.searchQuery && this.searchQuery !== '') ? { query: this.searchQuery, @@ -178,7 +178,7 @@ export default defineComponent({ }, tagUsers(): any { return { - endpoint: 'hashtags/users', + endpoint: 'hashtags/users' as const, limit: 30, params: { tag: this.tag, diff --git a/packages/client/src/pages/featured.vue b/packages/client/src/pages/featured.vue index efa74ca599..725c70f0f7 100644 --- a/packages/client/src/pages/featured.vue +++ b/packages/client/src/pages/featured.vue @@ -10,7 +10,7 @@ import * as symbols from '@/symbols'; import { i18n } from '@/i18n'; const pagination = { - endpoint: 'notes/featured', + endpoint: 'notes/featured' as const, limit: 10, offsetMode: true, }; diff --git a/packages/client/src/pages/federation.vue b/packages/client/src/pages/federation.vue index e23c4f8f16..610c9233a3 100644 --- a/packages/client/src/pages/federation.vue +++ b/packages/client/src/pages/federation.vue @@ -127,7 +127,7 @@ export default defineComponent({ state: 'federating', sort: '+pubSub', pagination: { - endpoint: 'federation/instances', + endpoint: 'federation/instances' as const, limit: 10, offsetMode: true, params: computed(() => ({ diff --git a/packages/client/src/pages/follow-requests.vue b/packages/client/src/pages/follow-requests.vue index 00dfa06564..764daa0d3e 100644 --- a/packages/client/src/pages/follow-requests.vue +++ b/packages/client/src/pages/follow-requests.vue @@ -42,7 +42,7 @@ import { i18n } from '@/i18n'; const paginationComponent = ref>(); const pagination = { - endpoint: 'following/requests/list', + endpoint: 'following/requests/list' as const, limit: 10, }; diff --git a/packages/client/src/pages/gallery/index.vue b/packages/client/src/pages/gallery/index.vue index cd0d2a40e4..a19d69d5c2 100644 --- a/packages/client/src/pages/gallery/index.vue +++ b/packages/client/src/pages/gallery/index.vue @@ -81,19 +81,19 @@ export default defineComponent({ }, tab: 'explore', recentPostsPagination: { - endpoint: 'gallery/posts', + endpoint: 'gallery/posts' as const, limit: 6, }, popularPostsPagination: { - endpoint: 'gallery/featured', + endpoint: 'gallery/featured' as const, limit: 5, }, myPostsPagination: { - endpoint: 'i/gallery/posts', + endpoint: 'i/gallery/posts' as const, limit: 5, }, likedPostsPagination: { - endpoint: 'i/gallery/likes', + endpoint: 'i/gallery/likes' as const, limit: 5, }, tags: [], @@ -106,7 +106,7 @@ export default defineComponent({ }, tagUsers(): any { return { - endpoint: 'hashtags/users', + endpoint: 'hashtags/users' as const, limit: 30, params: { tag: this.tag, diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue index 9d769deca0..fff2b6a74e 100644 --- a/packages/client/src/pages/gallery/post.vue +++ b/packages/client/src/pages/gallery/post.vue @@ -93,7 +93,7 @@ export default defineComponent({ }] } : null), otherPostsPagination: { - endpoint: 'users/gallery/posts', + endpoint: 'users/gallery/posts' as const, limit: 6, params: computed(() => ({ userId: this.post.user.id diff --git a/packages/client/src/pages/mentions.vue b/packages/client/src/pages/mentions.vue index ea23c6a2f6..bda56fc729 100644 --- a/packages/client/src/pages/mentions.vue +++ b/packages/client/src/pages/mentions.vue @@ -10,7 +10,7 @@ import * as symbols from '@/symbols'; import { i18n } from '@/i18n'; const pagination = { - endpoint: 'notes/mentions', + endpoint: 'notes/mentions' as const, limit: 10, }; diff --git a/packages/client/src/pages/messages.vue b/packages/client/src/pages/messages.vue index 448aa0241f..8efdc55586 100644 --- a/packages/client/src/pages/messages.vue +++ b/packages/client/src/pages/messages.vue @@ -10,7 +10,7 @@ import * as symbols from '@/symbols'; import { i18n } from '@/i18n'; const pagination = { - endpoint: 'notes/mentions', + endpoint: 'notes/mentions' as const, limit: 10, params: () => ({ visibility: 'specified' diff --git a/packages/client/src/pages/my-antennas/index.vue b/packages/client/src/pages/my-antennas/index.vue index d185e796c3..7138d269a9 100644 --- a/packages/client/src/pages/my-antennas/index.vue +++ b/packages/client/src/pages/my-antennas/index.vue @@ -38,7 +38,7 @@ export default defineComponent({ } }, pagination: { - endpoint: 'antennas/list', + endpoint: 'antennas/list' as const, limit: 10, }, }; diff --git a/packages/client/src/pages/my-clips/index.vue b/packages/client/src/pages/my-clips/index.vue index a5bbc3fd2d..ccfb9095d1 100644 --- a/packages/client/src/pages/my-clips/index.vue +++ b/packages/client/src/pages/my-clips/index.vue @@ -38,7 +38,7 @@ export default defineComponent({ } }, pagination: { - endpoint: 'clips/list', + endpoint: 'clips/list' as const, limit: 10, }, draft: null, diff --git a/packages/client/src/pages/my-groups/index.vue b/packages/client/src/pages/my-groups/index.vue index db5ccde466..4b2b2963a8 100644 --- a/packages/client/src/pages/my-groups/index.vue +++ b/packages/client/src/pages/my-groups/index.vue @@ -87,15 +87,15 @@ export default defineComponent({ })), tab: 'owned', ownedPagination: { - endpoint: 'users/groups/owned', + endpoint: 'users/groups/owned' as const, limit: 10, }, joinedPagination: { - endpoint: 'users/groups/joined', + endpoint: 'users/groups/joined' as const, limit: 10, }, invitationPagination: { - endpoint: 'i/user-group-invites', + endpoint: 'i/user-group-invites' as const, limit: 10, }, }; diff --git a/packages/client/src/pages/my-lists/index.vue b/packages/client/src/pages/my-lists/index.vue index 94a869b9ff..1b1b7e2d02 100644 --- a/packages/client/src/pages/my-lists/index.vue +++ b/packages/client/src/pages/my-lists/index.vue @@ -40,7 +40,7 @@ export default defineComponent({ }, }, pagination: { - endpoint: 'users/lists/list', + endpoint: 'users/lists/list' as const, limit: 10, }, }; diff --git a/packages/client/src/pages/note.vue b/packages/client/src/pages/note.vue index d40082381c..2249fa2b91 100644 --- a/packages/client/src/pages/note.vue +++ b/packages/client/src/pages/note.vue @@ -82,7 +82,7 @@ export default defineComponent({ showNext: false, error: null, prev: { - endpoint: 'users/notes', + endpoint: 'users/notes' as const, limit: 10, params: init => ({ userId: this.note.userId, @@ -91,7 +91,7 @@ export default defineComponent({ }, next: { reversed: true, - endpoint: 'users/notes', + endpoint: 'users/notes' as const, limit: 10, params: init => ({ userId: this.note.userId, diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue index 5cb3948f1c..429d1ddea2 100644 --- a/packages/client/src/pages/page.vue +++ b/packages/client/src/pages/page.vue @@ -106,7 +106,7 @@ export default defineComponent({ page: null, error: null, otherPostsPagination: { - endpoint: 'users/pages', + endpoint: 'users/pages' as const, limit: 6, params: computed(() => ({ userId: this.page.user.id diff --git a/packages/client/src/pages/pages.vue b/packages/client/src/pages/pages.vue index f1dd64f119..dcccf7f7c4 100644 --- a/packages/client/src/pages/pages.vue +++ b/packages/client/src/pages/pages.vue @@ -62,15 +62,15 @@ export default defineComponent({ })), tab: 'featured', featuredPagesPagination: { - endpoint: 'pages/featured', + endpoint: 'pages/featured' as const, noPaging: true, }, myPagesPagination: { - endpoint: 'i/pages', + endpoint: 'i/pages' as const, limit: 5, }, likedPagesPagination: { - endpoint: 'i/page-likes', + endpoint: 'i/page-likes' as const, limit: 5, }, }; diff --git a/packages/client/src/pages/search.vue b/packages/client/src/pages/search.vue index d25d5af147..ce2b7035da 100644 --- a/packages/client/src/pages/search.vue +++ b/packages/client/src/pages/search.vue @@ -18,7 +18,7 @@ const props = defineProps<{ }>(); const pagination = { - endpoint: 'notes/search', + endpoint: 'notes/search' as const, limit: 10, params: computed(() => ({ query: props.query, diff --git a/packages/client/src/pages/settings/apps.vue b/packages/client/src/pages/settings/apps.vue index a75e06165d..9c0fa8a54d 100644 --- a/packages/client/src/pages/settings/apps.vue +++ b/packages/client/src/pages/settings/apps.vue @@ -58,7 +58,7 @@ export default defineComponent({ bg: 'var(--bg)', }, pagination: { - endpoint: 'i/apps', + endpoint: 'i/apps' as const, limit: 100, params: { sort: '+lastUsedAt' diff --git a/packages/client/src/pages/settings/mute-block.vue b/packages/client/src/pages/settings/mute-block.vue index a4f9b41e80..903d32d08c 100644 --- a/packages/client/src/pages/settings/mute-block.vue +++ b/packages/client/src/pages/settings/mute-block.vue @@ -56,11 +56,11 @@ export default defineComponent({ }, tab: 'mute', mutingPagination: { - endpoint: 'mute/list', + endpoint: 'mute/list' as const, limit: 10, }, blockingPagination: { - endpoint: 'blocking/list', + endpoint: 'blocking/list' as const, limit: 10, }, } diff --git a/packages/client/src/pages/settings/security.vue b/packages/client/src/pages/settings/security.vue index 2386340028..6fb3f1c413 100644 --- a/packages/client/src/pages/settings/security.vue +++ b/packages/client/src/pages/settings/security.vue @@ -66,7 +66,7 @@ export default defineComponent({ bg: 'var(--bg)', }, pagination: { - endpoint: 'i/signin-history', + endpoint: 'i/signin-history' as const, limit: 5, }, } diff --git a/packages/client/src/pages/tag.vue b/packages/client/src/pages/tag.vue index debee2606d..8d8dc0a65c 100644 --- a/packages/client/src/pages/tag.vue +++ b/packages/client/src/pages/tag.vue @@ -14,7 +14,7 @@ const props = defineProps<{ }>(); const pagination = { - endpoint: 'notes/search-by-tag', + endpoint: 'notes/search-by-tag' as const, limit: 10, params: computed(() => ({ tag: props.tag, diff --git a/packages/client/src/pages/user/clips.vue b/packages/client/src/pages/user/clips.vue index aad5317ce0..870e6f7174 100644 --- a/packages/client/src/pages/user/clips.vue +++ b/packages/client/src/pages/user/clips.vue @@ -28,7 +28,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'users/clips', + endpoint: 'users/clips' as const, limit: 20, params: { userId: this.user.id, diff --git a/packages/client/src/pages/user/follow-list.vue b/packages/client/src/pages/user/follow-list.vue index e12ea477ca..b34757c166 100644 --- a/packages/client/src/pages/user/follow-list.vue +++ b/packages/client/src/pages/user/follow-list.vue @@ -33,14 +33,14 @@ export default defineComponent({ data() { return { followingPagination: { - endpoint: 'users/following', + endpoint: 'users/following' as const, limit: 20, params: computed(() => ({ userId: this.user.id, })), }, followersPagination: { - endpoint: 'users/followers', + endpoint: 'users/followers' as const, limit: 20, params: computed(() => ({ userId: this.user.id, diff --git a/packages/client/src/pages/user/gallery.vue b/packages/client/src/pages/user/gallery.vue index 88f0604f1f..07dda4a292 100644 --- a/packages/client/src/pages/user/gallery.vue +++ b/packages/client/src/pages/user/gallery.vue @@ -29,7 +29,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'users/gallery/posts', + endpoint: 'users/gallery/posts' as const, limit: 6, params: computed(() => ({ userId: this.user.id diff --git a/packages/client/src/pages/user/pages.vue b/packages/client/src/pages/user/pages.vue index 3075dd5729..6ce84da0a2 100644 --- a/packages/client/src/pages/user/pages.vue +++ b/packages/client/src/pages/user/pages.vue @@ -27,7 +27,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'users/pages', + endpoint: 'users/pages' as const, limit: 20, params: computed(() => ({ userId: this.user.id, diff --git a/packages/client/src/pages/user/reactions.vue b/packages/client/src/pages/user/reactions.vue index f51f6669c3..5cb035bace 100644 --- a/packages/client/src/pages/user/reactions.vue +++ b/packages/client/src/pages/user/reactions.vue @@ -36,7 +36,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'users/reactions', + endpoint: 'users/reactions' as const, limit: 20, params: computed(() => ({ userId: this.user.id, diff --git a/packages/client/src/ui/deck/direct-column.vue b/packages/client/src/ui/deck/direct-column.vue index 4206b09b97..7bf6344153 100644 --- a/packages/client/src/ui/deck/direct-column.vue +++ b/packages/client/src/ui/deck/direct-column.vue @@ -32,7 +32,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'notes/mentions', + endpoint: 'notes/mentions' as const, limit: 10, params: computed(() => ({ visibility: 'specified' diff --git a/packages/client/src/ui/deck/mentions-column.vue b/packages/client/src/ui/deck/mentions-column.vue index 4b8dc0c4ee..e007b3e08a 100644 --- a/packages/client/src/ui/deck/mentions-column.vue +++ b/packages/client/src/ui/deck/mentions-column.vue @@ -32,7 +32,7 @@ export default defineComponent({ data() { return { pagination: { - endpoint: 'notes/mentions', + endpoint: 'notes/mentions' as const, limit: 10, }, } From 7271fbb09217bc79380e924d3831fe054c9c3ab6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 13 Jan 2022 02:29:27 +0900 Subject: [PATCH 09/35] wip: refactor(client): migrate paging components to composition api --- packages/client/src/ui/deck/direct-column.vue | 45 ++++++------------- .../client/src/ui/deck/mentions-column.vue | 39 +++++----------- 2 files changed, 25 insertions(+), 59 deletions(-) diff --git a/packages/client/src/ui/deck/direct-column.vue b/packages/client/src/ui/deck/direct-column.vue index 7bf6344153..ca70f693c3 100644 --- a/packages/client/src/ui/deck/direct-column.vue +++ b/packages/client/src/ui/deck/direct-column.vue @@ -2,43 +2,26 @@ - + - diff --git a/packages/client/src/ui/deck/mentions-column.vue b/packages/client/src/ui/deck/mentions-column.vue index e007b3e08a..6822e7ef06 100644 --- a/packages/client/src/ui/deck/mentions-column.vue +++ b/packages/client/src/ui/deck/mentions-column.vue @@ -2,40 +2,23 @@ - + - From 2900f998b13e2fd1880224a53cc2472110b55dd4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 13 Jan 2022 02:36:51 +0900 Subject: [PATCH 10/35] wip: refactor(client): migrate paging components to composition api --- packages/client/src/pages/emojis.emoji.vue | 42 +++++----- packages/client/src/pages/emojis.vue | 76 +++++++++---------- .../client/src/pages/timeline.tutorial.vue | 24 ++---- 3 files changed, 58 insertions(+), 84 deletions(-) diff --git a/packages/client/src/pages/emojis.emoji.vue b/packages/client/src/pages/emojis.emoji.vue index 5dab72daea..83539ce7a3 100644 --- a/packages/client/src/pages/emojis.emoji.vue +++ b/packages/client/src/pages/emojis.emoji.vue @@ -8,35 +8,29 @@ - \ No newline at end of file + diff --git a/packages/client/src/pages/user/reactions.vue b/packages/client/src/pages/user/reactions.vue index 0381542b4d..d2c1f92ebb 100644 --- a/packages/client/src/pages/user/reactions.vue +++ b/packages/client/src/pages/user/reactions.vue @@ -7,7 +7,7 @@ - + diff --git a/packages/client/src/scripts/check-word-mute.ts b/packages/client/src/scripts/check-word-mute.ts index 3b1fa75b1e..55637bb3b3 100644 --- a/packages/client/src/scripts/check-word-mute.ts +++ b/packages/client/src/scripts/check-word-mute.ts @@ -1,4 +1,4 @@ -export async function checkWordMute(note: Record, me: Record | null | undefined, mutedWords: string[][]): Promise { +export function checkWordMute(note: Record, me: Record | null | undefined, mutedWords: string[][]): boolean { // 自分自身 if (me && (note.userId === me.id)) return false; diff --git a/packages/client/src/scripts/get-note-menu.ts b/packages/client/src/scripts/get-note-menu.ts new file mode 100644 index 0000000000..61120d53ba --- /dev/null +++ b/packages/client/src/scripts/get-note-menu.ts @@ -0,0 +1,310 @@ +import { Ref } from 'vue'; +import * as misskey from 'misskey-js'; +import { $i } from '@/account'; +import { i18n } from '@/i18n'; +import { instance } from '@/instance'; +import * as os from '@/os'; +import copyToClipboard from '@/scripts/copy-to-clipboard'; +import { url } from '@/config'; +import { noteActions } from '@/store'; +import { pleaseLogin } from './please-login'; + +export function getNoteMenu(props: { + note: misskey.entities.Note; + menuButton: Ref; + translation: Ref; + translating: Ref; +}) { + const isRenote = ( + props.note.renote != null && + props.note.text == null && + props.note.fileIds.length === 0 && + props.note.poll == null + ); + + let appearNote = isRenote ? props.note.renote as misskey.entities.Note : props.note; + + function del(): void { + os.confirm({ + type: 'warning', + text: i18n.locale.noteDeleteConfirm, + }).then(({ canceled }) => { + if (canceled) return; + + os.api('notes/delete', { + noteId: appearNote.id + }); + }); + } + + function delEdit(): void { + os.confirm({ + type: 'warning', + text: i18n.locale.deleteAndEditConfirm, + }).then(({ canceled }) => { + if (canceled) return; + + os.api('notes/delete', { + noteId: appearNote.id + }); + + os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel }); + }); + } + + function toggleFavorite(favorite: boolean): void { + os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { + noteId: appearNote.id + }); + } + + function toggleWatch(watch: boolean): void { + os.apiWithDialog(watch ? 'notes/watching/create' : 'notes/watching/delete', { + noteId: appearNote.id + }); + } + + function toggleThreadMute(mute: boolean): void { + os.apiWithDialog(mute ? 'notes/thread-muting/create' : 'notes/thread-muting/delete', { + noteId: appearNote.id + }); + } + + function copyContent(): void { + copyToClipboard(appearNote.text); + os.success(); + } + + function copyLink(): void { + copyToClipboard(`${url}/notes/${appearNote.id}`); + os.success(); + } + + function togglePin(pin: boolean): void { + os.apiWithDialog(pin ? 'i/pin' : 'i/unpin', { + noteId: appearNote.id + }, undefined, null, e => { + if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') { + os.alert({ + type: 'error', + text: i18n.locale.pinLimitExceeded + }); + } + }); + } + + async function clip(): Promise { + const clips = await os.api('clips/list'); + os.popupMenu([{ + icon: 'fas fa-plus', + text: i18n.locale.createNew, + action: async () => { + const { canceled, result } = await os.form(i18n.locale.createNewClip, { + name: { + type: 'string', + label: i18n.locale.name + }, + description: { + type: 'string', + required: false, + multiline: true, + label: i18n.locale.description + }, + isPublic: { + type: 'boolean', + label: i18n.locale.public, + default: false + } + }); + if (canceled) return; + + const clip = await os.apiWithDialog('clips/create', result); + + os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); + } + }, null, ...clips.map(clip => ({ + text: clip.name, + action: () => { + os.apiWithDialog('clips/add-note', { clipId: clip.id, noteId: appearNote.id }); + } + }))], props.menuButton.value, { + }).then(focus); + } + + async function promote(): Promise { + const { canceled, result: days } = await os.inputNumber({ + title: i18n.locale.numberOfDays, + }); + + if (canceled) return; + + os.apiWithDialog('admin/promo/create', { + noteId: appearNote.id, + expiresAt: Date.now() + (86400000 * days), + }); + } + + function share(): void { + navigator.share({ + title: i18n.t('noteOf', { user: appearNote.user.name }), + text: appearNote.text, + url: `${url}/notes/${appearNote.id}`, + }); + } + + async function translate(): Promise { + if (props.translation.value != null) return; + props.translating.value = true; + const res = await os.api('notes/translate', { + noteId: appearNote.id, + targetLang: localStorage.getItem('lang') || navigator.language, + }); + props.translating.value = false; + props.translation.value = res; + } + + let menu; + if ($i) { + const statePromise = os.api('notes/state', { + noteId: appearNote.id + }); + + menu = [{ + icon: 'fas fa-copy', + text: i18n.locale.copyContent, + action: copyContent + }, { + icon: 'fas fa-link', + text: i18n.locale.copyLink, + action: copyLink + }, (appearNote.url || appearNote.uri) ? { + icon: 'fas fa-external-link-square-alt', + text: i18n.locale.showOnRemote, + action: () => { + window.open(appearNote.url || appearNote.uri, '_blank'); + } + } : undefined, + { + icon: 'fas fa-share-alt', + text: i18n.locale.share, + action: share + }, + instance.translatorAvailable ? { + icon: 'fas fa-language', + text: i18n.locale.translate, + action: translate + } : undefined, + null, + statePromise.then(state => state.isFavorited ? { + icon: 'fas fa-star', + text: i18n.locale.unfavorite, + action: () => toggleFavorite(false) + } : { + icon: 'fas fa-star', + text: i18n.locale.favorite, + action: () => toggleFavorite(true) + }), + { + icon: 'fas fa-paperclip', + text: i18n.locale.clip, + action: () => clip() + }, + (appearNote.userId != $i.id) ? statePromise.then(state => state.isWatching ? { + icon: 'fas fa-eye-slash', + text: i18n.locale.unwatch, + action: () => toggleWatch(false) + } : { + icon: 'fas fa-eye', + text: i18n.locale.watch, + action: () => toggleWatch(true) + }) : undefined, + statePromise.then(state => state.isMutedThread ? { + icon: 'fas fa-comment-slash', + text: i18n.locale.unmuteThread, + action: () => toggleThreadMute(false) + } : { + icon: 'fas fa-comment-slash', + text: i18n.locale.muteThread, + action: () => toggleThreadMute(true) + }), + appearNote.userId == $i.id ? ($i.pinnedNoteIds || []).includes(appearNote.id) ? { + icon: 'fas fa-thumbtack', + text: i18n.locale.unpin, + action: () => togglePin(false) + } : { + icon: 'fas fa-thumbtack', + text: i18n.locale.pin, + action: () => togglePin(true) + } : undefined, + /* + ...($i.isModerator || $i.isAdmin ? [ + null, + { + icon: 'fas fa-bullhorn', + text: i18n.locale.promote, + action: promote + }] + : [] + ),*/ + ...(appearNote.userId != $i.id ? [ + null, + { + icon: 'fas fa-exclamation-circle', + text: i18n.locale.reportAbuse, + action: () => { + const u = `${url}/notes/${appearNote.id}`; + os.popup(import('@/components/abuse-report-window.vue'), { + user: appearNote.user, + initialComment: `Note: ${u}\n-----\n` + }, {}, 'closed'); + } + }] + : [] + ), + ...(appearNote.userId == $i.id || $i.isModerator || $i.isAdmin ? [ + null, + appearNote.userId == $i.id ? { + icon: 'fas fa-edit', + text: i18n.locale.deleteAndEdit, + action: delEdit + } : undefined, + { + icon: 'fas fa-trash-alt', + text: i18n.locale.delete, + danger: true, + action: del + }] + : [] + )] + .filter(x => x !== undefined); + } else { + menu = [{ + icon: 'fas fa-copy', + text: i18n.locale.copyContent, + action: copyContent + }, { + icon: 'fas fa-link', + text: i18n.locale.copyLink, + action: copyLink + }, (appearNote.url || appearNote.uri) ? { + icon: 'fas fa-external-link-square-alt', + text: i18n.locale.showOnRemote, + action: () => { + window.open(appearNote.url || appearNote.uri, '_blank'); + } + } : undefined] + .filter(x => x !== undefined); + } + + if (noteActions.length > 0) { + menu = menu.concat([null, ...noteActions.map(action => ({ + icon: 'fas fa-plug', + text: action.title, + action: () => { + action.handler(appearNote); + } + }))]); + } + + return menu; +} diff --git a/packages/client/src/scripts/use-note-capture.ts b/packages/client/src/scripts/use-note-capture.ts new file mode 100644 index 0000000000..bb00e464e3 --- /dev/null +++ b/packages/client/src/scripts/use-note-capture.ts @@ -0,0 +1,123 @@ +import { onUnmounted, Ref } from 'vue'; +import * as misskey from 'misskey-js'; +import { stream } from '@/stream'; +import { $i } from '@/account'; + +export function useNoteCapture(props: { + rootEl: Ref; + appearNote: Ref; +}) { + const appearNote = props.appearNote; + const connection = $i ? stream : null; + + function onStreamNoteUpdated(data): void { + const { type, id, body } = data; + + if (id !== appearNote.value.id) return; + + switch (type) { + case 'reacted': { + const reaction = body.reaction; + + const updated = JSON.parse(JSON.stringify(appearNote.value)); + + if (body.emoji) { + const emojis = appearNote.value.emojis || []; + if (!emojis.includes(body.emoji)) { + updated.emojis = [...emojis, body.emoji]; + } + } + + // TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる + const currentCount = (appearNote.value.reactions || {})[reaction] || 0; + + updated.reactions[reaction] = currentCount + 1; + + if ($i && (body.userId === $i.id)) { + updated.myReaction = reaction; + } + + appearNote.value = updated; + break; + } + + case 'unreacted': { + const reaction = body.reaction; + + const updated = JSON.parse(JSON.stringify(appearNote.value)); + + // TODO: reactionsプロパティがない場合ってあったっけ? なければ || {} は消せる + const currentCount = (appearNote.value.reactions || {})[reaction] || 0; + + updated.reactions[reaction] = Math.max(0, currentCount - 1); + + if ($i && (body.userId === $i.id)) { + updated.myReaction = null; + } + + appearNote.value = updated; + break; + } + + case 'pollVoted': { + const choice = body.choice; + + const updated = JSON.parse(JSON.stringify(appearNote.value)); + + const choices = [...appearNote.value.poll.choices]; + choices[choice] = { + ...choices[choice], + votes: choices[choice].votes + 1, + ...($i && (body.userId === $i.id) ? { + isVoted: true + } : {}) + }; + + updated.poll.choices = choices; + + appearNote.value = updated; + break; + } + + case 'deleted': { + const updated = JSON.parse(JSON.stringify(appearNote.value)); + updated.value = true; + appearNote.value = updated; + break; + } + } + } + + function capture(withHandler = false): void { + if (connection) { + // TODO: このノートがストリーミング経由で流れてきた場合のみ sr する + connection.send(document.body.contains(props.rootEl.value) ? 'sr' : 's', { id: appearNote.value.id }); + if (withHandler) connection.on('noteUpdated', onStreamNoteUpdated); + } + } + + function decapture(withHandler = false): void { + if (connection) { + connection.send('un', { + id: appearNote.value.id, + }); + if (withHandler) connection.off('noteUpdated', onStreamNoteUpdated); + } + } + + function onStreamConnected() { + capture(false); + } + + capture(true); + if (connection) { + connection.on('_connected_', onStreamConnected); + } + + onUnmounted(() => { + decapture(true); + if (connection) { + connection.off('_connected_', onStreamConnected); + } + }); +} diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts index 745d323100..a57e8ec62b 100644 --- a/packages/client/src/store.ts +++ b/packages/client/src/store.ts @@ -160,7 +160,7 @@ export const defaultStore = markRaw(new Storage('base', { }, useReactionPickerForContextMenu: { where: 'device', - default: true + default: false }, showGapBetweenNotesInTimeline: { where: 'device', From 41ece00789dedbcacfdfff885707ace15b0e2696 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 14 Jan 2022 10:29:18 +0900 Subject: [PATCH 15/35] wip: refactor(client): migrate paging components to composition api --- .../client/src/components/note-preview.vue | 18 ++++------- .../client/src/components/note-simple.vue | 30 +++++-------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/packages/client/src/components/note-preview.vue b/packages/client/src/components/note-preview.vue index bdcb8d5eed..a78b499654 100644 --- a/packages/client/src/components/note-preview.vue +++ b/packages/client/src/components/note-preview.vue @@ -14,20 +14,12 @@ -