From 0a94e740d22883217c9c2ec9a1db789375d71b76 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 27 Feb 2022 18:16:38 +0000 Subject: [PATCH] android: refactor modal views without navigation controller (#381) * android: refactor modal views without navigation controller * refactor navigation * make alert manager global * disable CRPendingSubSummary in terminal, hamburger menu instead of gear --- .../java/chat/simplex/app/MainActivity.kt | 129 ++++++------------ .../main/java/chat/simplex/app/SimplexApp.kt | 71 +--------- .../java/chat/simplex/app/model/ChatModel.kt | 3 +- .../java/chat/simplex/app/model/SimpleXAPI.kt | 19 +-- .../java/chat/simplex/app/views/SplashView.kt | 11 +- .../chat/simplex/app/views/TerminalView.kt | 58 ++++---- .../simplex/app/views/chat/ChatInfoView.kt | 13 +- .../chat/simplex/app/views/chat/ChatView.kt | 10 +- .../simplex/app/views/chat/SendMsgView.kt | 3 + .../app/views/chat/item/TextItemView.kt | 2 +- .../app/views/chatlist/ChatHelpView.kt | 107 ++++----------- .../app/views/chatlist/ChatListNavLinkView.kt | 12 +- .../app/views/chatlist/ChatListView.kt | 23 ++-- .../simplex/app/views/helpers/AlertManager.kt | 81 +++++++++++ .../simplex/app/views/helpers/ModalView.kt | 59 ++++++++ .../app/views/newchat/AddContactView.kt | 26 +--- .../app/views/newchat/ConnectContactView.kt | 100 ++++++-------- .../simplex/app/views/newchat/NewChatSheet.kt | 26 ++-- .../app/views/usersettings/HelpView.kt | 32 ++--- .../views/usersettings/MarkdownHelpView.kt | 88 +++++------- .../app/views/usersettings/SettingsView.kt | 33 +++-- .../app/views/usersettings/UserAddressView.kt | 17 +-- .../app/views/usersettings/UserProfileView.kt | 18 +-- apps/ios/SimpleX.xcodeproj/project.pbxproj | 61 +++++---- src/Simplex/Chat/View.hs | 5 +- 25 files changed, 447 insertions(+), 560 deletions(-) create mode 100644 apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt create mode 100644 apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index 30ee947668..a41051877d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -8,22 +8,27 @@ import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.lifecycle.AndroidViewModel -import androidx.navigation.* -import androidx.navigation.compose.* -import chat.simplex.app.model.* +import chat.simplex.app.model.ChatModel +import chat.simplex.app.model.NtfManager import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.* -import chat.simplex.app.views.chat.ChatInfoView +import chat.simplex.app.views.SplashView +import chat.simplex.app.views.WelcomeView import chat.simplex.app.views.chat.ChatView import chat.simplex.app.views.chatlist.ChatListView import chat.simplex.app.views.chatlist.openChat +import chat.simplex.app.views.helpers.AlertManager import chat.simplex.app.views.helpers.withApi import chat.simplex.app.views.newchat.* -import chat.simplex.app.views.usersettings.* -import kotlinx.serialization.decodeFromString + +//import kotlinx.serialization.decodeFromString class MainActivity: ComponentActivity() { private val vm by viewModels() @@ -35,7 +40,13 @@ class MainActivity: ComponentActivity() { // vm.app.initiateBackgroundWork() setContent { SimpleXTheme { - Navigation(vm.chatModel) + Surface( + Modifier + .background(MaterialTheme.colors.background) + .fillMaxSize() + ) { + MainPage(vm.chatModel) + } } } } @@ -47,84 +58,20 @@ class SimplexViewModel(application: Application): AndroidViewModel(application) } @Composable -fun MainPage(chatModel: ChatModel, nav: NavController) { - when (chatModel.userCreated.value) { - null -> SplashView() - false -> WelcomeView(chatModel) // { nav.navigate(Pages.ChatList.route) } - true -> if (chatModel.chatId.value == null) { - ChatListView(chatModel, nav) - } else { - ChatView(chatModel, nav) - } - } -} - -@Composable -fun Navigation(chatModel: ChatModel) { - println("*** in Navigation") - val nav = rememberNavController() - val scope = rememberCoroutineScope() +fun MainPage(chatModel: ChatModel) { Box { - NavHost(navController = nav, startDestination = Pages.Home.route) { - composable(route = Pages.Home.route) { - println("*** composable MainPage") - MainPage(chatModel, nav) - } - composable(route = Pages.Welcome.route) { - WelcomeView(chatModel) - } - composable(route = Pages.AddContact.route) { - AddContactView(chatModel, nav) - } - composable(route = Pages.Connect.route) { - ConnectContactView(chatModel, nav) - } - composable(route = Pages.ChatInfo.route) { - ChatInfoView(chatModel, nav) - } - composable(route = Pages.Terminal.route) { - TerminalView(chatModel, nav) - } - composable( - Pages.TerminalItemDetails.route + "/{identifier}", - arguments = listOf( - navArgument("identifier") { - type = NavType.LongType - } - ) - ) { entry -> DetailView(entry.arguments!!.getLong("identifier"), chatModel.terminalItems, nav) } - composable(route = Pages.UserProfile.route) { - UserProfileView(chatModel, nav) - } - composable(route = Pages.UserAddress.route) { - UserAddressView(chatModel, nav) - } - composable(route = Pages.Help.route) { - HelpView(chatModel, nav) - } - composable(route = Pages.Markdown.route) { - MarkdownHelpView(nav) - } + when (chatModel.userCreated.value) { + null -> SplashView() + false -> WelcomeView(chatModel) + true -> + if (chatModel.chatId.value == null) ChatListView(chatModel) + else ChatView(chatModel) } - val am = chatModel.alertManager - if (am.presentAlert.value) am.alertView.value?.invoke() + ModalManager.shared.showInView() + AlertManager.shared.showInView() } } -sealed class Pages(val route: String) { - object Home: Pages("home") - object Terminal: Pages("terminal") - object Welcome: Pages("welcome") - object TerminalItemDetails: Pages("details") - object AddContact: Pages("add_contact") - object Connect: Pages("connect") - object ChatInfo: Pages("chat_info") - object UserProfile: Pages("user_profile") - object UserAddress: Pages("user_address") - object Help: Pages("help") - object Markdown: Pages("markdown") -} - fun processIntent(intent: Intent?, chatModel: ChatModel) { when (intent?.action) { NtfManager.OpenChatAction -> { @@ -148,8 +95,8 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) { // TODO open from chat list view chatModel.appOpenUrl.value = uri } else { - withUriAction(chatModel, uri) { action -> - chatModel.alertManager.showAlertMsg( + withUriAction(uri) { action -> + AlertManager.shared.showAlertMsg( title = "Connect via $action link?", text = "Your profile will be sent to the contact that you received this link from.", confirmText = "Connect", @@ -164,10 +111,10 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) { } } -fun testJson() { - val str = """ - {} - """.trimIndent() - - println(json.decodeFromString(str)) -} +//fun testJson() { +// val str = """ +// {} +// """.trimIndent() +// +// println(json.decodeFromString(str)) +//} diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt index aa04839b3f..cb4f058c4b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt @@ -3,9 +3,6 @@ package chat.simplex.app import android.app.Application import android.net.LocalServerSocket import android.util.Log -import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf import androidx.work.* import chat.simplex.app.model.* import chat.simplex.app.views.helpers.withApi @@ -48,7 +45,7 @@ class SimplexApp: Application() { super.onCreate() ntfManager = NtfManager(applicationContext) val ctrl = chatInit(applicationContext.filesDir.toString()) - controller = ChatController(ctrl, AlertManager(), ntfManager, applicationContext) + controller = ChatController(ctrl, ntfManager, applicationContext) chatModel = controller.chatModel withApi { val user = controller.apiGetActiveUser() @@ -56,72 +53,6 @@ class SimplexApp: Application() { } } - class AlertManager { - var alertView = mutableStateOf<(@Composable () -> Unit)?>(null) - var presentAlert = mutableStateOf(false) - - fun showAlert(alert: @Composable () -> Unit) { - Log.d("SIMPLEX", "AlertManager.showAlert") - alertView.value = alert - presentAlert.value = true - } - - fun hideAlert() { - presentAlert.value = false - alertView.value = null - } - - fun showAlertDialog( - title: String, - text: String? = null, - confirmText: String = "Ok", - onConfirm: (() -> Unit)? = null, - dismissText: String = "Cancel", - onDismiss: (() -> Unit)? = null - ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } - showAlert { - AlertDialog( - onDismissRequest = this::hideAlert, - title = { Text(title) }, - text = alertText, - confirmButton = { - Button(onClick = { - onConfirm?.invoke() - hideAlert() - }) { Text(confirmText) } - }, - dismissButton = { - Button(onClick = { - onDismiss?.invoke() - hideAlert() - }) { Text(dismissText) } - } - ) - } - } - - fun showAlertMsg( - title: String, text: String? = null, - confirmText: String = "Ok", onConfirm: (() -> Unit)? = null - ) { - val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } - showAlert { - AlertDialog( - onDismissRequest = this::hideAlert, - title = { Text(title) }, - text = alertText, - confirmButton = { - Button(onClick = { - onConfirm?.invoke() - hideAlert() - }) { Text(confirmText) } - } - ) - } - } - } - companion object { init { val socketName = "local.socket.address.listen.native.cmd2" diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index 726b2fb5d4..75b8c74220 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -7,13 +7,12 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.font.* import androidx.compose.ui.text.style.TextDecoration -import chat.simplex.app.SimplexApp import chat.simplex.app.ui.theme.SecretColor import kotlinx.datetime.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -class ChatModel(val controller: ChatController, val alertManager: SimplexApp.AlertManager) { +class ChatModel(val controller: ChatController) { var currentUser = mutableStateOf(null) var userCreated = mutableStateOf(null) var chats = mutableStateListOf() diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 875b90b1e2..9e4b899f79 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -5,7 +5,9 @@ import android.app.ActivityManager.RunningAppProcessInfo import android.content.Context import android.util.Log import androidx.compose.runtime.mutableStateOf -import chat.simplex.app.* +import chat.simplex.app.chatRecvMsg +import chat.simplex.app.chatSendCmd +import chat.simplex.app.views.helpers.AlertManager import chat.simplex.app.views.helpers.withApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -18,8 +20,8 @@ import kotlin.concurrent.thread typealias ChatCtrl = Long -open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.AlertManager, val ntfManager: NtfManager, val appContext: Context) { - var chatModel = ChatModel(this, alertManager) +open class ChatController(val ctrl: ChatCtrl, val ntfManager: NtfManager, val appContext: Context) { + var chatModel = ChatModel(this) suspend fun startChat(u: User) { Log.d("SIMPLEX (user)", u.toString()) @@ -139,14 +141,14 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert when { r is CR.SentConfirmation || r is CR.SentInvitation -> return true r is CR.ContactAlreadyExists -> { - alertManager.showAlertMsg("Contact already exists", + AlertManager.shared.showAlertMsg("Contact already exists", "You are already connected to ${r.contact.displayName} via this link" ) return false } r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat && r.chatError.errorType is ChatErrorType.InvalidConnReq -> { - alertManager.showAlertMsg("Invalid connection link", + AlertManager.shared.showAlertMsg("Invalid connection link", "Please check that you used the correct link or ask your contact to send you another one." ) return false @@ -165,15 +167,14 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert is CR.ChatCmdError -> { val e = r.chatError if (e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.ContactGroups) { - alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( "Can't delete contact!", "Contact ${e.errorType.contact.displayName} cannot be deleted, it is a member of the group(s) ${e.errorType.groupNames}" ) - return false } } + else -> apiErrorAlert("apiDeleteChat", "Error deleting ${type.chatTypeName}", r) } - apiErrorAlert("apiDeleteChat", "Error deleting ${type.chatTypeName}", r) return false } @@ -234,7 +235,7 @@ open class ChatController(val ctrl: ChatCtrl, val alertManager: SimplexApp.Alert fun apiErrorAlert(method: String, title: String, r: CR) { val errMsg = "${r.responseType}: ${r.details}" Log.e("SIMPLEX", "$method bad response: $errMsg") - alertManager.showAlertMsg(title, errMsg) + AlertManager.shared.showAlertMsg(title, errMsg) } fun processReceivedMsg(r: CR) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt index 4fa4b311d3..f37a6fca8e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/SplashView.kt @@ -1,17 +1,18 @@ package chat.simplex.app.views import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color @Composable fun SplashView() { - Box(modifier = Modifier - .fillMaxSize() - .background(color = Color.White) + Surface( + Modifier + .background(MaterialTheme.colors.background) + .fillMaxSize() ) { // Image( // painter = painterResource(R.drawable.logo), diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index d12a4a62d9..5a9e3c0411 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -1,8 +1,10 @@ package chat.simplex.app.views import android.content.res.Configuration +import androidx.activity.compose.BackHandler import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.* import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.* @@ -15,19 +17,20 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.NavController import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.SendMsgView import chat.simplex.app.views.helpers.CloseSheetBar import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.newchat.ModalManager import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.launch @Composable -fun TerminalView(chatModel: ChatModel, nav: NavController) { - TerminalLayout(chatModel.terminalItems, nav::popBackStack, nav::navigate) { cmd -> +fun TerminalView(chatModel: ChatModel, close: () -> Unit) { + BackHandler(onBack = close) + TerminalLayout(chatModel.terminalItems, close) { cmd -> withApi { // show "in progress" chatModel.controller.sendCmd(CC.Console(cmd)) @@ -37,39 +40,45 @@ fun TerminalView(chatModel: ChatModel, nav: NavController) { } @Composable -fun TerminalLayout(terminalItems: List , close: () -> Unit, navigate: (String) -> Unit, - sendCommand: (String) -> Unit) { +fun TerminalLayout(terminalItems: List , close: () -> Unit, sendCommand: (String) -> Unit) { ProvideWindowInsets(windowInsetsAnimationsEnabled = true) { Scaffold( topBar = { CloseSheetBar(close) }, bottomBar = { SendMsgView(sendCommand) }, modifier = Modifier.navigationBarsWithImePadding() ) { contentPadding -> - Box( + Surface( modifier = Modifier .padding(contentPadding) .fillMaxWidth() .background(MaterialTheme.colors.background) ) { - TerminalLog(terminalItems, navigate) + TerminalLog(terminalItems) } } } } @Composable -fun TerminalLog(terminalItems: List, navigate: (String) -> Unit) { +fun TerminalLog(terminalItems: List) { val listState = rememberLazyListState() val scope = rememberCoroutineScope() LazyColumn(state = listState) { items(terminalItems) { item -> - Text("${item.date.toString().subSequence(11, 19)} ${item.label}", - style = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 18.sp, color = MaterialTheme.colors.primary), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier - .padding(horizontal = 8.dp, vertical = 4.dp) - .clickable { navigate("details/${item.id}") }) + Text("${item.date.toString().subSequence(11, 19)} ${item.label}", + style = TextStyle(fontFamily = FontFamily.Monospace, fontSize = 18.sp, color = MaterialTheme.colors.primary), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .padding(horizontal = 8.dp, vertical = 4.dp) + .clickable { + ModalManager.shared.showModal { + SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { + Text(item.details) + } + } + } + ) } val len = terminalItems.count() if (len > 1) { @@ -80,22 +89,6 @@ fun TerminalLog(terminalItems: List, navigate: (String) -> Unit) { } } -@Composable -fun DetailView(identifier: Long, terminalItems: List, nav: NavController){ - Surface( - Modifier - .background(MaterialTheme.colors.background) - .fillMaxSize() - ) { - Column { - CloseSheetBar(nav::popBackStack) - SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { - Text((terminalItems.firstOrNull { it.id == identifier })?.details ?: "") - } - } - } -} - @Preview(showBackground = true) @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, @@ -108,7 +101,6 @@ fun PreviewTerminalLayout() { TerminalLayout( terminalItems = TerminalItem.sampleData, close = {}, - navigate = {}, sendCommand = {} ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt index 061fab5e3b..cc272b8fb8 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatInfoView.kt @@ -1,5 +1,6 @@ package chat.simplex.app.views.chat +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -14,19 +15,19 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.* @Composable -fun ChatInfoView(chatModel: ChatModel, nav: NavController) { +fun ChatInfoView(chatModel: ChatModel, close: () -> Unit) { + BackHandler(onBack = close) val chat = chatModel.chats.firstOrNull { it.id == chatModel.chatId.value } if (chat != null) { ChatInfoLayout(chat, - close = nav::popBackStack, + close = close, deleteContact = { - chatModel.alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( title = "Delete contact?", text = "Contact and all messages will be deleted - this cannot be undone!", confirmText = "Delete", @@ -37,7 +38,7 @@ fun ChatInfoView(chatModel: ChatModel, nav: NavController) { if (r) { chatModel.removeChat(cInfo.id) chatModel.chatId.value = null - nav.popBackStack() + close() } } } @@ -92,7 +93,7 @@ fun ChatInfoLayout(chat: Chat, close: () -> Unit, deleteContact: () -> Unit) { Spacer(Modifier.weight(1F)) - Box(Modifier.padding(24.dp)) { + Box(Modifier.padding(48.dp)) { SimpleButton( "Delete contact", icon = Icons.Outlined.Delete, color = Color.Red, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt index 7270720ebf..774afd42ef 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ChatView.kt @@ -18,13 +18,12 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import chat.simplex.app.Pages import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.item.ChatItemView import chat.simplex.app.views.helpers.ChatInfoImage import chat.simplex.app.views.helpers.withApi +import chat.simplex.app.views.newchat.ModalManager import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.delay @@ -32,7 +31,7 @@ import kotlinx.coroutines.launch import kotlinx.datetime.Clock @Composable -fun ChatView(chatModel: ChatModel, nav: NavController) { +fun ChatView(chatModel: ChatModel) { val chat: Chat? = chatModel.chats.firstOrNull { chat -> chat.chatInfo.id == chatModel.chatId.value } if (chat == null) { chatModel.chatId.value = null @@ -55,7 +54,7 @@ fun ChatView(chatModel: ChatModel, nav: NavController) { } ChatLayout(chat, chatModel.chatItems, back = { chatModel.chatId.value = null }, - info = { nav.navigate(Pages.ChatInfo.route) }, + info = { ModalManager.shared.showCustomModal { close -> ChatInfoView(chatModel, close) } }, sendMessage = { msg -> withApi { // show "in progress" @@ -86,9 +85,8 @@ fun ChatLayout( bottomBar = { SendMsgView(sendMessage) }, modifier = Modifier.navigationBarsWithImePadding() ) { contentPadding -> - Box( + Surface( modifier = Modifier - .padding(contentPadding) .fillMaxWidth() .background(MaterialTheme.colors.background) ) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt index d9df249303..639933dde5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt @@ -15,9 +15,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme @Composable @@ -33,6 +35,7 @@ fun SendMsgView(sendMessage: (String) -> Unit) { autoCorrect = true ), modifier = Modifier.padding(8.dp), + cursorBrush = SolidColor(HighOrLowlight), decorationBox = { innerTextField -> Surface( shape = RoundedCornerShape(18.dp), diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt index 949648138e..df2528dd25 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/TextItemView.kt @@ -58,7 +58,7 @@ fun appendGroupMember(b: AnnotatedString.Builder, chatItem: ChatItem, groupMembe @Composable fun MarkdownText ( chatItem: ChatItem, - style: TextStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground), + style: TextStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onSurface), maxLines: Int = Int.MAX_VALUE, overflow: TextOverflow = TextOverflow.Clip, uriHandler: UriHandler? = null, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt index 8fb912c6d5..c12239f580 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatHelpView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.chat import android.content.res.Configuration -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -18,35 +17,28 @@ import androidx.compose.ui.unit.dp import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.usersettings.simplexTeamUri +val bold = SpanStyle(fontWeight = FontWeight.Bold) + @Composable -fun ChatHelpView(addContact: () -> Unit, doAddContact: Boolean) { +fun ChatHelpView(addContact: (() -> Unit)? = null) { Column( - Modifier - .fillMaxWidth() - .background(MaterialTheme.colors.background), horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(10.dp) ) { val uriHandler = LocalUriHandler.current - Text( - "Thank you for installing SimpleX Chat!", - color = MaterialTheme.colors.onBackground - ) + Text("Thank you for installing SimpleX Chat!") Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append("You can ") - } + append("You can ") withStyle(SpanStyle(color = MaterialTheme.colors.primary)) { append("connect to SimpleX team") } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(".") - } + append(".") }, - modifier = Modifier - .clickable(onClick = { uriHandler.openUri(simplexTeamUri) }) + modifier = Modifier.clickable(onClick = { + uriHandler.openUri(simplexTeamUri) + }) ) Column( @@ -56,46 +48,30 @@ fun ChatHelpView(addContact: () -> Unit, doAddContact: Boolean) { ) { Text( "To start a new chat", - color = MaterialTheme.colors.onBackground, style = MaterialTheme.typography.h2 ) Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Text( - "Tap button", - color = MaterialTheme.colors.onBackground - ) + Text("Tap button") Icon( Icons.Outlined.PersonAdd, "Add Contact", - modifier = if (doAddContact) Modifier.clickable(onClick = addContact) else Modifier, - tint = MaterialTheme.colors.onBackground, - ) - Text( - "above, then:", - color = MaterialTheme.colors.onBackground + modifier = if (addContact != null) Modifier.clickable(onClick = addContact) else Modifier, ) + Text("above, then:") } Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold)) { - append("Add new contact") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(": to create your one-time QR Code for your contact.") - } + withStyle(bold) { append("Add new contact") } + append(": to create your one-time QR Code for your contact.") } ) Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold)) { - append("Scan QR code") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(": to connect to your contact who shows QR code to you.") - } + withStyle(bold) { append("Scan QR code") } + append(": to connect to your contact who shows QR code to you.") } ) } @@ -105,51 +81,22 @@ fun ChatHelpView(addContact: () -> Unit, doAddContact: Boolean) { horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(10.dp) ) { - Text( - "To connect via link", - color = MaterialTheme.colors.onBackground, - style = MaterialTheme.typography.h2 - ) - Text( - "If you received SimpleX Chat invitation link you can open it in your browser:", - color = MaterialTheme.colors.onBackground - ) + Text("To connect via link", style = MaterialTheme.typography.h2) + Text("If you received SimpleX Chat invitation link you can open it in your browser:") Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append("\uD83D\uDCBB desktop: scan displayed QR code from the app, via ") - } - withStyle( - SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold) - ) { - append("Scan QR code") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(".") - } + append("\uD83D\uDCBB desktop: scan displayed QR code from the app, via ") + withStyle(bold) { append("Scan QR code") } + append(".") } ) Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append("\uD83D\uDCF1 mobile: tap ") - } - withStyle( - SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold) - ) { - append("Open in mobile app") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(", then tap ") - } - withStyle( - SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold) - ) { - append("Connect") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(" in the app.") - } + append("\uD83D\uDCF1 mobile: tap ") + withStyle(bold) { append("Open in mobile app") } + append(", then tap ") + withStyle(bold) { append("Connect") } + append(" in the app.") } ) } @@ -165,6 +112,6 @@ fun ChatHelpView(addContact: () -> Unit, doAddContact: Boolean) { @Composable fun PreviewChatHelpLayout() { SimpleXTheme { - ChatHelpView({}, false) + ChatHelpView({}) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt index 9efbb1183a..fa25259b37 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListNavLinkView.kt @@ -11,20 +11,20 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import chat.simplex.app.Pages + import chat.simplex.app.model.* import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.helpers.AlertManager import chat.simplex.app.views.helpers.withApi import kotlinx.datetime.Clock @Composable -fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel, nav: NavController) { +fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) { ChatListNavLinkLayout( chat = chat, click = { if (chat.chatInfo is ChatInfo.ContactRequest) { - contactRequestAlertDialog(chat.chatInfo, chatModel, nav) + contactRequestAlertDialog(chat.chatInfo, chatModel) } else { withApi { openChat(chatModel, chat.chatInfo) } } @@ -40,8 +40,8 @@ suspend fun openChat(chatModel: ChatModel, cInfo: ChatInfo) { } } -fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel, navController: NavController) { - chatModel.alertManager.showAlertDialog( +fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) { + AlertManager.shared.showAlertDialog( title = "Accept connection request?", text = "If you choose to reject sender will NOT be notified", confirmText = "Accept", 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 6f06d3e366..65b216fdee 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 @@ -8,15 +8,13 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.PersonAdd -import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.views.chat.ChatHelpView import chat.simplex.app.views.newchat.NewChatSheet @@ -39,7 +37,7 @@ class ScaffoldController(val scope: CoroutineScope) { } fun toggleSheet() { - if (state.bottomSheetState.isExpanded ?: false) collapse() else expand() + if (state.bottomSheetState.isExpanded) collapse() else expand() } fun toggleDrawer() = scope.launch { @@ -62,25 +60,24 @@ fun scaffoldController(): ScaffoldController { } @Composable -fun ChatListView(chatModel: ChatModel, nav: NavController) { +fun ChatListView(chatModel: ChatModel) { val scaffoldCtrl = scaffoldController() BottomSheetScaffold( scaffoldState = scaffoldCtrl.state, - drawerContent = { SettingsView(chatModel, nav) }, + drawerContent = { SettingsView(chatModel) }, sheetPeekHeight = 0.dp, - sheetContent = { NewChatSheet(chatModel, scaffoldCtrl, nav) }, + sheetContent = { NewChatSheet(chatModel, scaffoldCtrl) }, sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp), ) { Box { Column( modifier = Modifier - .padding(vertical = 8.dp) .fillMaxSize() .background(MaterialTheme.colors.background) ) { ChatListToolbar(scaffoldCtrl) if (chatModel.chats.isNotEmpty()) { - ChatList(chatModel, nav) + ChatList(chatModel) } else { val user = chatModel.currentUser.value Help(scaffoldCtrl, displayName = user?.profile?.displayName) @@ -111,7 +108,7 @@ fun Help(scaffoldCtrl: ScaffoldController, displayName: String?) { style = MaterialTheme.typography.h1, color = MaterialTheme.colors.onBackground ) - ChatHelpView({ scaffoldCtrl.toggleSheet() }, true) + ChatHelpView({ scaffoldCtrl.toggleSheet() }) Row( Modifier.padding(top = 30.dp), verticalAlignment = Alignment.CenterVertically, @@ -143,7 +140,7 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) { ) { IconButton(onClick = { scaffoldCtrl.toggleDrawer() }) { Icon( - Icons.Outlined.Settings, + Icons.Outlined.Menu, "Settings", tint = MaterialTheme.colors.primary, modifier = Modifier.padding(10.dp) @@ -167,13 +164,13 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) { } @Composable -fun ChatList(chatModel: ChatModel, navController: NavController) { +fun ChatList(chatModel: ChatModel) { Divider(Modifier.padding(horizontal = 8.dp)) LazyColumn( modifier = Modifier.fillMaxWidth() ) { items(chatModel.chats) { chat -> - ChatListNavLinkView(chat, chatModel, navController) + ChatListNavLinkView(chat, chatModel) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt new file mode 100644 index 0000000000..610ffc949b --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt @@ -0,0 +1,81 @@ +package chat.simplex.app.views.helpers + +import android.util.Log +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf + +class AlertManager { + var alertView = mutableStateOf<(@Composable () -> Unit)?>(null) + var presentAlert = mutableStateOf(false) + + fun showAlert(alert: @Composable () -> Unit) { + Log.d("SIMPLEX", "AlertManager.showAlert") + alertView.value = alert + presentAlert.value = true + } + + fun hideAlert() { + presentAlert.value = false + alertView.value = null + } + + fun showAlertDialog( + title: String, + text: String? = null, + confirmText: String = "Ok", + onConfirm: (() -> Unit)? = null, + dismissText: String = "Cancel", + onDismiss: (() -> Unit)? = null + ) { + val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } + showAlert { + AlertDialog( + onDismissRequest = this::hideAlert, + title = { Text(title) }, + text = alertText, + confirmButton = { + Button(onClick = { + onConfirm?.invoke() + hideAlert() + }) { Text(confirmText) } + }, + dismissButton = { + Button(onClick = { + onDismiss?.invoke() + hideAlert() + }) { Text(dismissText) } + } + ) + } + } + + fun showAlertMsg( + title: String, text: String? = null, + confirmText: String = "Ok", onConfirm: (() -> Unit)? = null + ) { + val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) } + showAlert { + AlertDialog( + onDismissRequest = this::hideAlert, + title = { Text(title) }, + text = alertText, + confirmButton = { + Button(onClick = { + onConfirm?.invoke() + hideAlert() + }) { Text(confirmText) } + } + ) + } + } + + @Composable + fun showInView() { + if (presentAlert.value) alertView.value?.invoke() + } + + companion object { + val shared = AlertManager() + } +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt new file mode 100644 index 0000000000..e0401c7099 --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/ModalView.kt @@ -0,0 +1,59 @@ +package chat.simplex.app.views.newchat + +import android.util.Log +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import chat.simplex.app.views.helpers.CloseSheetBar + +@Composable +fun ModalView(close: () -> Unit, content: @Composable () -> Unit) { + BackHandler(onBack = close) + Surface( + Modifier + .background(MaterialTheme.colors.background) + .fillMaxSize() + ) { + Column { + CloseSheetBar(close) + Box(Modifier.padding(horizontal = 16.dp)) { content() } + } + } +} + +class ModalManager { + private val modalViews = arrayListOf<(@Composable (close: () -> Unit) -> Unit)?>() + private val modalCount = mutableStateOf(0) + + fun showModal(content: @Composable () -> Unit) { + showCustomModal { close -> ModalView(close, content) } + } + + fun showCustomModal(modal: @Composable (close: () -> Unit) -> Unit) { + Log.d("SIMPLEX", "ModalManager.showModal") + modalViews.add(modal) + modalCount.value = modalViews.count() + } + + fun closeModal() { + if (!modalViews.isEmpty()) { + modalViews.removeAt(modalViews.count() - 1) + } + modalCount.value = modalViews.count() + } + + @Composable + fun showInView() { + if (modalCount.value > 0) modalViews.lastOrNull()?.invoke(::closeModal) + } + + companion object { + val shared = ModalManager() + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt index f221c97567..be27d1d6e0 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddContactView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.newchat import android.content.res.Configuration -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -16,60 +15,46 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleButton import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.CloseSheetBar import chat.simplex.app.views.helpers.shareText @Composable -fun AddContactView(chatModel: ChatModel, nav: NavController) { +fun AddContactView(chatModel: ChatModel) { val connReq = chatModel.connReqInvitation if (connReq != null) { val cxt = LocalContext.current AddContactLayout( connReq = connReq, - close = { nav.popBackStack() }, share = { shareText(cxt, connReq) } ) } } @Composable -fun AddContactLayout(connReq: String, close: () -> Unit, share: () -> Unit) { +fun AddContactLayout(connReq: String, share: () -> Unit) { Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - .padding(horizontal = 8.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp) ) { - CloseSheetBar(close) Text( "Add contact", style = MaterialTheme.typography.h1, - color = MaterialTheme.colors.onBackground ) Text( "Show QR code to your contact\nto scan from the app", style = MaterialTheme.typography.h2, textAlign = TextAlign.Center, - color = MaterialTheme.colors.onBackground ) QRCode(connReq) Text( buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append("If you cannot meet in person, you can ") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold)) { + append("If you cannot meet in person, you can ") + withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { append("scan QR code in the video call") } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { - append(", or you can share the invitation link via any other channel.") - } + append(", or you can share the invitation link via any other channel.") }, textAlign = TextAlign.Center, style = MaterialTheme.typography.caption, @@ -92,7 +77,6 @@ fun PreviewAddContactView() { SimpleXTheme { AddContactLayout( connReq = "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", - close = {}, share = {} ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt index e9715229f0..4abcf2bc46 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/ConnectContactView.kt @@ -2,7 +2,7 @@ package chat.simplex.app.views.newchat import android.content.res.Configuration import android.net.Uri -import androidx.compose.foundation.background +import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable @@ -13,44 +13,41 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.CloseSheetBar +import chat.simplex.app.views.helpers.AlertManager import chat.simplex.app.views.helpers.withApi @Composable -fun ConnectContactView(chatModel: ChatModel, nav: NavController) { +fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) { + BackHandler(onBack = close) ConnectContactLayout( qrCodeScanner = { QRCodeScanner { connReqUri -> try { val uri = Uri.parse(connReqUri) - withUriAction(chatModel, uri) { action -> + withUriAction(uri) { action -> connectViaUri(chatModel, action, uri) } } catch (e: RuntimeException) { - chatModel.alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( title = "Invalid QR code", text = "This QR code is not a link!" ) } - nav.popBackStack() + close() } }, - close = { nav.popBackStack() } + close = close ) } -fun withUriAction( - chatModel: ChatModel, uri: Uri, - run: suspend (String) -> Unit -) { +fun withUriAction(uri: Uri, run: suspend (String) -> Unit) { val action = uri.path?.drop(1) if (action == "contact" || action == "invitation") { withApi { run(action) } } else { - chatModel.alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( title = "Invalid link!", text = "This link is not a valid connection link!" ) @@ -63,7 +60,7 @@ suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri) { val whenConnected = if (action == "contact") "your connection request is accepted" else "your contact's device is online" - chatModel.alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( title = "Connection request sent!", text = "You will be connected when $whenConnected, please wait or check later!" ) @@ -72,50 +69,41 @@ suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri) { @Composable fun ConnectContactLayout(qrCodeScanner: @Composable () -> Unit, close: () -> Unit) { - Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - .padding(horizontal = 8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - CloseSheetBar(close) - Text( - "Scan QR code", - style = MaterialTheme.typography.h1, - color = MaterialTheme.colors.onBackground - ) - Text( - "Your chat profile will be sent\nto your contact", - style = MaterialTheme.typography.h2, - textAlign = TextAlign.Center, - color = MaterialTheme.colors.onBackground, - modifier = Modifier.padding(bottom = 4.dp) - ) - Box( - Modifier - .fillMaxWidth() - .aspectRatio(ratio = 1F) - ) { qrCodeScanner() } - Text( - buildAnnotatedString { - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { + ModalView(close) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Scan QR code", + style = MaterialTheme.typography.h1, + ) + Text( + "Your chat profile will be sent\nto your contact", + style = MaterialTheme.typography.h2, + textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 4.dp) + ) + Box( + Modifier + .fillMaxWidth() + .aspectRatio(ratio = 1F) + ) { qrCodeScanner() } + Text( + buildAnnotatedString { append("If you cannot meet in person, you can ") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground, fontWeight = FontWeight.Bold)) { - append("scan QR code in the video call") - } - withStyle(SpanStyle(color = MaterialTheme.colors.onBackground)) { + withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append("scan QR code in the video call") + } append(", or you can create the invitation link.") - } - }, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.caption, - modifier = Modifier - .padding(horizontal = 16.dp) - .padding(top = 4.dp) - ) + }, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.caption, + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 4.dp) + ) + } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt index 4b338f4f3d..65d882ae71 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/NewChatSheet.kt @@ -14,8 +14,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import chat.simplex.app.Pages import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.SimpleXTheme @@ -24,7 +22,7 @@ import chat.simplex.app.views.helpers.withApi import com.google.accompanist.permissions.rememberPermissionState @Composable -fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController, nav: NavController) { +fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController) { val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA) NewChatSheetLayout( addContact = { @@ -34,13 +32,13 @@ fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController, nav: Nav // hide spinner if (chatModel.connReqInvitation != null) { newChatCtrl.collapse() - nav.navigate(Pages.AddContact.route) + ModalManager.shared.showModal { AddContactView(chatModel) } } } }, scanCode = { newChatCtrl.collapse() - nav.navigate(Pages.Connect.route) + ModalManager.shared.showCustomModal { close -> ConnectContactView(chatModel, close) } cameraPermissionState.launchPermissionRequest() } ) @@ -48,24 +46,34 @@ fun NewChatSheet(chatModel: ChatModel, newChatCtrl: ScaffoldController, nav: Nav @Composable fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) { - Row(Modifier + Row( + Modifier .fillMaxWidth() .padding(horizontal = 8.dp, vertical = 48.dp), horizontalArrangement = Arrangement.SpaceEvenly ) { - Box(Modifier.weight(1F).fillMaxWidth()) { + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { ActionButton( "Add contact", "(create QR code\nor link)", Icons.Outlined.PersonAdd, click = addContact ) } - Box(Modifier.weight(1F).fillMaxWidth()) { + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { ActionButton( "Scan QR code", "(in person or in video call)", Icons.Outlined.QrCode, click = scanCode ) } - Box(Modifier.weight(1F).fillMaxWidth()) { + Box( + Modifier + .weight(1F) + .fillMaxWidth()) { ActionButton( "Create Group", "(coming soon!)", Icons.Outlined.GroupAdd, disabled = true diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt index fcc225912e..9d7d380c7a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HelpView.kt @@ -1,8 +1,8 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -10,40 +10,27 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.ChatHelpView -import chat.simplex.app.views.helpers.CloseSheetBar @Composable -fun HelpView(chatModel: ChatModel, nav: NavController) { +fun HelpView(chatModel: ChatModel) { val user = chatModel.currentUser.value if (user != null) { - HelpLayout( - displayName = user.profile.displayName, - back = nav::popBackStack - ) + HelpLayout(displayName = user.profile.displayName) } } @Composable -fun HelpLayout(displayName: String, back: () -> Unit) { - Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - .padding(horizontal = 8.dp), - horizontalAlignment = Alignment.Start - ) { - CloseSheetBar(back) +fun HelpLayout(displayName: String) { + Column(horizontalAlignment = Alignment.Start) { Text( "Welcome $displayName!", Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1, - color = MaterialTheme.colors.onBackground ) - ChatHelpView({}, false) + ChatHelpView() } } @@ -56,9 +43,6 @@ fun HelpLayout(displayName: String, back: () -> Unit) { @Composable fun PreviewHelpView() { SimpleXTheme { - HelpLayout( - displayName = "Alice", - back = {} - ) + HelpLayout(displayName = "Alice") } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt index 0e9ec8fb87..40def46dba 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/MarkdownHelpView.kt @@ -1,69 +1,53 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.selection.SelectionContainer -import androidx.compose.material.* +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.* import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.Format import chat.simplex.app.model.FormatColor import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.CloseSheetBar @Composable -fun MarkdownHelpView(nav: NavController) { - MarkdownHelpLayout(nav::popBackStack) -} - -@Composable -fun MarkdownHelpLayout(back: () -> Unit) { - Surface( - Modifier - .background(MaterialTheme.colors.background) - .fillMaxSize() - ) { - Column { - CloseSheetBar(back) - Column(Modifier.padding(horizontal = 16.dp)) { - Text( - "How to use markdown", - style = MaterialTheme.typography.h1, - ) - Text( - "You can use markdown to format messages:", - Modifier.padding(vertical = 16.dp) - ) - MdFormat("*bold*", "bold text", Format.Bold()) - MdFormat("_italic_", "italic text", Format.Italic()) - MdFormat("~strike~", "strikethrough text", Format.StrikeThrough()) - MdFormat("`code`", "a = b + c", Format.Snippet()) - Row { - MdSyntax("!1 colored!") - Text(buildAnnotatedString { - withStyle(Format.Colored(FormatColor.red).style) { append("red text") } - append(" (") - appendColor(this, "1", FormatColor.red, ", ") - appendColor(this, "2", FormatColor.green, ", ") - appendColor(this, "3", FormatColor.blue, ", ") - appendColor(this, "4", FormatColor.yellow, ", ") - appendColor(this, "5", FormatColor.cyan, ", ") - appendColor(this, "6", FormatColor.magenta, ")") - }) - } - Row { - MdSyntax("#secret#") - SelectionContainer { - Text(buildAnnotatedString { - withStyle(Format.Secret().style) { append("secret text") } - }) - } - } +fun MarkdownHelpView() { + Column(Modifier.padding(horizontal = 16.dp)) { + Text( + "How to use markdown", + style = MaterialTheme.typography.h1, + ) + Text( + "You can use markdown to format messages:", + Modifier.padding(vertical = 16.dp) + ) + MdFormat("*bold*", "bold text", Format.Bold()) + MdFormat("_italic_", "italic text", Format.Italic()) + MdFormat("~strike~", "strikethrough text", Format.StrikeThrough()) + MdFormat("`code`", "a = b + c", Format.Snippet()) + Row { + MdSyntax("!1 colored!") + Text(buildAnnotatedString { + withStyle(Format.Colored(FormatColor.red).style) { append("red text") } + append(" (") + appendColor(this, "1", FormatColor.red, ", ") + appendColor(this, "2", FormatColor.green, ", ") + appendColor(this, "3", FormatColor.blue, ", ") + appendColor(this, "4", FormatColor.yellow, ", ") + appendColor(this, "5", FormatColor.cyan, ", ") + appendColor(this, "6", FormatColor.magenta, ")") + }) + } + Row { + MdSyntax("#secret#") + SelectionContainer { + Text(buildAnnotatedString { + withStyle(Format.Secret().style) { append("secret text") } + }) } } } @@ -102,6 +86,6 @@ fun appendColor(b: AnnotatedString.Builder, s: String, c: FormatColor, after: St @Composable fun PreviewMarkdownHelpView() { SimpleXTheme { - MarkdownHelpLayout(back = {}) + MarkdownHelpView() } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index 9581ca62bc..0e55619137 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -17,20 +17,24 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import chat.simplex.app.Pages import chat.simplex.app.R import chat.simplex.app.model.ChatModel import chat.simplex.app.model.Profile import chat.simplex.app.ui.theme.SimpleXTheme +import chat.simplex.app.views.TerminalView +import chat.simplex.app.views.newchat.ModalManager @Composable -fun SettingsView(chatModel: ChatModel, nav: NavController) { +fun SettingsView(chatModel: ChatModel) { val user = chatModel.currentUser.value if (user != null) { SettingsLayout( profile = user.profile, - navigate = nav::navigate +// showModal = { modal -> ModalManager.shared.showModal { modal(chatModel) } }, + showProfile = { ModalManager.shared.showModal { UserProfileView(chatModel) } }, + showAddress = { ModalManager.shared.showModal { UserAddressView(chatModel) } }, + showHelp = { ModalManager.shared.showModal { HelpView(chatModel) } }, + showTerminal = { ModalManager.shared.showCustomModal { close -> TerminalView(chatModel, close) } } ) } } @@ -41,7 +45,11 @@ val simplexTeamUri = @Composable fun SettingsLayout( profile: Profile, - navigate: (String) -> Unit +// showModal: (modal: @Composable (ChatModel) -> Unit) -> Unit, + showProfile: () -> Unit, + showAddress: () -> Unit, + showHelp: () -> Unit, + showTerminal: () -> Unit ) { val uriHandler = LocalUriHandler.current Surface( @@ -62,7 +70,7 @@ fun SettingsLayout( ) Spacer(Modifier.height(30.dp)) - SettingsSectionView({ navigate(Pages.UserProfile.route) }, 60.dp) { + SettingsSectionView(showProfile, 60.dp) { Icon( Icons.Outlined.AccountCircle, contentDescription = "Avatar Placeholder", @@ -78,7 +86,7 @@ fun SettingsLayout( } } Divider(Modifier.padding(horizontal = 8.dp)) - SettingsSectionView({ navigate(Pages.UserAddress.route) }) { + SettingsSectionView(showAddress) { Icon( Icons.Outlined.QrCode, contentDescription = "Address", @@ -88,7 +96,7 @@ fun SettingsLayout( } Spacer(Modifier.height(24.dp)) - SettingsSectionView({ navigate(Pages.Help.route) }) { + SettingsSectionView(showHelp) { Icon( Icons.Outlined.HelpOutline, contentDescription = "Chat help", @@ -96,7 +104,7 @@ fun SettingsLayout( Spacer(Modifier.padding(horizontal = 4.dp)) Text("How to use SimpleX Chat") } - SettingsSectionView({ navigate(Pages.Markdown.route) }) { + SettingsSectionView({ ModalManager.shared.showModal { MarkdownHelpView() } }) { Icon( Icons.Outlined.TextFormat, contentDescription = "Markdown help", @@ -130,7 +138,7 @@ fun SettingsLayout( } Spacer(Modifier.height(24.dp)) - SettingsSectionView({ navigate(Pages.Terminal.route) }) { + SettingsSectionView(showTerminal) { Icon( painter = painterResource(id = R.drawable.ic_outline_terminal), contentDescription = "Chat console", @@ -183,7 +191,10 @@ fun PreviewSettingsLayout() { SimpleXTheme { SettingsLayout( profile = Profile.sampleData, - navigate = {} + showProfile = {}, + showAddress = {}, + showHelp = {}, + showTerminal = {} ) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt index d082048f8d..a6a36bf16f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserAddressView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material.MaterialTheme import androidx.compose.material.Text @@ -14,7 +13,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.ui.theme.SimpleButton import chat.simplex.app.ui.theme.SimpleXTheme @@ -22,11 +20,10 @@ import chat.simplex.app.views.helpers.* import chat.simplex.app.views.newchat.QRCode @Composable -fun UserAddressView(chatModel: ChatModel, nav: NavController) { +fun UserAddressView(chatModel: ChatModel) { val cxt = LocalContext.current UserAddressLayout( userAddress = chatModel.userAddress.value, - back = { nav.popBackStack() }, createAddress = { withApi { chatModel.userAddress.value = chatModel.controller.apiCreateUserAddress() @@ -34,7 +31,7 @@ fun UserAddressView(chatModel: ChatModel, nav: NavController) { }, share = { userAddress: String -> shareText(cxt, userAddress) }, deleteAddress = { - chatModel.alertManager.showAlertMsg( + AlertManager.shared.showAlertMsg( title = "Delete address?", text = "All your contacts will remain connected", confirmText = "Delete", @@ -52,31 +49,23 @@ fun UserAddressView(chatModel: ChatModel, nav: NavController) { @Composable fun UserAddressLayout( userAddress: String?, - back: () -> Unit, createAddress: () -> Unit, share: (String) -> Unit, deleteAddress: () -> Unit ) { Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - .padding(horizontal = 8.dp), horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.Top ) { - CloseSheetBar(back) Text( "Your chat address", Modifier.padding(bottom = 24.dp), style = MaterialTheme.typography.h1, - color = MaterialTheme.colors.onBackground ) Text( "You can share your address as a link or as a QR code - anybody will be able to connect to you, " + "and if you later delete it - you won't lose your contacts.", Modifier.padding(bottom = 24.dp), - color = MaterialTheme.colors.onBackground ) Column( Modifier @@ -119,7 +108,6 @@ fun PreviewUserAddressLayoutNoAddress() { SimpleXTheme { UserAddressLayout( userAddress = null, - back = {}, createAddress = {}, share = { _ -> }, deleteAddress = {}, @@ -138,7 +126,6 @@ fun PreviewUserAddressLayoutAddressCreated() { SimpleXTheme { UserAddressLayout( userAddress = "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", - back = {}, createAddress = {}, share = { _ -> }, deleteAddress = {}, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt index 6af1f44db1..4dd2ad0d44 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.BasicTextField @@ -15,15 +14,13 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.navigation.NavController import chat.simplex.app.model.ChatModel import chat.simplex.app.model.Profile import chat.simplex.app.ui.theme.SimpleXTheme -import chat.simplex.app.views.helpers.CloseSheetBar import chat.simplex.app.views.helpers.withApi @Composable -fun UserProfileView(chatModel: ChatModel, nav: NavController) { +fun UserProfileView(chatModel: ChatModel) { val user = chatModel.currentUser.value if (user != null) { var editProfile by remember { mutableStateOf(false) } @@ -31,7 +28,6 @@ fun UserProfileView(chatModel: ChatModel, nav: NavController) { UserProfileLayout( editProfile = editProfile, profile = profile, - back = { nav.popBackStack() }, editProfileOff = { editProfile = false }, editProfileOn = { editProfile = true }, saveProfile = { displayName: String, fullName: String -> @@ -54,19 +50,11 @@ fun UserProfileView(chatModel: ChatModel, nav: NavController) { fun UserProfileLayout( editProfile: Boolean, profile: Profile, - back: () -> Unit, editProfileOff: () -> Unit, editProfileOn: () -> Unit, saveProfile: (String, String) -> Unit, ) { - Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - .padding(horizontal = 8.dp), - horizontalAlignment = Alignment.Start - ) { - CloseSheetBar(back) + Column(horizontalAlignment = Alignment.Start) { Text( "Your chat profile", Modifier.padding(bottom = 24.dp), @@ -185,7 +173,6 @@ fun PreviewUserProfileLayoutEditOff() { UserProfileLayout( profile = Profile.sampleData, editProfile = false, - back = {}, editProfileOff = {}, editProfileOn = {}, saveProfile = { _, _ -> } @@ -205,7 +192,6 @@ fun PreviewUserProfileLayoutEditOn() { UserProfileLayout( profile = Profile.sampleData, editProfile = true, - back = {}, editProfileOff = {}, editProfileOn = {}, saveProfile = { _, _ -> } diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 617f9d6479..09221d89a5 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -13,16 +13,6 @@ 5C116CDD27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; 5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; - 5C27D00827C7D8B500DD6182 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00327C7D8B500DD6182 /* libgmpxx.a */; }; - 5C27D00927C7D8B500DD6182 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00327C7D8B500DD6182 /* libgmpxx.a */; }; - 5C27D00A27C7D8B500DD6182 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00427C7D8B500DD6182 /* libgmp.a */; }; - 5C27D00B27C7D8B500DD6182 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00427C7D8B500DD6182 /* libgmp.a */; }; - 5C27D00C27C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00527C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a */; }; - 5C27D00D27C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00527C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a */; }; - 5C27D00E27C7D8B500DD6182 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00627C7D8B500DD6182 /* libffi.a */; }; - 5C27D00F27C7D8B500DD6182 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00627C7D8B500DD6182 /* libffi.a */; }; - 5C27D01027C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00727C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a */; }; - 5C27D01127C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C27D00727C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a */; }; 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; @@ -39,6 +29,16 @@ 5C5346A927B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; }; 5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; }; 5C577F7E27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; }; + 5C67D2FA27CB9FA100E4261F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F527CB9FA100E4261F /* libgmp.a */; }; + 5C67D2FB27CB9FA100E4261F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F527CB9FA100E4261F /* libgmp.a */; }; + 5C67D2FC27CB9FA100E4261F /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F627CB9FA100E4261F /* libgmpxx.a */; }; + 5C67D2FD27CB9FA100E4261F /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F627CB9FA100E4261F /* libgmpxx.a */; }; + 5C67D2FE27CB9FA100E4261F /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F727CB9FA100E4261F /* libffi.a */; }; + 5C67D2FF27CB9FA100E4261F /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F727CB9FA100E4261F /* libffi.a */; }; + 5C67D30027CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F827CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a */; }; + 5C67D30127CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F827CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a */; }; + 5C67D30227CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F927CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a */; }; + 5C67D30327CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C67D2F927CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a */; }; 5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; 5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; 5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; }; @@ -125,11 +125,6 @@ 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = ""; }; 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = ""; }; 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = ""; }; - 5C27D00327C7D8B500DD6182 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - 5C27D00427C7D8B500DD6182 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - 5C27D00527C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a"; sourceTree = ""; }; - 5C27D00627C7D8B500DD6182 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 5C27D00727C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a"; sourceTree = ""; }; 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = ""; }; 5C2E260927A2C63500F70299 /* MyPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = MyPlayground.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = ""; }; @@ -140,6 +135,11 @@ 5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX (iOS).entitlements"; sourceTree = ""; }; 5C5346A727B59A6A004DF848 /* ChatHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelp.swift; sourceTree = ""; }; 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHelp.swift; sourceTree = ""; }; + 5C67D2F527CB9FA100E4261F /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 5C67D2F627CB9FA100E4261F /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + 5C67D2F727CB9FA100E4261F /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + 5C67D2F827CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a"; sourceTree = ""; }; + 5C67D2F927CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a"; sourceTree = ""; }; 5C6AD81227A834E300348BD7 /* NewChatButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatButton.swift; sourceTree = ""; }; 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMetaView.swift; sourceTree = ""; }; 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavLinkPlain.swift; sourceTree = ""; }; @@ -186,13 +186,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5C27D00827C7D8B500DD6182 /* libgmpxx.a in Frameworks */, - 5C27D01027C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a in Frameworks */, - 5C27D00A27C7D8B500DD6182 /* libgmp.a in Frameworks */, + 5C67D30227CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a in Frameworks */, 5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */, + 5C67D2FE27CB9FA100E4261F /* libffi.a in Frameworks */, + 5C67D2FC27CB9FA100E4261F /* libgmpxx.a in Frameworks */, 5C764E83279C748B000C6508 /* libz.tbd in Frameworks */, - 5C27D00E27C7D8B500DD6182 /* libffi.a in Frameworks */, - 5C27D00C27C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a in Frameworks */, + 5C67D2FA27CB9FA100E4261F /* libgmp.a in Frameworks */, + 5C67D30027CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a in Frameworks */, 5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -201,13 +201,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5C27D00927C7D8B500DD6182 /* libgmpxx.a in Frameworks */, - 5C27D00F27C7D8B500DD6182 /* libffi.a in Frameworks */, + 5C67D2FD27CB9FA100E4261F /* libgmpxx.a in Frameworks */, 5C764E85279C748C000C6508 /* libz.tbd in Frameworks */, - 5C27D00B27C7D8B500DD6182 /* libgmp.a in Frameworks */, - 5C27D01127C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a in Frameworks */, - 5C27D00D27C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a in Frameworks */, + 5C67D2FB27CB9FA100E4261F /* libgmp.a in Frameworks */, + 5C67D2FF27CB9FA100E4261F /* libffi.a in Frameworks */, 5C764E84279C748C000C6508 /* libiconv.tbd in Frameworks */, + 5C67D30127CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a in Frameworks */, + 5C67D30327CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -259,11 +259,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - 5C27D00627C7D8B500DD6182 /* libffi.a */, - 5C27D00427C7D8B500DD6182 /* libgmp.a */, - 5C27D00327C7D8B500DD6182 /* libgmpxx.a */, - 5C27D00727C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY-ghc8.10.7.a */, - 5C27D00527C7D8B500DD6182 /* libHSsimplex-chat-1.2.1-KSWVFEZPyZUBOUtDs8BKKY.a */, + 5C67D2F727CB9FA100E4261F /* libffi.a */, + 5C67D2F527CB9FA100E4261F /* libgmp.a */, + 5C67D2F627CB9FA100E4261F /* libgmpxx.a */, + 5C67D2F827CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM-ghc8.10.7.a */, + 5C67D2F927CB9FA100E4261F /* libHSsimplex-chat-1.3.0-7NpjDBQ3CKKBWRdMwbLLzM.a */, ); path = Libraries; sourceTree = ""; @@ -833,6 +833,7 @@ "$(inherited)", "@executable_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = ""; "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios"; "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim"; MARKETING_VERSION = 0.4; diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index e753ed7dfb..08c2842af3 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -123,10 +123,7 @@ 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 + CRPendingSubSummary _ -> [] CRSndFileSubError SndFileTransfer {fileId, fileName} e -> ["sent file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e] CRRcvFileSubError RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} e ->