From ba1ff0144e1c7976b351d1bc70564a8c23ee6afd Mon Sep 17 00:00:00 2001 From: Amisha Date: Thu, 28 Mar 2024 14:51:03 +0530 Subject: [PATCH] Remove member and its related classes --- Data/Data.xcodeproj/project.pbxproj | 12 -- Data/Data/DI/AppAssembly.swift | 8 - Data/Data/Model/Expense.swift | 4 +- Data/Data/Model/Groups.swift | 4 +- Data/Data/Model/Member.swift | 36 ---- Data/Data/Repository/GroupRepository.swift | 169 ++++-------------- Data/Data/Repository/MemberRepository.swift | 38 ---- Data/Data/Store/MemberStore.swift | 143 --------------- .../UI/Home/Expense/AddExpenseViewModel.swift | 1 - .../Detail Selection/ChooseGroupView.swift | 25 ++- .../ChooseGroupViewModel.swift | 5 +- .../ChoosePayerViewModel.swift | 21 +-- .../Add Member/JoinMemberViewModel.swift | 32 ++-- .../Groups/Create Group/CreateGroupView.swift | 2 +- .../Create Group/CreateGroupViewModel.swift | 3 +- 15 files changed, 74 insertions(+), 429 deletions(-) delete mode 100644 Data/Data/Model/Member.swift delete mode 100644 Data/Data/Repository/MemberRepository.swift delete mode 100644 Data/Data/Store/MemberStore.swift diff --git a/Data/Data.xcodeproj/project.pbxproj b/Data/Data.xcodeproj/project.pbxproj index 58025b70..9412250c 100644 --- a/Data/Data.xcodeproj/project.pbxproj +++ b/Data/Data.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ D89DBE532B8DC9F700E5F1BD /* AppRoute.swift in Sources */ = {isa = PBXBuildFile; fileRef = D89DBE522B8DC9F700E5F1BD /* AppRoute.swift */; }; D8A7CA752BA5AB670014EC67 /* UserStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A7CA742BA5AB670014EC67 /* UserStore.swift */; }; D8A7CA772BA5AB800014EC67 /* GroupStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A7CA762BA5AB800014EC67 /* GroupStore.swift */; }; - D8A7CA792BA5AB920014EC67 /* MemberStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A7CA782BA5AB920014EC67 /* MemberStore.swift */; }; D8A7CA7B2BA5B6AC0014EC67 /* ShareCodeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A7CA7A2BA5B6AC0014EC67 /* ShareCodeStore.swift */; }; D8A7CA802BA867F80014EC67 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8A7CA7F2BA867F80014EC67 /* String+Extension.swift */; }; D8AC25BB2B7F327A00CEAAD3 /* SplitoPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8AC25BA2B7F327A00CEAAD3 /* SplitoPreference.swift */; }; @@ -33,8 +32,6 @@ D8AC25C32B7F390B00CEAAD3 /* AppAssembly.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8AC25C22B7F390B00CEAAD3 /* AppAssembly.swift */; }; D8D14A522BA0917D00F45FF2 /* SharedCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D14A512BA0917D00F45FF2 /* SharedCode.swift */; }; D8D14A542BA092F500F45FF2 /* ShareCodeRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D14A532BA092F400F45FF2 /* ShareCodeRepository.swift */; }; - D8D14A5A2BA1E6E400F45FF2 /* MemberRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D14A592BA1E6E400F45FF2 /* MemberRepository.swift */; }; - D8D14A5C2BA1E73600F45FF2 /* Member.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D14A5B2BA1E73600F45FF2 /* Member.swift */; }; D8D42AA92B872726009B345D /* DDLogProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D42AA82B872726009B345D /* DDLogProvider.swift */; }; D8DF8BBF2B7A315100165138 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8DF8BB62B7A315000165138 /* Data.framework */; }; D8DF8BC42B7A315100165138 /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8DF8BC32B7A315100165138 /* DataTests.swift */; }; @@ -71,7 +68,6 @@ D89DBE522B8DC9F700E5F1BD /* AppRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRoute.swift; sourceTree = ""; }; D8A7CA742BA5AB670014EC67 /* UserStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserStore.swift; sourceTree = ""; }; D8A7CA762BA5AB800014EC67 /* GroupStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupStore.swift; sourceTree = ""; }; - D8A7CA782BA5AB920014EC67 /* MemberStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberStore.swift; sourceTree = ""; }; D8A7CA7A2BA5B6AC0014EC67 /* ShareCodeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareCodeStore.swift; sourceTree = ""; }; D8A7CA7F2BA867F80014EC67 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; D8AC25BA2B7F327A00CEAAD3 /* SplitoPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitoPreference.swift; sourceTree = ""; }; @@ -80,8 +76,6 @@ D8AC25C22B7F390B00CEAAD3 /* AppAssembly.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAssembly.swift; sourceTree = ""; }; D8D14A512BA0917D00F45FF2 /* SharedCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedCode.swift; sourceTree = ""; }; D8D14A532BA092F400F45FF2 /* ShareCodeRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareCodeRepository.swift; sourceTree = ""; }; - D8D14A592BA1E6E400F45FF2 /* MemberRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberRepository.swift; sourceTree = ""; }; - D8D14A5B2BA1E73600F45FF2 /* Member.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Member.swift; sourceTree = ""; }; D8D42A9E2B870BBA009B345D /* BaseStyle.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BaseStyle.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D8D42AA82B872726009B345D /* DDLogProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDLogProvider.swift; sourceTree = ""; }; D8DF8BB62B7A315000165138 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -140,7 +134,6 @@ children = ( D89DBE452B8CBE0F00E5F1BD /* UserRepository.swift */, D83B15082B999789004A5F4F /* GroupRepository.swift */, - D8D14A592BA1E6E400F45FF2 /* MemberRepository.swift */, D85E86E42BAB088F002EDF76 /* ExpenseRepository.swift */, D8D14A532BA092F400F45FF2 /* ShareCodeRepository.swift */, ); @@ -153,7 +146,6 @@ D89DBE272B88802800E5F1BD /* Country.swift */, D89DBE472B8CBE4C00E5F1BD /* AppUser.swift */, D83B15042B9996C0004A5F4F /* Groups.swift */, - D8D14A5B2BA1E73600F45FF2 /* Member.swift */, D85E86DD2BAB0292002EDF76 /* Expense.swift */, D8D14A512BA0917D00F45FF2 /* SharedCode.swift */, ); @@ -199,7 +191,6 @@ D8AC25BA2B7F327A00CEAAD3 /* SplitoPreference.swift */, D8A7CA742BA5AB670014EC67 /* UserStore.swift */, D8A7CA762BA5AB800014EC67 /* GroupStore.swift */, - D8A7CA782BA5AB920014EC67 /* MemberStore.swift */, D8A7CA7A2BA5B6AC0014EC67 /* ShareCodeStore.swift */, ); path = Store; @@ -480,7 +471,6 @@ D8A7CA802BA867F80014EC67 /* String+Extension.swift in Sources */, D89DBE282B88802800E5F1BD /* Country.swift in Sources */, D83B15092B999789004A5F4F /* GroupRepository.swift in Sources */, - D8D14A5A2BA1E6E400F45FF2 /* MemberRepository.swift in Sources */, D89DBE532B8DC9F700E5F1BD /* AppRoute.swift in Sources */, D8A7CA752BA5AB670014EC67 /* UserStore.swift in Sources */, D89DBE402B8C9E7400E5F1BD /* RouterView.swift in Sources */, @@ -488,8 +478,6 @@ D8A7CA772BA5AB800014EC67 /* GroupStore.swift in Sources */, D8AC25BE2B7F359B00CEAAD3 /* FirebaseProvider.swift in Sources */, D89DBE1D2B872F0B00E5F1BD /* NonceGenerator.swift in Sources */, - D8D14A5C2BA1E73600F45FF2 /* Member.swift in Sources */, - D8A7CA792BA5AB920014EC67 /* MemberStore.swift in Sources */, D85E86DE2BAB0292002EDF76 /* Expense.swift in Sources */, D8D42AA92B872726009B345D /* DDLogProvider.swift in Sources */, ); diff --git a/Data/Data/DI/AppAssembly.swift b/Data/Data/DI/AppAssembly.swift index 98e8aca2..b494b511 100644 --- a/Data/Data/DI/AppAssembly.swift +++ b/Data/Data/DI/AppAssembly.swift @@ -39,10 +39,6 @@ public class AppAssembly: Assembly { GroupStore.init() }.inObjectScope(.container) - container.register(MemberStore.self) { _ in - MemberStore.init() - }.inObjectScope(.container) - container.register(ShareCodeStore.self) { _ in ShareCodeStore.init() }.inObjectScope(.container) @@ -55,10 +51,6 @@ public class AppAssembly: Assembly { GroupRepository.init() }.inObjectScope(.container) - container.register(MemberRepository.self) { _ in - MemberRepository.init() - }.inObjectScope(.container) - container.register(ShareCodeRepository.self) { _ in ShareCodeRepository.init() }.inObjectScope(.container) diff --git a/Data/Data/Model/Expense.swift b/Data/Data/Model/Expense.swift index 2afdac0b..1e5aaea3 100644 --- a/Data/Data/Model/Expense.swift +++ b/Data/Data/Model/Expense.swift @@ -15,10 +15,10 @@ public struct Expense: Codable { let amount: Double let date: Date let paidBy: AppUser - let splitTo: [Member] // Reference to users involved in the split + let splitTo: [String] // Reference to users involved in the split let splitType: SplitType - public init(name: String, amount: Double, date: Date, paidBy: AppUser, splitTo: [Member], splitType: SplitType) { + public init(name: String, amount: Double, date: Date, paidBy: AppUser, splitTo: [String], splitType: SplitType) { self.name = name self.amount = amount self.date = date diff --git a/Data/Data/Model/Groups.swift b/Data/Data/Model/Groups.swift index 18028f5f..5b8c4e86 100644 --- a/Data/Data/Model/Groups.swift +++ b/Data/Data/Model/Groups.swift @@ -13,11 +13,11 @@ public struct Groups: Codable, Identifiable { public var name: String public var createdBy: String - public var members: [Member] + public var members: [String] public var imageUrl: String? public var createdAt: Timestamp - public init(name: String, createdBy: String, members: [Member], imageUrl: String? = nil, createdAt: Timestamp) { + public init(name: String, createdBy: String, members: [String], imageUrl: String? = nil, createdAt: Timestamp) { self.name = name self.createdBy = createdBy self.members = members diff --git a/Data/Data/Model/Member.swift b/Data/Data/Model/Member.swift deleted file mode 100644 index 1e594386..00000000 --- a/Data/Data/Model/Member.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// Member.swift -// Data -// -// Created by Amisha Italiya on 13/03/24. -// - -import FirebaseFirestore - -public struct Member: Codable { - - @DocumentID public var id: String? // Automatically generated ID by Firestore - - public var userId: String - public var groupId: String - public var totalBalance: Double? - public var owesToOthers: Double? - public var owedByOthers: Double? - - public init(userId: String, groupId: String, totalBalance: Double? = nil, owesToOthers: Double? = nil, owedByOthers: Double? = nil) { - self.userId = userId - self.groupId = groupId - self.totalBalance = totalBalance - self.owesToOthers = owesToOthers - self.owedByOthers = owedByOthers - } - - enum CodingKeys: String, CodingKey { - case id - case userId = "user_id" - case groupId = "group_id" - case totalBalance = "total_balance" - case owesToOthers = "owes_to_others" - case owedByOthers = "owed_by_others" - } -} diff --git a/Data/Data/Repository/GroupRepository.swift b/Data/Data/Repository/GroupRepository.swift index d228bdc7..c575ef5f 100644 --- a/Data/Data/Repository/GroupRepository.swift +++ b/Data/Data/Repository/GroupRepository.swift @@ -13,112 +13,44 @@ public class GroupRepository: ObservableObject { @Inject private var preference: SplitoPreference @Inject private var storageManager: StorageManager - @Inject private var memberRepository: MemberRepository private var cancelables = Set() public func createGroup(group: Groups, imageData: Data?) -> AnyPublisher { Future { [weak self] promise in - - guard let self else { return } - - self.createGroupInStore(group: group) - .flatMap { docId -> AnyPublisher<(String, Member), ServiceError> in - return self.addCreatorToMembers(groupId: docId) - .map { (docId, $0) } - .eraseToAnyPublisher() - } - .flatMap { docId, member -> AnyPublisher in - var newGroup = group - newGroup.id = docId - newGroup.members.append(member) - return self.finalizeGroupCreation(group: newGroup, imageData: imageData) - } - .sink { completion in - if case let .failure(error) = completion { - promise(.failure(error)) - } - } receiveValue: { docId in - promise(.success(docId)) - } - .store(in: &self.cancelables) - } - .eraseToAnyPublisher() - } - - private func createGroupInStore(group: Groups) -> AnyPublisher { - Future { [weak self] promise in - - guard let self else { - promise(.failure(.unexpectedError)) - return - } + guard let self else { promise(.failure(.unexpectedError)); return } self.store.createGroup(group: group) { docId in guard let docId else { promise(.failure(.databaseError)) return } - promise(.success(docId)) - } - }.eraseToAnyPublisher() - } - private func addCreatorToMembers(groupId: String) -> AnyPublisher { - Future { [weak self] promise in - - guard let self, let userId = self.preference.user?.id else { return } - var member = Member(userId: userId, groupId: groupId) - - self.memberRepository.addMemberToMembers(member: member) { memberId in - if let memberId { - member.id = memberId - promise(.success(member)) + if let imageData { + var newGroup = group + newGroup.id = docId + self.uploadImage(imageData: imageData, group: newGroup) + .sink { completion in + switch completion { + case .finished: + return + case .failure(let error): + promise(.failure(error)) + } + } receiveValue: { _ in + promise(.success(docId)) + }.store(in: &self.cancelables) } else { - promise(.failure(.databaseError)) + promise(.success(docId)) } } }.eraseToAnyPublisher() } - private func finalizeGroupCreation(group: Groups, imageData: Data?) -> AnyPublisher { - Future { [weak self] promise in - guard let self, let groupId = group.id else { return } - if let imageData { - self.uploadImage(imageData: imageData, group: group) - .sink { completion in - switch completion { - case .finished: - return - case .failure(let error): - promise(.failure(error)) - } - } receiveValue: { _ in - promise(.success(groupId)) - }.store(in: &self.cancelables) - } else { - self.updateGroup(group: group) - .sink { completion in - switch completion { - case .finished: - return - case .failure(let error): - promise(.failure(error)) - } - } receiveValue: { _ in - promise(.success(groupId)) - }.store(in: &self.cancelables) - } - }.eraseToAnyPublisher() - } - private func uploadImage(imageData: Data, group: Groups) -> AnyPublisher { Future { [weak self] promise in - guard let self, let groupId = group.id else { - promise(.failure(.unexpectedError)) - return - } + 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 { @@ -144,54 +76,6 @@ public class GroupRepository: ObservableObject { }.eraseToAnyPublisher() } - public func addMemberToGroup(groupId: String, memberId: String) -> AnyPublisher { - Future { [weak self] promise in - guard let self else { - promise(.failure(.unexpectedError)) - return - } - - self.fetchGroupBy(id: groupId) - .flatMap { group -> AnyPublisher<(Groups?, Member?), ServiceError> in - return self.fetchMemberWith(id: memberId) - .map { (group, $0) } - .eraseToAnyPublisher() - } - .flatMap { group, member -> AnyPublisher in - guard var group, let member else { - return Fail(error: .dataNotFound).eraseToAnyPublisher() - } - group.members.append(member) - return self.updateGroup(group: group) - } - .sink { completion in - if case let .failure(error) = completion { - promise(.failure(error)) - } - } receiveValue: { _ in - promise(.success(groupId)) - } - .store(in: &self.cancelables) - }.eraseToAnyPublisher() - } - - public func fetchMemberWith(id: String) -> AnyPublisher { - Future { [weak self] promise in - guard let self else { return } - self.memberRepository.fetchMemberBy(id: id) - .sink { completion in - switch completion { - case .failure(let error): - promise(.failure(error)) - case .finished: - break - } - } receiveValue: { member in - promise(.success(member)) - }.store(in: &self.cancelables) - }.eraseToAnyPublisher() - } - public func updateGroup(group: Groups) -> AnyPublisher { store.updateGroup(group: group) } @@ -199,21 +83,34 @@ public class GroupRepository: ObservableObject { public func fetchGroups(userId: String) -> AnyPublisher<[Groups], ServiceError> { Future { [weak self] promise in guard let self else { return } + self.store.fetchGroups(userId: userId) .sink { completion in if case .failure(let error) = completion { promise(.failure(error)) } - } receiveValue: { [weak self] groups in - guard let self else { return } + } receiveValue: { groups in // Show only those groups in which the user is part of - let filteredGroups = groups.filter { $0.createdBy == userId || $0.members.contains(where: { $0.userId == userId }) } + let filteredGroups = groups.filter { $0.createdBy == userId || $0.members.contains { $0 == userId } } promise(.success(filteredGroups)) }.store(in: &self.cancelables) }.eraseToAnyPublisher() } + public func addMemberToGroup(memberId: String, groupId: String) -> AnyPublisher { + return fetchGroupBy(id: groupId) + .flatMap { group -> AnyPublisher in + guard let group else { return Fail(error: .dataNotFound).eraseToAnyPublisher() } + + var newGroup = group + newGroup.members.append(memberId) + + return self.updateGroup(group: newGroup) + } + .eraseToAnyPublisher() + } + public func fetchGroupBy(id: String) -> AnyPublisher { store.fetchGroupBy(id: id) } diff --git a/Data/Data/Repository/MemberRepository.swift b/Data/Data/Repository/MemberRepository.swift deleted file mode 100644 index 93fa07e9..00000000 --- a/Data/Data/Repository/MemberRepository.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// MemberRepository.swift -// Data -// -// Created by Amisha Italiya on 13/03/24. -// - -import Combine - -public class MemberRepository: ObservableObject { - - @Inject private var store: MemberStore - - @Inject var preference: SplitoPreference - @Inject var codeRepository: ShareCodeRepository - - private var cancelables = Set() - - public func addMemberToMembers(member: Member, completion: @escaping (String?) -> Void) { - store.addMember(member: member, completion: completion) - } - - public func updateMember(member: Member) -> AnyPublisher { - store.updateMember(member: member) - } - - public func fetchMemberBy(id: String) -> AnyPublisher { - store.fetchMemberBy(id: id) - } - - public func fetchMembers() -> AnyPublisher<[Member], ServiceError> { - store.fetchMembers() - } - - public func fetchMembersByGroup(id: String) -> AnyPublisher<[Member], ServiceError> { - store.fetchMembersByGroup(id: id) - } -} diff --git a/Data/Data/Store/MemberStore.swift b/Data/Data/Store/MemberStore.swift deleted file mode 100644 index 32d69bad..00000000 --- a/Data/Data/Store/MemberStore.swift +++ /dev/null @@ -1,143 +0,0 @@ -// -// MemberStore.swift -// Data -// -// Created by Amisha Italiya on 16/03/24. -// - -import Combine -import FirebaseFirestoreInternal - -class MemberStore: ObservableObject { - - private let DATABASE_NAME: String = "members" - - @Inject private var database: Firestore - - func addMember(member: Member, completion: @escaping (String?) -> Void) { - do { - let member = try database.collection(DATABASE_NAME).addDocument(from: member) - completion(member.documentID) - return - } catch { - LogE("MemberStore :: \(#function) error: \(error.localizedDescription)") - } - completion(nil) - } - - func updateMember(member: Member) -> AnyPublisher { - Future { [weak self] promise in - guard let self, let docID = member.id else { - promise(.failure(.unexpectedError)) - return - } - do { - try self.database.collection(self.DATABASE_NAME).document(docID).setData(from: member, merge: true) - promise(.success(())) - } catch { - LogE("MemberStore :: \(#function) error: \(error.localizedDescription)") - promise(.failure(.databaseError)) - } - }.eraseToAnyPublisher() - } - - func fetchMemberBy(id: String) -> AnyPublisher { - Future { [weak self] promise in - guard let self else { - promise(.failure(.unexpectedError)) - return - } - - self.database.collection(DATABASE_NAME).document(id).getDocument { snapshot, error in - if let error { - LogE("MemberStore :: \(#function) error: \(error.localizedDescription)") - promise(.failure(.unexpectedError)) - return - } - - guard let snapshot else { - LogE("MemberStore :: \(#function) The document is not available.") - promise(.failure(.databaseError)) - return - } - - do { - let member = try snapshot.data(as: Member.self) - promise(.success(member)) - } catch { - LogE("MemberStore :: \(#function) Decode error: \(error.localizedDescription)") - promise(.failure(.decodingError)) - } - } - }.eraseToAnyPublisher() - } - - func fetchMembers() -> AnyPublisher<[Member], ServiceError> { - Future { [weak self] promise in - guard let self else { - promise(.failure(.unexpectedError)) - return - } - - self.database.collection(DATABASE_NAME).getDocuments { snapshot, error in - if let error { - LogE("MemberStore :: \(#function) error: \(error.localizedDescription)") - promise(.failure(.unexpectedError)) - return - } - - guard let snapshot else { - LogE("MemberStore :: \(#function) The document is not available.") - promise(.failure(.databaseError)) - return - } - - do { - let members = try snapshot.documents.compactMap { document -> Member? in - try document.data(as: Member.self) - } - promise(.success(members)) - } catch { - LogE("MemberStore :: \(#function) Decode error: \(error.localizedDescription)") - promise(.failure(.decodingError)) - } - } - }.eraseToAnyPublisher() - } - - func fetchMembersByGroup(id: String) -> AnyPublisher<[Member], ServiceError> { - Future { [weak self] promise in - guard let self else { - promise(.failure(.unexpectedError)) - return - } - - // Construct a Firestore query to fetch documents where group_id matches the provided groupId - self.database.collection(DATABASE_NAME) - .whereField("group_id", isEqualTo: id) - .getDocuments { snapshot, error in - if let error { - LogE("MemberStore :: \(#function) error: \(error.localizedDescription)") - promise(.failure(.unexpectedError)) - return - } - - guard let snapshot else { - LogE("MemberStore :: \(#function) The document is not available.") - promise(.failure(.databaseError)) - return - } - - do { - let members = try snapshot.documents.compactMap { document -> Member? in - try document.data(as: Member.self) - } - promise(.success(members)) - } catch { - LogE("MemberStore :: \(#function) Decode error: \(error.localizedDescription)") - promise(.failure(.decodingError)) - } - } - }.eraseToAnyPublisher() - } -} diff --git a/Splito/UI/Home/Expense/AddExpenseViewModel.swift b/Splito/UI/Home/Expense/AddExpenseViewModel.swift index e921a86c..bc416230 100644 --- a/Splito/UI/Home/Expense/AddExpenseViewModel.swift +++ b/Splito/UI/Home/Expense/AddExpenseViewModel.swift @@ -14,7 +14,6 @@ class AddExpenseViewModel: BaseViewModel, ObservableObject { @Published var expenseAmount = "" @Published var expenseDate = Date() - @Published var paidByMember: Member? @Published var selectedGroup: Groups? @Published var showGroupSelection = false diff --git a/Splito/UI/Home/Expense/Detail Selection/ChooseGroupView.swift b/Splito/UI/Home/Expense/Detail Selection/ChooseGroupView.swift index a3847f90..a14db568 100644 --- a/Splito/UI/Home/Expense/Detail Selection/ChooseGroupView.swift +++ b/Splito/UI/Home/Expense/Detail Selection/ChooseGroupView.swift @@ -17,13 +17,19 @@ struct ChooseGroupView: View { @Environment(\.dismiss) var dismiss var body: some View { - VStack(alignment: .center, spacing: 0) { + VStack(alignment: .center, spacing: 20) { if case .loading = viewModel.currentViewState { LoaderView(tintColor: primaryColor, scaleSize: 2) - } else if case .success(let groups) = viewModel.currentViewState { - ScrollView(showsIndicators: false) { - VSpacer(30) + } else if case .noGroups = viewModel.currentViewState { + NoGroupFoundView() + } else if case .hasGroups(let groups) = viewModel.currentViewState { + VSpacer(10) + + Text("Choose Group") + .font(.Header3()) + .foregroundColor(.primary) + ScrollView(showsIndicators: false) { LazyVStack(spacing: 16) { ForEach(groups) { group in GroupCellView(group: group, isSelected: group.id == viewModel.selectedGroup?.id) @@ -52,6 +58,17 @@ struct ChooseGroupView: View { } } +struct NoGroupFoundView: View { + + var body: some View { + VStack { + Text("You are not part of any group.") + .font(.subTitle1()) + .foregroundColor(primaryColor) + } + } +} + struct GroupCellView: View { var group: Groups diff --git a/Splito/UI/Home/Expense/Detail Selection/ChooseGroupViewModel.swift b/Splito/UI/Home/Expense/Detail Selection/ChooseGroupViewModel.swift index b918d548..f8c99cb7 100644 --- a/Splito/UI/Home/Expense/Detail Selection/ChooseGroupViewModel.swift +++ b/Splito/UI/Home/Expense/Detail Selection/ChooseGroupViewModel.swift @@ -38,7 +38,7 @@ class ChooseGroupViewModel: BaseViewModel, ObservableObject { self?.showToastFor(error) } } receiveValue: { [weak self] groups in - self?.currentViewState = .success(groups: groups) + self?.currentViewState = groups.isEmpty ? .noGroups : .hasGroups(groups: groups) self?.groups = groups } .store(in: &cancelables) @@ -54,6 +54,7 @@ extension ChooseGroupViewModel { enum ViewState { case initial case loading - case success(groups: [Groups]) + case hasGroups(groups: [Groups]) + case noGroups } } diff --git a/Splito/UI/Home/Expense/Detail Selection/ChoosePayerViewModel.swift b/Splito/UI/Home/Expense/Detail Selection/ChoosePayerViewModel.swift index e0276fcd..97ce7ecb 100644 --- a/Splito/UI/Home/Expense/Detail Selection/ChoosePayerViewModel.swift +++ b/Splito/UI/Home/Expense/Detail Selection/ChoosePayerViewModel.swift @@ -10,14 +10,9 @@ import Combine class ChoosePayerViewModel: BaseViewModel, ObservableObject { - @Inject var memberRepository: MemberRepository - @Published var groupId: String @Published var currentViewState: ViewState = .initial - @Published var members: [Member] = [] - @Published var selectedMember: Member? - init(groupId: String) { self.groupId = groupId super.init() @@ -27,20 +22,6 @@ class ChoosePayerViewModel: BaseViewModel, ObservableObject { func fetchMembers() { currentViewState = .loading - memberRepository.fetchMembersByGroup(id: groupId) - .sink { [weak self] completion in - switch completion { - case .finished: - return - case .failure(let error): - self?.currentViewState = .initial - self?.showToastFor(error) - } - } receiveValue: { members in - print("XXX --- Member: \(members.count)") - self.currentViewState = .success(member: members) - } - .store(in: &cancelables) } } @@ -48,6 +29,6 @@ extension ChoosePayerViewModel { enum ViewState { case initial case loading - case success(member: [Member]) + case success } } diff --git a/Splito/UI/Home/Groups/Add Member/JoinMemberViewModel.swift b/Splito/UI/Home/Groups/Add Member/JoinMemberViewModel.swift index db584135..2c28d24e 100644 --- a/Splito/UI/Home/Groups/Add Member/JoinMemberViewModel.swift +++ b/Splito/UI/Home/Groups/Add Member/JoinMemberViewModel.swift @@ -12,7 +12,6 @@ class JoinMemberViewModel: BaseViewModel, ObservableObject { @Inject var preference: SplitoPreference @Inject var groupRepository: GroupRepository - @Inject var memberRepository: MemberRepository @Inject var codeRepository: ShareCodeRepository @Published var code = "" @@ -64,32 +63,21 @@ class JoinMemberViewModel: BaseViewModel, ObservableObject { } } - // Add member to the collection func addMember(groupId: String, completion: @escaping () -> Void) { guard let userId = preference.user?.id else { return } currentState = .loading - let member = Member(userId: userId, groupId: groupId) - - memberRepository.addMemberToMembers(member: member) { [weak self] memberId in - guard let self, let memberId else { - self?.showAlertFor(message: "Something went wrong") - return - } - self.groupRepository.addMemberToGroup(groupId: groupId, memberId: memberId) - .sink { [weak self] result in - switch result { - case .failure(let error): - self?.currentState = .initial - self?.showToastFor(error) - completion() - case .finished: - self?.currentState = .initial - } - } receiveValue: { _ in + groupRepository.addMemberToGroup(memberId: userId, groupId: groupId) + .sink { [weak self] result in + if case .failure(let error) = result { + self?.currentState = .initial + self?.showToastFor(error) completion() - }.store(in: &self.cancelables) - } + } + } receiveValue: { _ in + self.currentState = .initial + completion() + }.store(in: &cancelables) } func goToGroupHome() { diff --git a/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift b/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift index 5d490924..8c7d731e 100644 --- a/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift +++ b/Splito/UI/Home/Groups/Create Group/CreateGroupView.swift @@ -55,7 +55,7 @@ struct CreateGroupView: View { image: $viewModel.profileImage, isPresented: $viewModel.showImagePicker) } .toolbar { - ToolbarItem(placement: .topBarLeading) { + ToolbarItem(placement: .topBarTrailing) { Button { viewModel.handleDoneAction() } label: { diff --git a/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift b/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift index 8c497beb..01009c76 100644 --- a/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift +++ b/Splito/UI/Home/Groups/Create Group/CreateGroupViewModel.swift @@ -24,7 +24,6 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject { @Inject var preference: SplitoPreference @Inject var storageManager: StorageManager @Inject var groupRepository: GroupRepository - @Inject var memberRepository: MemberRepository @Published var groupName = "" @Published var sourceTypeIsCamera = false @@ -87,7 +86,7 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject { func handleDoneAction() { currentState = .loading let userId = preference.user?.id ?? "" - let group = Groups(name: groupName.capitalized, createdBy: userId, members: [], imageUrl: nil, createdAt: Timestamp()) + let group = Groups(name: groupName.capitalized, createdBy: userId, members: [userId], imageUrl: nil, createdAt: Timestamp()) let resizedImage = profileImage?.aspectFittedToHeight(200) let imageData = resizedImage?.jpegData(compressionQuality: 0.2)