mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-22 17:46:18 +00:00
conditions for all
This commit is contained in:
@@ -131,6 +131,11 @@ class ChatItemDummyModel: ObservableObject {
|
||||
func sendUpdate() { objectWillChange.send() }
|
||||
}
|
||||
|
||||
enum UsageConditionsAction {
|
||||
case reviewUpdatedConditions(acceptForOperators: [ServerOperator], deadline: Date?)
|
||||
case viewAcceptedConditions(acceptedForOperators: [ServerOperator])
|
||||
}
|
||||
|
||||
final class ChatModel: ObservableObject {
|
||||
@Published var onboardingStage: OnboardingStage?
|
||||
@Published var setDeliveryReceipts = false
|
||||
@@ -265,6 +270,19 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
var usageConditionsAction: UsageConditionsAction? {
|
||||
let usedOperators = serverOperators.filter { $0.enabled }
|
||||
if usedOperators.isEmpty {
|
||||
return nil
|
||||
} else if usedOperators.allSatisfy({ $0.conditionsAcceptance.conditionsAccepted }) {
|
||||
return .viewAcceptedConditions(acceptedForOperators: usedOperators)
|
||||
} else {
|
||||
let acceptForOperators = usedOperators.filter { !$0.conditionsAcceptance.conditionsAccepted }
|
||||
let deadline = usedOperators.compactMap { $0.conditionsAcceptance.reviewAvailableDeadline }.first
|
||||
return .reviewUpdatedConditions(acceptForOperators: acceptForOperators, deadline: deadline)
|
||||
}
|
||||
}
|
||||
|
||||
func hasChat(_ id: String) -> Bool {
|
||||
chats.first(where: { $0.id == id }) != nil
|
||||
}
|
||||
|
||||
@@ -19,23 +19,49 @@ private enum NetworkAlert: Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
private enum NetworkAndServersSheet: Identifiable {
|
||||
case showConditions(conditionsAction: UsageConditionsAction)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .showConditions: return "showConditions"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NetworkAndServers: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@State private var serverOperators: [ServerOperator] = []
|
||||
@State private var sheetItem: NetworkAndServersSheet? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
let conditionsAction = m.usageConditionsAction
|
||||
let smpServers = [ServerCfg.sampleData.preset, ServerCfg.sampleData.preset]
|
||||
let xftpServers = [ServerCfg.sampleData.xftpPreset, ServerCfg.sampleData.xftpPreset]
|
||||
Section {
|
||||
ForEach($serverOperators) { srvOperator in
|
||||
serverOperatorView(srvOperator, smpServers, xftpServers)
|
||||
}
|
||||
|
||||
if let conditionsAction = conditionsAction {
|
||||
conditionsButton(conditionsAction)
|
||||
}
|
||||
} header: {
|
||||
Text("Preset servers")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
} footer: {
|
||||
switch conditionsAction {
|
||||
case let .reviewUpdatedConditions(_, deadline):
|
||||
if let deadline = deadline {
|
||||
Text("Review conditions until: \(conditionsTimestamp(deadline)).")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
@@ -86,6 +112,13 @@ struct NetworkAndServers: View {
|
||||
.onAppear {
|
||||
serverOperators = ChatModel.shared.serverOperators
|
||||
}
|
||||
.sheet(item: $sheetItem, onDismiss: { serverOperators = ChatModel.shared.serverOperators }) { item in
|
||||
switch item {
|
||||
case let .showConditions(conditionsAction):
|
||||
UsageConditionsView(conditionsAction: conditionsAction)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func serverOperatorView(
|
||||
@@ -117,6 +150,19 @@ struct NetworkAndServers: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func conditionsButton(_ conditionsAction: UsageConditionsAction) -> some View {
|
||||
Button {
|
||||
sheetItem = .showConditions(conditionsAction: conditionsAction)
|
||||
} label: {
|
||||
switch conditionsAction {
|
||||
case .reviewUpdatedConditions:
|
||||
Text("Review conditions")
|
||||
case .viewAcceptedConditions:
|
||||
Text("Accepted conditions")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NetworkServersView_Previews: PreviewProvider {
|
||||
|
||||
@@ -79,7 +79,7 @@ struct OperatorView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showConditionsSheet, onDismiss: onUseToggleSheetDismissed) {
|
||||
UsageConditionsView(serverOperator: $serverOperatorToEdit, serverOperatorToEdit: serverOperatorToEdit)
|
||||
SingleOperatorUsageConditionsView(serverOperator: $serverOperatorToEdit, serverOperatorToEdit: serverOperatorToEdit)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
@@ -203,7 +203,7 @@ struct OperatorView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func conditionsTimestamp(_ date: Date) -> String {
|
||||
func conditionsTimestamp(_ date: Date) -> String {
|
||||
let localDateFormatter = DateFormatter()
|
||||
localDateFormatter.dateStyle = .medium
|
||||
localDateFormatter.timeStyle = .none
|
||||
@@ -228,25 +228,36 @@ struct OperatorInfoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct UsageConditionsView: View {
|
||||
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.
|
||||
|
||||
Laoreet auctor morbi a varius rutrum diam porta? In ad erat condimentum erat leo ornare. Eu venenatis inceptos rhoncus urna fringilla dis proin ante. Cras dignissim rutrum et faucibus feugiat neque curae tempus. Tellus ligula id dapibus, diam sollicitudin velit odio aliquam lectus. Maecenas ullamcorper arcu interdum cubilia donec iaculis. Maximus penatibus turpis a; vel fermentum ridiculus magna phasellus pellentesque. Eros tellus libero varius potenti; lobortis iaculis.
|
||||
|
||||
Mollis condimentum potenti velit at rutrum tellus maximus suscipit nec. Vehicula aenean dui netus enim aliquam. Aliquam libero rhoncus per pharetra accumsan eros. Urna non eu sem varius vivamus mus tellus aptent quam. Tristique mi natoque lectus volutpat facilisi commodo ac consequat. Proin parturient facilisi senectus egestas ultrices. Fringilla nisi urna convallis molestie lorem varius phasellus a ornare. Ullamcorper varius praesent facilisi habitasse massa.
|
||||
|
||||
Potenti dolor ridiculus est faucibus leo. Euismod consequat ultricies fringilla sociosqu duis sollicitudin. Eget convallis lacinia lacus justo per habitasse parturient. Donec nunc himenaeos pretium donec cursus pharetra ac phasellus? Fringilla sodales egestas orci ligula per ligula semper pellentesque. Potenti non dignissim tempor; orci rutrum elit.
|
||||
|
||||
Habitasse eu sapien eleifend gravida tortor potenti senectus euismod. Lectus enim fames turpis lectus facilisi efficitur elit porttitor facilisi. Nisl quam senectus quam augue integer leo. In aliquam tempor nibh proin felis tortor elementum sodales lacinia. Ut per placerat bibendum magna dapibus fermentum bibendum amet congue. Curae bibendum enim platea per faucibus imperdiet morbi hac varius. Conubia feugiat justo hac faucibus dis.
|
||||
"""
|
||||
|
||||
func conditionsTextView() -> some View {
|
||||
ScrollView {
|
||||
Text(conditionsText)
|
||||
.padding()
|
||||
}
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||
.fill(Color(uiColor: .secondarySystemGroupedBackground))
|
||||
)
|
||||
}
|
||||
|
||||
struct SingleOperatorUsageConditionsView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@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.
|
||||
|
||||
Laoreet auctor morbi a varius rutrum diam porta? In ad erat condimentum erat leo ornare. Eu venenatis inceptos rhoncus urna fringilla dis proin ante. Cras dignissim rutrum et faucibus feugiat neque curae tempus. Tellus ligula id dapibus, diam sollicitudin velit odio aliquam lectus. Maecenas ullamcorper arcu interdum cubilia donec iaculis. Maximus penatibus turpis a; vel fermentum ridiculus magna phasellus pellentesque. Eros tellus libero varius potenti; lobortis iaculis.
|
||||
|
||||
Mollis condimentum potenti velit at rutrum tellus maximus suscipit nec. Vehicula aenean dui netus enim aliquam. Aliquam libero rhoncus per pharetra accumsan eros. Urna non eu sem varius vivamus mus tellus aptent quam. Tristique mi natoque lectus volutpat facilisi commodo ac consequat. Proin parturient facilisi senectus egestas ultrices. Fringilla nisi urna convallis molestie lorem varius phasellus a ornare. Ullamcorper varius praesent facilisi habitasse massa.
|
||||
|
||||
Potenti dolor ridiculus est faucibus leo. Euismod consequat ultricies fringilla sociosqu duis sollicitudin. Eget convallis lacinia lacus justo per habitasse parturient. Donec nunc himenaeos pretium donec cursus pharetra ac phasellus? Fringilla sodales egestas orci ligula per ligula semper pellentesque. Potenti non dignissim tempor; orci rutrum elit.
|
||||
|
||||
Habitasse eu sapien eleifend gravida tortor potenti senectus euismod. Lectus enim fames turpis lectus facilisi efficitur elit porttitor facilisi. Nisl quam senectus quam augue integer leo. In aliquam tempor nibh proin felis tortor elementum sodales lacinia. Ut per placerat bibendum magna dapibus fermentum bibendum amet congue. Curae bibendum enim platea per faucibus imperdiet morbi hac varius. Conubia feugiat justo hac faucibus dis.
|
||||
"""
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Group {
|
||||
@@ -307,17 +318,6 @@ struct UsageConditionsView: View {
|
||||
.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 {
|
||||
@@ -347,6 +347,64 @@ struct UsageConditionsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct UsageConditionsView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var conditionsAction: UsageConditionsAction
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Group {
|
||||
Text("Conditions of use")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.top)
|
||||
.padding(.top)
|
||||
|
||||
switch conditionsAction {
|
||||
case let .reviewUpdatedConditions(acceptForOperators, _):
|
||||
|
||||
Text("Conditions will be accepted for following operator(s): \(acceptForOperators.map { $0.name }.joined(separator: ", ")).")
|
||||
|
||||
conditionsTextView()
|
||||
|
||||
acceptConditionsButton()
|
||||
.padding(.bottom)
|
||||
|
||||
case let .viewAcceptedConditions(acceptedForOperators):
|
||||
|
||||
Text("Conditions are accepted for following operator(s): \(acceptedForOperators.map { $0.name }.joined(separator: ", ")).")
|
||||
|
||||
conditionsTextView()
|
||||
.padding(.bottom)
|
||||
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
|
||||
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)
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Accept conditions")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
OperatorView(
|
||||
serverOperator: Binding.constant(ServerOperator.sampleData1),
|
||||
|
||||
@@ -1236,6 +1236,14 @@ public enum UsageConditionsAcceptance: Decodable, Hashable {
|
||||
case .reviewRequired: false
|
||||
}
|
||||
}
|
||||
|
||||
public var reviewAvailableDeadline: Date? {
|
||||
switch self {
|
||||
case .accepted: nil
|
||||
case let .reviewAvailable(deadline): deadline
|
||||
case .reviewRequired: nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ServerOperator: Identifiable, Decodable {
|
||||
|
||||
Reference in New Issue
Block a user