mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-21 07:05:24 +00:00
wip
This commit is contained in:
@@ -14,10 +14,12 @@ import SimpleXChat
|
||||
struct OperatorView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@Environment(\.editMode) private var editMode
|
||||
let serverProtocol: ServerProtocol
|
||||
@Binding var serverOperator: ServerOperator
|
||||
@State var serverOperatorToEdit: ServerOperator
|
||||
@State var useOperator: Bool
|
||||
@State private var useOperatorToggleReset: Bool = false
|
||||
@State private var showConditionsSheet: Bool = false
|
||||
@State var currServers: [ServerCfg]
|
||||
@State var servers: [ServerCfg] = []
|
||||
@State private var selectedServer: String? = nil
|
||||
@@ -28,12 +30,24 @@ struct OperatorView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
Section(header: Text("Operator").foregroundColor(theme.colors.secondary)) {
|
||||
Text(serverOperator.name)
|
||||
Section {
|
||||
infoViewLink()
|
||||
useOperatorToggle()
|
||||
viewConditionsButton()
|
||||
} header: {
|
||||
Text("Operator")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
} footer: {
|
||||
switch (serverOperatorToEdit.latestConditionsAcceptance) {
|
||||
case let .accepted(date): Text("Accepted on: \(conditionsTimestamp(date)).")
|
||||
case let .reviewAvailable(deadline): Text("Review until: \(conditionsTimestamp(deadline)).")
|
||||
case .reviewRequired: EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
useOperatorSection()
|
||||
if serverOperatorToEdit.enabled {
|
||||
usageRolesSection()
|
||||
}
|
||||
|
||||
serversSection()
|
||||
|
||||
@@ -41,7 +55,7 @@ struct OperatorView: View {
|
||||
Button("Reset") { servers = currServers }
|
||||
.disabled(Set(servers) == Set(currServers))
|
||||
Button("Test servers") {}
|
||||
Button("Save servers") {}
|
||||
Button("Save") {}
|
||||
.disabled(true)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +64,6 @@ struct OperatorView: View {
|
||||
serverOperator = serverOperatorToEdit
|
||||
dismiss()
|
||||
})
|
||||
.toolbar { EditButton() }
|
||||
.onAppear {
|
||||
// this condition is needed to prevent re-setting the servers when exiting single server view
|
||||
if justOpened {
|
||||
@@ -58,6 +71,10 @@ struct OperatorView: View {
|
||||
justOpened = false
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showConditionsSheet) {
|
||||
UsageConditionsView(serverOperator: $serverOperatorToEdit, serverOperatorToEdit: serverOperatorToEdit)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
|
||||
private func infoViewLink() -> some View {
|
||||
@@ -67,44 +84,33 @@ struct OperatorView: View {
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
Text("Information")
|
||||
Text(serverOperator.name)
|
||||
}
|
||||
}
|
||||
|
||||
private func useOperatorSection() -> some View {
|
||||
Section(header: Text("Use operator").foregroundColor(theme.colors.secondary)) {
|
||||
conditionsViewLink()
|
||||
if let reviewDeadline = serverOperatorToEdit.latestConditionsAcceptance.reviewDeadline {
|
||||
infoRow("Review until", deadlineTimestamp(reviewDeadline))
|
||||
|
||||
private func useOperatorToggle() -> some View {
|
||||
Toggle("Use operator", isOn: $useOperator)
|
||||
.onChange(of: useOperator) { useOperatorToggle in
|
||||
if useOperatorToggleReset {
|
||||
useOperatorToggleReset = false
|
||||
} else if useOperatorToggle {
|
||||
if serverOperatorToEdit.latestConditionsAcceptance.usageAllowed {
|
||||
serverOperatorToEdit.enabled = true
|
||||
} else {
|
||||
showConditionsSheet = true
|
||||
}
|
||||
} else {
|
||||
serverOperatorToEdit.enabled = false
|
||||
}
|
||||
}
|
||||
Toggle("Use operator", isOn: $serverOperatorToEdit.enabled)
|
||||
.disabled(!serverOperatorToEdit.latestConditionsAcceptance.usageAllowed)
|
||||
.foregroundColor(!serverOperatorToEdit.latestConditionsAcceptance.usageAllowed ? theme.colors.secondary : theme.colors.onBackground)
|
||||
Group {
|
||||
Toggle("for storage", isOn: $serverOperatorToEdit.roles.storage)
|
||||
Toggle("as proxy", isOn: $serverOperatorToEdit.roles.proxy)
|
||||
}
|
||||
.padding(.leading, 24)
|
||||
.disabled(!serverOperatorToEdit.enabled)
|
||||
.foregroundColor(!serverOperatorToEdit.enabled ? theme.colors.secondary : theme.colors.onBackground)
|
||||
}
|
||||
}
|
||||
|
||||
private func deadlineTimestamp(_ date: Date) -> String {
|
||||
let localDateFormatter = DateFormatter()
|
||||
localDateFormatter.dateStyle = .medium
|
||||
localDateFormatter.timeStyle = .none
|
||||
return localDateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
@ViewBuilder private func conditionsViewLink() -> some View {
|
||||
NavigationLink() {
|
||||
UsageConditionsView(serverOperator: $serverOperatorToEdit, serverOperatorToEdit: serverOperatorToEdit)
|
||||
.navigationBarTitle("Conditions of use")
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
private func viewConditionsButton() -> some View {
|
||||
Button {
|
||||
showConditionsSheet = true
|
||||
} label: {
|
||||
if case .accepted = serverOperatorToEdit.latestConditionsAcceptance {
|
||||
if case .accepted = serverOperator.latestConditionsAcceptance {
|
||||
Text("Conditions accepted")
|
||||
} else {
|
||||
Text("Review conditions")
|
||||
@@ -112,17 +118,25 @@ struct OperatorView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func conditionsTimestamp(_ date: Date) -> String {
|
||||
let localDateFormatter = DateFormatter()
|
||||
localDateFormatter.dateStyle = .medium
|
||||
localDateFormatter.timeStyle = .none
|
||||
return localDateFormatter.string(from: date)
|
||||
}
|
||||
|
||||
private func usageRolesSection() -> some View {
|
||||
Section(header: Text("Use operator").foregroundColor(theme.colors.secondary)) {
|
||||
Toggle("For storage", isOn: $serverOperatorToEdit.roles.storage)
|
||||
Toggle("As proxy", isOn: $serverOperatorToEdit.roles.proxy)
|
||||
}
|
||||
}
|
||||
|
||||
private func serversSection() -> some View {
|
||||
Section {
|
||||
ForEach($servers) { srv in
|
||||
protocolServerView(srv)
|
||||
}
|
||||
.onMove { indexSet, offset in
|
||||
servers.move(fromOffsets: indexSet, toOffset: offset)
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
servers.remove(atOffsets: indexSet)
|
||||
}
|
||||
} header: {
|
||||
Text("\(serverOperator.name) \(proto) servers")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
@@ -141,7 +155,7 @@ struct OperatorView: View {
|
||||
server: server,
|
||||
serverToEdit: srv
|
||||
)
|
||||
.navigationBarTitle(srv.preset ? "Preset server" : "Your server")
|
||||
.navigationBarTitle("\(serverOperator.name) server")
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
@@ -226,27 +240,49 @@ struct UsageConditionsView: View {
|
||||
"""
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
Section {
|
||||
Text(conditionsText)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
Text("Use operator \(serverOperator.name)")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.horizontal)
|
||||
.padding(.top)
|
||||
.padding(.top)
|
||||
|
||||
Section {
|
||||
if case .accepted = serverOperator.latestConditionsAcceptance {
|
||||
Text("Conditions accepted")
|
||||
} else {
|
||||
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
|
||||
serverOperator = serverOperatorToEdit
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Accept conditions")
|
||||
}
|
||||
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)
|
||||
|
||||
if !serverOperator.latestConditionsAcceptance.conditionsAccepted {
|
||||
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)
|
||||
serverOperatorToEdit.latestConditionsAcceptance = .accepted(date: Date.now)
|
||||
serverOperator = serverOperatorToEdit
|
||||
dismiss()
|
||||
} label: {
|
||||
Text("Accept conditions")
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +293,7 @@ struct UsageConditionsView: View {
|
||||
serverProtocol: .smp,
|
||||
serverOperator: Binding.constant(ServerOperator.sampleData1),
|
||||
serverOperatorToEdit: ServerOperator.sampleData1,
|
||||
useOperator: ServerOperator.sampleData1.enabled,
|
||||
currServers: [ServerCfg.sampleData.preset]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -198,6 +198,7 @@ struct ProtocolServersView: View {
|
||||
serverProtocol: .smp,
|
||||
serverOperator: serverOperator,
|
||||
serverOperatorToEdit: srvOperator,
|
||||
useOperator: srvOperator.enabled,
|
||||
currServers: servers
|
||||
)
|
||||
.navigationBarTitle("\(srvOperator.name) servers")
|
||||
|
||||
@@ -1195,10 +1195,18 @@ public struct UserProtoServers: Decodable {
|
||||
}
|
||||
|
||||
public enum UsageConditionsAcceptance: Decodable, Hashable {
|
||||
case accepted
|
||||
case accepted(date: Date)
|
||||
case reviewAvailable(deadline: Date)
|
||||
case reviewRequired
|
||||
|
||||
public var conditionsAccepted: Bool {
|
||||
switch self {
|
||||
case .accepted: true
|
||||
case .reviewAvailable: false
|
||||
case .reviewRequired: false
|
||||
}
|
||||
}
|
||||
|
||||
public var usageAllowed: Bool {
|
||||
switch self {
|
||||
case .accepted: true
|
||||
@@ -1206,14 +1214,6 @@ public enum UsageConditionsAcceptance: Decodable, Hashable {
|
||||
case .reviewRequired: false
|
||||
}
|
||||
}
|
||||
|
||||
public var reviewDeadline: 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