move ProfileContainerViewModel into Binders package

This commit is contained in:
Satindar Dhillon
2022-02-23 16:13:29 -08:00
parent b6e73d3404
commit 48de634e98
7 changed files with 155 additions and 136 deletions

View File

@ -1,7 +1,11 @@
import Combine
import Models
import Services
import SwiftUI
import Utils
import Views
// TODO: remove this view model
extension ProfileContainerViewModel {
static func make(services: Services) -> ProfileContainerViewModel {
let viewModel = ProfileContainerViewModel()
@ -39,3 +43,114 @@ extension ProfileContainerViewModel {
.store(in: &subscriptions)
}
}
final class ProfileContainerViewModel: ObservableObject {
@Published var isLoading = false
@Published var profileCardData = ProfileCardData()
enum Action {
case logout
case loadProfileData
case showIntercomMessenger
case deleteAccount
}
var subscriptions = Set<AnyCancellable>()
let performActionSubject = PassthroughSubject<Action, Never>()
init() {}
}
struct ProfileContainerView: View {
@ObservedObject private var viewModel: ProfileContainerViewModel
@State private var showLogoutConfirmation = false
init(viewModel: ProfileContainerViewModel) {
self.viewModel = viewModel
}
var body: some View {
#if os(iOS)
Form {
innerBody
}
#elseif os(macOS)
List {
innerBody
}
.listStyle(InsetListStyle())
#endif
}
private var innerBody: some View {
Group {
Section {
ProfileCard(data: viewModel.profileCardData)
.onAppear {
viewModel.performActionSubject.send(.loadProfileData)
}
}
Section {
NavigationLink(destination: BasicWebAppView.privacyPolicyWebView) {
Text("Privacy Policy")
}
NavigationLink(destination: BasicWebAppView.termsConditionsWebView) {
Text("Terms and Conditions")
}
#if os(iOS)
Button(
action: {
viewModel.performActionSubject.send(.showIntercomMessenger)
},
label: { Text("Feedback") }
)
#endif
}
Section {
if FeatureFlag.showAccountDeletion {
NavigationLink(
destination: ManageAccountView(handleAccountDeletion: {
viewModel.performActionSubject.send(.deleteAccount)
})
) {
Text("Manage Account")
}
}
Text("Logout")
.onTapGesture {
showLogoutConfirmation = true
}
.alert(isPresented: $showLogoutConfirmation) {
Alert(
title: Text("Are you sure you want to logout?"),
primaryButton: .destructive(Text("Confirm")) {
viewModel.performActionSubject.send(.logout)
},
secondaryButton: .cancel()
)
}
}
}
.navigationTitle("Profile")
}
}
private extension BasicWebAppView {
static let privacyPolicyWebView: BasicWebAppView = {
omnivoreWebView(path: "privacy")
}()
static let termsConditionsWebView: BasicWebAppView = {
omnivoreWebView(path: "terms")
}()
private static func omnivoreWebView(path: String) -> BasicWebAppView {
let urlString = "https://omnivore.app/\(path)?isAppEmbedView=true"
return BasicWebAppView(request: URLRequest(url: URL(string: urlString)!))
}
}

View File

@ -12,9 +12,11 @@ extension HomeFeedViewModel {
LinkItemDetailViewModel.make(feedItem: feedItem, services: services)
}
if UIDevice.isIPhone {
viewModel.profileContainerViewModel = ProfileContainerViewModel.make(services: services)
}
#if os(iOS)
if UIDevice.isIPhone {
viewModel.profileContainerViewModel = ProfileContainerViewModel.make(services: services)
}
#endif
viewModel.bind(services: services)
viewModel.loadItems(dataService: services.dataService, searchQuery: nil, isRefresh: false)

View File

@ -12,10 +12,14 @@ public struct ProfileCardData {
}
}
struct ProfileCard: View {
public struct ProfileCard: View {
let data: ProfileCardData
var body: some View {
public init(data: ProfileCardData) {
self.data = data
}
public var body: some View {
HStack(alignment: .center) {
Group {
if let url = data.imageURL {

View File

@ -1,115 +1 @@
import Combine
import Models
import SwiftUI
import Utils
public final class ProfileContainerViewModel: ObservableObject {
@Published public var isLoading = false
@Published public var profileCardData = ProfileCardData()
public enum Action {
case logout
case loadProfileData
case showIntercomMessenger
case deleteAccount
}
public var subscriptions = Set<AnyCancellable>()
public let performActionSubject = PassthroughSubject<Action, Never>()
public init() {}
}
public struct ProfileContainerView: View {
@ObservedObject private var viewModel: ProfileContainerViewModel
@State private var showLogoutConfirmation = false
public init(viewModel: ProfileContainerViewModel) {
self.viewModel = viewModel
}
public var body: some View {
#if os(iOS)
Form {
innerBody
}
#elseif os(macOS)
List {
innerBody
}
.listStyle(InsetListStyle())
#endif
}
private var innerBody: some View {
Group {
Section {
ProfileCard(data: viewModel.profileCardData)
.onAppear {
viewModel.performActionSubject.send(.loadProfileData)
}
}
Section {
NavigationLink(destination: BasicWebAppView.privacyPolicyWebView) {
Text("Privacy Policy")
}
NavigationLink(destination: BasicWebAppView.termsConditionsWebView) {
Text("Terms and Conditions")
}
#if os(iOS)
Button(
action: {
viewModel.performActionSubject.send(.showIntercomMessenger)
},
label: { Text("Feedback") }
)
#endif
}
Section {
if FeatureFlag.showAccountDeletion {
NavigationLink(
destination: ManageAccountView(handleAccountDeletion: {
viewModel.performActionSubject.send(.deleteAccount)
})
) {
Text("Manage Account")
}
}
Text("Logout")
.onTapGesture {
showLogoutConfirmation = true
}
.alert(isPresented: $showLogoutConfirmation) {
Alert(
title: Text("Are you sure you want to logout?"),
primaryButton: .destructive(Text("Confirm")) {
viewModel.performActionSubject.send(.logout)
},
secondaryButton: .cancel()
)
}
}
}
.navigationTitle("Profile")
}
}
private extension BasicWebAppView {
static let privacyPolicyWebView: BasicWebAppView = {
omnivoreWebView(path: "privacy")
}()
static let termsConditionsWebView: BasicWebAppView = {
omnivoreWebView(path: "terms")
}()
private static func omnivoreWebView(path: String) -> BasicWebAppView {
let urlString = "https://omnivore.app/\(path)?isAppEmbedView=true"
return BasicWebAppView(request: URLRequest(url: URL(string: urlString)!))
}
}

View File

@ -1,10 +1,14 @@
import SwiftUI
struct ManageAccountView: View {
public struct ManageAccountView: View {
let handleAccountDeletion: () -> Void
@State private var showDeleteAccountConfirmation = false
var body: some View {
public init(handleAccountDeletion: @escaping () -> Void) {
self.handleAccountDeletion = handleAccountDeletion
}
public var body: some View {
Button(
action: {
showDeleteAccountConfirmation = true

View File

@ -8,20 +8,20 @@ import SwiftUI
// https://stackoverflow.com/questions/63526478/swiftui-userinterfacesizeclass-for-universal-macos-ios-views
#if os(macOS)
enum UserInterfaceSizeClass {
public enum UserInterfaceSizeClass {
case compact
case regular
}
struct HorizontalSizeClassEnvironmentKey: EnvironmentKey {
static let defaultValue: UserInterfaceSizeClass = .regular
public struct HorizontalSizeClassEnvironmentKey: EnvironmentKey {
public static let defaultValue: UserInterfaceSizeClass = .regular
}
struct VerticalSizeClassEnvironmentKey: EnvironmentKey {
static let defaultValue: UserInterfaceSizeClass = .regular
public struct VerticalSizeClassEnvironmentKey: EnvironmentKey {
public static let defaultValue: UserInterfaceSizeClass = .regular
}
extension EnvironmentValues {
public extension EnvironmentValues {
var horizontalSizeClass: UserInterfaceSizeClass {
get { self[HorizontalSizeClassEnvironmentKey.self] }
set { self[HorizontalSizeClassEnvironmentKey.self] = newValue }

View File

@ -2,22 +2,26 @@ import SwiftUI
import WebKit
#if os(iOS)
struct BasicWebAppView: UIViewRepresentable {
public struct BasicWebAppView: UIViewRepresentable {
let request: URLRequest
let webView = WKWebView()
func makeCoordinator() -> BasicWebAppViewCoordinator {
public init(request: URLRequest) {
self.request = request
}
public func makeCoordinator() -> BasicWebAppViewCoordinator {
BasicWebAppViewCoordinator()
}
func makeUIView(context _: Context) -> WKWebView {
public func makeUIView(context _: Context) -> WKWebView {
webView.scrollView.isScrollEnabled = true
webView.isOpaque = false
webView.backgroundColor = UIColor.clear
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {
public func updateUIView(_ webView: WKWebView, context: Context) {
if context.coordinator.needsReload {
webView.load(request)
context.coordinator.needsReload = false
@ -27,18 +31,22 @@ import WebKit
#endif
#if os(macOS)
struct BasicWebAppView: NSViewRepresentable {
public struct BasicWebAppView: NSViewRepresentable {
let request: URLRequest
func makeCoordinator() -> BasicWebAppViewCoordinator {
public init(request: URLRequest) {
self.request = request
}
public func makeCoordinator() -> BasicWebAppViewCoordinator {
BasicWebAppViewCoordinator()
}
func makeNSView(context _: Context) -> WKWebView {
public func makeNSView(context _: Context) -> WKWebView {
WebView(frame: CGRect.zero)
}
func updateNSView(_ webView: WKWebView, context: Context) {
public func updateNSView(_ webView: WKWebView, context: Context) {
if context.coordinator.needsReload {
webView.load(request)
context.coordinator.needsReload = false
@ -47,7 +55,7 @@ import WebKit
}
#endif
final class BasicWebAppViewCoordinator: NSObject {
public final class BasicWebAppViewCoordinator: NSObject {
var needsReload = true
override init() {