Skip to content

Commit

Permalink
Implement remove member, leave and delete group
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-amisha-i committed Apr 3, 2024
1 parent 0bbc456 commit 15ba9ce
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 94 deletions.
92 changes: 53 additions & 39 deletions Data/Data/Repository/GroupRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,45 +69,7 @@ public class GroupRepository: ObservableObject {
}.eraseToAnyPublisher()
}

public func addMemberToGroup(memberId: String, groupId: String) -> AnyPublisher<String, ServiceError> {
return fetchGroupBy(id: groupId)
.flatMap { group -> AnyPublisher<String, ServiceError> 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 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<String, ServiceError> {
public func updateGroup(group: Groups) -> AnyPublisher<Void, ServiceError> {
store.updateGroup(group: group)
}

Expand Down Expand Up @@ -173,4 +135,56 @@ public class GroupRepository: ObservableObject {
}
}.eraseToAnyPublisher()
}

public func addMemberToGroup(memberId: String, groupId: String) -> AnyPublisher<Void, ServiceError> {
return fetchGroupBy(id: groupId)
.flatMap { group -> AnyPublisher<Void, ServiceError> 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 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 removeMemberFrom(group: Groups, memberId: String) -> AnyPublisher<Void, ServiceError> {
var newGroup = group
newGroup.members.removeAll(where: { $0 == memberId })
if newGroup.members.isEmpty {
return deleteGroup(groupID: newGroup.id ?? "")
} else {
return updateGroup(group: newGroup)
}
}

public func deleteGroup(groupID: String) -> AnyPublisher<Void, ServiceError> {
store.deleteGroup(groupID: groupID)
}
}
9 changes: 9 additions & 0 deletions Data/Data/Router/RouterView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,13 @@ public class Router<T: Hashable>: ObservableObject {
public func popToRoot() {
paths = []
}

public func popTo(_ path: T, inclusive: Bool = false) {
if let index = paths.lastIndex(of: path) {
let endIndex = inclusive ? index + 1 : index
paths.removeSubrange(endIndex..<paths.endIndex)
} else {
LogD("Router: path not found.")
}
}
}
23 changes: 21 additions & 2 deletions Data/Data/Store/GroupStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ class GroupStore: ObservableObject {
completion(nil)
}

func updateGroup(group: Groups) -> AnyPublisher<String, ServiceError> {
func updateGroup(group: Groups) -> AnyPublisher<Void, ServiceError> {
Future { [weak self] promise in
guard let self, let docID = group.id else {
promise(.failure(.unexpectedError))
return
}
do {
try self.database.collection(self.DATABASE_NAME).document(docID).setData(from: group, merge: true)
promise(.success(docID))
promise(.success(()))
} catch {
LogE("GroupStore :: \(#function) error: \(error.localizedDescription)")
promise(.failure(.databaseError))
Expand Down Expand Up @@ -105,4 +105,23 @@ class GroupStore: ObservableObject {
}
}.eraseToAnyPublisher()
}

func deleteGroup(groupID: String) -> AnyPublisher<Void, ServiceError> {
Future { [weak self] promise in
guard let self else {
promise(.failure(.unexpectedError))
return
}

self.database.collection(DATABASE_NAME).document(groupID).delete { error in
if let error {
LogE("GroupStore :: \(#function) error: \(error.localizedDescription)")
promise(.failure(.databaseError))
} else {
promise(.success(()))
}
}
}
.eraseToAnyPublisher()
}
}
4 changes: 2 additions & 2 deletions Splito/UI/Home/Groups/Create Group/CreateGroupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ struct CreateGroupView: View {
if case .loading = viewModel.currentState {
LoaderView(tintColor: primaryColor, scaleSize: 2)
} else {
VStack(spacing: 40) {
VSpacer(30)
VStack {
VSpacer(40)

AddGroupNameView(image: viewModel.profileImage, imageUrl: viewModel.profileImageUrl,
groupName: $viewModel.groupName, handleProfileTap: viewModel.handleProfileTap)
Expand Down
127 changes: 90 additions & 37 deletions Splito/UI/Home/Groups/Group/Group Setting/GroupSettingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,22 @@ struct GroupSettingView: View {
VStack {
if case .loading = viewModel.currentViewState {
LoaderView(tintColor: primaryColor, scaleSize: 2)
} else if case .success(let group) = viewModel.currentViewState {
} else if case .initial = viewModel.currentViewState {
ScrollView(showsIndicators: false) {
VStack(alignment: .leading, spacing: 12) {
VStack(alignment: .leading, spacing: 30) {
VSpacer(20)

GroupTitleView(group: group)
.onTapGesture {
GroupTitleView(group: viewModel.group)
.onTouchGesture {
viewModel.handleEditGroupTap()
}

Divider()
.frame(height: 1)
.background(disableLightText)

VSpacer(10)

GroupMembersView(members: viewModel.members) {
viewModel.handleAddMemberTap()
GroupMembersView(members: viewModel.members, onAddMemberTap: viewModel.handleAddMemberTap) { user in
viewModel.handleMemberTap(member: user)
}

GroupAdvanceSettingsView(onLeaveGroupTap: viewModel.handleLeaveGroupTap,
onDeleteGroupTap: viewModel.handleLeaveGroupTap)
}
}
}
Expand All @@ -46,67 +43,123 @@ struct GroupSettingView: View {
.backport.alert(isPresented: $viewModel.showAlert, alertStruct: viewModel.alert)
.frame(maxWidth: isIpad ? 600 : nil, alignment: .center)
.navigationBarTitle("Group settings", displayMode: .inline)
.confirmationDialog("", isPresented: $viewModel.showLeaveGroupDialog, titleVisibility: .hidden) {
// Show disable when member has debt
Button("Leave Group") {
viewModel.showAlert = true
}
}
.confirmationDialog("", isPresented: $viewModel.showRemoveMemberDialog, titleVisibility: .hidden) {
// Show disable when member has debt
Button("Remove from group") {
viewModel.showAlert = true
}
}
}
}

private struct GroupTitleView: View {

let group: Groups
let group: Groups?

var body: some View {
HStack(alignment: .center, spacing: 16) {
GroupProfileImageView(imageUrl: group.imageUrl)
VStack(spacing: 10) {
HStack(alignment: .center, spacing: 16) {
GroupProfileImageView(imageUrl: group?.imageUrl)

Text(group.name)
.font(.subTitle1())
.foregroundColor(primaryText)
Text(group?.name ?? "")
.font(.subTitle1())
.foregroundColor(primaryText)

Spacer()
Spacer()

Text("Edit")
.font(.bodyBold(17))
.foregroundColor(primaryColor)
Text("Edit")
.font(.bodyBold(17))
.foregroundColor(primaryColor)
}
.padding(.horizontal, 22)

Divider()
.frame(height: 1)
.background(disableLightText)
}
.padding(.horizontal, 22)
}
}

private struct GroupMembersView: View {

var members: [AppUser]
var onAddMemberTap: () -> Void
var onMemberTap: (AppUser) -> Void

var body: some View {
VStack(alignment: .leading, spacing: 26) {
Text("Group members")
.font(.subTitle2())
.foregroundColor(primaryText)

HStack(spacing: 30) {
Image(systemName: "person.badge.plus")
.resizable()
.frame(width: 26, height: 26)

Text("Add people to group")
.font(.subTitle1())
}
.frame(height: 40)
.padding(.leading, 16)
.foregroundColor(primaryText)
.onTapGesture {
onAddMemberTap()
}
GroupListEditCellView(icon: "person.badge.plus", text: "Add people to group", onTap: onAddMemberTap)

LazyVStack(spacing: 20) {
ForEach(members) { member in
GroupMemberCellView(member: member)
.onTouchGesture {
onMemberTap(member)
}
}
}
}
.padding(.horizontal, 22)
}
}

private struct GroupAdvanceSettingsView: View {

var onLeaveGroupTap: () -> Void
var onDeleteGroupTap: () -> Void

var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text("Advanced settings")
.font(.subTitle2())
.foregroundColor(primaryText)

GroupListEditCellView(icon: "arrow.left.square", text: "Leave group",
isDistructive: true, onTap: onLeaveGroupTap)

GroupListEditCellView(icon: "trash", text: "Delete group",
isDistructive: true, onTap: onDeleteGroupTap)
}
.padding(.horizontal, 22)
}
}

private struct GroupListEditCellView: View {

var icon: String
var text: String
var isDistructive: Bool = false

var onTap: () -> Void

var body: some View {
HStack(spacing: 32) {
Image(systemName: icon)
.resizable()
.frame(width: 22, height: 22)

Text(text)
.font(.subTitle2())
}
.frame(height: 40)
.padding(.leading, 16)
.foregroundColor(isDistructive ? awarenessColor : primaryText)
.onTouchGesture {
onTap()
}
}
}

private struct GroupMemberCellView: View {

@Inject var preference: SplitoPreference
Expand Down
Loading

0 comments on commit 15ba9ce

Please sign in to comment.