Skip to content

Commit

Permalink
Add sections to the change role screen.
Browse files Browse the repository at this point in the history
Simplify implementation.
  • Loading branch information
pixlwave committed Mar 26, 2024
1 parent 91532f9 commit bacdf57
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 48 deletions.
4 changes: 4 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,7 @@
CCBEC2100CAF2EEBE9DB4156 /* TemplateScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA40B98B098B6F0371B750B3 /* TemplateScreenModels.swift */; };
CD0088B763CD970CF1CBF8CB /* DateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B5E97E9615A158C76B2AB77 /* DateTests.swift */; };
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; };
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */; };
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; };
CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; };
CE6F237360875D3D573FD0B2 /* RoomNotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6B522BD637845AB9570B10 /* RoomNotificationSettingsProxy.swift */; };
Expand Down Expand Up @@ -2009,6 +2010,7 @@
E9D059BFE329BE09B6D96A9F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
EA4D639E27D5882A6A71AECF /* GlobalSearchScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenViewModelTests.swift; sourceTree = "<group>"; };
EA880E78AF4BD24E45A7808C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = "<group>"; };
EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSection.swift; sourceTree = "<group>"; };
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = "<group>"; };
EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenModels.swift; sourceTree = "<group>"; };
EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3217,6 +3219,7 @@
children = (
6B2A421198FD20AAAED20004 /* RoomChangeRolesScreen.swift */,
23E6EB7960BC9D0F7396B3BD /* RoomChangeRolesScreenRow.swift */,
EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */,
3D9B45D584D232CB9E5C7734 /* RoomChangeRolesScreenSelectedItem.swift */,
);
path = View;
Expand Down Expand Up @@ -6121,6 +6124,7 @@
244407B18B2F2D6466BA5961 /* RoomChangeRolesScreenCoordinator.swift in Sources */,
7FF6E1FBE6E9517FD29A1D8E /* RoomChangeRolesScreenModels.swift in Sources */,
7F941B063C94E1718DFC2CF3 /* RoomChangeRolesScreenRow.swift in Sources */,
CDAE3A37D4DF136F9D07DB61 /* RoomChangeRolesScreenSection.swift in Sources */,
BD6685592716CA957D7BAAC4 /* RoomChangeRolesScreenSelectedItem.swift in Sources */,
3EC5A41F9FB7DD63A4DC6144 /* RoomChangeRolesScreenViewModel.swift in Sources */,
4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions ElementX/Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,9 @@
"screen_room_change_permissions_member_moderation" = "Member moderation";
"screen_room_change_permissions_messages_and_content" = "Messages and content";
"screen_room_change_permissions_room_details" = "Room details";
"screen_room_change_role_section_administrators" = "Admins";
"screen_room_change_role_section_moderators" = "Moderators";
"screen_room_change_role_section_users" = "Members";
"screen_room_details_invite_people_title" = "Invite people";
"screen_room_details_leave_conversation_title" = "Leave conversation";
"screen_room_details_leave_room_title" = "Leave room";
Expand Down
6 changes: 6 additions & 0 deletions ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,12 @@ internal enum L10n {
}
/// Edit Moderators
internal static var screenRoomChangeRoleModeratorsTitle: String { return L10n.tr("Localizable", "screen_room_change_role_moderators_title") }
/// Admins
internal static var screenRoomChangeRoleSectionAdministrators: String { return L10n.tr("Localizable", "screen_room_change_role_section_administrators") }
/// Moderators
internal static var screenRoomChangeRoleSectionModerators: String { return L10n.tr("Localizable", "screen_room_change_role_section_moderators") }
/// Members
internal static var screenRoomChangeRoleSectionUsers: String { return L10n.tr("Localizable", "screen_room_change_role_section_users") }
/// You have unsaved changes.
internal static var screenRoomChangeRoleUnsavedChangesDescription: String { return L10n.tr("Localizable", "screen_room_change_role_unsaved_changes_description") }
/// Save changes?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@ enum RoomChangeRolesScreenViewModelAction {
struct RoomChangeRolesScreenViewState: BindableState {
/// The screen's current mode (which role we are promoting/demoting users to/from.
let mode: RoomMemberDetails.Role
/// All of the room's members.
var members: [RoomMemberDetails]
/// All of the room's admins..
var administrators: [RoomMemberDetails]
/// All of the room's moderators.
var moderators: [RoomMemberDetails]
/// All of the room's regular users.
var users: [RoomMemberDetails]

var bindings: RoomChangeRolesScreenViewStateBindings

/// The members selected for promotion to the current role.
Expand All @@ -48,19 +53,24 @@ struct RoomChangeRolesScreenViewState: BindableState {
}
}

