From 476dfb0f351c9b879de90fe3d1aef826ab03b82b Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 5 Mar 2022 01:22:57 +0900 Subject: [PATCH 01/25] =?UTF-8?q?fix(client):=20register=5Fnote=5Fview=5Fi?= =?UTF-8?q?nterruptor()=E3=81=8C=E5=8B=95=E3=81=8B=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #8318 --- CHANGELOG.md | 1 + packages/client/src/components/note.vue | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1d49f2e..dfb09b606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ You should also include the user name that made the change. - Client: リアクションピッカーの高さが低くなったまま戻らないことがあるのを修正 @syuilo - Client: ユーザー名オートコンプリートが正しく動作しない問題を修正 @syuilo - Client: タッチ操作だとウィジェットの編集がしにくいのを修正 @xianonn +- Client: register_note_view_interruptor()が動かないのを修正 @syuilo ## 12.107.0 (2022/02/12) diff --git a/packages/client/src/components/note.vue b/packages/client/src/components/note.vue index 93286a28b..3cd7a819d 100644 --- a/packages/client/src/components/note.vue +++ b/packages/client/src/components/note.vue @@ -138,7 +138,18 @@ const props = defineProps<{ const inChannel = inject('inChannel', null); -const note = $ref(JSON.parse(JSON.stringify(props.note))); +let note = $ref(JSON.parse(JSON.stringify(props.note))); + +// plugin +if (noteViewInterruptors.length > 0) { + onMounted(async () => { + let result = JSON.parse(JSON.stringify(note)); + for (const interruptor of noteViewInterruptors) { + result = await interruptor.handler(result); + } + note = result; + }); +} const isRenote = ( note.renote != null && @@ -152,7 +163,7 @@ const menuButton = ref(); const renoteButton = ref>(); const renoteTime = ref(); const reactButton = ref(); -let appearNote = $ref(isRenote ? note.renote as misskey.entities.Note : note); +let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note); const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); const collapsed = ref(appearNote.cw == null && appearNote.text != null && ( From 5431b5124902deb577bd3bc08e637ba5a15ccdd1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 5 Mar 2022 01:23:34 +0900 Subject: [PATCH 02/25] fix --- packages/client/src/components/note-detailed.vue | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/note-detailed.vue b/packages/client/src/components/note-detailed.vue index 80233d608..d30284ca5 100644 --- a/packages/client/src/components/note-detailed.vue +++ b/packages/client/src/components/note-detailed.vue @@ -154,7 +154,18 @@ const props = defineProps<{ const inChannel = inject('inChannel', null); -const note = $ref(JSON.parse(JSON.stringify(props.note))); +let note = $ref(JSON.parse(JSON.stringify(props.note))); + +// plugin +if (noteViewInterruptors.length > 0) { + onMounted(async () => { + let result = JSON.parse(JSON.stringify(note)); + for (const interruptor of noteViewInterruptors) { + result = await interruptor.handler(result); + } + note = result; + }); +} const isRenote = ( note.renote != null && @@ -168,7 +179,7 @@ const menuButton = ref(); const renoteButton = ref>(); const renoteTime = ref(); const reactButton = ref(); -let appearNote = $ref(isRenote ? note.renote as misskey.entities.Note : note); +let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note); const isMyRenote = $i && ($i.id === note.userId); const showContent = ref(false); const isDeleted = ref(false); From 446e4ce0c3193fe6ead9ec2969424816a2c33a28 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sat, 5 Mar 2022 02:00:45 +0900 Subject: [PATCH 03/25] =?UTF-8?q?fix:=20iPhone=20X=E4=BB=A5=E9=99=8D(=3F)?= =?UTF-8?q?=E3=81=A7=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E3=81=8C=E5=85=A8=E3=81=A6=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=8D?= =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20(#8375)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add safe-area-inset-bottom to spacer * fix * :v: * fix --- packages/client/src/components/notification-toast.vue | 2 +- packages/client/src/ui/universal.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/notification-toast.vue b/packages/client/src/components/notification-toast.vue index 4c5783e52..b808647bb 100644 --- a/packages/client/src/components/notification-toast.vue +++ b/packages/client/src/components/notification-toast.vue @@ -53,7 +53,7 @@ onMounted(() => { } @media (max-width: 500px) { - bottom: 92px; + bottom: calc(env(safe-area-inset-bottom, 0px) + 92px); padding: 0 8px; } diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue index 000fec9b4..a5ff7a625 100644 --- a/packages/client/src/ui/universal.vue +++ b/packages/client/src/ui/universal.vue @@ -265,7 +265,7 @@ const wallpaper = localStorage.getItem('wallpaper') != null; min-width: 0; > .spacer { - height: 82px; + height: calc(env(safe-area-inset-bottom, 0px) + 96px); @media (min-width: ($widgets-hide-threshold + 1px)) { display: none; From 8bb586c1fde8e90468d1ed22e9fcee2488248ac3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 5 Mar 2022 02:01:38 +0900 Subject: [PATCH 04/25] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfb09b606..d647b98f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ You should also include the user name that made the change. - Client: ユーザー名オートコンプリートが正しく動作しない問題を修正 @syuilo - Client: タッチ操作だとウィジェットの編集がしにくいのを修正 @xianonn - Client: register_note_view_interruptor()が動かないのを修正 @syuilo +- Client: iPhone X以降(?)でページの内容が全て表示しきれないのを修正 @tamaina ## 12.107.0 (2022/02/12) From 2442592ef1a51cad901cf5e7fe30cdc85d7568cc Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 6 Mar 2022 16:06:27 +0900 Subject: [PATCH 05/25] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=B3=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=83=88=E7=B5=82=E4=BA=86=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #4664 --- CHANGELOG.md | 1 + locales/ja-JP.yml | 2 ++ .../1646549089451-poll-ended-notification.js | 18 ++++++++++ .../src/models/entities/notification.ts | 3 +- .../src/models/repositories/notification.ts | 6 ++++ packages/backend/src/queue/index.ts | 4 ++- .../processors/ended-poll-notification.ts | 33 +++++++++++++++++++ packages/backend/src/queue/queues.ts | 3 +- packages/backend/src/queue/types.ts | 5 +++ packages/backend/src/services/note/create.ts | 10 ++++++ packages/backend/src/types.ts | 2 +- .../client/src/components/notification.vue | 19 +++++++++-- .../client/src/components/notifications.vue | 7 ++-- .../client/src/sw/compose-notification.ts | 5 +++ 14 files changed, 108 insertions(+), 10 deletions(-) create mode 100644 packages/backend/migration/1646549089451-poll-ended-notification.js create mode 100644 packages/backend/src/queue/processors/ended-poll-notification.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d647b98f2..03bad049b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ You should also include the user name that made the change. ### Improvements - インスタンスデフォルトテーマを設定できるように @syuilo - ミュートに期限を設定できるように @syuilo +- アンケートが終了したときに通知が作成されるように @syuilo - プロフィールの追加情報を最大16まで保存できるように @syuilo - 連合チャートにPub&Subを追加 @syuilo - デフォルトで10秒以上時間がかかるデータベースへのクエリは中断されるように @syuilo diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8f48c2940..13f9efe9b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1667,6 +1667,7 @@ _notification: youReceivedFollowRequest: "フォローリクエストが来ました" yourFollowRequestAccepted: "フォローリクエストが承認されました" youWereInvitedToGroup: "グループに招待されました" + pollEnded: "アンケートの結果が出ました" _types: all: "すべて" @@ -1677,6 +1678,7 @@ _notification: quote: "引用" reaction: "リアクション" pollVote: "アンケートに投票された" + pollEnded: "アンケートが終了" receiveFollowRequest: "フォロー申請を受け取った" followRequestAccepted: "フォローが受理された" groupInvited: "グループに招待された" diff --git a/packages/backend/migration/1646549089451-poll-ended-notification.js b/packages/backend/migration/1646549089451-poll-ended-notification.js new file mode 100644 index 000000000..38a38ce64 --- /dev/null +++ b/packages/backend/migration/1646549089451-poll-ended-notification.js @@ -0,0 +1,18 @@ + +export class pollEndedNotification1646549089451 { + name = 'pollEndedNotification1646549089451' + + async up(queryRunner) { + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); + } + + async down(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); + } +} diff --git a/packages/backend/src/models/entities/notification.ts b/packages/backend/src/models/entities/notification.ts index 4a4739b8c..db3dba363 100644 --- a/packages/backend/src/models/entities/notification.ts +++ b/packages/backend/src/models/entities/notification.ts @@ -59,7 +59,8 @@ export class Notification { * renote - (自分または自分がWatchしている)投稿がRenoteされた * quote - (自分または自分がWatchしている)投稿が引用Renoteされた * reaction - (自分または自分がWatchしている)投稿にリアクションされた - * pollVote - (自分または自分がWatchしている)投稿の投票に投票された + * pollVote - (自分または自分がWatchしている)投稿のアンケートに投票された + * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した * receiveFollowRequest - フォローリクエストされた * followRequestAccepted - 自分の送ったフォローリクエストが承認された * groupInvited - グループに招待された diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/models/repositories/notification.ts index 441bb7926..8e72d8aab 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/models/repositories/notification.ts @@ -67,6 +67,12 @@ export class NotificationRepository extends Repository { }), choice: notification.choice, } : {}), + ...(notification.type === 'pollEnded' ? { + note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + detail: true, + _hint_: options._hintForEachNotes_, + }), + } : {}), ...(notification.type === 'groupInvited' ? { invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!), } : {}), diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 1b1c21c4f..50bcccbb7 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -8,10 +8,11 @@ import processInbox from './processors/inbox.js'; import processDb from './processors/db/index.js'; import processObjectStorage from './processors/object-storage/index.js'; import processSystemQueue from './processors/system/index.js'; +import { endedPollNotification } from './processors/ended-poll-notification.js'; import { queueLogger } from './logger.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { getJobInfo } from './get-job-info.js'; -import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues.js'; +import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue } from './queues.js'; import { ThinUser } from './types.js'; import { IActivity } from '@/remote/activitypub/type.js'; @@ -255,6 +256,7 @@ export default function() { deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); + endedPollNotificationQueue.process(endedPollNotification); processDb(dbQueue); processObjectStorage(objectStorageQueue); diff --git a/packages/backend/src/queue/processors/ended-poll-notification.ts b/packages/backend/src/queue/processors/ended-poll-notification.ts new file mode 100644 index 000000000..afac27921 --- /dev/null +++ b/packages/backend/src/queue/processors/ended-poll-notification.ts @@ -0,0 +1,33 @@ +import Bull from 'bull'; +import { In } from 'typeorm'; +import { Notes, Polls, PollVotes } from '@/models/index.js'; +import { queueLogger } from '../logger.js'; +import { EndedPollNotificationJobData } from '@/queue/types.js'; +import { createNotification } from '@/services/create-notification.js'; + +const logger = queueLogger.createSubLogger('ended-poll-notification'); + +export async function endedPollNotification(job: Bull.Job, done: any): Promise { + const note = await Notes.findOne(job.data.noteId); + if (note == null || !note.hasPoll) { + done(); + return; + } + + const votes = await PollVotes.createQueryBuilder('vote') + .select('vote.userId') + .where('vote.noteId = :noteId', { noteId: note.id }) + .innerJoinAndSelect('vote.user', 'user') + .andWhere('user.host IS NULL') + .getMany(); + + const userIds = [...new Set([note.userId, ...votes.map(v => v.userId)])]; + + for (const userId of userIds) { + createNotification(userId, 'pollEnded', { + noteId: note.id, + }); + } + + done(); +} diff --git a/packages/backend/src/queue/queues.ts b/packages/backend/src/queue/queues.ts index 6ac4ec69c..02df58736 100644 --- a/packages/backend/src/queue/queues.ts +++ b/packages/backend/src/queue/queues.ts @@ -1,8 +1,9 @@ import config from '@/config/index.js'; import { initialize as initializeQueue } from './initialize.js'; -import { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData } from './types.js'; +import { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData } from './types.js'; export const systemQueue = initializeQueue>('system'); +export const endedPollNotificationQueue = initializeQueue('endedPollNotification'); export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); export const dbQueue = initializeQueue('db'); diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index de5f5d139..5191caea4 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -1,4 +1,5 @@ import { DriveFile } from '@/models/entities/drive-file.js'; +import { Note } from '@/models/entities/note'; import { User } from '@/models/entities/user.js'; import { IActivity } from '@/remote/activitypub/type.js'; import httpSignature from 'http-signature'; @@ -41,6 +42,10 @@ export type ObjectStorageFileJobData = { key: string; }; +export type EndedPollNotificationJobData = { + noteId: Note['id']; +}; + export type ThinUser = { id: User['id']; }; diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index ed242a0b5..8c5f13362 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -34,6 +34,7 @@ import { deliverToRelays } from '../relay.js'; import { Channel } from '@/models/entities/channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { getAntennas } from '@/misc/antenna-cache.js'; +import { endedPollNotificationQueue } from '@/queue/queues.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -296,6 +297,15 @@ export default async (user: { id: User['id']; username: User['username']; host: incRenoteCount(data.renote); } + if (data.poll && data.poll.expiresAt) { + const delay = data.poll.expiresAt.getTime() - Date.now(); + endedPollNotificationQueue.add({ + noteId: note.id, + }, { + delay + }); + } + if (!silent) { if (Users.isLocalUser(user)) activeUsersChart.write(user); diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 20f6f8bb8..573e2faf8 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue index d855f81f8..1a360f990 100644 --- a/packages/client/src/components/notification.vue +++ b/packages/client/src/components/notification.vue @@ -1,7 +1,8 @@