Use EnvironmentObject instead of constructor injection

This commit is contained in:
William Brawner 2021-10-05 19:26:54 -06:00
parent 491ce8c10f
commit 090343de0c
16 changed files with 194 additions and 217 deletions

View file

@ -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;

View file

@ -13,7 +13,7 @@ struct BudgetDetailsView: View {
ScrollView {
VStack {
// ProgressView(value: .constant(50.0, maxValue: 100))
CategoryListView(self.dataStoreProvider, budget: budget)
CategoryListView(budget)
}
}
}

View file

@ -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")
}
}.onAppear {
self.budgetsDataStore.getBudgets()
}
}
let dataStoreProvider: DataStoreProvider
init(_ dataStoreProvider: DataStoreProvider) {
self.dataStoreProvider = dataStoreProvider
self.budgetsDataStore = dataStoreProvider.budgetsDataStore()
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

View file

@ -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) {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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)

View file

@ -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)
}
}
//

View file

@ -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)
}
}

View file

@ -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)
}
Picker("prompt_budget", selection: self.budgetId) {
ForEach(budgets) { budget in
Text(budget.name)
}
)
}
default:
return AnyView(
Picker("prompt_budget", selection: self.budgetId) {
Text("")
}
)
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)
Picker("prompt_category", selection: self.categoryId) {
ForEach(categories) { category in
Text(category.title)
}
}.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)
}
}
case .failure(.loading):
VStack {
ActivityIndicator(isAnimating: .constant(true), style: .large)
}.onAppear {
if budgetId.wrappedValue != "" {
if !self.requestId.contains(budgetId.wrappedValue) {
self.requestId = categoryDataStore.getCategories(budgetId: self.budgetId.wrappedValue, expense: self.expense.wrappedValue == TransactionType.expense, count: nil, page: nil)
}
}
)
}
default:
return AnyView(
EmptyView()
)
EmptyView()
}
}
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")
if budgetId.wrappedValue != "" {
categoryDataStore.getCategories(budgetId: budgetId.wrappedValue, expense: expense.wrappedValue == TransactionType.expense, count: nil, page: nil)
}
self.categoryDataStore = categoryDataStore
@EnvironmentObject var categoryDataStore: CategoryDataStore
init(_ budgetId: Binding<String>, categoryId: Binding<String>, expense: Binding<TransactionType>) {
self.budgetId = budgetId
self.categoryId = categoryId
self.expense = expense
}
}

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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
}
}