From 3311bd866b23e03354b3e92fc55d634399be8d02 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 20 May 2022 09:28:57 +0200 Subject: [PATCH 1/9] add move notification type --- locales/en-US.yml | 2 + .../src/models/entities/notification.ts | 40 +++++++++++++------ .../src/models/repositories/notification.ts | 3 ++ packages/foundkey-js/src/consts.ts | 2 +- packages/foundkey-js/src/entities.ts | 5 +++ 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 887d7d287..9b53f3fc0 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1293,6 +1293,7 @@ _notification: yourFollowRequestAccepted: "Your follow request was accepted" youWereInvitedToGroup: "{userName} invited you to a group" pollEnded: "Poll results have become available" + moved: "{name} has moved to a different account" emptyPushNotificationMessage: "Push notifications have been updated" _types: follow: "New followers" @@ -1306,6 +1307,7 @@ _notification: receiveFollowRequest: "Received follow requests" followRequestAccepted: "Accepted follow requests" groupInvited: "Group invitations" + move: "Others moving accounts" app: "Notifications from linked apps" _actions: followBack: "followed you back" diff --git a/packages/backend/src/models/entities/notification.ts b/packages/backend/src/models/entities/notification.ts index cab6551f7..a0d6cbde4 100644 --- a/packages/backend/src/models/entities/notification.ts +++ b/packages/backend/src/models/entities/notification.ts @@ -52,19 +52,20 @@ export class Notification { public notifier: User | null; /** - * 通知の種類。 - * follow - フォローされた - * mention - 投稿で自分が言及された - * reply - (自分または自分がWatchしている)投稿が返信された - * renote - (自分または自分がWatchしている)投稿がRenoteされた - * quote - (自分または自分がWatchしている)投稿が引用Renoteされた - * reaction - (自分または自分がWatchしている)投稿にリアクションされた - * pollVote - (自分または自分がWatchしている)投稿のアンケートに投票された - * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した - * receiveFollowRequest - フォローリクエストされた - * followRequestAccepted - 自分の送ったフォローリクエストが承認された - * groupInvited - グループに招待された - * app - アプリ通知 + * Type of notification. + * follow - notifier followed notifiee + * mention - notifiee was mentioned + * reply - notifiee (author or watching) was replied to + * renote - notifiee (author or watching) was renoted + * quote - notifiee (author or watching) was quoted + * reaction - notifiee (author or watching) had a reaction added to the note + * pollVote - new vote in a poll notifiee authored or watched + * pollEnded - notifiee's poll ended + * receiveFollowRequest - notifiee received a new follow request + * followRequestAccepted - notifier accepted notifees follow request + * groupInvited - notifiee was invited into a group + * move - notifier moved + * app - custom application notification */ @Index() @Column('enum', { @@ -129,6 +130,19 @@ export class Notification { }) public choice: number | null; + @Column({ + ...id(), + nullable: true, + comment: 'The ID of the moved to account.', + }) + public moveTargetId: User['id'] | null; + + @ManyToOne(() => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public moveTarget: User | null; + /** * アプリ通知のbody */ diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/models/repositories/notification.ts index c62a24959..c1e2273be 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/models/repositories/notification.ts @@ -44,6 +44,9 @@ export const NotificationRepository = db.getRepository(Notification).extend({ ...(notification.type === 'groupInvited' ? { invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!), } : {}), + ...(notification.type === 'move' ? { + moveTarget: Users.pack(notification.moveTarget ?? notification.moveTargetId), + } : {}), ...(notification.type === 'app' ? { body: notification.customBody, header: notification.customHeader || token?.name, diff --git a/packages/foundkey-js/src/consts.ts b/packages/foundkey-js/src/consts.ts index a90b8720a..a7ea18d25 100644 --- a/packages/foundkey-js/src/consts.ts +++ b/packages/foundkey-js/src/consts.ts @@ -1,4 +1,4 @@ -export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'move', 'app'] as const; export const noteNotificationTypes = ['mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded'] as const; diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index 30dd90582..c8a91fba9 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -223,6 +223,11 @@ export type Notification = { invitation: UserGroup; user: User; userId: User['id']; +} | { + type: 'move', + user: User; + userId: User['id']; + moveTarget: User; } | { type: 'app'; header?: string | null; From 3c2092935c9d12adbd4a75b4e78eb2c9857c4028 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Fri, 20 May 2022 10:36:14 +0200 Subject: [PATCH 2/9] server: add object hint to resolvePerson --- packages/backend/src/remote/activitypub/models/person.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 803fd03c9..ba677a9dc 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -376,7 +376,7 @@ export async function updatePerson(value: IObject | string, resolver: Resolver): * If the target Person is registered in FoundKey, return it; otherwise, fetch it from a remote server and return it. * Fetch the person from the remote server, register it in FoundKey, and return it. */ -export async function resolvePerson(uri: string, resolver: Resolver): Promise { +export async function resolvePerson(uri: string, resolver: Resolver, hint?: IObject): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); //#region このサーバーに既に登録されていたらそれを返す @@ -388,7 +388,7 @@ export async function resolvePerson(uri: string, resolver: Resolver): Promise Date: Fri, 20 May 2022 10:38:29 +0200 Subject: [PATCH 3/9] server: implement receiving Move activities For now only creates notifications. --- packages/backend/src/models/entities/user.ts | 14 ++++- .../src/remote/activitypub/kernel/index.ts | 5 +- .../remote/activitypub/kernel/move/index.ts | 62 +++++++++++++++++++ .../backend/src/remote/activitypub/type.ts | 5 ++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/remote/activitypub/kernel/move/index.ts diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 0aee4c90b..6cfca187a 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -1,4 +1,4 @@ -import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { Entity, Column, Index, OneToOne, ManyToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { id } from '../id.js'; import { DriveFile } from './drive-file.js'; @@ -230,6 +230,18 @@ export class User { }) public federateBlocks: boolean; + @Column({ + ...id(), + nullable: true, + }) + public movedToId: User['id'] | null; + + @ManyToOne(() => User, { + onDelete: 'SET NULL' + }) + @JoinColumn() + public movedTo: User | null; + constructor(data: Partial) { if (data == null) return; diff --git a/packages/backend/src/remote/activitypub/kernel/index.ts b/packages/backend/src/remote/activitypub/kernel/index.ts index 79e2dce75..46a972a7e 100644 --- a/packages/backend/src/remote/activitypub/kernel/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/index.ts @@ -4,7 +4,7 @@ import { Resolver } from '@/remote/activitypub/resolver.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { shouldBlockInstance } from '@/misc/should-block-instance.js'; import { apLogger } from '../logger.js'; -import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag, getApId } from '../type.js'; +import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag, isMove, getApId } from '../type.js'; import create from './create/index.js'; import performDeleteActivity from './delete/index.js'; import performUpdateActivity from './update/index.js'; @@ -19,6 +19,7 @@ import add from './add/index.js'; import remove from './remove/index.js'; import block from './block/index.js'; import flag from './flag/index.js'; +import { move } from './move/index.js'; export async function performActivity(actor: CacheableRemoteUser, activity: IObject, resolver: Resolver): Promise { if (isCollectionOrOrderedCollection(activity)) { @@ -73,6 +74,8 @@ async function performOneActivity(actor: CacheableRemoteUser, activity: IObject, await block(actor, activity); } else if (isFlag(activity)) { await flag(actor, activity); + } else if (isMove(activity)) { + await move(actor, activity, resolver); } else { apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); } diff --git a/packages/backend/src/remote/activitypub/kernel/move/index.ts b/packages/backend/src/remote/activitypub/kernel/move/index.ts new file mode 100644 index 000000000..e64656e09 --- /dev/null +++ b/packages/backend/src/remote/activitypub/kernel/move/index.ts @@ -0,0 +1,62 @@ +import { IsNull } from 'typeorm'; +import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { resolvePerson } from '@/remote/activitypub/models/person.js'; +import { Followings, Users } from '@/models/index.js'; +import { createNotification } from '@/services/create-notification.js'; +import Resolver from '../../resolver.js'; +import { IMove, isActor, getApId } from '../../type.js'; + +export async function move(actor: CacheableRemoteUser, activity: IMove, resolver: Resolver): Promise { + // actor is not move origin + if (activity.object == null || getApId(activity.object) !== actor.uri) return; + + // actor already moved + if (actor.movedTo != null) return; + + // no move target + if (activity.target == null) return; + + /* the database resolver can not be used here, because: + * 1. It must be ensured that the latest data is used. + * 2. The AP representation is needed, because `alsoKnownAs` + * is not stored in the database. + * This also checks whether the move target is blocked + */ + const movedToAp = await resolver.resolve(getApId(activity.target)); + + // move target is not an actor + if (!isActor(movedToAp)) return; + + // move destination has not accepted + if (!Array.isArray(movedToAp.alsoKnownAs) || !movedToAp.alsoKnownAs.includes(actor.id)) return; + + // ensure the user exists + const movedTo = await resolvePerson(getApId(activity.target), resolver, movedToAp); + // move target is already suspended + if (movedTo.isSuspended) return; + + // process move for local followers + const followings = Followings.find({ + select: { + followerId: true, + }, + where: { + followeeId: actor.id, + followerHost: IsNull(), + }, + }); + + await Promise.all([ + Users.update(actor.id, { + movedToId: movedTo.id, + }), + ...followings.map(async (following) => { + // TODO: autoAcceptMove? + + await createNotification(following.followerId, 'move', { + notifierId: actor.id, + moveTargetId: movedTo.id, + }); + }), + ]); +} diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts index 6298d44d8..8a46ac594 100644 --- a/packages/backend/src/remote/activitypub/type.ts +++ b/packages/backend/src/remote/activitypub/type.ts @@ -296,6 +296,10 @@ export interface IFlag extends IActivity { type: 'Flag'; } +export interface IMove extends IActivity { + type: 'Move'; +} + export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create'; export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete'; export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update'; @@ -310,6 +314,7 @@ export const isLike = (object: IObject): object is ILike => getApType(object) == export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce'; export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block'; export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag'; +export const isMove = (object: IObject): object is IMove => getApType(object) === 'Move'; export interface ILink { href: string; From 5ad18c8626681ec5983c52d1bfe3a1cdb9bc9d75 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 20 Nov 2022 22:05:16 +0100 Subject: [PATCH 4/9] server: add migration for movedTo user/notif --- .../migration/1668977715500-movedTo.js | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/backend/migration/1668977715500-movedTo.js diff --git a/packages/backend/migration/1668977715500-movedTo.js b/packages/backend/migration/1668977715500-movedTo.js new file mode 100644 index 000000000..721e01743 --- /dev/null +++ b/packages/backend/migration/1668977715500-movedTo.js @@ -0,0 +1,35 @@ +export class movedTo1668977715500 { + name = 'movedTo1668977715500'; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "movedToId" character varying(32)`); + await queryRunner.query(`ALTER TABLE "notification" ADD "moveTargetId" character varying(32)`); + await queryRunner.query(`COMMENT ON COLUMN "notification"."moveTargetId" IS 'The ID of the moved to account.'`); + await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_16fef167e4253ccdc8971b01f6e" FOREIGN KEY ("movedToId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_078db271ad52ccc345b7b2b026a" FOREIGN KEY ("moveTargetId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TYPE "notification_type_enum" ADD VALUE 'move'`); + await queryRunner.query(`ALTER TYPE "user_profile_mutingnotificationtypes_enum" ADD VALUE 'move'`); + } + + async down(queryRunner) { + // remove 'move' from user muting notifications type enum + await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'pollEnded')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`); + await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`); + + // remove 'move' from notification type enum + await queryRunner.query(`DELETE FROM "notification" WHERE "type" = 'move'`); + await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" 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_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"`); + + await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_078db271ad52ccc345b7b2b026a"`); + await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_16fef167e4253ccdc8971b01f6e"`); + await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "moveTargetId"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedToId"`); + } +} From 72b8489ae759368d0624fa50f39babeb13afdae0 Mon Sep 17 00:00:00 2001 From: Johann150 Date: Sun, 20 Nov 2022 20:06:03 +0100 Subject: [PATCH 5/9] client: display move notification --- .../notification-badges/suitcase-solid.png | Bin 0 -> 1123 bytes packages/client/src/components/notification.vue | 5 +++++ packages/sw/src/scripts/create-notification.ts | 14 ++++++++++++++ packages/sw/src/sw.ts | 3 +++ 4 files changed, 22 insertions(+) create mode 100644 packages/backend/assets/notification-badges/suitcase-solid.png diff --git a/packages/backend/assets/notification-badges/suitcase-solid.png b/packages/backend/assets/notification-badges/suitcase-solid.png new file mode 100644 index 0000000000000000000000000000000000000000..b52688586cc00f1d224b397871906acb291f0c1b GIT binary patch literal 1123 zcmeAS@N?(olHy`uVBq!ia0vp^DIm~}U&3=GU4JY5_^D(1Ys>)$b@ROa}{ z@86fV?u=TOn*Gvo&yprF&j(Arr>JfXx~OQ=#^R{bv7(f7VRvkk!v`tFQjO%r&FrU- z2LCtP$ahs$D<;^mDQr#X$JdEv?&Wi5);_uU^Y6abHE{;*b1T#8?$0zgw*G9>e*Dhd zpffp=AD9d{Y*^MXh^5IrRx;rF!&Spq^X%Po(B4ZxTcV{jq8V$#EESgw&@MN@BX>3V+~%{dS*@k zvGgx7os1h~B#bua{J*}5mGK+HYvXn1(Q<5MPos2ByI(u9edYZI*Vy;ug1`MlO?PC^P{>aS@87-u@|TM4jF}gIEWCBHOJ?(vUngFz-f(v1I{%CJ=8JAy`AH`0^v|u~ zS^*OhL{ubLIo%wa0w*MB{pT~>?0UfKLEVFMVzd7|ey41j7MY&re{Dy-?CV!|Zk{n? z`m39_i)9Y8-_GByUi@ZA`_-n$Kf39T7+lcn#nzbSYVS2Lj?r-G!{y+86);kwI zn&xjlko7?M!7E*!H40bt!W*3rtP1Ndz5HLmxSH(;V};~~m)pH_*>afcOg68*oIgkC z$eC@-@eJRe%v`%%myK`sO>OhIn>UdSM%iYrsf>%e!1xy9}$-~8aSn7F6# z+`QPO%LVV1l>0Nhv*W3WD4RF$TjTC}&ft29eOxucY4hfpG3_z?>2BPy`@jEVAI{_4 zJXcq@H7>vVcmGN4WpAtszkfaueBk$iscRO0ou+-D`aoLw|KG=?^KMOl`fmE2TJ{Z$ zKc*bJsg+vR!#tni_1)U#yW{8WbG#kAe%J5w2fWkD%r~%B@I>(OY}tAGBww@4TANRI zOq_oEPuYAx@ + {{ i18n.ts.reject }} + + {{ i18n.ts.moved }} +
+
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 9d5e31335..873840943 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -217,6 +217,20 @@ async function composeNotification(data ], }]; + case 'move': + return [t('_notification.moved', { name: getUserName(data.body.user) }), { + body: getUserName(data.body.moveTarget), + icon: data.body.moveTarget.avatarUrl, + badge: iconUrl('suitcase'), + data, + action: [ + { + action: 'accept', + title: t('follow'), + }, + ], + }]; + case 'app': return [data.body.header || data.body.body, { body: data.body.header && data.body.body, diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index 10cfc69d3..67846cea6 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -96,6 +96,9 @@ self.addEventListener('notificationclick', Date: Sun, 20 Nov 2022 21:21:12 +0100 Subject: [PATCH 6/9] server: implement moveTo property on actors Co-authored-by: Mary Strodl Co-authored-by: amybones --- .../src/remote/activitypub/models/person.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index ba677a9dc..17d9858e4 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -39,7 +39,7 @@ const summaryLength = 2048; * @param x Fetched object * @param uri Fetch target URI */ -function validateActor(x: IObject): IActor { +async function validateActor(x: IObject, resolver: Resolver): Promise { if (x == null) { throw new Error('invalid Actor: object is null'); } @@ -61,6 +61,22 @@ function validateActor(x: IObject): IActor { throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user'); } + if (x.movedTo !== undefined) { + if (!(typeof x.movedTo === 'string' && x.movedTo.length > 0)) { + throw new Error('invalid Actor: wrong movedTo'); + } + if (x.movedTo === uri) { + throw new Error('invalid Actor: moved to self'); + } + // This may throw an exception if we cannot resolve the move target. + // If we are processing an incoming activity, this is desired behaviour + // because that will cause the activity to be retried. + await resolvePerson(x.movedTo, resolver) + .then(moveTarget => { + x.movedTo = moveTarget.id + }); + } + if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) { throw new Error('invalid Actor: wrong inbox'); } @@ -137,7 +153,7 @@ export async function fetchPerson(uri: string): Promise { export async function createPerson(value: string | IObject, resolver: Resolver): Promise { const object = await resolver.resolve(value) as any; - const person = validateActor(object); + const person = await validateActor(object, resolver); apLogger.info(`Creating the Person: ${person.id}`); @@ -177,6 +193,7 @@ export async function createPerson(value: string | IObject, resolver: Resolver): isBot, isCat: (person as any).isCat === true, showTimelineReplies: false, + movedToId: person.movedTo, })) as IRemoteUser; await transactionalEntityManager.save(new UserProfile({ @@ -287,7 +304,7 @@ export async function updatePerson(value: IObject | string, resolver: Resolver): const object = await resolver.resolve(value); - const person = validateActor(object); + const person = await validateActor(object, resolver); apLogger.info(`Updating the Person: ${person.id}`); @@ -328,6 +345,7 @@ export async function updatePerson(value: IObject | string, resolver: Resolver): isCat: (person as any).isCat === true, isLocked: !!person.manuallyApprovesFollowers, isExplorable: !!person.discoverable, + movedToId: person.movedTo, } as Partial; if (avatar) { From c1f7ad0c1424d6509b557b4b624c74ce4d0f841b Mon Sep 17 00:00:00 2001 From: Johann150 Date: Tue, 22 Nov 2022 15:55:59 +0100 Subject: [PATCH 7/9] server: add movedTo to packed user --- packages/backend/src/models/repositories/user.ts | 4 ++++ packages/foundkey-js/src/entities.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 060bc0cd6..134a4dcbd 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -300,6 +300,10 @@ export const UserRepository = db.getRepository(User).extend({ }), emojis: populateEmojis(user.emojis, user.host), onlineStatus: this.getOnlineStatus(user), + movedTo: !user.movedToId ? undefined : this.pack(user.movedTo ?? user.movedToId, me, { + ...opts, + detail: false, + }), ...(opts.detail ? { url: profile!.url, diff --git a/packages/foundkey-js/src/entities.ts b/packages/foundkey-js/src/entities.ts index c8a91fba9..3742630bb 100644 --- a/packages/foundkey-js/src/entities.ts +++ b/packages/foundkey-js/src/entities.ts @@ -28,6 +28,7 @@ export type UserLite = { faviconUrl: Instance['faviconUrl']; themeColor: Instance['themeColor']; }; + movedTo?: UserLite; }; export type UserDetailed = UserLite & { From aa428bd1a475d8156272c230ce1b0c212040a01f Mon Sep 17 00:00:00 2001 From: Johann150 Date: Tue, 22 Nov 2022 16:17:06 +0100 Subject: [PATCH 8/9] client: display moved information on profile --- locales/en-US.yml | 1 + packages/client/src/pages/user/home.vue | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/locales/en-US.yml b/locales/en-US.yml index 9b53f3fc0..8a0c4e707 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -829,6 +829,7 @@ oauthErrorGoBack: "An error happened while trying to authenticate a 3rd party ap \ Please go back and try again." appAuthorization: "App authorization" noPermissionsRequested: "(No permissions requested.)" +movedTo: "This user has moved to {handle}." _emailUnavailable: used: "This email address is already being used" format: "The format of this email address is invalid" diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue index 2fcee6987..9236d1905 100644 --- a/packages/client/src/pages/user/home.vue +++ b/packages/client/src/pages/user/home.vue @@ -9,6 +9,16 @@
+ + + + + +
- {{ i18n.ts.moved }} + + +