Merge branch 'master' into server-operators

This commit is contained in:
Evgeny Poberezkin
2024-11-18 16:55:07 +00:00
11 changed files with 233 additions and 276 deletions

View File

@@ -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)
}
}

View File

@@ -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<Pair<SharedPreference<Boolean>, 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"

View File

@@ -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
}

View File

@@ -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,

View File

@@ -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<Boolean>) {
@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<Animate
}
}
@Composable
private fun ChatListCard(
close: () -> 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<TextFieldValue>, listState: LazyListState) {
if (!chatModel.desktopNoUserNoRemote) {
@@ -641,6 +712,7 @@ private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, 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<TextFieldValue>, 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<TextFieldValue>, 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()
}
}
}

View File

@@ -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)
}
}

View File

@@ -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 <T> SelectableCard(currentValue: State<T>, 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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}
}
}
}

View File

@@ -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()

View File

@@ -378,6 +378,7 @@
<string name="contact_tap_to_connect">Tap to Connect</string>
<string name="connect_with_contact_name_question">Connect with %1$s?</string>
<string name="search_or_paste_simplex_link">Search or paste SimpleX link</string>
<string name="address_creation_instruction">Tap Create SimpleX address in the menu to create it later.</string>
<!-- ChatView.kt -->
<string name="no_selected_chat">No selected chat</string>