Use EnvironmentObject instead of constructor injection
This commit is contained in:
parent
491ce8c10f
commit
090343de0c
16 changed files with 194 additions and 217 deletions
|
@ -643,6 +643,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Twigs/Twigs.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
|
@ -667,6 +668,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Twigs/Twigs.entitlements;
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
|
|
|
@ -13,7 +13,7 @@ struct BudgetDetailsView: View {
|
|||
ScrollView {
|
||||
VStack {
|
||||
// ProgressView(value: .constant(50.0, maxValue: 100))
|
||||
CategoryListView(self.dataStoreProvider, budget: budget)
|
||||
CategoryListView(budget)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import SwiftUI
|
|||
import Combine
|
||||
|
||||
struct BudgetListsView: View {
|
||||
@ObservedObject var budgetsDataStore: BudgetsDataStore
|
||||
@EnvironmentObject var budgetsDataStore: BudgetsDataStore
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
|
@ -20,7 +20,7 @@ struct BudgetListsView: View {
|
|||
case .success(let budgets):
|
||||
Section {
|
||||
List(budgets) { budget in
|
||||
BudgetListItemView(self.dataStoreProvider, budget: budget)
|
||||
BudgetListItemView(budget)
|
||||
}
|
||||
}.navigationBarTitle("budgets")
|
||||
case .failure(.loading):
|
||||
|
@ -31,26 +31,19 @@ struct BudgetListsView: View {
|
|||
// TODO: Handle each network failure type
|
||||
Text("budgets_load_failure").navigationBarTitle("budgets")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
init(_ dataStoreProvider: DataStoreProvider) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.budgetsDataStore = dataStoreProvider.budgetsDataStore()
|
||||
}.onAppear {
|
||||
self.budgetsDataStore.getBudgets()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct BudgetListItemView: View {
|
||||
var budget: Budget
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(
|
||||
destination: TabbedBudgetView(self.dataStoreProvider, budget: budget)
|
||||
destination: TabbedBudgetView(budget)
|
||||
.navigationBarTitle(budget.name)
|
||||
) {
|
||||
VStack(alignment: .leading) {
|
||||
|
@ -66,8 +59,7 @@ struct BudgetListItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
init (_ dataStoreProvider: DataStoreProvider, budget: Budget) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
init (_ budget: Budget) {
|
||||
self.budget = budget
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +67,7 @@ struct BudgetListItemView: View {
|
|||
#if DEBUG
|
||||
struct BudgetListsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
BudgetListsView(MockDataStoreProvider())
|
||||
BudgetListsView()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -11,11 +11,7 @@ import Combine
|
|||
|
||||
class CategoryDataStore: ObservableObject {
|
||||
private var currentRequest: AnyCancellable? = nil
|
||||
var categories: Result<[Category], NetworkError> = .failure(.loading) {
|
||||
didSet {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
@Published var categories: [String:Result<[Category], NetworkError>] = ["":.failure(.loading)]
|
||||
|
||||
var category: Result<Category, NetworkError> = .failure(.loading) {
|
||||
didSet {
|
||||
|
@ -23,8 +19,9 @@ class CategoryDataStore: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func getCategories(budgetId: String? = nil, expense: Bool? = nil, archived: Bool? = false, count: Int? = nil, page: Int? = nil) {
|
||||
self.categories = .failure(.loading)
|
||||
func getCategories(budgetId: String? = nil, expense: Bool? = nil, archived: Bool? = false, count: Int? = nil, page: Int? = nil) -> String {
|
||||
let requestId = "\(budgetId ?? "all")-\(String(describing: expense))"
|
||||
self.categories[requestId] = .failure(.loading)
|
||||
|
||||
self.currentRequest = categoryRepository.getCategories(budgetId: budgetId, expense: expense, archived: archived, count: count, page: page)
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -32,14 +29,17 @@ class CategoryDataStore: ObservableObject {
|
|||
switch completion {
|
||||
case .finished:
|
||||
self.currentRequest = nil
|
||||
self.objectWillChange.send() // TODO: Remove hack after finding better way to update dictionary values
|
||||
return
|
||||
case .failure(let error):
|
||||
self.categories = .failure(error)
|
||||
self.categories[requestId] = .failure(error)
|
||||
}
|
||||
}, receiveValue: { (categories) in
|
||||
print("Received \(categories.count) categories")
|
||||
self.categories = .success(categories)
|
||||
self.categories[requestId] = .success(categories)
|
||||
})
|
||||
|
||||
return requestId
|
||||
}
|
||||
|
||||
func getCategory(_ categoryId: String) {
|
||||
|
|
|
@ -10,49 +10,43 @@ import SwiftUI
|
|||
import Combine
|
||||
|
||||
struct CategoryListView: View {
|
||||
@ObservedObject var categoryDataStore: CategoryDataStore
|
||||
@EnvironmentObject var categoryDataStore: CategoryDataStore
|
||||
@State var requestId: String = ""
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
stateContent
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
switch self.categoryDataStore.categories {
|
||||
switch self.categoryDataStore.categories[requestId] {
|
||||
case .success(let categories):
|
||||
return AnyView(
|
||||
Section {
|
||||
List(categories) { category in
|
||||
CategoryListItemView(self.dataStoreProvider, budget: budget, category: category)
|
||||
CategoryListItemView(budget, category: category)
|
||||
}
|
||||
}
|
||||
)
|
||||
case .failure(.loading):
|
||||
return AnyView(VStack {
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
})
|
||||
}.onAppear {
|
||||
if self.requestId == "" {
|
||||
self.requestId = categoryDataStore.getCategories(budgetId: budget.id)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
return AnyView(Text("budgets_load_failure"))
|
||||
Text("budgets_load_failure")
|
||||
}
|
||||
}
|
||||
|
||||
private let dataStoreProvider: DataStoreProvider
|
||||
private let budget: Budget
|
||||
init(_ dataStoreProvider: DataStoreProvider, budget: Budget) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
let categoryDataStore = dataStoreProvider.categoryDataStore()
|
||||
init(_ budget: Budget) {
|
||||
self.budget = budget
|
||||
categoryDataStore.getCategories(budgetId: budget.id)
|
||||
self.categoryDataStore = categoryDataStore
|
||||
}
|
||||
}
|
||||
|
||||
struct CategoryListItemView: View {
|
||||
var category: Category
|
||||
let budget: Budget
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
let sumId: String
|
||||
@ObservedObject var transactionDataStore: TransactionDataStore
|
||||
@State var sumId: String = ""
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
|
||||
var progressTintColor: Color {
|
||||
get {
|
||||
|
@ -66,7 +60,7 @@ struct CategoryListItemView: View {
|
|||
|
||||
var body: some View {
|
||||
NavigationLink(
|
||||
destination: TransactionListView(self.dataStoreProvider, budget: self.budget, category: category)
|
||||
destination: TransactionListView(self.budget, category: category)
|
||||
.navigationBarTitle(category.title)
|
||||
) {
|
||||
VStack(alignment: .leading) {
|
||||
|
@ -83,6 +77,10 @@ struct CategoryListItemView: View {
|
|||
}
|
||||
progressView
|
||||
}
|
||||
}.onAppear {
|
||||
if self.sumId == "" {
|
||||
self.sumId = transactionDataStore.sum(categoryId: category.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,13 +111,9 @@ struct CategoryListItemView: View {
|
|||
return Text(verbatim: remaining).foregroundColor(color)
|
||||
}
|
||||
|
||||
init (_ dataStoreProvider: DataStoreProvider, budget: Budget, category: Category) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
init (_ budget: Budget, category: Category) {
|
||||
self.budget = budget
|
||||
self.category = category
|
||||
let transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
self.transactionDataStore = transactionDataStore
|
||||
self.sumId = transactionDataStore.sum(categoryId: category.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,14 +9,14 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
@ObservedObject var authenticationDataStore: AuthenticationDataStore
|
||||
@EnvironmentObject var authenticationDataStore: AuthenticationDataStore
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
if showLogin() {
|
||||
LoginView(authenticationDataStore)
|
||||
LoginView()
|
||||
} else {
|
||||
BudgetListsView(dataStoreProvider)
|
||||
BudgetListsView()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,6 @@ struct ContentView: View {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private let dataStoreProvider: DataStoreProvider
|
||||
|
||||
init (_ dataStoreProvider: DataStoreProvider) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.authenticationDataStore = dataStoreProvider.authenticationDataStore()
|
||||
}
|
||||
}
|
||||
|
||||
//struct ContentView_Previews: PreviewProvider {
|
||||
|
|
|
@ -12,8 +12,16 @@ import Combine
|
|||
struct LoginView: View {
|
||||
@State var username: String = ""
|
||||
@State var password: String = ""
|
||||
@ObservedObject var userData: AuthenticationDataStore
|
||||
let showLoader: Bool
|
||||
@EnvironmentObject var userData: AuthenticationDataStore
|
||||
var showLoader: Bool {
|
||||
get {
|
||||
if case self.userData.currentUser = Result<User, UserStatus>.failure(UserStatus.authenticating) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
LoadingView(
|
||||
|
@ -42,15 +50,6 @@ struct LoginView: View {
|
|||
}.navigationBarHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
init (_ userData: AuthenticationDataStore) {
|
||||
self.userData = userData
|
||||
if case userData.currentUser = Result<User, UserStatus>.failure(UserStatus.authenticating) {
|
||||
self.showLoader = true
|
||||
} else {
|
||||
self.showLoader = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//struct ContentView_Previews: PreviewProvider {
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
import SwiftUI
|
||||
|
||||
struct ProfileView: View {
|
||||
let currentUser: User
|
||||
@EnvironmentObject var authDataStore: AuthenticationDataStore
|
||||
var currentUser: User {
|
||||
get {
|
||||
return try! authDataStore.currentUser.get()
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 10) {
|
||||
|
@ -32,18 +37,12 @@ struct ProfileView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
init(_ dataStoreProvider: DataStoreProvider) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.currentUser = try! dataStoreProvider.authenticationDataStore().currentUser.get()
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct ProfileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ProfileView(MockDataStoreProvider())
|
||||
ProfileView()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
// Create the SwiftUI view that provides the window contents.
|
||||
let contentView = ContentView(dataStoreProvider)
|
||||
let contentView = ContentView()
|
||||
.environmentObject(dataStoreProvider.authenticationDataStore())
|
||||
.environmentObject(dataStoreProvider.budgetsDataStore())
|
||||
.environmentObject(dataStoreProvider.categoryDataStore())
|
||||
.environmentObject(dataStoreProvider.transactionDataStore())
|
||||
.environmentObject(dataStoreProvider.userDataStore())
|
||||
// Use a UIHostingController as window root view controller.
|
||||
if let windowScene = scene as? UIWindowScene {
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
|
|
|
@ -9,14 +9,15 @@
|
|||
import SwiftUI
|
||||
|
||||
struct TabbedBudgetView: View {
|
||||
let budget: Budget
|
||||
@State var isAddingTransaction = false
|
||||
@State var selectedTab: Int = 0
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
TransactionListView(dataStoreProvider, budget: self.budget)
|
||||
TransactionListView(self.budget)
|
||||
.sheet(isPresented: $isAddingTransaction, content: {
|
||||
AddTransactionView(self.dataStoreProvider)
|
||||
AddTransactionView(budgetId: self.budget.id)
|
||||
.navigationBarTitle("add_transaction")
|
||||
})
|
||||
.tabItem {
|
||||
|
@ -28,18 +29,20 @@ struct TabbedBudgetView: View {
|
|||
selectedTab = 0
|
||||
}
|
||||
|
||||
CategoryListView(dataStoreProvider, budget: self.budget).tabItem {
|
||||
CategoryListView(self.budget).tabItem {
|
||||
Image(systemName: "chart.pie.fill")
|
||||
Text("categories")
|
||||
}
|
||||
.tag(1)
|
||||
.onAppear {
|
||||
selectedTab = 1
|
||||
}
|
||||
|
||||
ProfileView(dataStoreProvider).tabItem {
|
||||
ProfileView().tabItem {
|
||||
Image(systemName: "person.circle.fill")
|
||||
Text("profile")
|
||||
}
|
||||
.tag(2)
|
||||
.onAppear {
|
||||
selectedTab = 2
|
||||
}
|
||||
|
@ -58,13 +61,8 @@ struct TabbedBudgetView: View {
|
|||
)
|
||||
}
|
||||
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
let budget: Budget
|
||||
init (_ dataStoreProvider: DataStoreProvider, budget: Budget) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
init (_ budget: Budget) {
|
||||
self.budget = budget
|
||||
// Warm up the caches
|
||||
self.dataStoreProvider.categoryDataStore().getCategories(budgetId: budget.id)
|
||||
}
|
||||
}
|
||||
//
|
||||
|
|
|
@ -11,6 +11,8 @@ import Combine
|
|||
|
||||
struct AddTransactionView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@EnvironmentObject var authDataStore: AuthenticationDataStore
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
@State var title: String = ""
|
||||
@State var description: String = ""
|
||||
@State var date: Date = Date()
|
||||
|
@ -18,7 +20,11 @@ struct AddTransactionView: View {
|
|||
@State var type: TransactionType = .expense
|
||||
@State var budgetId: String = ""
|
||||
@State var categoryId: String = ""
|
||||
let createdBy: String
|
||||
var createdBy: String {
|
||||
get {
|
||||
return try! authDataStore.currentUser.get().id
|
||||
}
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
switch transactionDataStore.transaction {
|
||||
|
@ -36,7 +42,6 @@ struct AddTransactionView: View {
|
|||
type: self.$type,
|
||||
budgetId: self.$budgetId,
|
||||
categoryId: self.$categoryId,
|
||||
dataStoreProvider: self.dataStoreProvider,
|
||||
deleteAction: nil
|
||||
))
|
||||
}
|
||||
|
@ -65,22 +70,19 @@ struct AddTransactionView: View {
|
|||
})
|
||||
}
|
||||
.onDisappear {
|
||||
_ = self.transactionDataStore.getTransactions(self.budgetId, categoryId: self.categoryId)
|
||||
self.title = ""
|
||||
self.description = ""
|
||||
self.date = Date()
|
||||
self.amount = ""
|
||||
self.type = .expense
|
||||
self.budgetId = ""
|
||||
self.categoryId = ""
|
||||
}
|
||||
}
|
||||
|
||||
@ObservedObject var transactionDataStore: TransactionDataStore
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
init(_ dataStoreProvider: DataStoreProvider) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
self.createdBy = try! dataStoreProvider.authenticationDataStore().currentUser.get().id
|
||||
init(budgetId: String, categoryId: String = "") {
|
||||
self._budgetId = State(initialValue: budgetId)
|
||||
self._categoryId = State(initialValue: categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ struct EditTransactionForm: View {
|
|||
@Binding var budgetId: String
|
||||
@Binding var categoryId: String
|
||||
@State private var showingAlert = false
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
let deleteAction: (() -> ())?
|
||||
|
||||
var body: some View {
|
||||
|
@ -32,8 +31,8 @@ struct EditTransactionForm: View {
|
|||
Text(type.localizedKey)
|
||||
}
|
||||
}
|
||||
BudgetPicker(self.dataStoreProvider, budgetId: self.$budgetId)
|
||||
CategoryPicker(self.dataStoreProvider, budgetId: self.$budgetId, categoryId: self.$categoryId, expense: self.$type)
|
||||
BudgetPicker(self.$budgetId)
|
||||
CategoryPicker(self.$budgetId, categoryId: self.$categoryId, expense: self.$type)
|
||||
if deleteAction != nil {
|
||||
Button(action: {
|
||||
self.showingAlert = true
|
||||
|
@ -53,71 +52,68 @@ struct EditTransactionForm: View {
|
|||
|
||||
struct BudgetPicker: View {
|
||||
var budgetId: Binding<String>
|
||||
var stateContent: AnyView {
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
switch self.budgetsDataStore.budgets {
|
||||
case .success(let budgets):
|
||||
return AnyView(
|
||||
Picker("prompt_budget", selection: self.budgetId) {
|
||||
ForEach(budgets) { budget in
|
||||
Text(budget.name)
|
||||
}
|
||||
}
|
||||
)
|
||||
default:
|
||||
return AnyView(
|
||||
Picker("prompt_budget", selection: self.budgetId) {
|
||||
Text("")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
stateContent
|
||||
}
|
||||
|
||||
@ObservedObject var budgetsDataStore: BudgetsDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, budgetId: Binding<String>) {
|
||||
let budgetsDataStore = dataStoreProvider.budgetsDataStore()
|
||||
budgetsDataStore.getBudgets()
|
||||
self.budgetsDataStore = budgetsDataStore
|
||||
@EnvironmentObject var budgetsDataStore: BudgetsDataStore
|
||||
init(_ budgetId: Binding<String>) {
|
||||
self.budgetId = budgetId
|
||||
}
|
||||
}
|
||||
|
||||
struct CategoryPicker: View {
|
||||
let budgetId: Binding<String>
|
||||
var categoryId: Binding<String>
|
||||
var stateContent: AnyView {
|
||||
switch self.categoryDataStore.categories {
|
||||
let expense: Binding<TransactionType>
|
||||
@State var requestId: String = ""
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
switch self.categoryDataStore.categories[requestId] {
|
||||
case .success(let categories):
|
||||
print("Using returned categories")
|
||||
return AnyView(
|
||||
Picker("prompt_category", selection: self.categoryId) {
|
||||
ForEach(categories) { category in
|
||||
Text(category.title)
|
||||
}
|
||||
}
|
||||
)
|
||||
default:
|
||||
return AnyView(
|
||||
EmptyView()
|
||||
)
|
||||
}.onAppear {
|
||||
if !self.requestId.contains(budgetId.wrappedValue) {
|
||||
self.requestId = categoryDataStore.getCategories(budgetId: self.budgetId.wrappedValue, expense: self.expense.wrappedValue == TransactionType.expense, count: nil, page: nil)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
stateContent
|
||||
}
|
||||
|
||||
@ObservedObject var categoryDataStore: CategoryDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, budgetId: Binding<String>, categoryId: Binding<String>, expense: Binding<TransactionType>) {
|
||||
let categoryDataStore = dataStoreProvider.categoryDataStore()
|
||||
print("Requesting categories")
|
||||
case .failure(.loading):
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}.onAppear {
|
||||
if budgetId.wrappedValue != "" {
|
||||
categoryDataStore.getCategories(budgetId: budgetId.wrappedValue, expense: expense.wrappedValue == TransactionType.expense, count: nil, page: nil)
|
||||
if !self.requestId.contains(budgetId.wrappedValue) {
|
||||
self.requestId = categoryDataStore.getCategories(budgetId: self.budgetId.wrappedValue, expense: self.expense.wrappedValue == TransactionType.expense, count: nil, page: nil)
|
||||
}
|
||||
self.categoryDataStore = categoryDataStore
|
||||
}
|
||||
}
|
||||
default:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
@EnvironmentObject var categoryDataStore: CategoryDataStore
|
||||
init(_ budgetId: Binding<String>, categoryId: Binding<String>, expense: Binding<TransactionType>) {
|
||||
self.budgetId = budgetId
|
||||
self.categoryId = categoryId
|
||||
self.expense = expense
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,7 @@ import Combine
|
|||
class TransactionDataStore: ObservableObject {
|
||||
private var currentRequest: AnyCancellable? = nil
|
||||
private var sumRequests: [String:AnyCancellable] = [:]
|
||||
var transactions: Result<[Transaction], NetworkError> = .failure(.loading) {
|
||||
didSet {
|
||||
self.objectWillChange.send()
|
||||
}
|
||||
}
|
||||
@Published var transactions: [String:Result<[Transaction], NetworkError>] = ["": .failure(.loading)]
|
||||
|
||||
var transaction: Result<Transaction, NetworkError> = .failure(.unknown) {
|
||||
didSet {
|
||||
|
@ -30,16 +26,16 @@ class TransactionDataStore: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
func getTransactions(_ budget: Budget, category: Category? = nil, from: Date? = nil, count: Int? = nil, page: Int? = nil) {
|
||||
self.transactions = .failure(.loading)
|
||||
func getTransactions(_ budgetId: String, categoryId: String? = nil, from: Date? = nil, count: Int? = nil, page: Int? = nil) -> String {
|
||||
let requestId = "\(budgetId)-\(categoryId ?? "all")"
|
||||
self.transactions[requestId] = .failure(.loading)
|
||||
|
||||
let budgetIds: [String] = [budget.id]
|
||||
var categoryIds: [String] = []
|
||||
if category != nil {
|
||||
categoryIds.append(category!.id)
|
||||
if let categoryId = categoryId {
|
||||
categoryIds.append(categoryId)
|
||||
}
|
||||
self.currentRequest = self.transactionRepository.getTransactions(
|
||||
budgetIds: budgetIds,
|
||||
budgetIds: [budgetId],
|
||||
categoryIds: categoryIds,
|
||||
from: Date(timeIntervalSince1970: 0),
|
||||
count: count,
|
||||
|
@ -50,14 +46,17 @@ class TransactionDataStore: ObservableObject {
|
|||
switch completion {
|
||||
case .finished:
|
||||
self.currentRequest = nil
|
||||
self.objectWillChange.send() // TODO: Remove this hack for updating dictionary values
|
||||
return
|
||||
case .failure(let error):
|
||||
print("Error loading transactions: \(error.name)")
|
||||
self.transactions = .failure(error)
|
||||
self.transactions[requestId] = .failure(error)
|
||||
}
|
||||
}, receiveValue: { (transactions) in
|
||||
self.transactions = .success(transactions)
|
||||
self.transactions[requestId] = .success(transactions)
|
||||
})
|
||||
|
||||
return requestId
|
||||
}
|
||||
|
||||
func getTransaction(_ transactionId: String) {
|
||||
|
@ -141,7 +140,7 @@ class TransactionDataStore: ObservableObject {
|
|||
|
||||
func reset() {
|
||||
self.transaction = .failure(.unknown)
|
||||
self.transactions = .failure(.loading)
|
||||
self.transactions = ["": .failure(.loading)]
|
||||
}
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
|
|
@ -37,22 +37,23 @@ struct TransactionDetailsView: View {
|
|||
.foregroundColor(.secondary)
|
||||
Spacer().frame(height: 20.0)
|
||||
LabeledField(label: "notes", value: transaction.description, showDivider: true)
|
||||
CategoryLineItem(self.dataStoreProvider, categoryId: transaction.categoryId)
|
||||
BudgetLineItem(self.dataStoreProvider, budgetId: transaction.budgetId)
|
||||
UserLineItem(self.dataStoreProvider, userId: transaction.createdBy)
|
||||
CategoryLineItem(transaction.categoryId)
|
||||
BudgetLineItem(transaction.budgetId)
|
||||
UserLineItem(transaction.createdBy)
|
||||
}.padding()
|
||||
}
|
||||
.navigationBarItems(trailing: NavigationLink(
|
||||
destination: TransactionEditView(
|
||||
self.dataStoreProvider,
|
||||
transaction: transaction,
|
||||
transaction,
|
||||
shouldNavigateUp: self.$shouldNavigateUp
|
||||
).navigationBarTitle("edit")
|
||||
) {
|
||||
Text("edit")
|
||||
}))
|
||||
case .failure(.loading):
|
||||
return AnyView(EmbeddedLoadingView())
|
||||
return AnyView(EmbeddedLoadingView().onAppear {
|
||||
self.transactionDataStore.getTransaction(self.transactionId)
|
||||
})
|
||||
case.failure(.deleted):
|
||||
self.presentationMode.wrappedValue.dismiss()
|
||||
return AnyView(EmptyView())
|
||||
|
@ -61,15 +62,10 @@ struct TransactionDetailsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
let transactionId: String
|
||||
@ObservedObject var transactionDataStore: TransactionDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, transactionId: String) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
let transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
self.transactionDataStore = transactionDataStore
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
init(_ transactionId: String) {
|
||||
self.transactionId = transactionId
|
||||
self.transactionDataStore.getTransaction(self.transactionId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +92,11 @@ struct LabeledField: View {
|
|||
|
||||
struct CategoryLineItem: View {
|
||||
var body: some View {
|
||||
stateContent
|
||||
stateContent.onAppear {
|
||||
if let id = self.categoryId {
|
||||
categoryDataStore.getCategory(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
|
@ -108,19 +108,18 @@ struct CategoryLineItem: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ObservedObject var categoryDataStore: CategoryDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, categoryId: String?) {
|
||||
let categoryDataStore = dataStoreProvider.categoryDataStore()
|
||||
if let id = categoryId {
|
||||
categoryDataStore.getCategory(id)
|
||||
}
|
||||
self.categoryDataStore = categoryDataStore
|
||||
@EnvironmentObject var categoryDataStore: CategoryDataStore
|
||||
let categoryId: String?
|
||||
init(_ categoryId: String?) {
|
||||
self.categoryId = categoryId
|
||||
}
|
||||
}
|
||||
|
||||
struct BudgetLineItem: View {
|
||||
var body: some View {
|
||||
stateContent
|
||||
stateContent.onAppear {
|
||||
budgetDataStore.getBudget(budgetId)
|
||||
}
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
|
@ -132,17 +131,18 @@ struct BudgetLineItem: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ObservedObject var budgetDataStore: BudgetsDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, budgetId: String) {
|
||||
let budgetDataStore = dataStoreProvider.budgetsDataStore()
|
||||
budgetDataStore.getBudget(budgetId)
|
||||
self.budgetDataStore = budgetDataStore
|
||||
@EnvironmentObject var budgetDataStore: BudgetsDataStore
|
||||
let budgetId: String
|
||||
init(_ budgetId: String) {
|
||||
self.budgetId = budgetId
|
||||
}
|
||||
}
|
||||
|
||||
struct UserLineItem: View {
|
||||
var body: some View {
|
||||
stateContent
|
||||
stateContent.onAppear {
|
||||
userDataStore.getUser(userId)
|
||||
}
|
||||
}
|
||||
|
||||
var stateContent: AnyView {
|
||||
|
@ -154,18 +154,17 @@ struct UserLineItem: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ObservedObject var userDataStore: UserDataStore
|
||||
init(_ dataStoreProvider: DataStoreProvider, userId: String) {
|
||||
let userDataStore = dataStoreProvider.userDataStore()
|
||||
userDataStore.getUser(userId)
|
||||
self.userDataStore = userDataStore
|
||||
@EnvironmentObject var userDataStore: UserDataStore
|
||||
let userId: String
|
||||
init(_ userId: String) {
|
||||
self.userId = userId
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct TransactionDetailsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TransactionDetailsView(MockDataStoreProvider(), transactionId: "2")
|
||||
TransactionDetailsView("2")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -17,7 +17,11 @@ struct TransactionEditView: View {
|
|||
@State var type: TransactionType
|
||||
@State var budgetId: String
|
||||
@State var categoryId: String
|
||||
let createdBy: String
|
||||
var createdBy: String {
|
||||
get {
|
||||
try! authDataStore.currentUser.get().id
|
||||
}
|
||||
}
|
||||
let id: String?
|
||||
var shouldNavigateUp: Binding<Bool>
|
||||
|
||||
|
@ -38,7 +42,6 @@ struct TransactionEditView: View {
|
|||
type: self.$type,
|
||||
budgetId: self.$budgetId,
|
||||
categoryId: self.$categoryId,
|
||||
dataStoreProvider: self.dataStoreProvider,
|
||||
deleteAction: {
|
||||
self.transactionDataStore.deleteTransaction(self.id!)
|
||||
}
|
||||
|
@ -64,12 +67,9 @@ struct TransactionEditView: View {
|
|||
})
|
||||
}
|
||||
|
||||
@ObservedObject var transactionDataStore: TransactionDataStore
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
init(_ dataStoreProvider: DataStoreProvider, transaction: Transaction, shouldNavigateUp: Binding<Bool>) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
self.createdBy = try! dataStoreProvider.authenticationDataStore().currentUser.get().id
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
@EnvironmentObject var authDataStore: AuthenticationDataStore
|
||||
init(_ transaction: Transaction, shouldNavigateUp: Binding<Bool>) {
|
||||
self.id = transaction.id
|
||||
self._title = State<String>(initialValue: transaction.title)
|
||||
self._description = State<String>(initialValue: transaction.description ?? "")
|
||||
|
@ -85,7 +85,7 @@ struct TransactionEditView: View {
|
|||
#if DEBUG
|
||||
struct TransactionEditView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TransactionEditView(MockDataStoreProvider(), transaction: MockTransactionRepository.transaction, shouldNavigateUp: .constant(false))
|
||||
TransactionEditView(MockTransactionRepository.transaction, shouldNavigateUp: .constant(false))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -10,20 +10,25 @@ import SwiftUI
|
|||
import Combine
|
||||
|
||||
struct TransactionListView: View {
|
||||
@ObservedObject var transactionDataStore: TransactionDataStore
|
||||
@EnvironmentObject var transactionDataStore: TransactionDataStore
|
||||
@State var requestId: String = ""
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
switch transactionDataStore.transactions {
|
||||
switch transactionDataStore.transactions[requestId] {
|
||||
case .success(let transactions):
|
||||
Section {
|
||||
List(transactions) { transaction in
|
||||
TransactionListItemView(self.dataStoreProvider, transaction: transaction)
|
||||
TransactionListItemView(transaction)
|
||||
}
|
||||
}
|
||||
case .failure(.loading):
|
||||
VStack {
|
||||
ActivityIndicator(isAnimating: .constant(true), style: .large)
|
||||
}.onAppear {
|
||||
if self.requestId == "" {
|
||||
self.requestId = transactionDataStore.getTransactions(self.budget.id, categoryId: self.category?.id)
|
||||
}
|
||||
}
|
||||
default:
|
||||
// TODO: Handle each network failure type
|
||||
|
@ -31,25 +36,20 @@ struct TransactionListView: View {
|
|||
}
|
||||
}
|
||||
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
let budget: Budget
|
||||
let category: Category?
|
||||
init(_ dataStoreProvider: DataStoreProvider, budget: Budget, category: Category? = nil) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
self.transactionDataStore = dataStoreProvider.transactionDataStore()
|
||||
init(_ budget: Budget, category: Category? = nil) {
|
||||
self.budget = budget
|
||||
self.category = category
|
||||
self.transactionDataStore.getTransactions(self.budget, category: self.category)
|
||||
}
|
||||
}
|
||||
|
||||
struct TransactionListItemView: View {
|
||||
var transaction: Transaction
|
||||
let dataStoreProvider: DataStoreProvider
|
||||
|
||||
var body: some View {
|
||||
NavigationLink(
|
||||
destination: TransactionDetailsView(self.dataStoreProvider, transactionId: transaction.id)
|
||||
destination: TransactionDetailsView(transaction.id)
|
||||
.navigationBarTitle("details", displayMode: .inline)
|
||||
) {
|
||||
HStack {
|
||||
|
@ -74,8 +74,7 @@ struct TransactionListItemView: View {
|
|||
}
|
||||
}
|
||||
|
||||
init (_ dataStoreProvider: DataStoreProvider, transaction: Transaction) {
|
||||
self.dataStoreProvider = dataStoreProvider
|
||||
init (_ transaction: Transaction) {
|
||||
self.transaction = transaction
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue