diff --git a/lib/utils/entity-text.js b/lib/utils/entity-text.js --- a/lib/utils/entity-text.js +++ b/lib/utils/entity-text.js @@ -31,7 +31,9 @@ +name?: ?string, // displays threadInfo.name if set, or 'user1, user2, and user3' +display: 'uiName', - +uiName: string, + // If uiName is EntityText, then at render time ThreadEntity will be + // replaced with a pluralized list of uiName's UserEntities + +uiName: $ReadOnlyArray | string, } | { +type: 'thread', @@ -212,11 +214,25 @@ function getNameForThreadEntity( entity: ThreadEntity, threadID: ?string, - params?: EntityTextToRawStringParams, + params?: ?EntityTextToRawStringParams, ): string { const { name: userGeneratedName, display } = entity; if (entity.display === 'uiName') { - return userGeneratedName ? userGeneratedName : entity.uiName; + if (userGeneratedName) { + return userGeneratedName; + } + const { uiName } = entity; + if (typeof uiName === 'string') { + return uiName; + } + let userEntities = uiName; + if (!params?.ignoreViewer) { + userEntities = userEntities.filter(innerEntity => !innerEntity.isViewer); + } + const pluralized = pluralizeEntityText( + userEntities.map(innerEntity => [innerEntity]), + ); + return entityTextToRawString(pluralized, params); } invariant( entity.display === 'shortName', @@ -261,7 +277,7 @@ }; function entityTextToRawString( entityText: EntityText, - params?: EntityTextToRawStringParams, + params?: ?EntityTextToRawStringParams, ): string { const textParts = entityText.map(entity => { if (typeof entity === 'string') { @@ -343,22 +359,104 @@ } type TextEntity = { +type: 'text', +text: string }; +type ShadowUserEntity = { + +type: 'shadowUser', + +username: string, + +originalUsername: string, +}; type EntityTextComponentAsObject = | UserEntity | ThreadEntity | ColorEntity - | TextEntity; + | TextEntity + | ShadowUserEntity; function entityTextToObjects( entityText: EntityText, ): EntityTextComponentAsObject[] { - return entityText.map(entity => - typeof entity === 'string' ? { type: 'text', text: entity } : entity, - ); + const objs = []; + for (const entity of entityText) { + if (typeof entity === 'string') { + objs.push({ type: 'text', text: entity }); + continue; + } + objs.push(entity); + if ( + entity.type === 'thread' && + entity.display === 'uiName' && + typeof entity.uiName !== 'string' + ) { + for (const innerEntity of entity.uiName) { + if (typeof innerEntity === 'string' || innerEntity.type !== 'user') { + continue; + } + const { username } = innerEntity; + if (username) { + objs.push({ + type: 'shadowUser', + originalUsername: username, + username, + }); + } + } + } + } + return objs; } function entityTextFromObjects( objects: $ReadOnlyArray, ): EntityText { - return objects.map(entity => (entity.type === 'text' ? entity.text : entity)); + const shadowUserMap = new Map(); + for (const obj of objects) { + if (obj.type === 'shadowUser' && obj.username !== obj.originalUsername) { + shadowUserMap.set(obj.originalUsername, obj.username); + } + } + return objects + .map(entity => { + if (entity.type === 'text') { + return entity.text; + } else if (entity.type === 'shadowUser') { + return null; + } else if ( + entity.type === 'thread' && + entity.display === 'uiName' && + typeof entity.uiName !== 'string' + ) { + const uiName = []; + let changeOccurred = false; + for (const innerEntity of entity.uiName) { + if (typeof innerEntity === 'string' || innerEntity.type !== 'user') { + uiName.push(innerEntity); + continue; + } + const { username } = innerEntity; + if (!username) { + uiName.push(innerEntity); + continue; + } + const ensName = shadowUserMap.get(username); + if (!ensName) { + uiName.push(innerEntity); + continue; + } + changeOccurred = true; + uiName.push({ + ...innerEntity, + username: ensName, + }); + } + if (!changeOccurred) { + return entity; + } + return { + ...entity, + uiName, + }; + } else { + return entity; + } + }) + .filter(Boolean); } function useENSNamesForEntityText(entityText: ?EntityText): ?EntityText {