diff --git a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift index 66883ca2a..b6c6f0471 100644 --- a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift +++ b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift @@ -91,6 +91,10 @@ extension AccessibilityTests { try await performAccessibilityAudit(named: "ComposerToolbar_Previews") } + func testCopyTextButton() async throws { + try await performAccessibilityAudit(named: "CopyTextButton_Previews") + } + func testCreateRoom() async throws { try await performAccessibilityAudit(named: "CreateRoom_Previews") } diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0b2286e60..b9585d7db 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -540,6 +540,7 @@ 6298AB0906DDD3525CD78C6B /* LoremSwiftum in Frameworks */ = {isa = PBXBuildFile; productRef = 1A6B622CCFDEFB92D9CF1CA5 /* LoremSwiftum */; }; 62A7FC3A0191BC7181AA432B /* AudioRecorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 907FA4DE17DEA1A3738EFB83 /* AudioRecorder.swift */; }; 62C5876C4254C58C2086F0DE /* HomeScreenContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */; }; + 633501761094E09DFBEBFFAD /* CopyTextButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B682FE2C44C5E163E7023B05 /* CopyTextButton.swift */; }; 63780F9DA06573E38A471ECA /* GenericCallLinkWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C202C1C7E330F124981A31 /* GenericCallLinkWidgetDriver.swift */; }; 6386EA3C898AD1A4BC1DC8A5 /* TimelineMediaPreviewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD40B92FCF20165658296AD /* TimelineMediaPreviewModifier.swift */; }; 639A0A27383EC655B0E81E95 /* SpaceScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459A3921046977CBF4F3C359 /* SpaceScreenViewModelProtocol.swift */; }; @@ -2458,6 +2459,7 @@ B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; B65DDCF8E41759890355ACBC /* AuthenticationStartScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenViewModelProtocol.swift; sourceTree = ""; }; + B682FE2C44C5E163E7023B05 /* CopyTextButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyTextButton.swift; sourceTree = ""; }; B68B31232312AFC844440BFE /* DeclineAndBlockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeclineAndBlockScreenModels.swift; sourceTree = ""; }; B69AEA8755382DB34892FB7B /* ThreadTimelineScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadTimelineScreenModels.swift; sourceTree = ""; }; B6A293D06BAB2B7A17D9314B /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = ""; }; @@ -3584,6 +3586,7 @@ 07934EF08BB39353E4A94272 /* BlurEffectView.swift */, 8CC23C63849452BC86EA2852 /* ButtonStyle.swift */, FEC4B431B0117BDEE697DB4A /* ComposerDisabledView.swift */, + B682FE2C44C5E163E7023B05 /* CopyTextButton.swift */, E2776E63E02719B20758EB78 /* EditRoomAddressListRow.swift */, 8F4F0AB250EFA7B71FB2BDB2 /* HorizontalHighlightGradient.swift */, F320003F490B11F808ECC5E9 /* JoinedMembersBadgeView.swift */, @@ -5848,6 +5851,13 @@ path = MapLibre; sourceTree = ""; }; + C18958141C8ED6D778F779A4 /* CreateRoom */ = { + isa = PBXGroup; + children = ( + ); + path = CreateRoom; + sourceTree = ""; + }; C1CD278862878F9545608040 /* SessionVerificationScreen */ = { isa = PBXGroup; children = ( @@ -6230,6 +6240,7 @@ 53FB148CD26AFB6A5B9E20B3 /* BugReportScreen */, 1185EECDD07495D65AC84AFC /* CallScreen */, 90DC2E28718955ED87AD1456 /* CreatePollScreen */, + C18958141C8ED6D778F779A4 /* CreateRoom */, 821EB0D1C0019E3C7BBAEDBB /* CreateRoomScreen */, 3E1CCC4B607946CE90B4A827 /* DeclineAndBlockScreen */, 45F2BCFD6E9A6F040CC20582 /* EditRoomAddressScreen */, @@ -7707,6 +7718,7 @@ EA6613B29BA671F39CE1B1D2 /* ConfirmationDialog.swift in Sources */, AC7AA215D60FBC307F984028 /* Consumable.swift in Sources */, C3522917C0C367C403429EEC /* CoordinatorProtocol.swift in Sources */, + 633501761094E09DFBEBFFAD /* CopyTextButton.swift in Sources */, CE8296D4AD30DDC6D0C67A74 /* CreateRoomScreen.swift in Sources */, D38E59C48BE5499A48D12031 /* CreateRoomScreenCoordinator.swift in Sources */, 9DBF6524DFD8143A4D6A17F0 /* CreateRoomScreenModels.swift in Sources */, diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index ce64c26e9..5ba9b3b5a 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -500,9 +500,11 @@ "screen_bottom_sheet_manage_room_member_ban_member_confirmation_action" = "Ban"; "screen_bottom_sheet_manage_room_member_ban_member_confirmation_description" = "They won’t be able to join again if invited."; "screen_bottom_sheet_manage_room_member_ban_member_confirmation_title" = "Are you sure you want to ban this member?"; +"screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description" = "They won’t be able to join this space again if invited, but they’ll still keep their memberships of any rooms or subspaces."; "screen_bottom_sheet_manage_room_member_banning_user" = "Banning %1$@"; "screen_bottom_sheet_manage_room_member_kick_member_confirmation_action" = "Remove"; "screen_bottom_sheet_manage_room_member_kick_member_confirmation_title" = "Are you sure you want to remove this member?"; +"screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description" = "They will be able to join this space again if invited, and they’ll still keep their memberships of any rooms or subspaces."; "screen_bottom_sheet_manage_room_member_member_user_info" = "View profile"; "screen_bottom_sheet_manage_room_member_remove" = "Remove user"; "screen_bottom_sheet_manage_room_member_remove_confirmation_title" = "Remove member and ban from joining in the future?"; diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 38d93e442..7d2abcdf4 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -1322,6 +1322,8 @@ internal enum L10n { internal static var screenBottomSheetManageRoomMemberBanMemberConfirmationDescription: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_ban_member_confirmation_description") } /// Are you sure you want to ban this member? internal static var screenBottomSheetManageRoomMemberBanMemberConfirmationTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_ban_member_confirmation_title") } + /// They won’t be able to join this space again if invited, but they’ll still keep their memberships of any rooms or subspaces. + internal static var screenBottomSheetManageRoomMemberBanMemberFromSpaceConfirmationDescription: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_ban_member_from_space_confirmation_description") } /// Banning %1$@ internal static func screenBottomSheetManageRoomMemberBanningUser(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_banning_user", String(describing: p1)) @@ -1332,6 +1334,8 @@ internal enum L10n { internal static var screenBottomSheetManageRoomMemberKickMemberConfirmationDescription: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_kick_member_confirmation_description") } /// Are you sure you want to remove this member? internal static var screenBottomSheetManageRoomMemberKickMemberConfirmationTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_kick_member_confirmation_title") } + /// They will be able to join this space again if invited, and they’ll still keep their memberships of any rooms or subspaces. + internal static var screenBottomSheetManageRoomMemberKickMemberFromSpaceConfirmationDescription: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_kick_member_from_space_confirmation_description") } /// View profile internal static var screenBottomSheetManageRoomMemberMemberUserInfo: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_member_user_info") } /// Remove user diff --git a/ElementX/Sources/Other/SwiftUI/Views/CopyTextButton.swift b/ElementX/Sources/Other/SwiftUI/Views/CopyTextButton.swift new file mode 100644 index 000000000..3b491649b --- /dev/null +++ b/ElementX/Sources/Other/SwiftUI/Views/CopyTextButton.swift @@ -0,0 +1,39 @@ +// +// Copyright 2025 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Compound +import SwiftUI + +/// A button that contains text that is copied on tap +struct CopyTextButton: View { + let content: String + + var body: some View { + Button { + UIPasteboard.general.string = content + } label: { + Label { + Text(content) + .lineLimit(1) + } icon: { + CompoundIcon(\.copy, size: .small, relativeTo: .compound.bodyLG) + .accessibilityHidden(true) + } + .font(.compound.bodyLG) + .foregroundStyle(.compound.textSecondary) + .labelStyle(.custom(spacing: 4, iconLayout: .trailing)) + } + .accessibilityHint(L10n.actionCopy) + } +} + +struct CopyTextButton_Previews: PreviewProvider, TestablePreview { + static var previews: some View { + CopyTextButton(content: "Copy me!") + .previewLayout(.sizeThatFits) + } +} diff --git a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift index 89166e769..0f46bb1a5 100644 --- a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift +++ b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift @@ -30,6 +30,7 @@ enum TestablePreviewsDictionary { "CollapsibleRoomTimelineView_Previews" : CollapsibleRoomTimelineView_Previews.self, "CompletionSuggestion_Previews" : CompletionSuggestion_Previews.self, "ComposerToolbar_Previews" : ComposerToolbar_Previews.self, + "CopyTextButton_Previews" : CopyTextButton_Previews.self, "CreateRoom_Previews" : CreateRoom_Previews.self, "DeactivateAccountScreen_Previews" : DeactivateAccountScreen_Previews.self, "DeclineAndBlockScreen_Previews" : DeclineAndBlockScreen_Previews.self, diff --git a/ElementX/Sources/Screens/ManageRoomMemberSheet/ManageRoomMemberSheetViewModel.swift b/ElementX/Sources/Screens/ManageRoomMemberSheet/ManageRoomMemberSheetViewModel.swift index da5680a4f..eb353a3b2 100644 --- a/ElementX/Sources/Screens/ManageRoomMemberSheet/ManageRoomMemberSheetViewModel.swift +++ b/ElementX/Sources/Screens/ManageRoomMemberSheet/ManageRoomMemberSheetViewModel.swift @@ -59,7 +59,7 @@ class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, Manage case .kick: state.bindings.alertInfo = .init(id: alertType, title: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationTitle, - message: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationDescription, + message: roomProxy.infoPublisher.value.isSpace ? L10n.screenBottomSheetManageRoomMemberKickMemberFromSpaceConfirmationDescription : L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationDescription, primaryButton: .init(title: L10n.actionCancel, role: .cancel) { }, secondaryButton: .init(title: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationAction) { [weak self] in Task { await self?.kickMember(id: memberID, name: memberName, reason: reason) } }, textFields: [.init(placeholder: L10n.commonReason, @@ -69,7 +69,7 @@ class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, Manage case .ban: state.bindings.alertInfo = .init(id: alertType, title: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationTitle, - message: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationDescription, + message: roomProxy.infoPublisher.value.isSpace ? L10n.screenBottomSheetManageRoomMemberBanMemberFromSpaceConfirmationDescription : L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationDescription, primaryButton: .init(title: L10n.actionCancel, role: .cancel) { }, secondaryButton: .init(title: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationAction) { [weak self] in Task { await self?.banMember(id: memberID, name: memberName, reason: reason) } }, textFields: [.init(placeholder: L10n.commonReason, diff --git a/ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift b/ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift index 1a80ab6aa..e56a13b91 100644 --- a/ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift +++ b/ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift @@ -28,6 +28,10 @@ struct SpaceHeaderView: View { .foregroundStyle(.compound.textPrimary) .multilineTextAlignment(.center) + if let alias = spaceRoomProxy.canonicalAlias { + CopyTextButton(content: alias) + } + spaceDetails JoinedMembersBadgeView(heroes: spaceRoomProxy.heroes, @@ -62,7 +66,7 @@ struct SpaceHeaderView: View { } } - var spaceDetails: some View { + private var spaceDetails: some View { Label { Text(spaceDetailsVisibilityTitle) .font(.compound.bodyLG) @@ -74,7 +78,7 @@ struct SpaceHeaderView: View { } } - var spaceDetailsVisibilityTitle: String { + private var spaceDetailsVisibilityTitle: String { switch spaceRoomProxy.visibility { case .public: L10n.commonPublicSpace case .private: L10n.commonPrivateSpace @@ -83,7 +87,7 @@ struct SpaceHeaderView: View { } } - var spaceDetailsVisibilityIcon: KeyPath { + private var spaceDetailsVisibilityIcon: KeyPath { switch spaceRoomProxy.visibility { case .public: \.public case .private: \.lock @@ -122,6 +126,7 @@ struct SpaceHeaderView_Previews: PreviewProvider, TestablePreview { childrenCount: 20, joinedMembersCount: 78, topic: "Description of the space goes right here.", + canonicalAlias: "#space:matrix.org", joinRule: .public)), SpaceRoomProxyMock(.init(id: "!space3:matrix.org", name: "Subspace", @@ -135,6 +140,7 @@ struct SpaceHeaderView_Previews: PreviewProvider, TestablePreview { "Sem amet enim habitant nibh augue mauris.", "Interdum mauris ultrices tincidunt proin morbi erat aenean risus nibh.", "Diam amet sit fermentum vulputate faucibus."].joined(separator: " "), + canonicalAlias: "#subspace:matrix.org", joinRule: .knockRestricted(rules: [.roomMembership(roomId: "")]))) ] } diff --git a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenModels.swift b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenModels.swift index f3954134b..ff19b7a0d 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenModels.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenModels.swift @@ -28,7 +28,12 @@ struct SpaceScreenViewState: BindableState { var selectedSpaceRoomID: String? var joiningRoomIDs: Set = [] - var isSpaceManagementEnabled = false + var canEditBaseInfo = false + var canEditRolesAndPermissions = false + + var isSpaceManagementEnabled: Bool { + canEditBaseInfo || canEditRolesAndPermissions + } var bindings = SpaceScreenViewStateBindings() } diff --git a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift index 27b53192a..bbcb62ca7 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceScreen/SpaceScreenViewModel.swift @@ -80,7 +80,17 @@ class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtoc } appSettings.$spaceSettingsEnabled - .weakAssign(to: \.state.isSpaceManagementEnabled, on: self) + .combineLatest(roomProxy.infoPublisher) + .sink { [weak self] isEnabled, roomInfo in + guard let self else { return } + guard isEnabled, let powerLevels = roomInfo.powerLevels else { + state.canEditBaseInfo = false + state.canEditRolesAndPermissions = false + return + } + state.canEditBaseInfo = powerLevels.canOwnUserEditBaseInfo() + state.canEditRolesAndPermissions = powerLevels.canOwnUserEditRolesAndPermissions() + } .store(in: &cancellables) } } diff --git a/ElementX/Sources/Screens/Spaces/SpaceScreen/View/LeaveSpaceView.swift b/ElementX/Sources/Screens/Spaces/SpaceScreen/View/LeaveSpaceView.swift index c0dcb9d9b..920d78881 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceScreen/View/LeaveSpaceView.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceScreen/View/LeaveSpaceView.swift @@ -97,7 +97,7 @@ struct LeaveSpaceView: View { Label(leaveHandle.confirmationTitle, icon: \.leave) } .buttonStyle(.compound(.primary)) - } else if context.viewState.isSpaceManagementEnabled { + } else if context.viewState.canEditRolesAndPermissions { Button { context.send(viewAction: .rolesAndPermissions) } label: { diff --git a/ElementX/Sources/Screens/Spaces/SpaceScreen/View/SpaceScreen.swift b/ElementX/Sources/Screens/Spaces/SpaceScreen/View/SpaceScreen.swift index a45c43780..ccea15ba9 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceScreen/View/SpaceScreen.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceScreen/View/SpaceScreen.swift @@ -114,6 +114,7 @@ struct SpaceScreen_Previews: PreviewProvider, TestablePreview { joinedMembersCount: 76, heroes: [.mockDan, .mockBob, .mockCharlie, .mockVerbose], topic: "Description of the space goes right here. Lorem ipsum dolor sit amet consectetur. Leo viverra morbi habitant in.", + canonicalAlias: "#engineering-team:element.io", joinRule: .knockRestricted(rules: [.roomMembership(roomId: "")]))) let spaceRoomListProxy = SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy, initialSpaceRooms: .mockSpaceList)) diff --git a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenModels.swift b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenModels.swift index 145cf08b6..19ce21982 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenModels.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenModels.swift @@ -15,6 +15,7 @@ struct SpaceSettingsScreenViewState: BindableState { var joinedMembersCount: Int var hasMemberIdentityVerificationStateViolations = false + var canEditBaseInfo = false var canEditRolesOrPermissions = false } diff --git a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenViewModel.swift b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenViewModel.swift index 7166760ef..40f875c33 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenViewModel.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/SpaceSettingsScreenViewModel.swift @@ -57,6 +57,7 @@ class SpaceSettingsScreenViewModel: SpaceSettingsScreenViewModelType, SpaceSetti if let powerLevels = roomInfo.powerLevels { state.canEditRolesOrPermissions = powerLevels.canOwnUserEditRolesAndPermissions() + state.canEditBaseInfo = powerLevels.canOwnUserEditBaseInfo() } } diff --git a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/View/SpaceSettingsScreen.swift b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/View/SpaceSettingsScreen.swift index 4e9ce6a44..cbd96fd66 100644 --- a/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/View/SpaceSettingsScreen.swift +++ b/ElementX/Sources/Screens/Spaces/SpaceSettingsScreen/View/SpaceSettingsScreen.swift @@ -26,17 +26,24 @@ struct SpaceSettingsScreen: View { private var editSection: some View { Section { - ListRow(kind: .custom { - Button { - context.send(viewAction: .processTapEdit) - } label: { - editSectionContent - } - }) + ListRow(kind: .custom { editRow }) } } - private var editSectionContent: some View { + @ViewBuilder + private var editRow: some View { + if context.viewState.canEditBaseInfo { + Button { + context.send(viewAction: .processTapEdit) + } label: { + editRowContent + } + } else { + editRowContent + } + } + + private var editRowContent: some View { HStack(spacing: 12) { RoomAvatarImage(avatar: context.viewState.details.avatar, avatarSize: .room(on: .spaceSettings), @@ -57,7 +64,9 @@ struct SpaceSettingsScreen: View { } .frame(maxWidth: .infinity, alignment: .leading) - ListRowAccessory.navigationLink + if context.viewState.canEditBaseInfo { + ListRowAccessory.navigationLink + } } .padding(.horizontal, ListRowPadding.horizontal) .padding(.vertical, 16) @@ -108,16 +117,29 @@ struct SpaceSettingsScreen: View { // MARK: - Previews struct SpaceSettingsScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = SpaceSettingsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space", - avatarURL: .mockMXCAvatar, - isSpace: true, - canonicalAlias: "#space:matrix.org", - members: .allMembersAsCreator)), - userSession: UserSessionMock(.init())) + static let ownerViewModel = SpaceSettingsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space", + avatarURL: .mockMXCAvatar, + isSpace: true, + canonicalAlias: "#space:matrix.org", + members: .allMembersAsCreator)), + userSession: UserSessionMock(.init())) + + static let userViewModel = SpaceSettingsScreenViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Space", + avatarURL: .mockMXCAvatar, + isSpace: true, + canonicalAlias: "#space:matrix.org", + members: .allMembers)), + userSession: UserSessionMock(.init())) static var previews: some View { NavigationStack { - SpaceSettingsScreen(context: viewModel.context) + SpaceSettingsScreen(context: ownerViewModel.context) } + .previewDisplayName("Owner") + + NavigationStack { + SpaceSettingsScreen(context: userViewModel.context) + } + .previewDisplayName("User") } } diff --git a/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift index 08ea98024..98178f495 100644 --- a/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomPowerLevelProxyProtocol.swift @@ -36,3 +36,12 @@ protocol RoomPowerLevelsProxyProtocol { func canUserPinOrUnpin(userID: String) -> Result func canUserJoinCall(userID: String) -> Result } + +// MARK: - Helpers + +extension RoomPowerLevelsProxyProtocol { + /// Can own user edit either the room name, avatar or topic + func canOwnUserEditBaseInfo() -> Bool { + canOwnUser(sendStateEvent: .roomAvatar) || canOwnUser(sendStateEvent: .roomName) || canOwnUser(sendStateEvent: .roomTopic) + } +} diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index 7d3f6d72b..a264312a5 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -137,6 +137,12 @@ extension PreviewTests { } } + func testCopyTextButton() async throws { + for (index, preview) in CopyTextButton_Previews._allPreviews.enumerated() { + try await assertSnapshots(matching: preview, step: index) + } + } + func testCreateRoom() async throws { for (index, preview) in CreateRoom_Previews._allPreviews.enumerated() { try await assertSnapshots(matching: preview, step: index) diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-en-GB-0.png new file mode 100644 index 000000000..12f9ddc91 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3d494a30652c54d4ab3d503eecfb1b849db78f7ea372d392f5b93efc2a3ecd7 +size 5941 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-pseudo-0.png new file mode 100644 index 000000000..12f9ddc91 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPad-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b3d494a30652c54d4ab3d503eecfb1b849db78f7ea372d392f5b93efc2a3ecd7 +size 5941 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-en-GB-0.png new file mode 100644 index 000000000..5e9595d0e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-en-GB-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:824417be28d83b6cf5a3200e3e792e639c648b3a6288bc29fe461b85b069a7ae +size 4544 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-pseudo-0.png new file mode 100644 index 000000000..5e9595d0e --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/copyTextButton.iPhone-16-pseudo-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:824417be28d83b6cf5a3200e3e792e639c648b3a6288bc29fe461b85b069a7ae +size 4544 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-en-GB-0.png index 0cd4d84a5..aab8ef473 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:41a11b24dc33a848b1e92e221ebf6f7eed9da0d17883baba30b49488a8dd916d -size 203835 +oid sha256:486d97c665bfd6f9ac41c3c65182476080467159d4dda64e89e725960ac8a3f7 +size 216418 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-pseudo-0.png index 773294c2b..76ad99dd7 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80abb6f07c275acaedb46078abfac094b304d3c799c050003e1d20dca3d745fa -size 206909 +oid sha256:dfc40f590b6c37545bfa46c7569ee27b6a0ae868a55960a53e2fd3d74c564412 +size 219887 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-en-GB-0.png index 581810fa6..ebe6ff49e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f972c103ce2c14ed5c676741f9a3f8f480702815e677749d46908abd0fa7dada -size 132742 +oid sha256:5778379ebc5f0df879016579fa4dccfce7e9546dc06fd5b9cb5fa3cfd3e8f050 +size 143496 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-pseudo-0.png index 64fd274a5..49fea7a1e 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceHeaderView.iPhone-16-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:430dfde8c142f7ad7474be587c0c217daa9cb9801fe0ff0eff291f8f9cbf57cf -size 139505 +oid sha256:5adc8046dfeadc62fb0d22fba84e0f5c0c61c7bb780a432b800e2cdb29066427 +size 149951 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-en-GB-0.png index 83333ffb5..f5e054cd4 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0657e0d482b9a3bc949d0bd9b2206baa14413c58031a9333cdd105ccecab4d8 -size 234610 +oid sha256:96f0a0f4dd66edce3a75e51a16d059bdfbe32ef61dc8e78ccbb22868205c36ea +size 233953 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-pseudo-0.png index f8570272a..c4dfdce08 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f770bab4c29db4148a4266a9d2956f4b6268ec473b8a013a5aa89219613f5db -size 264406 +oid sha256:c4c8eb821de73289ae5c220b9c1fa9f2e58c33e3617634ae0cacb5d9043e587f +size 263913 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-en-GB-0.png index 7cf788659..926ba9420 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b2acf1f719ab9acb6887d700607a3e741ec69ae7cabbfd3fd2d00ebef7c7ced -size 175169 +oid sha256:8891be05350788529385e39d9edf71e52c7d2536a471773b831358278b316973 +size 175952 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-pseudo-0.png index 464998fc2..5fd710029 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceScreen.iPhone-16-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f68408d2eca92a7dec827ef79f01179ae58c4a5b55dd4d2c6de425d525d06842 -size 200045 +oid sha256:a46b3ab9af6aeca9bc98ffd6efe3669a03361c8efc6cba99e303d04f11fdbc07 +size 197703 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPad-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPad-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPad-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPad-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPad-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPad-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPhone-16-en-GB.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPhone-16-en-GB-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPhone-16-en-GB.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPhone-16-pseudo.png similarity index 100% rename from PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.iPhone-16-pseudo-0.png rename to PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.Owner-iPhone-16-pseudo.png diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-en-GB.png new file mode 100644 index 000000000..ea1fb682f --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e172a847ec495abaf46e8a42879252a75db06fa3e2cd4ff1c31964726f08422e +size 125408 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-pseudo.png new file mode 100644 index 000000000..5e6ff0267 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2133099664d02d4c53a86ad516f1e6c321e852455504ac18acd135267d109f6 +size 129691 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-en-GB.png new file mode 100644 index 000000000..c496dc772 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e2b5de1805e2d163be5b82ef98814cedacef5c02ee18160f55856a8fd7bd1b9 +size 78430 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-pseudo.png new file mode 100644 index 000000000..84d9fbf3a --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/spaceSettingsScreen.User-iPhone-16-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e83067633005469ed20f25d0d6e1c92d436983c230860faed77b65bebb3c0bc0 +size 82233