From 0d22280358fe3a6727b5b4a8400d75e1d034f570 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 22 Aug 2025 09:53:21 +0000 Subject: [PATCH] core: forward member name; ui: don't show "Member" label if unknown member was created with name (#6211) --- apps/ios/SimpleXChat/ChatTypes.swift | 16 +++++++++----- .../chat/simplex/common/model/ChatModel.kt | 20 +++++++++++------ .../commonMain/resources/MR/base/strings.xml | 2 +- src/Simplex/Chat/Library/Internal.hs | 8 ++++++- src/Simplex/Chat/Library/Subscriber.hs | 22 ++++++++++--------- src/Simplex/Chat/Protocol.hs | 6 ++--- 6 files changed, 47 insertions(+), 27 deletions(-) diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 0e4b63a2e0..aed5373664 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2522,7 +2522,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { get { let p = memberProfile let name = p.localAlias == "" ? p.displayName : p.localAlias - return pastMember(name) + return unknownMember(name) } } public var fullName: String { get { memberProfile.fullName } } @@ -2549,13 +2549,19 @@ public struct GroupMember: Identifiable, Decodable, Hashable { ? p.displayName + (p.fullName == "" || p.fullName == p.displayName ? "" : " / \(p.fullName)") : p.localAlias ) - return pastMember(name) + return unknownMember(name) } } - private func pastMember(_ name: String) -> String { + private func unknownMember(_ name: String) -> String { memberStatus == .memUnknown - ? String.localizedStringWithFormat(NSLocalizedString("Past member %@", comment: "past/unknown group member"), name) + ? ( + memberId.hasPrefix(name) + // unknown member was created using memberId for name + ? String.localizedStringWithFormat(NSLocalizedString("Member %@", comment: "past/unknown group member"), name) + // unknown member was created with name + : name + ) : name } @@ -2565,7 +2571,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { let fullName = p.displayName + (p.fullName == "" || p.fullName == p.displayName ? "" : " / \(p.fullName)") let name = p.localAlias == "" ? fullName : "\(p.localAlias) (\(fullName))" - return pastMember(name) + return unknownMember(name) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index c00507c0a3..047d14d9f1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2247,7 +2247,7 @@ data class GroupMember ( get() { val p = memberProfile val name = p.localAlias.ifEmpty { p.displayName } - return pastMember(name) + return unknownMember(name) } override val fullName: String get() = memberProfile.fullName override val shortDescr: String? get() = memberProfile.shortDescr @@ -2262,13 +2262,19 @@ data class GroupMember ( get() { val p = memberProfile val name = p.localAlias.ifEmpty { p.displayName + (if (p.fullName == "" || p.fullName == p.displayName) "" else " / ${p.fullName}") } - return pastMember(name) + return unknownMember(name) } - private fun pastMember(name: String): String { - return if (memberStatus == GroupMemberStatus.MemUnknown) - String.format(generalGetString(MR.strings.past_member_vName), name) - else + private fun unknownMember(name: String): String { + return if (memberStatus == GroupMemberStatus.MemUnknown) { + if (memberId.startsWith(name)) { + // unknown member was created using memberId for name + String.format(generalGetString(MR.strings.past_member_vName), name) + } else { + // unknown member was created with name + name + } + } else name } @@ -2282,7 +2288,7 @@ data class GroupMember ( } else { fullName } - return pastMember(name) + return unknownMember(name) } val memberActive: Boolean get() = when (this.memberStatus) { diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index b1a9e39157..151a2019be 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1760,7 +1760,7 @@ connecting unknown - Past member %1$s + Member %1$s No contacts to add diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index f1fdacb114..413a36f567 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -1196,9 +1196,15 @@ sendHistory user gInfo@GroupInfo {groupId, membership} m@GroupMember {activeConn _ -> pure [] let fileDescrChatMsgs = map (ChatMessage senderVRange Nothing) fileDescrEvents GroupMember {memberId} = sender - msgForwardEvents = map (\cm -> XGrpMsgForward memberId cm itemTs) (xMsgNewChatMsg : fileDescrChatMsgs) + memberName = Just $ memberShortenedName sender + msgForwardEvents = map (\cm -> XGrpMsgForward memberId memberName cm itemTs) (xMsgNewChatMsg : fileDescrChatMsgs) pure msgForwardEvents +memberShortenedName :: GroupMember -> ContactName +memberShortenedName GroupMember {memberProfile = LocalProfile {displayName}} + | T.length displayName <= 16 = displayName + | otherwise = T.take 16 displayName `T.snoc` '…' + splitFileDescr :: Int -> RcvFileDescrText -> NonEmpty FileDescr splitFileDescr partSize rfdText = splitParts 1 rfdText where diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index e911dc8d03..3517f76152 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -974,7 +974,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = XGrpPrefs ps' -> (,False) <$> xGrpPrefs gInfo' m'' ps' -- TODO [knocking] why don't we forward these messages? XGrpDirectInv connReq mContent_ msgScope -> memberCanSend m'' msgScope $ (Nothing, False) <$ xGrpDirectInv gInfo' m'' conn' connReq mContent_ msg brokerTs - XGrpMsgForward memberId msg' msgTs -> (Nothing, False) <$ xGrpMsgForward gInfo' m'' memberId msg' msgTs + XGrpMsgForward memberId memberName msg' msgTs -> (Nothing, False) <$ xGrpMsgForward gInfo' m'' memberId memberName msg' msgTs XInfoProbe probe -> (Nothing, False) <$ xInfoProbe (COMGroupMember m'') probe XInfoProbeCheck probeHash -> (Nothing, False) <$ xInfoProbeCheck (COMGroupMember m'') probeHash XInfoProbeOk probe -> (Nothing, False) <$ xInfoProbeOk (COMGroupMember m'') probe @@ -1001,7 +1001,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = forwardMsgs groupForwardScope fwdMsgs = do ms <- buildMemberList let GroupMember {memberId} = m - events = L.map (\cm -> XGrpMsgForward memberId cm brokerTs) fwdMsgs + memberName = Just $ memberShortenedName m + events = L.map (\cm -> XGrpMsgForward memberId memberName cm brokerTs) fwdMsgs unless (null ms) $ void $ sendGroupMessages_ user gInfo ms events where buildMemberList = case groupForwardScope of @@ -2924,7 +2925,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = toView CEvtMemberBlockedForAll {user, groupInfo = gInfo', byMember = m', member = bm', blocked} pure $ memberEventForwardScope bm Left (SEGroupMemberNotFoundByMemberId _) -> do - bm <- createUnknownMember gInfo memId + bm <- createUnknownMember gInfo memId Nothing bm' <- setMemberBlocked bm toView $ CEvtUnknownMemberBlocked user gInfo m bm' pure $ Just GFSMain @@ -3023,7 +3024,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = forwardToMember :: GroupMember -> CM () forwardToMember member = do let GroupMember {memberId} = m - event = XGrpMsgForward memberId chatMsg brokerTs + memberName = Just $ memberShortenedName m + event = XGrpMsgForward memberId memberName chatMsg brokerTs sendGroupMemberMessage gInfo member event Nothing (pure ()) isUserGrpFwdRelay :: GroupInfo -> Bool @@ -3181,13 +3183,13 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = toViewTE $ TEContactVerificationReset user ct createInternalChatItem user (CDDirectRcv ct) (CIRcvConnEvent RCEVerificationCodeReset) Nothing - xGrpMsgForward :: GroupInfo -> GroupMember -> MemberId -> ChatMessage 'Json -> UTCTime -> CM () - xGrpMsgForward gInfo@GroupInfo {groupId} m@GroupMember {memberRole, localDisplayName} memberId chatMsg msgTs = do + xGrpMsgForward :: GroupInfo -> GroupMember -> MemberId -> Maybe ContactName -> ChatMessage 'Json -> UTCTime -> CM () + xGrpMsgForward gInfo@GroupInfo {groupId} m@GroupMember {memberRole, localDisplayName} memberId memberName chatMsg msgTs = do when (memberRole < GRAdmin) $ throwChatError (CEGroupContactRole localDisplayName) withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memberId) >>= \case Right author -> processForwardedMsg author Left (SEGroupMemberNotFoundByMemberId _) -> do - unknownAuthor <- createUnknownMember gInfo memberId + unknownAuthor <- createUnknownMember gInfo memberId memberName toView $ CEvtUnknownMemberCreated user gInfo m unknownAuthor processForwardedMsg unknownAuthor Left e -> throwError $ ChatErrorStore e @@ -3216,9 +3218,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = XGrpPrefs ps' -> void $ xGrpPrefs gInfo author ps' _ -> messageError $ "x.grp.msg.forward: unsupported forwarded event " <> T.pack (show $ toCMEventTag event) - createUnknownMember :: GroupInfo -> MemberId -> CM GroupMember - createUnknownMember gInfo memberId = do - let name = nameFromMemberId memberId + createUnknownMember :: GroupInfo -> MemberId -> Maybe ContactName -> CM GroupMember + createUnknownMember gInfo memberId memberName = do + let name = fromMaybe (nameFromMemberId memberId) memberName withStore $ \db -> createNewUnknownGroupMember db vr user gInfo memberId name directMsgReceived :: Contact -> Connection -> MsgMeta -> NonEmpty MsgReceipt -> CM () diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 165d376814..d6b826491e 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -348,7 +348,7 @@ data ChatMsgEvent (e :: MsgEncoding) where XGrpInfo :: GroupProfile -> ChatMsgEvent 'Json XGrpPrefs :: GroupPreferences -> ChatMsgEvent 'Json XGrpDirectInv :: ConnReqInvitation -> Maybe MsgContent -> Maybe MsgScope -> ChatMsgEvent 'Json - XGrpMsgForward :: MemberId -> ChatMessage 'Json -> UTCTime -> ChatMsgEvent 'Json + XGrpMsgForward :: MemberId -> Maybe ContactName -> ChatMessage 'Json -> UTCTime -> ChatMsgEvent 'Json XInfoProbe :: Probe -> ChatMsgEvent 'Json XInfoProbeCheck :: ProbeHash -> ChatMsgEvent 'Json XInfoProbeOk :: Probe -> ChatMsgEvent 'Json @@ -1136,7 +1136,7 @@ appJsonToCM AppMessageJson {v, msgId, event, params} = do XGrpInfo_ -> XGrpInfo <$> p "groupProfile" XGrpPrefs_ -> XGrpPrefs <$> p "groupPreferences" XGrpDirectInv_ -> XGrpDirectInv <$> p "connReq" <*> opt "content" <*> opt "scope" - XGrpMsgForward_ -> XGrpMsgForward <$> p "memberId" <*> p "msg" <*> p "msgTs" + XGrpMsgForward_ -> XGrpMsgForward <$> p "memberId" <*> opt "memberName" <*> p "msg" <*> p "msgTs" XInfoProbe_ -> XInfoProbe <$> p "probe" XInfoProbeCheck_ -> XInfoProbeCheck <$> p "probeHash" XInfoProbeOk_ -> XInfoProbeOk <$> p "probe" @@ -1195,7 +1195,7 @@ chatToAppMessage chatMsg@ChatMessage {chatVRange, msgId, chatMsgEvent} = case en XGrpInfo p -> o ["groupProfile" .= p] XGrpPrefs p -> o ["groupPreferences" .= p] XGrpDirectInv connReq content scope -> o $ ("content" .=? content) $ ("scope" .=? scope) ["connReq" .= connReq] - XGrpMsgForward memberId msg msgTs -> o ["memberId" .= memberId, "msg" .= msg, "msgTs" .= msgTs] + XGrpMsgForward memberId memberName msg msgTs -> o $ ("memberName" .=? memberName) ["memberId" .= memberId, "msg" .= msg, "msgTs" .= msgTs] XInfoProbe probe -> o ["probe" .= probe] XInfoProbeCheck probeHash -> o ["probeHash" .= probeHash] XInfoProbeOk probe -> o ["probe" .= probe]