Skip to content

Commit

Permalink
Fix expense view
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-amisha-i committed Mar 29, 2024
1 parent ba1ff01 commit 8f15815
Show file tree
Hide file tree
Showing 19 changed files with 253 additions and 164 deletions.
99 changes: 62 additions & 37 deletions Data/Data/Repository/GroupRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ public class GroupRepository: ObservableObject {
@Inject private var store: GroupStore

@Inject private var preference: SplitoPreference
@Inject private var userRepository: UserRepository
@Inject private var storageManager: StorageManager

private var cancelables = Set<AnyCancellable>()
private var cancelable = Set<AnyCancellable>()

public func createGroup(group: Groups, imageData: Data?) -> AnyPublisher<String, ServiceError> {
Future { [weak self] promise in
Expand All @@ -39,45 +40,16 @@ public class GroupRepository: ObservableObject {
}
} receiveValue: { _ in
promise(.success(docId))
}.store(in: &self.cancelables)
}.store(in: &self.cancelable)
} else {
promise(.success(docId))
}
}
}.eraseToAnyPublisher()
}

private func uploadImage(imageData: Data, group: Groups) -> AnyPublisher<Void, ServiceError> {
Future { [weak self] promise in

guard let self, let groupId = group.id else { promise(.failure(.unexpectedError)); return }

self.storageManager.uploadImage(for: .group, id: groupId, imageData: imageData) { url in
guard let url else {
promise(.failure(.databaseError))
return
}

var newGroup = group
newGroup.imageUrl = url

self.updateGroup(group: newGroup)
.sink { completion in
switch completion {
case .finished:
return
case .failure(let error):
promise(.failure(error))
}
} receiveValue: { _ in
promise(.success(()))
}.store(in: &self.cancelables)
}
}.eraseToAnyPublisher()
}

public func updateGroup(group: Groups) -> AnyPublisher<Void, ServiceError> {
store.updateGroup(group: group)
public func fetchGroupBy(id: String) -> AnyPublisher<Groups?, ServiceError> {
store.fetchGroupBy(id: id)
}

public func fetchGroups(userId: String) -> AnyPublisher<[Groups], ServiceError> {
Expand All @@ -93,8 +65,7 @@ public class GroupRepository: ObservableObject {
// Show only those groups in which the user is part of
let filteredGroups = groups.filter { $0.createdBy == userId || $0.members.contains { $0 == userId } }
promise(.success(filteredGroups))
}.store(in: &self.cancelables)

}.store(in: &self.cancelable)
}.eraseToAnyPublisher()
}

Expand All @@ -111,7 +82,61 @@ public class GroupRepository: ObservableObject {
.eraseToAnyPublisher()
}

