android, desktop: offer creating a profile for a one-time invitation link

Adds "Add profile" to the profile picker reached from "Share profile" on a
one-time link, so a link can be handed out under a new identity without setting
one up in settings first. Reuses the existing users_add string.

Extracts the existing selection handler into selectProfile so the new profile
takes exactly the same path as picking an existing one - the connection change
and the switch cannot drift apart.

The row sits outside the active-profile branch: inside it, it would disappear
whenever the active profile is filtered out by the search text. It is only
offered when there is a connection to move, so it does not appear in the share
list, where a new profile would have nothing to share into.
This commit is contained in:
Narasimha-sc
2026-08-03 16:12:10 +00:00
parent c270a1531d
commit 49344c32fb
@@ -39,6 +39,7 @@ import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.item.CIFileViewScope
import chat.simplex.common.views.chat.topPaddingToContent
import chat.simplex.common.views.createProfileForInvitation
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.common.BuildConfigCommon
@@ -293,6 +294,8 @@ fun ActiveProfilePicker(
showIncognito: Boolean = true
) {
val switchingProfile = remember { mutableStateOf(false) }
// Not rememberSaveable, and hoisted out of the lazy item: either strands it true.
val creatingProfile = remember { mutableStateOf(false) }
val incognito = remember {
chatModel.showingInvitation.value?.conn?.incognito ?: controller.appPrefs.incognito.get()
}
@@ -359,6 +362,26 @@ fun ActiveProfilePicker(
}
}
@Composable
fun NewProfileOption() {
ProfilePickerOption(
title = stringResource(MR.strings.users_add),
disabled = switchingProfile.value || creatingProfile.value,
selected = false,
onSelected = { createProfileForInvitation(rhId, creatingProfile) { selectProfile(it) } },
image = {
Box(Modifier.size(42.dp), contentAlignment = Alignment.Center) {
Icon(
painterResource(MR.images.ic_manage_accounts),
contentDescription = null,
Modifier.size(24.dp),
tint = MaterialTheme.colors.primary,
)
}
}
)
}
@Composable
fun ProfilePickerUserOption(user: User) {
val selected = selectedProfile?.userId == user.userId && !incognito
@@ -455,6 +478,15 @@ fun ActiveProfilePicker(
ProfilePickerUserOption(p)
}
}
// Outside the branch above: inside it, the row would disappear whenever the
// active profile is filtered out by the search text. Only offered when there
// is a connection to move to the new profile - in the share list there is
// nothing for a brand new profile to share into.
if (contactConnection != null) {
item {
NewProfileOption()
}
}
item {
Spacer(Modifier.imePadding().padding(bottom = DEFAULT_BOTTOM_PADDING))
}