diff --git a/ArtemisKit/Sources/Messages/ViewModels/MessageDetailViewModels/ProfileViewModel.swift b/ArtemisKit/Sources/Messages/ViewModels/MessageDetailViewModels/ProfileViewModel.swift index 592d08aa..1b6a5a42 100644 --- a/ArtemisKit/Sources/Messages/ViewModels/MessageDetailViewModels/ProfileViewModel.swift +++ b/ArtemisKit/Sources/Messages/ViewModels/MessageDetailViewModels/ProfileViewModel.swift @@ -22,11 +22,13 @@ class ProfileViewModel { let role: UserRole? private var login: String? let course: Course + var actions: [ProfileInfoSheetAction] - init(course: Course, user: ConversationUser, role: UserRole?) { + init(course: Course, user: ConversationUser, role: UserRole?, actions: [ProfileInfoSheetAction]) { self.course = course self.user = user self.role = role + self.actions = actions } // We can only create a conversation if we have the user's login diff --git a/ArtemisKit/Sources/Messages/Views/ConversationView/ConversationInfoSheetView.swift b/ArtemisKit/Sources/Messages/Views/ConversationView/ConversationInfoSheetView.swift index 3a70b286..2c495bf9 100644 --- a/ArtemisKit/Sources/Messages/Views/ConversationView/ConversationInfoSheetView.swift +++ b/ArtemisKit/Sources/Messages/Views/ConversationView/ConversationInfoSheetView.swift @@ -134,27 +134,32 @@ private extension ConversationInfoSheetView { } content: { members in ForEach(members, id: \.id) { member in if let name = member.name { - Menu { - if let login = member.login, - !(conversation.baseConversation is OneToOneChat) { - Button(R.string.localizable.sendMessage(), systemImage: "bubble.left.fill") { - viewModel.sendMessageToUser(with: login, navigationController: navigationController) { - dismiss() - } - } - } - Divider() - removeUserButton(member: member) - } label: { - HStack { - Text(name) - Spacer() - if UserSessionFactory.shared.user?.login == member.login { - Chip(text: R.string.localizable.youLabel(), backgroundColor: .Artemis.artemisBlue) - } + HStack { + ProfilePictureView( + user: member, + role: nil, + course: viewModel.course, + size: 25, + actions: [ + ProfileInfoSheetAction( + title: R.string.localizable.removeUserButtonLabel(), + iconName: "person.badge.minus", + isDestructive: true, + isEnabled: UserSessionFactory.shared.user?.login != member.login && viewModel.canRemoveUsers, + action: { + viewModel.isLoading = true + Task { + await viewModel.removeMemberFromConversation(member: member) + viewModel.isLoading = false + } + }) + ]) + Text(name) + Spacer() + if UserSessionFactory.shared.user?.login == member.login { + Chip(text: R.string.localizable.youLabel(), backgroundColor: .Artemis.artemisBlue) } } - .buttonStyle(.plain) .swipeActions(edge: .trailing) { removeUserButton(member: member) } @@ -180,6 +185,7 @@ private extension ConversationInfoSheetView { viewModel.isLoading = false } } + .foregroundStyle(.red) } } diff --git a/ArtemisKit/Sources/Messages/Views/MessageDetailView/ProfilePictureView.swift b/ArtemisKit/Sources/Messages/Views/MessageDetailView/ProfilePictureView.swift index cfb30035..0ae716ab 100644 --- a/ArtemisKit/Sources/Messages/Views/MessageDetailView/ProfilePictureView.swift +++ b/ArtemisKit/Sources/Messages/Views/MessageDetailView/ProfilePictureView.swift @@ -12,9 +12,11 @@ import SwiftUI struct ProfilePictureView: View { @State private var viewModel: ProfileViewModel + let size: CGFloat - init(user: ConversationUser, role: UserRole?, course: Course) { - self._viewModel = State(initialValue: ProfileViewModel(course: course, user: user, role: role)) + init(user: ConversationUser, role: UserRole?, course: Course, size: CGFloat = 44, actions: [ProfileInfoSheetAction] = []) { + self._viewModel = State(initialValue: ProfileViewModel(course: course, user: user, role: role, actions: actions)) + self.size = size } var body: some View { @@ -26,10 +28,10 @@ struct ProfilePictureView: View { DefaultProfilePictureView(viewModel: viewModel) } } else { - DefaultProfilePictureView(viewModel: viewModel) + DefaultProfilePictureView(viewModel: viewModel, font: size < 35 ? .caption.bold() : .headline.bold()) } } - .frame(width: 44, height: 44) + .frame(width: size, height: size) .clipShape(.rect(cornerRadius: .m)) .sheet(isPresented: $viewModel.showProfileSheet) { ProfileInfoSheet(viewModel: viewModel) @@ -121,6 +123,15 @@ struct ProfileInfoSheet: View { dismiss() } } + ForEach(viewModel.actions, id: \.hashValue) { action in + if action.isEnabled { + Button(action.title, systemImage: action.iconName) { + action.action() + dismiss() + } + .foregroundStyle(action.isDestructive ? Color.red : Color.blue) + } + } } } } @@ -165,3 +176,20 @@ struct ProfileInfoSheet: View { } } } + +struct ProfileInfoSheetAction: Hashable, Equatable { + static func == (lhs: ProfileInfoSheetAction, rhs: ProfileInfoSheetAction) -> Bool { + lhs.hashValue == rhs.hashValue + } + + let title: String + let iconName: String + let isDestructive: Bool + let isEnabled: Bool + let action: () -> Void + + func hash(into hasher: inout Hasher) { + hasher.combine(title) + hasher.combine(iconName) + } +}