mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-30 16:25:57 +00:00
ui: allow to add/edit profile short description, length limit; allow to set welcome message for address without auto-accept (#6086)
This commit is contained in:
@@ -26,6 +26,7 @@ struct GroupProfileView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@Binding var groupInfo: GroupInfo
|
||||
@State var groupProfile: GroupProfile
|
||||
@State private var shortDescr: String = ""
|
||||
@State private var currentProfileHash: Int?
|
||||
@State private var showChooseSource = false
|
||||
@State private var showImagePicker = false
|
||||
@@ -55,8 +56,16 @@ struct GroupProfileView: View {
|
||||
if fullName != "" && fullName != groupProfile.displayName {
|
||||
TextField("Group full name (optional)", text: $groupProfile.fullName)
|
||||
}
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
// TextField("Short description", text: Binding(get: {groupProfile.shortDescr ?? ""}, set: {groupProfile.shortDescr = $0}))
|
||||
HStack {
|
||||
TextField("Short description", text: $shortDescr)
|
||||
if !shortDescrFitsLimit() {
|
||||
Button {
|
||||
showAlert(NSLocalizedString("Description too large", comment: "alert title"))
|
||||
} label: {
|
||||
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text("Group profile is stored on members' devices, not on the servers.")
|
||||
}
|
||||
@@ -64,9 +73,13 @@ struct GroupProfileView: View {
|
||||
Section {
|
||||
Button("Reset") {
|
||||
groupProfile = groupInfo.groupProfile
|
||||
shortDescr = groupInfo.groupProfile.shortDescr ?? ""
|
||||
currentProfileHash = groupProfile.hashValue
|
||||
}
|
||||
.disabled(currentProfileHash == groupProfile.hashValue)
|
||||
.disabled(
|
||||
currentProfileHash == groupProfile.hashValue &&
|
||||
(groupInfo.groupProfile.shortDescr ?? "") == shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
)
|
||||
Button("Save group profile", action: saveProfile)
|
||||
.disabled(!canUpdateProfile)
|
||||
}
|
||||
@@ -109,6 +122,7 @@ struct GroupProfileView: View {
|
||||
}
|
||||
.onAppear {
|
||||
currentProfileHash = groupProfile.hashValue
|
||||
shortDescr = groupInfo.groupProfile.shortDescr ?? ""
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
withAnimation { focusDisplayName = true }
|
||||
}
|
||||
@@ -141,9 +155,13 @@ struct GroupProfileView: View {
|
||||
}
|
||||
|
||||
private var canUpdateProfile: Bool {
|
||||
currentProfileHash != groupProfile.hashValue &&
|
||||
(
|
||||
currentProfileHash != groupProfile.hashValue ||
|
||||
(groupProfile.shortDescr ?? "") != shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
) &&
|
||||
groupProfile.displayName.trimmingCharacters(in: .whitespaces) != "" &&
|
||||
validNewProfileName
|
||||
validNewProfileName &&
|
||||
shortDescrFitsLimit()
|
||||
}
|
||||
|
||||
private var validNewProfileName: Bool {
|
||||
@@ -151,11 +169,16 @@ struct GroupProfileView: View {
|
||||
|| validDisplayName(groupProfile.displayName.trimmingCharacters(in: .whitespaces))
|
||||
}
|
||||
|
||||
private func shortDescrFitsLimit() -> Bool {
|
||||
chatJsonLength(shortDescr) <= MAX_BIO_LENGTH_BYTES
|
||||
}
|
||||
|
||||
func saveProfile() {
|
||||
Task {
|
||||
do {
|
||||
groupProfile.displayName = groupProfile.displayName.trimmingCharacters(in: .whitespaces)
|
||||
groupProfile.fullName = groupProfile.fullName.trimmingCharacters(in: .whitespaces)
|
||||
groupProfile.shortDescr = shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
let gInfo = try await apiUpdateGroup(groupInfo.groupId, groupProfile)
|
||||
await MainActor.run {
|
||||
currentProfileHash = groupProfile.hashValue
|
||||
@@ -174,6 +197,9 @@ struct GroupProfileView: View {
|
||||
|
||||
struct GroupProfileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
GroupProfileView(groupInfo: Binding.constant(GroupInfo.sampleData), groupProfile: GroupProfile.sampleData)
|
||||
GroupProfileView(
|
||||
groupInfo: Binding.constant(GroupInfo.sampleData),
|
||||
groupProfile: GroupProfile.sampleData
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +157,9 @@ struct GroupWelcomeView: View {
|
||||
|
||||
struct GroupWelcomeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
GroupProfileView(groupInfo: Binding.constant(GroupInfo.sampleData), groupProfile: GroupProfile.sampleData)
|
||||
GroupProfileView(
|
||||
groupInfo: Binding.constant(GroupInfo.sampleData),
|
||||
groupProfile: GroupProfile.sampleData
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ enum UserProfileAlert: Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
let MAX_BIO_LENGTH_BYTES = 160
|
||||
|
||||
struct CreateProfile: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@@ -38,14 +40,13 @@ struct CreateProfile: View {
|
||||
Section {
|
||||
TextField("Enter your name…", text: $displayName)
|
||||
.focused($focusDisplayName)
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
// TextField("Bio", text: $profileBio)
|
||||
TextField("Bio", text: $profileBio)
|
||||
Button {
|
||||
createProfile()
|
||||
} label: {
|
||||
Label("Create profile", systemImage: "checkmark")
|
||||
}
|
||||
.disabled(!canCreateProfile(displayName))
|
||||
.disabled(!canCreateProfile(displayName) || !bioFitsLimit())
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Your profile")
|
||||
@@ -55,11 +56,14 @@ struct CreateProfile: View {
|
||||
let validName = mkValidName(name)
|
||||
if name != validName {
|
||||
Spacer()
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.red)
|
||||
.onTapGesture {
|
||||
alert = .invalidNameError(validName: validName)
|
||||
}
|
||||
validationErrorIndicator {
|
||||
alert = .invalidNameError(validName: validName)
|
||||
}
|
||||
} else if !bioFitsLimit() {
|
||||
Spacer()
|
||||
validationErrorIndicator {
|
||||
showAlert(NSLocalizedString("Bio too large", comment: "alert title"))
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 20)
|
||||
@@ -81,6 +85,18 @@ struct CreateProfile: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func validationErrorIndicator(_ onTap: @escaping () -> Void) -> some View {
|
||||
Image(systemName: "exclamationmark.circle")
|
||||
.foregroundColor(.red)
|
||||
.onTapGesture {
|
||||
onTap()
|
||||
}
|
||||
}
|
||||
|
||||
private func bioFitsLimit() -> Bool {
|
||||
chatJsonLength(profileBio) <= MAX_BIO_LENGTH_BYTES
|
||||
}
|
||||
|
||||
private func createProfile() {
|
||||
hideKeyboard()
|
||||
let shortDescr: String? = if profileBio.isEmpty { nil } else { profileBio }
|
||||
|
||||
@@ -459,13 +459,16 @@ struct UserAddressSettingsView: View {
|
||||
Section {
|
||||
shareWithContactsButton()
|
||||
autoAcceptToggle().disabled(settings.businessAddress)
|
||||
if settings.autoAccept && !ChatModel.shared.addressShortLinkDataSet && !settings.businessAddress {
|
||||
acceptIncognitoToggle()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO v6.4.1 move auto-reply editor here
|
||||
// messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.autoReply)
|
||||
|
||||
if settings.autoAccept {
|
||||
autoAcceptSection()
|
||||
Section {
|
||||
messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.autoReply)
|
||||
} header: {
|
||||
Text("Welcome message")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
|
||||
Section {
|
||||
@@ -541,21 +544,6 @@ struct UserAddressSettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func autoAcceptSection() -> some View {
|
||||
Section {
|
||||
if !ChatModel.shared.addressShortLinkDataSet && !settings.businessAddress {
|
||||
acceptIncognitoToggle()
|
||||
}
|
||||
// TODO v6.4.1 show this message editor even with auto-accept disabled
|
||||
messageEditor(placeholder: NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"), text: $settings.autoReply)
|
||||
} header: {
|
||||
Text("Auto-accept")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
} footer: {
|
||||
Text("Sent to your contact after connection.")
|
||||
}
|
||||
}
|
||||
|
||||
private func acceptIncognitoToggle() -> some View {
|
||||
settingsRow(
|
||||
settings.autoAcceptIncognito ? "theatermasks.fill" : "theatermasks",
|
||||
|
||||
@@ -15,6 +15,7 @@ struct UserProfile: View {
|
||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
|
||||
@State private var profile = Profile(displayName: "", fullName: "")
|
||||
@State private var currentProfileHash: Int?
|
||||
@State private var shortDescr = ""
|
||||
// Modals
|
||||
@State private var showChooseSource = false
|
||||
@State private var showImagePicker = false
|
||||
@@ -43,8 +44,16 @@ struct UserProfile: View {
|
||||
if let user = chatModel.currentUser, showFullName(user) {
|
||||
TextField("Full name (optional)", text: $profile.fullName)
|
||||
}
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
// TextField("Bio", text: Binding(get: {profile.shortDescr ?? ""}, set: {profile.shortDescr = $0}))
|
||||
HStack {
|
||||
TextField("Bio", text: $shortDescr)
|
||||
if !bioFitsLimit() {
|
||||
Button {
|
||||
showAlert(NSLocalizedString("Bio too large", comment: "alert title"))
|
||||
} label: {
|
||||
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text("Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.")
|
||||
}
|
||||
@@ -53,7 +62,10 @@ struct UserProfile: View {
|
||||
Button(action: getCurrentProfile) {
|
||||
Text("Reset")
|
||||
}
|
||||
.disabled(currentProfileHash == profile.hashValue)
|
||||
.disabled(
|
||||
currentProfileHash == profile.hashValue &&
|
||||
(profile.shortDescr ?? "") == shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
)
|
||||
Button(action: saveProfile) {
|
||||
Text("Save (and notify contacts)")
|
||||
}
|
||||
@@ -118,11 +130,19 @@ struct UserProfile: View {
|
||||
private func showFullName(_ user: User) -> Bool {
|
||||
user.profile.fullName != "" && user.profile.fullName != user.profile.displayName
|
||||
}
|
||||
|
||||
|
||||
private func bioFitsLimit() -> Bool {
|
||||
chatJsonLength(shortDescr) <= MAX_BIO_LENGTH_BYTES
|
||||
}
|
||||
|
||||
private var canSaveProfile: Bool {
|
||||
currentProfileHash != profile.hashValue &&
|
||||
(
|
||||
currentProfileHash != profile.hashValue ||
|
||||
(chatModel.currentUser?.profile.shortDescr ?? "") != shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
) &&
|
||||
profile.displayName.trimmingCharacters(in: .whitespaces) != "" &&
|
||||
validDisplayName(profile.displayName)
|
||||
validDisplayName(profile.displayName) &&
|
||||
bioFitsLimit()
|
||||
}
|
||||
|
||||
private func saveProfile() {
|
||||
@@ -130,6 +150,7 @@ struct UserProfile: View {
|
||||
Task {
|
||||
do {
|
||||
profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces)
|
||||
profile.shortDescr = shortDescr.trimmingCharacters(in: .whitespaces)
|
||||
if let (newProfile, _) = try await apiUpdateProfile(profile: profile) {
|
||||
await MainActor.run {
|
||||
chatModel.updateCurrentUser(newProfile)
|
||||
@@ -148,6 +169,7 @@ struct UserProfile: View {
|
||||
if let user = chatModel.currentUser {
|
||||
profile = fromLocalProfile(user.profile)
|
||||
currentProfileHash = profile.hashValue
|
||||
shortDescr = profile.shortDescr ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
const val MAX_BIO_LENGTH_BYTES = 160
|
||||
|
||||
fun bioFitsLimit(bio: String): Boolean {
|
||||
return chatJsonLength(bio) <= MAX_BIO_LENGTH_BYTES
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -68,18 +74,28 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
ProfileNameField(displayName, "", { it.trim() == mkValidName(it) }, focusRequester)
|
||||
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
// Text(
|
||||
// stringResource(MR.strings.short_descr),
|
||||
// fontSize = 16.sp
|
||||
// )
|
||||
// ProfileNameField(shortDescr, "")
|
||||
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(MR.strings.short_descr),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
Spacer(Modifier.height(20.dp))
|
||||
if (!bioFitsLimit(shortDescr.value)) {
|
||||
IconButton(
|
||||
onClick = { AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.bio_too_large)) },
|
||||
Modifier.size(20.dp)) {
|
||||
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileNameField(shortDescr, "", isValid = { bioFitsLimit(it) })
|
||||
}
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_check),
|
||||
stringResource(MR.strings.create_another_profile_button),
|
||||
disabled = !canCreateProfile(displayName.value),
|
||||
disabled = !canCreateProfile(displayName.value) || !bioFitsLimit(shortDescr.value),
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
click = {
|
||||
|
||||
@@ -68,7 +68,7 @@ fun GroupProfileLayout(
|
||||
shortDescr.value.trim() == (groupProfile.shortDescr ?: "") &&
|
||||
groupProfile.image == profileImage.value
|
||||
val closeWithAlert = {
|
||||
if (dataUnchanged || !canUpdateProfile(displayName.value, groupProfile)) {
|
||||
if (dataUnchanged || !canUpdateProfile(displayName.value, shortDescr.value, groupProfile)) {
|
||||
close()
|
||||
} else {
|
||||
showUnsavedChangesAlert({
|
||||
@@ -143,18 +143,27 @@ fun GroupProfileLayout(
|
||||
ProfileNameField(fullName)
|
||||
}
|
||||
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
// Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
// Text(
|
||||
// stringResource(MR.strings.group_short_descr_field),
|
||||
// fontSize = 16.sp,
|
||||
// modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
|
||||
// )
|
||||
// ProfileNameField(shortDescr)
|
||||
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(MR.strings.group_short_descr_field),
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
if (!bioFitsLimit(shortDescr.value)) {
|
||||
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
|
||||
IconButton(
|
||||
onClick = { AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.group_descr_too_large)) },
|
||||
Modifier.size(20.dp)
|
||||
) {
|
||||
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileNameField(shortDescr, "", isValid = { bioFitsLimit(it) })
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
val enabled = !dataUnchanged && canUpdateProfile(displayName.value, groupProfile)
|
||||
val enabled = !dataUnchanged && canUpdateProfile(displayName.value, shortDescr.value, groupProfile)
|
||||
if (enabled) {
|
||||
Text(
|
||||
stringResource(MR.strings.save_group_profile),
|
||||
@@ -189,8 +198,8 @@ fun GroupProfileLayout(
|
||||
}
|
||||
}
|
||||
|
||||
private fun canUpdateProfile(displayName: String, groupProfile: GroupProfile): Boolean =
|
||||
displayName.trim().isNotEmpty() && isValidNewProfileName(displayName, groupProfile)
|
||||
private fun canUpdateProfile(displayName: String, shortDescr: String, groupProfile: GroupProfile): Boolean =
|
||||
displayName.trim().isNotEmpty() && isValidNewProfileName(displayName, groupProfile) && bioFitsLimit(shortDescr)
|
||||
|
||||
private fun isValidNewProfileName(displayName: String, groupProfile: GroupProfile): Boolean =
|
||||
displayName == groupProfile.displayName || isValidDisplayName(displayName.trim())
|
||||
|
||||
@@ -396,19 +396,16 @@ private fun ModalData.UserAddressSettings(
|
||||
SectionView {
|
||||
ShareWithContactsButton(shareViaProfile, setProfileAddress)
|
||||
AutoAcceptToggle(addressSettingsState) { saveAddressSettings(addressSettingsState.value, savedAddressSettingsState) }
|
||||
if (addressSettingsState.value.autoAccept && !chatModel.addressShortLinkDataSet() && !addressSettingsState.value.businessAddress) {
|
||||
AcceptIncognitoToggle(addressSettingsState)
|
||||
}
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
|
||||
// TODO v6.4.1 move auto-reply editor here
|
||||
// SectionView(stringResource(MR.strings.address_welcome_message).uppercase()) {
|
||||
// AutoReplyEditor(addressSettingsState)
|
||||
// }
|
||||
// SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
if (addressSettingsState.value.autoAccept) {
|
||||
AutoAcceptSection(addressSettingsState = addressSettingsState)
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
SectionView(stringResource(MR.strings.address_welcome_message).uppercase()) {
|
||||
AutoReplyEditor(addressSettingsState)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
|
||||
saveAddressSettingsButton(addressSettingsState.value == savedAddressSettingsState.value) {
|
||||
saveAddressSettings(addressSettingsState.value, savedAddressSettingsState)
|
||||
@@ -572,18 +569,6 @@ private class AddressSettingsState {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AutoAcceptSection(addressSettingsState: MutableState<AddressSettingsState>) {
|
||||
SectionView(stringResource(MR.strings.auto_accept_contact).uppercase()) {
|
||||
if (!chatModel.addressShortLinkDataSet() && !addressSettingsState.value.businessAddress) {
|
||||
AcceptIncognitoToggle(addressSettingsState)
|
||||
}
|
||||
// TODO v6.4.1 show this message editor even with auto-accept disabled
|
||||
AutoReplyEditor(addressSettingsState)
|
||||
SectionTextFooter(stringResource(MR.strings.sent_to_your_contact_after_connection))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AcceptIncognitoToggle(addressSettingsState: MutableState<AddressSettingsState>) {
|
||||
PreferenceToggleWithIcon(
|
||||
|
||||
@@ -92,7 +92,7 @@ fun UserProfileLayout(
|
||||
shortDescr.value.trim() == (profile.shortDescr ?: "") &&
|
||||
profile.image == profileImage.value
|
||||
val closeWithAlert = {
|
||||
if (dataUnchanged || !canSaveProfile(displayName.value, profile)) {
|
||||
if (dataUnchanged || !canSaveProfile(displayName.value, shortDescr.value, profile)) {
|
||||
close()
|
||||
} else {
|
||||
showUnsavedChangesAlert({ saveProfile(displayName.value, fullName.value, shortDescr.value, profileImage.value) }, close)
|
||||
@@ -148,18 +148,27 @@ fun UserProfileLayout(
|
||||
ProfileNameField(fullName)
|
||||
}
|
||||
|
||||
// TODO enable in v6.4.1, limit to 160 characters
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
// Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
// Text(
|
||||
// stringResource(MR.strings.short_descr__field),
|
||||
// fontSize = 16.sp,
|
||||
// modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF)
|
||||
// )
|
||||
// ProfileNameField(shortDescr)
|
||||
Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(MR.strings.short_descr__field),
|
||||
fontSize = 16.sp,
|
||||
)
|
||||
if (!bioFitsLimit(shortDescr.value)) {
|
||||
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
|
||||
IconButton(
|
||||
onClick = { AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.bio_too_large)) },
|
||||
Modifier.size(20.dp)
|
||||
) {
|
||||
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileNameField(shortDescr)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
val enabled = !dataUnchanged && canSaveProfile(displayName.value, profile)
|
||||
val enabled = !dataUnchanged && canSaveProfile(displayName.value, shortDescr.value, profile)
|
||||
val saveModifier: Modifier = Modifier.clickable(enabled) { saveProfile(displayName.value, fullName.value, shortDescr.value, profileImage.value) }
|
||||
val saveColor: Color = if (enabled) MaterialTheme.colors.primary else MaterialTheme.colors.secondary
|
||||
Text(
|
||||
@@ -225,8 +234,8 @@ private fun isValidNewProfileName(displayName: String, profile: Profile): Boolea
|
||||
private fun showFullName(profile: Profile): Boolean =
|
||||
profile.fullName.trim().isNotEmpty() && profile.fullName.trim() != profile.displayName.trim()
|
||||
|
||||
private fun canSaveProfile(displayName: String, profile: Profile): Boolean =
|
||||
displayName.trim().isNotEmpty() && isValidNewProfileName(displayName, profile)
|
||||
private fun canSaveProfile(displayName: String, shortDescr: String, profile: Profile): Boolean =
|
||||
displayName.trim().isNotEmpty() && isValidNewProfileName(displayName, profile) && bioFitsLimit(shortDescr)
|
||||
|
||||
@Preview/*(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
|
||||
@@ -1108,6 +1108,7 @@
|
||||
<string name="display_name__field">Profile name:</string>
|
||||
<string name="full_name__field">Full name:</string>
|
||||
<string name="short_descr__field">Bio:</string>
|
||||
<string name="bio_too_large">Bio too large</string>
|
||||
<string name="your_current_profile">Your current profile</string>
|
||||
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.</string>
|
||||
<string name="edit_image">Edit image</string>
|
||||
@@ -1916,6 +1917,7 @@
|
||||
<string name="group_display_name_field">Enter group name:</string>
|
||||
<string name="group_full_name_field">Group full name:</string>
|
||||
<string name="group_short_descr_field">Short description:</string>
|
||||
<string name="group_descr_too_large">Description too large</string>
|
||||
<string name="group_main_profile_sent">Your chat profile will be sent to group members</string>
|
||||
<string name="chat_main_profile_sent">Your chat profile will be sent to chat members</string>
|
||||
<string name="create_group_button">Create group</string>
|
||||
|
||||
Reference in New Issue
Block a user