diff --git a/lib/selectors/user-selectors.js b/lib/selectors/user-selectors.js --- a/lib/selectors/user-selectors.js +++ b/lib/selectors/user-selectors.js @@ -71,16 +71,22 @@ const username = userInfos[memberInfo.id] ? userInfos[memberInfo.id].username : null; - const { permissions, ...memberInfoSansPermissions } = memberInfo; + const { id, role, isSender, minimallyEncoded } = memberInfo; if (memberInfo.id === currentUserID) { relativeMemberInfos.unshift({ - ...memberInfoSansPermissions, + id, + role, + isSender, + minimallyEncoded, username, isViewer: true, }); } else { relativeMemberInfos.push({ - ...memberInfoSansPermissions, + id, + role, + isSender, + minimallyEncoded, username, isViewer: false, }); diff --git a/lib/shared/redux/legacy-update-roles-and-permissions.js b/lib/shared/redux/legacy-update-roles-and-permissions.js --- a/lib/shared/redux/legacy-update-roles-and-permissions.js +++ b/lib/shared/redux/legacy-update-roles-and-permissions.js @@ -14,6 +14,7 @@ ThreadStoreThreadInfos, LegacyMemberInfo, MixedRawThreadInfos, + ThickMemberInfo, } from '../../types/thread-types.js'; import { values } from '../../utils/objects.js'; @@ -51,6 +52,12 @@ +[member: string]: ?ThreadPermissionsBlob, }; +type BaseMemberInfo = { + +id: string, + +role: ?string, + ... +}; + // This migration utility can only be used with LegacyRawThreadInfos function legacyUpdateRolesAndPermissions( threadStoreInfos: MixedRawThreadInfos, @@ -81,18 +88,19 @@ node.children?.map(recursivelyUpdateRoles); }; - const recursivelyUpdatePermissions = ( - node: $ReadOnly, + const updateMembers = ( + threadInfo: LegacyRawThreadInfo, + members: $ReadOnlyArray<$ReadOnly>, memberToThreadPermissionsFromParent: ?MemberToThreadPermissionsFromParent, - ): void => { - const threadInfo: LegacyRawThreadInfo = - updatedThreadStoreInfos[node.threadID]; - + ): { + members: $ReadOnlyArray<$ReadOnly>, + memberToThreadPermissionsForChildren: { [string]: ?ThreadPermissionsBlob }, + } => { const updatedMembers = []; const memberToThreadPermissionsForChildren: { [string]: ?ThreadPermissionsBlob, } = {}; - for (const member: LegacyMemberInfo of threadInfo.members) { + for (const member of members) { const { id, role } = member; const rolePermissions = role ? threadInfo.roles[role].permissions : null; @@ -116,11 +124,51 @@ memberToThreadPermissionsForChildren[member.id] = makePermissionsForChildrenBlob(computedPermissions); } - - updatedThreadStoreInfos[node.threadID] = { - ...threadInfo, + return { members: updatedMembers, + memberToThreadPermissionsForChildren, }; + }; + + const recursivelyUpdatePermissions = ( + node: $ReadOnly, + memberToThreadPermissionsFromParent: ?MemberToThreadPermissionsFromParent, + ): void => { + const threadInfo: LegacyRawThreadInfo = + updatedThreadStoreInfos[node.threadID]; + + let memberToThreadPermissionsForChildren: { + [string]: ?ThreadPermissionsBlob, + }; + if (threadInfo.thick) { + const { + members: updatedMembers, + memberToThreadPermissionsForChildren: test, + } = updateMembers( + threadInfo, + threadInfo.members, + memberToThreadPermissionsFromParent, + ); + updatedThreadStoreInfos[node.threadID] = { + ...threadInfo, + members: updatedMembers, + }; + memberToThreadPermissionsForChildren = test; + } else { + const { + members: updatedMembers, + memberToThreadPermissionsForChildren: test, + } = updateMembers( + threadInfo, + threadInfo.members, + memberToThreadPermissionsFromParent, + ); + updatedThreadStoreInfos[node.threadID] = { + ...threadInfo, + members: updatedMembers, + }; + memberToThreadPermissionsForChildren = test; + } node.children?.map(child => recursivelyUpdatePermissions(child, memberToThreadPermissionsForChildren), diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -71,12 +71,15 @@ threadTypes, threadTypeIsCommunityRoot, assertThreadType, + threadTypeIsThick, + assertThinThreadType, + assertThickThreadType, } from '../types/thread-types-enum.js'; import type { LegacyRawThreadInfo, ClientLegacyRoleInfo, ServerThreadInfo, - ServerMemberInfo, + ThickMemberInfo, UserProfileThreadInfo, MixedRawThreadInfos, LegacyMemberInfo, @@ -470,38 +473,84 @@ specialRole: specialRoles.DEFAULT_ROLE, }; - const rawThreadInfo: RawThreadInfo = { - minimallyEncoded: true, - id: threadID, - type: threadType, - name: name ?? null, - description: null, - color: threadColor ?? generatePendingThreadColor(memberIDs), - creationTime: now, - parentThreadID: parentThreadInfo?.id ?? null, - containingThreadID: getContainingThreadID(parentThreadInfo, threadType), - community: getCommunity(parentThreadInfo), - members: members.map(member => - minimallyEncodeMemberInfo({ - id: member.id, + let rawThreadInfo: RawThreadInfo; + if (threadTypeIsThick(threadType)) { + const thickThreadType = assertThickThreadType(threadType); + rawThreadInfo = { + minimallyEncoded: true, + thick: true, + id: threadID, + type: thickThreadType, + name: name ?? null, + description: null, + color: threadColor ?? generatePendingThreadColor(memberIDs), + creationTime: now, + parentThreadID: parentThreadInfo?.id ?? null, + containingThreadID: getContainingThreadID( + parentThreadInfo, + thickThreadType, + ), + community: getCommunity(parentThreadInfo), + members: members.map(member => + minimallyEncodeMemberInfo({ + id: member.id, + role: role.id, + permissions: membershipPermissions, + isSender: false, + subscription: defaultSubscription, + }), + ), + roles: { + [role.id]: role, + }, + currentUser: minimallyEncodeThreadCurrentUserInfo({ role: role.id, permissions: membershipPermissions, - isSender: false, + subscription: defaultSubscription, + unread: false, }), - ), - roles: { - [role.id]: role, - }, - currentUser: minimallyEncodeThreadCurrentUserInfo({ - role: role.id, - permissions: membershipPermissions, - subscription: defaultSubscription, - unread: false, - }), - repliesCount: 0, - sourceMessageID, - pinnedCount: 0, - }; + repliesCount: 0, + sourceMessageID, + pinnedCount: 0, + }; + } else { + const thinThreadType = assertThinThreadType(threadType); + rawThreadInfo = { + minimallyEncoded: true, + id: threadID, + type: thinThreadType, + name: name ?? null, + description: null, + color: threadColor ?? generatePendingThreadColor(memberIDs), + creationTime: now, + parentThreadID: parentThreadInfo?.id ?? null, + containingThreadID: getContainingThreadID( + parentThreadInfo, + thinThreadType, + ), + community: getCommunity(parentThreadInfo), + members: members.map(member => + minimallyEncodeMemberInfo({ + id: member.id, + role: role.id, + permissions: membershipPermissions, + isSender: false, + }), + ), + roles: { + [role.id]: role, + }, + currentUser: minimallyEncodeThreadCurrentUserInfo({ + role: role.id, + permissions: membershipPermissions, + subscription: defaultSubscription, + unread: false, + }), + repliesCount: 0, + sourceMessageID, + pinnedCount: 0, + }; + } const userInfos: { [string]: UserInfo } = {}; for (const member of members) { @@ -1024,7 +1073,9 @@ // Since we don't have access to all of the ancestor ThreadInfos, we approximate // "parent admin" as anybody with CHANGE_ROLE permissions. function memberHasAdminPowers( - memberInfo: LegacyMemberInfo | MemberInfoWithPermissions | ServerMemberInfo, + memberInfo: + | { +minimallyEncoded: true, +permissions: string, ... } + | { +minimallyEncoded?: void, +permissions: ThreadPermissionsInfo, ... }, ): boolean { if (memberInfo.minimallyEncoded) { return hasPermission(memberInfo.permissions, threadPermissions.CHANGE_ROLE); diff --git a/lib/shared/updates/delete-account-spec.js b/lib/shared/updates/delete-account-spec.js --- a/lib/shared/updates/delete-account-spec.js +++ b/lib/shared/updates/delete-account-spec.js @@ -25,19 +25,35 @@ const operations = []; for (const threadID in storeThreadInfos) { const threadInfo = storeThreadInfos[threadID]; - const newMembers = threadInfo.members.filter( - member => member.id !== update.deletedUserID, - ); - if (newMembers.length < threadInfo.members.length) { - const updatedThread = { - ...threadInfo, - members: newMembers, - }; + + let replacedThreadInfo; + if (threadInfo.thick) { + const newMembers = threadInfo.members.filter( + member => member.id !== update.deletedUserID, + ); + if (newMembers.length < threadInfo.members.length) { + replacedThreadInfo = { + ...threadInfo, + members: newMembers, + }; + } + } else { + const newMembers = threadInfo.members.filter( + member => member.id !== update.deletedUserID, + ); + if (newMembers.length < threadInfo.members.length) { + replacedThreadInfo = { + ...threadInfo, + members: newMembers, + }; + } + } + if (replacedThreadInfo) { operations.push({ type: 'replace', payload: { - id: threadID, - threadInfo: updatedThread, + id: threadInfo.id, + threadInfo: replacedThreadInfo, }, }); } diff --git a/lib/types/minimally-encoded-thread-permissions-types.js b/lib/types/minimally-encoded-thread-permissions-types.js --- a/lib/types/minimally-encoded-thread-permissions-types.js +++ b/lib/types/minimally-encoded-thread-permissions-types.js @@ -4,12 +4,16 @@ import _mapValues from 'lodash/fp/mapValues.js'; import type { ClientAvatar } from './avatar-types.js'; +import type { ThreadPermissionsInfo } from './thread-permission-types.js'; import type { ThreadType } from './thread-types-enum.js'; import type { LegacyMemberInfo, LegacyRawThreadInfo, + LegacyThinRawThreadInfo, + LegacyThickRawThreadInfo, ClientLegacyRoleInfo, LegacyThreadCurrentUserInfo, + ThickMemberInfo, } from './thread-types.js'; import { decodeThreadRolePermissionsBitmaskArray, @@ -117,9 +121,19 @@ return rest; } -const minimallyEncodeMemberInfo = ( - memberInfo: LegacyMemberInfo, -): MemberInfoWithPermissions => { +export type MinimallyEncodedThickMemberInfo = $ReadOnly<{ + ...ThickMemberInfo, + +minimallyEncoded: true, + +permissions: string, +}>; + +const minimallyEncodeMemberInfo = ( + memberInfo: T, +): $ReadOnly<{ + ...T, + +minimallyEncoded: true, + +permissions: string, +}> => { invariant( !('minimallyEncoded' in memberInfo), 'memberInfo is already minimally encoded.', @@ -131,9 +145,20 @@ }; }; -const decodeMinimallyEncodedMemberInfo = ( - minimallyEncodedMemberInfo: MemberInfoWithPermissions, -): LegacyMemberInfo => { +const decodeMinimallyEncodedMemberInfo = < + T: MemberInfoWithPermissions | MinimallyEncodedThickMemberInfo, +>( + minimallyEncodedMemberInfo: T, +): $ReadOnly<{ + ...$Diff< + T, + { + +minimallyEncoded: true, + +permissions: string, + }, + >, + +permissions: ThreadPermissionsInfo, +}> => { const { minimallyEncoded, ...rest } = minimallyEncodedMemberInfo; return { ...rest, @@ -143,23 +168,24 @@ }; }; -export type RelativeMemberInfo = { - +id: string, - +role: ?string, - +isSender: boolean, +export type ThinRawThreadInfo = $ReadOnly<{ + ...LegacyThinRawThreadInfo, +minimallyEncoded: true, - +username: ?string, - +isViewer: boolean, -}; + +members: $ReadOnlyArray, + +roles: { +[id: string]: RoleInfo }, + +currentUser: ThreadCurrentUserInfo, +}>; -export type RawThreadInfo = $ReadOnly<{ - ...LegacyRawThreadInfo, +export type ThickRawThreadInfo = $ReadOnly<{ + ...LegacyThickRawThreadInfo, +minimallyEncoded: true, - +members: $ReadOnlyArray, + +members: $ReadOnlyArray, +roles: { +[id: string]: RoleInfo }, +currentUser: ThreadCurrentUserInfo, }>; +export type RawThreadInfo = ThinRawThreadInfo | ThickRawThreadInfo; + const minimallyEncodeRawThreadInfo = ( rawThreadInfo: LegacyRawThreadInfo, ): RawThreadInfo => { @@ -167,27 +193,49 @@ !('minimallyEncoded' in rawThreadInfo), 'rawThreadInfo is already minimally encoded.', ); - const { members, roles, currentUser, ...rest } = rawThreadInfo; - return { - ...rest, - minimallyEncoded: true, - members: members.map(minimallyEncodeMemberInfo), - roles: _mapValues(minimallyEncodeRoleInfo)(roles), - currentUser: minimallyEncodeThreadCurrentUserInfo(currentUser), - }; + if (rawThreadInfo.thick) { + const { members, roles, currentUser, ...rest } = rawThreadInfo; + return { + ...rest, + minimallyEncoded: true, + members: members.map(minimallyEncodeMemberInfo), + roles: _mapValues(minimallyEncodeRoleInfo)(roles), + currentUser: minimallyEncodeThreadCurrentUserInfo(currentUser), + }; + } else { + const { members, roles, currentUser, ...rest } = rawThreadInfo; + return { + ...rest, + minimallyEncoded: true, + members: members.map(minimallyEncodeMemberInfo), + roles: _mapValues(minimallyEncodeRoleInfo)(roles), + currentUser: minimallyEncodeThreadCurrentUserInfo(currentUser), + }; + } }; const decodeMinimallyEncodedRawThreadInfo = ( minimallyEncodedRawThreadInfo: RawThreadInfo, ): LegacyRawThreadInfo => { - const { minimallyEncoded, members, roles, currentUser, ...rest } = - minimallyEncodedRawThreadInfo; - return { - ...rest, - members: members.map(decodeMinimallyEncodedMemberInfo), - roles: _mapValues(decodeMinimallyEncodedRoleInfo)(roles), - currentUser: decodeMinimallyEncodedThreadCurrentUserInfo(currentUser), - }; + if (minimallyEncodedRawThreadInfo.thick) { + const { minimallyEncoded, members, roles, currentUser, ...rest } = + minimallyEncodedRawThreadInfo; + return { + ...rest, + members: members.map(decodeMinimallyEncodedMemberInfo), + roles: _mapValues(decodeMinimallyEncodedRoleInfo)(roles), + currentUser: decodeMinimallyEncodedThreadCurrentUserInfo(currentUser), + }; + } else { + const { minimallyEncoded, members, roles, currentUser, ...rest } = + minimallyEncodedRawThreadInfo; + return { + ...rest, + members: members.map(decodeMinimallyEncodedMemberInfo), + roles: _mapValues(decodeMinimallyEncodedRoleInfo)(roles), + currentUser: decodeMinimallyEncodedThreadCurrentUserInfo(currentUser), + }; + } }; export type RoleInfoWithoutSpecialRole = $ReadOnly<{ @@ -200,6 +248,15 @@ +roles: { +[id: string]: RoleInfoWithoutSpecialRole }, }>; +export type RelativeMemberInfo = { + +id: string, + +role: ?string, + +isSender: boolean, + +minimallyEncoded: true, + +username: ?string, + +isViewer: boolean, +}; + export type ThreadInfo = $ReadOnly<{ +minimallyEncoded: true, +id: string, diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -30,7 +30,12 @@ threadRolePermissionsBlobValidator, type UserSurfacedPermission, } from './thread-permission-types.js'; -import { type ThreadType, threadTypeValidator } from './thread-types-enum.js'; +import { + type ThreadType, + type ThinThreadType, + type ThickThreadType, + threadTypeValidator, +} from './thread-types-enum.js'; import type { ClientUpdateInfo, ServerUpdateInfo } from './update-types.js'; import type { UserInfo, UserInfos } from './user-types.js'; import type { SpecialRole } from '../permissions/special-roles.js'; @@ -87,9 +92,9 @@ unread: t.maybe(t.Boolean), }); -export type LegacyRawThreadInfo = { +export type LegacyThinRawThreadInfo = { +id: string, - +type: ThreadType, + +type: ThinThreadType, +name: ?string, +avatar?: ?ClientAvatar, +description: ?string, @@ -105,6 +110,39 @@ +repliesCount: number, +pinnedCount?: number, }; + +export type ThickMemberInfo = { + +id: string, + +role: ?string, + +permissions: ThreadPermissionsInfo, + +subscription: ThreadSubscription, + +isSender: boolean, +}; + +export type LegacyThickRawThreadInfo = { + +thick: true, + +id: string, + +type: ThickThreadType, + +name: ?string, + +avatar?: ?ClientAvatar, + +description: ?string, + +color: string, // hex, without "#" or "0x" + +creationTime: number, // millisecond timestamp + +parentThreadID: ?string, + +containingThreadID: ?string, + +community: ?string, + +members: $ReadOnlyArray, + +roles: { +[id: string]: ClientLegacyRoleInfo }, + +currentUser: LegacyThreadCurrentUserInfo, + +sourceMessageID?: string, + +repliesCount: number, + +pinnedCount?: number, +}; + +export type LegacyRawThreadInfo = + | LegacyThinRawThreadInfo + | LegacyThickRawThreadInfo; + export type LegacyRawThreadInfos = { +[id: string]: LegacyRawThreadInfo, }; diff --git a/lib/utils/thread-ops-utils.js b/lib/utils/thread-ops-utils.js --- a/lib/utils/thread-ops-utils.js +++ b/lib/utils/thread-ops-utils.js @@ -8,7 +8,6 @@ threadCurrentUserInfoValidator, } from '../permissions/minimally-encoded-raw-thread-info-validators.js'; import type { - MemberInfoWithPermissions, RawThreadInfo, RoleInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; @@ -18,7 +17,12 @@ minimallyEncodeRoleInfo, minimallyEncodeThreadCurrentUserInfo, } from '../types/minimally-encoded-thread-permissions-types.js'; -import { assertThreadType } from '../types/thread-types-enum.js'; +import { + assertThreadType, + threadTypeIsThick, + assertThinThreadType, + assertThickThreadType, +} from '../types/thread-types-enum.js'; import { type ClientDBThreadInfo, legacyMemberInfoValidator, @@ -30,7 +34,7 @@ function convertRawThreadInfoToClientDBThreadInfo( rawThreadInfo: LegacyRawThreadInfo | RawThreadInfo, ): ClientDBThreadInfo { - const { minimallyEncoded, ...rest } = rawThreadInfo; + const { minimallyEncoded, thick, ...rest } = rawThreadInfo; return { ...rest, creationTime: rawThreadInfo.creationTime.toString(), @@ -46,17 +50,18 @@ ): RawThreadInfo { // 1. Validate and potentially minimally encode `rawMembers`. const rawMembers = JSON.parse(clientDBThreadInfo.members); - const minimallyEncodedMembers: $ReadOnlyArray = - rawMembers.map(rawMember => { - invariant( - memberInfoWithPermissionsValidator.is(rawMember) || - legacyMemberInfoValidator.is(rawMember), - 'rawMember must be valid [MinimallyEncoded/Legacy]MemberInfo', - ); - return rawMember.minimallyEncoded - ? rawMember - : minimallyEncodeMemberInfo(rawMember); - }); + const minimallyEncodedMembers = rawMembers.map(rawMember => { + invariant( + // TODO these must be updated to accept new client-only change + // that subscription field may be present + memberInfoWithPermissionsValidator.is(rawMember) || + legacyMemberInfoValidator.is(rawMember), + 'rawMember must be valid [MinimallyEncoded/Legacy]MemberInfo', + ); + return rawMember.minimallyEncoded + ? rawMember + : minimallyEncodeMemberInfo(rawMember); + }); // 2. Validate and potentially minimally encode `rawRoles`. const rawRoles = JSON.parse(clientDBThreadInfo.roles); @@ -86,23 +91,48 @@ ? rawCurrentUser : minimallyEncodeThreadCurrentUserInfo(rawCurrentUser); - let rawThreadInfo: RawThreadInfo = { - minimallyEncoded: true, - id: clientDBThreadInfo.id, - type: assertThreadType(clientDBThreadInfo.type), - name: clientDBThreadInfo.name, - description: clientDBThreadInfo.description, - color: clientDBThreadInfo.color, - creationTime: Number(clientDBThreadInfo.creationTime), - parentThreadID: clientDBThreadInfo.parentThreadID, - containingThreadID: clientDBThreadInfo.containingThreadID, - community: clientDBThreadInfo.community, - members: minimallyEncodedMembers, - roles: minimallyEncodedRoles, - currentUser: minimallyEncodedCurrentUser, - repliesCount: clientDBThreadInfo.repliesCount, - pinnedCount: clientDBThreadInfo.pinnedCount, - }; + let rawThreadInfo: RawThreadInfo; + const threadType = assertThreadType(clientDBThreadInfo.type); + if (threadTypeIsThick(threadType)) { + const thickThreadType = assertThickThreadType(threadType); + rawThreadInfo = { + minimallyEncoded: true, + thick: true, + id: clientDBThreadInfo.id, + type: thickThreadType, + name: clientDBThreadInfo.name, + description: clientDBThreadInfo.description, + color: clientDBThreadInfo.color, + creationTime: Number(clientDBThreadInfo.creationTime), + parentThreadID: clientDBThreadInfo.parentThreadID, + containingThreadID: clientDBThreadInfo.containingThreadID, + community: clientDBThreadInfo.community, + members: minimallyEncodedMembers, + roles: minimallyEncodedRoles, + currentUser: minimallyEncodedCurrentUser, + repliesCount: clientDBThreadInfo.repliesCount, + pinnedCount: clientDBThreadInfo.pinnedCount, + }; + } else { + const thinThreadType = assertThinThreadType(threadType); + rawThreadInfo = { + minimallyEncoded: true, + id: clientDBThreadInfo.id, + type: thinThreadType, + name: clientDBThreadInfo.name, + description: clientDBThreadInfo.description, + color: clientDBThreadInfo.color, + creationTime: Number(clientDBThreadInfo.creationTime), + parentThreadID: clientDBThreadInfo.parentThreadID, + containingThreadID: clientDBThreadInfo.containingThreadID, + community: clientDBThreadInfo.community, + members: minimallyEncodedMembers, + roles: minimallyEncodedRoles, + currentUser: minimallyEncodedCurrentUser, + repliesCount: clientDBThreadInfo.repliesCount, + pinnedCount: clientDBThreadInfo.pinnedCount, + }; + } if (clientDBThreadInfo.sourceMessageID) { rawThreadInfo = { diff --git a/native/redux/edit-thread-permission-migration.js b/native/redux/edit-thread-permission-migration.js --- a/native/redux/edit-thread-permission-migration.js +++ b/native/redux/edit-thread-permission-migration.js @@ -7,10 +7,11 @@ ClientLegacyRoleInfo, LegacyRawThreadInfos, LegacyThreadCurrentUserInfo, + ThickMemberInfo, } from 'lib/types/thread-types.js'; function addDetailedThreadEditPermissionsToUser< - T: LegacyMemberInfo | LegacyThreadCurrentUserInfo, + T: LegacyMemberInfo | LegacyThreadCurrentUserInfo | ThickMemberInfo, >(threadInfo: LegacyRawThreadInfo, member: T, threadID: string): T { let newPermissions = null; if (threadInfo.type === threadTypes.GENESIS_PRIVATE) { @@ -64,10 +65,6 @@ const newThreadInfos: { [string]: LegacyRawThreadInfo } = {}; for (const threadID in threadInfos) { const threadInfo: LegacyRawThreadInfo = threadInfos[threadID]; - const updatedMembers = threadInfo.members.map(member => - addDetailedThreadEditPermissionsToUser(threadInfo, member, threadID), - ); - const updatedCurrentUser = addDetailedThreadEditPermissionsToUser( threadInfo, threadInfo.currentUser, @@ -82,13 +79,29 @@ ); } - const newThreadInfo = { - ...threadInfo, - members: updatedMembers, - currentUser: updatedCurrentUser, - roles: updatedRoles, - }; - newThreadInfos[threadID] = newThreadInfo; + if (threadInfo.thick) { + const updatedMembers = threadInfo.members.map(member => + addDetailedThreadEditPermissionsToUser(threadInfo, member, threadID), + ); + const newThreadInfo = { + ...threadInfo, + members: updatedMembers, + currentUser: updatedCurrentUser, + roles: updatedRoles, + }; + newThreadInfos[threadID] = newThreadInfo; + } else { + const updatedMembers = threadInfo.members.map(member => + addDetailedThreadEditPermissionsToUser(threadInfo, member, threadID), + ); + const newThreadInfo = { + ...threadInfo, + members: updatedMembers, + currentUser: updatedCurrentUser, + roles: updatedRoles, + }; + newThreadInfos[threadID] = newThreadInfo; + } } return newThreadInfos; } diff --git a/native/redux/manage-pins-permission-migration.js b/native/redux/manage-pins-permission-migration.js --- a/native/redux/manage-pins-permission-migration.js +++ b/native/redux/manage-pins-permission-migration.js @@ -6,6 +6,7 @@ LegacyThreadCurrentUserInfo, ClientLegacyRoleInfo, LegacyRawThreadInfos, + ThickMemberInfo, } from 'lib/types/thread-types.js'; type ThreadStoreThreadInfos = LegacyRawThreadInfos; @@ -13,7 +14,10 @@ const adminRoleName = 'Admins'; function addManagePinsThreadPermissionToUser< - TargetMemberInfo: LegacyMemberInfo | LegacyThreadCurrentUserInfo, + TargetMemberInfo: + | LegacyMemberInfo + | LegacyThreadCurrentUserInfo + | ThickMemberInfo, >( threadInfo: LegacyRawThreadInfo, member: TargetMemberInfo, @@ -62,10 +66,6 @@ const newThreadInfos: { [string]: LegacyRawThreadInfo } = {}; for (const threadID in threadInfos) { const threadInfo: LegacyRawThreadInfo = threadInfos[threadID]; - const updatedMembers = threadInfo.members.map(member => - addManagePinsThreadPermissionToUser(threadInfo, member, threadID), - ); - const updatedCurrentUser = addManagePinsThreadPermissionToUser( threadInfo, threadInfo.currentUser, @@ -79,13 +79,29 @@ ); } - const updatedThreadInfo = { - ...threadInfo, - members: updatedMembers, - currentUser: updatedCurrentUser, - roles: updatedRoles, - }; - newThreadInfos[threadID] = updatedThreadInfo; + if (threadInfo.thick) { + const updatedMembers = threadInfo.members.map(member => + addManagePinsThreadPermissionToUser(threadInfo, member, threadID), + ); + const updatedThreadInfo = { + ...threadInfo, + members: updatedMembers, + currentUser: updatedCurrentUser, + roles: updatedRoles, + }; + newThreadInfos[threadID] = updatedThreadInfo; + } else { + const updatedMembers = threadInfo.members.map(member => + addManagePinsThreadPermissionToUser(threadInfo, member, threadID), + ); + const updatedThreadInfo = { + ...threadInfo, + members: updatedMembers, + currentUser: updatedCurrentUser, + roles: updatedRoles, + }; + newThreadInfos[threadID] = updatedThreadInfo; + } } return newThreadInfos; }