public func fetchGroupBy(id: String) -> AnyPublisher<Groups?, ServiceError> {
store.fetchGroupBy(id: id)
public func fetchMembersBy(groupId: String) -> AnyPublisher<[AppUser], ServiceError> {
fetchGroupBy(id: groupId)
.flatMap { group -> AnyPublisher<[AppUser], ServiceError> in
guard let group else {
return Fail(error: .dataNotFound).eraseToAnyPublisher()
}

// Create a publisher for each member ID and fetch user data
let memberPublishers = group.members.map { (userId: String) -> AnyPublisher<AppUser?, ServiceError> in
return self.fetchMemberBy(userId: userId)
}

return Publishers.MergeMany(memberPublishers)
.compactMap { $0 }
.collect()
.mapError { $0 }
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}

public func fetchMemberBy(userId: String) -> AnyPublisher<AppUser?, ServiceError> {
userRepository.fetchUserBy(userID: userId)
}

public func updateGroup(group: Groups) -> AnyPublisher<Void, ServiceError> {
store.updateGroup(group: group)
}

private func uploadImage(imageData: Data, group: Groups) -> AnyPublisher<Void, ServiceError> {
Future { [weak self] promise in

guard let self, let groupId = group.id else { promise(.failure(.unexpectedError)); return }

self.storageManager.uploadImage(for: .group, id: groupId, imageData: imageData) { url in
guard let url else {
promise(.failure(.databaseError))
return
}

var newGroup = group
newGroup.imageUrl = url

self.updateGroup(group: newGroup)
.sink { completion in
switch completion {
case .finished:
return
case .failure(let error):
promise(.failure(error))
}
} receiveValue: { _ in
promise(.success(()))
}.store(in: &self.cancelable)
}
}.eraseToAnyPublisher()
}
}
4 changes: 2 additions & 2 deletions Data/Data/Repository/ShareCodeRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class ShareCodeRepository: ObservableObject {

@Inject private var store: ShareCodeStore

private var cancelables = Set<AnyCancellable>()
private var cancelable = Set<AnyCancellable>()

public func addSharedCode(sharedCode: SharedCode, completion: @escaping (String?) -> Void) {
store.addSharedCode(sharedCode: sharedCode, completion: completion)
Expand All @@ -38,6 +38,6 @@ public class ShareCodeRepository: ObservableObject {
}
} receiveValue: { code in
completion(code == nil)
}.store(in: &cancelables)
}.store(in: &cancelable)
}
}
69 changes: 31 additions & 38 deletions Data/Data/Repository/UserRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,41 @@ public class UserRepository: ObservableObject {

@Inject private var store: UserStore

private var cancelables = Set<AnyCancellable>()
private var cancelable = Set<AnyCancellable>()

public func storeUser(user: AppUser) -> AnyPublisher<AppUser, ServiceError> {
Future { [weak self] promise in

guard let self else {
promise(.failure(.unexpectedError))
return
return self.store.fetchUsers()
.flatMap { [weak self] users -> AnyPublisher<AppUser, ServiceError> in
guard let self else {
return Fail(error: .unexpectedError).eraseToAnyPublisher()
}

if let searchedUser = users.first(where: { $0.id == user.id }) {
return Just(searchedUser).setFailureType(to: ServiceError.self).eraseToAnyPublisher()
} else {
return self.store.addUser(user: user)
.mapError { error in
LogE("UserRepository :: \(#function) addUser failed, error: \(error.localizedDescription).")
return .databaseError
}
.map { _ in user }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}
.eraseToAnyPublisher()
}

self.store.fetchUsers()
.sink { completion in
switch completion {
case .finished:
LogD("UserRepository :: \(#function) storeUser finished.")
case .failure(let error):
LogE("UserRepository :: \(#function) storeUser failed, error: \(error.localizedDescription).")
promise(.failure(error))
}
} receiveValue: { [weak self] users in
guard let self else { return }
let searchedUser = users.first(where: { $0.id == user.id })

if let searchedUser {
promise(.success(searchedUser))
} else {
self.store.addUser(user: user)
.receive(on: DispatchQueue.main)
.sink { completion in
switch completion {
case .failure(let error):
LogE("UserRepository :: \(#function) addUser failed, error: \(error.localizedDescription).")
promise(.failure(error))
case .finished:
LogD("UserRepository :: \(#function) addUser finished.")
}
} receiveValue: { _ in
promise(.success(user))
}.store(in: &cancelables)
}
}.store(in: &cancelables)
}.eraseToAnyPublisher()
public func fetchUserBy(userID: String) -> AnyPublisher<AppUser?, ServiceError> {
return self.store.fetchUsers()
.map { users -> AppUser? in
return users.first(where: { $0.id == userID })
}
.mapError { error -> ServiceError in
LogE("UserRepository :: \(#function) fetchUserByID failed, error: \(error.localizedDescription).")
return .databaseError
}
.eraseToAnyPublisher()
}

public func updateUser(user: AppUser) -> AnyPublisher<Void, ServiceError> {
Expand Down
2 changes: 1 addition & 1 deletion Splito/UI/BaseViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ open class BaseViewModel {
@Published public var alert: AlertPrompt = .init(message: "")
@Published public var showAlert: Bool = false

public var cancelables = Set<AnyCancellable>()
public var cancelable = Set<AnyCancellable>()

public init() { }

Expand Down
18 changes: 13 additions & 5 deletions Splito/UI/Home/Expense/AddExpenseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,23 @@ struct AddExpenseView: View {
}
.padding(.trailing, 20)

PaidByView()
PaidByView(payerName: viewModel.selectedPayer?.firstName ?? "You") {
viewModel.showPayerSelection = viewModel.selectedGroup != nil
}
}
.padding(.horizontal, 20)
.background(backgroundColor)
.navigationBarTitle("Add an expense", displayMode: .inline)
.sheet(isPresented: $viewModel.showGroupSelection) {
ChooseGroupView(viewModel: ChooseGroupViewModel(selectedGroup: viewModel.selectedGroup) { group in
viewModel.selectedGroup = group
viewModel.selectedPayer = nil
})
}
.sheet(isPresented: $viewModel.showMemberSelection) {
ChoosePayerView(viewModel: ChoosePayerViewModel(groupId: viewModel.selectedGroup?.id ?? ""))
.sheet(isPresented: $viewModel.showPayerSelection) {
ChoosePayerView(viewModel: ChoosePayerViewModel(groupId: viewModel.selectedGroup?.id ?? "", selectedPayer: viewModel.selectedPayer) { payer in
viewModel.selectedPayer = payer
})
}
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Expand Down Expand Up @@ -135,16 +140,19 @@ struct GroupSelectionView: View {

struct PaidByView: View {

let payerName: String
var onTap: () -> Void

var body: some View {
HStack(spacing: 10) {
Text("Paid by")
.font(.subTitle2())
.foregroundColor(primaryText)

Button {

onTap()
} label: {
Text("you")
Text(payerName)
.font(.subTitle2())
.foregroundColor(secondaryText)
}
Expand Down
3 changes: 2 additions & 1 deletion Splito/UI/Home/Expense/AddExpenseViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ class AddExpenseViewModel: BaseViewModel, ObservableObject {
@Published var expenseDate = Date()

@Published var selectedGroup: Groups?
@Published var selectedPayer: AppUser?

@Published var showGroupSelection = false
@Published var showMemberSelection = false
@Published var showPayerSelection = false

@Published var openForGroupSelection = false

Expand Down
17 changes: 5 additions & 12 deletions Splito/UI/Home/Expense/Detail Selection/ChooseGroupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ struct ChooseGroupView: View {
.font(.Header3())
.foregroundColor(.primary)

VSpacer(10)

ScrollView(showsIndicators: false) {
LazyVStack(spacing: 16) {
ForEach(groups) { group in
Expand All @@ -44,21 +46,12 @@ struct ChooseGroupView: View {
}
.padding(.horizontal, 30)
.background(backgroundColor)
.navigationBarTitle("Choose group", displayMode: .inline)
.toastView(toast: $viewModel.toast)
.backport.alert(isPresented: $viewModel.showAlert, alertStruct: viewModel.alert)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
dismiss()
} label: {
Text("Cancel")
}
}
}
}
}

struct NoGroupFoundView: View {
private struct NoGroupFoundView: View {

var body: some View {
VStack {
Expand All @@ -69,7 +62,7 @@ struct NoGroupFoundView: View {
}
}

struct GroupCellView: View {
private struct GroupCellView: View {

var group: Groups
var isSelected: Bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class ChooseGroupViewModel: BaseViewModel, ObservableObject {
@Inject var preference: SplitoPreference
@Inject var groupRepository: GroupRepository

@Published var groups: [Groups] = []
@Published var selectedGroup: Groups?
@Published var currentViewState: ViewState = .initial

Expand All @@ -39,9 +38,8 @@ class ChooseGroupViewModel: BaseViewModel, ObservableObject {
}
} receiveValue: { [weak self] groups in
self?.currentViewState = groups.isEmpty ? .noGroups : .hasGroups(groups: groups)
self?.groups = groups
}
.store(in: &cancelables)
.store(in: &cancelable)
}

func handleGroupSelection(group: Groups) {
Expand Down
Loading

0 comments on commit 8f15815

Please sign in to comment.