From 3af2848275dc9eda910b59280dfd81c7e9525116 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 26 May 2023 15:19:20 +0400 Subject: [PATCH 1/3] ios: fix database view crashing when in Japanese (#2516) --- .../SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff | 2 +- apps/ios/ja.lproj/Localizable.strings | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index 39d331773a..18d8359928 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -154,7 +154,7 @@ %lld file(s) with total size of %@ - 合計サイズ %@ の %lld 件のファイル + %lld 個のファイル(合計サイズ: %@) No comment provided by engineer. diff --git a/apps/ios/ja.lproj/Localizable.strings b/apps/ios/ja.lproj/Localizable.strings index 0b0407856c..255d5f4654 100644 --- a/apps/ios/ja.lproj/Localizable.strings +++ b/apps/ios/ja.lproj/Localizable.strings @@ -152,7 +152,7 @@ "%lld contact(s) selected" = "%lld 件の連絡先が選択されました"; /* No comment provided by engineer. */ -"%lld file(s) with total size of %@" = "合計サイズ %@ の %lld 件のファイル"; +"%lld file(s) with total size of %@" = "%lld 個のファイル(合計サイズ: %@)"; /* No comment provided by engineer. */ "%lld members" = "%lld 人のメンバー"; From 42d4f94fecfc3f5222e6f79b2fb715cf98f54ab1 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 26 May 2023 15:49:26 +0400 Subject: [PATCH 2/3] mobile: always show chat list search (#2517) --- .../app/views/chatlist/ChatListView.kt | 2 +- .../Shared/Views/ChatList/ChatListView.swift | 40 +++++++++++-------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt index 6104f7e6b0..07c859e508 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt @@ -176,7 +176,7 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user BackHandler(onBack = hideSearchOnBack) } val barButtons = arrayListOf<@Composable RowScope.() -> Unit>() - if (chatModel.chats.size >= 8) { + if (chatModel.chats.size > 0) { barButtons.add { IconButton({ showSearch = true }) { Icon(painterResource(R.drawable.ic_search_500), stringResource(android.R.string.search_go).capitalize(Locale.current), tint = MaterialTheme.colors.primary) diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 296bcced21..c372acdd24 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -29,11 +29,7 @@ struct ChatListView: View { if chatModel.chats.isEmpty { onboardingButtons() } - if chatModel.chats.count > 8 { - chatList.searchable(text: $searchText) - } else { - chatList - } + chatListView } } if userPickerVisible { @@ -47,18 +43,12 @@ struct ChatListView: View { } } - var chatList: some View { - List { - ForEach(filteredChats(), id: \.viewId) { chat in - ChatListNavLink(chat: chat) - .padding(.trailing, -16) - .disabled(chatModel.chatRunning != true) - } - } - .onChange(of: chatModel.chatId) { _ in - if chatModel.chatId == nil, let chatId = chatModel.chatToTop { - chatModel.chatToTop = nil - chatModel.popChat(chatId) + private var chatListView: some View { + VStack { + if chatModel.chats.count > 0 { + chatList.searchable(text: $searchText) + } else { + chatList } } .onChange(of: chatModel.appOpenUrl) { _ in connectViaUrl() } @@ -116,6 +106,22 @@ struct ChatListView: View { } } + private var chatList: some View { + List { + ForEach(filteredChats(), id: \.viewId) { chat in + ChatListNavLink(chat: chat) + .padding(.trailing, -16) + .disabled(chatModel.chatRunning != true) + } + } + .onChange(of: chatModel.chatId) { _ in + if chatModel.chatId == nil, let chatId = chatModel.chatToTop { + chatModel.chatToTop = nil + chatModel.popChat(chatId) + } + } + } + private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View { Circle() .frame(width: size, height: size) From bd069aea499e729132a2e325600f4d5f88c11b56 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Fri, 26 May 2023 17:36:06 +0400 Subject: [PATCH 3/3] core: add debug info to CEInvalidChatMessage (#2518) --- src/Simplex/Chat.hs | 34 ++++++++++++++++++---------------- src/Simplex/Chat/Controller.hs | 2 +- src/Simplex/Chat/View.hs | 7 ++++++- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 16596b1073..74cf584a4f 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -2738,7 +2738,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do _ -> messageError $ "unsupported message: " <> T.pack (show event) CONF confId _ connInfo -> do -- confirming direct connection with a member - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of XGrpMemInfo _memId _memProfile -> do -- TODO check member ID @@ -2747,7 +2747,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do allowAgentConnectionAsync user conn confId XOk _ -> messageError "CONF from member must have x.grp.mem.info" INFO connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of XGrpMemInfo _memId _memProfile -> do -- TODO check member ID @@ -2863,7 +2863,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do _ -> throwChatError $ CECommandError "unexpected cmdFunction" CRContactUri _ -> throwChatError $ CECommandError "unexpected ConnectionRequestUri type" CONF confId _ connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case memberCategory m of GCInviteeMember -> case chatMsgEvent of @@ -2884,7 +2884,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do | otherwise -> messageError "x.grp.mem.info: memberId is different from expected" _ -> messageError "CONF from member must have x.grp.mem.info" INFO connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of XGrpMemInfo memId _memProfile | sameMemberId memId m -> do @@ -3028,7 +3028,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- SMP CONF for SndFileConnection happens for direct file protocol -- when recipient of the file "joins" connection created by the sender CONF confId _ connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of -- TODO save XFileAcpt message XFileAcpt name @@ -3098,7 +3098,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do -- when sender of the file "joins" connection created by the recipient -- (sender doesn't create connections for all group members) CONF confId _ connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of XOk -> allowAgentConnectionAsync user conn confId XOk -- [async agent commands] no continuation needed, but command should be asynchronous for stability _ -> pure () @@ -3161,7 +3161,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do processUserContactRequest :: ACommand 'Agent e -> ConnectionEntity -> Connection -> UserContact -> m () processUserContactRequest agentMsg connEntity conn UserContact {userContactLinkId} = case agentMsg of REQ invId _ connInfo -> do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage conn connInfo case chatMsgEvent of XContact p xContactId_ -> profileContactRequest invId p xContactId_ XInfo p -> profileContactRequest invId p Nothing @@ -3959,7 +3959,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do saveConnInfo :: Connection -> ConnInfo -> m () saveConnInfo activeConn connInfo = do - ChatMessage {chatMsgEvent} <- parseChatMessage connInfo + ChatMessage {chatMsgEvent} <- parseChatMessage activeConn connInfo case chatMsgEvent of XInfo p -> do ct <- withStore $ \db -> createDirectContact db user activeConn p @@ -4153,16 +4153,18 @@ sendFileInline_ FileTransferMeta {filePath, chunkSize} sharedMsgId sendMsg = else sendChunks (chunkNo + 1) rest chSize = fromIntegral chunkSize -parseChatMessage :: ChatMonad m => ByteString -> m (ChatMessage 'Json) -parseChatMessage = parseChatMessage_ +parseChatMessage :: ChatMonad m => Connection -> ByteString -> m (ChatMessage 'Json) +parseChatMessage conn = parseChatMessage_ conn Nothing {-# INLINE parseChatMessage #-} -parseAChatMessage :: ChatMonad m => ByteString -> m AChatMessage -parseAChatMessage = parseChatMessage_ +parseAChatMessage :: ChatMonad m => Connection -> MsgMeta -> ByteString -> m AChatMessage +parseAChatMessage conn msgMeta = parseChatMessage_ conn (Just msgMeta) {-# INLINE parseAChatMessage #-} -parseChatMessage_ :: (ChatMonad m, StrEncoding s) => ByteString -> m s -parseChatMessage_ s = liftEither . first (ChatError . CEInvalidChatMessage (safeDecodeUtf8 s)) $ strDecode s +parseChatMessage_ :: (ChatMonad m, StrEncoding s) => Connection -> Maybe MsgMeta -> ByteString -> m s +parseChatMessage_ conn msgMeta s = liftEither . first (ChatError . errType) $ strDecode s + where + errType = CEInvalidChatMessage conn (msgMetaToJson <$> msgMeta) (safeDecodeUtf8 s) sendFileChunk :: ChatMonad m => User -> SndFileTransfer -> m () sendFileChunk user ft@SndFileTransfer {fileId, fileStatus, agentConnId = AgentConnId acId} = @@ -4376,8 +4378,8 @@ sendPendingGroupMessages user GroupMember {groupMemberId, localDisplayName} conn _ -> pure () saveRcvMSG :: ChatMonad m => Connection -> ConnOrGroupId -> MsgMeta -> MsgBody -> CommandId -> m RcvMessage -saveRcvMSG Connection {connId} connOrGroupId agentMsgMeta msgBody agentAckCmdId = do - ACMsg _ ChatMessage {msgId = sharedMsgId_, chatMsgEvent} <- parseAChatMessage msgBody +saveRcvMSG conn@Connection {connId} connOrGroupId agentMsgMeta msgBody agentAckCmdId = do + ACMsg _ ChatMessage {msgId = sharedMsgId_, chatMsgEvent} <- parseAChatMessage conn agentMsgMeta msgBody let agentMsgId = fst $ recipient agentMsgMeta newMsg = NewMessage {chatMsgEvent, msgBody} rcvMsgDelivery = RcvMsgDelivery {connId, agentMsgId, agentMsgMeta, agentAckCmdId} diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index f78ce7e0e6..cd2916c83b 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -779,7 +779,7 @@ data ChatErrorType | CEChatNotStopped | CEChatStoreChanged | CEInvalidConnReq - | CEInvalidChatMessage {messageData :: Text, message :: String} + | CEInvalidChatMessage {connection :: Connection, msgMeta :: Maybe MsgMetaJSON, messageData :: Text, message :: String} | CEContactNotReady {contact :: Contact} | CEContactDisabled {contact :: Contact} | CEConnectionDisabled {connection :: Connection} diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index c13318250e..8978772236 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -1384,7 +1384,12 @@ viewChatError logLevel = \case CEChatNotStopped -> ["error: chat not stopped"] CEChatStoreChanged -> ["error: chat store changed, please restart chat"] CEInvalidConnReq -> viewInvalidConnReq - CEInvalidChatMessage msg e -> [plain $ "chat message error: " <> e <> " (" <> T.unpack (T.take 120 msg) <> ")"] + CEInvalidChatMessage Connection {connId} msgMeta_ msg e -> + [ plain $ + ("chat message error: " <> e <> " (" <> T.unpack (T.take 120 msg) <> ")") + <> (", connection id: " <> show connId) + <> maybe "" (\MsgMetaJSON {rcvId} -> ", agent msg rcv id: " <> show rcvId) msgMeta_ + ] CEContactNotReady c -> [ttyContact' c <> ": not ready"] CEContactDisabled Contact {localDisplayName = c} -> [ttyContact c <> ": disabled, to enable: " <> highlight ("/enable " <> c) <> ", to delete: " <> highlight ("/d " <> c)] CEConnectionDisabled Connection {connId, connType} -> [plain $ "connection " <> textEncode connType <> " (" <> tshow connId <> ") is disabled" | logLevel <= CLLWarning]