ios: rework existing users notice, condition views (#5214)

This commit is contained in:
spaced4ndy
2024-11-19 20:48:51 +04:00
committed by GitHub
parent 181f72fa1f
commit 58c92ed004
6 changed files with 159 additions and 67 deletions
+18 -8
View File
@@ -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: {