- @State private var sheetItem: ChooseServerOperatorsSheet? = nil
-
- var body: some View {
- GeometryReader { g in
- ScrollView {
- VStack(alignment: .leading, spacing: 20) {
- Text("Server operators")
- .font(.largeTitle)
- .bold()
- .frame(maxWidth: .infinity, alignment: .center)
- .padding(.top, 25)
-
- infoText()
- .frame(maxWidth: .infinity, alignment: .center)
-
- Spacer()
-
- ForEach(serverOperators) { srvOperator in
- operatorCheckView(srvOperator)
- }
- VStack {
- Text("SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app.").padding(.bottom, 8)
- Text("You can configure servers via settings.")
- }
- .font(.footnote)
- .multilineTextAlignment(.center)
- .frame(maxWidth: .infinity, alignment: .center)
- .padding(.horizontal, 16)
-
- Spacer()
-
- VStack(spacing: 8) {
- setOperatorsButton()
- onboardingButtonPlaceholder()
- }
- }
- .frame(minHeight: g.size.height)
- }
- .sheet(item: $sheetItem) { item in
- switch item {
- case .showInfo:
- ChooseServerOperatorsInfoView()
- }
- }
- .frame(maxHeight: .infinity, alignment: .top)
- }
- .frame(maxHeight: .infinity, alignment: .top)
- .padding(25)
- .interactiveDismissDisabled(selectedOperatorIds.isEmpty)
- }
-
- private func infoText() -> some View {
- Button {
- sheetItem = .showInfo
- } label: {
- Label("How it helps privacy", systemImage: "info.circle")
- .font(.headline)
- }
- }
-
- private func operatorCheckView(_ serverOperator: ServerOperator) -> some View {
- let checked = selectedOperatorIds.contains(serverOperator.operatorId)
- let icon = checked ? "checkmark.circle.fill" : "circle"
- let iconColor = checked ? theme.colors.primary : Color(uiColor: .tertiaryLabel).asAnotherColorFromSecondary(theme)
- return HStack(spacing: 10) {
- Image(serverOperator.largeLogo(colorScheme))
- .resizable()
- .scaledToFit()
- .frame(height: 48)
- Spacer()
- Image(systemName: icon)
- .resizable()
- .scaledToFit()
- .frame(width: 26, height: 26)
- .foregroundColor(iconColor)
- }
- .background(theme.colors.background)
- .padding()
- .clipShape(RoundedRectangle(cornerRadius: 18))
- .overlay(
- RoundedRectangle(cornerRadius: 18)
- .stroke(Color(uiColor: .secondarySystemFill), lineWidth: 2)
- )
- .padding(.horizontal, 2)
- .onTapGesture {
- if checked {
- selectedOperatorIds.remove(serverOperator.operatorId)
- } else {
- selectedOperatorIds.insert(serverOperator.operatorId)
- }
- }
- }
-
- private func setOperatorsButton() -> some View {
- Button {
- dismiss()
- } label: {
- Text("OK")
- }
- .buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty))
- .disabled(selectedOperatorIds.isEmpty)
- }
-}
-
let operatorsPostLink = URL(string: "https://simplex.chat/blog/20241125-servers-operated-by-flux-true-privacy-and-decentralization-for-all-users.html")!
struct ChooseServerOperatorsInfoView: View {
@@ -408,5 +444,5 @@ struct ChooseServerOperatorsInfoView: View {
}
#Preview {
- OnboardingConditionsView()
+ ChooseServerOperators(onboarding: true)
}
diff --git a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift
index ae72cb1be5..53cf73f1c9 100644
--- a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift
+++ b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift
@@ -62,7 +62,8 @@ struct CreateProfile: View {
.frame(height: 20)
} footer: {
VStack(alignment: .leading, spacing: 8) {
- Text("Your profile is stored on your device and only shared with your contacts.")
+ Text("Your profile, contacts and delivered messages are stored on your device.")
+ Text("The profile is only shared with your contacts.")
}
.foregroundColor(theme.colors.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
@@ -117,22 +118,25 @@ struct CreateFirstProfile: View {
@State private var nextStepNavLinkActive = false
var body: some View {
- let v = VStack(alignment: .leading, spacing: 16) {
- VStack(alignment: .center, spacing: 16) {
- Text("Create profile")
+ VStack(alignment: .leading, spacing: 20) {
+ VStack(alignment: .center, spacing: 20) {
+ Text("Create your profile")
.font(.largeTitle)
.bold()
.multilineTextAlignment(.center)
-
- Text("Your profile is stored on your device and only shared with your contacts.")
+
+ Text("Your profile, contacts and delivered messages are stored on your device.")
+ .font(.callout)
+ .foregroundColor(theme.colors.secondary)
+ .multilineTextAlignment(.center)
+
+ Text("The profile is only shared with your contacts.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
.multilineTextAlignment(.center)
}
- .fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity) // Ensures it takes up the full width
.padding(.horizontal, 10)
- .onTapGesture { focusDisplayName = false }
HStack {
let name = displayName.trimmingCharacters(in: .whitespaces)
@@ -170,23 +174,12 @@ struct CreateFirstProfile: View {
}
}
.onAppear() {
- if #available(iOS 16, *) {
- focusDisplayName = true
- } else {
- // it does not work before animation completes on iOS 15
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
- focusDisplayName = true
- }
- }
+ focusDisplayName = true
}
.padding(.horizontal, 25)
+ .padding(.top, 10)
.padding(.bottom, 25)
.frame(maxWidth: .infinity, alignment: .leading)
- if #available(iOS 16, *) {
- return v.padding(.top, 10)
- } else {
- return v.padding(.top, 75).ignoresSafeArea(.all, edges: .top)
- }
}
func createProfileButton() -> some View {
@@ -214,7 +207,7 @@ struct CreateFirstProfile: View {
}
private func nextStepDestinationView() -> some View {
- OnboardingConditionsView()
+ ChooseServerOperators(onboarding: true)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
}
@@ -243,15 +236,15 @@ private func showCreateProfileAlert(
_ error: Error
) {
let m = ChatModel.shared
- switch error as? ChatError {
- case .errorStore(.duplicateName),
- .error(.userExists):
+ switch error as? ChatResponse {
+ case .chatCmdError(_, .errorStore(.duplicateName)),
+ .chatCmdError(_, .error(.userExists)):
if m.currentUser == nil {
AlertManager.shared.showAlert(duplicateUserAlert)
} else {
showAlert(.duplicateUserError)
}
- case .error(.invalidDisplayName):
+ case .chatCmdError(_, .error(.invalidDisplayName)):
if m.currentUser == nil {
AlertManager.shared.showAlert(invalidDisplayNameAlert)
} else {
diff --git a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift
index a2f5db7f03..befb34b318 100644
--- a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift
+++ b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift
@@ -31,7 +31,7 @@ struct CreateSimpleXAddress: View {
Spacer()
if let userAddress = m.userAddress {
- SimpleXCreatedLinkQRCode(link: userAddress.connLinkContact, short: Binding.constant(false))
+ SimpleXLinkQRCode(uri: userAddress.connReqContact)
.frame(maxHeight: g.size.width)
shareQRCodeButton(userAddress)
.frame(maxWidth: .infinity)
@@ -77,9 +77,9 @@ struct CreateSimpleXAddress: View {
progressIndicator = true
Task {
do {
- let connLinkContact = try await apiCreateUserAddress(short: false)
+ let connReqContact = try await apiCreateUserAddress()
DispatchQueue.main.async {
- m.userAddress = UserContactLink(connLinkContact: connLinkContact)
+ m.userAddress = UserContactLink(connReqContact: connReqContact)
}
await MainActor.run { progressIndicator = false }
} catch let error {
@@ -121,7 +121,7 @@ struct CreateSimpleXAddress: View {
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
Button {
- showShareSheet(items: [simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: false))])
+ showShareSheet(items: [simplexChatLink(userAddress.connReqContact)])
} label: {
Label("Share", systemImage: "square.and.arrow.up")
}
@@ -189,7 +189,7 @@ struct SendAddressMailView: View {
let messageBody = String(format: NSLocalizedString("""
Hi!
Connect to me via SimpleX Chat
- """, comment: "email text"), simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: false)))
+ """, comment: "email text"), simplexChatLink(userAddress.connReqContact))
MailView(
isShowing: self.$showMailView,
result: $mailViewResult,
diff --git a/apps/ios/Shared/Views/Onboarding/OnboardingView.swift b/apps/ios/Shared/Views/Onboarding/OnboardingView.swift
index 8f448dc508..b2b1b8fa68 100644
--- a/apps/ios/Shared/Views/Onboarding/OnboardingView.swift
+++ b/apps/ios/Shared/Views/Onboarding/OnboardingView.swift
@@ -23,7 +23,7 @@ struct OnboardingView: View {
case .step3_CreateSimpleXAddress: // deprecated
CreateSimpleXAddress()
case .step3_ChooseServerOperators:
- OnboardingConditionsView()
+ ChooseServerOperators(onboarding: true)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
case .step4_SetNotificationsMode:
@@ -44,7 +44,7 @@ enum OnboardingStage: String, Identifiable {
case step1_SimpleXInfo
case step2_CreateProfile // deprecated
case step3_CreateSimpleXAddress // deprecated
- case step3_ChooseServerOperators // changed to simplified conditions
+ case step3_ChooseServerOperators
case step4_SetNotificationsMode
case onboardingComplete
diff --git a/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift b/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift
index 31865e7af9..97e1f49382 100644
--- a/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift
+++ b/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift
@@ -17,7 +17,7 @@ struct SetNotificationsMode: View {
var body: some View {
GeometryReader { g in
- let v = ScrollView {
+ ScrollView {
VStack(alignment: .center, spacing: 20) {
Text("Push notifications")
.font(.largeTitle)
@@ -57,17 +57,11 @@ struct SetNotificationsMode: View {
.padding(25)
.frame(minHeight: g.size.height)
}
- if #available(iOS 16.4, *) {
- v.scrollBounceBehavior(.basedOnSize)
- } else {
- v
- }
}
.frame(maxHeight: .infinity)
.sheet(isPresented: $showInfo) {
NotificationsInfoView()
}
- .navigationBarHidden(true) // necessary on iOS 15
}
private func setNotificationsMode(_ token: DeviceToken, _ mode: NotificationsMode) {
diff --git a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift
index 9f41a37b1d..dbae3e9fb3 100644
--- a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift
+++ b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift
@@ -18,7 +18,7 @@ struct SimpleXInfo: View {
var body: some View {
GeometryReader { g in
- let v = ScrollView {
+ ScrollView {
VStack(alignment: .leading) {
VStack(alignment: .center, spacing: 10) {
Image(colorScheme == .light ? "logo" : "logo-light")
@@ -36,7 +36,7 @@ struct SimpleXInfo: View {
.font(.headline)
}
}
-
+
Spacer()
VStack(alignment: .leading) {
@@ -66,9 +66,6 @@ struct SimpleXInfo: View {
}
}
}
- .padding(.horizontal, 25)
- .padding(.top, 75)
- .padding(.bottom, 25)
.frame(minHeight: g.size.height)
}
.sheet(isPresented: Binding(
@@ -91,17 +88,14 @@ struct SimpleXInfo: View {
createProfileNavLinkActive: $createProfileNavLinkActive
)
}
- if #available(iOS 16.4, *) {
- v.scrollBounceBehavior(.basedOnSize)
- } else {
- v
- }
}
.onAppear() {
setLastVersionDefault()
}
.frame(maxHeight: .infinity)
- .navigationBarHidden(true) // necessary on iOS 15
+ .padding(.horizontal, 25)
+ .padding(.top, 75)
+ .padding(.bottom, 25)
}
private func onboardingInfoRow(_ image: String, _ title: LocalizedStringKey, _ text: LocalizedStringKey, width: CGFloat) -> some View {
@@ -135,7 +129,6 @@ struct SimpleXInfo: View {
NavigationLink(isActive: $createProfileNavLinkActive) {
CreateFirstProfile()
- .modifier(ThemedBackground())
} label: {
EmptyView()
}
diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
index f65a21623a..f7c7145dcc 100644
--- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
+++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift
@@ -594,6 +594,8 @@ func shouldShowWhatsNew() -> Bool {
}
fileprivate struct NewOperatorsView: View {
+ @State private var showOperatorsSheet = false
+
var body: some View {
VStack(alignment: .leading) {
Image((operatorsInfo[.flux] ?? ServerOperator.dummyOperatorInfo).largeLogo)
@@ -604,7 +606,16 @@ fileprivate struct NewOperatorsView: View {
.multilineTextAlignment(.leading)
.lineLimit(10)
HStack {
- Text("Enable Flux in Network & servers settings for better metadata privacy.")
+ Button("Enable Flux") {
+ showOperatorsSheet = true
+ }
+ Text("for better metadata privacy.")
+ }
+ }
+ .sheet(isPresented: $showOperatorsSheet) {
+ NavigationView {
+ ChooseServerOperators(onboarding: false)
+ .modifier(ThemedBackground())
}
}
}
diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
index 01b25baed8..67020e09e7 100644
--- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
+++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift
@@ -456,12 +456,12 @@ struct ConnectDesktopView: View {
}
} catch let e {
await MainActor.run {
- switch e as? ChatError {
- case .errorRemoteCtrl(.badInvitation): alert = .badInvitationError
- case .error(.commandError): alert = .badInvitationError
- case let .errorRemoteCtrl(.badVersion(v)): alert = .badVersionError(version: v)
- case .errorAgent(.RCP(.version)): alert = .badVersionError(version: nil)
- case .errorAgent(.RCP(.ctrlAuth)): alert = .desktopDisconnectedError
+ switch e as? ChatResponse {
+ case .chatCmdError(_, .errorRemoteCtrl(.badInvitation)): alert = .badInvitationError
+ case .chatCmdError(_, .error(.commandError)): alert = .badInvitationError
+ case let .chatCmdError(_, .errorRemoteCtrl(.badVersion(v))): alert = .badVersionError(version: v)
+ case .chatCmdError(_, .errorAgent(.RCP(.version))): alert = .badVersionError(version: nil)
+ case .chatCmdError(_, .errorAgent(.RCP(.ctrlAuth))): alert = .desktopDisconnectedError
default: errorAlert(e)
}
}
diff --git a/apps/ios/Shared/Views/TerminalView.swift b/apps/ios/Shared/Views/TerminalView.swift
index 554219eb69..2b58abef65 100644
--- a/apps/ios/Shared/Views/TerminalView.swift
+++ b/apps/ios/Shared/Views/TerminalView.swift
@@ -145,18 +145,18 @@ struct TerminalView: View {
}
func consoleSendMessage() {
+ let cmd = ChatCommand.string(composeState.message)
if composeState.message.starts(with: "/sql") && (!prefPerformLA || !developerTools) {
- let resp: APIResult = APIResult.error(ChatError.error(errorType: ChatErrorType.commandError(message: "Failed reading: empty")))
+ let resp = ChatResponse.chatCmdError(user_: nil, chatError: ChatError.error(errorType: ChatErrorType.commandError(message: "Failed reading: empty")))
Task {
- await TerminalItems.shared.addCommand(.now, .string(composeState.message), resp)
+ await TerminalItems.shared.addCommand(.now, cmd, resp)
}
} else {
- let cmd = composeState.message
DispatchQueue.global().async {
Task {
- await MainActor.run { composeState.inProgress = true }
- await sendTerminalCmd(cmd)
- await MainActor.run { composeState.inProgress = false }
+ composeState.inProgress = true
+ _ = await chatSendCmd(cmd)
+ composeState.inProgress = false
}
}
}
@@ -164,38 +164,12 @@ struct TerminalView: View {
}
}
-func sendTerminalCmd(_ cmd: String) async {
- let start: Date = .now
- await withCheckedContinuation { (cont: CheckedContinuation) in
- let d = sendSimpleXCmdStr(cmd)
- Task {
- guard let d else {
- await TerminalItems.shared.addCommand(start, ChatCommand.string(cmd), APIResult.error(.invalidJSON(json: nil)))
- return
- }
- let r0: APIResult = decodeAPIResult(d)
- guard case .invalid = r0 else {
- await TerminalItems.shared.addCommand(start, .string(cmd), r0)
- return
- }
- let r1: APIResult = decodeAPIResult(d)
- guard case .invalid = r1 else {
- await TerminalItems.shared.addCommand(start, .string(cmd), r1)
- return
- }
- let r2: APIResult = decodeAPIResult(d)
- await TerminalItems.shared.addCommand(start, .string(cmd), r2)
- }
- cont.resume(returning: ())
- }
-}
-
struct TerminalView_Previews: PreviewProvider {
static var previews: some View {
let chatModel = ChatModel()
chatModel.terminalItems = [
- .err(.now, APIResult.invalid(type: "contactSubscribed", json: "{}".data(using: .utf8)!).unexpected),
- .err(.now, APIResult.invalid(type: "newChatItems", json: "{}".data(using: .utf8)!).unexpected)
+ .resp(.now, ChatResponse.response(type: "contactSubscribed", json: "{}")),
+ .resp(.now, ChatResponse.response(type: "newChatItems", json: "{}"))
]
return NavigationView {
TerminalView()
diff --git a/apps/ios/Shared/Views/UserSettings/AppSettings.swift b/apps/ios/Shared/Views/UserSettings/AppSettings.swift
index 44e0b20958..00532c0a8e 100644
--- a/apps/ios/Shared/Views/UserSettings/AppSettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/AppSettings.swift
@@ -38,6 +38,7 @@ extension AppSettings {
privacyLinkPreviewsGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
}
+ if let val = privacyChatListOpenLinks { privacyChatListOpenLinksDefault.set(val) }
if let val = privacyShowChatPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) }
if let val = privacySaveLastDraft { def.setValue(val, forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) }
if let val = privacyProtectScreen { def.setValue(val, forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) }
@@ -77,6 +78,7 @@ extension AppSettings {
c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get()
c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get()
c.privacyLinkPreviews = def.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
+ c.privacyChatListOpenLinks = privacyChatListOpenLinksDefault.get()
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
c.privacySaveLastDraft = def.bool(forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT)
c.privacyProtectScreen = def.bool(forKey: DEFAULT_PRIVACY_PROTECT_SCREEN)
diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift
index fa698f8b7c..55f2e837b8 100644
--- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift
@@ -209,16 +209,11 @@ struct AdvancedNetworkSettings: View {
}
Section {
- Picker("Use web port", selection: $netCfg.smpWebPortServers) {
- ForEach(SMPWebPortServers.allCases, id: \.self) { Text($0.text) }
- }
- .frame(height: 36)
+ Toggle("Use web port", isOn: $netCfg.smpWebPort)
} header: {
Text("TCP port for messaging")
} footer: {
- netCfg.smpWebPortServers == .preset
- ? Text("Use TCP port 443 for preset servers only.")
- : Text("Use TCP port \(netCfg.smpWebPortServers == .all ? "443" : "5223") when no port is specified.")
+ Text("Use TCP port \(netCfg.smpWebPort ? "443" : "5223") when no port is specified.")
}
Section("TCP connection") {
diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift
index afbccc109c..24da6a94a8 100644
--- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift
+++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift
@@ -38,9 +38,9 @@ struct OperatorView: View {
.allowsHitTesting(!testing)
}
- private func operatorView() -> some View {
+ @ViewBuilder private func operatorView() -> some View {
let duplicateHosts = findDuplicateHosts(serverErrors)
- return VStack {
+ VStack {
List {
Section {
infoViewLink()
@@ -500,14 +500,14 @@ struct SingleOperatorUsageConditionsView: View {
}
}
- private func acceptConditionsButton() -> some View {
+ @ViewBuilder private func acceptConditionsButton() -> some View {
let operatorIds = ChatModel.shared.conditions.serverOperators
.filter {
$0.operatorId == userServers[operatorIndex].operator_.operatorId || // Opened operator
($0.enabled && !$0.conditionsAcceptance.conditionsAccepted) // Other enabled operators with conditions not accepted
}
.map { $0.operatorId }
- return Button {
+ Button {
acceptForOperators(operatorIds, operatorIndex)
} label: {
Text("Accept conditions")
diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift
index b9737914ec..ed3c5c773c 100644
--- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift
+++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift
@@ -38,9 +38,9 @@ struct YourServersView: View {
.allowsHitTesting(!testing)
}
- private func yourServersView() -> some View {
+ @ViewBuilder private func yourServersView() -> some View {
let duplicateHosts = findDuplicateHosts(serverErrors)
- return List {
+ List {
if !userServers[operatorIndex].smpServers.filter({ !$0.deleted }).isEmpty {
Section {
ForEach($userServers[operatorIndex].smpServers) { srv in
diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
index eba7f8066a..0b9d1ef76c 100644
--- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
+++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift
@@ -14,13 +14,12 @@ struct PrivacySettings: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_PRIVACY_ACCEPT_IMAGES) private var autoAcceptImages = true
@AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true
+ @State private var chatListOpenLinks = privacyChatListOpenLinksDefault.get()
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
@AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true
@AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
- @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
- @AppStorage(DEFAULT_PRIVACY_SHORT_LINKS) private var shortSimplexLinks = false
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@State private var currentLAMode = privacyLocalAuthModeDefault.get()
@@ -76,6 +75,17 @@ struct PrivacySettings: View {
privacyLinkPreviewsGroupDefault.set(linkPreviews)
}
}
+ settingsRow("arrow.up.right.circle", color: theme.colors.secondary) {
+ Picker("Open links from chat list", selection: $chatListOpenLinks) {
+ ForEach(PrivacyChatListOpenLinksMode.allCases) { mode in
+ Text(mode.text)
+ }
+ }
+ }
+ .frame(height: 36)
+ .onChange(of: chatListOpenLinks) { mode in
+ privacyChatListOpenLinksDefault.set(mode)
+ }
settingsRow("message", color: theme.colors.secondary) {
Toggle("Show last messages", isOn: $showChatPreviews)
}
@@ -101,11 +111,6 @@ struct PrivacySettings: View {
.onChange(of: simplexLinkMode) { mode in
privacySimplexLinkModeDefault.set(mode)
}
- if developerTools {
- settingsRow("link.badge.plus", color: theme.colors.secondary) {
- Toggle("Use short links (BETA)", isOn: $shortSimplexLinks)
- }
- }
} header: {
Text("Chats")
.foregroundColor(theme.colors.secondary)
diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
index e06b1c4dd3..80e2a537da 100644
--- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift
+++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift
@@ -29,10 +29,10 @@ let DEFAULT_WEBRTC_ICE_SERVERS = "webrtcICEServers"
let DEFAULT_CALL_KIT_CALLS_IN_RECENTS = "callKitCallsInRecents"
let DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages" // unused. Use GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES instead
let DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews" // deprecated, moved to app group
+let DEFAULT_PRIVACY_CHAT_LIST_OPEN_LINKS = "privacyChatListOpenLinks"
let DEFAULT_PRIVACY_SIMPLEX_LINK_MODE = "privacySimplexLinkMode"
let DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS = "privacyShowChatPreviews"
let DEFAULT_PRIVACY_SAVE_LAST_DRAFT = "privacySaveLastDraft"
-let DEFAULT_PRIVACY_SHORT_LINKS = "privacyShortLinks"
let DEFAULT_PRIVACY_PROTECT_SCREEN = "privacyProtectScreen"
let DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET = "privacyDeliveryReceiptsSet"
let DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS = "privacyMediaBlurRadius"
@@ -99,7 +99,6 @@ let appDefaults: [String: Any] = [
DEFAULT_PRIVACY_SIMPLEX_LINK_MODE: SimpleXLinkMode.description.rawValue,
DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS: true,
DEFAULT_PRIVACY_SAVE_LAST_DRAFT: true,
- DEFAULT_PRIVACY_SHORT_LINKS: false,
DEFAULT_PRIVACY_PROTECT_SCREEN: false,
DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET: false,
DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS: 0,
@@ -184,6 +183,8 @@ let connectViaLinkTabDefault = EnumDefault(defaults: UserDefa
let privacySimplexLinkModeDefault = EnumDefault(defaults: UserDefaults.standard, forKey: DEFAULT_PRIVACY_SIMPLEX_LINK_MODE, withDefault: .description)
+let privacyChatListOpenLinksDefault = EnumDefault(defaults: UserDefaults.standard, forKey: DEFAULT_PRIVACY_CHAT_LIST_OPEN_LINKS, withDefault: PrivacyChatListOpenLinksMode.ask)
+
let privacyLocalAuthModeDefault = EnumDefault(defaults: UserDefaults.standard, forKey: DEFAULT_LA_MODE, withDefault: .system)
let privacyDeliveryReceiptsSet = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET)
@@ -280,159 +281,159 @@ struct SettingsView: View {
}
}
- func settingsView() -> some View {
- List {
- let user = chatModel.currentUser
- Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
- NavigationLink {
- NotificationsView()
- .navigationTitle("Notifications")
- .modifier(ThemedBackground(grouped: true))
- } label: {
- HStack {
- notificationsIcon()
- Text("Notifications")
- }
- }
- .disabled(chatModel.chatRunning != true)
-
- NavigationLink {
- NetworkAndServers()
- .navigationTitle("Network & servers")
- .modifier(ThemedBackground(grouped: true))
- } label: {
- settingsRow("externaldrive.connected.to.line.below", color: theme.colors.secondary) { Text("Network & servers") }
- }
- .disabled(chatModel.chatRunning != true)
-
- NavigationLink {
- CallSettings()
- .navigationTitle("Your calls")
- .modifier(ThemedBackground(grouped: true))
- } label: {
- settingsRow("video", color: theme.colors.secondary) { Text("Audio & video calls") }
- }
- .disabled(chatModel.chatRunning != true)
-
- NavigationLink {
- PrivacySettings()
- .navigationTitle("Your privacy")
- .modifier(ThemedBackground(grouped: true))
- } label: {
- settingsRow("lock", color: theme.colors.secondary) { Text("Privacy & security") }
- }
- .disabled(chatModel.chatRunning != true)
-
- if UIApplication.shared.supportsAlternateIcons {
+ @ViewBuilder func settingsView() -> some View {
+ let user = chatModel.currentUser
+ List {
+ Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
NavigationLink {
- AppearanceSettings()
- .navigationTitle("Appearance")
+ NotificationsView()
+ .navigationTitle("Notifications")
.modifier(ThemedBackground(grouped: true))
} label: {
- settingsRow("sun.max", color: theme.colors.secondary) { Text("Appearance") }
+ HStack {
+ notificationsIcon()
+ Text("Notifications")
+ }
}
.disabled(chatModel.chatRunning != true)
- }
- }
-
- Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) {
- chatDatabaseRow()
- NavigationLink {
- MigrateFromDevice(showProgressOnSettings: $showProgress)
- .toolbar {
- // Redaction broken for `.navigationTitle` - using a toolbar item instead.
- ToolbarItem(placement: .principal) {
- Text("Migrate device").font(.headline)
- }
- }
- .modifier(ThemedBackground(grouped: true))
- .navigationBarTitleDisplayMode(.large)
- } label: {
- settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
- }
- }
-
- Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
- if let user = user {
+
NavigationLink {
- ChatHelp(dismissSettingsSheet: dismiss)
- .navigationTitle("Welcome \(user.displayName)!")
+ NetworkAndServers()
+ .navigationTitle("Network & servers")
+ .modifier(ThemedBackground(grouped: true))
+ } label: {
+ settingsRow("externaldrive.connected.to.line.below", color: theme.colors.secondary) { Text("Network & servers") }
+ }
+ .disabled(chatModel.chatRunning != true)
+
+ NavigationLink {
+ CallSettings()
+ .navigationTitle("Your calls")
+ .modifier(ThemedBackground(grouped: true))
+ } label: {
+ settingsRow("video", color: theme.colors.secondary) { Text("Audio & video calls") }
+ }
+ .disabled(chatModel.chatRunning != true)
+
+ NavigationLink {
+ PrivacySettings()
+ .navigationTitle("Your privacy")
+ .modifier(ThemedBackground(grouped: true))
+ } label: {
+ settingsRow("lock", color: theme.colors.secondary) { Text("Privacy & security") }
+ }
+ .disabled(chatModel.chatRunning != true)
+
+ if UIApplication.shared.supportsAlternateIcons {
+ NavigationLink {
+ AppearanceSettings()
+ .navigationTitle("Appearance")
+ .modifier(ThemedBackground(grouped: true))
+ } label: {
+ settingsRow("sun.max", color: theme.colors.secondary) { Text("Appearance") }
+ }
+ .disabled(chatModel.chatRunning != true)
+ }
+ }
+
+ Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) {
+ chatDatabaseRow()
+ NavigationLink {
+ MigrateFromDevice(showProgressOnSettings: $showProgress)
+ .toolbar {
+ // Redaction broken for `.navigationTitle` - using a toolbar item instead.
+ ToolbarItem(placement: .principal) {
+ Text("Migrate device").font(.headline)
+ }
+ }
+ .modifier(ThemedBackground(grouped: true))
+ .navigationBarTitleDisplayMode(.large)
+ } label: {
+ settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
+ }
+ }
+
+ Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
+ if let user = user {
+ NavigationLink {
+ ChatHelp(dismissSettingsSheet: dismiss)
+ .navigationTitle("Welcome \(user.displayName)!")
+ .modifier(ThemedBackground())
+ .frame(maxHeight: .infinity, alignment: .top)
+ } label: {
+ settingsRow("questionmark", color: theme.colors.secondary) { Text("How to use it") }
+ }
+ }
+ NavigationLink {
+ WhatsNewView(viaSettings: true, updatedConditions: false)
+ .modifier(ThemedBackground())
+ .navigationBarTitleDisplayMode(.inline)
+ } label: {
+ settingsRow("plus", color: theme.colors.secondary) { Text("What's new") }
+ }
+ NavigationLink {
+ SimpleXInfo(onboarding: false)
+ .navigationBarTitle("", displayMode: .inline)
.modifier(ThemedBackground())
.frame(maxHeight: .infinity, alignment: .top)
} label: {
- settingsRow("questionmark", color: theme.colors.secondary) { Text("How to use it") }
+ settingsRow("info", color: theme.colors.secondary) { Text("About SimpleX Chat") }
}
- }
- NavigationLink {
- WhatsNewView(viaSettings: true, updatedConditions: false)
- .modifier(ThemedBackground())
- .navigationBarTitleDisplayMode(.inline)
- } label: {
- settingsRow("plus", color: theme.colors.secondary) { Text("What's new") }
- }
- NavigationLink {
- SimpleXInfo(onboarding: false)
- .navigationBarTitle("", displayMode: .inline)
- .modifier(ThemedBackground())
- .frame(maxHeight: .infinity, alignment: .top)
- } label: {
- settingsRow("info", color: theme.colors.secondary) { Text("About SimpleX Chat") }
- }
- settingsRow("number", color: theme.colors.secondary) {
- Button("Send questions and ideas") {
- dismiss()
- DispatchQueue.main.async {
- UIApplication.shared.open(simplexTeamURL)
+ settingsRow("number", color: theme.colors.secondary) {
+ Button("Send questions and ideas") {
+ dismiss()
+ DispatchQueue.main.async {
+ UIApplication.shared.open(simplexTeamURL)
+ }
}
}
+ .disabled(chatModel.chatRunning != true)
+ settingsRow("envelope", color: theme.colors.secondary) { Text("[Send us email](mailto:chat@simplex.chat)") }
}
- .disabled(chatModel.chatRunning != true)
- settingsRow("envelope", color: theme.colors.secondary) { Text("[Send us email](mailto:chat@simplex.chat)") }
- }
- Section(header: Text("Support SimpleX Chat").foregroundColor(theme.colors.secondary)) {
- settingsRow("keyboard", color: theme.colors.secondary) { Text("[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)") }
- settingsRow("star", color: theme.colors.secondary) {
- Button("Rate the app") {
- if let scene = sceneDelegate.windowScene {
- SKStoreReviewController.requestReview(in: scene)
+ Section(header: Text("Support SimpleX Chat").foregroundColor(theme.colors.secondary)) {
+ settingsRow("keyboard", color: theme.colors.secondary) { Text("[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)") }
+ settingsRow("star", color: theme.colors.secondary) {
+ Button("Rate the app") {
+ if let scene = sceneDelegate.windowScene {
+ SKStoreReviewController.requestReview(in: scene)
+ }
}
}
+ ZStack(alignment: .leading) {
+ Image(colorScheme == .dark ? "github_light" : "github")
+ .resizable()
+ .frame(width: 24, height: 24)
+ .opacity(0.5)
+ .colorMultiply(theme.colors.secondary)
+ Text("[Star on GitHub](https://github.com/simplex-chat/simplex-chat)")
+ .padding(.leading, indent)
+ }
}
- ZStack(alignment: .leading) {
- Image(colorScheme == .dark ? "github_light" : "github")
- .resizable()
- .frame(width: 24, height: 24)
- .opacity(0.5)
- .colorMultiply(theme.colors.secondary)
- Text("[Star on GitHub](https://github.com/simplex-chat/simplex-chat)")
- .padding(.leading, indent)
- }
- }
- Section(header: Text("Develop").foregroundColor(theme.colors.secondary)) {
- NavigationLink {
- DeveloperView()
- .navigationTitle("Developer tools")
- .modifier(ThemedBackground(grouped: true))
- } label: {
- settingsRow("chevron.left.forwardslash.chevron.right", color: theme.colors.secondary) { Text("Developer tools") }
- }
- NavigationLink {
- VersionView()
- .navigationBarTitle("App version")
- .modifier(ThemedBackground())
- } label: {
- Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))")
+ Section(header: Text("Develop").foregroundColor(theme.colors.secondary)) {
+ NavigationLink {
+ DeveloperView()
+ .navigationTitle("Developer tools")
+ .modifier(ThemedBackground(grouped: true))
+ } label: {
+ settingsRow("chevron.left.forwardslash.chevron.right", color: theme.colors.secondary) { Text("Developer tools") }
+ }
+ NavigationLink {
+ VersionView()
+ .navigationBarTitle("App version")
+ .modifier(ThemedBackground())
+ } label: {
+ Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))")
+ }
}
}
- }
- .navigationTitle("Your settings")
- .modifier(ThemedBackground(grouped: true))
- .onDisappear {
- chatModel.showingTerminal = false
- chatModel.terminalItems = []
- }
+ .navigationTitle("Your settings")
+ .modifier(ThemedBackground(grouped: true))
+ .onDisappear {
+ chatModel.showingTerminal = false
+ chatModel.terminalItems = []
+ }
}
private func chatDatabaseRow() -> some View {
diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift
index 4813edf96c..7965215b49 100644
--- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift
+++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift
@@ -8,7 +8,7 @@
import SwiftUI
import MessageUI
-@preconcurrency import SimpleXChat
+import SimpleXChat
struct UserAddressView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@@ -16,7 +16,6 @@ struct UserAddressView: View {
@EnvironmentObject var theme: AppTheme
@State var shareViaProfile = false
@State var autoCreate = false
- @State private var showShortLink = true
@State private var aas = AutoAcceptState()
@State private var savedAAS = AutoAcceptState()
@State private var showMailView = false
@@ -136,8 +135,8 @@ struct UserAddressView: View {
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
Section {
- SimpleXCreatedLinkQRCode(link: userAddress.connLinkContact, short: $showShortLink)
- .id("simplex-contact-address-qrcode-\(userAddress.connLinkContact.simplexChatUri(short: showShortLink))")
+ SimpleXLinkQRCode(uri: userAddress.connReqContact)
+ .id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
shareQRCodeButton(userAddress)
// if MFMailComposeViewController.canSendMail() {
// shareViaEmailButton(userAddress)
@@ -154,7 +153,8 @@ struct UserAddressView: View {
}
addressSettingsButton(userAddress)
} header: {
- ToggleShortLinkHeader(text: Text("For social media"), link: userAddress.connLinkContact, short: $showShortLink)
+ Text("For social media")
+ .foregroundColor(theme.colors.secondary)
} footer: {
if aas.business {
Text("Add your team members to the conversations.")
@@ -193,10 +193,9 @@ struct UserAddressView: View {
progressIndicator = true
Task {
do {
- let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS)
- let connLinkContact = try await apiCreateUserAddress(short: short)
+ let connReqContact = try await apiCreateUserAddress()
DispatchQueue.main.async {
- chatModel.userAddress = UserContactLink(connLinkContact: connLinkContact)
+ chatModel.userAddress = UserContactLink(connReqContact: connReqContact)
alert = .shareOnCreate
progressIndicator = false
}
@@ -232,7 +231,7 @@ struct UserAddressView: View {
private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View {
Button {
- showShareSheet(items: [simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: showShortLink))])
+ showShareSheet(items: [simplexChatLink(userAddress.connReqContact)])
} label: {
settingsRow("square.and.arrow.up", color: theme.colors.secondary) {
Text("Share address")
@@ -295,28 +294,6 @@ struct UserAddressView: View {
}
}
-struct ToggleShortLinkHeader: View {
- @EnvironmentObject var theme: AppTheme
- let text: Text
- var link: CreatedConnLink
- @Binding var short: Bool
-
- var body: some View {
- if link.connShortLink == nil {
- text.foregroundColor(theme.colors.secondary)
- } else {
- HStack {
- text.foregroundColor(theme.colors.secondary)
- Spacer()
- Text(short ? "Full link" : "Short link")
- .textCase(.none)
- .foregroundColor(theme.colors.primary)
- .onTapGesture { short.toggle() }
- }
- }
- }
-}
-
private struct AutoAcceptState: Equatable {
var enable = false
var incognito = false
@@ -565,7 +542,7 @@ private func saveAAS(_ aas: Binding, _ savedAAS: Binding some View {
+ @ViewBuilder private func profileActionView(_ action: UserProfileAction) -> some View {
let passwordValid = actionPassword == actionPassword.trimmingCharacters(in: .whitespaces)
let passwordField = PassphraseField(key: $actionPassword, placeholder: "Profile password", valid: passwordValid)
let actionEnabled: (User) -> Bool = { user in actionPassword != "" && passwordValid && correctPassword(user, actionPassword) }
- return List {
+ List {
switch action {
case let .deleteUser(user, delSMPQueues):
actionHeader("Delete profile", user)
diff --git a/apps/ios/SimpleX (iOS).entitlements b/apps/ios/SimpleX (iOS).entitlements
index 2ec32def0a..c78a7cb941 100644
--- a/apps/ios/SimpleX (iOS).entitlements
+++ b/apps/ios/SimpleX (iOS).entitlements
@@ -9,10 +9,6 @@
applinks:simplex.chat
applinks:www.simplex.chat
applinks:simplex.chat?mode=developer
- applinks:*.simplex.im
- applinks:*.simplex.im?mode=developer
- applinks:*.simplexonflux.com
- applinks:*.simplexonflux.com?mode=developer
com.apple.security.application-groups
diff --git a/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff b/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff
index e965e5a1a5..aba5e1384e 100644
--- a/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff
+++ b/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff
@@ -553,9 +553,8 @@
يمكنك أنت وجهة اتصالك إرسال رسائل صوتية.
No comment provided by engineer.
-
+
By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA).
- حسب ملف تعريف الدردشة (افتراضي) أو [حسب الاتصال] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA).
No comment provided by engineer.
@@ -583,9 +582,8 @@
إلغاء
No comment provided by engineer.
-
+
Cannot access keychain to save database password
- لا يمكن الوصول إلى سلسلة المفاتيح لحفظ كلمة مرور قاعدة البيانات
No comment provided by engineer.
@@ -603,9 +601,8 @@
تغيير عبارة مرور قاعدة البيانات؟
No comment provided by engineer.
-
+
Change member role?
- تغيير دور العضو؟
No comment provided by engineer.
@@ -672,14 +669,12 @@
تحقق من عنوان الخادم وحاول مرة أخرى.
No comment provided by engineer.
-
+
Choose file
- اختر الملف
No comment provided by engineer.
-
+
Choose from library
- اختر من المكتبة
No comment provided by engineer.
@@ -761,9 +756,8 @@
جارِ الاتصال بالخادم…
No comment provided by engineer.
-
+
Connecting to server… (error: %@)
- الاتصال بالخادم... (الخطأ: %@)
No comment provided by engineer.
@@ -814,9 +808,8 @@
جهة الاتصال مخفية:
notification
-
+
Contact is connected
- تم الاتصال
notification
@@ -851,9 +844,8 @@
Core built at: %@
No comment provided by engineer.
-
+
Core version: v%@
- الإصدار الأساسي: v%@
No comment provided by engineer.
@@ -903,9 +895,8 @@
عبارة المرور الحالية…
No comment provided by engineer.
-
+
Currently maximum supported file size is %@.
- الحد الأقصى لحجم الملف المدعوم حاليًا هو %@.
No comment provided by engineer.
@@ -923,11 +914,9 @@
قاعدة البيانات مُعمّاة!
No comment provided by engineer.
-
+
Database encryption passphrase will be updated and stored in the keychain.
- سيتم تحديث عبارة المرور الخاصة بتشفير قاعدة البيانات وتخزينها في سلسلة المفاتيح.
-
No comment provided by engineer.
@@ -962,9 +951,8 @@
عبارة مرور قاعدة البيانات وتصديرها
No comment provided by engineer.
-
+
Database passphrase is different from saved in the keychain.
- عبارة المرور الخاصة بقاعدة البيانات مختلفة عن تلك المحفوظة في سلسلة المفاتيح.
No comment provided by engineer.
@@ -972,11 +960,9 @@
عبارة مرور قاعدة البيانات مطلوبة لفتح الدردشة.
No comment provided by engineer.
-
+
Database will be encrypted and the passphrase stored in the keychain.
- سيتم تشفير قاعدة البيانات وتخزين عبارة المرور في سلسلة المفاتيح.
-
No comment provided by engineer.
@@ -986,9 +972,8 @@
No comment provided by engineer.
-
+
Database will be migrated when the app restarts
- سيتم نقل قاعدة البيانات عند إعادة تشغيل التطبيق
No comment provided by engineer.
@@ -1088,44 +1073,36 @@
حذف المجموعة؟
No comment provided by engineer.
-
+
Delete invitation
- حذف الدعوة
No comment provided by engineer.
-
+
Delete link
- حذف الرابط
No comment provided by engineer.
-
+
Delete link?
- حذف الرابط؟
No comment provided by engineer.
-
+
Delete message?
- حذف الرسالة؟
No comment provided by engineer.
-
+
Delete messages
- حذف الرسائل
No comment provided by engineer.
-
+
Delete messages after
- حذف الرسائل بعد
No comment provided by engineer.
-
+
Delete old database
- حذف قاعدة البيانات القديمة
No comment provided by engineer.
-
+
Delete old database?
- حذف قاعدة البيانات القديمة؟
No comment provided by engineer.
@@ -1142,9 +1119,8 @@
حذف قائمة الانتظار
server test step
-
+
Delete user profile?
- حذف ملف تعريف المستخدم؟
No comment provided by engineer.
@@ -1152,9 +1128,8 @@
الوصف
No comment provided by engineer.
-
+
Develop
- يطور
No comment provided by engineer.
@@ -1187,34 +1162,28 @@
رسائل مباشرة
chat feature
-
+
Direct messages between members are prohibited.
- الرسائل المباشرة بين الأعضاء ممنوعة.
No comment provided by engineer.
-
+
Disable SimpleX Lock
- تعطيل قفل SimpleX
authentication reason
-
+
Disappearing messages
- الرسائل المختفية
chat feature
-
+
Disappearing messages are prohibited in this chat.
- يُحظر اختفاء الرسائل في هذه الدردشة.
No comment provided by engineer.
-
+
Disappearing messages are prohibited.
- الرسائل المختفية ممنوعة.
No comment provided by engineer.
-
+
Disconnect
- قطع الاتصال
server test step
@@ -1225,14 +1194,12 @@
Display name:
No comment provided by engineer.
-
+
Do NOT use SimpleX for emergency calls.
- لا تستخدم SimpleX لإجراء مكالمات الطوارئ.
No comment provided by engineer.
-
+
Do it later
- افعل ذلك لاحقا
No comment provided by engineer.
@@ -1285,93 +1252,76 @@
تفعيل الإشعارات دورية؟
No comment provided by engineer.
-
+
Encrypt
- التشفير
No comment provided by engineer.
-
+
Encrypt database?
- تشفير قاعدة البيانات؟
No comment provided by engineer.
-
+
Encrypted database
- قاعدة بيانات مشفرة
No comment provided by engineer.
-
+
Encrypted message or another event
- رسالة مشفرة أو حدث آخر
notification
-
+
Encrypted message: database error
- رسالة مشفرة: خطأ في قاعدة البيانات
notification
-
+
Encrypted message: keychain error
- رسالة مشفرة: خطأ في سلسلة المفاتيح
notification
-
+
Encrypted message: no passphrase
- الرسالة المشفرة: لا توجد عبارة مرور
notification
-
+
Encrypted message: unexpected error
- رسالة مشفرة: خطأ غير متوقع
notification
-
+
Enter correct passphrase.
- أدخل عبارة المرور الصحيحة.
No comment provided by engineer.
-
+
Enter passphrase…
- أدخل عبارة المرور…
No comment provided by engineer.
-
+
Enter server manually
- أدخل الخادم يدوياً
No comment provided by engineer.
-
+
Error
- خطأ
No comment provided by engineer.
-
+
Error accepting contact request
- خطأ في قبول طلب الاتصال
No comment provided by engineer.
Error accessing database file
No comment provided by engineer.
-
+
Error adding member(s)
- خطأ في إضافة عضو (أعضاء)
No comment provided by engineer.
-
+
Error changing address
- خطأ في تغيير العنوان
No comment provided by engineer.
-
+
Error changing role
- خطأ في تغيير الدور المتغير
No comment provided by engineer.
-
+
Error changing setting
- خطأ في تغيير الإعدادات
No comment provided by engineer.
@@ -2826,8 +2776,8 @@ We will be adding server redundancy to prevent lost messages.
The old database was not removed during the migration, it can be deleted.
No comment provided by engineer.
-
- Your profile is stored on your device and only shared with your contacts.
+
+ The profile is only shared with your contacts.
No comment provided by engineer.
@@ -5439,324 +5389,6 @@ This is your own one-time link!
Enable self-destruct passcode
تفعيل رمز التدمير الذاتي
-
- Can't message member
- لا يمكن الاتصال بالعضو
-
-
- Color chats with the new themes.
- محادثات ملونة مع السمات الجديدة.
-
-
- All chats will be removed from the list %@, and the list deleted.
- ستتم إزالة جميع الدردشات من القائمة %@، وسيتم حذف القائمة.
-
-
- Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!
- البلغارية والفنلندية والتايلاندية والأوكرانية - شكرًا للمستخدمين و[Weblate] (https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!
-
-
- Choose _Migrate from another device_ on the new device and scan QR code.
- اختر _الترحيل من جهاز آخر_ على الجهاز الجديد وامسح رمز الاستجابة السريعة.
-
-
- Conditions will be accepted for the operator(s): **%@**.
- سيتم قبول شروط المشغل (المشغلين): **%@**.
-
-
- Conditions will be accepted on: %@.
- سيتم قبول الشروط على: %@.
-
-
- Confirmed
- تم التأكيد
-
-
- Connection is blocked by server operator:
-%@
- تم حظر الاتصال من قبل مشغل الخادم:
-%@
-
-
- Can't call member
- لا يمكن الاتصال بالعضو
-
-
- Chat already exists
- الدردشة موجودة بالفعل
-
-
- Check messages every 20 min.
- تحقق من الرسائل كل 20 دقيقة.
-
-
- Check messages when allowed.
- تحقق من الرسائل عندما يُسمح بذلك.
-
-
- Cannot forward message
- لا يمكن إعادة توجيه الرسالة
-
-
- Chat preferences were changed.
- تم تغيير تفضيلات المحادثة.
-
-
- Conditions are already accepted for these operator(s): **%@**.
- الشروط مقبولة بالفعل لهذا المشغل (المشغلين): **%@**.
-
-
- Conditions will be accepted for operator(s): **%@**.
- سيتم قبول شروط المشغل (المشغلين): **%@**.
-
-
- Conditions accepted on: %@.
- الشروط المقبولة على: %@.
-
-
- Conditions are accepted for the operator(s): **%@**.
- يتم قبول شروط المشغل (المشغلين): **%@**.
-
-
- Conditions will be automatically accepted for enabled operators on: %@.
- سيتم قبول الشروط تلقائيًا للمشغلين الممكّنين على: %@.
-
-
- Create new profile in [desktop app](https://simplex.chat/downloads/). 💻
- أنشئ ملفًا شخصيًا جديدًا في [تطبيق سطح المكتب](https://simplex.chat/downloads/). 💻
-
-
- Error adding server
- خطأ في إضافة الخادم
-
-
- Created at: %@
- تم الإنشاء في: %@
-
-
- Delete %lld messages of members?
- حذف %lld الرسائل القديمة للأعضاء؟
-
-
- Disappearing message
- رسالة اختفاء
-
-
- Enabled
- ممكّنة
-
-
- Encrypted message: database migration error
- رسالة مشفرة: خطأ في ترحيل قاعدة البيانات
-
-
- Delete list?
- Delete list?
-
-
- Delivered even when Apple drops them.
- يتم تسليمها حتى عندما تسقطها شركة Apple.
-
-
- Destination server address of %@ is incompatible with forwarding server %@ settings.
- عنوان خادم الوجهة %@ غير متوافق مع إعدادات خادم التوجيه %@.
-
-
- Destination server version of %@ is incompatible with forwarding server %@.
- إصدار خادم الوجهة لـ %@ غير متوافق مع خادم التوجيه %@.
-
-
- Don't create address
- لا تنشئ عنوان
-
-
- Done
- تم
-
-
- Duration
- المدة
-
-
- Encrypt local files
- تشفير الملفات المحلية
-
-
- Encryption renegotiation in progress.
- إعادة التفاوض على التشفير قيد التنفيذ.
-
-
- Enter Passcode
- أدخل رمز المرور
-
-
- Enter passphrase
- قم بأدخل عبارة المرور
-
-
- Enter welcome message…
- أدخل رسالة ترحيب…
-
-
- Enter your name…
- أدخل اسمك…
-
-
- Error changing to incognito!
- خطأ في التغيير إلى التصفح المتخفي!
-
-
- Delete %lld messages?
- حذف %lld رسائل؟
-
-
- Error aborting address change
- خطأ في إجهاض تغيير العنوان
-
-
- Disappears at
- يختفي عند
-
-
- Do not use credentials with proxy.
- لا تستخدم بيانات الاعتماد مع البروكسي.
-
-
- Error accepting conditions
- خطأ في قبول الشروط
-
-
- Enter password above to show!
- أدخل كلمة المرور أعلاه للعرض!
-
-
- Error changing connection profile
- خطأ في تغيير ملف تعريف الاتصال
-
-
- Desktop app version %@ is not compatible with this app.
- إصدار تطبيق سطح المكتب %@ غير متوافق مع هذا التطبيق.
-
-
- Encrypt stored files & media
- تشفير الملفات والوسائط المخزنة
-
-
- Enter this device name…
- أدخل اسم الجهاز…
-
-
- Enter welcome message… (optional)
- أدخل رسالة ترحيب... (اختياري)
-
-
- Correct name to %@?
- الاسم الصحيح ل %@؟
-
-
- Delete member message?
- حذف رسالة العضو؟
-
-
- Disable automatic message deletion?
- تعطيل حذف الرسائل التلقائي؟
-
-
- Disable delete messages
- تعطيل حذف الرسائل
-
-
- Disable for all
- تعطيل للجميع
-
-
- Disabled
- عاجز
-
-
- Documents:
- المستندات:
-
-
- By using SimpleX Chat you agree to:
-- send only legal content in public groups.
-- respect other users – no spam.
- باستخدامك SimpleX Chat، فإنك توافق على:
-- إرسال محتوى قانوني فقط في المجموعات العامة.
-- احترام المستخدمين الآخرين - ممنوع إرسال رسائل مزعجة.
-
-
- Configure server operators
- تكوين مشغلي الخادم
-
-
- Enable Flux in Network & servers settings for better metadata privacy.
- تمكين التدفق في إعدادات الشبكة والخوادم لتحسين خصوصية البيانات الوصفية.
-
-
- Discover and join groups
- اكتشف المجموعات وانضم إليها
-
-
- Discover via local network
- اكتشف عبر الشبكة المحلية
-
-
- Enabled for
- ممكّن ل
-
-
- Encrypted message: app is stopped
- رسالة مشفرة: تم إيقاف التطبيق
-
-
- Enter group name…
- أدخل اسم المجموعة…
-
-
- Do NOT use private routing.
- لا تستخدم التوجيه الخاص.
-
-
- Encryption re-negotiation error
- خطأ في إعادة تفاوض التشفير
-
-
- Connection with desktop stopped
- تم إيقاف الاتصال بسطح المكتب
-
-
- Destination server error: %@
- خطأ خادم الوجهة: %@
-
-
- Do NOT send messages directly, even if your or destination server does not support private routing.
- لا ترسل الرسائل بشكل مباشر، حتى لو كان خادمك أو خادم الوجهة لا يدعم التوجيه الخاص.
-
-
- Direct messages between members are prohibited in this chat.
- يُحظر إرسال الرسائل المباشرة بين الأعضاء في هذه الدردشة.
-
-
- Disconnect desktop?
- فصل سطح المكتب؟
-
-
- Disable (keep overrides)
- تعطيل (الاحتفاظ بالتجاوزات)
-
-
- Disappears at: %@
- يختفي عند: %@
-
-
- Do not send history to new members.
- لا ترسل التاريخ إلى الأعضاء الجدد.
-
-
- Encryption re-negotiation failed.
- فشل إعادة التفاوض على التشفير.
-