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 ThreadEntity will be replaced + // with uiName's array of EntityTextComponents at render time + +uiName: EntityText | string, } | { +type: 'thread', @@ -216,7 +218,14 @@ ): 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; + } + return entityTextToRawString(uiName); } invariant( entity.display === 'shortName', @@ -343,22 +352,99 @@ } 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 = []; + 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; + } + uiName.push({ + ...innerEntity, + username: ensName, + }); + } + return { + ...entity, + uiName, + }; + } else { + return entity; + } + }) + .filter(Boolean); } function useENSNamesForEntityText(entityText: ?EntityText): ?EntityText {