mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
ios: rework existing users notice, condition views (#5214)
This commit is contained in:
@@ -10,11 +10,13 @@ import Intents
|
||||
import SimpleXChat
|
||||
|
||||
private enum NoticesSheet: Identifiable {
|
||||
case notices(showWhatsNew: Bool, showOperatorsNotice: Bool)
|
||||
case whatsNew(updatedConditions: Bool)
|
||||
case updatedConditions
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .notices: return "notices"
|
||||
case .whatsNew: return "whatsNew"
|
||||
case .updatedConditions: return "updatedConditions"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,10 +276,12 @@ struct ContentView: View {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
if !noticesShown {
|
||||
let showWhatsNew = shouldShowWhatsNew()
|
||||
let showOperatorsNotice = chatModel.conditions.conditionsAction?.showNotice ?? false
|
||||
noticesShown = showWhatsNew || showOperatorsNotice
|
||||
if noticesShown {
|
||||
noticesSheetItem = .notices(showWhatsNew: showWhatsNew, showOperatorsNotice: showOperatorsNotice)
|
||||
let showUpdatedConditions = chatModel.conditions.conditionsAction?.showNotice ?? false
|
||||
noticesShown = showWhatsNew || showUpdatedConditions
|
||||
if showWhatsNew {
|
||||
noticesSheetItem = .whatsNew(updatedConditions: showUpdatedConditions)
|
||||
} else if showUpdatedConditions {
|
||||
noticesSheetItem = .updatedConditions
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,8 +292,14 @@ struct ContentView: View {
|
||||
.onChange(of: chatModel.appOpenUrl) { _ in connectViaUrl() }
|
||||
.sheet(item: $noticesSheetItem) { item in
|
||||
switch item {
|
||||
case let .notices(showWhatsNew, showOperatorsNotice):
|
||||
WhatsNewView(showWhatsNew: showWhatsNew, showOperatorsNotice: showOperatorsNotice)
|
||||
case let .whatsNew(updatedConditions):
|
||||
WhatsNewView(updatedConditions: updatedConditions)
|
||||
case .updatedConditions:
|
||||
UsageConditionsView(
|
||||
currUserServers: Binding.constant([]),
|
||||
userServers: Binding.constant([])
|
||||
)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
if chatModel.setDeliveryReceipts {
|
||||
|
||||
@@ -41,15 +41,27 @@ struct OnboardingButtonStyle: ButtonStyle {
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChooseServerOperatorsSheet: Identifiable {
|
||||
case showInfo
|
||||
case showConditions
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .showInfo: return "showInfo"
|
||||
case .showConditions: return "showConditions"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChooseServerOperators: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var onboarding: Bool
|
||||
@State private var showInfoSheet = false
|
||||
@State private var serverOperators: [ServerOperator] = []
|
||||
@State private var selectedOperatorIds = Set<Int64>()
|
||||
@State private var reviewConditionsNavLinkActive = false
|
||||
@State private var sheetItem: ChooseServerOperatorsSheet? = nil
|
||||
@State private var justOpened = true
|
||||
|
||||
var selectedOperators: [ServerOperator] { serverOperators.filter { selectedOperatorIds.contains($0.operatorId) } }
|
||||
@@ -74,25 +86,34 @@ struct ChooseServerOperators: View {
|
||||
.font(.footnote)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
// .padding(.horizontal, 32)
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
Spacer()
|
||||
|
||||
let reviewForOperators = selectedOperators.filter { !$0.conditionsAcceptance.conditionsAccepted }
|
||||
let canReviewLater = reviewForOperators.allSatisfy { $0.conditionsAcceptance.usageAllowed }
|
||||
let currEnabledOperatorIds = Set(serverOperators.filter { $0.enabled }.map { $0.operatorId })
|
||||
|
||||
VStack(spacing: 8) {
|
||||
if !reviewForOperators.isEmpty {
|
||||
reviewConditionsButton()
|
||||
} else if selectedOperatorIds != currEnabledOperatorIds && !selectedOperatorIds.isEmpty {
|
||||
setOperatorsButton()
|
||||
} else {
|
||||
continueButton()
|
||||
}
|
||||
if onboarding {
|
||||
Button("Conditions of use") {
|
||||
// TODO open accepted conditions
|
||||
Group {
|
||||
if reviewForOperators.isEmpty {
|
||||
Button("Conditions of use") {
|
||||
sheetItem = .showConditions
|
||||
}
|
||||
} else {
|
||||
Text("Conditions of use")
|
||||
.foregroundColor(.clear)
|
||||
}
|
||||
}
|
||||
.font(.callout)
|
||||
.foregroundColor(reviewForOperators.isEmpty ? .accentColor : .clear)
|
||||
.padding(.top)
|
||||
}
|
||||
}
|
||||
@@ -123,8 +144,17 @@ struct ChooseServerOperators: View {
|
||||
justOpened = false
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showInfoSheet) {
|
||||
ChooseServerOperatorsInfoView()
|
||||
.sheet(item: $sheetItem) { item in
|
||||
switch item {
|
||||
case .showInfo:
|
||||
ChooseServerOperatorsInfoView()
|
||||
case .showConditions:
|
||||
UsageConditionsView(
|
||||
currUserServers: Binding.constant([]),
|
||||
userServers: Binding.constant([])
|
||||
)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
@@ -140,7 +170,7 @@ struct ChooseServerOperators: View {
|
||||
.frame(width: 20, height: 20)
|
||||
.foregroundColor(theme.colors.primary)
|
||||
.onTapGesture {
|
||||
showInfoSheet = true
|
||||
sheetItem = .showInfo
|
||||
}
|
||||
|
||||
Text("Select network operators to use.")
|
||||
@@ -200,6 +230,28 @@ struct ChooseServerOperators: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func setOperatorsButton() -> some View {
|
||||
Button {
|
||||
Task {
|
||||
if let enabledOperators = enabledOperators(serverOperators) {
|
||||
let r = try await setServerOperators(operators: enabledOperators)
|
||||
await MainActor.run {
|
||||
ChatModel.shared.conditions = r
|
||||
continueToNextStep()
|
||||
}
|
||||
} else {
|
||||
await MainActor.run {
|
||||
continueToNextStep()
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("Update")
|
||||
}
|
||||
.buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty))
|
||||
.disabled(selectedOperatorIds.isEmpty)
|
||||
}
|
||||
|
||||
private func continueButton() -> some View {
|
||||
Button {
|
||||
continueToNextStep()
|
||||
|
||||
@@ -18,6 +18,7 @@ struct HowItWorks: View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("How SimpleX works")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.vertical)
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
|
||||
@@ -526,7 +526,7 @@ private let versionDescriptions: [VersionDescription] = [
|
||||
.view(FeatureView(
|
||||
icon: nil,
|
||||
title: "Network decentralization",
|
||||
view: newOperatorsView
|
||||
view: { NewOperatorsView() }
|
||||
)),
|
||||
.feature(Description(
|
||||
icon: "text.quote",
|
||||
@@ -549,20 +549,37 @@ func shouldShowWhatsNew() -> Bool {
|
||||
return v != lastVersion
|
||||
}
|
||||
|
||||
fileprivate func newOperatorsView() -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
Image((operatorsInfo[.flux] ?? ServerOperator.dummyOperatorInfo).largeLogo)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(height: 48)
|
||||
Text("The second preset operator in the app!")
|
||||
.multilineTextAlignment(.leading)
|
||||
.lineLimit(10)
|
||||
HStack {
|
||||
Button("Enable Flux") {
|
||||
|
||||
fileprivate struct NewOperatorsView: View {
|
||||
@State private var showOperatorsSheet = false
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Image((operatorsInfo[.flux] ?? ServerOperator.dummyOperatorInfo).largeLogo)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(height: 48)
|
||||
Text("The second preset operator in the app!")
|
||||
.multilineTextAlignment(.leading)
|
||||
.lineLimit(10)
|
||||
HStack {
|
||||
Button("Enable Flux") {
|
||||
showOperatorsSheet = true
|
||||
}
|
||||
Text("for better metadata privacy.")
|
||||
}
|
||||
Text("for better metadata privacy.")
|
||||
}
|
||||
.sheet(isPresented: $showOperatorsSheet) {
|
||||
ChooseServerOperators(onboarding: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum WhatsNewViewSheet: Identifiable {
|
||||
case showConditions
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .showConditions: return "showConditions"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,13 +590,13 @@ struct WhatsNewView: View {
|
||||
@State var currentVersion = versionDescriptions.count - 1
|
||||
@State var currentVersionNav = versionDescriptions.count - 1
|
||||
var viaSettings = false
|
||||
@State var showWhatsNew: Bool
|
||||
var showOperatorsNotice: Bool
|
||||
var updatedConditions: Bool
|
||||
@State private var sheetItem: WhatsNewViewSheet? = nil
|
||||
|
||||
var body: some View {
|
||||
viewBody()
|
||||
whatsNewView()
|
||||
.task {
|
||||
if showOperatorsNotice {
|
||||
if updatedConditions {
|
||||
do {
|
||||
let conditionsId = ChatModel.shared.conditions.currentConditions.conditionsId
|
||||
try await setConditionsNotified(conditionsId: conditionsId)
|
||||
@@ -588,14 +605,16 @@ struct WhatsNewView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func viewBody() -> some View {
|
||||
if showWhatsNew {
|
||||
whatsNewView()
|
||||
} else if showOperatorsNotice {
|
||||
ChooseServerOperators(onboarding: false)
|
||||
}
|
||||
.sheet(item: $sheetItem) { item in
|
||||
switch item {
|
||||
case .showConditions:
|
||||
UsageConditionsView(
|
||||
currUserServers: Binding.constant([]),
|
||||
userServers: Binding.constant([])
|
||||
)
|
||||
.modifier(ThemedBackground(grouped: true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func whatsNewView() -> some View {
|
||||
@@ -623,22 +642,19 @@ struct WhatsNewView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
if updatedConditions {
|
||||
Button("View updated conditions") {
|
||||
sheetItem = .showConditions
|
||||
}
|
||||
}
|
||||
if !viaSettings {
|
||||
Spacer()
|
||||
|
||||
if showOperatorsNotice {
|
||||
Button("View updated conditions") {
|
||||
showWhatsNew = false
|
||||
}
|
||||
.font(.title3)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
} else {
|
||||
Button("Ok") {
|
||||
dismiss()
|
||||
}
|
||||
.font(.title3)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
Button("Ok") {
|
||||
dismiss()
|
||||
}
|
||||
.font(.title3)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
@@ -729,6 +745,6 @@ struct WhatsNewView: View {
|
||||
|
||||
struct NewFeaturesView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WhatsNewView(showWhatsNew: true, showOperatorsNotice: false)
|
||||
WhatsNewView(updatedConditions: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ private enum NetworkAlert: Identifiable {
|
||||
}
|
||||
|
||||
private enum NetworkAndServersSheet: Identifiable {
|
||||
case showConditions(conditionsAction: UsageConditionsAction)
|
||||
case showConditions
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
@@ -65,7 +65,7 @@ struct NetworkAndServers: View {
|
||||
switch conditionsAction {
|
||||
case let .review(_, deadline, _):
|
||||
if let deadline = deadline, anyOperatorEnabled {
|
||||
Text("Conditions will be considered accepted on: \(conditionsTimestamp(deadline)).")
|
||||
Text("Conditions will be accepted on: \(conditionsTimestamp(deadline)).")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
}
|
||||
default:
|
||||
@@ -171,9 +171,8 @@ struct NetworkAndServers: View {
|
||||
}
|
||||
.sheet(item: $sheetItem) { item in
|
||||
switch item {
|
||||
case let .showConditions(conditionsAction):
|
||||
case .showConditions:
|
||||
UsageConditionsView(
|
||||
conditionsAction: conditionsAction,
|
||||
currUserServers: $currUserServers,
|
||||
userServers: $userServers
|
||||
)
|
||||
@@ -221,7 +220,7 @@ struct NetworkAndServers: View {
|
||||
|
||||
private func conditionsButton(_ conditionsAction: UsageConditionsAction) -> some View {
|
||||
Button {
|
||||
sheetItem = .showConditions(conditionsAction: conditionsAction)
|
||||
sheetItem = .showConditions
|
||||
} label: {
|
||||
switch conditionsAction {
|
||||
case .review:
|
||||
@@ -236,7 +235,6 @@ struct NetworkAndServers: View {
|
||||
struct UsageConditionsView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
var conditionsAction: UsageConditionsAction
|
||||
@Binding var currUserServers: [UserOperatorServers]
|
||||
@Binding var userServers: [UserOperatorServers]
|
||||
|
||||
@@ -248,14 +246,29 @@ struct UsageConditionsView: View {
|
||||
.padding(.top)
|
||||
.padding(.top)
|
||||
|
||||
switch conditionsAction {
|
||||
switch ChatModel.shared.conditions.conditionsAction {
|
||||
|
||||
case let .review(operators, _, _):
|
||||
case .none:
|
||||
ConditionsTextView()
|
||||
.padding(.bottom)
|
||||
.padding(.bottom)
|
||||
|
||||
case let .review(operators, deadline, _):
|
||||
Text("Conditions will be accepted for the operator(s): **\(operators.map { $0.legalName_ }.joined(separator: ", "))**.")
|
||||
ConditionsTextView()
|
||||
acceptConditionsButton(operators.map { $0.operatorId })
|
||||
.padding(.bottom)
|
||||
.padding(.bottom)
|
||||
VStack(spacing: 8) {
|
||||
acceptConditionsButton(operators.map { $0.operatorId })
|
||||
if let deadline = deadline {
|
||||
Text("Conditions will be automatically accepted for enabled operators on: \(conditionsTimestamp(deadline)).")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
.font(.footnote)
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
}
|
||||
.padding(.bottom)
|
||||
.padding(.bottom)
|
||||
|
||||
case let .accepted(operators):
|
||||
Text("Conditions are accepted for the operator(s): **\(operators.map { $0.legalName_ }.joined(separator: ", "))**.")
|
||||
|
||||
@@ -367,7 +367,7 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
NavigationLink {
|
||||
WhatsNewView(viaSettings: true, showWhatsNew: true, showOperatorsNotice: false)
|
||||
WhatsNewView(viaSettings: true, updatedConditions: false)
|
||||
.modifier(ThemedBackground())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
} label: {
|
||||
|
||||
Reference in New Issue
Block a user