Add sidebar layout for macOS and iPad
This commit is contained in:
parent
12a20c30b9
commit
733f1cc764
7 changed files with 173 additions and 72 deletions
|
@ -47,6 +47,7 @@
|
|||
543ECE42233E82A40018A9D9 /* AuthenticationDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 543ECE41233E82A40018A9D9 /* AuthenticationDataStore.swift */; };
|
||||
8043EB84271F26ED00498E73 /* CategoryDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8043EB83271F26ED00498E73 /* CategoryDetailsView.swift */; };
|
||||
806C7850272B700B00FA1375 /* TwigsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 806C784F272B700B00FA1375 /* TwigsApp.swift */; };
|
||||
80820145275FFD380040996E /* SidebarBudgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80820144275FFD380040996E /* SidebarBudgetView.swift */; };
|
||||
8094A9C327567CAC006C6C62 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = 8094A9C227567CAC006C6C62 /* Collections */; };
|
||||
809B942327221EC800B1DAE2 /* CategoryFormSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 809B942227221EC800B1DAE2 /* CategoryFormSheet.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
@ -116,6 +117,7 @@
|
|||
543ECE41233E82A40018A9D9 /* AuthenticationDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationDataStore.swift; sourceTree = "<group>"; };
|
||||
8043EB83271F26ED00498E73 /* CategoryDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryDetailsView.swift; sourceTree = "<group>"; };
|
||||
806C784F272B700B00FA1375 /* TwigsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwigsApp.swift; sourceTree = "<group>"; };
|
||||
80820144275FFD380040996E /* SidebarBudgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarBudgetView.swift; sourceTree = "<group>"; };
|
||||
809B942227221EC800B1DAE2 /* CategoryFormSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryFormSheet.swift; sourceTree = "<group>"; };
|
||||
809B94242722597800B1DAE2 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
@ -191,10 +193,10 @@
|
|||
28AC94E1233C373900BFB70A = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28AC94EB233C373900BFB70A /* Products */,
|
||||
28AC94EC233C373900BFB70A /* Twigs */,
|
||||
28AC9503233C373A00BFB70A /* TwigsTests */,
|
||||
28AC950E233C373A00BFB70A /* TwigsUITests */,
|
||||
28AC94EB233C373900BFB70A /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
@ -218,6 +220,7 @@
|
|||
28AC94F1233C373900BFB70A /* LoginView.swift */,
|
||||
2888234623512DBF003D3847 /* Observable.swift */,
|
||||
28B9E50D2346BCB2007C3909 /* RegistrationView.swift */,
|
||||
80820144275FFD380040996E /* SidebarBudgetView.swift */,
|
||||
2841022623419A2B00EAFA29 /* TabbedBudgetView.swift */,
|
||||
806C784F272B700B00FA1375 /* TwigsApp.swift */,
|
||||
28AC94F3233C373A00BFB70A /* Assets.xcassets */,
|
||||
|
@ -465,6 +468,7 @@
|
|||
28FE6AF62342E4CC00D5543E /* BudgetRepository.swift in Sources */,
|
||||
28FE6B022344331B00D5543E /* TransactionDataStore.swift in Sources */,
|
||||
543ECE42233E82A40018A9D9 /* AuthenticationDataStore.swift in Sources */,
|
||||
80820145275FFD380040996E /* SidebarBudgetView.swift in Sources */,
|
||||
284102302342D97300EAFA29 /* BudgetListsView.swift in Sources */,
|
||||
282126BD235CDE1400072D52 /* ProgressView.swift in Sources */,
|
||||
806C7850272B700B00FA1375 /* TwigsApp.swift in Sources */,
|
||||
|
|
|
@ -15,25 +15,21 @@ struct BudgetListsView: View {
|
|||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
switch budgetDataStore.budgets {
|
||||
case .success(let budgets):
|
||||
Section {
|
||||
List(budgets) { budget in
|
||||
BudgetListItemView(budget)
|
||||
}
|
||||
}.navigationBarTitle("budgets")
|
||||
case .failure(.loading):
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}.navigationBarTitle("budgets")
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
Text("budgets_load_failure").navigationBarTitle("budgets")
|
||||
Button("action_retry", action: {
|
||||
self.budgetDataStore.getBudgets()
|
||||
})
|
||||
switch budgetDataStore.budgets {
|
||||
case .success(let budgets):
|
||||
Section("budgets") {
|
||||
ForEach(budgets) { budget in
|
||||
BudgetListItemView(budget)
|
||||
}
|
||||
}
|
||||
case .failure(.loading):
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
Text("budgets_load_failure").navigationBarTitle("budgets")
|
||||
Button("action_retry", action: {
|
||||
self.budgetDataStore.getBudgets()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<true/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
|
|
82
Twigs/SidebarBudgetView.swift
Normal file
82
Twigs/SidebarBudgetView.swift
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// SidebarBudgetView.swift
|
||||
// Twigs
|
||||
//
|
||||
// Created by William Brawner on 12/7/21.
|
||||
// Copyright © 2021 William Brawner. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SidebarBudgetView: View {
|
||||
@EnvironmentObject var authenticationDataStore: AuthenticationDataStore
|
||||
@EnvironmentObject var budgetDataStore: BudgetsDataStore
|
||||
@EnvironmentObject var categoryDataStore: CategoryDataStore
|
||||
let apiService: TwigsApiService
|
||||
@State var isSelectingBudget = true
|
||||
@State var hasSelectedBudget = false
|
||||
@State var isAddingTransaction = false
|
||||
@State var tabSelection: Int? = 0
|
||||
|
||||
@ViewBuilder
|
||||
var mainView: some View {
|
||||
if case let .success(budget) = budgetDataStore.budget {
|
||||
NavigationView {
|
||||
List {
|
||||
NavigationLink(
|
||||
tag: 0,
|
||||
selection: $tabSelection,
|
||||
destination: { BudgetDetailsView(budget: budget).navigationBarTitle("overview") },
|
||||
label: { Label("overview", systemImage: "chart.line.uptrend.xyaxis.circle.fill") }
|
||||
)
|
||||
.keyboardShortcut("1")
|
||||
NavigationLink(
|
||||
tag: 1,
|
||||
selection: $tabSelection,
|
||||
destination: { TransactionListView(budget).navigationBarTitle("transactions") },
|
||||
label: { Label("transactions", systemImage: "dollarsign.circle.fill") })
|
||||
.keyboardShortcut("2")
|
||||
NavigationLink(
|
||||
tag: 2,
|
||||
selection: $tabSelection,
|
||||
destination: { CategoryListView(budget).navigationBarTitle("categories") },
|
||||
label: { Label("categories", systemImage: "chart.pie.fill") })
|
||||
.keyboardShortcut("3")
|
||||
NavigationLink(
|
||||
tag: 3,
|
||||
selection: $tabSelection,
|
||||
destination: { RecurringTransactionsListView(dataStore: RecurringTransactionDataStore(apiService, budgetId: budget.id)).navigationBarTitle("recurring_transactions") },
|
||||
label: { Label("recurring_transactions", systemImage: "arrow.triangle.2.circlepath.circle.fill") })
|
||||
.keyboardShortcut("4")
|
||||
BudgetListsView()
|
||||
}
|
||||
.navigationTitle(budget.name)
|
||||
}
|
||||
} else {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
mainView
|
||||
.sheet(isPresented: $authenticationDataStore.showLogin,
|
||||
onDismiss: {
|
||||
self.budgetDataStore.getBudgets()
|
||||
},
|
||||
content: {
|
||||
LoginView()
|
||||
.environmentObject(authenticationDataStore)
|
||||
.onDisappear {
|
||||
self.budgetDataStore.getBudgets()
|
||||
}
|
||||
})
|
||||
.interactiveDismissDisabled(true)
|
||||
}
|
||||
}
|
||||
|
||||
//struct SidebarBudgetView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// SidebarBudgetView()
|
||||
// }
|
||||
//}
|
|
@ -24,6 +24,11 @@ struct TabbedBudgetView: View {
|
|||
NavigationView {
|
||||
BudgetDetailsView(budget: budget)
|
||||
.navigationBarTitle("overview")
|
||||
.navigationBarItems(leading: HStack {
|
||||
Button("budgets", action: {
|
||||
self.budgetDataStore.showBudgetSelection = true
|
||||
}).padding()
|
||||
})
|
||||
}
|
||||
.tabItem {
|
||||
Image(systemName: "chart.line.uptrend.xyaxis.circle.fill")
|
||||
|
@ -88,8 +93,9 @@ struct TabbedBudgetView: View {
|
|||
}
|
||||
}).sheet(isPresented: $budgetDataStore.showBudgetSelection,
|
||||
content: {
|
||||
BudgetListsView()
|
||||
.environmentObject(budgetDataStore)
|
||||
List {
|
||||
BudgetListsView()
|
||||
}
|
||||
})
|
||||
.interactiveDismissDisabled(true)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Collections
|
||||
|
||||
struct TransactionListView: View {
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
|
@ -16,62 +17,65 @@ struct TransactionListView: View {
|
|||
let header: AnyView?
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
switch transactionDataStore.transactions[requestId] {
|
||||
case .success(let transactions):
|
||||
Group {
|
||||
if transactions.isEmpty {
|
||||
Text("no_transactions")
|
||||
} else {
|
||||
List {
|
||||
if let header = header {
|
||||
Section {
|
||||
header
|
||||
}
|
||||
}
|
||||
ForEach(transactions.keys, id: \.self) { (key: String) in
|
||||
Group {
|
||||
Section(header: Text(key)) {
|
||||
ForEach(transactions[key]!) { transaction in
|
||||
TransactionListItemView(transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
private func TransactionList(_ transactions: OrderedDictionary<String, [Transaction]>) -> some View {
|
||||
if transactions.isEmpty {
|
||||
Text("no_transactions")
|
||||
} else {
|
||||
if let header = header {
|
||||
Section {
|
||||
header
|
||||
}
|
||||
}
|
||||
ForEach(transactions.keys, id: \.self) { (key: String) in
|
||||
Group {
|
||||
Section(header: Text(key)) {
|
||||
ForEach(transactions[key]!) { transaction in
|
||||
TransactionListItemView(transaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isAddingTransaction, content: {
|
||||
AddTransactionView(showSheet: $isAddingTransaction, budgetId: self.budget.id)
|
||||
.navigationBarTitle("add_transaction")
|
||||
})
|
||||
.navigationBarItems(
|
||||
trailing: HStack {
|
||||
Button(action: {
|
||||
self.isAddingTransaction = true
|
||||
}) {
|
||||
Image(systemName: "plus")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
)
|
||||
case nil, .failure(.loading):
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}.onAppear {
|
||||
if transactionDataStore.transactions[requestId] == nil || self.requestId == "" {
|
||||
self.requestId = transactionDataStore.getTransactions(self.budget.id, categoryId: self.category?.id)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
Text("budgets_load_failure")
|
||||
Button("action_retry", action: {
|
||||
self.requestId = transactionDataStore.getTransactions(self.budget.id, categoryId: self.category?.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
switch transactionDataStore.transactions[requestId] {
|
||||
case .success(let transactions):
|
||||
List {
|
||||
TransactionList(transactions)
|
||||
}
|
||||
.sheet(isPresented: $isAddingTransaction, content: {
|
||||
AddTransactionView(showSheet: $isAddingTransaction, budgetId: self.budget.id)
|
||||
.navigationBarTitle("add_transaction")
|
||||
})
|
||||
.navigationBarItems(
|
||||
trailing: HStack {
|
||||
Button(action: {
|
||||
self.isAddingTransaction = true
|
||||
}) {
|
||||
Image(systemName: "plus")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
)
|
||||
case nil, .failure(.loading):
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large).onAppear {
|
||||
if transactionDataStore.transactions[requestId] == nil || self.requestId == "" {
|
||||
self.requestId = transactionDataStore.getTransactions(self.budget.id, categoryId: self.category?.id)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
List {
|
||||
Text("budgets_load_failure")
|
||||
Button("action_retry", action: {
|
||||
self.requestId = transactionDataStore.getTransactions(self.budget.id, categoryId: self.category?.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let budget: Budget
|
||||
let category: Category?
|
||||
init(_ budget: Budget, category: Category? = nil, header: AnyView? = nil) {
|
||||
|
|
|
@ -33,9 +33,18 @@ struct TwigsApp: App {
|
|||
)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var mainView: some View {
|
||||
if UIDevice.current.userInterfaceIdiom == .mac || UIDevice.current.userInterfaceIdiom == .pad {
|
||||
SidebarBudgetView(apiService: apiService)
|
||||
} else {
|
||||
TabbedBudgetView(apiService: apiService)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
TabbedBudgetView()
|
||||
mainView
|
||||
.environmentObject(dataStoreProvider.authenticationDataStore())
|
||||
.environmentObject(dataStoreProvider.budgetsDataStore())
|
||||
.environmentObject(dataStoreProvider.categoryDataStore())
|
||||
|
|
Loading…
Reference in a new issue