another.im-ios/Monal/Classes/BoardingCards.swift
2024-11-18 15:53:52 +01:00

255 lines
11 KiB
Swift

//
// BoardingCards.swift
// Monal
//
// Created by Vaidik Dubey on 05/06/24.
// Copyright © 2024 monal-im.org. All rights reserved.
//
import FrameUp
class OnboardingState: ObservableObject {
@defaultsDB("hasCompletedOnboarding")
var hasCompletedOnboarding: Bool
}
struct OnboardingCard: Identifiable {
let id = UUID()
let title: Text?
let description: Text?
let imageName: String?
let articleText: Text?
let customView: AnyView?
let nextText: String?
}
struct OnboardingView: View {
var delegate: SheetDismisserProtocol
let cards: [OnboardingCard]
@ObservedObject var onboardingState = OnboardingState()
@State private var currentIndex = 0
var body: some View {
ZStack {
/// Ensure the ZStack takes the entire area
Color.clear
ForEach(Array(zip(cards, cards.indices)), id: \.1) { card, index in
/// Only show card that's visible
if index == currentIndex {
GeometryReader { proxy in
SmartScrollView(.vertical, showsIndicators: true, optionalScrolling: true, shrinkToFit: false) {
VStack(alignment: .leading, spacing: 16) {
if currentIndex > 0 {
Button {
currentIndex -= 1
} label: {
Label("Back", systemImage: "chevron.left")
.labelStyle(.iconOnly)
.padding(10)
}
} else {
//make sure the space the "back" label will take, is already reserved to not have "jumps" when pressing next
Text("").padding(10)
}
HStack {
if let imageName = card.imageName {
Image(systemName: imageName)
.font(.custom("MarkerFelt-Wide", size: 80))
.foregroundColor(.accentColor)
.accessibilityHidden(true)
}
card.title?
.font(.title)
.fontWeight(.bold)
.foregroundColor(.primary)
.padding(.bottom, 4)
/// This ensures text doesn't get truncated which sometimes happens in ScrollView
.fixedSize(horizontal: false, vertical: true)
}
.accessibilityElement(children: .combine)
.accessibilityAddTraits(.isHeader)
if let description = card.description {
description
.font(.custom("HelveticaNeue-Medium", size: 20))
.foregroundColor(.primary)
.multilineTextAlignment(.leading)
/// This ensures text doesn't get truncated which sometimes happens in ScrollView
.fixedSize(horizontal: false, vertical: true)
}
if card.imageName != nil || card.description != nil || card.imageName != nil {
Spacer().frame(height: 1)
Divider()
Spacer().frame(height: 1)
}
card.articleText?
.font(.custom("HelveticaNeue-Medium", size: 20))
.foregroundColor(.primary)
.multilineTextAlignment(.leading)
card.customView
Spacer()
Group {
if index < cards.count - 1 {
Button {
currentIndex += 1
} label: {
HStack {
Text(card.nextText ?? NSLocalizedString("Next", comment:"onboarding"))
.fontWeight(.bold)
Image(systemName: "chevron.right")
}
}
} else {
Button {
onboardingState.hasCompletedOnboarding = true
delegate.dismissWithoutAnimation()
} label: {
Text(card.nextText ?? NSLocalizedString("Close", comment:"onboarding"))
}
.buttonStyle(MonalProminentButtonStyle())
}
}
.frame(maxWidth: .infinity, alignment: .trailing)
}
.padding(.bottom, 16)
.padding()
/// Sets the minimum frame height to the available height of the scrollview and the maxHeight to infinity
.frame(minHeight: proxy.size.height, maxHeight: .infinity)
}
}
.accessibilityAddTraits(.isModal)
}
}
}
.onAppear {
if UIDevice.current.userInterfaceIdiom != .pad {
//force portrait mode and lock ui there
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
(UIApplication.shared.delegate as! MonalAppDelegate).orientationLock = .portrait
}
}
}
}
@ViewBuilder
func createOnboardingView(delegate: SheetDismisserProtocol) -> some View {
#if IS_QUICKSY
let cards = [
OnboardingCard(
title: Text("Welcome to Quicksy !"),
description: nil,
imageName: "hand.wave",
articleText: Text("""
Quicksy syncs your contact list in regular intervals to make suggestions about possible contacts who are already on Quicksy.
Quicksy shares and stores images, audio recordings, videos and other media to deliver them to the intended recipients. Files will be stored for up to 30 days.
Find more Information in our [Privacy Policy](https://quicksy.im/privacy.htm).
"""),
customView: nil,
nextText: "Accept and continue"
),
]
#else
let cards = [
OnboardingCard(
title: Text("Welcome to Monal !"),
description: Text("Become part of a worldwide decentralized chat network!"),
imageName: "hand.wave",
articleText: Text("""
Modern iOS and macOS XMPP chat client.\n\nXMPP is a federated network: Just like email, you can register your account on many servers and still talk to anyone, even if they signed up on a different server.\n\nUsing Monal instead of a centralized chat app therefore increases your digital sovereignty.
"""),
customView: nil,
nextText: nil
),
OnboardingCard(
title: Text("Features"),
description: nil,
imageName: "sparkles",
articleText: Text("""
🛜 Decentralized Network :
Leverages the decentralized nature of XMPP, avoiding central servers and increasing your digital sovereignty.
🌐 Data privacy :
We do not sell or track information for external parties (nor for anyone else).
🔐 End-to-end encryption :
Secure multi-end messaging using the OMEMO protocol.
👨‍💻 Open Source :
The app's source code is publicly available for audit and contribution.
"""),
customView: nil,
nextText: nil
),
OnboardingCard(
title: Text("Settings"),
description: Text("These are important privacy settings you may want to review!"),
imageName: "gear",
articleText: nil,
customView: AnyView(PrivacySettingsSubview(onboardingPart:0)),
nextText: nil
),
OnboardingCard(
title: Text("Settings"),
description: Text("These are important privacy settings you may want to review!"),
imageName: "gear",
articleText: nil,
customView: AnyView(PrivacySettingsSubview(onboardingPart:1)),
nextText: nil
),
OnboardingCard(
title: Text("Even more to customize!"),
description: Text("You can customize even more, just use the button below to open the settings."),
imageName: "hand.wave",
articleText: nil,
customView: AnyView(TakeMeToSettingsView(delegate:delegate)),
nextText: nil
),
]
#endif
OnboardingView(delegate: delegate, cards: cards)
}
struct TakeMeToSettingsView: View {
@ObservedObject var onboardingState = OnboardingState()
var delegate: SheetDismisserProtocol
var body: some View {
HStack {
Spacer()
Button(action: {
let appDelegate = UIApplication.shared.delegate as! MonalAppDelegate
if let activeChats = appDelegate.activeChats {
activeChats.prependGeneralSettings()
}
onboardingState.hasCompletedOnboarding = true
delegate.dismissWithoutAnimation()
}) {
Text("Take me to settings")
}
.buttonStyle(MonalProminentButtonStyle())
Spacer()
}
}
}
struct OnboardingView_Previews: PreviewProvider {
static var delegate = SheetDismisserProtocol()
static var previews: some View {
createOnboardingView(delegate: delegate)
.environmentObject(OnboardingState())
}
}