ios, core: fix add contact screen, add logging, additional chat events (#380)

* ios, core: fix add contact screen, add logging, additional chat events

* fix alert dialogues

* fix precedence parsing error

* update alert messages
This commit is contained in:
Evgeny Poberezkin 2022-02-26 20:21:32 +00:00 committed by GitHub
parent 1110a78e06
commit 0413865a3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 39 deletions

View file

@ -268,9 +268,8 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert
// if chatModel.upsertChatItem(cInfo, cItem) {
// NtfManager.shared.notifyMessageReceived(cInfo, cItem)
// }
// default:
// logger.debug("unsupported event: \(res.responseType)")
// }
else ->
Log.d("SIMPLEX" , "unsupported event: ${r.responseType}")
}
}
@ -394,6 +393,7 @@ class APIResponse(val resp: CR, val corr: String? = null) {
sealed class CR {
@Serializable @SerialName("activeUser") class ActiveUser(val user: User): CR()
@Serializable @SerialName("chatStarted") class ChatStarted: CR()
@Serializable @SerialName("chatRunning") class ChatRunning: CR()
@Serializable @SerialName("apiChats") class ApiChats(val chats: List<Chat>): CR()
@Serializable @SerialName("apiChat") class ApiChat(val chat: Chat): CR()
@Serializable @SerialName("invitation") class Invitation(val connReqInvitation: String): CR()
@ -430,6 +430,7 @@ sealed class CR {
val responseType: String get() = when(this) {
is ActiveUser -> "activeUser"
is ChatStarted -> "chatStarted"
is ChatRunning -> "chatRunning"
is ApiChats -> "apiChats"
is ApiChat -> "apiChats"
is Invitation -> "invitation"
@ -467,6 +468,7 @@ sealed class CR {
val details: String get() = when(this) {
is ActiveUser -> json.encodeToString(user)
is ChatStarted -> noDetails()
is ChatRunning -> noDetails()
is ApiChats -> json.encodeToString(chats)
is ApiChat -> json.encodeToString(chat)
is Invitation -> connReqInvitation

View file

@ -13,32 +13,31 @@ struct ContentView: View {
@State private var showNotificationAlert = false
var body: some View {
ZStack {
if let user = chatModel.currentUser {
ChatListView(user: user)
.onAppear {
do {
try apiStartChat()
chatModel.chats = try apiGetChats()
} catch {
fatalError("Failed to start or load chats: \(error)")
}
ChatReceiver.shared.start()
NtfManager.shared.requestAuthorization(onDeny: {
alertManager.showAlert(notificationAlert())
})
if let user = chatModel.currentUser {
ChatListView(user: user)
.onAppear {
do {
try apiStartChat()
chatModel.chats = try apiGetChats()
} catch {
fatalError("Failed to start or load chats: \(error)")
}
} else {
WelcomeView()
}
ChatReceiver.shared.start()
NtfManager.shared.requestAuthorization(onDeny: {
alertManager.showAlert(notificationAlert())
})
}
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
} else {
WelcomeView()
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
}
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
}
func notificationAlert() -> Alert {
Alert(
title: Text("Notification are disabled!"),
message: Text("Please open settings to enable"),
message: Text("The app can notify you when you receive messages or contact requests - please open settings to enable."),
primaryButton: .default(Text("Open Settings")) {
DispatchQueue.main.async {
UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil)

View file

@ -95,6 +95,7 @@ enum ChatResponse: Decodable, Error {
case response(type: String, json: String)
case activeUser(user: User)
case chatStarted
case chatRunning
case apiChats(chats: [ChatData])
case apiChat(chat: ChatData)
case invitation(connReqInvitation: String)
@ -131,6 +132,7 @@ enum ChatResponse: Decodable, Error {
case let .response(type, _): return "* \(type)"
case .activeUser: return "activeUser"
case .chatStarted: return "chatStarted"
case .chatRunning: return "chatRunning"
case .apiChats: return "apiChats"
case .apiChat: return "apiChat"
case .invitation: return "invitation"
@ -170,6 +172,7 @@ enum ChatResponse: Decodable, Error {
case let .response(_, json): return json
case let .activeUser(user): return String(describing: user)
case .chatStarted: return noDetails
case .chatRunning: return noDetails
case let .apiChats(chats): return String(describing: chats)
case let .apiChat(chat): return String(describing: chat)
case let .invitation(connReqInvitation): return connReqInvitation
@ -306,8 +309,8 @@ func apiSendMessage(type: ChatType, id: Int64, msg: MsgContent) async throws ->
throw r
}
func apiAddContact() async throws -> String {
let r = await chatSendCmd(.addContact)
func apiAddContact() throws -> String {
let r = chatSendCmdSync(.addContact)
if case let .invitation(connReqInvitation) = r { return connReqInvitation }
throw r
}
@ -574,7 +577,10 @@ private func getChatCtrl() -> chat_ctrl {
if let controller = chatController { return controller }
let dataDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path + "/mobile_v1"
var cstr = dataDir.cString(using: .utf8)!
logger.debug("getChatCtrl: chat_init")
ChatModel.shared.terminalItems.append(.cmd(.now, .string("chat_init")))
chatController = chat_init(&cstr)
ChatModel.shared.terminalItems.append(.resp(.now, .response(type: "chat_controller", json: "chat_controller: no details")))
return chatController!
}

View file

@ -84,7 +84,7 @@ struct ChatListView: View {
let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
return Alert(
title: Text("Connect via \(action) link?"),
message: Text("Your profile will be sent to the contact that you received this link from: \(link)"),
message: Text("Your profile will be sent to the contact that you received this link from"),
primaryButton: .default(Text("Connect")) {
DispatchQueue.main.async {
Task {

View file

@ -22,7 +22,9 @@ struct AddContactView: View {
.multilineTextAlignment(.center)
QRCode(uri: connReqInvitation)
.padding()
Text("If you can't show QR code, you can share the invitation link via any channel")
(Text("If you cannot meet in person, you can ") +
Text("scan QR code in the video call").bold() +
Text(", or you can share the invitation link via any other channel."))
.font(.subheadline)
.multilineTextAlignment(.center)
.padding(.horizontal)

View file

@ -35,18 +35,20 @@ struct NewChatButton: View {
}
func addContactAction() {
Task {
do {
connReqInvitation = try await apiAddContact()
addContact = true
} catch {
DispatchQueue.global().async {
connectionErrorAlert(error)
}
logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)")
do {
connReqInvitation = try apiAddContact()
addContact = true
} catch {
DispatchQueue.global().async {
connectionErrorAlert(error)
}
logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)")
}
}
func addContactSheet() -> some View {
AddContactView(connReqInvitation: connReqInvitation)
}
func connectContactSheet() -> some View {
ConnectContactView(completed: { err in

View file

@ -144,7 +144,10 @@ processChatCommand = \case
user <- withStore $ \st -> createUser st p True
atomically . writeTVar u $ Just user
pure $ CRActiveUser user
StartChat -> withUser' $ \user -> startChatController user $> CRChatStarted
StartChat -> withUser' $ \user ->
asks agentAsync >>= readTVarIO >>= \case
Just _ -> pure CRChatRunning
_ -> startChatController user $> CRChatStarted
APIGetChats -> CRApiChats <$> withUser (\user -> withStore (`getChatPreviews` user))
APIGetChat cType cId pagination -> withUser $ \user -> case cType of
CTDirect -> CRApiChat . AChat SCTDirect <$> withStore (\st -> getDirectChat st user cId pagination)
@ -528,7 +531,9 @@ subscribeUserConnections user@User {userId} = do
subscribe cId `catchError` (toView . CRRcvFileSubError ft)
subscribePendingConnections n = do
cs <- withStore (`getPendingConnections` user)
subscribeConns n cs `catchError` \_ -> pure ()
summary <- pooledForConcurrentlyN n cs $ \Connection {agentConnId = acId@(AgentConnId cId)} ->
PendingSubStatus acId <$> ((subscribe cId $> Nothing) `catchError` (pure . Just))
toView $ CRPendingSubSummary summary
subscribeUserContactLink n = do
cs <- withStore (`getUserContactLinkConnections` userId)
(subscribeConns n cs >> toView CRUserContactLinkSubscribed)

View file

@ -133,6 +133,7 @@ data ChatCommand
data ChatResponse
= CRActiveUser {user :: User}
| CRChatStarted
| CRChatRunning
| CRApiChats {chats :: [AChat]}
| CRApiChat {chat :: AChat}
| CRNewChatItem {chatItem :: AChatItem}
@ -204,6 +205,7 @@ data ChatResponse
| CRMemberSubError {groupInfo :: GroupInfo, contactName :: ContactName, chatError :: ChatError} -- TODO Contact? or GroupMember?
| CRMemberSubErrors {memberSubErrors :: [MemberSubError]}
| CRGroupSubscribed {groupInfo :: GroupInfo}
| CRPendingSubSummary {pendingSubStatus :: [PendingSubStatus]}
| CRSndFileSubError {sndFileTransfer :: SndFileTransfer, chatError :: ChatError}
| CRRcvFileSubError {rcvFileTransfer :: RcvFileTransfer, chatError :: ChatError}
| CRUserContactLinkSubscribed
@ -236,6 +238,16 @@ data MemberSubError = MemberSubError
instance ToJSON MemberSubError where
toEncoding = J.genericToEncoding J.defaultOptions
data PendingSubStatus = PendingSubStatus
{ connId :: AgentConnId,
connError :: Maybe ChatError
}
deriving (Show, Generic)
instance ToJSON PendingSubStatus where
toJSON = J.genericToJSON J.defaultOptions {J.omitNothingFields = True}
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
data ChatError
= ChatError {errorType :: ChatErrorType}
| ChatErrorAgent {agentError :: AgentErrorType}

View file

@ -39,6 +39,7 @@ responseToView :: Bool -> ChatResponse -> [StyledString]
responseToView testView = \case
CRActiveUser User {profile} -> viewUserProfile profile
CRChatStarted -> ["chat started"]
CRChatRunning -> []
CRApiChats chats -> if testView then testViewChats chats else [plain . bshow $ J.encode chats]
CRApiChat chat -> if testView then testViewChat chat else [plain . bshow $ J.encode chat]
CRNewChatItem (AChatItem _ _ chat item) -> viewChatItem chat item
@ -103,9 +104,9 @@ responseToView testView = \case
CRContactSubscribed c -> [ttyContact' c <> ": connected to server"]
CRContactSubError c e -> [ttyContact' c <> ": contact error " <> sShow e]
CRContactSubSummary summary ->
(if null connected then [] else [sShow (length connected) <> " contacts connected (use " <> highlight' "/cs" <> " for the list)"]) <> viewErrorsSummary errors " contact errors"
(if null subscribed then [] else [sShow (length subscribed) <> " contacts connected (use " <> highlight' "/cs" <> " for the list)"]) <> viewErrorsSummary errors " contact errors"
where
(errors, connected) = partition (isJust . contactError) summary
(errors, subscribed) = partition (isJust . contactError) summary
CRGroupInvitation GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}} ->
[groupInvitation ldn fullName]
CRReceivedGroupInvitation g c role -> viewReceivedGroupInvitation g c role
@ -122,6 +123,10 @@ responseToView testView = \case
CRMemberSubError g c e -> [ttyGroup' g <> " member " <> ttyContact c <> " error: " <> sShow e]
CRMemberSubErrors summary -> viewErrorsSummary summary " group member errors"
CRGroupSubscribed g -> [ttyFullGroup g <> ": connected to server(s)"]
CRPendingSubSummary summary ->
(if null subscribed then [] else [sShow (length subscribed) <> " pending connections subscribed"]) <> viewErrorsSummary errors " connection errors"
where
(errors, subscribed) = partition (isJust . connError) summary
CRSndFileSubError SndFileTransfer {fileId, fileName} e ->
["sent file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
CRRcvFileSubError RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} e ->