From d1ae3ba2d39fdd2aa9c67a9fa6ab5de7ddf955aa Mon Sep 17 00:00:00 2001 From: Diogo Date: Mon, 18 Nov 2024 15:36:54 +0000 Subject: [PATCH] desktop, android: add address card to chat list and remove address from onboarding (#5177) * desktop, android: add address card to chat list * add create address button to address learn more view * envelope size to match avatars * refactor * no color for info icon * envelope padding * remove address from onboarding * show create in address card info * backwards compatibility for address onboarding step * paddings between cards * paddings * toolbar -> chats -> cards * dont hide address card * update string --------- Co-authored-by: Evgeny Poberezkin --- .../kotlin/chat/simplex/common/App.kt | 3 +- .../chat/simplex/common/model/SimpleXAPI.kt | 3 + .../chat/simplex/common/platform/Core.kt | 7 +- .../chat/simplex/common/views/WelcomeView.kt | 6 +- .../common/views/chatlist/ChatListView.kt | 200 +++++++++++++----- .../views/onboarding/CreateSimpleXAddress.kt | 197 ----------------- .../views/onboarding/SetNotificationsMode.kt | 14 ++ .../onboarding/SetupDatabasePassphrase.kt | 2 +- .../usersettings/UserAddressLearnMore.kt | 27 ++- .../views/usersettings/UserAddressView.kt | 49 +++-- .../commonMain/resources/MR/base/strings.xml | 1 + 11 files changed, 233 insertions(+), 276 deletions(-) delete mode 100644 apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/CreateSimpleXAddress.kt diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index ee38cb80fe..7af1d574ad 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -194,7 +194,8 @@ fun MainScreen() { OnboardingStage.Step2_CreateProfile -> CreateFirstProfile(chatModel) {} OnboardingStage.LinkAMobile -> LinkAMobile() OnboardingStage.Step2_5_SetupDatabasePassphrase -> SetupDatabasePassphrase(chatModel) - OnboardingStage.Step3_CreateSimpleXAddress -> CreateSimpleXAddress(chatModel, null) + // Ensure backwards compatibility with old onboarding stage for address creation, otherwise notification setup would be skipped + OnboardingStage.Step3_CreateSimpleXAddress -> SetNotificationsMode(chatModel) OnboardingStage.Step4_SetNotificationsMode -> SetNotificationsMode(chatModel) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index fab85fa679..5674920914 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -179,6 +179,7 @@ class AppPreferences { val liveMessageAlertShown = mkBoolPreference(SHARED_PREFS_LIVE_MESSAGE_ALERT_SHOWN, false) val showHiddenProfilesNotice = mkBoolPreference(SHARED_PREFS_SHOW_HIDDEN_PROFILES_NOTICE, true) val oneHandUICardShown = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN, false) + val addressCreationCardShown = mkBoolPreference(SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN, false) val showMuteProfileAlert = mkBoolPreference(SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT, true) val appLanguage = mkStrPreference(SHARED_PREFS_APP_LANGUAGE, null) val appUpdateChannel = mkEnumPreference(SHARED_PREFS_APP_UPDATE_CHANNEL, AppUpdatesChannel.DISABLED) { AppUpdatesChannel.entries.firstOrNull { it.name == this } } @@ -254,6 +255,7 @@ class AppPreferences { val hintPreferences: List, Boolean>> = listOf( laNoticeShown to false, oneHandUICardShown to false, + addressCreationCardShown to false, liveMessageAlertShown to false, showHiddenProfilesNotice to true, showMuteProfileAlert to true, @@ -408,6 +410,7 @@ class AppPreferences { private const val SHARED_PREFS_LIVE_MESSAGE_ALERT_SHOWN = "LiveMessageAlertShown" private const val SHARED_PREFS_SHOW_HIDDEN_PROFILES_NOTICE = "ShowHiddenProfilesNotice" private const val SHARED_PREFS_ONE_HAND_UI_CARD_SHOWN = "OneHandUICardShown" + private const val SHARED_PREFS_ADDRESS_CREATION_CARD_SHOWN = "AddressCreationCardShown" private const val SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT = "ShowMuteProfileAlert" private const val SHARED_PREFS_STORE_DB_PASSPHRASE = "StoreDBPassphrase" private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt index 57b93d4d6e..79132b5eb1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt @@ -137,8 +137,13 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat } } else if (startChat().await()) { val savedOnboardingStage = appPreferences.onboardingStage.get() + val next = if (appPlatform.isAndroid) { + OnboardingStage.Step4_SetNotificationsMode + } else { + OnboardingStage.OnboardingComplete + } val newStage = if (listOf(OnboardingStage.Step1_SimpleXInfo, OnboardingStage.Step2_CreateProfile).contains(savedOnboardingStage) && chatModel.users.size == 1) { - OnboardingStage.Step3_CreateSimpleXAddress + next } else { savedOnboardingStage } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt index e1e3dcb56b..17658d23e8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt @@ -165,7 +165,7 @@ fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) { if (!chatModel.connectedToRemote()) { chatModel.localUserCreated.value = true } - controller.appPrefs.onboardingStage.set(OnboardingStage.Step3_CreateSimpleXAddress) + controller.appPrefs.onboardingStage.set(OnboardingStage.Step4_SetNotificationsMode) controller.startChat(user) controller.switchUIRemoteHost(null) close() @@ -181,7 +181,7 @@ fun createProfileInProfiles(chatModel: ChatModel, displayName: String, close: () chatModel.currentUser.value = user if (chatModel.users.isEmpty()) { chatModel.controller.startChat(user) - chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.Step3_CreateSimpleXAddress) + chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.Step4_SetNotificationsMode) } else { val users = chatModel.controller.listUsers(rhId) chatModel.users.clear() @@ -204,7 +204,7 @@ fun createProfileOnboarding(chatModel: ChatModel, displayName: String, close: () onboardingStage.set(if (appPlatform.isDesktop && chatModel.controller.appPrefs.initialRandomDBPassphrase.get() && !chatModel.desktopOnboardingRandomPassword.value) { OnboardingStage.Step2_5_SetupDatabasePassphrase } else { - OnboardingStage.Step3_CreateSimpleXAddress + OnboardingStage.Step4_SetNotificationsMode }) } else { // the next two lines are only needed for failure case when because of the database error the app gets stuck on on-boarding screen, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 586bca87d0..9661a305cc 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -34,7 +34,9 @@ import chat.simplex.common.platform.* import chat.simplex.common.views.call.Call import chat.simplex.common.views.chat.item.CIFileViewScope import chat.simplex.common.views.chat.topPaddingToContent +import chat.simplex.common.views.mkValidName import chat.simplex.common.views.newchat.* +import chat.simplex.common.views.showInvalidNameAlert import chat.simplex.common.views.usersettings.* import chat.simplex.res.MR import kotlinx.coroutines.* @@ -72,58 +74,39 @@ private fun showNewChatSheet(oneHandUI: State) { @Composable fun ToggleChatListCard() { - Column( - modifier = Modifier - .padding(16.dp) - .clip(RoundedCornerShape(18.dp)) + ChatListCard( + close = { + appPrefs.oneHandUICardShown.set(true) + AlertManager.shared.showAlertMsg( + title = generalGetString(MR.strings.one_hand_ui), + text = generalGetString(MR.strings.one_hand_ui_change_instruction), + ) + } ) { - Box( + Column( modifier = Modifier - .background(MaterialTheme.appColors.sentMessage) + .padding(horizontal = DEFAULT_PADDING) + .padding(top = DEFAULT_PADDING) ) { - Box( - modifier = Modifier.fillMaxWidth().matchParentSize().padding(5.dp), - contentAlignment = Alignment.TopEnd + Row( + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() ) { - IconButton( - onClick = { - appPrefs.oneHandUICardShown.set(true) - AlertManager.shared.showAlertMsg( - title = generalGetString(MR.strings.one_hand_ui), - text = generalGetString(MR.strings.one_hand_ui_change_instruction), - ) - } - ) { - Icon( - painterResource(MR.images.ic_close), stringResource(MR.strings.back), tint = MaterialTheme.colors.secondary - ) - } + Text(stringResource(MR.strings.one_hand_ui_card_title), style = MaterialTheme.typography.h3) } - Column( - modifier = Modifier - .padding(horizontal = DEFAULT_PADDING) - .padding(top = DEFAULT_PADDING) + Row( + Modifier.fillMaxWidth().padding(top = 6.dp, bottom = 12.dp), + verticalAlignment = Alignment.CenterVertically ) { - Row( - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - Text(stringResource(MR.strings.one_hand_ui_card_title), style = MaterialTheme.typography.h3) - } - Row( - Modifier.fillMaxWidth().padding(top = 6.dp, bottom = 12.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Text(stringResource(MR.strings.one_hand_ui), Modifier.weight(10f), style = MaterialTheme.typography.body1) + Text(stringResource(MR.strings.one_hand_ui), Modifier.weight(10f), style = MaterialTheme.typography.body1) - Spacer(Modifier.fillMaxWidth().weight(1f)) + Spacer(Modifier.fillMaxWidth().weight(1f)) - SharedPreferenceToggle( - appPrefs.oneHandUI, - enabled = true - ) - } + SharedPreferenceToggle( + appPrefs.oneHandUI, + enabled = true + ) } } } @@ -196,6 +179,94 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow Unit, + onCardClick: (() -> Unit)? = null, + content: @Composable BoxScope.() -> Unit +) { + Column( + modifier = Modifier.clip(RoundedCornerShape(18.dp)) + ) { + Box( + modifier = Modifier + .background(MaterialTheme.appColors.sentMessage) + .clickable { + onCardClick?.invoke() + } + ) { + Box( + modifier = Modifier.fillMaxWidth().matchParentSize().padding(5.dp), + contentAlignment = Alignment.TopEnd + ) { + IconButton( + onClick = { + close() + } + ) { + Icon( + painterResource(MR.images.ic_close), stringResource(MR.strings.back), tint = MaterialTheme.colors.secondary + ) + } + } + content() + } + } +} + +@Composable +private fun AddressCreationCard() { + ChatListCard( + close = { + appPrefs.addressCreationCardShown.set(true) + AlertManager.shared.showAlertMsg( + title = generalGetString(MR.strings.simplex_address), + text = generalGetString(MR.strings.address_creation_instruction), + ) + }, + onCardClick = { + ModalManager.start.showModal { + UserAddressLearnMore(showCreateAddressButton = true) + } + } + ) { + Box(modifier = Modifier.matchParentSize().padding(end = (DEFAULT_PADDING_HALF + 2.dp) * fontSizeSqrtMultiplier, bottom = 2.dp), contentAlignment = Alignment.BottomEnd) { + TextButton( + onClick = { + ModalManager.start.showModalCloseable { close -> + UserAddressView(chatModel = chatModel, shareViaProfile = false, autoCreateAddress = true, close = close) + } + }, + ) { + Text(stringResource(MR.strings.create_address_button), style = MaterialTheme.typography.body1) + } + } + Row( + Modifier + .fillMaxWidth() + .padding(DEFAULT_PADDING), + verticalAlignment = Alignment.CenterVertically + ) { + Box(Modifier.padding(vertical = 4.dp)) { + Box(Modifier.background(MaterialTheme.colors.primary, CircleShape).padding(12.dp)) { + ProfileImage(size = 37.dp, null, icon = MR.images.ic_mail_filled, color = Color.White, backgroundColor = Color.Red) + } + } + Column(modifier = Modifier.padding(start = DEFAULT_PADDING)) { + Text(stringResource(MR.strings.your_simplex_contact_address), style = MaterialTheme.typography.h3) + Spacer(Modifier.fillMaxWidth().padding(DEFAULT_PADDING_HALF)) + Row(verticalAlignment = Alignment.CenterVertically) { + Text(stringResource(MR.strings.how_to_use_simplex_chat), Modifier.padding(end = DEFAULT_SPACE_AFTER_ICON), style = MaterialTheme.typography.body1) + Icon( + painterResource(MR.images.ic_info), + null, + ) + } + } + } + } +} + @Composable private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState, listState: LazyListState) { if (!chatModel.desktopNoUserNoRemote) { @@ -641,6 +712,7 @@ private fun BoxScope.ChatList(searchText: MutableState, listStat val keyboardState by getKeyboardState() val oneHandUI = remember { appPrefs.oneHandUI.state } val oneHandUICardShown = remember { appPrefs.oneHandUICardShown.state } + val addressCreationCardShown = remember { appPrefs.addressCreationCardShown.state } LaunchedEffect(listState.firstVisibleItemIndex, listState.firstVisibleItemScrollOffset) { val currentIndex = listState.firstVisibleItemIndex @@ -709,20 +781,15 @@ private fun BoxScope.ChatList(searchText: MutableState, listStat } } } - if (!oneHandUICardShown.value && chats.size > 1) { - item { - ToggleChatListCard() - } - } itemsIndexed(chats, key = { _, chat -> chat.remoteHostId to chat.id }) { index, chat -> val nextChatSelected = remember(chat.id, chats) { derivedStateOf { chatModel.chatId.value != null && chats.getOrNull(index + 1)?.id == chatModel.chatId.value } } ChatListNavLinkView(chat, nextChatSelected) } - if (!oneHandUICardShown.value && chats.size <= 1) { + if (!oneHandUICardShown.value || !addressCreationCardShown.value) { item { - ToggleChatListCard() + ChatListFeatureCards() } } if (appPlatform.isAndroid) { @@ -741,7 +808,36 @@ private fun BoxScope.ChatList(searchText: MutableState, listStat } if (!oneHandUICardShown.value) { LaunchedEffect(chats.size) { - if (chats.size >= 3) appPrefs.oneHandUICardShown.set(true) + if (chats.size >= 3) { + appPrefs.oneHandUICardShown.set(true) + } + } + } + + if (!addressCreationCardShown.value) { + LaunchedEffect(chatModel.userAddress.value) { + if (chatModel.userAddress.value != null) { + appPrefs.addressCreationCardShown.set(true) + } + } + } +} + +@Composable +private fun ChatListFeatureCards() { + val oneHandUI = remember { appPrefs.oneHandUI.state } + val oneHandUICardShown = remember { appPrefs.oneHandUICardShown.state } + val addressCreationCardShown = remember { appPrefs.addressCreationCardShown.state } + + Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) { + if (!oneHandUICardShown.value && !oneHandUI.value) { + ToggleChatListCard() + } + if (!addressCreationCardShown.value) { + AddressCreationCard() + } + if (!oneHandUICardShown.value && oneHandUI.value) { + ToggleChatListCard() } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/CreateSimpleXAddress.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/CreateSimpleXAddress.kt deleted file mode 100644 index 28ad0fdb7b..0000000000 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/CreateSimpleXAddress.kt +++ /dev/null @@ -1,197 +0,0 @@ -package chat.simplex.common.views.onboarding - -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.LocalUriHandler -import androidx.compose.ui.text.font.FontWeight -import dev.icerock.moko.resources.compose.painterResource -import dev.icerock.moko.resources.compose.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import chat.simplex.common.model.* -import chat.simplex.common.platform.* -import chat.simplex.common.ui.theme.* -import chat.simplex.common.views.helpers.* -import chat.simplex.common.views.newchat.SimpleXLinkQRCode -import chat.simplex.common.views.newchat.simplexChatLink -import chat.simplex.res.MR - -@Composable -fun CreateSimpleXAddress(m: ChatModel, rhId: Long?) { - var progressIndicator by remember { mutableStateOf(false) } - val userAddress = remember { m.userAddress } - val clipboard = LocalClipboardManager.current - val uriHandler = LocalUriHandler.current - - LaunchedEffect(Unit) { - prepareChatBeforeAddressCreation(rhId) - } - - CreateSimpleXAddressLayout( - userAddress.value, - share = { address: String -> clipboard.shareText(address) }, - sendEmail = { address -> - uriHandler.sendEmail( - generalGetString(MR.strings.email_invite_subject), - generalGetString(MR.strings.email_invite_body).format(simplexChatLink(address.connReqContact)) - ) - }, - createAddress = { - withBGApi { - progressIndicator = true - val connReqContact = m.controller.apiCreateUserAddress(rhId) - if (connReqContact != null) { - m.userAddress.value = UserContactLinkRec(connReqContact) - progressIndicator = false - } - } - }, - nextStep = { - val next = if (appPlatform.isAndroid) { - OnboardingStage.Step4_SetNotificationsMode - } else { - OnboardingStage.OnboardingComplete - } - m.controller.appPrefs.onboardingStage.set(next) - }, - ) - - if (progressIndicator) { - ProgressIndicator() - } -} - -@Composable -private fun CreateSimpleXAddressLayout( - userAddress: UserContactLinkRec?, - share: (String) -> Unit, - sendEmail: (UserContactLinkRec) -> Unit, - createAddress: () -> Unit, - nextStep: () -> Unit, -) { - CompositionLocalProvider(LocalAppBarHandler provides rememberAppBarHandler()) { - ModalView({}, showClose = false) { - ColumnWithScrollBar( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - AppBarTitle(stringResource(MR.strings.simplex_address)) - - Spacer(Modifier.weight(1f)) - - if (userAddress != null) { - SimpleXLinkQRCode(userAddress.connReqContact) - Spacer(Modifier.height(DEFAULT_PADDING_HALF)) - Row { - ShareAddressButton { share(simplexChatLink(userAddress.connReqContact)) } - Spacer(Modifier.width(DEFAULT_PADDING * 2)) - ShareViaEmailButton { sendEmail(userAddress) } - } - Spacer(Modifier.height(DEFAULT_PADDING)) - Spacer(Modifier.weight(1f)) - Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) { - OnboardingActionButton( - modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier.widthIn(min = 300.dp), - labelId = MR.strings.continue_to_next_step, - onboarding = null, - onclick = nextStep - ) - // Reserve space - TextButtonBelowOnboardingButton("", null) - } - } else { - Button(createAddress, Modifier, shape = CircleShape, contentPadding = PaddingValues()) { - Icon(painterResource(MR.images.ic_mail_filled), null, Modifier.size(100.dp).background(MaterialTheme.colors.primary, CircleShape).padding(25.dp), tint = Color.White) - } - Spacer(Modifier.height(DEFAULT_PADDING)) - Spacer(Modifier.weight(1f)) - Text(stringResource(MR.strings.create_simplex_address), style = MaterialTheme.typography.h3, fontWeight = FontWeight.Bold) - TextBelowButton(stringResource(MR.strings.you_can_make_address_visible_via_settings)) - Spacer(Modifier.height(DEFAULT_PADDING)) - Spacer(Modifier.weight(1f)) - - Column(Modifier.widthIn(max = if (appPlatform.isAndroid) 450.dp else 1000.dp).align(Alignment.CenterHorizontally), horizontalAlignment = Alignment.CenterHorizontally) { - OnboardingActionButton( - modifier = if (appPlatform.isAndroid) Modifier.padding(horizontal = DEFAULT_PADDING * 2).fillMaxWidth() else Modifier.widthIn(min = 300.dp), - labelId = MR.strings.create_address_button, - onboarding = null, - onclick = createAddress - ) - TextButtonBelowOnboardingButton(stringResource(MR.strings.dont_create_address), nextStep) - } - } - } - } - } -} - -@Composable -fun ShareAddressButton(onClick: () -> Unit) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - IconButton(onClick, Modifier.padding(bottom = DEFAULT_PADDING_HALF).border(1.dp, MaterialTheme.colors.secondary.copy(0.1f), CircleShape)) { - Icon( - painterResource(MR.images.ic_share_filled), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary, - modifier = Modifier.size(50.dp).padding(DEFAULT_PADDING_HALF) - ) - } - Text(stringResource(MR.strings.share_verb)) - } -} - -@Composable -fun ShareViaEmailButton(onClick: () -> Unit) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - IconButton(onClick, Modifier.padding(bottom = DEFAULT_PADDING_HALF).border(1.dp, MaterialTheme.colors.secondary.copy(0.1f), CircleShape)) { - Icon( - painterResource(MR.images.ic_mail), generalGetString(MR.strings.share_verb), tint = MaterialTheme.colors.primary, - modifier = Modifier.size(50.dp).padding(DEFAULT_PADDING_HALF) - ) - } - Text(stringResource(MR.strings.invite_friends_short)) - } -} - -@Composable -private fun TextBelowButton(text: String) { - Text( - text, - Modifier - .fillMaxWidth() - .padding(horizontal = DEFAULT_PADDING * 3, vertical = DEFAULT_PADDING_HALF), - style = MaterialTheme.typography.subtitle1, - color = MaterialTheme.colors.secondary, - textAlign = TextAlign.Center, - ) -} - -@Composable -private fun ProgressIndicator() { - Box( - Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - CircularProgressIndicator( - Modifier - .padding(horizontal = 2.dp) - .size(30.dp), - color = MaterialTheme.colors.secondary, - strokeWidth = 3.dp - ) - } -} - -private fun prepareChatBeforeAddressCreation(rhId: Long?) { - // No visible users but may have hidden. In this case chat should be started anyway because it's stopped on this stage with hidden users - if (chatModel.users.any { u -> !u.user.hidden }) return - withBGApi { - val user = chatModel.controller.apiGetActiveUser(rhId) ?: return@withBGApi - chatModel.currentUser.value = user - chatModel.controller.startChat(user) - } -} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetNotificationsMode.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetNotificationsMode.kt index e480d4330b..d6d5753b6c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetNotificationsMode.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetNotificationsMode.kt @@ -25,6 +25,10 @@ import chat.simplex.res.MR @Composable fun SetNotificationsMode(m: ChatModel) { + LaunchedEffect(Unit) { + prepareChatBeforeNotificationsSetup(m) + } + CompositionLocalProvider(LocalAppBarHandler provides rememberAppBarHandler()) { ModalView({}, showClose = false) { ColumnWithScrollBar(Modifier.themedBackground(bgLayerSize = LocalAppBarHandler.current?.backgroundGraphicsLayerSize, bgLayer = LocalAppBarHandler.current?.backgroundGraphicsLayer)) { @@ -94,3 +98,13 @@ fun SelectableCard(currentValue: State, newValue: T, title: String, descr } Spacer(Modifier.height(14.dp)) } + +private fun prepareChatBeforeNotificationsSetup(chatModel: ChatModel) { + // No visible users but may have hidden. In this case chat should be started anyway because it's stopped on this stage with hidden users + if (chatModel.users.any { u -> !u.user.hidden }) return + withBGApi { + val user = chatModel.controller.apiGetActiveUser(null) ?: return@withBGApi + chatModel.currentUser.value = user + chatModel.controller.startChat(user) + } +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetupDatabasePassphrase.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetupDatabasePassphrase.kt index d0a3e601d2..4ad2675e83 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetupDatabasePassphrase.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SetupDatabasePassphrase.kt @@ -36,7 +36,7 @@ fun SetupDatabasePassphrase(m: ChatModel) { val confirmNewKey = rememberSaveable { mutableStateOf("") } fun nextStep() { if (appPlatform.isAndroid || chatModel.currentUser.value != null) { - m.controller.appPrefs.onboardingStage.set(OnboardingStage.Step3_CreateSimpleXAddress) + m.controller.appPrefs.onboardingStage.set(OnboardingStage.Step4_SetNotificationsMode) } else { m.controller.appPrefs.onboardingStage.set(OnboardingStage.LinkAMobile) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressLearnMore.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressLearnMore.kt index 6d6b72d2d1..efc161ac65 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressLearnMore.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressLearnMore.kt @@ -1,9 +1,16 @@ package chat.simplex.common.views.usersettings import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.* import androidx.compose.runtime.Composable +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.sp import chat.simplex.common.platform.ColumnWithScrollBar +import chat.simplex.common.platform.chatModel import chat.simplex.common.ui.theme.DEFAULT_PADDING import dev.icerock.moko.resources.compose.stringResource import chat.simplex.common.views.helpers.* @@ -12,12 +19,30 @@ import chat.simplex.common.views.onboarding.ReadableTextWithLink import chat.simplex.res.MR @Composable -fun UserAddressLearnMore() { +fun UserAddressLearnMore(showCreateAddressButton: Boolean = false) { ColumnWithScrollBar(Modifier .padding(horizontal = DEFAULT_PADDING)) { AppBarTitle(stringResource(MR.strings.simplex_address), withPadding = false) ReadableText(MR.strings.you_can_share_your_address) ReadableText(MR.strings.you_wont_lose_your_contacts_if_delete_address) ReadableText(MR.strings.you_can_accept_or_reject_connection) ReadableTextWithLink(MR.strings.read_more_in_user_guide_with_link, "https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address") + + if (showCreateAddressButton) { + Spacer(Modifier.weight(1f)) + Column(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING * 2), horizontalAlignment = Alignment.CenterHorizontally) { + Button( + onClick = { + ModalManager.start.showModalCloseable { close -> + UserAddressView(chatModel = chatModel, shareViaProfile = false, autoCreateAddress = true, close = close) + } + }, + shape = CircleShape, + contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 2, vertical = DEFAULT_PADDING), + colors = ButtonDefaults.buttonColors(MaterialTheme.colors.primary, disabledBackgroundColor = MaterialTheme.colors.secondary) + ) { + Text(stringResource(MR.strings.create_simplex_address), style = MaterialTheme.typography.h2, color = Color.White, fontSize = 18.sp, fontWeight = FontWeight.Medium) + } + } + } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt index b357272e16..aa9ba70b02 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt @@ -33,6 +33,7 @@ fun UserAddressView( chatModel: ChatModel, viaCreateLinkView: Boolean = false, shareViaProfile: Boolean = false, + autoCreateAddress: Boolean = false, close: () -> Unit ) { // TODO close when remote host changes @@ -58,6 +59,33 @@ fun UserAddressView( } } } + + fun createAddress() { + withBGApi { + progressIndicator = true + val connReqContact = chatModel.controller.apiCreateUserAddress(user?.value?.remoteHostId) + if (connReqContact != null) { + chatModel.userAddress.value = UserContactLinkRec(connReqContact) + + AlertManager.shared.showAlertDialog( + title = generalGetString(MR.strings.share_address_with_contacts_question), + text = generalGetString(MR.strings.add_address_to_your_profile), + confirmText = generalGetString(MR.strings.share_verb), + onConfirm = { + setProfileAddress(true) + shareViaProfile.value = true + } + ) + } + progressIndicator = false + } + } + + LaunchedEffect(autoCreateAddress) { + if (autoCreateAddress) { + createAddress() + } + } val userAddress = remember { chatModel.userAddress } val clipboard = LocalClipboardManager.current val uriHandler = LocalUriHandler.current @@ -67,26 +95,7 @@ fun UserAddressView( userAddress = userAddress.value, shareViaProfile, onCloseHandler, - createAddress = { - withBGApi { - progressIndicator = true - val connReqContact = chatModel.controller.apiCreateUserAddress(user?.value?.remoteHostId) - if (connReqContact != null) { - chatModel.userAddress.value = UserContactLinkRec(connReqContact) - - AlertManager.shared.showAlertDialog( - title = generalGetString(MR.strings.share_address_with_contacts_question), - text = generalGetString(MR.strings.add_address_to_your_profile), - confirmText = generalGetString(MR.strings.share_verb), - onConfirm = { - setProfileAddress(true) - shareViaProfile.value = true - } - ) - } - progressIndicator = false - } - }, + createAddress = { createAddress() }, learnMore = { ModalManager.start.showModal { UserAddressLearnMore() diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 0ada4d3095..673100bd8d 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -378,6 +378,7 @@ Tap to Connect Connect with %1$s? Search or paste SimpleX link + Tap Create SimpleX address in the menu to create it later. No selected chat