From 89d77c7427a5c2efbe068243e919101abdd511ae Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Tue, 28 Oct 2025 10:31:14 +0700 Subject: [PATCH 01/15] refactor: rename userName to proposedUsername in group metadata --- .../agent/ipexCommunicationFixtures.ts | 2 +- .../__fixtures__/agent/multiSigFixtures.ts | 4 +- .../agent/records/identifierMetadataRecord.ts | 2 +- src/core/agent/services/identifier.types.ts | 4 +- .../agent/services/identifierService.test.ts | 84 ++++++++++------ src/core/agent/services/identifierService.ts | 99 +++++++++++++------ .../services/ipexCommunicationService.test.ts | 6 +- .../services/keriaNotificationService.test.ts | 14 +-- .../agent/services/multiSigService.test.ts | 56 ++++------- src/core/agent/services/multiSigService.ts | 11 ++- .../v1.2.0.3-group-scoped-username.ts | 2 +- src/core/utils/habName.test.ts | 22 ++--- src/core/utils/habName.ts | 22 +++-- .../reducers/profileCache/profilesCache.ts | 7 +- src/ui/__fixtures__/filteredIdentifierFix.ts | 16 ++- src/ui/components/EditProfile/EditProfile.tsx | 48 ++++++--- .../IdentifierSelectorModal.tsx | 13 ++- .../IdentifierAttributeDetailModal.tsx | 2 +- .../components/ProfileContent.tsx | 5 +- .../ProfileStateModal/ProfileStateModal.tsx | 6 ++ .../ManagePassword/ManagePassword.test.tsx | 30 +++--- .../components/MultiSigRequest/ErrorPage.tsx | 8 +- .../pages/ProfileSetup/ProfileSetup.test.tsx | 4 +- src/ui/pages/ProfileSetup/ProfileSetup.tsx | 5 +- src/ui/pages/Profiles/Profiles.test.tsx | 3 +- .../Profiles/components/ProfileItem.test.tsx | 4 +- .../SetupGroupProfile.test.tsx | 2 +- .../SetupGroupProfile/SetupGroupProfile.tsx | 2 +- .../InitializeGroup/InitializeGroup.test.tsx | 2 +- .../InitializeGroup/InitializeGroup.tsx | 7 +- .../PendingGroup/PendingGroup.test.tsx | 10 +- .../components/PendingGroup/PendingGroup.tsx | 6 +- .../SetupConnections.test.tsx | 8 +- .../SetupConnections/SetupConnections.tsx | 10 +- src/utils/transformGroupIdentifier.test.ts | 12 +-- src/utils/transformGroupIdentifier.ts | 13 +-- 36 files changed, 318 insertions(+), 233 deletions(-) diff --git a/src/core/__fixtures__/agent/ipexCommunicationFixtures.ts b/src/core/__fixtures__/agent/ipexCommunicationFixtures.ts index 7cbc851854..ffc8df4f67 100644 --- a/src/core/__fixtures__/agent/ipexCommunicationFixtures.ts +++ b/src/core/__fixtures__/agent/ipexCommunicationFixtures.ts @@ -514,7 +514,7 @@ const multisigParticipantsProps = { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "IdentifierName2", + proposedUsername: "IdentifierName2", }, }, multisigMembers: { diff --git a/src/core/__fixtures__/agent/multiSigFixtures.ts b/src/core/__fixtures__/agent/multiSigFixtures.ts index 27fb69a887..e415eb54ef 100644 --- a/src/core/__fixtures__/agent/multiSigFixtures.ts +++ b/src/core/__fixtures__/agent/multiSigFixtures.ts @@ -17,7 +17,7 @@ const memberMetadataRecordProps: IdentifierMetadataRecordProps = { groupId: "groupid", groupInitiator: true, groupCreated: false, - userName: "testUser", + proposedUsername: "testUser", }, }; @@ -119,7 +119,7 @@ const memberIdentifierRecord = { groupId: "08f22dee-8cb0-4d65-8600-a82bbc3f6fd7", groupInitiator: true, groupCreated: true, - userName: "testUser", + proposedUsername: "testUser", }, updatedAt: new Date("2024-06-28T03:55:04.260Z"), } as IdentifierMetadataRecordProps; diff --git a/src/core/agent/records/identifierMetadataRecord.ts b/src/core/agent/records/identifierMetadataRecord.ts index f05e36b5b5..69dd0ca701 100644 --- a/src/core/agent/records/identifierMetadataRecord.ts +++ b/src/core/agent/records/identifierMetadataRecord.ts @@ -5,7 +5,7 @@ interface GroupMetadata { groupId: string; groupInitiator: boolean; groupCreated: boolean; - userName: string; + proposedUsername: string; } interface IdentifierMetadataRecordProps { diff --git a/src/core/agent/services/identifier.types.ts b/src/core/agent/services/identifier.types.ts index 3e8a745f53..44b3d43a37 100644 --- a/src/core/agent/services/identifier.types.ts +++ b/src/core/agent/services/identifier.types.ts @@ -10,7 +10,7 @@ interface GroupMetadata { groupId: string; groupInitiator: boolean; groupCreated: boolean; - userName: string; + proposedUsername: string; initiatorName?: string; } @@ -28,6 +28,8 @@ interface IdentifierShortDetails { creationStatus: CreationStatus; groupMetadata?: GroupMetadata; groupMemberPre?: string; + groupUsername?: string; + groupId?: string; } interface IdentifierDetails extends IdentifierShortDetails { diff --git a/src/core/agent/services/identifierService.test.ts b/src/core/agent/services/identifierService.test.ts index 602cce863f..2c5831e6f5 100644 --- a/src/core/agent/services/identifierService.test.ts +++ b/src/core/agent/services/identifierService.test.ts @@ -168,7 +168,7 @@ const groupMetadata = { groupId: "group-id", groupInitiator: true, groupCreated: false, - userName: "testUser", + proposedUsername: "testUser", }; const keriMetadataRecordProps = { @@ -296,6 +296,9 @@ describe("Single sig service of agent", () => { theme: 0, creationStatus: CreationStatus.COMPLETE, groupMetadata, + groupMemberPre: undefined, + groupUsername: "testUser", + groupId: "group-id", }, ]); }); @@ -311,6 +314,18 @@ describe("Single sig service of agent", () => { groupMetadata: undefined, }), ]); + identifierStorage.getIdentifierMetadata = jest + .fn() + .mockImplementation(async (identifierId: string) => { + if (identifierId === "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5InX") { + return new IdentifierMetadataRecord({ + ...keriMetadataRecordProps, + id: identifierId, + groupMetadata, + }); + } + return keriMetadataRecord; + }); expect(await identifierService.getIdentifiers(false)).toStrictEqual([ { id: keriMetadataRecord.id, @@ -319,6 +334,9 @@ describe("Single sig service of agent", () => { theme: 0, creationStatus: CreationStatus.COMPLETE, groupMetadata, + groupMemberPre: undefined, + groupUsername: "testUser", + groupId: "group-id", }, { id: "EIZ-n_hHHY5ERGTzvpXYBkB6_yBAM4RXcjQG3-JykFvT", @@ -328,8 +346,11 @@ describe("Single sig service of agent", () => { creationStatus: CreationStatus.COMPLETE, groupMemberPre: "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5InX", groupMetadata: undefined, + groupUsername: "testUser", + groupId: "group-id", }, ]); + identifierStorage.getIdentifierMetadata = jest.fn(); }); test("can get all identifiers without error if there are none", async () => { @@ -397,6 +418,8 @@ describe("Single sig service of agent", () => { theme: 0, groupMetadata: keriMetadataRecord.groupMetadata, groupMemberPre: keriMetadataRecord.groupMemberPre, + groupUsername: "testUser", + groupId: "group-id", ...identifierStateKeria.state, creationStatus: CreationStatus.COMPLETE, members: undefined, @@ -420,6 +443,8 @@ describe("Single sig service of agent", () => { theme: 0, groupMetadata: keriMetadataRecord.groupMetadata, groupMemberPre: keriMetadataRecord.groupMemberPre, + groupUsername: "testUser", + groupId: "group-id", ...identifierStateKeria.state, creationStatus: CreationStatus.COMPLETE, members: [ @@ -598,7 +623,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupInitiator: true, groupId: "DCF6b0c5aVm_26_sCTgLB4An6oUxEM5pVDDLqxxXDxHd", - userName: "testUser", + proposedUsername: "testUser", }, }); @@ -641,7 +666,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupInitiator: true, groupId: "DCF6b0c5aVm_26_sCTgLB4An6oUxEM5pVDDLqxxXDxHd", - userName: "testUser", + proposedUsername: "testUser", }, }, }, @@ -703,7 +728,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupInitiator: false, groupId: "DCF6b0c5aVm_26_sCTgLB4An6oUxEM5pVDDLqxxXDxHd", - userName: "testUser", + proposedUsername: "testUser", }, }); @@ -746,7 +771,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupInitiator: false, groupId: "DCF6b0c5aVm_26_sCTgLB4An6oUxEM5pVDDLqxxXDxHd", - userName: "testUser", + proposedUsername: "testUser", }, }, }, @@ -1276,7 +1301,7 @@ describe("Single sig service of agent", () => { groupId: "test-group-123", groupInitiator: false, groupCreated: true, - userName: "testuser", + proposedUsername: "testuser", }, }; @@ -1299,7 +1324,7 @@ describe("Single sig service of agent", () => { }); expect(updateIdentifierMock).toBeCalledWith("member-identifier-id", { - name: `1.2.0.3:${newTheme}:0:${memberMetadata.groupMetadata.groupId}:${memberMetadata.groupMetadata.userName}:${newDisplayName}`, + name: `1.2.0.3:${newTheme}:0:${memberMetadata.groupMetadata.groupId}:${memberMetadata.groupMetadata.proposedUsername}:${newDisplayName}`, }); expect(identifierStorage.updateIdentifierMetadata).toBeCalledWith( @@ -1326,7 +1351,7 @@ describe("Single sig service of agent", () => { groupId: "test-group-123", groupInitiator: true, groupCreated: true, - userName: "testuser", + proposedUsername: "testuser", }; identifierStorage.getIdentifierMetadata = jest.fn().mockResolvedValue({ @@ -1340,7 +1365,7 @@ describe("Single sig service of agent", () => { theme: newTheme, }); expect(updateIdentifierMock).toBeCalledWith(keriMetadataRecord.id, { - name: `1.2.0.3:${newTheme}:1:${groupMetadata.groupId}:${groupMetadata.userName}:${newDisplayName}`, + name: `1.2.0.3:${newTheme}:1:${groupMetadata.groupId}:${groupMetadata.proposedUsername}:${newDisplayName}`, }); expect(identifierStorage.updateIdentifierMetadata).toBeCalledWith( keriMetadataRecord.id, @@ -1386,7 +1411,7 @@ describe("Single sig service of agent", () => { groupId: "test-group-123", groupInitiator: true, groupCreated: true, - userName: "oldusername", + proposedUsername: "oldusername", }; const memberMetadata = { ...keriMetadataRecord, @@ -1421,17 +1446,14 @@ describe("Single sig service of agent", () => { { groupMetadata: { ...memberMetadata.groupMetadata, - userName: newUsername, + proposedUsername: newUsername, }, } ); expect(identifierStorage.updateIdentifierMetadata).toBeCalledWith( keriMetadataRecord.id, { - groupMetadata: { - ...groupMetadata, - userName: newUsername, - }, + groupMetadata: undefined, } ); }); @@ -1442,7 +1464,7 @@ describe("Single sig service of agent", () => { groupId: "test-group-123", groupInitiator: true, groupCreated: false, - userName: "oldusername", + proposedUsername: "oldusername", }; identifierStorage.getIdentifierMetadata = jest.fn().mockResolvedValue({ @@ -1466,7 +1488,7 @@ describe("Single sig service of agent", () => { { groupMetadata: { ...groupMetadata, - userName: newUsername, + proposedUsername: newUsername, }, } ); @@ -1485,7 +1507,7 @@ describe("Single sig service of agent", () => { "newusername" ) ).rejects.toThrow( - `${IdentifierService.INVALID_GROUP_IDENTIFIER}: ${keriMetadataRecord.groupMemberPre}` + `${IdentifierService.INVALID_GROUP_IDENTIFIER}: ${keriMetadataRecord.id}` ); }); @@ -1494,7 +1516,7 @@ describe("Single sig service of agent", () => { groupId: "test-group-123", groupInitiator: true, groupCreated: true, - userName: "oldusername", + proposedUsername: "oldusername", }; const memberMetadataWithoutGroup = { ...keriMetadataRecord, @@ -1738,7 +1760,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: false, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, creationStatus: CreationStatus.COMPLETE, createdAt: new Date("2024-12-10T07:28:18.217384+00:00"), @@ -1766,7 +1788,7 @@ describe("Single sig service of agent", () => { groupId: "group3", groupCreated: false, groupInitiator: false, - userName: "user3", + proposedUsername: "user3", }, creationStatus: CreationStatus.COMPLETE, createdAt: new Date("2024-12-10T07:28:18.217384+00:00"), @@ -1780,7 +1802,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: true, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, } ); @@ -1802,7 +1824,7 @@ describe("Single sig service of agent", () => { groupId: "group3", groupCreated: true, groupInitiator: false, - userName: "user3", + proposedUsername: "user3", }, } ); @@ -1926,7 +1948,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: true, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, } ); @@ -2022,7 +2044,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupId: "group1", groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, id: "EL-EboMhx-DaBLiAS_Vm3qtJOubb2rkcS3zLU_r7UXtl", isDeleted: false, @@ -2047,7 +2069,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: true, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, } ); @@ -2167,7 +2189,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: false, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, creationStatus: CreationStatus.FAILED, createdAt: new Date("2024-12-10T07:28:18.217384+00:00"), @@ -2192,7 +2214,7 @@ describe("Single sig service of agent", () => { groupId: "group1", groupCreated: true, groupInitiator: true, - userName: "user1", + proposedUsername: "user1", }, } ); @@ -2369,7 +2391,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupId: "ED4KeyyTKFj72B008OTGgDCrFo6y7B2B73kfyzu5Inx", groupInitiator: true, - userName: "memberOne", + proposedUsername: "memberOne", }, }, true @@ -2382,7 +2404,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupId: "ED4KeyyTKFj", groupInitiator: true, - userName: "memberOne", + proposedUsername: "memberOne", }, }, true @@ -2395,7 +2417,7 @@ describe("Single sig service of agent", () => { groupCreated: false, groupId: "ED4KeyyTKFj", groupInitiator: false, - userName: "memberTwo", + proposedUsername: "memberTwo", }, }, true diff --git a/src/core/agent/services/identifierService.ts b/src/core/agent/services/identifierService.ts index 0830f3d507..09291f514d 100644 --- a/src/core/agent/services/identifierService.ts +++ b/src/core/agent/services/identifierService.ts @@ -117,20 +117,45 @@ class IdentifierService extends AgentService { ? await this.identifierStorage.getUserFacingIdentifierRecords() : await this.identifierStorage.getIdentifierRecords(); - for (let i = 0; i < records.length; i++) { - const metadata = records[i]; - const identifier: IdentifierShortDetails = { + for (const metadata of records) { + let groupUsername = metadata.groupMetadata?.proposedUsername ?? undefined; + let groupId = metadata.groupMetadata?.groupId; + + if (metadata.groupMemberPre) { + try { + const memberMetadata = + await this.identifierStorage.getIdentifierMetadata( + metadata.groupMemberPre + ); + groupUsername = + memberMetadata.groupMetadata?.proposedUsername ?? groupUsername; + groupId = memberMetadata.groupMetadata?.groupId ?? groupId; + } catch (error) { + if ( + !(error instanceof Error) || + error.message !== + IdentifierStorage.IDENTIFIER_METADATA_RECORD_MISSING + ) { + throw error; + } + } + } + + const groupMetadata = metadata.groupMemberPre + ? undefined + : metadata.groupMetadata; + + identifiers.push({ displayName: metadata.displayName, id: metadata.id, createdAtUTC: metadata.createdAt.toISOString(), theme: metadata.theme, creationStatus: metadata.creationStatus ?? false, - groupMetadata: metadata.groupMetadata, - }; - if (metadata.groupMemberPre) { - identifier.groupMemberPre = metadata.groupMemberPre; - } - identifiers.push(identifier); + groupMetadata, + groupMemberPre: metadata.groupMemberPre, + groupUsername, + groupId, + }); } return identifiers; } @@ -168,6 +193,32 @@ class IdentifierService extends AgentService { ).signing.map((member: { aid: string }) => member.aid); } + let groupUsername = metadata.groupMetadata?.proposedUsername ?? undefined; + let groupId = metadata.groupMetadata?.groupId; + + if (metadata.groupMemberPre) { + try { + const memberMetadata = + await this.identifierStorage.getIdentifierMetadata( + metadata.groupMemberPre + ); + groupUsername = + memberMetadata.groupMetadata?.proposedUsername ?? groupUsername; + groupId = memberMetadata.groupMetadata?.groupId ?? groupId; + } catch (error) { + if ( + !(error instanceof Error) || + error.message !== IdentifierStorage.IDENTIFIER_METADATA_RECORD_MISSING + ) { + throw error; + } + } + } + + const groupMetadata = metadata.groupMemberPre + ? undefined + : metadata.groupMetadata; + return { id: hab.prefix, displayName: metadata.displayName, @@ -175,7 +226,9 @@ class IdentifierService extends AgentService { theme: metadata.theme, groupMemberPre: metadata.groupMemberPre, creationStatus: metadata.creationStatus, - groupMetadata: metadata.groupMetadata, + groupMetadata, + groupUsername, + groupId, s: hab.state.s, dt: hab.state.dt, kt: hab.state.kt, @@ -212,7 +265,7 @@ class IdentifierService extends AgentService { groupId: parsed.groupMetadata.groupId, groupCreated: false, groupInitiator: parsed.groupMetadata.groupInitiator, - userName: parsed.groupMetadata.userName, + proposedUsername: parsed.groupMetadata.proposedUsername, }, }; } else { @@ -239,8 +292,8 @@ class IdentifierService extends AgentService { let name: string; if (metadata.groupMetadata) { const initiatorFlag = metadata.groupMetadata.groupInitiator ? "1" : "0"; - const userNamePart = metadata.groupMetadata.userName; - name = `${LATEST_IDENTIFIER_VERSION}:${metadata.theme}:${initiatorFlag}:${metadata.groupMetadata.groupId}:${userNamePart}:${metadata.displayName}`; + const proposedUsernamePart = metadata.groupMetadata.proposedUsername; + name = `${LATEST_IDENTIFIER_VERSION}:${metadata.theme}:${initiatorFlag}:${metadata.groupMetadata.groupId}:${proposedUsernamePart}:${metadata.displayName}`; } else { name = `${LATEST_IDENTIFIER_VERSION}:${metadata.theme}:${metadata.displayName}`; } @@ -536,7 +589,7 @@ class IdentifierService extends AgentService { const initiatorFlag = memberMetadata.groupMetadata.groupInitiator ? "1" : "0"; - const memberName = `${LATEST_IDENTIFIER_VERSION}:${data.theme}:${initiatorFlag}:${memberMetadata.groupMetadata.groupId}:${memberMetadata.groupMetadata.userName}:${data.displayName}`; + const memberName = `${LATEST_IDENTIFIER_VERSION}:${data.theme}:${initiatorFlag}:${memberMetadata.groupMetadata.groupId}:${memberMetadata.groupMetadata.proposedUsername}:${data.displayName}`; await this.props.signifyClient .identifiers() @@ -554,7 +607,7 @@ class IdentifierService extends AgentService { const initiatorFlag = identifierMetadata.groupMetadata.groupInitiator ? "1" : "0"; - name = `${LATEST_IDENTIFIER_VERSION}:${data.theme}:${initiatorFlag}:${identifierMetadata.groupMetadata.groupId}:${identifierMetadata.groupMetadata.userName}:${data.displayName}`; + name = `${LATEST_IDENTIFIER_VERSION}:${data.theme}:${initiatorFlag}:${identifierMetadata.groupMetadata.groupId}:${identifierMetadata.groupMetadata.proposedUsername}:${data.displayName}`; } else { name = `${LATEST_IDENTIFIER_VERSION}:${data.theme}:${data.displayName}`; } @@ -576,12 +629,6 @@ class IdentifierService extends AgentService { const identifierMetadata = await this.identifierStorage.getIdentifierMetadata(identifier); - if (!identifierMetadata.groupMetadata) { - throw new Error( - `${IdentifierService.INVALID_GROUP_IDENTIFIER}: ${identifierMetadata.groupMemberPre}` - ); - } - if (identifierMetadata.groupMemberPre) { const memberMetadata = await this.identifierStorage.getIdentifierMetadata( identifierMetadata.groupMemberPre @@ -604,19 +651,15 @@ class IdentifierService extends AgentService { const memberGroupMetadata: GroupMetadata = { ...memberMetadata.groupMetadata, - userName: username, + proposedUsername: username, }; await this.identifierStorage.updateIdentifierMetadata( identifierMetadata.groupMemberPre, { groupMetadata: memberGroupMetadata } ); - const groupMetadata: GroupMetadata = { - ...identifierMetadata.groupMetadata, - userName: username, - }; return this.identifierStorage.updateIdentifierMetadata(identifier, { - groupMetadata, + groupMetadata: undefined, }); } @@ -638,7 +681,7 @@ class IdentifierService extends AgentService { const groupMetadata: GroupMetadata = { ...identifierMetadata.groupMetadata, - userName: username, + proposedUsername: username, }; return this.identifierStorage.updateIdentifierMetadata(identifier, { groupMetadata, diff --git a/src/core/agent/services/ipexCommunicationService.test.ts b/src/core/agent/services/ipexCommunicationService.test.ts index 0b4c43b806..f162abf01c 100644 --- a/src/core/agent/services/ipexCommunicationService.test.ts +++ b/src/core/agent/services/ipexCommunicationService.test.ts @@ -593,7 +593,7 @@ describe("Receive group ACDC actions", () => { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "", + proposedUsername: "", }, }, multisigMembers: { @@ -785,7 +785,7 @@ describe("Receive group ACDC actions", () => { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "", + proposedUsername: "", }, }, multisigMembers: { @@ -936,7 +936,7 @@ describe("Receive group ACDC actions", () => { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "", + proposedUsername: "", }, }, multisigMembers: { diff --git a/src/core/agent/services/keriaNotificationService.test.ts b/src/core/agent/services/keriaNotificationService.test.ts index eb31ec84ed..6707d5f6cc 100644 --- a/src/core/agent/services/keriaNotificationService.test.ts +++ b/src/core/agent/services/keriaNotificationService.test.ts @@ -165,7 +165,7 @@ const notificationStorage = jest.mocked({ findById: jest.fn(), findExpectedById: jest.fn().mockResolvedValue({ id: "test-notification", - a: { r: NotificationRoute.ExnIpexGrant } + a: { r: NotificationRoute.ExnIpexGrant }, }), findAllByQuery: jest.fn(), getAll: jest.fn(), @@ -477,13 +477,13 @@ describe("Signify notification service of agent", () => { notificationStorage.deleteById = jest.fn(); notificationStorage.findExpectedById = jest.fn().mockResolvedValue({ id: "uuid", - a: { r: NotificationRoute.ExnIpexGrant } + a: { r: NotificationRoute.ExnIpexGrant }, }); const mockOperationPendingStorage = { findAllByQuery: jest.fn(), deleteById: jest.fn(), } as any; - + await deleteNotificationRecordById( agentServicesProps.signifyClient, notificationStorage, @@ -503,13 +503,13 @@ describe("Signify notification service of agent", () => { notificationStorage.deleteById = jest.fn(); notificationStorage.findExpectedById = jest.fn().mockResolvedValue({ id: "uuid", - a: { r: NotificationRoute.LocalAcdcRevoked } + a: { r: NotificationRoute.LocalAcdcRevoked }, }); const mockOperationPendingStorage = { findAllByQuery: jest.fn(), deleteById: jest.fn(), } as any; - + await deleteNotificationRecordById( agentServicesProps.signifyClient, notificationStorage, @@ -2201,7 +2201,7 @@ describe("Group IPEX presentation", () => { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "", + proposedUsername: "", }, }, multisigMembers: { @@ -3975,7 +3975,7 @@ describe("Long running operation tracker", () => { groupId: "group-id", groupInitiator: true, groupCreated: true, - userName: "", + proposedUsername: "", }, }, multisigMembers: { diff --git a/src/core/agent/services/multiSigService.test.ts b/src/core/agent/services/multiSigService.test.ts index 55d078fee9..9cedc91292 100644 --- a/src/core/agent/services/multiSigService.test.ts +++ b/src/core/agent/services/multiSigService.test.ts @@ -508,12 +508,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); @@ -573,7 +570,7 @@ describe("Creation of multi-sig", () => { groupId: "groupid", groupInitiator: false, groupCreated: false, - userName: "", + proposedUsername: "", }, }) ); @@ -702,12 +699,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); @@ -830,12 +824,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); @@ -1008,12 +999,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); @@ -1145,12 +1133,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); @@ -1261,12 +1246,9 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: { - groupId: "groupid", - groupInitiator: true, - groupCreated: true, - userName: "testUser", - }, + groupMetadata: undefined, + groupUsername: "testUser", + groupId: "groupid", }, }, }); diff --git a/src/core/agent/services/multiSigService.ts b/src/core/agent/services/multiSigService.ts index d095851953..892ca3ff28 100644 --- a/src/core/agent/services/multiSigService.ts +++ b/src/core/agent/services/multiSigService.ts @@ -188,14 +188,12 @@ class MultiSigService extends AgentService { .get(multisigId as string)) as HabState; try { - // @TODO: groupMetadata is only intended for the mHab - for now, it's copied to gHab so the UI has access to the group name. groupCreated is always kept as false in gHab so it does not disappear from the list of userFacing identifiers - this approach should be tidied up. await this.identifierStorage.createIdentifierMetadataRecord({ id: multisigId, displayName: mHabRecord.displayName, theme: mHabRecord.theme, creationStatus, groupMemberPre: memberPrefix, - groupMetadata: mHabRecord.groupMetadata, createdAt: new Date(multisigDetail.icp_dt), }); } catch (error) { @@ -226,7 +224,9 @@ class MultiSigService extends AgentService { creationStatus, groupMemberPre: memberPrefix, createdAtUTC: multisigDetail.icp_dt, - groupMetadata: mHabRecord.groupMetadata, + groupMetadata: undefined, + groupUsername: mHabRecord.groupMetadata.proposedUsername, + groupId: mHabRecord.groupMetadata.groupId, }, }, }); @@ -569,7 +569,6 @@ class MultiSigService extends AgentService { theme: mHabRecord.theme, creationStatus, groupMemberPre: mHabRecord.id, - groupMetadata: mHabRecord.groupMetadata, createdAt: new Date(multisigDetail.icp_dt), }); } catch (error) { @@ -600,7 +599,9 @@ class MultiSigService extends AgentService { creationStatus, groupMemberPre: mHabRecord.id, createdAtUTC: multisigDetail.icp_dt, - groupMetadata: mHabRecord.groupMetadata, + groupMetadata: undefined, + groupUsername: mHabRecord.groupMetadata.proposedUsername, + groupId: mHabRecord.groupMetadata.groupId, }, }, }); diff --git a/src/core/storage/sqliteStorage/migrations/v1.2.0.3-group-scoped-username.ts b/src/core/storage/sqliteStorage/migrations/v1.2.0.3-group-scoped-username.ts index 01193355aa..be2240c591 100644 --- a/src/core/storage/sqliteStorage/migrations/v1.2.0.3-group-scoped-username.ts +++ b/src/core/storage/sqliteStorage/migrations/v1.2.0.3-group-scoped-username.ts @@ -32,7 +32,7 @@ export const MIGRATION_V1203: LocalMigration = { recordValue = { ...recordValue, - groupMetadata: { ...groupMetadata, userName: "" }, + groupMetadata: { ...groupMetadata, proposedUsername: "" }, }; statements.push({ diff --git a/src/core/utils/habName.test.ts b/src/core/utils/habName.test.ts index de86cf1417..0e0d68045f 100644 --- a/src/core/utils/habName.test.ts +++ b/src/core/utils/habName.test.ts @@ -12,7 +12,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "groupId123", - userName: "", + proposedUsername: "", }, }, }, @@ -24,7 +24,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: false, groupId: "groupId456", - userName: "", + proposedUsername: "", }, }, }, @@ -36,7 +36,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "gr@up!d", - userName: "", + proposedUsername: "", }, }, }, @@ -48,7 +48,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "group-with-hyphens", - userName: "", + proposedUsername: "", }, }, }, @@ -60,7 +60,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "group-id-extra", - userName: "", + proposedUsername: "", }, }, }, @@ -69,7 +69,7 @@ describe("habName", () => { expect(result).toEqual(expect.objectContaining(expected)); }); - // Tests for new format names (1.2.0.3:theme:groupInitiator:groupId:userName:displayName) + // Tests for new format names (1.2.0.3:theme:groupInitiator:groupId:proposedUsername:displayName) test.each([ { name: "1.2.0.3:XX:1:groupId789:user123:MyNewGroup", @@ -80,7 +80,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "groupId789", - userName: "user123", + proposedUsername: "user123", }, }, }, @@ -93,7 +93,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "gr@up!d", - userName: "us$er%name", + proposedUsername: "us$er%name", }, }, }, @@ -106,7 +106,7 @@ describe("habName", () => { groupMetadata: { groupInitiator: true, groupId: "group-with-hyphens", - userName: "user123", + proposedUsername: "user123", }, }, }, @@ -138,7 +138,7 @@ describe("habName", () => { { name: "1.2.0.3:XX:1:groupId789:user123", // Invalid number of parts for new format (5 parts) errorMessage: - "Invalid new format name: Expected 3 or 6 parts separated by colons (version:theme:displayName or version:theme:groupInitiator:groupId:userName:displayName).", + "Invalid new format name: Expected 3 or 6 parts separated by colons (version:theme:displayName or version:theme:groupInitiator:groupId:proposedUsername:displayName).", }, { name: "03:1-group-id:", // Missing display name for old format @@ -180,7 +180,7 @@ describe("habName", () => { groupMetadata: { groupId: "groupXYZ", groupInitiator: true, - userName: "formattedUser", + proposedUsername: "formattedUser", }, }, expected: "1.2.0.3:XX:1:groupXYZ:formattedUser:FormattedGroup", diff --git a/src/core/utils/habName.ts b/src/core/utils/habName.ts index ad03681a52..e2756920ba 100644 --- a/src/core/utils/habName.ts +++ b/src/core/utils/habName.ts @@ -4,20 +4,20 @@ export interface HabNameParts { groupMetadata?: { groupInitiator: boolean; groupId: string; - userName: string; + proposedUsername: string; }; theme: string; } // Old format: theme:groupInitiator-groupId:displayName or theme:displayName -// New format: version:theme:groupInitiator:groupId:userName:displayName or version:theme:displayName +// New format: version:theme:groupInitiator:groupId:proposedUsername:displayName or version:theme:displayName export function parseHabName(name: string): HabNameParts { const parts = name.split(":"); if (name.startsWith("1.2.0.3:")) { if (parts.length !== 3 && parts.length !== 6) { throw new Error( - "Invalid new format name: Expected 3 or 6 parts separated by colons (version:theme:displayName or version:theme:groupInitiator:groupId:userName:displayName)." + "Invalid new format name: Expected 3 or 6 parts separated by colons (version:theme:displayName or version:theme:groupInitiator:groupId:proposedUsername:displayName)." ); } @@ -34,7 +34,7 @@ export function parseHabName(name: string): HabNameParts { const groupInitiatorStr = parts[2]; const groupId = parts[3]; - const userName = parts[4]; + const proposedUsername = parts[4]; const displayName = parts[5]; if (groupInitiatorStr !== "1" && groupInitiatorStr !== "0") { @@ -45,8 +45,10 @@ export function parseHabName(name: string): HabNameParts { if (!groupId || groupId.trim() === "") { throw new Error("Invalid new format name: groupId cannot be empty."); } - if (!userName) { - throw new Error("Invalid new format name: userName cannot be null."); + if (!proposedUsername) { + throw new Error( + "Invalid new format name: proposedUsername cannot be null." + ); } return { @@ -56,7 +58,7 @@ export function parseHabName(name: string): HabNameParts { groupMetadata: { groupInitiator: groupInitiatorStr === "1", groupId, - userName, + proposedUsername, }, }; } @@ -106,7 +108,7 @@ export function parseHabName(name: string): HabNameParts { groupMetadata: { groupInitiator: groupInitiatorStr === "1", groupId, - userName: "", + proposedUsername: "", }, }; } @@ -119,8 +121,8 @@ export function formatToV1_2_0_3(parts: HabNameParts): string { if (parts.groupMetadata) { const groupInitiatorStr = parts.groupMetadata.groupInitiator ? "1" : "0"; const groupIdPart = parts.groupMetadata.groupId || ""; - const userNamePart = parts.groupMetadata.userName || ""; - return `${version}:${themePart}:${groupInitiatorStr}:${groupIdPart}:${userNamePart}:${displayNamePart}`; + const proposedUsernamePart = parts.groupMetadata.proposedUsername || ""; + return `${version}:${themePart}:${groupInitiatorStr}:${groupIdPart}:${proposedUsernamePart}:${displayNamePart}`; } else { return `${version}:${themePart}:${displayNamePart}`; } diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index dfa566289d..636203f585 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -67,8 +67,10 @@ export const profilesCacheSlice = createSlice({ if (existedProfile) { existedProfile.identity = action.payload; } else { + const incomingGroupId = + action.payload.groupId ?? action.payload.groupMetadata?.groupId; const multisigConnections = - action.payload.groupMetadata?.groupId === state.multiSigGroup?.groupId + incomingGroupId && incomingGroupId === state.multiSigGroup?.groupId ? (state.multiSigGroup?.connections as MultisigConnectionDetails[]) : []; @@ -334,7 +336,8 @@ export const profilesCacheSlice = createSlice({ // For multisig connections, filter by groupId since all group members should see all connections // Check if this profile has group metadata to determine if it should have multisig connections - const profileGroupId = profile.identity?.groupMetadata?.groupId; + const profileGroupId = + profile.identity?.groupId ?? profile.identity?.groupMetadata?.groupId; if (profileGroupId) { profile.multisigConnections = allMultisig diff --git a/src/ui/__fixtures__/filteredIdentifierFix.ts b/src/ui/__fixtures__/filteredIdentifierFix.ts index 4f18c7096e..3e15f0d41c 100644 --- a/src/ui/__fixtures__/filteredIdentifierFix.ts +++ b/src/ui/__fixtures__/filteredIdentifierFix.ts @@ -12,7 +12,7 @@ const failedMultisignIdentifierFix: IdentifierShortDetails[] = [ groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: false, - userName: "test", + proposedUsername: "test", }, }, ]; @@ -28,7 +28,7 @@ const multisignIdentifierFix: IdentifierShortDetails[] = [ groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: true, - userName: "test", + proposedUsername: "test", }, }, ]; @@ -44,7 +44,7 @@ const pendingMemberIdentifierFix: IdentifierShortDetails[] = [ groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: false, - userName: "test", + proposedUsername: "test", }, }, ]; @@ -56,12 +56,8 @@ const pendingGroupIdentifierFix: IdentifierShortDetails = { theme: 0, creationStatus: CreationStatus.PENDING, groupMemberPre: "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5Inb", - groupMetadata: { - groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", - groupCreated: false, - groupInitiator: false, - userName: "test", - }, + groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", + groupUsername: "test", }; const filteredIdentifierFix: IdentifierShortDetails[] = [ @@ -93,6 +89,8 @@ const filteredIdentifierFix: IdentifierShortDetails[] = [ theme: 0, creationStatus: CreationStatus.COMPLETE, groupMemberPre: "EHzi_GBx0jIgd3G0Qqcjg3ZaLJ6d84wp6q0qUvC_iOQ4", + groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", + groupUsername: "GID 1", }, { displayName: "Profess", diff --git a/src/ui/components/EditProfile/EditProfile.tsx b/src/ui/components/EditProfile/EditProfile.tsx index 12a9b8caed..d02bbbfec1 100644 --- a/src/ui/components/EditProfile/EditProfile.tsx +++ b/src/ui/components/EditProfile/EditProfile.tsx @@ -41,10 +41,13 @@ const EditProfile = ({ const currentIdentifier = profiles[cardData.id]; const [isLoading, setLoading] = useState(false); + const getCurrentUsername = () => + cardData.groupMemberPre + ? cardData.groupUsername || "" + : cardData.groupMetadata?.proposedUsername || ""; + const [newDisplayName, setNewDisplayName] = useState( - editType === "userName" - ? cardData.groupMetadata?.userName || "" - : cardData.displayName + editType === "userName" ? getCurrentUsername() : cardData.displayName ); const [keyboardIsOpen, setKeyboardIsOpen] = useState(false); @@ -73,18 +76,25 @@ const EditProfile = ({ setModalIsOpen(false); }; + const baselineValue = + editType === "userName" ? getCurrentUsername() : cardData.displayName; + const verifyDisplayName = newDisplayName.length > 0 && newDisplayName.length <= DISPLAY_NAME_LENGTH && - newDisplayName.trim() !== cardData.displayName.trim(); + newDisplayName.trim() !== baselineValue.trim(); useEffect(() => { setNewDisplayName( - editType === "userName" - ? cardData.groupMetadata?.userName || "" - : cardData.displayName + editType === "userName" ? getCurrentUsername() : cardData.displayName ); - }, [editType, cardData.displayName, cardData.groupMetadata?.userName]); + }, [ + editType, + cardData.displayName, + cardData.groupMetadata?.proposedUsername, + cardData.groupUsername, + cardData.groupMemberPre, + ]); const handleSubmit = async () => { try { @@ -115,11 +125,15 @@ const EditProfile = ({ params.displayName = newDisplayName; params.groupMetadata = cardData.groupMetadata; await Agent.agent.identifiers.updateIdentifier(cardData.id, params); - } else if (isGroup && cardData.groupMetadata) { - params.groupMetadata = { - ...cardData.groupMetadata, - userName: newDisplayName, - }; + } else if (editType === "userName") { + if (!isGroup && cardData.groupMetadata) { + params.groupMetadata = { + ...cardData.groupMetadata, + proposedUsername: newDisplayName, + }; + } else { + params.groupMetadata = cardData.groupMetadata; + } await Agent.agent.identifiers.updateGroupUsername( cardData.id, @@ -131,12 +145,20 @@ const EditProfile = ({ ...currentIdentifier.identity, displayName: params.displayName, groupMetadata: params.groupMetadata, + groupUsername: + editType === "userName" && isGroup + ? newDisplayName + : currentIdentifier.identity.groupUsername, }; setCardData({ ...cardData, displayName: params.displayName, groupMetadata: params.groupMetadata, + groupUsername: + editType === "userName" && isGroup + ? newDisplayName + : cardData.groupUsername, }); handleCancel(); diff --git a/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx b/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx index e2e3b2ef1c..9b260a684a 100644 --- a/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx +++ b/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx @@ -27,11 +27,14 @@ const IdentifierSelectorModal = ({ const result = identifiers ? identifiers : Object.values(profiles) - .filter( - (item) => item.identity.creationStatus === CreationStatus.COMPLETE - ) - .filter((item) => !item.identity.groupMetadata?.groupId) - .map((item) => item.identity); + .filter( + (item) => item.identity.creationStatus === CreationStatus.COMPLETE + ) + .filter( + (item) => + !(item.identity.groupId ?? item.identity.groupMetadata?.groupId) + ) + .map((item) => item.identity); return result.map( (identifier): CardItem => ({ diff --git a/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx b/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx index 7cb25e0a64..a446255088 100644 --- a/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx +++ b/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx @@ -40,7 +40,7 @@ const IdentifierAttributeDetailModal = ({ if (!memberConnection?.label) { currentUserIndex = index; - name = data.groupMetadata?.userName || ""; + name = data.groupUsername || data.groupMetadata?.proposedUsername || ""; } const rank = index >= 0 ? index % 5 : 0; diff --git a/src/ui/components/ProfileDetailsModal/components/ProfileContent.tsx b/src/ui/components/ProfileDetailsModal/components/ProfileContent.tsx index 92a2e408f5..b9e141c335 100644 --- a/src/ui/components/ProfileDetailsModal/components/ProfileContent.tsx +++ b/src/ui/components/ProfileDetailsModal/components/ProfileContent.tsx @@ -95,7 +95,10 @@ const ProfileContent = ({ let name = memberConnection?.label || member; if (!memberConnection?.label) { - name = cardData.groupMetadata?.userName || ""; + name = + cardData.groupUsername || + cardData.groupMetadata?.proposedUsername || + ""; } const rank = index >= 0 ? index % 5 : 0; diff --git a/src/ui/components/ProfileStateModal/ProfileStateModal.tsx b/src/ui/components/ProfileStateModal/ProfileStateModal.tsx index 662e3cbbcd..6a21d8b9e6 100644 --- a/src/ui/components/ProfileStateModal/ProfileStateModal.tsx +++ b/src/ui/components/ProfileStateModal/ProfileStateModal.tsx @@ -123,6 +123,12 @@ const ProfileStateModal = () => { return; } + if (creationStatus === CreationStatus.PENDING && groupMemberPre) { + setIsOpen(true); + setHiddenContent(false); + return; + } + getDetails(); }, [ currentProfile?.identity.creationStatus, diff --git a/src/ui/components/Settings/components/ManagePassword/ManagePassword.test.tsx b/src/ui/components/Settings/components/ManagePassword/ManagePassword.test.tsx index cdbd23b315..80b0a6c8da 100644 --- a/src/ui/components/Settings/components/ManagePassword/ManagePassword.test.tsx +++ b/src/ui/components/Settings/components/ManagePassword/ManagePassword.test.tsx @@ -149,12 +149,10 @@ describe("Manage password", () => { }); await waitFor(() => { - expect( - queryByText( - TRANSLATIONS.settings.sections.security.managepassword.page.alert - .enablemessage - ) - ).toBeNull(); + const openAlert = document.querySelector( + '[data-testid="alert-cancel-enable-password"][is-open="true"]' + ); + expect(openAlert).toBeNull(); }); await waitFor(() => { @@ -219,12 +217,10 @@ describe("Manage password", () => { }); await waitFor(() => { - expect( - queryByText( - TRANSLATIONS.settings.sections.security.managepassword.page.alert - .enablemessage - ) - ).toBeNull(); + const openAlert = document.querySelector( + '[data-testid="alert-cancel-enable-password"][is-open="true"]' + ); + expect(openAlert).toBeNull(); }); await waitFor(() => { @@ -300,12 +296,10 @@ describe("Manage password", () => { }); await waitFor(() => { - expect( - queryByText( - TRANSLATIONS.settings.sections.security.managepassword.page.alert - .disablemessage - ) - ).toBeNull(); + const openAlert = document.querySelector( + '[data-testid="alert-cancel"][is-open="true"]' + ); + expect(openAlert).toBeNull(); }); await waitFor(() => { diff --git a/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx b/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx index 2410bbd15f..f42fb2cf82 100644 --- a/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx +++ b/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx @@ -43,9 +43,11 @@ const ErrorPage = ({ ? connection.groupId : undefined; - const identifier = Object.values(profiles).find( - (item) => item.identity.groupMetadata?.groupId === multiSignGroupId - ); + const identifier = Object.values(profiles).find((item) => { + const profileGroupId = + item.identity.groupId ?? item.identity.groupMetadata?.groupId; + return profileGroupId === multiSignGroupId; + }); if (identifier) { setResumeMultiSig(identifier.identity); diff --git a/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx b/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx index 2ef58cc535..887dbe5de7 100644 --- a/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx +++ b/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx @@ -582,7 +582,7 @@ describe("Profile setup", () => { groupMetadata: expect.objectContaining({ groupInitiator: true, groupCreated: false, - userName: "testUser", + proposedUsername: "testUser", initiatorName: "testUser", groupId: expect.any(String), }), @@ -966,7 +966,7 @@ describe("Profile setup: use as modal", () => { groupId: "0AAPHBnxoGK4tDuL4g87Eo9D", groupCreated: false, groupInitiator: false, - userName: "test", + proposedUsername: "test", }, }, connections: [], diff --git a/src/ui/pages/ProfileSetup/ProfileSetup.tsx b/src/ui/pages/ProfileSetup/ProfileSetup.tsx index 979d54df52..9ee2bcabf1 100644 --- a/src/ui/pages/ProfileSetup/ProfileSetup.tsx +++ b/src/ui/pages/ProfileSetup/ProfileSetup.tsx @@ -137,7 +137,7 @@ export const ProfileSetup = ({ ? false // Ensure joiner is not the initiator : true, groupCreated: false, - userName: userName, + proposedUsername: userName, initiatorName: stateCache.pendingJoinGroupMetadata?.isPendingJoinGroup ? stateCache.pendingJoinGroupMetadata?.initiatorName || undefined : userName, // Set initiatorName to userName for the initiator @@ -335,7 +335,8 @@ export const ProfileSetup = ({ Object.values(profiles).some( (profile) => profile.identity.id === scanGroupId || - profile.identity.groupMetadata?.groupId === scanGroupId + (profile.identity.groupId ?? + profile.identity.groupMetadata?.groupId) === scanGroupId ) ) { handleCloseScan(); diff --git a/src/ui/pages/Profiles/Profiles.test.tsx b/src/ui/pages/Profiles/Profiles.test.tsx index cd7355c2a8..a3a77683b8 100644 --- a/src/ui/pages/Profiles/Profiles.test.tsx +++ b/src/ui/pages/Profiles/Profiles.test.tsx @@ -182,7 +182,8 @@ describe("Profiles", () => { const setIsOpenMock = jest.fn(); const pendingIdentifier = filteredIdentifierFix.find( (idObj) => - idObj.creationStatus === CreationStatus.PENDING && !!idObj.groupMetadata + idObj.creationStatus === CreationStatus.PENDING && + (!!idObj.groupMetadata || !!idObj.groupMemberPre) ); if (!pendingIdentifier) { throw new Error( diff --git a/src/ui/pages/Profiles/components/ProfileItem.test.tsx b/src/ui/pages/Profiles/components/ProfileItem.test.tsx index 28e43da59b..6d81ee4aa4 100644 --- a/src/ui/pages/Profiles/components/ProfileItem.test.tsx +++ b/src/ui/pages/Profiles/components/ProfileItem.test.tsx @@ -118,7 +118,7 @@ describe("ProfileItem", () => { groupId: "id", groupInitiator: true, groupCreated: false, - userName: "initiator", + proposedUsername: "initiator", }, }; const onClickMock = jest.fn(); @@ -146,7 +146,7 @@ describe("ProfileItem", () => { groupId: "id", groupInitiator: true, groupCreated: false, - userName: "initiator", + proposedUsername: "initiator", }, }; const onClickMock = jest.fn(); diff --git a/src/ui/pages/SetupGroupProfile/SetupGroupProfile.test.tsx b/src/ui/pages/SetupGroupProfile/SetupGroupProfile.test.tsx index 54037b189d..b5286bb210 100644 --- a/src/ui/pages/SetupGroupProfile/SetupGroupProfile.test.tsx +++ b/src/ui/pages/SetupGroupProfile/SetupGroupProfile.test.tsx @@ -586,7 +586,7 @@ describe("Setup Connections", () => { await waitFor(() => { expect( - getByText(multisignIdentifierFix[0].groupMetadata!.userName) + getByText(multisignIdentifierFix[0].groupMetadata!.proposedUsername) ).toBeVisible(); }); }); diff --git a/src/ui/pages/SetupGroupProfile/SetupGroupProfile.tsx b/src/ui/pages/SetupGroupProfile/SetupGroupProfile.tsx index 01d0280281..0095cc47bc 100644 --- a/src/ui/pages/SetupGroupProfile/SetupGroupProfile.tsx +++ b/src/ui/pages/SetupGroupProfile/SetupGroupProfile.tsx @@ -34,7 +34,7 @@ const initialState: GroupInfomation = { groupId: "", groupInitiator: false, // Default to false for joiners groupCreated: false, - userName: "", + proposedUsername: "", initiatorName: "", }, }; diff --git a/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.test.tsx b/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.test.tsx index e1cc4da9fe..0d38fa9b2a 100644 --- a/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.test.tsx +++ b/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.test.tsx @@ -58,7 +58,7 @@ const initiatorGroupProfile = { groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: true, - userName: "Initiator", + proposedUsername: "Initiator", }, }; diff --git a/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.tsx b/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.tsx index 3be49ca333..37e63160cc 100644 --- a/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.tsx +++ b/src/ui/pages/SetupGroupProfile/components/InitializeGroup/InitializeGroup.tsx @@ -61,7 +61,7 @@ const InitializeGroup = ({ state, setState }: StageProps) => { }); members.unshift({ - name: profile?.identity.groupMetadata?.userName || "", + name: profile?.identity.groupMetadata?.proposedUsername || "", isCurrentUser: true, }); @@ -69,7 +69,10 @@ const InitializeGroup = ({ state, setState }: StageProps) => { ...member, rank: index >= 0 ? index % 5 : 0, })); - }, [profile?.identity.groupMetadata?.userName, state.selectedConnections]); + }, [ + profile?.identity.groupMetadata?.proposedUsername, + state.selectedConnections, + ]); const openSignerModal = () => setOpenSigners(true); diff --git a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.test.tsx b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.test.tsx index e4bd616923..f70f859556 100644 --- a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.test.tsx +++ b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.test.tsx @@ -79,7 +79,7 @@ const initiatorGroupProfile = { groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: true, - userName: "Initiator", + proposedUsername: "Initiator", }, }; @@ -89,7 +89,7 @@ const memberGroupProfile = { groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupCreated: false, groupInitiator: false, - userName: "Initiator", + proposedUsername: "Initiator", }, }; jest.mock("react-router-dom", () => ({ @@ -135,7 +135,7 @@ describe("Pending group", () => { time: Date.now(), passcodeIsSet: true, passwordIsSet: false, - userName: "Duke", + proposedUsername: "Duke", }, isOnline: true, }, @@ -266,7 +266,7 @@ describe("Pending group", () => { time: Date.now(), passcodeIsSet: true, passwordIsSet: false, - userName: "Duke", + proposedUsername: "Duke", }, isOnline: true, }, @@ -300,7 +300,7 @@ describe("Pending group", () => { groupId: "0AC8fs5EqOSKRNgjimwxdokY", groupInitiator: false, groupCreated: false, - userName: "QALZ", + proposedUsername: "QALZ", initiatorName: "ALZM", }, }, diff --git a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx index aabe79949e..4268f32756 100644 --- a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx +++ b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx @@ -106,7 +106,7 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { }); members.unshift({ - name: identity?.groupMetadata?.userName || "", + name: identity?.groupMetadata?.proposedUsername || "", isCurrentUser: true, accepted: groupDetails?.members.find( @@ -120,7 +120,7 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { })); }, [ state.selectedConnections, - identity?.groupMetadata?.userName, + identity?.groupMetadata?.proposedUsername, identity?.groupMemberPre, groupDetails?.members, isPendingMember, @@ -257,7 +257,7 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { customClass="pending-group" header={ ({ @@ -204,7 +204,7 @@ describe("Setup Connection", () => { time: Date.now(), passcodeIsSet: true, passwordIsSet: false, - userName: "Duke", + proposedUsername: "Duke", }, isOnline: true, }, @@ -301,7 +301,7 @@ describe("Setup Connection", () => { const calledArgs = getOobiMock.mock.calls[0]; expect(calledArgs[0]).toEqual(stage1State.newIdentifier.id); expect(calledArgs[1]).toEqual({ - alias: initiatorGroupProfile.groupMetadata.userName, + alias: initiatorGroupProfile.groupMetadata.proposedUsername, groupId: initiatorGroupProfile.groupMetadata.groupId, groupName: initiatorGroupProfile.displayName, }); @@ -328,7 +328,7 @@ describe("Setup Connection", () => { getByText(EN_TRANSLATIONS.setupgroupprofile.setupmembers.subtitle) ).toBeVisible(); expect( - getByText(initiatorGroupProfile.groupMetadata.userName) + getByText(initiatorGroupProfile.groupMetadata.proposedUsername) ).toBeVisible(); expect(getByTestId("avatar-button")).toBeVisible(); diff --git a/src/ui/pages/SetupGroupProfile/components/SetupConnections/SetupConnections.tsx b/src/ui/pages/SetupGroupProfile/components/SetupConnections/SetupConnections.tsx index ca2ec6d73a..bfdc99c5e5 100644 --- a/src/ui/pages/SetupGroupProfile/components/SetupConnections/SetupConnections.tsx +++ b/src/ui/pages/SetupGroupProfile/components/SetupConnections/SetupConnections.tsx @@ -49,7 +49,7 @@ const SetupConnections = ({ setState }: StageProps) => { const scanRef = useRef(null); const groupId = profile?.groupMetadata?.groupId; - const userName = profile?.groupMetadata?.userName; + const proposedUsername = profile?.groupMetadata?.proposedUsername; const { resolveGroupConnection } = useScanHandle(); const groupConnections = useAppSelector(getMultisigConnectionsCache); const [multiSigGroup, setMultiSigGroup] = useState< @@ -87,7 +87,7 @@ const SetupConnections = ({ setState }: StageProps) => { const fetchOobi = useCallback(async () => { if ( !groupId || - !userName || + !proposedUsername || !profile?.displayName || profile?.creationStatus === CreationStatus.PENDING || (profile.creationStatus === CreationStatus.COMPLETE && @@ -97,7 +97,7 @@ const SetupConnections = ({ setState }: StageProps) => { try { const oobiValue = await Agent.agent.connections.getOobi(profile.id, { - alias: userName, + alias: proposedUsername, groupId: groupId, groupName: profile?.displayName, }); @@ -109,7 +109,7 @@ const SetupConnections = ({ setState }: StageProps) => { } }, [ groupId, - userName, + proposedUsername, profile?.displayName, profile?.creationStatus, profile?.groupMemberPre, @@ -174,7 +174,7 @@ const SetupConnections = ({ setState }: StageProps) => { { b: ["BIe_q0F4EkYPEne6jUnSV1exxOYeGf_AMSMvegpF4XQP"], di: "test", groupMemberPre: "ELUXM-ajSu0o1qyFvss-3QQfkj3DOke9aHNwt72Byi9y", + groupId: "test-group-id", members: [ "EFZ-hSogn3-wXEahBbIW_oXYxAV_vH8eEhX6BwQHsYBu", "EFZ-hSogn3-wXEahBbIW_oXYxAV_vH8eEhX6BwQHsYB2", @@ -32,13 +33,10 @@ describe("transformGroupIdentifier", () => { createdAtUTC: "2024-03-07T11:54:56.886Z", theme: 0, creationStatus: CreationStatus.COMPLETE, - groupMetadata: { - groupId: "test", - groupInitiator: true, - groupCreated: false, - userName: "", - }, + groupMetadata: undefined, groupMemberPre: "ELUXM-ajSu0o1qyFvss-3QQfkj3DOke9aHNwt72Byi9y", + groupUsername: "", + groupId: "test-group-id", }, }; @@ -72,6 +70,8 @@ describe("transformGroupIdentifier", () => { creationStatus: CreationStatus.COMPLETE, groupMetadata: undefined, groupMemberPre: undefined, + groupUsername: undefined, + groupId: undefined, }, }; diff --git a/src/utils/transformGroupIdentifier.ts b/src/utils/transformGroupIdentifier.ts index 26a5c095f9..8c5196217d 100644 --- a/src/utils/transformGroupIdentifier.ts +++ b/src/utils/transformGroupIdentifier.ts @@ -17,15 +17,12 @@ export function transformGroupIdentifier( createdAtUTC: input.createdAtUTC, theme: input.theme, creationStatus: input.creationStatus, - groupMetadata: input.di - ? { - groupId: input.di, - groupInitiator: true, - groupCreated: false, - userName: "", - } - : undefined, + groupMetadata: input.groupMemberPre ? undefined : input.groupMetadata, groupMemberPre: input.groupMemberPre, + groupUsername: input.groupUsername ?? (input.di ? "" : undefined), + groupId: input.groupMemberPre + ? input.groupId + : input.groupMetadata?.groupId, }, }; From 7487df942b285248c5481d12ec41f90c90c6635b Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 30 Oct 2025 10:24:31 +0700 Subject: [PATCH 02/15] feat: add groupUsername to IdentifierMetadataRecord and related services --- .../agent/records/identifierMetadataRecord.ts | 3 ++ src/core/agent/records/identifierStorage.ts | 3 ++ src/core/agent/services/identifier.types.ts | 1 - .../agent/services/identifierService.test.ts | 8 ++- src/core/agent/services/identifierService.ts | 52 ++----------------- .../agent/services/multiSigService.test.ts | 14 +---- src/core/agent/services/multiSigService.ts | 6 +-- .../reducers/profileCache/profilesCache.ts | 14 +---- src/ui/__fixtures__/filteredIdentifierFix.ts | 2 - .../IdentifierSelectorModal.tsx | 5 +- .../components/MultiSigRequest/ErrorPage.tsx | 3 +- src/ui/pages/ProfileSetup/ProfileSetup.tsx | 3 +- src/utils/transformGroupIdentifier.test.ts | 2 - src/utils/transformGroupIdentifier.ts | 3 -- 14 files changed, 21 insertions(+), 98 deletions(-) diff --git a/src/core/agent/records/identifierMetadataRecord.ts b/src/core/agent/records/identifierMetadataRecord.ts index 69dd0ca701..522497ab54 100644 --- a/src/core/agent/records/identifierMetadataRecord.ts +++ b/src/core/agent/records/identifierMetadataRecord.ts @@ -17,6 +17,7 @@ interface IdentifierMetadataRecordProps { theme: number; groupMemberPre?: string; groupMetadata?: GroupMetadata; + groupUsername?: string; pendingDeletion?: boolean; sxlt?: string; tags?: Tags; @@ -30,6 +31,7 @@ class IdentifierMetadataRecord extends BaseRecord { pendingDeletion!: boolean; groupMemberPre?: string; groupMetadata?: GroupMetadata; + groupUsername?: string; sxlt?: string; static readonly type = "IdentifierMetadataRecord"; @@ -47,6 +49,7 @@ class IdentifierMetadataRecord extends BaseRecord { this.isDeleted = props.isDeleted ?? false; this.groupMetadata = props.groupMetadata; this.groupMemberPre = props.groupMemberPre; + this.groupUsername = props.groupUsername; this.pendingDeletion = props.pendingDeletion ?? false; this.sxlt = props.sxlt; this._tags = props.tags ?? {}; diff --git a/src/core/agent/records/identifierStorage.ts b/src/core/agent/records/identifierStorage.ts index 9c0451e020..531bce8025 100644 --- a/src/core/agent/records/identifierStorage.ts +++ b/src/core/agent/records/identifierStorage.ts @@ -70,6 +70,7 @@ class IdentifierStorage { | "creationStatus" | "isDeleted" | "groupMetadata" + | "groupUsername" | "pendingDeletion" > > @@ -85,6 +86,8 @@ class IdentifierStorage { identifierMetadataRecord.isDeleted = metadata.isDeleted; if (metadata.groupMetadata !== undefined) identifierMetadataRecord.groupMetadata = metadata.groupMetadata; + if (metadata.groupUsername !== undefined) + identifierMetadataRecord.groupUsername = metadata.groupUsername; if (metadata.pendingDeletion !== undefined) identifierMetadataRecord.pendingDeletion = metadata.pendingDeletion; await this.storageService.update(identifierMetadataRecord); diff --git a/src/core/agent/services/identifier.types.ts b/src/core/agent/services/identifier.types.ts index 44b3d43a37..f25bfcbee3 100644 --- a/src/core/agent/services/identifier.types.ts +++ b/src/core/agent/services/identifier.types.ts @@ -29,7 +29,6 @@ interface IdentifierShortDetails { groupMetadata?: GroupMetadata; groupMemberPre?: string; groupUsername?: string; - groupId?: string; } interface IdentifierDetails extends IdentifierShortDetails { diff --git a/src/core/agent/services/identifierService.test.ts b/src/core/agent/services/identifierService.test.ts index 2c5831e6f5..38e81699ab 100644 --- a/src/core/agent/services/identifierService.test.ts +++ b/src/core/agent/services/identifierService.test.ts @@ -178,6 +178,7 @@ const keriMetadataRecordProps = { theme: 0, creationStatus: CreationStatus.COMPLETE, groupMetadata, + groupUsername: "testUser", sxlt: "1AAHFlFbNZ29MWHve6gyXfaJr4q2xgCmNEadpkh7IPuP1weDcOEb-bv3CmOoXK3xIik85tc9AYlNxFn_sTMpcvlbog8k4T5rE35i", }; @@ -298,7 +299,6 @@ describe("Single sig service of agent", () => { groupMetadata, groupMemberPre: undefined, groupUsername: "testUser", - groupId: "group-id", }, ]); }); @@ -312,6 +312,7 @@ describe("Single sig service of agent", () => { displayName: "group", groupMemberPre: "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5InX", groupMetadata: undefined, + groupUsername: "testUser", }), ]); identifierStorage.getIdentifierMetadata = jest @@ -336,7 +337,6 @@ describe("Single sig service of agent", () => { groupMetadata, groupMemberPre: undefined, groupUsername: "testUser", - groupId: "group-id", }, { id: "EIZ-n_hHHY5ERGTzvpXYBkB6_yBAM4RXcjQG3-JykFvT", @@ -347,7 +347,6 @@ describe("Single sig service of agent", () => { groupMemberPre: "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5InX", groupMetadata: undefined, groupUsername: "testUser", - groupId: "group-id", }, ]); identifierStorage.getIdentifierMetadata = jest.fn(); @@ -419,7 +418,6 @@ describe("Single sig service of agent", () => { groupMetadata: keriMetadataRecord.groupMetadata, groupMemberPre: keriMetadataRecord.groupMemberPre, groupUsername: "testUser", - groupId: "group-id", ...identifierStateKeria.state, creationStatus: CreationStatus.COMPLETE, members: undefined, @@ -444,7 +442,6 @@ describe("Single sig service of agent", () => { groupMetadata: keriMetadataRecord.groupMetadata, groupMemberPre: keriMetadataRecord.groupMemberPre, groupUsername: "testUser", - groupId: "group-id", ...identifierStateKeria.state, creationStatus: CreationStatus.COMPLETE, members: [ @@ -1453,6 +1450,7 @@ describe("Single sig service of agent", () => { expect(identifierStorage.updateIdentifierMetadata).toBeCalledWith( keriMetadataRecord.id, { + groupUsername: newUsername, groupMetadata: undefined, } ); diff --git a/src/core/agent/services/identifierService.ts b/src/core/agent/services/identifierService.ts index 09291f514d..29bc9c1592 100644 --- a/src/core/agent/services/identifierService.ts +++ b/src/core/agent/services/identifierService.ts @@ -118,29 +118,6 @@ class IdentifierService extends AgentService { : await this.identifierStorage.getIdentifierRecords(); for (const metadata of records) { - let groupUsername = metadata.groupMetadata?.proposedUsername ?? undefined; - let groupId = metadata.groupMetadata?.groupId; - - if (metadata.groupMemberPre) { - try { - const memberMetadata = - await this.identifierStorage.getIdentifierMetadata( - metadata.groupMemberPre - ); - groupUsername = - memberMetadata.groupMetadata?.proposedUsername ?? groupUsername; - groupId = memberMetadata.groupMetadata?.groupId ?? groupId; - } catch (error) { - if ( - !(error instanceof Error) || - error.message !== - IdentifierStorage.IDENTIFIER_METADATA_RECORD_MISSING - ) { - throw error; - } - } - } - const groupMetadata = metadata.groupMemberPre ? undefined : metadata.groupMetadata; @@ -153,8 +130,7 @@ class IdentifierService extends AgentService { creationStatus: metadata.creationStatus ?? false, groupMetadata, groupMemberPre: metadata.groupMemberPre, - groupUsername, - groupId, + groupUsername: metadata.groupUsername, }); } return identifiers; @@ -193,28 +169,6 @@ class IdentifierService extends AgentService { ).signing.map((member: { aid: string }) => member.aid); } - let groupUsername = metadata.groupMetadata?.proposedUsername ?? undefined; - let groupId = metadata.groupMetadata?.groupId; - - if (metadata.groupMemberPre) { - try { - const memberMetadata = - await this.identifierStorage.getIdentifierMetadata( - metadata.groupMemberPre - ); - groupUsername = - memberMetadata.groupMetadata?.proposedUsername ?? groupUsername; - groupId = memberMetadata.groupMetadata?.groupId ?? groupId; - } catch (error) { - if ( - !(error instanceof Error) || - error.message !== IdentifierStorage.IDENTIFIER_METADATA_RECORD_MISSING - ) { - throw error; - } - } - } - const groupMetadata = metadata.groupMemberPre ? undefined : metadata.groupMetadata; @@ -227,8 +181,7 @@ class IdentifierService extends AgentService { groupMemberPre: metadata.groupMemberPre, creationStatus: metadata.creationStatus, groupMetadata, - groupUsername, - groupId, + groupUsername: metadata.groupUsername, s: hab.state.s, dt: hab.state.dt, kt: hab.state.kt, @@ -659,6 +612,7 @@ class IdentifierService extends AgentService { ); return this.identifierStorage.updateIdentifierMetadata(identifier, { + groupUsername: username, groupMetadata: undefined, }); } diff --git a/src/core/agent/services/multiSigService.test.ts b/src/core/agent/services/multiSigService.test.ts index 9cedc91292..c8b2074e4c 100644 --- a/src/core/agent/services/multiSigService.test.ts +++ b/src/core/agent/services/multiSigService.test.ts @@ -508,9 +508,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -699,9 +697,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -824,9 +820,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberPrefix, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -999,9 +993,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -1133,9 +1125,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -1246,9 +1236,7 @@ describe("Creation of multi-sig", () => { createdAtUTC: "2024-08-10T07:23:54.839894+00:00", groupMemberPre: memberMetadataRecord.id, theme: 0, - groupMetadata: undefined, groupUsername: "testUser", - groupId: "groupid", }, }, }); @@ -1583,7 +1571,9 @@ describe("Creation of multi-sig", () => { theme: 0, groupMetadata: { groupId: "test-group-id", + groupInitiator: true, groupCreated: false, + proposedUsername: "testUser", }, }, ]), diff --git a/src/core/agent/services/multiSigService.ts b/src/core/agent/services/multiSigService.ts index 892ca3ff28..8ebfb9bbe1 100644 --- a/src/core/agent/services/multiSigService.ts +++ b/src/core/agent/services/multiSigService.ts @@ -194,6 +194,7 @@ class MultiSigService extends AgentService { theme: mHabRecord.theme, creationStatus, groupMemberPre: memberPrefix, + groupUsername: mHabRecord.groupMetadata.proposedUsername, createdAt: new Date(multisigDetail.icp_dt), }); } catch (error) { @@ -224,9 +225,7 @@ class MultiSigService extends AgentService { creationStatus, groupMemberPre: memberPrefix, createdAtUTC: multisigDetail.icp_dt, - groupMetadata: undefined, groupUsername: mHabRecord.groupMetadata.proposedUsername, - groupId: mHabRecord.groupMetadata.groupId, }, }, }); @@ -569,6 +568,7 @@ class MultiSigService extends AgentService { theme: mHabRecord.theme, creationStatus, groupMemberPre: mHabRecord.id, + groupUsername: mHabRecord.groupMetadata.proposedUsername, createdAt: new Date(multisigDetail.icp_dt), }); } catch (error) { @@ -599,9 +599,7 @@ class MultiSigService extends AgentService { creationStatus, groupMemberPre: mHabRecord.id, createdAtUTC: multisigDetail.icp_dt, - groupMetadata: undefined, groupUsername: mHabRecord.groupMetadata.proposedUsername, - groupId: mHabRecord.groupMetadata.groupId, }, }, }); diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index 636203f585..79ff6ad5b1 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -67,24 +67,15 @@ export const profilesCacheSlice = createSlice({ if (existedProfile) { existedProfile.identity = action.payload; } else { - const incomingGroupId = - action.payload.groupId ?? action.payload.groupMetadata?.groupId; - const multisigConnections = - incomingGroupId && incomingGroupId === state.multiSigGroup?.groupId - ? (state.multiSigGroup?.connections as MultisigConnectionDetails[]) - : []; - state.profiles[action.payload.id] = { identity: action.payload, connections: [], - multisigConnections: multisigConnections || [], + multisigConnections: [], peerConnections: [], credentials: [], archivedCredentials: [], notifications: [], }; - - state.multiSigGroup = undefined; } }, addGroupProfile: (state, action: PayloadAction) => { @@ -336,8 +327,7 @@ export const profilesCacheSlice = createSlice({ // For multisig connections, filter by groupId since all group members should see all connections // Check if this profile has group metadata to determine if it should have multisig connections - const profileGroupId = - profile.identity?.groupId ?? profile.identity?.groupMetadata?.groupId; + const profileGroupId = profile.identity?.groupMetadata?.groupId; if (profileGroupId) { profile.multisigConnections = allMultisig diff --git a/src/ui/__fixtures__/filteredIdentifierFix.ts b/src/ui/__fixtures__/filteredIdentifierFix.ts index 3e15f0d41c..e3dbcbae6b 100644 --- a/src/ui/__fixtures__/filteredIdentifierFix.ts +++ b/src/ui/__fixtures__/filteredIdentifierFix.ts @@ -56,7 +56,6 @@ const pendingGroupIdentifierFix: IdentifierShortDetails = { theme: 0, creationStatus: CreationStatus.PENDING, groupMemberPre: "ED4KeyyTKFj-72B008OTGgDCrFo6y7B2B73kfyzu5Inb", - groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupUsername: "test", }; @@ -89,7 +88,6 @@ const filteredIdentifierFix: IdentifierShortDetails[] = [ theme: 0, creationStatus: CreationStatus.COMPLETE, groupMemberPre: "EHzi_GBx0jIgd3G0Qqcjg3ZaLJ6d84wp6q0qUvC_iOQ4", - groupId: "549eb79f-856c-4bb7-8dd5-d5eed865906a", groupUsername: "GID 1", }, { diff --git a/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx b/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx index 9b260a684a..9186c3d6f8 100644 --- a/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx +++ b/src/ui/components/IdentifierSelectorModal/IdentifierSelectorModal.tsx @@ -30,10 +30,7 @@ const IdentifierSelectorModal = ({ .filter( (item) => item.identity.creationStatus === CreationStatus.COMPLETE ) - .filter( - (item) => - !(item.identity.groupId ?? item.identity.groupMetadata?.groupId) - ) + .filter((item) => !item.identity.groupMetadata?.groupId) .map((item) => item.identity); return result.map( diff --git a/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx b/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx index f42fb2cf82..ba295ef7a1 100644 --- a/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx +++ b/src/ui/pages/NotificationDetails/components/MultiSigRequest/ErrorPage.tsx @@ -44,8 +44,7 @@ const ErrorPage = ({ : undefined; const identifier = Object.values(profiles).find((item) => { - const profileGroupId = - item.identity.groupId ?? item.identity.groupMetadata?.groupId; + const profileGroupId = item.identity.groupMetadata?.groupId; return profileGroupId === multiSignGroupId; }); diff --git a/src/ui/pages/ProfileSetup/ProfileSetup.tsx b/src/ui/pages/ProfileSetup/ProfileSetup.tsx index 9ee2bcabf1..a6b848977b 100644 --- a/src/ui/pages/ProfileSetup/ProfileSetup.tsx +++ b/src/ui/pages/ProfileSetup/ProfileSetup.tsx @@ -335,8 +335,7 @@ export const ProfileSetup = ({ Object.values(profiles).some( (profile) => profile.identity.id === scanGroupId || - (profile.identity.groupId ?? - profile.identity.groupMetadata?.groupId) === scanGroupId + profile.identity.groupMetadata?.groupId === scanGroupId ) ) { handleCloseScan(); diff --git a/src/utils/transformGroupIdentifier.test.ts b/src/utils/transformGroupIdentifier.test.ts index 9e08043c2f..1101da765a 100644 --- a/src/utils/transformGroupIdentifier.test.ts +++ b/src/utils/transformGroupIdentifier.test.ts @@ -36,7 +36,6 @@ describe("transformGroupIdentifier", () => { groupMetadata: undefined, groupMemberPre: "ELUXM-ajSu0o1qyFvss-3QQfkj3DOke9aHNwt72Byi9y", groupUsername: "", - groupId: "test-group-id", }, }; @@ -71,7 +70,6 @@ describe("transformGroupIdentifier", () => { groupMetadata: undefined, groupMemberPre: undefined, groupUsername: undefined, - groupId: undefined, }, }; diff --git a/src/utils/transformGroupIdentifier.ts b/src/utils/transformGroupIdentifier.ts index 8c5196217d..c3d5d1567d 100644 --- a/src/utils/transformGroupIdentifier.ts +++ b/src/utils/transformGroupIdentifier.ts @@ -20,9 +20,6 @@ export function transformGroupIdentifier( groupMetadata: input.groupMemberPre ? undefined : input.groupMetadata, groupMemberPre: input.groupMemberPre, groupUsername: input.groupUsername ?? (input.di ? "" : undefined), - groupId: input.groupMemberPre - ? input.groupId - : input.groupMetadata?.groupId, }, }; From a41452ee3238ba70c7b13f06cb2770c8c3f1c48d Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 30 Oct 2025 14:46:06 +0700 Subject: [PATCH 03/15] refactor(EditProfile): streamline parameter handling for displayName and groupMetadata updates --- src/ui/components/EditProfile/EditProfile.tsx | 83 ++++++++++--------- 1 file changed, 46 insertions(+), 37 deletions(-) diff --git a/src/ui/components/EditProfile/EditProfile.tsx b/src/ui/components/EditProfile/EditProfile.tsx index d02bbbfec1..3859295e2d 100644 --- a/src/ui/components/EditProfile/EditProfile.tsx +++ b/src/ui/components/EditProfile/EditProfile.tsx @@ -113,56 +113,65 @@ const EditProfile = ({ throw new Error(`${IDENTIFIER_NOT_EXIST} ${cardData.id}`); } - const params: Pick< - IdentifierMetadataRecordProps, - "theme" | "displayName" | "groupMetadata" - > = { - displayName: cardData.displayName, - theme: currentIdentifier.identity.theme, - }; - if (editType === "name") { - params.displayName = newDisplayName; - params.groupMetadata = cardData.groupMetadata; + const params: Pick< + IdentifierMetadataRecordProps, + "theme" | "displayName" | "groupMetadata" + > = { + displayName: newDisplayName, + theme: currentIdentifier.identity.theme, + groupMetadata: cardData.groupMetadata, + }; await Agent.agent.identifiers.updateIdentifier(cardData.id, params); - } else if (editType === "userName") { - if (!isGroup && cardData.groupMetadata) { - params.groupMetadata = { - ...cardData.groupMetadata, - proposedUsername: newDisplayName, - }; - } else { - params.groupMetadata = cardData.groupMetadata; - } + const updatedIdentifier: IdentifierShortDetails = { + ...currentIdentifier.identity, + displayName: params.displayName, + groupMetadata: params.groupMetadata, + }; + + setCardData({ + ...cardData, + displayName: params.displayName, + groupMetadata: params.groupMetadata, + }); + dispatch(addOrUpdateProfileIdentity(updatedIdentifier)); + } else if (editType === "userName") { await Agent.agent.identifiers.updateGroupUsername( cardData.id, newDisplayName ); - } - const updatedIdentifier: IdentifierShortDetails = { - ...currentIdentifier.identity, - displayName: params.displayName, - groupMetadata: params.groupMetadata, - groupUsername: - editType === "userName" && isGroup + // Update local state with new username + const updatedIdentifier: IdentifierShortDetails = { + ...currentIdentifier.identity, + groupUsername: isGroup ? newDisplayName : currentIdentifier.identity.groupUsername, - }; + groupMetadata: + !isGroup && cardData.groupMetadata + ? { + ...cardData.groupMetadata, + proposedUsername: newDisplayName, + } + : currentIdentifier.identity.groupMetadata, + }; - setCardData({ - ...cardData, - displayName: params.displayName, - groupMetadata: params.groupMetadata, - groupUsername: - editType === "userName" && isGroup - ? newDisplayName - : cardData.groupUsername, - }); + setCardData({ + ...cardData, + groupUsername: isGroup ? newDisplayName : cardData.groupUsername, + groupMetadata: + !isGroup && cardData.groupMetadata + ? { + ...cardData.groupMetadata, + proposedUsername: newDisplayName, + } + : cardData.groupMetadata, + }); + dispatch(addOrUpdateProfileIdentity(updatedIdentifier)); + } handleCancel(); - dispatch(addOrUpdateProfileIdentity(updatedIdentifier)); dispatch( setToastMsg( isGroup From cc1e4757207b000e61fe51c1207f29ddd9d6eabe Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Sat, 1 Nov 2025 01:19:51 +0700 Subject: [PATCH 04/15] refactor(ui): simplify username handling and remove unused groupMetadata updates --- src/ui/components/EditProfile/EditProfile.tsx | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/src/ui/components/EditProfile/EditProfile.tsx b/src/ui/components/EditProfile/EditProfile.tsx index 3859295e2d..fc8accc4e6 100644 --- a/src/ui/components/EditProfile/EditProfile.tsx +++ b/src/ui/components/EditProfile/EditProfile.tsx @@ -116,57 +116,38 @@ const EditProfile = ({ if (editType === "name") { const params: Pick< IdentifierMetadataRecordProps, - "theme" | "displayName" | "groupMetadata" + "theme" | "displayName" > = { displayName: newDisplayName, theme: currentIdentifier.identity.theme, - groupMetadata: cardData.groupMetadata, }; await Agent.agent.identifiers.updateIdentifier(cardData.id, params); const updatedIdentifier: IdentifierShortDetails = { ...currentIdentifier.identity, displayName: params.displayName, - groupMetadata: params.groupMetadata, }; setCardData({ ...cardData, displayName: params.displayName, - groupMetadata: params.groupMetadata, }); dispatch(addOrUpdateProfileIdentity(updatedIdentifier)); } else if (editType === "userName") { + // UI only allows editing username for fully created groups (with members) await Agent.agent.identifiers.updateGroupUsername( cardData.id, newDisplayName ); - // Update local state with new username const updatedIdentifier: IdentifierShortDetails = { ...currentIdentifier.identity, - groupUsername: isGroup - ? newDisplayName - : currentIdentifier.identity.groupUsername, - groupMetadata: - !isGroup && cardData.groupMetadata - ? { - ...cardData.groupMetadata, - proposedUsername: newDisplayName, - } - : currentIdentifier.identity.groupMetadata, + groupUsername: newDisplayName, }; setCardData({ ...cardData, - groupUsername: isGroup ? newDisplayName : cardData.groupUsername, - groupMetadata: - !isGroup && cardData.groupMetadata - ? { - ...cardData.groupMetadata, - proposedUsername: newDisplayName, - } - : cardData.groupMetadata, + groupUsername: newDisplayName, }); dispatch(addOrUpdateProfileIdentity(updatedIdentifier)); } From 8e30b6e37cceac9420563c54a3e20c10296ddfbe Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Mon, 3 Nov 2025 14:10:15 +0700 Subject: [PATCH 05/15] chore: fix type error after merge develop --- .../components/CredentialRequest/CredentialRequest.tsx | 4 ++-- .../components/ReceiveCredential/ReceiveCredential.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui/pages/NotificationDetails/components/CredentialRequest/CredentialRequest.tsx b/src/ui/pages/NotificationDetails/components/CredentialRequest/CredentialRequest.tsx index d7dfa7e5c5..2abfce890d 100644 --- a/src/ui/pages/NotificationDetails/components/CredentialRequest/CredentialRequest.tsx +++ b/src/ui/pages/NotificationDetails/components/CredentialRequest/CredentialRequest.tsx @@ -75,7 +75,7 @@ const CredentialRequest = ({ if (!memberConnection) { return { aid: member, - name: currentProfile?.identity.groupMetadata?.userName || "", + name: currentProfile?.identity.groupMetadata?.proposedUsername || "", joined: linkedGroup.linkedRequest.accepted, isCurrentUser: true, }; @@ -94,7 +94,7 @@ const CredentialRequest = ({ memberInfos, }); }, [ - currentProfile?.identity.groupMetadata?.userName, + currentProfile?.identity.groupMetadata?.proposedUsername, multisignConnectionsCache, notificationDetails.id, ]); diff --git a/src/ui/pages/NotificationDetails/components/ReceiveCredential/ReceiveCredential.tsx b/src/ui/pages/NotificationDetails/components/ReceiveCredential/ReceiveCredential.tsx index 1d6e015977..e347bea344 100644 --- a/src/ui/pages/NotificationDetails/components/ReceiveCredential/ReceiveCredential.tsx +++ b/src/ui/pages/NotificationDetails/components/ReceiveCredential/ReceiveCredential.tsx @@ -266,7 +266,7 @@ const ReceiveCredential = ({ let name = memberConnection?.label || member; if (!memberConnection?.label) { - name = profile?.identity.groupMetadata?.userName || ""; + name = profile?.identity.groupMetadata?.proposedUsername || ""; } return { From a07c5bd6ff40abcfcfda4a847e10a355cbb33238 Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Tue, 4 Nov 2025 14:50:27 +0700 Subject: [PATCH 06/15] test: add unit test for updating identifier groupUsername --- .../agent/records/identifierStorage.test.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/agent/records/identifierStorage.test.ts b/src/core/agent/records/identifierStorage.test.ts index 622bc95255..f3a18f5be0 100644 --- a/src/core/agent/records/identifierStorage.test.ts +++ b/src/core/agent/records/identifierStorage.test.ts @@ -91,6 +91,24 @@ describe("Identifier storage test", () => { expect(storageService.update).toBeCalled(); }); + test("Should update identifier groupUsername", async () => { + const identifierWithGroupUsername = new IdentifierMetadataRecord({ + ...identifierMetadataRecordProps, + groupUsername: "oldUsername", + }); + storageService.findById.mockResolvedValue(identifierWithGroupUsername); + + await identifierStorage.updateIdentifierMetadata( + identifierMetadataRecord.id, + { + groupUsername: "newUsername", + } + ); + + expect(identifierWithGroupUsername.groupUsername).toBe("newUsername"); + expect(storageService.update).toBeCalledWith(identifierWithGroupUsername); + }); + test("Should get all identifier pending deletion", async () => { storageService.findAllByQuery.mockResolvedValue([ { From 0ae64e78078676c5cf7e10c6f408f8ca5297ebaf Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Wed, 5 Nov 2025 15:17:42 +0700 Subject: [PATCH 07/15] refactor(ui): enhance username handling in various components and streamline identifier retrieval --- src/store/reducers/stateCache/utils.ts | 11 +++++++-- src/ui/components/AppWrapper/AppWrapper.tsx | 4 +++- .../IdentifierAttributeDetailModal.tsx | 15 ++++++------ .../components/ProfileContent.tsx | 18 +++++++------- .../components/PendingGroup/PendingGroup.tsx | 24 +++++++++++-------- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/store/reducers/stateCache/utils.ts b/src/store/reducers/stateCache/utils.ts index 6360ed8c1e..ba5b71c60f 100644 --- a/src/store/reducers/stateCache/utils.ts +++ b/src/store/reducers/stateCache/utils.ts @@ -28,9 +28,16 @@ const filterProfileData = ( const profileConnections = allConnections.filter( (conn) => conn.identifier === profileId ); + + // For gHab (group identifier), get groupId from mHab; for mHab/pending, use own groupMetadata + let groupIdToFilter = profile.groupMetadata?.groupId; + if (profile.groupMemberPre && identifiers[profile.groupMemberPre]) { + groupIdToFilter = + identifiers[profile.groupMemberPre].groupMetadata?.groupId; + } + const profileMultisigConnections = allMultisigConnections.filter( - (conn) => - "groupId" in conn && conn.groupId === profile.groupMetadata?.groupId + (conn) => "groupId" in conn && conn.groupId === groupIdToFilter ); const profilePeerConnections = allPeerConnections.filter( (conn) => conn.selectedAid === profileId diff --git a/src/ui/components/AppWrapper/AppWrapper.tsx b/src/ui/components/AppWrapper/AppWrapper.tsx index dec292f5d2..f5fd726389 100644 --- a/src/ui/components/AppWrapper/AppWrapper.tsx +++ b/src/ui/components/AppWrapper/AppWrapper.tsx @@ -374,6 +374,8 @@ const AppWrapper = (props: { children: ReactNode }) => { true ); const storedIdentifiers = await Agent.agent.identifiers.getIdentifiers(); + const allIdentifiersIncludingMember = + await Agent.agent.identifiers.getIdentifiers(false); const storedPeerConnections = await Agent.agent.peerConnectionPair.getAllPeerConnectionAccount(); @@ -397,7 +399,7 @@ const AppWrapper = (props: { children: ReactNode }) => { dispatch(updateRecentProfiles(profileHistories)); } - const identifiersDict = storedIdentifiers.reduce( + const identifiersDict = allIdentifiersIncludingMember.reduce( (acc: Record, identifier) => { acc[identifier.id] = identifier; return acc; diff --git a/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx b/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx index 36ae4ccc27..a3a8701f70 100644 --- a/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx +++ b/src/ui/components/ProfileDetailsModal/components/IdentifierAttributeDetailModal/IdentifierAttributeDetailModal.tsx @@ -40,18 +40,19 @@ const IdentifierAttributeDetailModal = ({ const memberConnection = multisignConnectionsCache.find( (c) => c.id === member ); - let name = memberConnection?.label || member; + const isCurrent = member === data.groupMemberPre; + const displayNameCandidate = isCurrent + ? data.groupUsername || data.groupMetadata?.proposedUsername || "" + : member; - if (!memberConnection?.label) { - currentUserIndex = index; - name = data.groupUsername || data.groupMetadata?.proposedUsername || ""; - } + const name = memberConnection?.label || displayNameCandidate; + if (isCurrent) currentUserIndex = index; const rank = index >= 0 ? index % 5 : 0; return { - name: name, - isCurrentUser: !memberConnection?.label, + name, + isCurrentUser: isCurrent, avatar: ( c.id === member ); - let name = memberConnection?.label || member; - - if (!memberConnection?.label) { - name = - cardData.groupUsername || - cardData.groupMetadata?.proposedUsername || - ""; - } + const isCurrent = member === cardData.groupMemberPre; + const name = + memberConnection?.label || + (isCurrent + ? cardData.groupUsername || + cardData.groupMetadata?.proposedUsername || + "" + : member); const rank = index >= 0 ? index % 5 : 0; @@ -112,7 +112,7 @@ const ProfileContent = ({ rank={rank} /> ), - isCurrentUser: !memberConnection?.label, + isCurrentUser: isCurrent, status: MemberAcceptStatus.None, }; }) diff --git a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx index b0323c403a..737c9a31a8 100644 --- a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx +++ b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx @@ -61,8 +61,7 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { null ); - const isMember = !identity?.groupMetadata?.groupInitiator; - const isPendingMember = isMember && initGroupNotification; + const isPendingMember = !!initGroupNotification; const rotationThreshold = isPendingMember ? multisigIcpDetails?.rotationThreshold @@ -105,7 +104,10 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { }); members.unshift({ - name: identity?.groupMetadata?.proposedUsername || "", + name: + identity?.groupUsername || + identity?.groupMetadata?.proposedUsername || + "", isCurrentUser: true, status: groupDetails?.members.find( (item) => item.aid === identity?.groupMemberPre @@ -114,7 +116,7 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { : MemberAcceptStatus.Waiting, }); - return members.map((member, index) => ({ + const mapped = members.map((member, index) => ({ ...member, avatar: ( { /> ), })); + return mapped; }, [ state.selectedConnections, identity?.groupMetadata?.proposedUsername, @@ -175,28 +178,27 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { }; const getInceptionStatus = useCallback(async () => { - if (!identity?.id || !identity?.groupMetadata) return; + // We can fetch inception status using gHab id even if groupMetadata is not stored on gHab + if (!identity?.id) return; try { setLoading(true); const details = await Agent.agent.multiSigs.getInceptionStatus( identity.id ); - setGroupDetails(details); } catch (e) { showError("Unable to load group: ", e, dispatch); } finally { setLoading(false); } - }, [dispatch, identity?.groupMetadata, identity?.id]); + }, [dispatch, identity?.id]); const fetchMultisigDetails = useCallback(async () => { if (!initGroupNotification) return; const details = await Agent.agent.multiSigs.getMultisigIcpDetails( initGroupNotification.a.d as string ); - setMultisigIcpDetails(details); }, [initGroupNotification]); @@ -207,7 +209,6 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { await fetchMultisigDetails(); return; } - await getInceptionStatus(); }, [ fetchMultisigDetails, @@ -262,7 +263,10 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { customClass="pending-group" header={ Date: Wed, 5 Nov 2025 15:39:26 +0700 Subject: [PATCH 08/15] feat(ui): add setProfileMultisigConnections action and update group creation handler to refresh multisig connections --- .../reducers/profileCache/profilesCache.ts | 15 ++++++++++ .../AppWrapper/coreEventListeners.ts | 28 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index 643dfc06c5..92eada5091 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -375,6 +375,20 @@ export const profilesCacheSlice = createSlice({ targetProfile.multisigConnections = [...existing, mapped]; }, + setProfileMultisigConnections: ( + state, + action: PayloadAction<{ + profileId: string; + connections: MultisigConnectionDetails[]; + }> + ) => { + const { profileId, connections } = action.payload; + const targetProfile = state.profiles[profileId]; + if (targetProfile) { + targetProfile.multisigConnections = connections; + } + }, + // Wallet Connection Actions setConnectedDApp: (state, action: PayloadAction) => { // Store in global state for cross-profile access @@ -488,6 +502,7 @@ export const { updateOrAddConnectionCache, removeConnectionCache, updateOrAddMultisigConnectionCache, + setProfileMultisigConnections, setOpenConnectionId, setMissingAliasConnection, setConnectedDApp, diff --git a/src/ui/components/AppWrapper/coreEventListeners.ts b/src/ui/components/AppWrapper/coreEventListeners.ts index 0c4d441c16..74b6e8ee4f 100644 --- a/src/ui/components/AppWrapper/coreEventListeners.ts +++ b/src/ui/components/AppWrapper/coreEventListeners.ts @@ -2,6 +2,7 @@ import { Agent } from "../../../core/agent/agent"; import { CreationStatus, isRegularConnectionDetails, + MultisigConnectionDetails, } from "../../../core/agent/agent.types"; import { EventTypes, @@ -17,6 +18,7 @@ import { addNotification, addOrUpdateProfileIdentity, deleteNotificationById, + setProfileMultisigConnections, updateOrAddConnectionCache, updateProfileCreationStatus, } from "../../../store/reducers/profileCache"; @@ -97,7 +99,31 @@ const groupCreatedHandler = async ( event: GroupCreatedEvent, dispatch: ReturnType ) => { - dispatch(addGroupProfileAsync(event.payload.group)); + await dispatch(addGroupProfileAsync(event.payload.group)); + + // Refresh multisig connections cache after group is created + // This ensures member names are displayed correctly without page refresh + const allConnections = await Agent.agent.connections.getMultisigConnections(); + const multisigConnections = allConnections as MultisigConnectionDetails[]; + + const mHabId = event.payload.group.groupMemberPre; + if (mHabId) { + const mHab = await Agent.agent.identifiers.getIdentifier(mHabId); + const groupId = mHab?.groupMetadata?.groupId; + + if (groupId) { + const groupConnections = multisigConnections.filter( + (conn) => conn.groupId === groupId + ); + + dispatch( + setProfileMultisigConnections({ + profileId: event.payload.group.id, + connections: groupConnections, + }) + ); + } + } }; export { From d6c5f337a653c3e4e30280292674e3e0ebd34e8a Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Wed, 5 Nov 2025 16:00:47 +0700 Subject: [PATCH 09/15] test: enhance group creation handler tests --- .../components/AppWrapper/AppWrapper.test.tsx | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/ui/components/AppWrapper/AppWrapper.test.tsx b/src/ui/components/AppWrapper/AppWrapper.test.tsx index 6ecae12cad..f1289b9ed4 100644 --- a/src/ui/components/AppWrapper/AppWrapper.test.tsx +++ b/src/ui/components/AppWrapper/AppWrapper.test.tsx @@ -41,6 +41,7 @@ import { setConnectedDApp, setPendingDAppConnection, addGroupProfileAsync, + setProfileMultisigConnections, } from "../../../store/reducers/profileCache"; import { setQueueIncomingRequest, @@ -50,6 +51,7 @@ import { IncomingRequestType } from "../../../store/reducers/stateCache/stateCac import { pendingGroupIdentifierFix, pendingIdentifierFix, + pendingMemberIdentifierFix, } from "../../__fixtures__/filteredIdentifierFix"; import { ToastMsgType } from "../../globals/types"; import { @@ -97,6 +99,7 @@ jest.mock("../../../core/agent/agent", () => { }, identifiers: { getIdentifiers: jest.fn().mockResolvedValue([]), + getIdentifier: jest.fn().mockResolvedValue(null), syncKeriaIdentifiers: jest.fn(), onIdentifierAdded: jest.fn(), getAvailableWitnesses: jest.fn(), @@ -515,14 +518,39 @@ describe("Group state changed handler", () => { const innerDispatch = jest.fn(); const getState = jest.fn(() => ({ profilesCache: { recentProfiles: [] } })); - dispatch.mockImplementation((func) => { - func(innerDispatch, getState); + dispatch.mockImplementation((action) => { + // Handle both thunk functions and plain actions + if (typeof action === "function") { + action(innerDispatch, getState); + } else { + innerDispatch(action); + } }); + // Mock getIdentifier to return mHab with groupMetadata + const mHabWithMetadata = pendingMemberIdentifierFix[0]; + Agent.agent.identifiers.getIdentifier = jest + .fn() + .mockResolvedValue(mHabWithMetadata); + + // Mock getMultisigConnections to return empty array + Agent.agent.connections.getMultisigConnections = jest + .fn() + .mockResolvedValue([]); + await groupCreatedHandler(groupCreatedEvent, dispatch); expect(innerDispatch).toBeCalledWith( addGroupProfile(pendingGroupIdentifierFix) ); + expect(Agent.agent.identifiers.getIdentifier).toBeCalledWith( + pendingGroupIdentifierFix.groupMemberPre + ); + expect(dispatch).toBeCalledWith( + setProfileMultisigConnections({ + profileId: pendingGroupIdentifierFix.id, + connections: [], + }) + ); }); }); From e979588c76cde371efc16efcb1620459b0296f0c Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 6 Nov 2025 14:15:47 +0700 Subject: [PATCH 10/15] feat(ui): update addGroupProfileAsync to fetch and dispatch multisig connections for group members --- .../reducers/profileCache/profilesCache.ts | 23 ++++++++++++++++ .../components/AppWrapper/AppWrapper.test.tsx | 13 +++------- .../AppWrapper/coreEventListeners.ts | 26 ------------------- 3 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index 92eada5091..cbe30aba10 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -449,6 +449,29 @@ export const profilesCacheSlice = createSlice({ export const addGroupProfileAsync = (group: IdentifierShortDetails) => async (dispatch: AppDispatch, getState: () => RootState) => { + const mHabId = group.groupMemberPre; + if (mHabId) { + const allConnections = + await Agent.agent.connections.getMultisigConnections(); + const multisigConnections = allConnections as MultisigConnectionDetails[]; + + const mHab = await Agent.agent.identifiers.getIdentifier(mHabId); + const groupId = mHab?.groupMetadata?.groupId; + + if (groupId) { + const groupConnections = multisigConnections.filter( + (conn) => conn.groupId === groupId + ); + + dispatch( + setProfileMultisigConnections({ + profileId: mHabId, + connections: groupConnections, + }) + ); + } + } + dispatch(addGroupProfile(group)); await Agent.agent.basicStorage.createOrUpdateBasicRecord( diff --git a/src/ui/components/AppWrapper/AppWrapper.test.tsx b/src/ui/components/AppWrapper/AppWrapper.test.tsx index f1289b9ed4..318c4a87a2 100644 --- a/src/ui/components/AppWrapper/AppWrapper.test.tsx +++ b/src/ui/components/AppWrapper/AppWrapper.test.tsx @@ -41,7 +41,6 @@ import { setConnectedDApp, setPendingDAppConnection, addGroupProfileAsync, - setProfileMultisigConnections, } from "../../../store/reducers/profileCache"; import { setQueueIncomingRequest, @@ -519,7 +518,6 @@ describe("Group state changed handler", () => { const getState = jest.fn(() => ({ profilesCache: { recentProfiles: [] } })); dispatch.mockImplementation((action) => { - // Handle both thunk functions and plain actions if (typeof action === "function") { action(innerDispatch, getState); } else { @@ -527,30 +525,25 @@ describe("Group state changed handler", () => { } }); - // Mock getIdentifier to return mHab with groupMetadata const mHabWithMetadata = pendingMemberIdentifierFix[0]; Agent.agent.identifiers.getIdentifier = jest .fn() .mockResolvedValue(mHabWithMetadata); - // Mock getMultisigConnections to return empty array Agent.agent.connections.getMultisigConnections = jest .fn() .mockResolvedValue([]); await groupCreatedHandler(groupCreatedEvent, dispatch); + expect(innerDispatch).toBeCalledWith( addGroupProfile(pendingGroupIdentifierFix) ); + + expect(Agent.agent.connections.getMultisigConnections).toBeCalled(); expect(Agent.agent.identifiers.getIdentifier).toBeCalledWith( pendingGroupIdentifierFix.groupMemberPre ); - expect(dispatch).toBeCalledWith( - setProfileMultisigConnections({ - profileId: pendingGroupIdentifierFix.id, - connections: [], - }) - ); }); }); diff --git a/src/ui/components/AppWrapper/coreEventListeners.ts b/src/ui/components/AppWrapper/coreEventListeners.ts index 74b6e8ee4f..0430594f8d 100644 --- a/src/ui/components/AppWrapper/coreEventListeners.ts +++ b/src/ui/components/AppWrapper/coreEventListeners.ts @@ -2,7 +2,6 @@ import { Agent } from "../../../core/agent/agent"; import { CreationStatus, isRegularConnectionDetails, - MultisigConnectionDetails, } from "../../../core/agent/agent.types"; import { EventTypes, @@ -18,7 +17,6 @@ import { addNotification, addOrUpdateProfileIdentity, deleteNotificationById, - setProfileMultisigConnections, updateOrAddConnectionCache, updateProfileCreationStatus, } from "../../../store/reducers/profileCache"; @@ -100,30 +98,6 @@ const groupCreatedHandler = async ( dispatch: ReturnType ) => { await dispatch(addGroupProfileAsync(event.payload.group)); - - // Refresh multisig connections cache after group is created - // This ensures member names are displayed correctly without page refresh - const allConnections = await Agent.agent.connections.getMultisigConnections(); - const multisigConnections = allConnections as MultisigConnectionDetails[]; - - const mHabId = event.payload.group.groupMemberPre; - if (mHabId) { - const mHab = await Agent.agent.identifiers.getIdentifier(mHabId); - const groupId = mHab?.groupMetadata?.groupId; - - if (groupId) { - const groupConnections = multisigConnections.filter( - (conn) => conn.groupId === groupId - ); - - dispatch( - setProfileMultisigConnections({ - profileId: event.payload.group.id, - connections: groupConnections, - }) - ); - } - } }; export { From 40b0ad2eac466ef5ce6fa545589fbaa01ef7556d Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 6 Nov 2025 14:22:38 +0700 Subject: [PATCH 11/15] refactor(core): simplify groupId filtering logic --- src/store/reducers/stateCache/utils.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/store/reducers/stateCache/utils.ts b/src/store/reducers/stateCache/utils.ts index ba5b71c60f..2b83ffb485 100644 --- a/src/store/reducers/stateCache/utils.ts +++ b/src/store/reducers/stateCache/utils.ts @@ -29,12 +29,9 @@ const filterProfileData = ( (conn) => conn.identifier === profileId ); - // For gHab (group identifier), get groupId from mHab; for mHab/pending, use own groupMetadata - let groupIdToFilter = profile.groupMetadata?.groupId; - if (profile.groupMemberPre && identifiers[profile.groupMemberPre]) { - groupIdToFilter = - identifiers[profile.groupMemberPre].groupMetadata?.groupId; - } + const groupIdToFilter = profile.groupMemberPre + ? identifiers[profile.groupMemberPre]?.groupMetadata?.groupId + : profile.groupMetadata?.groupId; const profileMultisigConnections = allMultisigConnections.filter( (conn) => "groupId" in conn && conn.groupId === groupIdToFilter From cbef0a430351cfe7e07690926b7caf4510a5a246 Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 6 Nov 2025 14:35:09 +0700 Subject: [PATCH 12/15] chore(ui): remove outdated comment --- .../SetupGroupProfile/components/PendingGroup/PendingGroup.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx index 737c9a31a8..e1863e1d92 100644 --- a/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx +++ b/src/ui/pages/SetupGroupProfile/components/PendingGroup/PendingGroup.tsx @@ -178,7 +178,6 @@ const PendingGroup = ({ state, isPendingGroup }: StageProps) => { }; const getInceptionStatus = useCallback(async () => { - // We can fetch inception status using gHab id even if groupMetadata is not stored on gHab if (!identity?.id) return; try { From 703448bebaea2f196125da9e281bbf61f32ee55b Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Thu, 6 Nov 2025 17:05:03 +0700 Subject: [PATCH 13/15] refactor(core): streamline profilesCache logic and remove unused setProfileMultisigConnections action --- .../reducers/profileCache/profilesCache.ts | 81 ++++++++----------- .../components/AppWrapper/AppWrapper.test.tsx | 16 +--- 2 files changed, 37 insertions(+), 60 deletions(-) diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index cbe30aba10..f00f3b00c8 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -66,17 +66,40 @@ export const profilesCacheSlice = createSlice({ const existedProfile = state.profiles[action.payload.id]; if (existedProfile) { existedProfile.identity = action.payload; - } else { - state.profiles[action.payload.id] = { - identity: action.payload, - connections: [], - multisigConnections: [], - peerConnections: [], - credentials: [], - archivedCredentials: [], - notifications: [], - }; + return; } + + const groupId = action.payload.groupMetadata?.groupId; + const cachedConnections: MultisigConnectionDetails[] = + groupId && state.multiSigGroup?.groupId === groupId + ? state.multiSigGroup.connections.map((connection) => { + const connectionWithGroup = + connection as MultisigConnectionDetails; + return { + id: connection.id, + label: connection.label, + createdAtUTC: connection.createdAtUTC, + status: connection.status, + logo: connection.logo, + oobi: connection.oobi, + contactId: connection.contactId || connection.id, + groupId, + hasAccepted: connectionWithGroup.hasAccepted, + } as MultisigConnectionDetails; + }) + : []; + + state.profiles[action.payload.id] = { + identity: action.payload, + connections: [], + multisigConnections: cachedConnections, + peerConnections: [], + credentials: [], + archivedCredentials: [], + notifications: [], + }; + + state.multiSigGroup = undefined; }, addGroupProfile: (state, action: PayloadAction) => { if (!action.payload.groupMemberPre) { @@ -375,20 +398,6 @@ export const profilesCacheSlice = createSlice({ targetProfile.multisigConnections = [...existing, mapped]; }, - setProfileMultisigConnections: ( - state, - action: PayloadAction<{ - profileId: string; - connections: MultisigConnectionDetails[]; - }> - ) => { - const { profileId, connections } = action.payload; - const targetProfile = state.profiles[profileId]; - if (targetProfile) { - targetProfile.multisigConnections = connections; - } - }, - // Wallet Connection Actions setConnectedDApp: (state, action: PayloadAction) => { // Store in global state for cross-profile access @@ -449,29 +458,6 @@ export const profilesCacheSlice = createSlice({ export const addGroupProfileAsync = (group: IdentifierShortDetails) => async (dispatch: AppDispatch, getState: () => RootState) => { - const mHabId = group.groupMemberPre; - if (mHabId) { - const allConnections = - await Agent.agent.connections.getMultisigConnections(); - const multisigConnections = allConnections as MultisigConnectionDetails[]; - - const mHab = await Agent.agent.identifiers.getIdentifier(mHabId); - const groupId = mHab?.groupMetadata?.groupId; - - if (groupId) { - const groupConnections = multisigConnections.filter( - (conn) => conn.groupId === groupId - ); - - dispatch( - setProfileMultisigConnections({ - profileId: mHabId, - connections: groupConnections, - }) - ); - } - } - dispatch(addGroupProfile(group)); await Agent.agent.basicStorage.createOrUpdateBasicRecord( @@ -525,7 +511,6 @@ export const { updateOrAddConnectionCache, removeConnectionCache, updateOrAddMultisigConnectionCache, - setProfileMultisigConnections, setOpenConnectionId, setMissingAliasConnection, setConnectedDApp, diff --git a/src/ui/components/AppWrapper/AppWrapper.test.tsx b/src/ui/components/AppWrapper/AppWrapper.test.tsx index 318c4a87a2..d36459f246 100644 --- a/src/ui/components/AppWrapper/AppWrapper.test.tsx +++ b/src/ui/components/AppWrapper/AppWrapper.test.tsx @@ -525,14 +525,8 @@ describe("Group state changed handler", () => { } }); - const mHabWithMetadata = pendingMemberIdentifierFix[0]; - Agent.agent.identifiers.getIdentifier = jest - .fn() - .mockResolvedValue(mHabWithMetadata); - - Agent.agent.connections.getMultisigConnections = jest - .fn() - .mockResolvedValue([]); + Agent.agent.identifiers.getIdentifier = jest.fn(); + Agent.agent.connections.getMultisigConnections = jest.fn(); await groupCreatedHandler(groupCreatedEvent, dispatch); @@ -540,10 +534,8 @@ describe("Group state changed handler", () => { addGroupProfile(pendingGroupIdentifierFix) ); - expect(Agent.agent.connections.getMultisigConnections).toBeCalled(); - expect(Agent.agent.identifiers.getIdentifier).toBeCalledWith( - pendingGroupIdentifierFix.groupMemberPre - ); + expect(Agent.agent.connections.getMultisigConnections).not.toBeCalled(); + expect(Agent.agent.identifiers.getIdentifier).not.toBeCalled(); }); }); From 171722337a9bd7913e59fabd18bd89a388fa4613 Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Fri, 7 Nov 2025 00:19:52 +0700 Subject: [PATCH 14/15] refactor(core): update connection types to use MultisigConnectionDetails for improved type safety --- src/core/agent/agent.types.ts | 2 +- src/core/agent/services/connectionService.ts | 35 ++++++++++++------- .../reducers/profileCache/profilesCache.ts | 20 +++-------- .../profileCache/profilesCache.types.ts | 3 +- .../reducers/stateCache/stateCache.types.ts | 4 +-- .../ConnectionDetails.types.ts | 5 +-- src/ui/pages/Connections/Connections.tsx | 2 -- .../ConnectionsBody/ConnectionsBody.types.ts | 5 +-- src/ui/pages/ProfileSetup/ProfileSetup.tsx | 2 +- 9 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/core/agent/agent.types.ts b/src/core/agent/agent.types.ts index ccefb96587..efcad263bb 100644 --- a/src/core/agent/agent.types.ts +++ b/src/core/agent/agent.types.ts @@ -234,7 +234,7 @@ type OobiScan = | { type: OobiType.MULTI_SIG_INITIATOR; groupId: string; - connection: ConnectionShortDetails; + connection: MultisigConnectionDetails; }; interface AgentServicesProps { diff --git a/src/core/agent/services/connectionService.ts b/src/core/agent/services/connectionService.ts index 25b9161a37..86543f1ad6 100644 --- a/src/core/agent/services/connectionService.ts +++ b/src/core/agent/services/connectionService.ts @@ -164,24 +164,23 @@ class ConnectionService extends AgentService { sharedIdentifier, }; - const connection: ConnectionShortDetails = { - id: connectionId, - createdAtUTC: connectionDate, - oobi: url, - status: ConnectionStatus.PENDING, - label: alias, - contactId: connectionId, - ...(groupId ? { groupId } : { identifier: sharedIdentifier ?? "" }), - }; - if (multiSigInvite) { const oobiResult = (await this.resolveOobi(url)) as { op: Operation & { response: State }; connection: Contact; alias: string; }; - connection.id = oobiResult.op.response.i; - connection.status = ConnectionStatus.CONFIRMED; + + const multisigConnection: MultisigConnectionDetails = { + id: oobiResult.op.response.i, + createdAtUTC: new Date(oobiResult.op.response.dt).toISOString(), + oobi: url, + status: ConnectionStatus.CONFIRMED, + label: alias, + contactId: oobiResult.op.response.i, + groupId, + }; + connectionMetadata.creationStatus = CreationStatus.COMPLETE; connectionMetadata.createdAtUTC = oobiResult.op.response.dt; connectionMetadata.status = ConnectionStatus.CONFIRMED; @@ -209,11 +208,21 @@ class ConnectionService extends AgentService { return { type: OobiType.MULTI_SIG_INITIATOR, groupId, - connection, + connection: multisigConnection, }; } } + const connection: ConnectionShortDetails = { + id: connectionId, + createdAtUTC: connectionDate, + oobi: url, + status: ConnectionStatus.PENDING, + label: alias, + contactId: connectionId, + ...(groupId ? { groupId } : { identifier: sharedIdentifier ?? "" }), + }; + await this.createConnectionMetadata(connectionId, connectionMetadata); if (!multiSigInvite) { diff --git a/src/store/reducers/profileCache/profilesCache.ts b/src/store/reducers/profileCache/profilesCache.ts index f00f3b00c8..076393d539 100644 --- a/src/store/reducers/profileCache/profilesCache.ts +++ b/src/store/reducers/profileCache/profilesCache.ts @@ -72,21 +72,11 @@ export const profilesCacheSlice = createSlice({ const groupId = action.payload.groupMetadata?.groupId; const cachedConnections: MultisigConnectionDetails[] = groupId && state.multiSigGroup?.groupId === groupId - ? state.multiSigGroup.connections.map((connection) => { - const connectionWithGroup = - connection as MultisigConnectionDetails; - return { - id: connection.id, - label: connection.label, - createdAtUTC: connection.createdAtUTC, - status: connection.status, - logo: connection.logo, - oobi: connection.oobi, - contactId: connection.contactId || connection.id, - groupId, - hasAccepted: connectionWithGroup.hasAccepted, - } as MultisigConnectionDetails; - }) + ? state.multiSigGroup.connections.map((connection) => ({ + ...connection, + contactId: connection.contactId || connection.id, + groupId, + })) : []; state.profiles[action.payload.id] = { diff --git a/src/store/reducers/profileCache/profilesCache.types.ts b/src/store/reducers/profileCache/profilesCache.types.ts index ec425a9826..4ac4b0a94b 100644 --- a/src/store/reducers/profileCache/profilesCache.types.ts +++ b/src/store/reducers/profileCache/profilesCache.types.ts @@ -1,5 +1,4 @@ import { - ConnectionShortDetails, MultisigConnectionDetails, RegularConnectionDetails, } from "../../../core/agent/agent.types"; @@ -9,7 +8,7 @@ import { KeriaNotification } from "../../../core/agent/services/keriaNotificatio interface MultiSigGroup { groupId: string; - connections: ConnectionShortDetails[]; + connections: MultisigConnectionDetails[]; } interface DAppConnection { diff --git a/src/store/reducers/stateCache/stateCache.types.ts b/src/store/reducers/stateCache/stateCache.types.ts index fa51c6a1d8..14f4a59746 100644 --- a/src/store/reducers/stateCache/stateCache.types.ts +++ b/src/store/reducers/stateCache/stateCache.types.ts @@ -3,7 +3,7 @@ import { LoginAttempts } from "../../../core/agent/services/auth.types"; import { PeerConnectSigningEvent } from "../../../core/cardano/walletConnect/peerConnection.types"; import { OperationType, ToastMsgType } from "../../../ui/globals/types"; import { DAppConnection } from "../profileCache"; -import { ConnectionShortDetails } from "../../../core/agent/agent.types"; +import { MultisigConnectionDetails } from "../../../core/agent/agent.types"; interface PayloadData { [key: string]: T; @@ -57,7 +57,7 @@ interface PendingJoinGroupMetadata { groupId: string; groupName: string; initiatorName: string | null; - connection: ConnectionShortDetails; + connection: MultisigConnectionDetails; } interface StateCacheProps { diff --git a/src/ui/pages/ConnectionDetails/ConnectionDetails.types.ts b/src/ui/pages/ConnectionDetails/ConnectionDetails.types.ts index 1be42ceefc..8243345e22 100644 --- a/src/ui/pages/ConnectionDetails/ConnectionDetails.types.ts +++ b/src/ui/pages/ConnectionDetails/ConnectionDetails.types.ts @@ -1,7 +1,4 @@ -import { - ConnectionShortDetails, - RegularConnectionDetails, -} from "../../../core/agent/agent.types"; +import { RegularConnectionDetails } from "../../../core/agent/agent.types"; interface ConnectionDetailsProps { connectionShortDetails: RegularConnectionDetails; diff --git a/src/ui/pages/Connections/Connections.tsx b/src/ui/pages/Connections/Connections.tsx index f28495af6c..09c11b5c62 100644 --- a/src/ui/pages/Connections/Connections.tsx +++ b/src/ui/pages/Connections/Connections.tsx @@ -2,7 +2,6 @@ import { IonButton, IonIcon, useIonViewWillEnter } from "@ionic/react"; import { useCallback, useEffect, useState } from "react"; import { Agent } from "../../../core/agent/agent"; import { - ConnectionShortDetails, RegularConnectionDetails, ConnectionStatus, } from "../../../core/agent/agent.types"; @@ -10,7 +9,6 @@ import { i18n } from "../../../i18n"; import { TabsRoutePath } from "../../../routes/paths"; import { useAppDispatch, useAppSelector } from "../../../store/hooks"; import { - getConnectionsCache, getOpenConnectionId, removeConnectionCache, setOpenConnectionId, diff --git a/src/ui/pages/Connections/components/ConnectionsBody/ConnectionsBody.types.ts b/src/ui/pages/Connections/components/ConnectionsBody/ConnectionsBody.types.ts index d6a57563b8..718c85495a 100644 --- a/src/ui/pages/Connections/components/ConnectionsBody/ConnectionsBody.types.ts +++ b/src/ui/pages/Connections/components/ConnectionsBody/ConnectionsBody.types.ts @@ -1,7 +1,4 @@ -import { - ConnectionShortDetails, - RegularConnectionDetails, -} from "../../../../../core/agent/agent.types"; +import { RegularConnectionDetails } from "../../../../../core/agent/agent.types"; import { MappedConnections } from "../../Connections.types"; interface ConnectionsBodyProps { diff --git a/src/ui/pages/ProfileSetup/ProfileSetup.tsx b/src/ui/pages/ProfileSetup/ProfileSetup.tsx index 250f517d14..29d513a7ff 100644 --- a/src/ui/pages/ProfileSetup/ProfileSetup.tsx +++ b/src/ui/pages/ProfileSetup/ProfileSetup.tsx @@ -376,7 +376,7 @@ export const ProfileSetup = ({ () => dispatch(setToastMsg(ToastMsgType.DUPLICATE_GROUP_ID_ERROR)) ); - if (!invitation) return; + if (!invitation || invitation.type !== "MULTI_SIG_INITIATOR") return; // Update Redux state with all metadata, including initiatorName const pendingJoinData = { From 4137cc9402e265f408f1524edd32bea93c32bcbe Mon Sep 17 00:00:00 2001 From: Sotatek-DucPhung Date: Fri, 7 Nov 2025 00:41:56 +0700 Subject: [PATCH 15/15] test(ui): update ProfileSetup tests to utilize MultisigConnectionDetails --- src/ui/pages/ProfileSetup/ProfileSetup.test.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx b/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx index 8256a94060..2dcc042093 100644 --- a/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx +++ b/src/ui/pages/ProfileSetup/ProfileSetup.test.tsx @@ -28,7 +28,7 @@ import { CustomInputProps } from "../../components/CustomInput/CustomInput.types import { makeTestStore } from "../../utils/makeTestStore"; import { ProfileSetup } from "./ProfileSetup"; import { - ConnectionShortDetails, + MultisigConnectionDetails, ConnectionStatus, CreationStatus, OobiType, @@ -42,19 +42,21 @@ jest.mock("signify-ts", () => ({ })), })); -const connection: ConnectionShortDetails = { +const multisigConnection: MultisigConnectionDetails = { id: "ebfeb1ebc6f1c276ef71212ec20", label: "Cambridge University", createdAtUTC: "2017-01-14T19:23:24Z", status: ConnectionStatus.CONFIRMED, groupId: "0AAPHBnxoGK4tDuL4g87Eo9D", contactId: "conn-id-1", + oobi: "http://keria:3902/oobi/test", }; const connectByOobiUrlMock = jest.fn((...arg: unknown[]): Promise => { return Promise.resolve({ - type: OobiType.NORMAL, - connection, + type: OobiType.MULTI_SIG_INITIATOR, + groupId: "0AAPHBnxoGK4tDuL4g87Eo9D", + connection: multisigConnection, }); }); jest.mock("../../../core/agent/agent", () => ({