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 9bd76e81bb..fdc038d64d 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 @@ -736,22 +736,18 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a return null } - suspend fun apiSwitchContact(contactId: Long) { - return when (val r = sendCmd(CC.APISwitchContact(contactId))) { - is CR.CmdOk -> {} - else -> { - apiErrorAlert("apiSwitchContact", generalGetString(R.string.error_changing_address), r) - } - } + suspend fun apiSwitchContact(contactId: Long): ConnectionStats? { + val r = sendCmd(CC.APISwitchContact(contactId)) + if (r is CR.ContactSwitchStarted) return r.connectionStats + apiErrorAlert("apiSwitchContact", generalGetString(R.string.error_changing_address), r) + return null } - suspend fun apiSwitchGroupMember(groupId: Long, groupMemberId: Long) { - return when (val r = sendCmd(CC.APISwitchGroupMember(groupId, groupMemberId))) { - is CR.CmdOk -> {} - else -> { - apiErrorAlert("apiSwitchGroupMember", generalGetString(R.string.error_changing_address), r) - } - } + suspend fun apiSwitchGroupMember(groupId: Long, groupMemberId: Long): ConnectionStats? { + val r = sendCmd(CC.APISwitchGroupMember(groupId, groupMemberId)) + if (r is CR.GroupMemberSwitchStarted) return r.connectionStats + apiErrorAlert("apiSwitchGroupMember", generalGetString(R.string.error_changing_address), r) + return null } suspend fun apiAbortSwitchContact(contactId: Long): ConnectionStats? { @@ -3298,6 +3294,8 @@ sealed class CR { @Serializable @SerialName("networkConfig") class NetworkConfig(val networkConfig: NetCfg): CR() @Serializable @SerialName("contactInfo") class ContactInfo(val user: User, val contact: Contact, val connectionStats: ConnectionStats, val customUserProfile: Profile? = null): CR() @Serializable @SerialName("groupMemberInfo") class GroupMemberInfo(val user: User, val groupInfo: GroupInfo, val member: GroupMember, val connectionStats_: ConnectionStats? = null): CR() + @Serializable @SerialName("contactSwitchStarted") class ContactSwitchStarted(val user: User, val contact: Contact, val connectionStats: ConnectionStats): CR() + @Serializable @SerialName("groupMemberSwitchStarted") class GroupMemberSwitchStarted(val user: User, val groupInfo: GroupInfo, val member: GroupMember, val connectionStats: ConnectionStats): CR() @Serializable @SerialName("contactSwitchAborted") class ContactSwitchAborted(val user: User, val contact: Contact, val connectionStats: ConnectionStats): CR() @Serializable @SerialName("groupMemberSwitchAborted") class GroupMemberSwitchAborted(val user: User, val groupInfo: GroupInfo, val member: GroupMember, val connectionStats: ConnectionStats): CR() @Serializable @SerialName("contactCode") class ContactCode(val user: User, val contact: Contact, val connectionCode: String): CR() @@ -3414,6 +3412,8 @@ sealed class CR { is NetworkConfig -> "networkConfig" is ContactInfo -> "contactInfo" is GroupMemberInfo -> "groupMemberInfo" + is ContactSwitchStarted -> "contactSwitchStarted" + is GroupMemberSwitchStarted -> "groupMemberSwitchStarted" is ContactSwitchAborted -> "contactSwitchAborted" is GroupMemberSwitchAborted -> "groupMemberSwitchAborted" is ContactCode -> "contactCode" @@ -3527,6 +3527,8 @@ sealed class CR { is NetworkConfig -> json.encodeToString(networkConfig) is ContactInfo -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats)}") is GroupMemberInfo -> withUser(user, "group: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionStats: ${json.encodeToString(connectionStats_)}") + is ContactSwitchStarted -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats)}") + is GroupMemberSwitchStarted -> withUser(user, "group: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionStats: ${json.encodeToString(connectionStats)}") is ContactSwitchAborted -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionStats: ${json.encodeToString(connectionStats)}") is GroupMemberSwitchAborted -> withUser(user, "group: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionStats: ${json.encodeToString(connectionStats)}") is ContactCode -> withUser(user, "contact: ${json.encodeToString(contact)}\nconnectionCode: $connectionCode") 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 00bf8d5444..75edc77c05 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 @@ -83,7 +83,7 @@ fun ChatInfoView( switchContactAddress = { showSwitchAddressAlert(switchAddress = { withApi { - chatModel.controller.apiSwitchContact(contact.contactId) + connStats.value = chatModel.controller.apiSwitchContact(contact.contactId) } }) }, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index d00548f755..32f68a8b91 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -790,9 +790,7 @@ fun ComposeView( } } - // TODO in 5.2 - allow if ttl is not configured - // val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) } - val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) && chat.chatInfo.timedMessagesTTL != null } + val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) } SendMsgView( composeState, showVoiceRecordIcon = true, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt index da832679a6..8e9520d04e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ContactPreferences.kt @@ -95,11 +95,11 @@ private fun ContactPreferencesLayout( applyPrefs(featuresAllowed.copy(fullDelete = it)) } SectionDividerSpaced(true, maxBottomPadding = false) -// val allowReactions: MutableState = remember(featuresAllowed) { mutableStateOf(featuresAllowed.reactions) } -// FeatureSection(ChatFeature.Reactions, user.fullPreferences.reactions.allow, contact.mergedPreferences.reactions, allowReactions) { -// applyPrefs(featuresAllowed.copy(reactions = it)) -// } -// SectionDividerSpaced(true, maxBottomPadding = false) + val allowReactions: MutableState = remember(featuresAllowed) { mutableStateOf(featuresAllowed.reactions) } + FeatureSection(ChatFeature.Reactions, user.fullPreferences.reactions.allow, contact.mergedPreferences.reactions, allowReactions) { + applyPrefs(featuresAllowed.copy(reactions = it)) + } + SectionDividerSpaced(true, maxBottomPadding = false) val allowVoice: MutableState = remember(featuresAllowed) { mutableStateOf(featuresAllowed.voice) } FeatureSection(ChatFeature.Voice, user.fullPreferences.voice.allow, contact.mergedPreferences.voice, allowVoice) { applyPrefs(featuresAllowed.copy(voice = it)) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt index cd0b1c460b..1ed56a8874 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupMemberInfoView.kt @@ -101,7 +101,7 @@ fun GroupMemberInfoView( switchMemberAddress = { showSwitchAddressAlert(switchAddress = { withApi { - chatModel.controller.apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) + connStats.value = chatModel.controller.apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) } }) }, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt index 06692e41b5..e23f0dd264 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupPreferences.kt @@ -30,9 +30,9 @@ fun GroupPreferencesView(m: ChatModel, chatId: String, close: () -> Unit,) { fun savePrefs(afterSave: () -> Unit = {}) { withApi { val gp = gInfo.groupProfile.copy(groupPreferences = preferences.toGroupPreferences()) - val gInfo = m.controller.apiUpdateGroup(gInfo.groupId, gp) - if (gInfo != null) { - m.updateGroup(gInfo) + val g = m.controller.apiUpdateGroup(gInfo.groupId, gp) + if (g != null) { + m.updateGroup(g) currentPreferences = preferences } afterSave() @@ -74,11 +74,11 @@ private fun GroupPreferencesLayout( AppBarTitle(stringResource(R.string.group_preferences)) val timedMessages = remember(preferences) { mutableStateOf(preferences.timedMessages.enable) } val onTTLUpdated = { ttl: Int? -> - applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl ?: 86400))) + applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl))) } FeatureSection(GroupFeature.TimedMessages, timedMessages, groupInfo, preferences, onTTLUpdated) { enable -> if (enable == GroupFeatureEnabled.ON) { - applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400))) + applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400))) } else { applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = currentPreferences.timedMessages.ttl))) } @@ -94,11 +94,11 @@ private fun GroupPreferencesLayout( applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = it))) } SectionDividerSpaced(true, maxBottomPadding = false) -// val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) } -// FeatureSection(GroupFeature.Reactions, allowReactions, groupInfo, preferences, onTTLUpdated) { -// applyPrefs(preferences.copy(reactions = GroupPreference(enable = it))) -// } -// SectionDividerSpaced(true, maxBottomPadding = false) + val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) } + FeatureSection(GroupFeature.Reactions, allowReactions, groupInfo, preferences, onTTLUpdated) { + applyPrefs(preferences.copy(reactions = GroupPreference(enable = it))) + } + SectionDividerSpaced(true, maxBottomPadding = false) val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) } FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) { applyPrefs(preferences.copy(voice = GroupPreference(enable = it))) @@ -144,7 +144,7 @@ private fun FeatureSection( selection = ttl, propagateExternalSelectionUpdate = true, // for Reset label = generalGetString(R.string.delete_after), - dropdownValues = TimedMessagesPreference.ttlValues.filterNotNull(), // TODO in 5.2 - allow "off" + dropdownValues = TimedMessagesPreference.ttlValues, customPickerTitle = generalGetString(R.string.delete_after), customPickerConfirmButtonText = generalGetString(R.string.custom_time_picker_select), onSelected = onTTLUpdated diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt index 335d633ae9..97a72dfe6f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CustomTimePicker.kt @@ -123,8 +123,8 @@ data class TimeUnitLimits( CustomTimeUnit.Minute -> TimeUnitLimits(CustomTimeUnit.Minute, maxValue = 120) CustomTimeUnit.Hour -> TimeUnitLimits(CustomTimeUnit.Hour, maxValue = 72) CustomTimeUnit.Day -> TimeUnitLimits(CustomTimeUnit.Day, maxValue = 60) - CustomTimeUnit.Week -> TimeUnitLimits(CustomTimeUnit.Week, maxValue = 12) // TODO in 5.2 - 54 - CustomTimeUnit.Month -> TimeUnitLimits(CustomTimeUnit.Month, maxValue = 3) // TODO in 5.2 - 12 + CustomTimeUnit.Week -> TimeUnitLimits(CustomTimeUnit.Week, maxValue = 52) + CustomTimeUnit.Month -> TimeUnitLimits(CustomTimeUnit.Month, maxValue = 12) } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt index e5b364af82..60dcb04838 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/CreateSimpleXAddress.kt @@ -9,7 +9,6 @@ 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.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -45,15 +44,14 @@ fun CreateSimpleXAddress(m: ChatModel) { val connReqContact = m.controller.apiCreateUserAddress() if (connReqContact != null) { m.userAddress.value = UserContactLinkRec(connReqContact) -// TODO uncomment in v5.2 -// try { -// val u = m.controller.apiSetProfileAddress(true) -// if (u != null) { -// m.updateUser(u) -// } -// } catch (e: Exception) { -// Log.e(TAG, "CreateSimpleXAddress apiSetProfileAddress: ${e.stackTraceToString()}") -// } + try { + val u = m.controller.apiSetProfileAddress(true) + if (u != null) { + m.updateUser(u) + } + } catch (e: Exception) { + Log.e(TAG, "CreateSimpleXAddress apiSetProfileAddress: ${e.stackTraceToString()}") + } progressIndicator = false } } @@ -94,8 +92,7 @@ private fun CreateSimpleXAddressLayout( ContinueButton(nextStep) } else { CreateAddressButton(createAddress) -// TODO remove color in v5.2 - TextBelowButton(stringResource(R.string.your_contacts_will_see_it), color = Color.Transparent) + TextBelowButton(stringResource(R.string.your_contacts_will_see_it)) Spacer(Modifier.weight(1f)) SkipButton(nextStep) } @@ -144,8 +141,7 @@ private fun SkipButton(onClick: () -> Unit) { } @Composable -private fun TextBelowButton(text: String, color: Color = Color.Unspecified) { - // TODO remove color in v5.2 +private fun TextBelowButton(text: String) { Text( text, Modifier @@ -153,7 +149,6 @@ private fun TextBelowButton(text: String, color: Color = Color.Unspecified) { .padding(horizontal = DEFAULT_PADDING * 3), style = MaterialTheme.typography.subtitle1, textAlign = TextAlign.Center, - color = color ) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt index d7af17673a..b967806652 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/Preferences.kt @@ -71,11 +71,11 @@ private fun PreferencesLayout( applyPrefs(preferences.copy(fullDelete = SimpleChatPreference(allow = it))) } SectionDividerSpaced(true, maxBottomPadding = false) -// val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.allow) } -// FeatureSection(ChatFeature.Reactions, allowReactions) { -// applyPrefs(preferences.copy(reactions = SimpleChatPreference(allow = it))) -// } -// SectionDividerSpaced(true, maxBottomPadding = false) + val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.allow) } + FeatureSection(ChatFeature.Reactions, allowReactions) { + applyPrefs(preferences.copy(reactions = SimpleChatPreference(allow = it))) + } + SectionDividerSpaced(true, maxBottomPadding = false) val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.allow) } FeatureSection(ChatFeature.Voice, allowVoice) { applyPrefs(preferences.copy(voice = SimpleChatPreference(allow = it))) 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 7d76465985..c9ce92f10b 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 @@ -69,16 +69,15 @@ fun UserAddressView( if (connReqContact != null) { chatModel.userAddress.value = UserContactLinkRec(connReqContact) -// TODO uncomment in v5.2 -// AlertManager.shared.showAlertDialog( -// title = generalGetString(R.string.share_address_with_contacts_question), -// text = generalGetString(R.string.add_address_to_your_profile), -// confirmText = generalGetString(R.string.share_verb), -// onConfirm = { -// setProfileAddress(true) -// shareViaProfile.value = true -// } -// ) + AlertManager.shared.showAlertDialog( + title = generalGetString(R.string.share_address_with_contacts_question), + text = generalGetString(R.string.add_address_to_your_profile), + confirmText = generalGetString(R.string.share_verb), + onConfirm = { + setProfileAddress(true) + shareViaProfile.value = true + } + ) } progressIndicator = false } @@ -204,8 +203,7 @@ private fun UserAddressLayout( QRCode(userAddress.connReqContact, Modifier.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF).aspectRatio(1f)) ShareAddressButton { share(userAddress.connReqContact) } ShareViaEmailButton { sendEmail(userAddress) } -// TODO uncomment in v5.2 -// ShareWithContactsButton(shareViaProfile, setProfileAddress) + ShareWithContactsButton(shareViaProfile, setProfileAddress) AutoAcceptToggle(autoAcceptState) { saveAas(autoAcceptState.value, autoAcceptStateSaved) } LearnMoreButton(learnMore) } diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 3881835fb8..3fb4833d66 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -480,12 +480,16 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (Con throw r } -func apiSwitchContact(contactId: Int64) async throws { - try await sendCommandOkResp(.apiSwitchContact(contactId: contactId)) +func apiSwitchContact(contactId: Int64) throws -> ConnectionStats { + let r = chatSendCmdSync(.apiSwitchContact(contactId: contactId)) + if case let .contactSwitchStarted(_, _, connectionStats) = r { return connectionStats } + throw r } -func apiSwitchGroupMember(_ groupId: Int64, _ groupMemberId: Int64) async throws { - try await sendCommandOkResp(.apiSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) +func apiSwitchGroupMember(_ groupId: Int64, _ groupMemberId: Int64) throws -> ConnectionStats { + let r = chatSendCmdSync(.apiSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) + if case let .groupMemberSwitchStarted(_, _, _, connectionStats) = r { return connectionStats } + throw r } func apiAbortSwitchContact(_ contactId: Int64) throws -> ConnectionStats { diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index 847efa9e25..a76ed8aa9a 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -368,7 +368,8 @@ struct ChatInfoView: View { private func switchContactAddress() { Task { do { - try await apiSwitchContact(contactId: contact.apiId) + let stats = try apiSwitchContact(contactId: contact.apiId) + connectionStats = stats } catch let error { logger.error("switchContactAddress apiSwitchContact error: \(responseError(error))") let a = getErrorAlert(error, "Error changing address") diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index a261471a51..a70f79ecb8 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -296,9 +296,7 @@ struct ComposeView: View { }, finishVoiceMessageRecording: finishVoiceMessageRecording, allowVoiceMessagesToContact: allowVoiceMessagesToContact, - // TODO in 5.2 - allow if ttl is not configured - // timedMessageAllowed: chat.chatInfo.featureEnabled(.timedMessages), - timedMessageAllowed: chat.chatInfo.featureEnabled(.timedMessages) && chat.chatInfo.timedMessagesTTL != nil, + timedMessageAllowed: chat.chatInfo.featureEnabled(.timedMessages), onMediaAdded: { media in if !media.isEmpty { chosenMedia = media }}, keyboardVisible: $keyboardVisible ) diff --git a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift index 9bee25f585..ff1892d996 100644 --- a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift +++ b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift @@ -24,7 +24,7 @@ struct ContactPreferencesView: View { List { timedMessagesFeatureSection() featureSection(.fullDelete, user.fullPreferences.fullDelete.allow, contact.mergedPreferences.fullDelete, $featuresAllowed.fullDelete) - // featureSection(.reactions, user.fullPreferences.reactions.allow, contact.mergedPreferences.reactions, $featuresAllowed.reactions) + featureSection(.reactions, user.fullPreferences.reactions.allow, contact.mergedPreferences.reactions, $featuresAllowed.reactions) featureSection(.voice, user.fullPreferences.voice.allow, contact.mergedPreferences.voice, $featuresAllowed.voice) featureSection(.calls, user.fullPreferences.calls.allow, contact.mergedPreferences.calls, $featuresAllowed.calls) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index 71ef5a68d5..1ff145b08c 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -356,7 +356,8 @@ struct GroupMemberInfoView: View { private func switchMemberAddress() { Task { do { - try await apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) + let stats = try apiSwitchGroupMember(groupInfo.apiId, member.groupMemberId) + connectionStats = stats } catch let error { logger.error("switchMemberAddress apiSwitchGroupMember error: \(responseError(error))") let a = getErrorAlert(error, "Error changing address") diff --git a/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift index 92d3710c1d..c013c29f1b 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift @@ -25,7 +25,7 @@ struct GroupPreferencesView: View { featureSection(.timedMessages, $preferences.timedMessages.enable) featureSection(.fullDelete, $preferences.fullDelete.enable) featureSection(.directMessages, $preferences.directMessages.enable) - // featureSection(.reactions, $preferences.reactions.enable) + featureSection(.reactions, $preferences.reactions.enable) featureSection(.voice, $preferences.voice.enable) if groupInfo.canEdit { @@ -79,7 +79,7 @@ struct GroupPreferencesView: View { DropdownCustomTimePicker( selection: $preferences.timedMessages.ttl, label: "Delete after", - dropdownValues: TimedMessagesPreference.ttlValues.filter { $0 != nil }, // TODO in 5.2 - allow "off" + dropdownValues: TimedMessagesPreference.ttlValues, customPickerConfirmButtonText: "Select", customPickerDescription: "Delete after" ) diff --git a/apps/ios/Shared/Views/Helpers/CustomTimePicker.swift b/apps/ios/Shared/Views/Helpers/CustomTimePicker.swift index 03bb9f10cb..09aae1cb15 100644 --- a/apps/ios/Shared/Views/Helpers/CustomTimePicker.swift +++ b/apps/ios/Shared/Views/Helpers/CustomTimePicker.swift @@ -26,8 +26,8 @@ struct CustomTimePicker: View { case .minute: return TimeUnitLimits.init(timeUnit: .minute, maxValue: 120) case .hour: return TimeUnitLimits.init(timeUnit: .hour, maxValue: 72) case .day: return TimeUnitLimits.init(timeUnit: .day, maxValue: 60) - case .week: return TimeUnitLimits.init(timeUnit: .week, maxValue: 12) // TODO in 5.2 - 54 - case .month: return TimeUnitLimits.init(timeUnit: .month, maxValue: 3) // TODO in 5.2 - 12 + case .week: return TimeUnitLimits.init(timeUnit: .week, maxValue: 52) + case .month: return TimeUnitLimits.init(timeUnit: .month, maxValue: 12) } } diff --git a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift index 48acf7a79b..049c4bee08 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift @@ -81,12 +81,11 @@ struct CreateSimpleXAddress: View { DispatchQueue.main.async { m.userAddress = UserContactLink(connReqContact: connReqContact) } -// TODO uncomment in v5.2 -// if let u = try await apiSetProfileAddress(on: true) { -// DispatchQueue.main.async { -// m.updateUser(u) -// } -// } + if let u = try await apiSetProfileAddress(on: true) { + DispatchQueue.main.async { + m.updateUser(u) + } + } await MainActor.run { progressIndicator = false } } catch let error { logger.error("CreateSimpleXAddress create address: \(responseError(error))") @@ -102,11 +101,9 @@ struct CreateSimpleXAddress: View { Text("Create SimpleX address").font(.title) } Text("Your contacts in SimpleX will see it.\nYou can change it in Settings.") - // TODO remove in in v5.2 - .foregroundColor(.clear) - .multilineTextAlignment(.center) - .font(.footnote) - .padding(.horizontal, 32) + .multilineTextAlignment(.center) + .font(.footnote) + .padding(.horizontal, 32) } } diff --git a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift index 3be84718f5..cf8978498b 100644 --- a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift +++ b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift @@ -20,7 +20,7 @@ struct PreferencesView: View { List { timedMessagesFeatureSection($preferences.timedMessages.allow) featureSection(.fullDelete, $preferences.fullDelete.allow) - // featureSection(.reactions, $preferences.reactions.allow) + featureSection(.reactions, $preferences.reactions.allow) featureSection(.voice, $preferences.voice.allow) featureSection(.calls, $preferences.calls.allow) diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index 56cab6bc47..134eeb969b 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -195,8 +195,7 @@ struct UserAddressView: View { if MFMailComposeViewController.canSendMail() { shareViaEmailButton(userAddress) } -// TODO uncomment in 5.2 -// shareWithContactsButton() + shareWithContactsButton() autoAcceptToggle() learnMoreButton() } header: { @@ -223,8 +222,7 @@ struct UserAddressView: View { let connReqContact = try await apiCreateUserAddress() DispatchQueue.main.async { chatModel.userAddress = UserContactLink(connReqContact: connReqContact) -// TODO uncomment in 5.2 -// alert = .shareOnCreate + alert = .shareOnCreate progressIndicator = false } } catch let error { diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 212eab8ccd..41961c6930 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -403,6 +403,8 @@ public enum ChatResponse: Decodable, Error { case networkConfig(networkConfig: NetCfg) case contactInfo(user: User, contact: Contact, connectionStats: ConnectionStats, customUserProfile: Profile?) case groupMemberInfo(user: User, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?) + case contactSwitchStarted(user: User, contact: Contact, connectionStats: ConnectionStats) + case groupMemberSwitchStarted(user: User, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) case contactSwitchAborted(user: User, contact: Contact, connectionStats: ConnectionStats) case groupMemberSwitchAborted(user: User, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) case contactCode(user: User, contact: Contact, connectionCode: String) @@ -524,6 +526,8 @@ public enum ChatResponse: Decodable, Error { case .networkConfig: return "networkConfig" case .contactInfo: return "contactInfo" case .groupMemberInfo: return "groupMemberInfo" + case .contactSwitchStarted: return "contactSwitchStarted" + case .groupMemberSwitchStarted: return "groupMemberSwitchStarted" case .contactSwitchAborted: return "contactSwitchAborted" case .groupMemberSwitchAborted: return "groupMemberSwitchAborted" case .contactCode: return "contactCode" @@ -644,6 +648,8 @@ public enum ChatResponse: Decodable, Error { case let .networkConfig(networkConfig): return String(describing: networkConfig) case let .contactInfo(u, contact, connectionStats, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))\ncustomUserProfile: \(String(describing: customUserProfile))") case let .groupMemberInfo(u, groupInfo, member, connectionStats_): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))") + case let .contactSwitchStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") + case let .groupMemberSwitchStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") case let .contactSwitchAborted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") case let .groupMemberSwitchAborted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)") diff --git a/src/Simplex/Chat/Help.hs b/src/Simplex/Chat/Help.hs index fa7dbafffe..8997f150d6 100644 --- a/src/Simplex/Chat/Help.hs +++ b/src/Simplex/Chat/Help.hs @@ -201,7 +201,7 @@ myAddressHelpInfo = styleMarkdown [ green "Your contact address commands:", indent <> highlight "/address " <> " - create your address", - -- indent <> highlight "/profile_address on/off " <> " - share address with your contacts (it will be added to your profile)", + indent <> highlight "/profile_address on/off " <> " - share address with your contacts (it will be added to your profile)", indent <> highlight "/delete_address " <> " - delete your address (accepted contacts will remain connected)", indent <> highlight "/show_address " <> " - show your address", indent <> highlight "/accept " <> " - accept contact request", diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 33c0276cca..1c3eeafe02 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -641,7 +641,7 @@ connReqContact_ intro cReq = "", "Anybody can send you contact requests with: " <> highlight' "/c ", "to show it again: " <> highlight' "/sa", - -- "to share with your contacts: " <> highlight' "/profile_address on", + "to share with your contacts: " <> highlight' "/profile_address on", "to delete it: " <> highlight' "/da" <> " (accepted contacts will remain connected)" ] diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index ec7a87cfea..3c1e29a8d6 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -358,7 +358,7 @@ getContactLink cc created = do cc <## "" cc <## "Anybody can send you contact requests with: /c " cc <## "to show it again: /sa" - -- cc <## "to share with your contacts: /profile_address on" + cc <## "to share with your contacts: /profile_address on" cc <## "to delete it: /da (accepted contacts will remain connected)" pure link