Adds Mac Preferences

This makes use of `@AppStorage` for preferences. Severeral more need to migrated from AppDefaults etc.
This commit is contained in:
Stuart Breckenridge
2020-06-29 21:04:50 +08:00
parent 2e6e934b6f
commit 1f6f5fa054
8 changed files with 506 additions and 55 deletions

View File

@@ -0,0 +1,96 @@
//
// MacPreferences.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
/// The `MacPreferences` object stores all macOS specific user preferences.
class MacPreferences: ObservableObject {
private struct AppKeys {
static let refreshInterval = "refreshInterval"
static let openInBackground = "openInBrowserInBackground"
static let showUnreadCountInDock = "showUnreadCountInDock"
static let checkForUpdatesAutomatically = "checkForAppUpdates"
static let downloadTestBuilds = "downloadTestBuilds"
static let sendCrashLogs = "sendCrashLogs"
}
// Refresh Interval
public let refreshIntervals:[String] = RefreshFrequencies.allCases.map({ $0.description })
@AppStorage(wrappedValue: 0, AppKeys.refreshInterval) var refreshFrequency {
didSet {
objectWillChange.send()
}
}
// Open in background
@AppStorage(wrappedValue: false, AppKeys.openInBackground) var openInBackground {
didSet {
objectWillChange.send()
}
}
// Unread Count in Dock
@AppStorage(wrappedValue: true, AppKeys.showUnreadCountInDock) var showUnreadCountInDock {
didSet {
objectWillChange.send()
}
}
// Check for App Updates
@AppStorage(wrappedValue: true, AppKeys.checkForUpdatesAutomatically) var checkForUpdatesAutomatically {
didSet {
objectWillChange.send()
}
}
// Test builds
@AppStorage(wrappedValue: false, AppKeys.downloadTestBuilds) var downloadTestBuilds {
didSet {
objectWillChange.send()
}
}
// Crash Logs
@AppStorage(wrappedValue: false, AppKeys.sendCrashLogs) var sendCrashLogs {
didSet {
objectWillChange.send()
}
}
}
enum RefreshFrequencies: CaseIterable, CustomStringConvertible {
case refreshEvery10Mins, refreshEvery20Mins, refreshHourly, refreshEvery2Hours, refreshEvery4Hours, refreshEvery8Hours, none
var description: String {
switch self {
case .refreshEvery10Mins:
return "Every 10 minutes"
case .refreshEvery20Mins:
return "Every 20 minutes"
case .refreshHourly:
return "Every hour"
case .refreshEvery2Hours:
return "Every 2 hours"
case .refreshEvery4Hours:
return "Every 4 hours"
case .refreshEvery8Hours:
return "Every 8 hours"
case .none:
return "Manually"
}
}
}

View File

@@ -0,0 +1,127 @@
//
// AccountsPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct AccountPreferencesViewModel {
let accountTypes = ["On My Mac", "FeedBin"]
var selectedAccount = Int?.none
}
struct AccountsPreferencesView: View {
@State private var viewModel = AccountPreferencesViewModel()
@State private var addAccountViewModel = AccountPreferencesViewModel()
@State private var showAddAccountView: Bool = false
var body: some View {
VStack {
HStack(alignment: .top, spacing: 10) {
VStack(alignment: .leading) {
List(selection: $viewModel.selectedAccount, content: {
ForEach(0..<viewModel.accountTypes.count, content: { i in
AccountDetailRow(imageName: "desktopcomputer", accountName: viewModel.accountTypes[i]).padding(.vertical, 8)
})
})
HStack {
Button("+", action: {
showAddAccountView.toggle()
})
Button("-", action: {})
.disabled(viewModel.selectedAccount == nil)
Spacer()
}
}.frame(width: 225, height: 300, alignment: .leading)
VStack(alignment: .leading) {
viewModel.selectedAccount == nil ? Text("Select Account") : Text(viewModel.accountTypes[viewModel.selectedAccount!])
Spacer()
}.frame(width: 225, height: 300, alignment: .leading)
}
Spacer()
}.sheet(isPresented: $showAddAccountView,
onDismiss: { showAddAccountView.toggle() },
content: {
AddAccountView()
})
}
}
struct AccountDetailRow: View {
var imageName: String
var accountName: String
var body: some View {
HStack {
Image(systemName: imageName).font(.headline)
Text(accountName).font(.headline)
}
}
}
struct AddAccountView: View {
@Environment(\.presentationMode) var presentationMode
let accountTypes = ["On My Mac", "FeedBin"]
@State var selectedAccount: Int = 0
@State private var userName: String = ""
@State private var password: String = ""
var body: some View {
VStack(alignment: .leading) {
Text("Add an Account").font(.headline)
Form {
Picker("Account Type",
selection: $selectedAccount,
content: {
ForEach(0..<accountTypes.count, content: {
AccountDetailRow(imageName: "desktopcomputer", accountName: accountTypes[$0])
})
})
if selectedAccount == 1 {
TextField("Email", text: $userName)
SecureField("Password", text: $password)
}
}
Spacer()
HStack {
Spacer()
Button(action: { presentationMode.wrappedValue.dismiss() }, label: {
Text("Cancel")
})
if selectedAccount == 0 {
Button("Add", action: {})
}
if selectedAccount != 0 {
Button("Create", action: {})
.disabled(userName.count == 0 || password.count == 0)
}
}
}.frame(width: 300, alignment: .top).padding()
}
}
class AddAccountModel: ObservableObject {
let accountTypes = ["On My Mac", "FeedBin"]
@Published var selectedAccount = Int?.none
}

