From 12a3c6872f0a31c923bf0cd7c183cb8776d58dda Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 28 Apr 2022 11:14:03 +0900 Subject: enhance: ドライブに画像ファイルをアップロードするときオリジナル画像を破棄してwebpublicのみ保持するオプション (#8216) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * Update packages/client/src/os.ts Co-authored-by: tamaina * メニューをComposition API化、switchアイテム追加 クライアントサイド画像圧縮の準備 * メニュー型定義を分離 (TypeScriptの型支援が効かないので) * disabled * make keepOriginal to follow setting value * :v: * fix * fix * :v: * WEBP * aaa * :v: * webp * lazy load browser-image-resizer * rename * rename 2 * Fix * clean up * add comment * clean up * jpeg, pngにもどす * fix * fix name * webpでなくする ただしサムネやプレビューはwebpのまま (テスト) * 動画サムネイルはjpegに * エラーハンドリング * :v: * v2.2.1-misskey-beta.2 * browser-image-resizer#v2.2.1-misskey.1 * :v: * fix alert * update browser-image-resizer to v2.2.1-misskey.2 * lockfile Co-authored-by: mei23 Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com> --- packages/client/src/ui/_common_/common.vue | 3 ++- packages/client/src/ui/_common_/upload.vue | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'packages/client/src/ui') diff --git a/packages/client/src/ui/_common_/common.vue b/packages/client/src/ui/_common_/common.vue index 05688d7c53..50d95539d1 100644 --- a/packages/client/src/ui/_common_/common.vue +++ b/packages/client/src/ui/_common_/common.vue @@ -17,7 +17,8 @@ -- cgit v1.2.3-freya From 766559c6e91deec660e39783badb42d88bbaac56 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 30 Apr 2022 21:52:07 +0900 Subject: feat: Improve Push Notification (#7667) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * clean up * ev => data * refactor * clean up * add type * antenna * channel * fix * add Packed type * add PackedRef * fix lint * add emoji schema * add reversiGame * add reversiMatching * remove signin schema (use Signin entity) * add schemas refs, fix Packed type * wip PackedHoge => Packed<'Hoge'> * add Packed type * note-reaction * user * user-group * user-list * note * app, messaging-message * notification * drive-file * drive-folder * following * muting * blocking * hashtag * page * app (with modifying schema) * import user? * channel * antenna * clip * gallery-post * emoji * Packed * reversi-matching * update stream.ts * https://github.com/misskey-dev/misskey/pull/7769#issuecomment-917542339 * fix lint * clean up? * add app * fix * nanka iroiro * wip * wip * fix lint * fix loginId * fix * refactor * refactor * remove follow action * clean up * Revert "remove follow action" This reverts commit defbb416480905af2150d1c92f10d8e1d1288c0a. * Revert "clean up" This reverts commit f94919cb9cff41e274044fc69c56ad36a33974f2. * remove fetch specification * renoteの条件追加 * apiFetch => cli * bypass fetch? * fix * refactor: use path alias * temp: add submodule * remove submodule * enhane: unison-reloadに指定したパスに移動できるように * null * null * feat: ログインするアカウントのIDをクエリ文字列で指定する機能 * null * await? * rename * rename * Update read.ts * merge * get-note-summary * fix * swパッケージに * add missing packages * fix getNoteSummary * add webpack-cli * :v: * remove plugins * sw-inject分離したがテストしてない * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix notification.vue * remove a blank line * disconnect intersection observer * disconnect2 * fix * :v: * clean up config * typesを戻した * Update packages/client/src/components/notification.vue Co-authored-by: Acid Chicken (硫酸鶏) * disconnect * oops * Failed to load the script unexpectedly回避 sw.jsとlib.tsを分離してみた * truncate notification * Update packages/client/src/ui/_common_/common.vue Co-authored-by: syuilo * clean up * clean up * キャッシュ対策 * Truncate push notification message * クライアントがあったらストリームに接続しているということなので通知しない判定の位置を修正 * components/drive-file-thumbnail.vue * components/drive-select-dialog.vue * components/drive-window.vue * merge * fix * Service Workerのビルドにesbuildを使うようにする * return createEmptyNotification() * fix * i18n.ts * update * :v: * remove ts-loader * fix * fix * enhance: Service Workerを常に登録するように * pollEnded * URLをsw.jsに戻す * clean up Co-authored-by: Acid Chicken (硫酸鶏) Co-authored-by: syuilo --- CHANGELOG.md | 4 +- locales/ja-JP.yml | 10 +- .../server/api/common/read-messaging-message.ts | 29 + .../src/server/api/common/read-notification.ts | 26 +- .../endpoints/notifications/mark-all-as-read.ts | 2 + .../src/server/api/endpoints/notifications/read.ts | 41 +- packages/backend/src/server/web/index.ts | 8 +- .../backend/src/services/create-notification.ts | 4 +- packages/backend/src/services/messages/create.ts | 2 +- packages/backend/src/services/push-notification.ts | 24 +- packages/client/src/components/notification.vue | 6 +- packages/client/src/components/notifications.vue | 25 + packages/client/src/components/ui/pagination.vue | 1 + packages/client/src/init.ts | 1 - packages/client/src/scripts/get-user-name.ts | 3 + packages/client/src/scripts/initialize-sw.ts | 30 +- packages/client/src/sw/compose-notification.ts | 107 ---- packages/client/src/sw/sw.ts | 123 ---- packages/client/src/ui/_common_/common.vue | 6 + packages/client/src/ui/_common_/sw-inject.ts | 45 ++ packages/client/tsconfig.json | 3 +- packages/client/webpack.config.js | 1 - packages/sw/.eslintrc.js | 22 + packages/sw/.npmrc | 2 + packages/sw/.yarnrc | 1 + packages/sw/build.js | 37 ++ packages/sw/package.json | 17 + packages/sw/src/filters/user.ts | 14 + packages/sw/src/scripts/create-notification.ts | 237 +++++++ packages/sw/src/scripts/get-account-from-id.ts | 7 + packages/sw/src/scripts/get-user-name.ts | 3 + packages/sw/src/scripts/i18n.ts | 29 + packages/sw/src/scripts/lang.ts | 47 ++ packages/sw/src/scripts/login-id.ts | 11 + packages/sw/src/scripts/notification-read.ts | 50 ++ packages/sw/src/scripts/operations.ts | 70 ++ packages/sw/src/sw.ts | 200 ++++++ packages/sw/src/types.ts | 31 + packages/sw/tsconfig.json | 39 ++ packages/sw/webpack.config.js | 71 +++ packages/sw/yarn.lock | 710 +++++++++++++++++++++ scripts/build.js | 8 + scripts/clean-all.js | 3 + scripts/clean.js | 1 + scripts/dev.js | 6 + scripts/install-packages.js | 8 + scripts/lint.js | 7 + 47 files changed, 1834 insertions(+), 298 deletions(-) create mode 100644 packages/client/src/scripts/get-user-name.ts delete mode 100644 packages/client/src/sw/compose-notification.ts delete mode 100644 packages/client/src/sw/sw.ts create mode 100644 packages/client/src/ui/_common_/sw-inject.ts create mode 100644 packages/sw/.eslintrc.js create mode 100644 packages/sw/.npmrc create mode 100644 packages/sw/.yarnrc create mode 100644 packages/sw/build.js create mode 100644 packages/sw/package.json create mode 100644 packages/sw/src/filters/user.ts create mode 100644 packages/sw/src/scripts/create-notification.ts create mode 100644 packages/sw/src/scripts/get-account-from-id.ts create mode 100644 packages/sw/src/scripts/get-user-name.ts create mode 100644 packages/sw/src/scripts/i18n.ts create mode 100644 packages/sw/src/scripts/lang.ts create mode 100644 packages/sw/src/scripts/login-id.ts create mode 100644 packages/sw/src/scripts/notification-read.ts create mode 100644 packages/sw/src/scripts/operations.ts create mode 100644 packages/sw/src/sw.ts create mode 100644 packages/sw/src/types.ts create mode 100644 packages/sw/tsconfig.json create mode 100644 packages/sw/webpack.config.js create mode 100644 packages/sw/yarn.lock (limited to 'packages/client/src/ui') diff --git a/CHANGELOG.md b/CHANGELOG.md index 088b7118a9..b07e9002cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## 12.x.x (unreleased) ### Improvements -- +- API: notifications/readは配列でも受け付けるように +- /share のクエリでリプライやファイル等の情報を渡せるように +- ページロードエラーページにリロードボタンを追加 ### Bugfixes - diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6326094dd8..4d04cd28c8 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -356,7 +356,7 @@ antennaExcludeKeywords: "除外キーワード" antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります" notifyAntenna: "新しいノートを通知する" withFileAntenna: "ファイルが添付されたノートのみ" -enableServiceworker: "ServiceWorkerを有効にする" +enableServiceworker: "ブラウザへのプッシュ通知を有効にする" antennaUsersDescription: "ユーザー名を改行で区切って指定します" caseSensitive: "大文字小文字を区別する" withReplies: "返信を含む" @@ -1668,8 +1668,9 @@ _notification: youWereFollowed: "フォローされました" youReceivedFollowRequest: "フォローリクエストが来ました" yourFollowRequestAccepted: "フォローリクエストが承認されました" - youWereInvitedToGroup: "グループに招待されました" + youWereInvitedToGroup: "{userName}があなたをグループに招待しました" pollEnded: "アンケートの結果が出ました" + emptyPushNotificationMessage: "プッシュ通知の更新をしました" _types: all: "すべて" @@ -1686,6 +1687,11 @@ _notification: groupInvited: "グループに招待された" app: "連携アプリからの通知" + _actions: + followBack: "フォローバック" + reply: "返信" + renote: "Renote" + _deck: alwaysShowMainColumn: "常にメインカラムを表示" columnAlign: "カラムの寄せ" diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts index 3638518e67..c4c18ffa06 100644 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ b/packages/backend/src/server/api/common/read-messaging-message.ts @@ -1,6 +1,7 @@ import { publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; import { publishMessagingStream } from '@/services/stream.js'; import { publishMessagingIndexStream } from '@/services/stream.js'; +import { pushNotification } from '@/services/push-notification.js'; import { User, IRemoteUser } from '@/models/entities/user.js'; import { MessagingMessage } from '@/models/entities/messaging-message.js'; import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js'; @@ -50,6 +51,21 @@ export async function readUserMessagingMessage( if (!await Users.getHasUnreadMessagingMessage(userId)) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 publishMainStream(userId, 'readAllMessagingMessages'); + pushNotification(userId, 'readAllMessagingMessages', undefined); + } else { + // そのユーザーとのメッセージで未読がなければイベント発行 + const count = await MessagingMessages.count({ + where: { + userId: otherpartyId, + recipientId: userId, + isRead: false, + }, + take: 1 + }); + + if (!count) { + pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId }); + } } } @@ -104,6 +120,19 @@ export async function readGroupMessagingMessage( if (!await Users.getHasUnreadMessagingMessage(userId)) { // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 publishMainStream(userId, 'readAllMessagingMessages'); + pushNotification(userId, 'readAllMessagingMessages', undefined); + } else { + // そのグループにおいて未読がなければイベント発行 + const unreadExist = await MessagingMessages.createQueryBuilder('message') + .where(`message.groupId = :groupId`, { groupId: groupId }) + .andWhere('message.userId != :userId', { userId: userId }) + .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) + .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない + .getOne().then(x => x != null); + + if (!unreadExist) { + pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId }); + } } } diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts index 1f575042a0..0dad35bcc2 100644 --- a/packages/backend/src/server/api/common/read-notification.ts +++ b/packages/backend/src/server/api/common/read-notification.ts @@ -1,4 +1,5 @@ import { publishMainStream } from '@/services/stream.js'; +import { pushNotification } from '@/services/push-notification.js'; import { User } from '@/models/entities/user.js'; import { Notification } from '@/models/entities/notification.js'; import { Notifications, Users } from '@/models/index.js'; @@ -16,28 +17,29 @@ export async function readNotification( isRead: true, }); - post(userId); + if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId); + else return postReadNotifications(userId, notificationIds); } export async function readNotificationByQuery( userId: User['id'], query: Record ) { - // Update documents - await Notifications.update({ + const notificationIds = await Notifications.find({ ...query, notifieeId: userId, isRead: false, - }, { - isRead: true, - }); + }).then(notifications => notifications.map(notification => notification.id)); + + return readNotification(userId, notificationIds); +} - post(userId); +function postReadAllNotifications(userId: User['id']) { + publishMainStream(userId, 'readAllNotifications'); + return pushNotification(userId, 'readAllNotifications', undefined); } -async function post(userId: User['id']) { - if (!await Users.getHasUnreadNotification(userId)) { - // 全ての(いままで未読だった)通知を(これで)読みましたよというイベントを発行 - publishMainStream(userId, 'readAllNotifications'); - } +function postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { + publishMainStream(userId, 'readNotifications', notificationIds); + return pushNotification(userId, 'readNotifications', { notificationIds }); } diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index abefe07be6..4575cba43f 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,4 +1,5 @@ import { publishMainStream } from '@/services/stream.js'; +import { pushNotification } from '@/services/push-notification.js'; import define from '../../define.js'; import { Notifications } from '@/models/index.js'; @@ -28,4 +29,5 @@ export default define(meta, paramDef, async (ps, user) => { // 全ての通知を読みましたよというイベントを発行 publishMainStream(user.id, 'readAllNotifications'); + pushNotification(user.id, 'readAllNotifications', undefined); }); diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index c7bc5dc0a5..65e96d4862 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,10 +1,12 @@ -import { publishMainStream } from '@/services/stream.js'; import define from '../../define.js'; -import { Notifications } from '@/models/index.js'; import { readNotification } from '../../common/read-notification.js'; -import { ApiError } from '../../error.js'; export const meta = { + desc: { + 'ja-JP': '通知を既読にします。', + 'en-US': 'Mark a notification as read.' + }, + tags: ['notifications', 'account'], requireCredential: true, @@ -21,23 +23,26 @@ export const meta = { } as const; export const paramDef = { - type: 'object', - properties: { - notificationId: { type: 'string', format: 'misskey:id' }, - }, - required: ['notificationId'], + oneOf: [ + { + type: 'object', + properties: { + notificationId: { type: 'string', format: 'misskey:id' }, + }, + required: ['notificationId'], + }, + { + type: 'object', + properties: { + notificationIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } }, + }, + required: ['notificationIds'], + }, + ], } as const; // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { - const notification = await Notifications.findOneBy({ - notifieeId: user.id, - id: ps.notificationId, - }); - - if (notification == null) { - throw new ApiError(meta.errors.noSuchNotification); - } - - readNotification(user.id, [notification.id]); + if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); + return readNotification(user.id, ps.notificationIds); }); diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index e80bf45d14..061ea50609 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -32,6 +32,7 @@ const _dirname = dirname(_filename); const staticAssets = `${_dirname}/../../../assets/`; const clientAssets = `${_dirname}/../../../../client/assets/`; const assets = `${_dirname}/../../../../../built/_client_dist_/`; +const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; // Init app const app = new Koa(); @@ -136,9 +137,10 @@ router.get('/twemoji/(.*)', async ctx => { }); // ServiceWorker -router.get('/sw.js', async ctx => { - await send(ctx as any, `/sw.${config.version}.js`, { - root: assets, +router.get(`/sw.js`, async ctx => { + await send(ctx as any, `/sw.js`, { + root: swAssets, + maxage: ms('10 minutes'), }); }); diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts index 9a53db1f38..d53a4235b8 100644 --- a/packages/backend/src/services/create-notification.ts +++ b/packages/backend/src/services/create-notification.ts @@ -1,5 +1,5 @@ import { publishMainStream } from '@/services/stream.js'; -import pushSw from './push-notification.js'; +import { pushNotification } from '@/services/push-notification.js'; import { Notifications, Mutings, UserProfiles, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { User } from '@/models/entities/user.js'; @@ -52,8 +52,8 @@ export async function createNotification( //#endregion publishMainStream(notifieeId, 'unreadNotification', packed); + pushNotification(notifieeId, 'notification', packed); - pushSw(notifieeId, 'notification', packed); if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); }, 2000); diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index e5cd5a30d2..e6b3204922 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -5,7 +5,7 @@ import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/i import { genId } from '@/misc/gen-id.js'; import { MessagingMessage } from '@/models/entities/messaging-message.js'; import { publishMessagingStream, publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; -import pushNotification from '../push-notification.js'; +import { pushNotification } from '@/services/push-notification.js'; import { Not } from 'typeorm'; import { Note } from '@/models/entities/note.js'; import renderNote from '@/remote/activitypub/renderer/note.js'; diff --git a/packages/backend/src/services/push-notification.ts b/packages/backend/src/services/push-notification.ts index 41122c92e8..5c3bafbb34 100644 --- a/packages/backend/src/services/push-notification.ts +++ b/packages/backend/src/services/push-notification.ts @@ -5,8 +5,15 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Packed } from '@/misc/schema.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; -type notificationType = 'notification' | 'unreadMessagingMessage'; -type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>; +// Defined also packages/sw/types.ts#L14-L21 +type pushNotificationsTypes = { + 'notification': Packed<'Notification'>; + 'unreadMessagingMessage': Packed<'MessagingMessage'>; + 'readNotifications': { notificationIds: string[] }; + 'readAllNotifications': undefined; + 'readAllMessagingMessages': undefined; + 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string }; +}; // プッシュメッセージサーバーには文字数制限があるため、内容を削減します function truncateNotification(notification: Packed<'Notification'>): any { @@ -17,12 +24,11 @@ function truncateNotification(notification: Packed<'Notification'>): any { ...notification.note, // textをgetNoteSummaryしたものに置き換える text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note), - ...{ - cw: undefined, - reply: undefined, - renote: undefined, - user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる - } + + cw: undefined, + reply: undefined, + renote: undefined, + user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる } }; } @@ -30,7 +36,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { return notification; } -export default async function(userId: string, type: notificationType, body: notificationBody) { +export async function pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]) { const meta = await fetchMeta(); if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue index 1a360f9905..3791c576ee 100644 --- a/packages/client/src/components/notification.vue +++ b/packages/client/src/components/notification.vue @@ -72,7 +72,7 @@ diff --git a/packages/client/src/components/ui/menu.vue b/packages/client/src/components/ui/menu.vue index a93cc8cda8..ca56048262 100644 --- a/packages/client/src/components/ui/menu.vue +++ b/packages/client/src/components/ui/menu.vue @@ -60,7 +60,7 @@ const props = defineProps<{ }>(); const emit = defineEmits<{ - (e: 'close'): void; + (ev: 'close'): void; }>(); let itemsEl = $ref(); diff --git a/packages/client/src/components/ui/modal-window.vue b/packages/client/src/components/ui/modal-window.vue index b4b8c2b965..6de29c83fa 100644 --- a/packages/client/src/components/ui/modal-window.vue +++ b/packages/client/src/components/ui/modal-window.vue @@ -79,10 +79,10 @@ export default defineComponent({ this.$refs.modal.close(); }, - onKeydown(e) { - if (e.which === 27) { // Esc - e.preventDefault(); - e.stopPropagation(); + onKeydown(evt) { + if (evt.which === 27) { // Esc + evt.preventDefault(); + evt.stopPropagation(); this.close(); } }, diff --git a/packages/client/src/components/ui/pagination.vue b/packages/client/src/components/ui/pagination.vue index 428a9d0225..c081e06acd 100644 --- a/packages/client/src/components/ui/pagination.vue +++ b/packages/client/src/components/ui/pagination.vue @@ -68,7 +68,7 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (e: 'queue', count: number): void; + (ev: 'queue', count: number): void; }>(); type Item = { id: string; [another: string]: unknown; }; @@ -112,7 +112,7 @@ const init = async (): Promise => { offset.value = res.length; error.value = false; fetching.value = false; - }, e => { + }, err => { error.value = true; fetching.value = false; }); @@ -155,7 +155,7 @@ const fetchMore = async (): Promise => { } offset.value += res.length; moreFetching.value = false; - }, e => { + }, err => { moreFetching.value = false; }); }; @@ -183,7 +183,7 @@ const fetchMoreAhead = async (): Promise => { } offset.value += res.length; moreFetching.value = false; - }, e => { + }, err => { moreFetching.value = false; }); }; diff --git a/packages/client/src/components/ui/popup-menu.vue b/packages/client/src/components/ui/popup-menu.vue index 8d6c1b5695..2bc7030d77 100644 --- a/packages/client/src/components/ui/popup-menu.vue +++ b/packages/client/src/components/ui/popup-menu.vue @@ -19,7 +19,7 @@ defineProps<{ }>(); const emit = defineEmits<{ - (e: 'closed'): void; + (ev: 'closed'): void; }>(); let modal = $ref>(); diff --git a/packages/client/src/components/ui/window.vue b/packages/client/src/components/ui/window.vue index fa32ecfdef..2066cf579d 100644 --- a/packages/client/src/components/ui/window.vue +++ b/packages/client/src/components/ui/window.vue @@ -139,10 +139,10 @@ export default defineComponent({ this.showing = false; }, - onKeydown(e) { - if (e.which === 27) { // Esc - e.preventDefault(); - e.stopPropagation(); + onKeydown(evt) { + if (evt.which === 27) { // Esc + evt.preventDefault(); + evt.stopPropagation(); this.close(); } }, @@ -162,15 +162,15 @@ export default defineComponent({ this.top(); }, - onHeaderMousedown(e) { + onHeaderMousedown(evt) { const main = this.$el as any; if (!contains(main, document.activeElement)) main.focus(); const position = main.getBoundingClientRect(); - const clickX = e.touches && e.touches.length > 0 ? e.touches[0].clientX : e.clientX; - const clickY = e.touches && e.touches.length > 0 ? e.touches[0].clientY : e.clientY; + const clickX = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientX : evt.clientX; + const clickY = evt.touches && evt.touches.length > 0 ? evt.touches[0].clientY : evt.clientY; const moveBaseX = clickX - position.left; const moveBaseY = clickY - position.top; const browserWidth = window.innerWidth; @@ -204,10 +204,10 @@ export default defineComponent({ }, // 上ハンドル掴み時 - onTopHandleMousedown(e) { + onTopHandleMousedown(evt) { const main = this.$el as any; - const base = e.clientY; + const base = evt.clientY; const height = parseInt(getComputedStyle(main, '').height, 10); const top = parseInt(getComputedStyle(main, '').top, 10); @@ -230,10 +230,10 @@ export default defineComponent({ }, // 右ハンドル掴み時 - onRightHandleMousedown(e) { + onRightHandleMousedown(evt) { const main = this.$el as any; - const base = e.clientX; + const base = evt.clientX; const width = parseInt(getComputedStyle(main, '').width, 10); const left = parseInt(getComputedStyle(main, '').left, 10); const browserWidth = window.innerWidth; @@ -254,10 +254,10 @@ export default defineComponent({ }, // 下ハンドル掴み時 - onBottomHandleMousedown(e) { + onBottomHandleMousedown(evt) { const main = this.$el as any; - const base = e.clientY; + const base = evt.clientY; const height = parseInt(getComputedStyle(main, '').height, 10); const top = parseInt(getComputedStyle(main, '').top, 10); const browserHeight = window.innerHeight; @@ -278,10 +278,10 @@ export default defineComponent({ }, // 左ハンドル掴み時 - onLeftHandleMousedown(e) { + onLeftHandleMousedown(evt) { const main = this.$el as any; - const base = e.clientX; + const base = evt.clientX; const width = parseInt(getComputedStyle(main, '').width, 10); const left = parseInt(getComputedStyle(main, '').left, 10); @@ -304,27 +304,27 @@ export default defineComponent({ }, // 左上ハンドル掴み時 - onTopLeftHandleMousedown(e) { - this.onTopHandleMousedown(e); - this.onLeftHandleMousedown(e); + onTopLeftHandleMousedown(evt) { + this.onTopHandleMousedown(evt); + this.onLeftHandleMousedown(evt); }, // 右上ハンドル掴み時 - onTopRightHandleMousedown(e) { - this.onTopHandleMousedown(e); - this.onRightHandleMousedown(e); + onTopRightHandleMousedown(evt) { + this.onTopHandleMousedown(evt); + this.onRightHandleMousedown(evt); }, // 右下ハンドル掴み時 - onBottomRightHandleMousedown(e) { - this.onBottomHandleMousedown(e); - this.onRightHandleMousedown(e); + onBottomRightHandleMousedown(evt) { + this.onBottomHandleMousedown(evt); + this.onRightHandleMousedown(evt); }, // 左下ハンドル掴み時 - onBottomLeftHandleMousedown(e) { - this.onBottomHandleMousedown(e); - this.onLeftHandleMousedown(e); + onBottomLeftHandleMousedown(evt) { + this.onBottomHandleMousedown(evt); + this.onLeftHandleMousedown(evt); }, // 高さを適用 diff --git a/packages/client/src/components/user-preview.vue b/packages/client/src/components/user-preview.vue index 51c5330564..f80947f75a 100644 --- a/packages/client/src/components/user-preview.vue +++ b/packages/client/src/components/user-preview.vue @@ -70,7 +70,7 @@ export default defineComponent({ }, mounted() { - if (typeof this.q == 'object') { + if (typeof this.q === 'object') { this.user = this.q; this.fetched = true; } else { diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue index dbef34d547..b34d21af07 100644 --- a/packages/client/src/components/user-select-dialog.vue +++ b/packages/client/src/components/user-select-dialog.vue @@ -60,9 +60,9 @@ import * as os from '@/os'; import { defaultStore } from '@/store'; const emit = defineEmits<{ - (e: 'ok', selected: misskey.entities.UserDetailed): void; - (e: 'cancel'): void; - (e: 'closed'): void; + (ev: 'ok', selected: misskey.entities.UserDetailed): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; }>(); let username = $ref(''); diff --git a/packages/client/src/components/visibility-picker.vue b/packages/client/src/components/visibility-picker.vue index 4b20063a51..c717c3a461 100644 --- a/packages/client/src/components/visibility-picker.vue +++ b/packages/client/src/components/visibility-picker.vue @@ -57,9 +57,9 @@ const props = withDefaults(defineProps<{ }); const emit = defineEmits<{ - (e: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void; - (e: 'changeLocalOnly', v: boolean): void; - (e: 'closed'): void; + (ev: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void; + (ev: 'changeLocalOnly', v: boolean): void; + (ev: 'closed'): void; }>(); let v = $ref(props.currentVisibility); diff --git a/packages/client/src/components/waiting-dialog.vue b/packages/client/src/components/waiting-dialog.vue index 7dfcc55695..9e631b55b1 100644 --- a/packages/client/src/components/waiting-dialog.vue +++ b/packages/client/src/components/waiting-dialog.vue @@ -21,8 +21,8 @@ const props = defineProps<{ }>(); const emit = defineEmits<{ - (e: 'done'); - (e: 'closed'); + (ev: 'done'); + (ev: 'closed'); }>(); function done() { diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue index 6e4122427b..b6835795cb 100644 --- a/packages/client/src/components/widgets.vue +++ b/packages/client/src/components/widgets.vue @@ -19,7 +19,7 @@
- +
diff --git a/packages/client/src/filters/bytes.ts b/packages/client/src/filters/bytes.ts index 50e63534b6..c80f2f0ed2 100644 --- a/packages/client/src/filters/bytes.ts +++ b/packages/client/src/filters/bytes.ts @@ -1,7 +1,7 @@ export default (v, digits = 0) => { if (v == null) return '?'; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; - if (v == 0) return '0'; + if (v === 0) return '0'; const isMinus = v < 0; if (isMinus) v = -v; const i = Math.floor(Math.log(v) / Math.log(1024)); diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts index 5dbbcb2a2b..bb6176e409 100644 --- a/packages/client/src/init.ts +++ b/packages/client/src/init.ts @@ -146,7 +146,7 @@ if ($i && $i.token) { try { document.body.innerHTML = '
Please wait...
'; await login(i); - } catch (e) { + } catch (err) { // Render the error screen // TODO: ちゃんとしたコンポーネントをレンダリングする(v10とかのトラブルシューティングゲーム付きのやつみたいな) document.body.innerHTML = '
Oops!
'; @@ -249,7 +249,7 @@ if (lastVersion !== version) { popup(defineAsyncComponent(() => import('@/components/updated.vue')), {}, {}, 'closed'); } } - } catch (e) { + } catch (err) { } } @@ -334,7 +334,7 @@ stream.on('_disconnected_', async () => { } }); -stream.on('emojiAdded', data => { +stream.on('emojiAdded', emojiData => { // TODO //store.commit('instance/set', ); }); diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts index 6e912aa2e5..d24eb2419a 100644 --- a/packages/client/src/instance.ts +++ b/packages/client/src/instance.ts @@ -4,11 +4,11 @@ import { api } from './os'; // TODO: 他のタブと永続化されたstateを同期 -const data = localStorage.getItem('instance'); +const instanceData = localStorage.getItem('instance'); // TODO: instanceをリアクティブにするかは再考の余地あり -export const instance: Misskey.entities.InstanceMetadata = reactive(data ? JSON.parse(data) : { +export const instance: Misskey.entities.InstanceMetadata = reactive(instanceData ? JSON.parse(instanceData) : { // TODO: set default values }); diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index 06a8ff99dc..6baf538917 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -59,10 +59,10 @@ export const apiWithDialog = (( token?: string | null | undefined, ) => { const promise = api(endpoint, data, token); - promiseDialog(promise, null, (e) => { + promiseDialog(promise, null, (err) => { alert({ type: 'error', - text: e.message + '\n' + (e as any).id, + text: err.message + '\n' + (err as any).id, }); }); @@ -72,7 +72,7 @@ export const apiWithDialog = (( export function promiseDialog>( promise: T, onSuccess?: ((res: any) => void) | null, - onFailure?: ((e: Error) => void) | null, + onFailure?: ((err: Error) => void) | null, text?: string, ): T { const showing = ref(true); @@ -88,14 +88,14 @@ export function promiseDialog>( showing.value = false; }, 1000); } - }).catch(e => { + }).catch(err => { showing.value = false; if (onFailure) { - onFailure(e); + onFailure(err); } else { alert({ type: 'error', - text: e + text: err, }); } }); diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue index ffb7fb34aa..38bcc41ea0 100644 --- a/packages/client/src/pages/admin/emojis.vue +++ b/packages/client/src/pages/admin/emojis.vue @@ -175,10 +175,10 @@ const menu = (ev: MouseEvent) => { type: 'info', text: i18n.ts.exportRequested, }); - }).catch((e) => { + }).catch((err) => { os.alert({ type: 'error', - text: e.message, + text: err.message, }); }); } @@ -195,10 +195,10 @@ const menu = (ev: MouseEvent) => { type: 'info', text: i18n.ts.importRequested, }); - }).catch((e) => { + }).catch((err) => { os.alert({ type: 'error', - text: e.message, + text: err.message, }); }); } diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue index 210226f283..9b7fa5678e 100644 --- a/packages/client/src/pages/admin/index.vue +++ b/packages/client/src/pages/admin/index.vue @@ -265,10 +265,10 @@ const invite = () => { type: 'info', text: x.code }); - }).catch(e => { + }).catch(err => { os.alert({ type: 'error', - text: e + text: err, }); }); }; diff --git a/packages/client/src/pages/channel-editor.vue b/packages/client/src/pages/channel-editor.vue index 3818c7481a..ea3a5dab76 100644 --- a/packages/client/src/pages/channel-editor.vue +++ b/packages/client/src/pages/channel-editor.vue @@ -111,8 +111,8 @@ export default defineComponent({ } }, - setBannerImage(e) { - selectFile(e.currentTarget ?? e.target, null).then(file => { + setBannerImage(evt) { + selectFile(evt.currentTarget ?? evt.target, null).then(file => { this.bannerId = file.id; }); }, diff --git a/packages/client/src/pages/emojis.category.vue b/packages/client/src/pages/emojis.category.vue index 9a317418be..1be004cf51 100644 --- a/packages/client/src/pages/emojis.category.vue +++ b/packages/client/src/pages/emojis.category.vue @@ -79,9 +79,9 @@ export default defineComponent({ } if (this.selectedTags.size === 0) { - this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q)); + this.searchEmojis = this.customEmojis.filter(emoji => emoji.name.includes(this.q) || emoji.aliases.includes(this.q)); } else { - this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t))); + this.searchEmojis = this.customEmojis.filter(emoji => (emoji.name.includes(this.q) || emoji.aliases.includes(this.q)) && [...this.selectedTags].every(t => emoji.aliases.includes(t))); } }, diff --git a/packages/client/src/pages/emojis.vue b/packages/client/src/pages/emojis.vue index 886b5f7119..f44b29df04 100644 --- a/packages/client/src/pages/emojis.vue +++ b/packages/client/src/pages/emojis.vue @@ -25,10 +25,10 @@ function menu(ev) { type: 'info', text: i18n.ts.exportRequested, }); - }).catch((e) => { + }).catch((err) => { os.alert({ type: 'error', - text: e.message, + text: err.message, }); }); } diff --git a/packages/client/src/pages/follow.vue b/packages/client/src/pages/follow.vue index d8a6824dca..e69e0481e0 100644 --- a/packages/client/src/pages/follow.vue +++ b/packages/client/src/pages/follow.vue @@ -20,7 +20,7 @@ export default defineComponent({ uri: acct }); promise.then(res => { - if (res.type == 'User') { + if (res.type === 'User') { this.follow(res.object); } else if (res.type === 'Note') { this.$router.push(`/notes/${res.object.id}`); diff --git a/packages/client/src/pages/gallery/edit.vue b/packages/client/src/pages/gallery/edit.vue index 25ee513186..a0c2d1a596 100644 --- a/packages/client/src/pages/gallery/edit.vue +++ b/packages/client/src/pages/gallery/edit.vue @@ -91,8 +91,8 @@ export default defineComponent({ }, methods: { - selectFile(e) { - selectFiles(e.currentTarget ?? e.target, null).then(files => { + selectFile(evt) { + selectFiles(evt.currentTarget ?? evt.target, null).then(files => { this.files = this.files.concat(files); }); }, diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue index 1755c23286..1ca3443e56 100644 --- a/packages/client/src/pages/gallery/post.vue +++ b/packages/client/src/pages/gallery/post.vue @@ -119,8 +119,8 @@ export default defineComponent({ postId: this.postId }).then(post => { this.post = post; - }).catch(e => { - this.error = e; + }).catch(err => { + this.error = err; }); }, diff --git a/packages/client/src/pages/messaging/index.vue b/packages/client/src/pages/messaging/index.vue index 88a1e07afc..61c8bb0ce3 100644 --- a/packages/client/src/pages/messaging/index.vue +++ b/packages/client/src/pages/messaging/index.vue @@ -90,14 +90,14 @@ export default defineComponent({ getAcct: Acct.toString, isMe(message) { - return message.userId == this.$i.id; + return message.userId === this.$i.id; }, onMessage(message) { if (message.recipientId) { this.messages = this.messages.filter(m => !( - (m.recipientId == message.recipientId && m.userId == message.userId) || - (m.recipientId == message.userId && m.userId == message.recipientId))); + (m.recipientId === message.recipientId && m.userId === message.userId) || + (m.recipientId === message.userId && m.userId === message.recipientId))); this.messages.unshift(message); } else if (message.groupId) { @@ -108,7 +108,7 @@ export default defineComponent({ onRead(ids) { for (const id of ids) { - const found = this.messages.find(m => m.id == id); + const found = this.messages.find(m => m.id === id); if (found) { if (found.recipientId) { found.isRead = true; diff --git a/packages/client/src/pages/messaging/messaging-room.form.vue b/packages/client/src/pages/messaging/messaging-room.form.vue index 35cb75743f..ad8aaae6b7 100644 --- a/packages/client/src/pages/messaging/messaging-room.form.vue +++ b/packages/client/src/pages/messaging/messaging-room.form.vue @@ -59,7 +59,7 @@ export default defineComponent({ return this.user ? 'user:' + this.user.id : 'group:' + this.group.id; }, canSend(): boolean { - return (this.text != null && this.text != '') || this.file != null; + return (this.text != null && this.text !== '') || this.file != null; }, room(): any { return this.$parent; @@ -88,12 +88,11 @@ export default defineComponent({ } }, methods: { - async onPaste(e: ClipboardEvent) { - const data = e.clipboardData; - const items = data.items; + async onPaste(evt: ClipboardEvent) { + const items = evt.clipboardData.items; - if (items.length == 1) { - if (items[0].kind == 'file') { + if (items.length === 1) { + if (items[0].kind === 'file') { const file = items[0].getAsFile(); const lio = file.name.lastIndexOf('.'); const ext = lio >= 0 ? file.name.slice(lio) : ''; @@ -101,7 +100,7 @@ export default defineComponent({ if (formatted) this.upload(file, formatted); } } else { - if (items[0].kind == 'file') { + if (items[0].kind === 'file') { os.alert({ type: 'error', text: this.$ts.onlyOneFileCanBeAttached @@ -110,23 +109,23 @@ export default defineComponent({ } }, - onDragover(e) { - const isFile = e.dataTransfer.items[0].kind == 'file'; - const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_; + onDragover(evt) { + const isFile = evt.dataTransfer.items[0].kind === 'file'; + const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; if (isFile || isDriveFile) { - e.preventDefault(); - e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move'; + evt.preventDefault(); + evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move'; } }, - onDrop(e): void { + onDrop(evt): void { // ファイルだったら - if (e.dataTransfer.files.length == 1) { - e.preventDefault(); - this.upload(e.dataTransfer.files[0]); + if (evt.dataTransfer.files.length === 1) { + evt.preventDefault(); + this.upload(evt.dataTransfer.files[0]); return; - } else if (e.dataTransfer.files.length > 1) { - e.preventDefault(); + } else if (evt.dataTransfer.files.length > 1) { + evt.preventDefault(); os.alert({ type: 'error', text: this.$ts.onlyOneFileCanBeAttached @@ -135,17 +134,17 @@ export default defineComponent({ } //#region ドライブのファイル - const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile != '') { + const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); + if (driveFile != null && driveFile !== '') { this.file = JSON.parse(driveFile); - e.preventDefault(); + evt.preventDefault(); } //#endregion }, - onKeydown(e) { + onKeydown(evt) { this.typing(); - if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canSend) { + if ((evt.which === 10 || evt.which === 13) && (evt.ctrlKey || evt.metaKey) && this.canSend) { this.send(); } }, @@ -154,8 +153,8 @@ export default defineComponent({ this.typing(); }, - chooseFile(e) { - selectFile(e.currentTarget ?? e.target, this.$ts.selectFile).then(file => { + chooseFile(evt) { + selectFile(evt.currentTarget ?? evt.target, this.$ts.selectFile).then(file => { this.file = file; }); }, @@ -193,9 +192,9 @@ export default defineComponent({ }, saveDraft() { - const data = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); - data[this.draftKey] = { + drafts[this.draftKey] = { updatedAt: new Date(), data: { text: this.text, @@ -203,15 +202,15 @@ export default defineComponent({ } } - localStorage.setItem('message_drafts', JSON.stringify(data)); + localStorage.setItem('message_drafts', JSON.stringify(drafts)); }, deleteDraft() { - const data = JSON.parse(localStorage.getItem('message_drafts') || '{}'); + const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}'); - delete data[this.draftKey]; + delete drafts[this.draftKey]; - localStorage.setItem('message_drafts', JSON.stringify(data)); + localStorage.setItem('message_drafts', JSON.stringify(drafts)); }, async insertEmoji(ev) { diff --git a/packages/client/src/pages/messaging/messaging-room.vue b/packages/client/src/pages/messaging/messaging-room.vue index 2ecc68eb54..fd1962218a 100644 --- a/packages/client/src/pages/messaging/messaging-room.vue +++ b/packages/client/src/pages/messaging/messaging-room.vue @@ -166,23 +166,23 @@ const Component = defineComponent({ }); }, - onDragover(e) { - const isFile = e.dataTransfer.items[0].kind == 'file'; - const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_; + onDragover(evt) { + const isFile = evt.dataTransfer.items[0].kind === 'file'; + const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; if (isFile || isDriveFile) { - e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move'; + evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move'; } else { - e.dataTransfer.dropEffect = 'none'; + evt.dataTransfer.dropEffect = 'none'; } }, - onDrop(e): void { + onDrop(evt): void { // ファイルだったら - if (e.dataTransfer.files.length == 1) { - this.form.upload(e.dataTransfer.files[0]); + if (evt.dataTransfer.files.length === 1) { + this.form.upload(evt.dataTransfer.files[0]); return; - } else if (e.dataTransfer.files.length > 1) { + } else if (evt.dataTransfer.files.length > 1) { os.alert({ type: 'error', text: this.$ts.onlyOneFileCanBeAttached @@ -191,8 +191,8 @@ const Component = defineComponent({ } //#region ドライブのファイル - const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile != '') { + const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); + if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); this.form.file = file; } @@ -209,7 +209,7 @@ const Component = defineComponent({ limit: max + 1, untilId: this.existMoreMessages ? this.messages[0].id : undefined }).then(messages => { - if (messages.length == max + 1) { + if (messages.length === max + 1) { this.existMoreMessages = true; messages.pop(); } else { @@ -235,7 +235,7 @@ const Component = defineComponent({ const _isBottom = isBottom(this.$el, 64); this.messages.push(message); - if (message.userId != this.$i.id && !document.hidden) { + if (message.userId !== this.$i.id && !document.hidden) { this.connection.send('read', { id: message.id }); @@ -246,7 +246,7 @@ const Component = defineComponent({ this.$nextTick(() => { this.scrollToBottom(); }); - } else if (message.userId != this.$i.id) { + } else if (message.userId !== this.$i.id) { // Notify this.notifyNewMessage(); } @@ -256,7 +256,7 @@ const Component = defineComponent({ if (this.user) { if (!Array.isArray(x)) x = [x]; for (const id of x) { - if (this.messages.some(x => x.id == id)) { + if (this.messages.some(x => x.id === id)) { const exist = this.messages.map(x => x.id).indexOf(id); this.messages[exist] = { ...this.messages[exist], @@ -266,7 +266,7 @@ const Component = defineComponent({ } } else if (this.group) { for (const id of x.ids) { - if (this.messages.some(x => x.id == id)) { + if (this.messages.some(x => x.id === id)) { const exist = this.messages.map(x => x.id).indexOf(id); this.messages[exist] = { ...this.messages[exist], diff --git a/packages/client/src/pages/note.vue b/packages/client/src/pages/note.vue index 9e174ef495..f0a18ecc36 100644 --- a/packages/client/src/pages/note.vue +++ b/packages/client/src/pages/note.vue @@ -136,8 +136,8 @@ export default defineComponent({ this.hasPrev = prev.length !== 0; this.hasNext = next.length !== 0; }); - }).catch(e => { - this.error = e; + }).catch(err => { + this.error = err; }); } } diff --git a/packages/client/src/pages/page.vue b/packages/client/src/pages/page.vue index b2c039a269..5bca971438 100644 --- a/packages/client/src/pages/page.vue +++ b/packages/client/src/pages/page.vue @@ -139,8 +139,8 @@ export default defineComponent({ username: this.username, }).then(page => { this.page = page; - }).catch(e => { - this.error = e; + }).catch(err => { + this.error = err; }); }, diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue index a19d6378d7..be464f040d 100644 --- a/packages/client/src/pages/settings/2fa.vue +++ b/packages/client/src/pages/settings/2fa.vue @@ -121,10 +121,10 @@ function submit() { }).then(() => { os.success(); $i!.twoFactorEnabled = true; - }).catch(e => { + }).catch(err => { os.alert({ type: 'error', - text: e + text: err, }); }); } diff --git a/packages/client/src/pages/settings/plugin.install.vue b/packages/client/src/pages/settings/plugin.install.vue index 6ece531462..96c0abfd99 100644 --- a/packages/client/src/pages/settings/plugin.install.vue +++ b/packages/client/src/pages/settings/plugin.install.vue @@ -43,7 +43,7 @@ async function install() { let ast; try { ast = parse(code.value); - } catch (e) { + } catch (err) { os.alert({ type: 'error', text: 'Syntax error :(' @@ -60,8 +60,8 @@ async function install() { return; } - const data = meta.get(null); - if (data == null) { + const metadata = meta.get(null); + if (metadata == null) { os.alert({ type: 'error', text: 'No metadata found :(' @@ -69,7 +69,7 @@ async function install() { return; } - const { name, version, author, description, permissions, config } = data; + const { name, version, author, description, permissions, config } = metadata; if (name == null || version == null || author == null) { os.alert({ type: 'error', diff --git a/packages/client/src/pages/settings/theme.install.vue b/packages/client/src/pages/settings/theme.install.vue index 0ef098f318..25fa6c012b 100644 --- a/packages/client/src/pages/settings/theme.install.vue +++ b/packages/client/src/pages/settings/theme.install.vue @@ -29,7 +29,7 @@ function parseThemeCode(code: string) { try { theme = JSON5.parse(code); - } catch (e) { + } catch (err) { os.alert({ type: 'error', text: i18n.ts._theme.invalid diff --git a/packages/client/src/pages/settings/webhook.edit.vue b/packages/client/src/pages/settings/webhook.edit.vue index bb3a25407e..3690526b41 100644 --- a/packages/client/src/pages/settings/webhook.edit.vue +++ b/packages/client/src/pages/settings/webhook.edit.vue @@ -43,6 +43,14 @@ import * as os from '@/os'; import * as symbols from '@/symbols'; import { i18n } from '@/i18n'; +defineExpose({ + [symbols.PAGE_INFO]: { + title: 'Edit webhook', + icon: 'fas fa-bolt', + bg: 'var(--bg)', + }, +}); + const webhook = await os.api('i/webhooks/show', { webhookId: new URLSearchParams(window.location.search).get('id') }); @@ -78,12 +86,4 @@ async function save(): Promise { active, }); } - -defineExpose({ - [symbols.PAGE_INFO]: { - title: 'Edit webhook', - icon: 'fas fa-bolt', - bg: 'var(--bg)', - }, -}); diff --git a/packages/client/src/pages/settings/word-mute.vue b/packages/client/src/pages/settings/word-mute.vue index 97a15da5b5..48fcb362b9 100644 --- a/packages/client/src/pages/settings/word-mute.vue +++ b/packages/client/src/pages/settings/word-mute.vue @@ -71,7 +71,7 @@ watch(hardMutedWords, () => { async function save() { const parseMutes = (mutes, tab) => { // split into lines, remove empty lines and unnecessary whitespace - let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line != ''); + let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== ''); // check each line if it is a RegExp or not for (let i = 0; i < lines.length; i++) { diff --git a/packages/client/src/pages/share.vue b/packages/client/src/pages/share.vue index 4d77de5819..b08ac2b237 100644 --- a/packages/client/src/pages/share.vue +++ b/packages/client/src/pages/share.vue @@ -153,11 +153,11 @@ export default defineComponent({ ); } //#endregion - } catch (e) { + } catch (err) { os.alert({ type: 'error', - title: e.message, - text: e.name + title: err.message, + text: err.name }); } diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue index 5f9f1b9783..4250673d91 100644 --- a/packages/client/src/pages/theme-editor.vue +++ b/packages/client/src/pages/theme-editor.vue @@ -128,7 +128,7 @@ function showPreview() { } function setBgColor(color: typeof bgColors[number]) { - if (theme.base != color.kind) { + if (theme.base !== color.kind) { const base = color.kind === 'dark' ? darkTheme : lightTheme; for (const prop of Object.keys(base.props)) { if (prop === 'accent') continue; diff --git a/packages/client/src/pages/user-info.vue b/packages/client/src/pages/user-info.vue index 516ab4d440..1b2682ed29 100644 --- a/packages/client/src/pages/user-info.vue +++ b/packages/client/src/pages/user-info.vue @@ -232,10 +232,10 @@ export default defineComponent({ await os.api('admin/delete-all-files-of-a-user', { userId: this.user.id }); os.success(); }; - await process().catch(e => { + await process().catch(err => { os.alert({ type: 'error', - text: e.toString() + text: err.toString(), }); }); await this.refreshUser(); diff --git a/packages/client/src/pages/user/index.vue b/packages/client/src/pages/user/index.vue index 17e815892b..a024dd28bc 100644 --- a/packages/client/src/pages/user/index.vue +++ b/packages/client/src/pages/user/index.vue @@ -260,8 +260,8 @@ export default defineComponent({ this.user = null; os.api('users/show', Acct.parse(this.acct)).then(user => { this.user = user; - }).catch(e => { - this.error = e; + }).catch(err => { + this.error = err; }); }, diff --git a/packages/client/src/theme-store.ts b/packages/client/src/theme-store.ts index e7962e7e8e..fdc92ed793 100644 --- a/packages/client/src/theme-store.ts +++ b/packages/client/src/theme-store.ts @@ -14,9 +14,9 @@ export async function fetchThemes(): Promise { try { const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' }); localStorage.setItem(lsCacheKey, JSON.stringify(themes)); - } catch (e) { - if (e.code === 'NO_SUCH_KEY') return; - throw e; + } catch (err) { + if (err.code === 'NO_SUCH_KEY') return; + throw err; } } @@ -28,7 +28,7 @@ export async function addTheme(theme: Theme): Promise { } export async function removeTheme(theme: Theme): Promise { - const themes = getThemes().filter(t => t.id != theme.id); + const themes = getThemes().filter(t => t.id !== theme.id); await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes }); localStorage.setItem(lsCacheKey, JSON.stringify(themes)); } diff --git a/packages/client/src/ui/_common_/sw-inject.ts b/packages/client/src/ui/_common_/sw-inject.ts index e3e2ddd7e6..371f80ca15 100644 --- a/packages/client/src/ui/_common_/sw-inject.ts +++ b/packages/client/src/ui/_common_/sw-inject.ts @@ -14,30 +14,29 @@ export function swInject() { console.log('sw msg', ev.data); } - const data = ev.data; // as SwMessage - if (data.type !== 'order') return; + if (ev.data.type !== 'order') return; - if (data.loginId !== $i?.id) { - return getAccountFromId(data.loginId).then(account => { + if (ev.data.loginId !== $i?.id) { + return getAccountFromId(ev.data.loginId).then(account => { if (!account) return; - return login(account.token, data.url); + return login(account.token, ev.data.url); }); } - switch (data.order) { + switch (ev.data.order) { case 'post': - return post(data.options); + return post(ev.data.options); case 'push': - if (router.currentRoute.value.path === data.url) { + if (router.currentRoute.value.path === ev.data.url) { return window.scroll({ top: 0, behavior: 'smooth' }); } if (navHook) { - return navHook(data.url); + return navHook(ev.data.url); } - if (sideViewHook && defaultStore.state.defaultSideView && data.url !== '/') { - return sideViewHook(data.url); + if (sideViewHook && defaultStore.state.defaultSideView && ev.data.url !== '/') { + return sideViewHook(ev.data.url); } - return router.push(data.url); + return router.push(ev.data.url); default: return; } diff --git a/packages/client/src/ui/classic.widgets.vue b/packages/client/src/ui/classic.widgets.vue index f42f27e926..6f9d18bde5 100644 --- a/packages/client/src/ui/classic.widgets.vue +++ b/packages/client/src/ui/classic.widgets.vue @@ -44,13 +44,13 @@ export default defineComponent({ }, removeWidget(widget) { - this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id != widget.id)); + this.$store.set('widgets', this.$store.state.widgets.filter(w => w.id !== widget.id)); }, updateWidget({ id, data }) { this.$store.set('widgets', this.$store.state.widgets.map(w => w.id === id ? { ...w, - data: data + data, } : w)); }, diff --git a/packages/client/src/ui/deck/deck-store.ts b/packages/client/src/ui/deck/deck-store.ts index c2c9ae540b..c847bf2b43 100644 --- a/packages/client/src/ui/deck/deck-store.ts +++ b/packages/client/src/ui/deck/deck-store.ts @@ -276,14 +276,14 @@ export function setColumnWidgets(id: Column['id'], widgets: ColumnWidget[]) { saveDeck(); } -export function updateColumnWidget(id: Column['id'], widgetId: string, data: any) { +export function updateColumnWidget(id: Column['id'], widgetId: string, WidgetData: any) { const columns = copy(deckStore.state.columns); const columnIndex = deckStore.state.columns.findIndex(c => c.id === id); const column = copy(deckStore.state.columns[columnIndex]); if (column == null) return; column.widgets = column.widgets.map(w => w.id === widgetId ? { ...w, - data: data + data: widgetData, } : w); columns[columnIndex] = column; deckStore.set('columns', columns); diff --git a/packages/client/src/ui/universal.widgets.vue b/packages/client/src/ui/universal.widgets.vue index 2660e80368..a42c085690 100644 --- a/packages/client/src/ui/universal.widgets.vue +++ b/packages/client/src/ui/universal.widgets.vue @@ -14,7 +14,7 @@ import { i18n } from '@/i18n'; import { defaultStore } from '@/store'; const emit = defineEmits<{ - (e: 'mounted', el: Element): void; + (ev: 'mounted', el: Element): void; }>(); let editMode = $ref(false); @@ -32,13 +32,13 @@ function addWidget(widget) { } function removeWidget(widget) { - defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id != widget.id)); + defaultStore.set('widgets', defaultStore.state.widgets.filter(w => w.id !== widget.id)); } function updateWidget({ id, data }) { defaultStore.set('widgets', defaultStore.state.widgets.map(w => w.id === id ? { ...w, - data: data + data, } : w)); } -- cgit v1.2.3-freya From 708fba989adab95b3a4c381f6b351979c5973591 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sat, 28 May 2022 07:28:12 +0200 Subject: feat(tests): add e2e tests for widgets (#8735) * test(e2e): add baseline for widget tests * chore(repo): enable test running in branch * fix(e2e): set viewport for widget tests * fix(client): add widget identifier classes to widgets * test(e2e): add memo widget test * fix(tests): force select value * fix(tests): force button press for widget addition * fix(tests): invoke select value differently * fix(tests): adjust widget submit * fix(tests): don't explicitly navigate for widget test * fix(tests): click label to hide select popup * fix(tests): just click modal background * fix(tests): adjust modal background selector * fix(tests): click all modal backgrounds * feat(e2e): add test for adding timeline widget * fix(client): add more widget identifier classes * feat(tests): add method abstraction for test cases * fix(tests): force-click overlays * fix(tests): force widget button press * fix(tests): remove timeout from final widget check * feat(tests): add widget removal test case * fix(client): use mk instead of msky as class prefix * fix(tests): check widgets for existence rather than visibility * chore(meta): don't run tests for specific feature branch --- cypress/integration/widgets.js | 84 +++++++++++++++++++++++++++ packages/client/src/components/widgets.vue | 4 +- packages/client/src/ui/universal.widgets.vue | 2 +- packages/client/src/widgets/activity.vue | 2 +- packages/client/src/widgets/aichan.vue | 2 +- packages/client/src/widgets/aiscript.vue | 2 +- packages/client/src/widgets/clock.vue | 2 +- packages/client/src/widgets/federation.vue | 2 +- packages/client/src/widgets/memo.vue | 2 +- packages/client/src/widgets/notifications.vue | 2 +- packages/client/src/widgets/photos.vue | 2 +- packages/client/src/widgets/post-form.vue | 2 +- packages/client/src/widgets/rss.vue | 2 +- packages/client/src/widgets/slideshow.vue | 2 +- packages/client/src/widgets/timeline.vue | 2 +- packages/client/src/widgets/trends.vue | 2 +- 16 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 cypress/integration/widgets.js (limited to 'packages/client/src/ui') diff --git a/cypress/integration/widgets.js b/cypress/integration/widgets.js new file mode 100644 index 0000000000..d63ff274bd --- /dev/null +++ b/cypress/integration/widgets.js @@ -0,0 +1,84 @@ +describe('After user signed in', () => { + beforeEach(() => { + cy.window(win => { + win.indexedDB.deleteDatabase('keyval-store'); + }); + cy.viewport('macbook-16'); + cy.request('POST', '/api/reset-db').as('reset'); + cy.get('@reset').its('status').should('equal', 204); + cy.reload(true); + + // インスタンス初期セットアップ + cy.request('POST', '/api/admin/accounts/create', { + username: 'admin', + password: 'pass', + }).its('body').as('admin'); + + // ユーザー作成 + cy.request('POST', '/api/signup', { + username: 'alice', + password: 'alice1234', + }).its('body').as('alice'); + + cy.visit('/'); + + cy.intercept('POST', '/api/signin').as('signin'); + + cy.get('[data-cy-signin]').click(); + cy.get('[data-cy-signin-username] input').type('alice'); + cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); + + cy.wait('@signin').as('signedIn'); + }); + + afterEach(() => { + // テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。 + // waitを入れることでそれを防止できる + cy.wait(1000); + }); + + it('widget edit toggle is visible', () => { + cy.get('.mk-widget-edit').should('be.visible'); + }); + + it('widget select should be visible in edit mode', () => { + cy.get('.mk-widget-edit').click(); + cy.get('.mk-widget-select').should('be.visible'); + }); + + it('first widget should be removed', () => { + cy.get('.mk-widget-edit').click(); + cy.get('.customize-container:first-child .remove._button').click(); + cy.get('.customize-container').should('have.length', 2); + }); + + function buildWidgetTest(widgetName) { + it(`${widgetName} widget should get added`, () => { + cy.get('.mk-widget-edit').click(); + cy.get('.mk-widget-select select').select(widgetName, { force: true }); + cy.get('.bg._modalBg.transparent').click({ multiple: true, force: true }); + cy.get('.mk-widget-add').click({ force: true }); + cy.get(`.mkw-${widgetName}`).should('exist'); + }); + } + + buildWidgetTest('memo'); + buildWidgetTest('notifications'); + buildWidgetTest('timeline'); + buildWidgetTest('calendar'); + buildWidgetTest('rss'); + buildWidgetTest('trends'); + buildWidgetTest('clock'); + buildWidgetTest('activity'); + buildWidgetTest('photos'); + buildWidgetTest('digitalClock'); + buildWidgetTest('federation'); + buildWidgetTest('postForm'); + buildWidgetTest('slideshow'); + buildWidgetTest('serverMetric'); + buildWidgetTest('onlineUsers'); + buildWidgetTest('jobQueue'); + buildWidgetTest('button'); + buildWidgetTest('aiscript'); + buildWidgetTest('aichan'); +}); diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue index b6835795cb..74dd79f733 100644 --- a/packages/client/src/components/widgets.vue +++ b/packages/client/src/components/widgets.vue @@ -2,11 +2,11 @@
diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue index 631beceb72..7fb9f5894c 100644 --- a/packages/client/src/widgets/activity.vue +++ b/packages/client/src/widgets/activity.vue @@ -1,5 +1,5 @@