From 5b21db31e65ebecc59078b2d220421389b996a54 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:53:15 +0400 Subject: [PATCH] global conditions hack --- apps/ios/Shared/Model/ChatModel.swift | 26 ++++ .../NetworkAndServers/OperatorView.swift | 145 ++++++++++++------ .../ProtocolServersView.swift | 2 +- apps/ios/SimpleXChat/APITypes.swift | 16 +- 4 files changed, 133 insertions(+), 56 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index e6970d4728..543ad600ba 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -193,6 +193,8 @@ final class ChatModel: ObservableObject { @Published var draft: ComposeState? @Published var draftChatId: String? @Published var networkInfo = UserNetworkInfo(networkType: .other, online: true) + // server operators + @Published var serverOperators: [ServerOperator] = [ServerOperator.sampleData1, ServerOperator.sampleData2, ServerOperator.sampleData3] var messageDelivery: Dictionary Void> = [:] @@ -239,6 +241,30 @@ final class ChatModel: ObservableObject { } } + var operatorsWithConditionsAccepted: [ServerOperator] { + serverOperators.filter { $0.conditionsAcceptance.conditionsAccepted } + } + + var enabledOperatorsWithConditionsNotAccepted: [ServerOperator] { + serverOperators.filter { $0.enabled && !$0.conditionsAcceptance.conditionsAccepted } + } + + func acceptConditionsForEnabledOperators(_ date: Date) { + for (i, serverOperator) in serverOperators.enumerated() { + if serverOperator.enabled && !serverOperator.conditionsAcceptance.conditionsAccepted { + var updatedOperator = serverOperator + updatedOperator.conditionsAcceptance = .accepted(date: date) + serverOperators[i] = updatedOperator + } + } + } + + func updateServerOperator(_ updatedOperator: ServerOperator) { + if let i = serverOperators.firstIndex(where: { $0.operatorId == updatedOperator.operatorId }) { + serverOperators[i] = updatedOperator + } + } + func hasChat(_ id: String) -> Bool { chats.first(where: { $0.id == id }) != nil } diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift index 3c901a2e9f..73b46480bc 100644 --- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift @@ -33,14 +33,14 @@ struct OperatorView: View { Section { infoViewLink() useOperatorToggle() - if serverOperatorToEdit.enabled || serverOperatorToEdit.latestConditionsAcceptance.conditionsAccepted { + if serverOperatorToEdit.enabled || serverOperatorToEdit.conditionsAcceptance.conditionsAccepted { viewConditionsButton() } } header: { Text("Operator") .foregroundColor(theme.colors.secondary) } footer: { - switch (serverOperatorToEdit.latestConditionsAcceptance) { + switch (serverOperatorToEdit.conditionsAcceptance) { case let .accepted(date): Text("Conditions accepted on: \(conditionsTimestamp(date)).") case let .reviewAvailable(deadline): @@ -70,6 +70,7 @@ struct OperatorView: View { } .modifier(BackButton(disabled: Binding.constant(false)) { serverOperator = serverOperatorToEdit + ChatModel.shared.updateServerOperator(serverOperatorToEdit) dismiss() }) .onAppear { @@ -108,24 +109,35 @@ struct OperatorView: View { if useOperatorToggleReset { useOperatorToggleReset = false } else if useOperatorToggle { - if serverOperatorToEdit.latestConditionsAcceptance.usageAllowed { + switch serverOperatorToEdit.conditionsAcceptance { + case .accepted: serverOperatorToEdit.enabled = true - } else { + ChatModel.shared.updateServerOperator(serverOperatorToEdit) + case .reviewAvailable: + if !ChatModel.shared.operatorsWithConditionsAccepted.isEmpty { + showConditionsSheet = true + } else { + serverOperatorToEdit.enabled = true + ChatModel.shared.updateServerOperator(serverOperatorToEdit) + } + case .reviewRequired: showConditionsSheet = true } } else { serverOperatorToEdit.enabled = false + ChatModel.shared.updateServerOperator(serverOperatorToEdit) } } } private func onUseToggleSheetDismissed() { - if useOperator { - if case .reviewRequired = serverOperatorToEdit.latestConditionsAcceptance { + if useOperator && !serverOperatorToEdit.enabled { + if case .reviewRequired = serverOperatorToEdit.conditionsAcceptance { useOperatorToggleReset = true useOperator = false - } else if serverOperatorToEdit.latestConditionsAcceptance.conditionsAccepted { + } else if serverOperatorToEdit.conditionsAcceptance.conditionsAccepted { serverOperatorToEdit.enabled = true + ChatModel.shared.updateServerOperator(serverOperatorToEdit) } } } @@ -134,7 +146,7 @@ struct OperatorView: View { Button { showConditionsSheet = true } label: { - if case .accepted = serverOperatorToEdit.latestConditionsAcceptance { + if case .accepted = serverOperatorToEdit.conditionsAcceptance { Text("Conditions accepted") } else { Text("Review conditions") @@ -250,6 +262,7 @@ struct UsageConditionsView: View { @EnvironmentObject var theme: AppTheme @Binding var serverOperator: ServerOperator @State var serverOperatorToEdit: ServerOperator + @State private var conditionsExpanded: Bool = false let conditionsText = """ Lorem ipsum odor amet, consectetuer adipiscing elit. Blandit mauris massa tempor ac; maximus accumsan magnis. Sollicitudin maximus tempor luctus sociosqu turpis dictum per imperdiet porttitor. Efficitur mattis fusce curae id efficitur. Non bibendum elementum faucibus vehicula morbi pulvinar. Accumsan habitant tincidunt sollicitudin taciti ad urna potenti velit. Primis laoreet pharetra magnis est dolor proin viverra. @@ -265,54 +278,100 @@ struct UsageConditionsView: View { var body: some View { VStack(alignment: .leading, spacing: 20) { - Text("Use operator \(serverOperator.name)") - .font(.largeTitle) - .bold() - .padding(.horizontal) - .padding(.top) - .padding(.top) - - if !serverOperator.latestConditionsAcceptance.conditionsAccepted { - Text("In order to use operator \(serverOperator.name), accept conditions of use.") - .foregroundColor(theme.colors.secondary) - .padding(.horizontal) - } - - ScrollView { - Text(conditionsText) - .padding() - } - .background( - RoundedRectangle(cornerRadius: 12, style: .continuous) - .fill(Color(uiColor: .secondarySystemGroupedBackground)) - ) - .padding(.horizontal) - Group { - if case let .accepted(date) = serverOperator.latestConditionsAcceptance { + Text("Use operator \(serverOperator.name)") + .font(.largeTitle) + .bold() + .padding(.top) + .padding(.top) + + let operatorsWithConditionsAccepted = ChatModel.shared.operatorsWithConditionsAccepted + + if case let .accepted(date) = serverOperator.conditionsAcceptance { + + conditionsTextView() + Text("Conditions accepted on: \(conditionsTimestamp(date)).") .foregroundColor(theme.colors.secondary) - } else { - HStack { - Spacer() + .padding(.bottom) + + } else if !operatorsWithConditionsAccepted.isEmpty { + + Text("You already accepted conditions of use for following operator(s): \(operatorsWithConditionsAccepted.map { $0.name }.joined(separator: ", ")).") + Text("Same conditions will apply to operator \(serverOperator.name).") + + conditionsAppliedToOtherOperatorsText() + + if !conditionsExpanded { Button { - // Should call api to save state here, not when saving all servers - // (It's counterintuitive to lose to closed sheet or Reset) - serverOperatorToEdit.latestConditionsAcceptance = .accepted(date: Date.now) - serverOperator = serverOperatorToEdit - dismiss() + conditionsExpanded = true } label: { - Text("Accept conditions") + Text("View conditions") } - .buttonStyle(.borderedProminent) Spacer() + } else { + conditionsTextView() } + + acceptConditionsButton() + .padding(.bottom) + + } else { + + Text("In order to use operator \(serverOperator.name), accept conditions of use.") + + conditionsAppliedToOtherOperatorsText() + + conditionsTextView() + + acceptConditionsButton() + .padding(.bottom) + } } .padding(.horizontal) - .padding(.bottom) + } + .frame(maxHeight: .infinity) + } + + private func conditionsTextView() -> some View { + ScrollView { + Text(conditionsText) + .padding() + } + .background( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(Color(uiColor: .secondarySystemGroupedBackground)) + ) + } + + @ViewBuilder private func conditionsAppliedToOtherOperatorsText() -> some View { + let otherEnabledOperators = ChatModel.shared.enabledOperatorsWithConditionsNotAccepted.filter { $0.operatorId != serverOperator.operatorId } + if !otherEnabledOperators.isEmpty { + Text("Conditions will also apply for following operator(s) you use: \(otherEnabledOperators.map { $0.name }.joined(separator: ", ")).") + } + } + + private func acceptConditionsButton() -> some View { + HStack { + Spacer() + + Button { + // Should call api to save state here, not when saving all servers + // (It's counterintuitive to lose to closed sheet or Reset) + let date = Date.now + ChatModel.shared.acceptConditionsForEnabledOperators(date) + serverOperatorToEdit.conditionsAcceptance = .accepted(date: date) + serverOperator = serverOperatorToEdit + dismiss() + } label: { + Text("Accept conditions") + } + .buttonStyle(.borderedProminent) + + Spacer() } } } diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift index 94a724a220..3f9ab252b7 100644 --- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift @@ -158,10 +158,10 @@ struct ProtocolServersView: View { } } .onAppear { + serverOperators = ChatModel.shared.serverOperators // this condition is needed to prevent re-setting the servers when exiting single server view if justOpened { do { - serverOperators = [ServerOperator.sampleData1, ServerOperator.sampleData2, ServerOperator.sampleData3] let r = try getUserProtoServers(serverProtocol) currServers = r.protoServers presetServers = r.presetServers diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 56aa85d5be..16be45f4cb 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -1236,21 +1236,13 @@ public enum UsageConditionsAcceptance: Decodable, Hashable { case .reviewRequired: false } } - - public var usageAllowed: Bool { - switch self { - case .accepted: true - case .reviewAvailable: true - case .reviewRequired: false - } - } } public struct ServerOperator: Identifiable, Decodable { public var operatorId: Int64 public var name: String public var tag: OperatorTag - public var latestConditionsAcceptance: UsageConditionsAcceptance + public var conditionsAcceptance: UsageConditionsAcceptance public var enabled: Bool public var roles: ServerRoles @@ -1264,7 +1256,7 @@ public struct ServerOperator: Identifiable, Decodable { operatorId: 1, name: "SimpleX Chat", tag: .simplex, - latestConditionsAcceptance: .reviewAvailable(deadline: Date.distantFuture), + conditionsAcceptance: .reviewAvailable(deadline: Date.distantFuture), enabled: true, roles: ServerRoles(storage: true, proxy: true) ) @@ -1273,7 +1265,7 @@ public struct ServerOperator: Identifiable, Decodable { operatorId: 2, name: "XYZ", tag: .xyz, - latestConditionsAcceptance: .reviewRequired, + conditionsAcceptance: .reviewRequired, enabled: false, roles: ServerRoles(storage: true, proxy: true) ) @@ -1282,7 +1274,7 @@ public struct ServerOperator: Identifiable, Decodable { operatorId: 3, name: "Demo", tag: .demo, - latestConditionsAcceptance: .reviewRequired, + conditionsAcceptance: .reviewRequired, enabled: false, roles: ServerRoles(storage: true, proxy: true) )