View File

@@ -0,0 +1,49 @@
//
// AdvancedPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct AdvancedPreferencesView: View {
@StateObject var preferences: MacPreferences
var body: some View {
VStack {
Form {
Toggle("Check for app updates automatically", isOn: $preferences.checkForUpdatesAutomatically)
Toggle("Download Test Builds", isOn: $preferences.downloadTestBuilds)
HStack {
Spacer()
Text("If youre not sure, don't enable test builds. Test builds may have bugs, which may include crashing bugs and data loss.").foregroundColor(.secondary)
Spacer()
}
HStack {
Spacer()
Button("Check for Updates", action: {})
Spacer()
}.padding(.vertical, 12)
Toggle("Send Crash Logs Automatically", isOn: $preferences.sendCrashLogs)
Spacer()
HStack {
Spacer()
Button("Privacy Policy", action: {})
Spacer()
}.padding(.top, 12)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -0,0 +1,33 @@
//
// GeneralPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct GeneralPreferencesView: View {
@ObservedObject private var preferences = MacPreferences()
var body: some View {
VStack {
Form {
Picker("Refresh Feeds",
selection: $preferences.refreshFrequency,
content: {
ForEach(0..<preferences.refreshIntervals.count, content: {
Text(preferences.refreshIntervals[$0])
})
}).frame(width: 300, alignment: .center)
Toggle("Open webpages in background in browser", isOn: $preferences.openInBackground)
Toggle("Show Unread Count in Dock", isOn: $preferences.showUnreadCountInDock)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -0,0 +1,89 @@
//
// MacPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct MacPreferenceViewModel {
enum PreferencePane: Int, CaseIterable {
case general = 0
case accounts = 1
case advanced = 2
var description: String {
switch self {
case .general:
return "General"
case .accounts:
return "Accounts"
case .advanced:
return "Advanced"
}
}
}
var currentPreferencePane: PreferencePane = PreferencePane.general
}
struct MacPreferencesView: View {
@EnvironmentObject var preferences: MacPreferences
@State private var viewModel = MacPreferenceViewModel()
var body: some View {
VStack {
if viewModel.currentPreferencePane == .general {
AnyView(GeneralPreferencesView())
}
else if viewModel.currentPreferencePane == .accounts {
AnyView(AccountsPreferencesView())
}
else {
AnyView(AdvancedPreferencesView(preferences: preferences))
}
}
.toolbar {
ToolbarItem {
Button(action: {
viewModel.currentPreferencePane = .general
}, label: {
Image(systemName: "checkmark.rectangle")
Text("General")
})
}
ToolbarItem {
Button(action: {
viewModel.currentPreferencePane = .accounts
}, label: {
Image(systemName: "network")
Text("Accounts")
})
}
ToolbarItem {
Button(action: {
viewModel.currentPreferencePane = .advanced
}, label: {
Image(systemName: "gearshape.fill")
Text("Advanced")
})
}
}
.presentedWindowToolbarStyle(UnifiedCompactWindowToolbarStyle())
.presentedWindowStyle(TitleBarWindowStyle())
.navigationTitle(Text(viewModel.currentPreferencePane.description))
}
}
struct MacPreferencesView_Previews: PreviewProvider {
static var previews: some View {
MacPreferencesView()
}
}