/// The visible members in the screen (after searching).
var visibleMembers: [RoomMemberDetails] {
guard !bindings.searchQuery.isEmpty else { return members }

return members.filter { member in
member.name?.localizedStandardContains(bindings.searchQuery) == true
|| member.id.localizedStandardContains(bindings.searchQuery)
}
/// The visible admins in the screen (after searching).
var visibleAdministrators: [RoomMemberDetails] {
administrators.filter { $0.matches(searchQuery: bindings.searchQuery) }
}

/// The visible mods in the screen (after searching).
var visibleModerators: [RoomMemberDetails] {
moderators.filter { $0.matches(searchQuery: bindings.searchQuery) }
}

/// The visible regular users in the screen (after searching).
var visibleUsers: [RoomMemberDetails] {
users.filter { $0.matches(searchQuery: bindings.searchQuery) }
}

/// All of the members who will gain/keep this screen's role after saving any changes.
var membersWithRole: [RoomMemberDetails] {
members.filter(isMemberSelected)
administrators.filter(isMemberSelected) + moderators.filter(isMemberSelected) + users.filter(isMemberSelected)
}

/// Whether or not any changes have been made to the members.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomCh
self.analytics = analytics

super.init(initialViewState: RoomChangeRolesScreenViewState(mode: mode,
members: [],
administrators: [],
moderators: [],
users: [],
bindings: .init()))

roomProxy.membersPublisher
Expand Down Expand Up @@ -82,10 +84,27 @@ class RoomChangeRolesScreenViewModel: RoomChangeRolesScreenViewModelType, RoomCh
// MARK: - Private

private func updateMembers(_ members: [RoomMemberProxyProtocol]) {
state.members = members.sorted().compactMap { member in
guard member.isActive else { return nil }
return RoomMemberDetails(withProxy: member)
var administrators = [RoomMemberDetails]()
var moderators = [RoomMemberDetails]()
var users = [RoomMemberDetails]()

for member in members.sorted() {
guard member.isActive else { continue }
let memberDetails = RoomMemberDetails(withProxy: member)

switch member.role {
case .administrator:
administrators.append(memberDetails)
case .moderator:
moderators.append(memberDetails)
case .user:
users.append(memberDetails)
}
}

state.administrators = administrators
state.moderators = moderators
state.users = users
}

private func toggleMember(_ member: RoomMemberDetails) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,20 @@ struct RoomChangeRolesScreen: View {
}
}

membersSection
RoomChangeRolesScreenSection(members: context.viewState.administrators,
title: L10n.screenRoomChangeRoleSectionAdministrators,
context: context)
RoomChangeRolesScreenSection(members: context.viewState.moderators,
title: L10n.screenRoomChangeRoleSectionModerators,
context: context)
RoomChangeRolesScreenSection(members: context.viewState.users,
title: L10n.screenRoomChangeRoleSectionUsers,
context: context)
}
}
}

@ViewBuilder
private var membersSection: some View {
if !context.viewState.visibleMembers.isEmpty {
Section {
ForEach(context.viewState.visibleMembers, id: \.id) { member in
RoomChangeRolesScreenRow(member: member,
imageProvider: context.imageProvider,
isSelected: context.viewState.isMemberSelected(member)) {
context.send(viewAction: .toggleMember(member))
}
.disabled(member.role == .administrator)
}
} header: {
Text(L10n.screenRoomMemberListRoomMembersHeaderTitle)
.compoundListSectionHeader()
}
} else {
Section.empty
}
}

@ScaledMetric private var cellWidth: CGFloat = 72

private var membersWithRoleSection: some View {
ScrollView(.horizontal, showsIndicators: false) {
ScrollViewReader { scrollView in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright 2024 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Compound
import SwiftUI

struct RoomChangeRolesScreenSection: View {
let members: [RoomMemberDetails]
let title: String

@ObservedObject var context: RoomChangeRolesScreenViewModel.Context

var body: some View {
if !members.isEmpty {
Section {
ForEach(members, id: \.id) { member in
RoomChangeRolesScreenRow(member: member,
imageProvider: context.imageProvider,
isSelected: context.viewState.isMemberSelected(member)) {
context.send(viewAction: .toggleMember(member))
}
.disabled(member.role == .administrator)
}
} header: {
Text(title)
.compoundListSectionHeader()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,3 @@ enum RoomMembersListScreenViewAction {
enum RoomMembersListScreenAlertType: Hashable {
case unbanConfirmation(RoomMemberDetails)
}

private extension RoomMemberDetails {
func matches(searchQuery: String) -> Bool {
guard !searchQuery.isEmpty else {
return true
}

return id.localizedCaseInsensitiveContains(searchQuery) || name?.localizedCaseInsensitiveContains(searchQuery) ?? false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ struct RoomMemberDetails: Identifiable, Hashable {

enum Role { case administrator, moderator, user }
let role: Role

func matches(searchQuery: String) -> Bool {
guard !searchQuery.isEmpty else { return true }
return id.localizedStandardContains(searchQuery) || name?.localizedStandardContains(searchQuery) == true
}
}

extension RoomMemberDetails {
Expand Down

0 comments on commit bacdf57

Please sign in to comment.