mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-06 04:18:31 +00:00
refactor: manage member sheet in timeline
better implementation updated tests Update ElementX/Sources/Screens/Timeline/TimelineViewModel.swift Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> fix
This commit is contained in:
@@ -164,6 +164,7 @@
|
|||||||
1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */; };
|
1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */; };
|
||||||
1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */; };
|
1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */; };
|
||||||
1BA04D05EBC6646958B1BE60 /* PlaceholderScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */; };
|
1BA04D05EBC6646958B1BE60 /* PlaceholderScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */; };
|
||||||
|
1C1750C009F7214B967928BC /* ManageRoomMemberSheetViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D80807B554CF9C524F98674F /* ManageRoomMemberSheetViewModelTests.swift */; };
|
||||||
1C409A26A99F0371C47AFA51 /* UserDiscoveryServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */; };
|
1C409A26A99F0371C47AFA51 /* UserDiscoveryServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F615A00DB223FF3280204D2 /* UserDiscoveryServiceProtocol.swift */; };
|
||||||
1C598D3B785645AAC7B35760 /* ReportRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292EEE1F71DCC205C45728F7 /* ReportRoomScreenCoordinator.swift */; };
|
1C598D3B785645AAC7B35760 /* ReportRoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 292EEE1F71DCC205C45728F7 /* ReportRoomScreenCoordinator.swift */; };
|
||||||
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */; };
|
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */; };
|
||||||
@@ -421,6 +422,7 @@
|
|||||||
5038E69A5E6A89DE1A345E04 /* ShouldScrollOnKeyboardDidShow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832397B5C3D00A4BF52C5F0B /* ShouldScrollOnKeyboardDidShow.swift */; };
|
5038E69A5E6A89DE1A345E04 /* ShouldScrollOnKeyboardDidShow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832397B5C3D00A4BF52C5F0B /* ShouldScrollOnKeyboardDidShow.swift */; };
|
||||||
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */; };
|
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */; };
|
||||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; };
|
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; };
|
||||||
|
510C4EDF826CA9C6CEEC6C95 /* ManageRoomMemberSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A34D9BCA1A7D9A56E1EAF1D /* ManageRoomMemberSheetViewModel.swift */; };
|
||||||
5139F4BD5A5DF6F8D11A9BDE /* NotificationPermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D0BA44B1838E65B507B277 /* NotificationPermissionsScreen.swift */; };
|
5139F4BD5A5DF6F8D11A9BDE /* NotificationPermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D0BA44B1838E65B507B277 /* NotificationPermissionsScreen.swift */; };
|
||||||
513AF15E0E84711B80D04B1B /* ReportRoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3E9684DCE6B66BD0B5DF67 /* ReportRoomScreenViewModelTests.swift */; };
|
513AF15E0E84711B80D04B1B /* ReportRoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3E9684DCE6B66BD0B5DF67 /* ReportRoomScreenViewModelTests.swift */; };
|
||||||
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
|
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
|
||||||
@@ -478,7 +480,6 @@
|
|||||||
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
||||||
5D99F63CC88BB29383019FC6 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; };
|
5D99F63CC88BB29383019FC6 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; };
|
||||||
5DB4334CBBA142376FF5FFEC /* preview_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 200626E8353AB2729444F991 /* preview_image.jpg */; };
|
5DB4334CBBA142376FF5FFEC /* preview_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 200626E8353AB2729444F991 /* preview_image.jpg */; };
|
||||||
5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */; };
|
|
||||||
5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; };
|
5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; };
|
||||||
5DFC2A889D3B39DD47AC63A8 /* PillUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C537DE821FED94D23467B6C4 /* PillUtilities.swift */; };
|
5DFC2A889D3B39DD47AC63A8 /* PillUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C537DE821FED94D23467B6C4 /* PillUtilities.swift */; };
|
||||||
5EC046E41755C095DAB1C3FF /* TimelineProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8C9BBB729C941BEE0E2A63 /* TimelineProviderProtocol.swift */; };
|
5EC046E41755C095DAB1C3FF /* TimelineProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8C9BBB729C941BEE0E2A63 /* TimelineProviderProtocol.swift */; };
|
||||||
@@ -1145,6 +1146,7 @@
|
|||||||
E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; };
|
E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; };
|
||||||
E49F74BD93230BDEFFE5EA51 /* RoomNotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D295F0081084F38DB20893 /* RoomNotificationSettingsScreenViewModelTests.swift */; };
|
E49F74BD93230BDEFFE5EA51 /* RoomNotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D295F0081084F38DB20893 /* RoomNotificationSettingsScreenViewModelTests.swift */; };
|
||||||
E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */; };
|
E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */; };
|
||||||
|
E4D261E237D5D45E6DF2D0F1 /* ManageRoomMemberSheetModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 787E84119E626E2F0E0BFBE8 /* ManageRoomMemberSheetModels.swift */; };
|
||||||
E4F924DECC66389C1C810550 /* AuthenticationStartScreenBackgroundImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D685B4DB38BB5BD87C956A /* AuthenticationStartScreenBackgroundImage.swift */; };
|
E4F924DECC66389C1C810550 /* AuthenticationStartScreenBackgroundImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D685B4DB38BB5BD87C956A /* AuthenticationStartScreenBackgroundImage.swift */; };
|
||||||
E58F1F3276E98A93F7D39219 /* RoomPollsHistoryScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */; };
|
E58F1F3276E98A93F7D39219 /* RoomPollsHistoryScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */; };
|
||||||
E5AB28123E2488F97E953AC0 /* CallNotificationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1ED17433ADC77287F8904F9 /* CallNotificationRoomTimelineItem.swift */; };
|
E5AB28123E2488F97E953AC0 /* CallNotificationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1ED17433ADC77287F8904F9 /* CallNotificationRoomTimelineItem.swift */; };
|
||||||
@@ -1212,11 +1214,13 @@
|
|||||||
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B5E17028C02DFA7DDA3931 /* RoomMemberProxyProtocol.swift */; };
|
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B5E17028C02DFA7DDA3931 /* RoomMemberProxyProtocol.swift */; };
|
||||||
F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; };
|
F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; };
|
||||||
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.swift */; };
|
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.swift */; };
|
||||||
|
F1C68F64FC8A66B6B9510BF7 /* ManageRoomMemberSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B30CD19ED6243FEDFBA8400 /* ManageRoomMemberSheetView.swift */; };
|
||||||
F252C0EA49088801F4CA6006 /* landscape_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 96CE9D6642DD487D8CC90C9C /* landscape_test_image.jpg */; };
|
F252C0EA49088801F4CA6006 /* landscape_test_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 96CE9D6642DD487D8CC90C9C /* landscape_test_image.jpg */; };
|
||||||
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
||||||
F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; };
|
F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; };
|
||||||
F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; };
|
F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; };
|
||||||
F2E580C0FBFBEFFE9D69893B /* RoomPreviewProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739077686814E4EA339B1C83 /* RoomPreviewProxyProtocol.swift */; };
|
F2E580C0FBFBEFFE9D69893B /* RoomPreviewProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 739077686814E4EA339B1C83 /* RoomPreviewProxyProtocol.swift */; };
|
||||||
|
F35FAD1B1B289E221A07D719 /* ManageRoomMemberSheetViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB7B588A06911B455AC0B4C9 /* ManageRoomMemberSheetViewModelProtocol.swift */; };
|
||||||
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; };
|
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; };
|
||||||
F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
|
F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
|
||||||
F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; };
|
F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; };
|
||||||
@@ -1489,6 +1493,7 @@
|
|||||||
1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreen.swift; sourceTree = "<group>"; };
|
1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreen.swift; sourceTree = "<group>"; };
|
||||||
1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItem.swift; sourceTree = "<group>"; };
|
1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItem.swift; sourceTree = "<group>"; };
|
1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
1B30CD19ED6243FEDFBA8400 /* ManageRoomMemberSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetView.swift; sourceTree = "<group>"; };
|
||||||
1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillAttachmentViewProvider.swift; sourceTree = "<group>"; };
|
1B53D6C5C0D14B04D3AB3F6E /* PillAttachmentViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillAttachmentViewProvider.swift; sourceTree = "<group>"; };
|
||||||
1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenModels.swift; sourceTree = "<group>"; };
|
1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenModels.swift; sourceTree = "<group>"; };
|
||||||
1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenModels.swift; sourceTree = "<group>"; };
|
1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
@@ -1930,6 +1935,7 @@
|
|||||||
7720ACAC6155AB7F9C70B546 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nb; path = nb.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
7720ACAC6155AB7F9C70B546 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nb; path = nb.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
7773CBFDBD458E0B7E270507 /* PillView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillView.swift; sourceTree = "<group>"; };
|
7773CBFDBD458E0B7E270507 /* PillView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillView.swift; sourceTree = "<group>"; };
|
||||||
780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModel.swift; sourceTree = "<group>"; };
|
780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
787E84119E626E2F0E0BFBE8 /* ManageRoomMemberSheetModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetModels.swift; sourceTree = "<group>"; };
|
||||||
78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinatorTests.swift; sourceTree = "<group>"; };
|
78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinatorTests.swift; sourceTree = "<group>"; };
|
||||||
7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerMock.swift; sourceTree = "<group>"; };
|
7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerMock.swift; sourceTree = "<group>"; };
|
||||||
@@ -2105,6 +2111,7 @@
|
|||||||
9A028783CFFF861C5E44FFB1 /* BadgeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeLabel.swift; sourceTree = "<group>"; };
|
9A028783CFFF861C5E44FFB1 /* BadgeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeLabel.swift; sourceTree = "<group>"; };
|
||||||
9A1C33355FFB0F0953C35036 /* ClientBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientBuilder.swift; sourceTree = "<group>"; };
|
9A1C33355FFB0F0953C35036 /* ClientBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientBuilder.swift; sourceTree = "<group>"; };
|
||||||
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
||||||
|
9A34D9BCA1A7D9A56E1EAF1D /* ManageRoomMemberSheetViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetViewModel.swift; sourceTree = "<group>"; };
|
||||||
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
|
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
9AA3AF94A06D319BB37E52DA /* TimelineItemFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemFactoryTests.swift; sourceTree = "<group>"; };
|
9AA3AF94A06D319BB37E52DA /* TimelineItemFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemFactoryTests.swift; sourceTree = "<group>"; };
|
||||||
9B06663F7858E45882E63471 /* StaticLocationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreen.swift; sourceTree = "<group>"; };
|
9B06663F7858E45882E63471 /* StaticLocationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreen.swift; sourceTree = "<group>"; };
|
||||||
@@ -2352,6 +2359,7 @@
|
|||||||
CA90BD288E5AE6BC643AFDDF /* TemplateScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenCoordinator.swift; sourceTree = "<group>"; };
|
CA90BD288E5AE6BC643AFDDF /* TemplateScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
CACA846B3E3E9A521D98B178 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
CACA846B3E3E9A521D98B178 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenViewModelTests.swift; sourceTree = "<group>"; };
|
CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
|
CB7B588A06911B455AC0B4C9 /* ManageRoomMemberSheetViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
CBBCC6E74774E79B599625D0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
CBBCC6E74774E79B599625D0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JoinedRoomSize+MemberCount.swift"; sourceTree = "<group>"; };
|
CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JoinedRoomSize+MemberCount.swift"; sourceTree = "<group>"; };
|
||||||
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenMemberCell.swift; sourceTree = "<group>"; };
|
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenMemberCell.swift; sourceTree = "<group>"; };
|
||||||
@@ -2409,6 +2417,7 @@
|
|||||||
D7B18089ED50324583BB2FB7 /* EditRoomAddressScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditRoomAddressScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
D7B18089ED50324583BB2FB7 /* EditRoomAddressScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditRoomAddressScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilder.swift; sourceTree = "<group>"; };
|
D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilder.swift; sourceTree = "<group>"; };
|
||||||
D7BEB970F500BFB248443FA1 /* BloomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloomView.swift; sourceTree = "<group>"; };
|
D7BEB970F500BFB248443FA1 /* BloomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloomView.swift; sourceTree = "<group>"; };
|
||||||
|
D80807B554CF9C524F98674F /* ManageRoomMemberSheetViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreen.swift; sourceTree = "<group>"; };
|
D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreen.swift; sourceTree = "<group>"; };
|
||||||
D8AA084E10B80D64449C02A9 /* SessionVerificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationTests.swift; sourceTree = "<group>"; };
|
D8AA084E10B80D64449C02A9 /* SessionVerificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationTests.swift; sourceTree = "<group>"; };
|
||||||
D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = "<group>"; };
|
D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
@@ -2572,7 +2581,6 @@
|
|||||||
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
|
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
|
||||||
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderMock.swift; sourceTree = "<group>"; };
|
FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderMock.swift; sourceTree = "<group>"; };
|
||||||
FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListManageMemberSheet.swift; sourceTree = "<group>"; };
|
|
||||||
FC9044BE0E4A66F5B963E834 /* AudioFileEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioFileEventsTimelineView.swift; sourceTree = "<group>"; };
|
FC9044BE0E4A66F5B963E834 /* AudioFileEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioFileEventsTimelineView.swift; sourceTree = "<group>"; };
|
||||||
FCE93F0CBF0D96B77111C413 /* AppLockFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockFlowCoordinator.swift; sourceTree = "<group>"; };
|
FCE93F0CBF0D96B77111C413 /* AppLockFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorController.swift; sourceTree = "<group>"; };
|
FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorController.swift; sourceTree = "<group>"; };
|
||||||
@@ -3490,6 +3498,17 @@
|
|||||||
path = QRCodeLoginScreen;
|
path = QRCodeLoginScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
3D76DA5827DF9396AC90E7B4 /* ManageRoomMemberSheet */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
787E84119E626E2F0E0BFBE8 /* ManageRoomMemberSheetModels.swift */,
|
||||||
|
9A34D9BCA1A7D9A56E1EAF1D /* ManageRoomMemberSheetViewModel.swift */,
|
||||||
|
CB7B588A06911B455AC0B4C9 /* ManageRoomMemberSheetViewModelProtocol.swift */,
|
||||||
|
40AE45E13BF744AA0E4EA0E4 /* View */,
|
||||||
|
);
|
||||||
|
path = ManageRoomMemberSheet;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
3E1CCC4B607946CE90B4A827 /* DeclineAndBlockScreen */ = {
|
3E1CCC4B607946CE90B4A827 /* DeclineAndBlockScreen */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -3559,6 +3578,14 @@
|
|||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
40AE45E13BF744AA0E4EA0E4 /* View */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1B30CD19ED6243FEDFBA8400 /* ManageRoomMemberSheetView.swift */,
|
||||||
|
);
|
||||||
|
path = View;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
40D9A816C45E0278C29DF883 /* SupportingFiles */ = {
|
40D9A816C45E0278C29DF883 /* SupportingFiles */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -4287,6 +4314,7 @@
|
|||||||
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */,
|
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */,
|
||||||
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */,
|
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */,
|
||||||
5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */,
|
5A43964330459965AF048A8C /* LoginScreenViewModelTests.swift */,
|
||||||
|
D80807B554CF9C524F98674F /* ManageRoomMemberSheetViewModelTests.swift */,
|
||||||
1D262A26713C18BB70C82CA5 /* MapTilerURLBuilderTests.swift */,
|
1D262A26713C18BB70C82CA5 /* MapTilerURLBuilderTests.swift */,
|
||||||
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */,
|
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */,
|
||||||
2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */,
|
2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */,
|
||||||
@@ -4878,7 +4906,6 @@
|
|||||||
949B06577E5265373013DDAB /* View */ = {
|
949B06577E5265373013DDAB /* View */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */,
|
|
||||||
1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */,
|
1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */,
|
||||||
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */,
|
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */,
|
||||||
);
|
);
|
||||||
@@ -5776,6 +5803,7 @@
|
|||||||
BF0415BE807CA2BCFC210008 /* KnockRequestsListScreen */,
|
BF0415BE807CA2BCFC210008 /* KnockRequestsListScreen */,
|
||||||
948DD12A5533BE1BC260E437 /* LocationSharing */,
|
948DD12A5533BE1BC260E437 /* LocationSharing */,
|
||||||
73E032ADD008D63812791D97 /* LogViewerScreen */,
|
73E032ADD008D63812791D97 /* LogViewerScreen */,
|
||||||
|
3D76DA5827DF9396AC90E7B4 /* ManageRoomMemberSheet */,
|
||||||
26397A1EDB867FD573821532 /* MediaEventsTimelineScreen */,
|
26397A1EDB867FD573821532 /* MediaEventsTimelineScreen */,
|
||||||
87E2774157D9C4894BCFF3F8 /* MediaPickerScreen */,
|
87E2774157D9C4894BCFF3F8 /* MediaPickerScreen */,
|
||||||
23605DD08620BE6558242469 /* MediaUploadPreviewScreen */,
|
23605DD08620BE6558242469 /* MediaUploadPreviewScreen */,
|
||||||
@@ -6776,6 +6804,7 @@
|
|||||||
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */,
|
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */,
|
||||||
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
|
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
|
||||||
7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */,
|
7434A7F02D587A920B376A9A /* LoginScreenViewModelTests.swift in Sources */,
|
||||||
|
1C1750C009F7214B967928BC /* ManageRoomMemberSheetViewModelTests.swift in Sources */,
|
||||||
3BEBDCB42BABFA3B456FECA7 /* MapTilerURLBuilderTests.swift in Sources */,
|
3BEBDCB42BABFA3B456FECA7 /* MapTilerURLBuilderTests.swift in Sources */,
|
||||||
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */,
|
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */,
|
||||||
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */,
|
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */,
|
||||||
@@ -7269,6 +7298,10 @@
|
|||||||
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */,
|
A5B9EF45C7B8ACEB4954AE36 /* LoginScreenViewModelProtocol.swift in Sources */,
|
||||||
B10F7D5C237417DA160F4603 /* LongPressWithFeedback.swift in Sources */,
|
B10F7D5C237417DA160F4603 /* LongPressWithFeedback.swift in Sources */,
|
||||||
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
|
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
|
||||||
|
E4D261E237D5D45E6DF2D0F1 /* ManageRoomMemberSheetModels.swift in Sources */,
|
||||||
|
F1C68F64FC8A66B6B9510BF7 /* ManageRoomMemberSheetView.swift in Sources */,
|
||||||
|
510C4EDF826CA9C6CEEC6C95 /* ManageRoomMemberSheetViewModel.swift in Sources */,
|
||||||
|
F35FAD1B1B289E221A07D719 /* ManageRoomMemberSheetViewModelProtocol.swift in Sources */,
|
||||||
C1D0AB8222D7BAFC9AF9C8C0 /* MapLibreMapView.swift in Sources */,
|
C1D0AB8222D7BAFC9AF9C8C0 /* MapLibreMapView.swift in Sources */,
|
||||||
C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */,
|
C9BE065FA7D4E77E4C61CB69 /* MapLibreModels.swift in Sources */,
|
||||||
E2DDA49BD62F03F180A42E30 /* MapLibreStaticMapView.swift in Sources */,
|
E2DDA49BD62F03F180A42E30 /* MapLibreStaticMapView.swift in Sources */,
|
||||||
@@ -7487,7 +7520,6 @@
|
|||||||
6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */,
|
6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */,
|
||||||
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */,
|
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */,
|
||||||
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */,
|
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */,
|
||||||
5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */,
|
|
||||||
C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */,
|
C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */,
|
||||||
1C9BB74711E5F24C77B7FED0 /* RoomMembersListScreenCoordinator.swift in Sources */,
|
1C9BB74711E5F24C77B7FED0 /* RoomMembersListScreenCoordinator.swift in Sources */,
|
||||||
A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */,
|
A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */,
|
||||||
|
|||||||
@@ -399,9 +399,26 @@
|
|||||||
"screen_advanced_settings_hide_invite_avatars_toggle_title" = "Hide avatars in room invite requests";
|
"screen_advanced_settings_hide_invite_avatars_toggle_title" = "Hide avatars in room invite requests";
|
||||||
"screen_advanced_settings_hide_timeline_media_toggle_title" = "Hide media previews in timeline";
|
"screen_advanced_settings_hide_timeline_media_toggle_title" = "Hide media previews in timeline";
|
||||||
"screen_advanced_settings_moderation_and_safety_section_title" = "Moderation and Safety";
|
"screen_advanced_settings_moderation_and_safety_section_title" = "Moderation and Safety";
|
||||||
|
"screen_advanced_settings_show_media_timeline_always_hide" = "Always hide";
|
||||||
|
"screen_advanced_settings_show_media_timeline_always_show" = "Always show";
|
||||||
|
"screen_advanced_settings_show_media_timeline_private_rooms" = "In private rooms";
|
||||||
|
"screen_advanced_settings_show_media_timeline_subtitle" = "A hidden media can always be shown by tapping on it";
|
||||||
|
"screen_advanced_settings_show_media_timeline_title" = "Show media in timeline";
|
||||||
|
"screen_bottom_sheet,manage_room_member_remove" = "Remove from room";
|
||||||
"screen_bottom_sheet_create_dm_confirmation_button_title" = "Send invite";
|
"screen_bottom_sheet_create_dm_confirmation_button_title" = "Send invite";
|
||||||
"screen_bottom_sheet_create_dm_message" = "Would you like to start a chat with %1$@?";
|
"screen_bottom_sheet_create_dm_message" = "Would you like to start a chat with %1$@?";
|
||||||
"screen_bottom_sheet_create_dm_title" = "Send invite?";
|
"screen_bottom_sheet_create_dm_title" = "Send invite?";
|
||||||
|
"screen_bottom_sheet_manage_room_member_ban" = "Ban from room";
|
||||||
|
"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 this room 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_banning_user" = "Banning %1$@";
|
||||||
|
"screen_bottom_sheet_manage_room_member_kick_member_confirmation_action" = "Remove";
|
||||||
|
"screen_bottom_sheet_manage_room_member_kick_member_confirmation_description" = "They will be able to join this room again if invited.";
|
||||||
|
"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_member_user_info" = "View profile";
|
||||||
|
"screen_bottom_sheet_manage_room_member_remove_confirmation_title" = "Remove member and ban from joining in the future?";
|
||||||
|
"screen_bottom_sheet_manage_room_member_removing_user" = "Removing %1$@…";
|
||||||
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
|
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
|
||||||
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
|
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
|
||||||
"screen_create_room_room_access_section_header" = "Room Access";
|
"screen_create_room_room_access_section_header" = "Room Access";
|
||||||
@@ -886,26 +903,14 @@
|
|||||||
"screen_room_member_details_unblock_user" = "Unblock user";
|
"screen_room_member_details_unblock_user" = "Unblock user";
|
||||||
"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user.";
|
"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user.";
|
||||||
"screen_room_member_details_verify_button_title" = "Verify %1$@";
|
"screen_room_member_details_verify_button_title" = "Verify %1$@";
|
||||||
"screen_room_member_list_ban_member_confirmation_action" = "Ban";
|
|
||||||
"screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited.";
|
|
||||||
"screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?";
|
|
||||||
"screen_room_member_list_banned_empty" = "There are no banned users in this room.";
|
"screen_room_member_list_banned_empty" = "There are no banned users in this room.";
|
||||||
"screen_room_member_list_banning_user" = "Banning %1$@";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_action" = "Remove";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_description" = "They will be able to join this room again if invited.";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_title" = "Are you sure you want to remove this member?";
|
|
||||||
"screen_room_member_list_manage_member_ban" = "Remove and ban member";
|
|
||||||
"screen_room_member_list_manage_member_remove" = "Remove from room";
|
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member";
|
"screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member";
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?";
|
|
||||||
"screen_room_member_list_manage_member_unban_action" = "Unban";
|
"screen_room_member_list_manage_member_unban_action" = "Unban";
|
||||||
"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited.";
|
"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited.";
|
||||||
"screen_room_member_list_manage_member_unban_title" = "Unban user";
|
"screen_room_member_list_manage_member_unban_title" = "Unban user";
|
||||||
"screen_room_member_list_manage_member_user_info" = "View profile";
|
|
||||||
"screen_room_member_list_mode_banned" = "Banned";
|
"screen_room_member_list_mode_banned" = "Banned";
|
||||||
"screen_room_member_list_mode_members" = "Members";
|
"screen_room_member_list_mode_members" = "Members";
|
||||||
"screen_room_member_list_pending_header_title" = "Pending";
|
"screen_room_member_list_pending_header_title" = "Pending";
|
||||||
"screen_room_member_list_removing_user" = "Removing %1$@…";
|
|
||||||
"screen_room_member_list_role_administrator" = "Admin";
|
"screen_room_member_list_role_administrator" = "Admin";
|
||||||
"screen_room_member_list_role_moderator" = "Moderator";
|
"screen_room_member_list_role_moderator" = "Moderator";
|
||||||
"screen_room_member_list_room_members_header_title" = "Room members";
|
"screen_room_member_list_room_members_header_title" = "Room members";
|
||||||
@@ -1134,9 +1139,10 @@
|
|||||||
"troubleshoot_notifications_test_current_push_provider_failure" = "No push providers selected.";
|
"troubleshoot_notifications_test_current_push_provider_failure" = "No push providers selected.";
|
||||||
"troubleshoot_notifications_test_current_push_provider_success" = "Current push provider: %1$@.";
|
"troubleshoot_notifications_test_current_push_provider_success" = "Current push provider: %1$@.";
|
||||||
"troubleshoot_notifications_test_current_push_provider_title" = "Current push provider";
|
"troubleshoot_notifications_test_current_push_provider_title" = "Current push provider";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_description" = "Ensure that the application has at least one push provider.";
|
"troubleshoot_notifications_test_detect_push_provider_description" = "Ensure that the application supports at least one push provider.";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_failure" = "No push providers found.";
|
"troubleshoot_notifications_test_detect_push_provider_failure" = "No push provider support found.";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_title" = "Detect push providers";
|
"troubleshoot_notifications_test_detect_push_provider_success_2" = "The application was built with support for: %1$@";
|
||||||
|
"troubleshoot_notifications_test_detect_push_provider_title" = "Push provider support";
|
||||||
"troubleshoot_notifications_test_display_notification_description" = "Check that the application can display notification.";
|
"troubleshoot_notifications_test_display_notification_description" = "Check that the application can display notification.";
|
||||||
"troubleshoot_notifications_test_display_notification_failure" = "The notification has not been clicked.";
|
"troubleshoot_notifications_test_display_notification_failure" = "The notification has not been clicked.";
|
||||||
"troubleshoot_notifications_test_display_notification_permission_failure" = "Cannot display the notification.";
|
"troubleshoot_notifications_test_display_notification_permission_failure" = "Cannot display the notification.";
|
||||||
@@ -1237,7 +1243,7 @@
|
|||||||
"screen_room_details_security_title" = "Security";
|
"screen_room_details_security_title" = "Security";
|
||||||
"screen_room_details_topic_title" = "Topic";
|
"screen_room_details_topic_title" = "Topic";
|
||||||
"screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again.";
|
"screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again.";
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member";
|
"screen_room_member_list_manage_member_remove_confirmation_ban" = "Ban from room";
|
||||||
"screen_room_notification_settings_mode_all_messages" = "All messages";
|
"screen_room_notification_settings_mode_all_messages" = "All messages";
|
||||||
"screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only";
|
"screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only";
|
||||||
"screen_room_timeline_reactions_show_less" = "Show less";
|
"screen_room_timeline_reactions_show_less" = "Show less";
|
||||||
|
|||||||
@@ -399,9 +399,26 @@
|
|||||||
"screen_advanced_settings_hide_invite_avatars_toggle_title" = "Hide avatars in room invite requests";
|
"screen_advanced_settings_hide_invite_avatars_toggle_title" = "Hide avatars in room invite requests";
|
||||||
"screen_advanced_settings_hide_timeline_media_toggle_title" = "Hide media previews in timeline";
|
"screen_advanced_settings_hide_timeline_media_toggle_title" = "Hide media previews in timeline";
|
||||||
"screen_advanced_settings_moderation_and_safety_section_title" = "Moderation and Safety";
|
"screen_advanced_settings_moderation_and_safety_section_title" = "Moderation and Safety";
|
||||||
|
"screen_advanced_settings_show_media_timeline_always_hide" = "Always hide";
|
||||||
|
"screen_advanced_settings_show_media_timeline_always_show" = "Always show";
|
||||||
|
"screen_advanced_settings_show_media_timeline_private_rooms" = "In private rooms";
|
||||||
|
"screen_advanced_settings_show_media_timeline_subtitle" = "A hidden media can always be shown by tapping on it";
|
||||||
|
"screen_advanced_settings_show_media_timeline_title" = "Show media in timeline";
|
||||||
|
"screen_bottom_sheet,manage_room_member_remove" = "Remove from room";
|
||||||
"screen_bottom_sheet_create_dm_confirmation_button_title" = "Send invite";
|
"screen_bottom_sheet_create_dm_confirmation_button_title" = "Send invite";
|
||||||
"screen_bottom_sheet_create_dm_message" = "Would you like to start a chat with %1$@?";
|
"screen_bottom_sheet_create_dm_message" = "Would you like to start a chat with %1$@?";
|
||||||
"screen_bottom_sheet_create_dm_title" = "Send invite?";
|
"screen_bottom_sheet_create_dm_title" = "Send invite?";
|
||||||
|
"screen_bottom_sheet_manage_room_member_ban" = "Ban from room";
|
||||||
|
"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 this room 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_banning_user" = "Banning %1$@";
|
||||||
|
"screen_bottom_sheet_manage_room_member_kick_member_confirmation_action" = "Remove";
|
||||||
|
"screen_bottom_sheet_manage_room_member_kick_member_confirmation_description" = "They will be able to join this room again if invited.";
|
||||||
|
"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_member_user_info" = "View profile";
|
||||||
|
"screen_bottom_sheet_manage_room_member_remove_confirmation_title" = "Remove member and ban from joining in the future?";
|
||||||
|
"screen_bottom_sheet_manage_room_member_removing_user" = "Removing %1$@…";
|
||||||
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
|
"screen_create_room_room_access_section_anyone_option_description" = "Anyone can join this room";
|
||||||
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
|
"screen_create_room_room_access_section_anyone_option_title" = "Anyone";
|
||||||
"screen_create_room_room_access_section_header" = "Room Access";
|
"screen_create_room_room_access_section_header" = "Room Access";
|
||||||
@@ -886,26 +903,14 @@
|
|||||||
"screen_room_member_details_unblock_user" = "Unblock user";
|
"screen_room_member_details_unblock_user" = "Unblock user";
|
||||||
"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user.";
|
"screen_room_member_details_verify_button_subtitle" = "Use the web app to verify this user.";
|
||||||
"screen_room_member_details_verify_button_title" = "Verify %1$@";
|
"screen_room_member_details_verify_button_title" = "Verify %1$@";
|
||||||
"screen_room_member_list_ban_member_confirmation_action" = "Ban";
|
|
||||||
"screen_room_member_list_ban_member_confirmation_description" = "They won’t be able to join this room again if invited.";
|
|
||||||
"screen_room_member_list_ban_member_confirmation_title" = "Are you sure you want to ban this member?";
|
|
||||||
"screen_room_member_list_banned_empty" = "There are no banned users in this room.";
|
"screen_room_member_list_banned_empty" = "There are no banned users in this room.";
|
||||||
"screen_room_member_list_banning_user" = "Banning %1$@";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_action" = "Remove";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_description" = "They will be able to join this room again if invited.";
|
|
||||||
"screen_room_member_list_kick_member_confirmation_title" = "Are you sure you want to remove this member?";
|
|
||||||
"screen_room_member_list_manage_member_ban" = "Remove and ban member";
|
|
||||||
"screen_room_member_list_manage_member_remove" = "Remove from room";
|
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member";
|
"screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member";
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?";
|
|
||||||
"screen_room_member_list_manage_member_unban_action" = "Unban";
|
"screen_room_member_list_manage_member_unban_action" = "Unban";
|
||||||
"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited.";
|
"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited.";
|
||||||
"screen_room_member_list_manage_member_unban_title" = "Unban user";
|
"screen_room_member_list_manage_member_unban_title" = "Unban user";
|
||||||
"screen_room_member_list_manage_member_user_info" = "View profile";
|
|
||||||
"screen_room_member_list_mode_banned" = "Banned";
|
"screen_room_member_list_mode_banned" = "Banned";
|
||||||
"screen_room_member_list_mode_members" = "Members";
|
"screen_room_member_list_mode_members" = "Members";
|
||||||
"screen_room_member_list_pending_header_title" = "Pending";
|
"screen_room_member_list_pending_header_title" = "Pending";
|
||||||
"screen_room_member_list_removing_user" = "Removing %1$@…";
|
|
||||||
"screen_room_member_list_role_administrator" = "Admin";
|
"screen_room_member_list_role_administrator" = "Admin";
|
||||||
"screen_room_member_list_role_moderator" = "Moderator";
|
"screen_room_member_list_role_moderator" = "Moderator";
|
||||||
"screen_room_member_list_room_members_header_title" = "Room members";
|
"screen_room_member_list_room_members_header_title" = "Room members";
|
||||||
@@ -1134,9 +1139,10 @@
|
|||||||
"troubleshoot_notifications_test_current_push_provider_failure" = "No push providers selected.";
|
"troubleshoot_notifications_test_current_push_provider_failure" = "No push providers selected.";
|
||||||
"troubleshoot_notifications_test_current_push_provider_success" = "Current push provider: %1$@.";
|
"troubleshoot_notifications_test_current_push_provider_success" = "Current push provider: %1$@.";
|
||||||
"troubleshoot_notifications_test_current_push_provider_title" = "Current push provider";
|
"troubleshoot_notifications_test_current_push_provider_title" = "Current push provider";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_description" = "Ensure that the application has at least one push provider.";
|
"troubleshoot_notifications_test_detect_push_provider_description" = "Ensure that the application supports at least one push provider.";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_failure" = "No push providers found.";
|
"troubleshoot_notifications_test_detect_push_provider_failure" = "No push provider support found.";
|
||||||
"troubleshoot_notifications_test_detect_push_provider_title" = "Detect push providers";
|
"troubleshoot_notifications_test_detect_push_provider_success_2" = "The application was built with support for: %1$@";
|
||||||
|
"troubleshoot_notifications_test_detect_push_provider_title" = "Push provider support";
|
||||||
"troubleshoot_notifications_test_display_notification_description" = "Check that the application can display notification.";
|
"troubleshoot_notifications_test_display_notification_description" = "Check that the application can display notification.";
|
||||||
"troubleshoot_notifications_test_display_notification_failure" = "The notification has not been clicked.";
|
"troubleshoot_notifications_test_display_notification_failure" = "The notification has not been clicked.";
|
||||||
"troubleshoot_notifications_test_display_notification_permission_failure" = "Cannot display the notification.";
|
"troubleshoot_notifications_test_display_notification_permission_failure" = "Cannot display the notification.";
|
||||||
@@ -1237,7 +1243,7 @@
|
|||||||
"screen_room_details_security_title" = "Security";
|
"screen_room_details_security_title" = "Security";
|
||||||
"screen_room_details_topic_title" = "Topic";
|
"screen_room_details_topic_title" = "Topic";
|
||||||
"screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again.";
|
"screen_room_error_failed_processing_media" = "Failed processing media to upload, please try again.";
|
||||||
"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member";
|
"screen_room_member_list_manage_member_remove_confirmation_ban" = "Ban from room";
|
||||||
"screen_room_notification_settings_mode_all_messages" = "All messages";
|
"screen_room_notification_settings_mode_all_messages" = "All messages";
|
||||||
"screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only";
|
"screen_room_notification_settings_mode_mentions_and_keywords" = "Mentions and Keywords only";
|
||||||
"screen_room_timeline_reactions_show_less" = "Show less";
|
"screen_room_timeline_reactions_show_less" = "Show less";
|
||||||
|
|||||||
@@ -978,6 +978,16 @@ internal enum L10n {
|
|||||||
internal static var screenAdvancedSettingsSharePresence: String { return L10n.tr("Localizable", "screen_advanced_settings_share_presence") }
|
internal static var screenAdvancedSettingsSharePresence: String { return L10n.tr("Localizable", "screen_advanced_settings_share_presence") }
|
||||||
/// If turned off, you won’t be able to send or receive read receipts or typing notifications.
|
/// If turned off, you won’t be able to send or receive read receipts or typing notifications.
|
||||||
internal static var screenAdvancedSettingsSharePresenceDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_share_presence_description") }
|
internal static var screenAdvancedSettingsSharePresenceDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_share_presence_description") }
|
||||||
|
/// Always hide
|
||||||
|
internal static var screenAdvancedSettingsShowMediaTimelineAlwaysHide: String { return L10n.tr("Localizable", "screen_advanced_settings_show_media_timeline_always_hide") }
|
||||||
|
/// Always show
|
||||||
|
internal static var screenAdvancedSettingsShowMediaTimelineAlwaysShow: String { return L10n.tr("Localizable", "screen_advanced_settings_show_media_timeline_always_show") }
|
||||||
|
/// In private rooms
|
||||||
|
internal static var screenAdvancedSettingsShowMediaTimelinePrivateRooms: String { return L10n.tr("Localizable", "screen_advanced_settings_show_media_timeline_private_rooms") }
|
||||||
|
/// A hidden media can always be shown by tapping on it
|
||||||
|
internal static var screenAdvancedSettingsShowMediaTimelineSubtitle: String { return L10n.tr("Localizable", "screen_advanced_settings_show_media_timeline_subtitle") }
|
||||||
|
/// Show media in timeline
|
||||||
|
internal static var screenAdvancedSettingsShowMediaTimelineTitle: String { return L10n.tr("Localizable", "screen_advanced_settings_show_media_timeline_title") }
|
||||||
/// Enable option to view message source in the timeline.
|
/// Enable option to view message source in the timeline.
|
||||||
internal static var screenAdvancedSettingsViewSourceDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_view_source_description") }
|
internal static var screenAdvancedSettingsViewSourceDescription: String { return L10n.tr("Localizable", "screen_advanced_settings_view_source_description") }
|
||||||
/// We won't record or profile any personal data
|
/// We won't record or profile any personal data
|
||||||
@@ -1082,6 +1092,8 @@ internal enum L10n {
|
|||||||
internal static var screenBlockedUsersUnblockAlertTitle: String { return L10n.tr("Localizable", "screen_blocked_users_unblock_alert_title") }
|
internal static var screenBlockedUsersUnblockAlertTitle: String { return L10n.tr("Localizable", "screen_blocked_users_unblock_alert_title") }
|
||||||
/// Unblocking…
|
/// Unblocking…
|
||||||
internal static var screenBlockedUsersUnblocking: String { return L10n.tr("Localizable", "screen_blocked_users_unblocking") }
|
internal static var screenBlockedUsersUnblocking: String { return L10n.tr("Localizable", "screen_blocked_users_unblocking") }
|
||||||
|
/// Remove from room
|
||||||
|
internal static var screenBottomSheetManageRoomMemberRemove: String { return L10n.tr("Localizable", "screen_bottom_sheet,manage_room_member_remove") }
|
||||||
/// Send invite
|
/// Send invite
|
||||||
internal static var screenBottomSheetCreateDmConfirmationButtonTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_create_dm_confirmation_button_title") }
|
internal static var screenBottomSheetCreateDmConfirmationButtonTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_create_dm_confirmation_button_title") }
|
||||||
/// Would you like to start a chat with %1$@?
|
/// Would you like to start a chat with %1$@?
|
||||||
@@ -1090,6 +1102,32 @@ internal enum L10n {
|
|||||||
}
|
}
|
||||||
/// Send invite?
|
/// Send invite?
|
||||||
internal static var screenBottomSheetCreateDmTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_create_dm_title") }
|
internal static var screenBottomSheetCreateDmTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_create_dm_title") }
|
||||||
|
/// Ban from room
|
||||||
|
internal static var screenBottomSheetManageRoomMemberBan: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_ban") }
|
||||||
|
/// Ban
|
||||||
|
internal static var screenBottomSheetManageRoomMemberBanMemberConfirmationAction: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_ban_member_confirmation_action") }
|
||||||
|
/// They won’t be able to join this room again if invited.
|
||||||
|
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") }
|
||||||
|
/// Banning %1$@
|
||||||
|
internal static func screenBottomSheetManageRoomMemberBanningUser(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_banning_user", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// Remove
|
||||||
|
internal static var screenBottomSheetManageRoomMemberKickMemberConfirmationAction: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_kick_member_confirmation_action") }
|
||||||
|
/// They will be able to join this room again if invited.
|
||||||
|
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") }
|
||||||
|
/// View profile
|
||||||
|
internal static var screenBottomSheetManageRoomMemberMemberUserInfo: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_member_user_info") }
|
||||||
|
/// Remove member and ban from joining in the future?
|
||||||
|
internal static var screenBottomSheetManageRoomMemberRemoveConfirmationTitle: String { return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_remove_confirmation_title") }
|
||||||
|
/// Removing %1$@…
|
||||||
|
internal static func screenBottomSheetManageRoomMemberRemovingUser(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "screen_bottom_sheet_manage_room_member_removing_user", String(describing: p1))
|
||||||
|
}
|
||||||
/// Attach screenshot
|
/// Attach screenshot
|
||||||
internal static var screenBugReportAttachScreenshot: String { return L10n.tr("Localizable", "screen_bug_report_attach_screenshot") }
|
internal static var screenBugReportAttachScreenshot: String { return L10n.tr("Localizable", "screen_bug_report_attach_screenshot") }
|
||||||
/// You may contact me if you have any follow up questions.
|
/// You may contact me if you have any follow up questions.
|
||||||
@@ -2066,56 +2104,28 @@ internal enum L10n {
|
|||||||
internal static func screenRoomMemberDetailsVerifyButtonTitle(_ p1: Any) -> String {
|
internal static func screenRoomMemberDetailsVerifyButtonTitle(_ p1: Any) -> String {
|
||||||
return L10n.tr("Localizable", "screen_room_member_details_verify_button_title", String(describing: p1))
|
return L10n.tr("Localizable", "screen_room_member_details_verify_button_title", String(describing: p1))
|
||||||
}
|
}
|
||||||
/// Ban
|
|
||||||
internal static var screenRoomMemberListBanMemberConfirmationAction: String { return L10n.tr("Localizable", "screen_room_member_list_ban_member_confirmation_action") }
|
|
||||||
/// They won’t be able to join this room again if invited.
|
|
||||||
internal static var screenRoomMemberListBanMemberConfirmationDescription: String { return L10n.tr("Localizable", "screen_room_member_list_ban_member_confirmation_description") }
|
|
||||||
/// Are you sure you want to ban this member?
|
|
||||||
internal static var screenRoomMemberListBanMemberConfirmationTitle: String { return L10n.tr("Localizable", "screen_room_member_list_ban_member_confirmation_title") }
|
|
||||||
/// There are no banned users in this room.
|
/// There are no banned users in this room.
|
||||||
internal static var screenRoomMemberListBannedEmpty: String { return L10n.tr("Localizable", "screen_room_member_list_banned_empty") }
|
internal static var screenRoomMemberListBannedEmpty: String { return L10n.tr("Localizable", "screen_room_member_list_banned_empty") }
|
||||||
/// Banning %1$@
|
|
||||||
internal static func screenRoomMemberListBanningUser(_ p1: Any) -> String {
|
|
||||||
return L10n.tr("Localizable", "screen_room_member_list_banning_user", String(describing: p1))
|
|
||||||
}
|
|
||||||
/// Plural format key: "%#@COUNT@"
|
/// Plural format key: "%#@COUNT@"
|
||||||
internal static func screenRoomMemberListHeaderTitle(_ p1: Int) -> String {
|
internal static func screenRoomMemberListHeaderTitle(_ p1: Int) -> String {
|
||||||
return L10n.tr("Localizable", "screen_room_member_list_header_title", p1)
|
return L10n.tr("Localizable", "screen_room_member_list_header_title", p1)
|
||||||
}
|
}
|
||||||
/// Remove
|
/// Ban from room
|
||||||
internal static var screenRoomMemberListKickMemberConfirmationAction: String { return L10n.tr("Localizable", "screen_room_member_list_kick_member_confirmation_action") }
|
|
||||||
/// They will be able to join this room again if invited.
|
|
||||||
internal static var screenRoomMemberListKickMemberConfirmationDescription: String { return L10n.tr("Localizable", "screen_room_member_list_kick_member_confirmation_description") }
|
|
||||||
/// Are you sure you want to remove this member?
|
|
||||||
internal static var screenRoomMemberListKickMemberConfirmationTitle: String { return L10n.tr("Localizable", "screen_room_member_list_kick_member_confirmation_title") }
|
|
||||||
/// Remove and ban member
|
|
||||||
internal static var screenRoomMemberListManageMemberBan: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_ban") }
|
|
||||||
/// Remove from room
|
|
||||||
internal static var screenRoomMemberListManageMemberRemove: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove") }
|
|
||||||
/// Remove and ban member
|
|
||||||
internal static var screenRoomMemberListManageMemberRemoveConfirmationBan: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_ban") }
|
internal static var screenRoomMemberListManageMemberRemoveConfirmationBan: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_ban") }
|
||||||
/// Only remove member
|
/// Only remove member
|
||||||
internal static var screenRoomMemberListManageMemberRemoveConfirmationKick: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_kick") }
|
internal static var screenRoomMemberListManageMemberRemoveConfirmationKick: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_kick") }
|
||||||
/// Remove member and ban from joining in the future?
|
|
||||||
internal static var screenRoomMemberListManageMemberRemoveConfirmationTitle: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_title") }
|
|
||||||
/// Unban
|
/// Unban
|
||||||
internal static var screenRoomMemberListManageMemberUnbanAction: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_action") }
|
internal static var screenRoomMemberListManageMemberUnbanAction: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_action") }
|
||||||
/// They will be able to join this room again if invited.
|
/// They will be able to join this room again if invited.
|
||||||
internal static var screenRoomMemberListManageMemberUnbanMessage: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_message") }
|
internal static var screenRoomMemberListManageMemberUnbanMessage: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_message") }
|
||||||
/// Unban user
|
/// Unban user
|
||||||
internal static var screenRoomMemberListManageMemberUnbanTitle: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_title") }
|
internal static var screenRoomMemberListManageMemberUnbanTitle: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_title") }
|
||||||
/// View profile
|
|
||||||
internal static var screenRoomMemberListManageMemberUserInfo: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_user_info") }
|
|
||||||
/// Banned
|
/// Banned
|
||||||
internal static var screenRoomMemberListModeBanned: String { return L10n.tr("Localizable", "screen_room_member_list_mode_banned") }
|
internal static var screenRoomMemberListModeBanned: String { return L10n.tr("Localizable", "screen_room_member_list_mode_banned") }
|
||||||
/// Members
|
/// Members
|
||||||
internal static var screenRoomMemberListModeMembers: String { return L10n.tr("Localizable", "screen_room_member_list_mode_members") }
|
internal static var screenRoomMemberListModeMembers: String { return L10n.tr("Localizable", "screen_room_member_list_mode_members") }
|
||||||
/// Pending
|
/// Pending
|
||||||
internal static var screenRoomMemberListPendingHeaderTitle: String { return L10n.tr("Localizable", "screen_room_member_list_pending_header_title") }
|
internal static var screenRoomMemberListPendingHeaderTitle: String { return L10n.tr("Localizable", "screen_room_member_list_pending_header_title") }
|
||||||
/// Removing %1$@…
|
|
||||||
internal static func screenRoomMemberListRemovingUser(_ p1: Any) -> String {
|
|
||||||
return L10n.tr("Localizable", "screen_room_member_list_removing_user", String(describing: p1))
|
|
||||||
}
|
|
||||||
/// Admin
|
/// Admin
|
||||||
internal static var screenRoomMemberListRoleAdministrator: String { return L10n.tr("Localizable", "screen_room_member_list_role_administrator") }
|
internal static var screenRoomMemberListRoleAdministrator: String { return L10n.tr("Localizable", "screen_room_member_list_role_administrator") }
|
||||||
/// Moderator
|
/// Moderator
|
||||||
@@ -2860,15 +2870,19 @@ internal enum L10n {
|
|||||||
}
|
}
|
||||||
/// Current push provider
|
/// Current push provider
|
||||||
internal static var troubleshootNotificationsTestCurrentPushProviderTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_current_push_provider_title") }
|
internal static var troubleshootNotificationsTestCurrentPushProviderTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_current_push_provider_title") }
|
||||||
/// Ensure that the application has at least one push provider.
|
/// Ensure that the application supports at least one push provider.
|
||||||
internal static var troubleshootNotificationsTestDetectPushProviderDescription: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_description") }
|
internal static var troubleshootNotificationsTestDetectPushProviderDescription: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_description") }
|
||||||
/// No push providers found.
|
/// No push provider support found.
|
||||||
internal static var troubleshootNotificationsTestDetectPushProviderFailure: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_failure") }
|
internal static var troubleshootNotificationsTestDetectPushProviderFailure: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_failure") }
|
||||||
/// Plural format key: "%#@COUNT@"
|
/// Plural format key: "%#@COUNT@"
|
||||||
internal static func troubleshootNotificationsTestDetectPushProviderSuccess(_ p1: Int) -> String {
|
internal static func troubleshootNotificationsTestDetectPushProviderSuccess(_ p1: Int) -> String {
|
||||||
return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_success", p1)
|
return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_success", p1)
|
||||||
}
|
}
|
||||||
/// Detect push providers
|
/// The application was built with support for: %1$@
|
||||||
|
internal static func troubleshootNotificationsTestDetectPushProviderSuccess2(_ p1: Any) -> String {
|
||||||
|
return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_success_2", String(describing: p1))
|
||||||
|
}
|
||||||
|
/// Push provider support
|
||||||
internal static var troubleshootNotificationsTestDetectPushProviderTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_title") }
|
internal static var troubleshootNotificationsTestDetectPushProviderTitle: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_detect_push_provider_title") }
|
||||||
/// Check that the application can display notification.
|
/// Check that the application can display notification.
|
||||||
internal static var troubleshootNotificationsTestDisplayNotificationDescription: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_display_notification_description") }
|
internal static var troubleshootNotificationsTestDisplayNotificationDescription: String { return L10n.tr("Localizable", "troubleshoot_notifications_test_display_notification_description") }
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
enum ManageRoomMemberSheetViewModelAction: Equatable {
|
||||||
|
case dismiss(shouldShowDetails: Bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ManageRoomMemberSheetViewState: BindableState {
|
||||||
|
let member: RoomMemberDetails
|
||||||
|
let canKick: Bool
|
||||||
|
let canBan: Bool
|
||||||
|
|
||||||
|
var bindings = ManageRoomMemberSheetViewStateBindings()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ManageRoomMemberSheetViewStateBindings {
|
||||||
|
var alertInfo: AlertInfo<ManageRoomMemberSheetViewAlertType>?
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ManageRoomMemberSheetViewAlertType {
|
||||||
|
case kick
|
||||||
|
case ban
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ManageRoomMemberSheetViewAction {
|
||||||
|
case kick
|
||||||
|
case ban
|
||||||
|
case displayDetails
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
//
|
||||||
|
// 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 Combine
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
typealias ManageRoomMemberSheetViewModelType = StateStoreViewModel<ManageRoomMemberSheetViewState, ManageRoomMemberSheetViewAction>
|
||||||
|
|
||||||
|
class ManageRoomMemberSheetViewModel: ManageRoomMemberSheetViewModelType, ManageRoomMemberSheetViewModelProtocol {
|
||||||
|
private let roomProxy: JoinedRoomProxyProtocol
|
||||||
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
|
private let analyticsService: AnalyticsService
|
||||||
|
|
||||||
|
private var actionsSubject: PassthroughSubject<ManageRoomMemberSheetViewModelAction, Never> = .init()
|
||||||
|
|
||||||
|
var actions: AnyPublisher<ManageRoomMemberSheetViewModelAction, Never> {
|
||||||
|
actionsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
init(member: RoomMemberDetails,
|
||||||
|
canKick: Bool,
|
||||||
|
canBan: Bool,
|
||||||
|
roomProxy: JoinedRoomProxyProtocol,
|
||||||
|
userIndicatorController: UserIndicatorControllerProtocol,
|
||||||
|
analyticsService: AnalyticsService,
|
||||||
|
mediaProvider: MediaProviderProtocol) {
|
||||||
|
self.userIndicatorController = userIndicatorController
|
||||||
|
self.roomProxy = roomProxy
|
||||||
|
self.analyticsService = analyticsService
|
||||||
|
super.init(initialViewState: .init(member: member, canKick: canKick, canBan: canBan), mediaProvider: mediaProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func process(viewAction: ManageRoomMemberSheetViewAction) {
|
||||||
|
switch viewAction {
|
||||||
|
case .kick:
|
||||||
|
displayAlert(.kick)
|
||||||
|
case .ban:
|
||||||
|
displayAlert(.ban)
|
||||||
|
case .displayDetails:
|
||||||
|
actionsSubject.send(.dismiss(shouldShowDetails: true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func displayAlert(_ alertType: ManageRoomMemberSheetViewAlertType) {
|
||||||
|
let member = state.member
|
||||||
|
var reason: String?
|
||||||
|
let binding: Binding<String> = .init(get: { reason ?? "" },
|
||||||
|
set: { reason = $0.isBlank ? nil : $0 })
|
||||||
|
switch alertType {
|
||||||
|
case .kick:
|
||||||
|
state.bindings.alertInfo = .init(id: alertType,
|
||||||
|
title: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationTitle,
|
||||||
|
message: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationDescription,
|
||||||
|
primaryButton: .init(title: L10n.actionCancel, role: .cancel) { },
|
||||||
|
secondaryButton: .init(title: L10n.screenBottomSheetManageRoomMemberKickMemberConfirmationAction) { [weak self] in Task { await self?.kickMember(member, reason: reason) } },
|
||||||
|
textFields: [.init(placeholder: L10n.commonReason,
|
||||||
|
text: binding,
|
||||||
|
autoCapitalization: .sentences,
|
||||||
|
autoCorrectionDisabled: false)])
|
||||||
|
case .ban:
|
||||||
|
state.bindings.alertInfo = .init(id: alertType,
|
||||||
|
title: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationTitle,
|
||||||
|
message: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationDescription,
|
||||||
|
primaryButton: .init(title: L10n.actionCancel, role: .cancel) { },
|
||||||
|
secondaryButton: .init(title: L10n.screenBottomSheetManageRoomMemberBanMemberConfirmationAction) { [weak self] in Task { await self?.banMember(member, reason: reason) } },
|
||||||
|
textFields: [.init(placeholder: L10n.commonReason,
|
||||||
|
text: binding,
|
||||||
|
autoCapitalization: .sentences,
|
||||||
|
autoCorrectionDisabled: false)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func kickMember(_ member: RoomMemberDetails, reason: String?) async {
|
||||||
|
let indicatorTitle = L10n.screenBottomSheetManageRoomMemberRemovingUser(member.name ?? member.id)
|
||||||
|
showManageMemberIndicator(title: indicatorTitle)
|
||||||
|
|
||||||
|
switch await roomProxy.kickUser(member.id, reason: reason) {
|
||||||
|
case .success:
|
||||||
|
hideManageMemberIndicator(title: indicatorTitle)
|
||||||
|
analyticsService.trackRoomModeration(action: .KickMember, role: nil)
|
||||||
|
actionsSubject.send(.dismiss(shouldShowDetails: false))
|
||||||
|
case .failure:
|
||||||
|
showManageMemberFailure(title: indicatorTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func banMember(_ member: RoomMemberDetails, reason: String?) async {
|
||||||
|
let indicatorTitle = L10n.screenBottomSheetManageRoomMemberBanningUser(member.name ?? member.id)
|
||||||
|
showManageMemberIndicator(title: indicatorTitle)
|
||||||
|
|
||||||
|
switch await roomProxy.banUser(member.id, reason: reason) {
|
||||||
|
case .success:
|
||||||
|
hideManageMemberIndicator(title: indicatorTitle)
|
||||||
|
analyticsService.trackRoomModeration(action: .BanMember, role: nil)
|
||||||
|
actionsSubject.send(.dismiss(shouldShowDetails: false))
|
||||||
|
case .failure:
|
||||||
|
showManageMemberFailure(title: indicatorTitle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showManageMemberIndicator(title: String) {
|
||||||
|
userIndicatorController.submitIndicator(UserIndicator(id: title,
|
||||||
|
type: .toast(progress: .indeterminate),
|
||||||
|
title: title,
|
||||||
|
persistent: true))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func hideManageMemberIndicator(title: String) {
|
||||||
|
userIndicatorController.retractIndicatorWithId(title)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showManageMemberFailure(title: String) {
|
||||||
|
userIndicatorController.retractIndicatorWithId(title)
|
||||||
|
userIndicatorController.submitIndicator(UserIndicator(title: L10n.commonFailed, iconName: "xmark"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ManageRoomMemberSheetViewModel: Identifiable {
|
||||||
|
var id: String { state.member.id }
|
||||||
|
}
|
||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// 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 Combine
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
protocol ManageRoomMemberSheetViewModelProtocol {
|
||||||
|
var actions: AnyPublisher<ManageRoomMemberSheetViewModelAction, Never> { get }
|
||||||
|
var context: ManageRoomMemberSheetViewModelType.Context { get }
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 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
|
||||||
|
|
||||||
|
struct ManageRoomMemberSheetView: View {
|
||||||
|
@ObservedObject var context: ManageRoomMemberSheetViewModelType.Context
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
AvatarHeaderView(member: context.viewState.member,
|
||||||
|
avatarSize: .user(on: .memberDetails),
|
||||||
|
mediaProvider: context.mediaProvider) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
ListRow(label: .default(title: L10n.screenBottomSheetManageRoomMemberMemberUserInfo,
|
||||||
|
icon: \.userProfileSolid),
|
||||||
|
kind: .navigationLink {
|
||||||
|
context.send(viewAction: .displayDetails)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
if context.viewState.canKick {
|
||||||
|
ListRow(label: .default(title: L10n.screenBottomSheetManageRoomMemberRemove,
|
||||||
|
icon: \.close,
|
||||||
|
role: .destructive),
|
||||||
|
kind: .button {
|
||||||
|
context.send(viewAction: .kick)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if context.viewState.canBan {
|
||||||
|
ListRow(label: .default(title: L10n.screenBottomSheetManageRoomMemberBan,
|
||||||
|
icon: \.block,
|
||||||
|
role: .destructive),
|
||||||
|
kind: .button {
|
||||||
|
context.send(viewAction: .ban)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.compoundList()
|
||||||
|
.scrollBounceBehavior(.basedOnSize)
|
||||||
|
.presentationDragIndicator(.visible)
|
||||||
|
.presentationDetents([.large, .fraction(0.67)]) // Maybe find a way to use the ideal height somehow?
|
||||||
|
.alert(item: $context.alertInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ManageRoomMemberSheetView_Previews: PreviewProvider, TestablePreview {
|
||||||
|
static let allActionsViewModel = ManageRoomMemberSheetViewModel.mock()
|
||||||
|
|
||||||
|
static let kickOnlyViewModel = ManageRoomMemberSheetViewModel.mock(canBan: false)
|
||||||
|
|
||||||
|
static let banOnlyViewModel = ManageRoomMemberSheetViewModel.mock(canKick: false)
|
||||||
|
|
||||||
|
static var previews: some View {
|
||||||
|
ManageRoomMemberSheetView(context: allActionsViewModel.context)
|
||||||
|
.previewDisplayName("All Actions")
|
||||||
|
ManageRoomMemberSheetView(context: kickOnlyViewModel.context)
|
||||||
|
.previewDisplayName("Kick Only")
|
||||||
|
ManageRoomMemberSheetView(context: banOnlyViewModel.context)
|
||||||
|
.previewDisplayName("Ban Only")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ManageRoomMemberSheetViewModel {
|
||||||
|
static func mock(canKick: Bool = true,
|
||||||
|
canBan: Bool = true) -> ManageRoomMemberSheetViewModel {
|
||||||
|
let member = RoomMemberDetails(withProxy: RoomMemberProxyMock.mockDan)
|
||||||
|
return ManageRoomMemberSheetViewModel(member: member,
|
||||||
|
canKick: canKick,
|
||||||
|
canBan: canBan,
|
||||||
|
roomProxy: JoinedRoomProxyMock(.init()),
|
||||||
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -80,7 +80,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
self?.displayMediaPreview(mediaPreviewViewModel)
|
self?.displayMediaPreview(mediaPreviewViewModel)
|
||||||
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
|
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
|
||||||
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen,
|
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen,
|
||||||
.tappedOnSenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
|
.displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
|
||||||
.composer, .hasScrolled, .viewInRoomTimeline:
|
.composer, .hasScrolled, .viewInRoomTimeline:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
|||||||
self?.displayMediaPreview(mediaPreviewViewModel)
|
self?.displayMediaPreview(mediaPreviewViewModel)
|
||||||
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
|
case .displayEmojiPicker, .displayReportContent, .displayCameraPicker, .displayMediaPicker,
|
||||||
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen,
|
.displayDocumentPicker, .displayLocationPicker, .displayPollForm, .displayMediaUploadPreviewScreen,
|
||||||
.tappedOnSenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
|
.displaySenderDetails, .displayMessageForwarding, .displayLocation, .displayResolveSendFailure,
|
||||||
.composer, .hasScrolled, .viewInRoomTimeline:
|
.composer, .hasScrolled, .viewInRoomTimeline:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -78,7 +78,7 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .tappedOnSenderDetails(let userID):
|
case .displaySenderDetails(let userID):
|
||||||
actionsSubject.send(.displayUser(userID: userID))
|
actionsSubject.send(.displayUser(userID: userID))
|
||||||
case .displayMessageForwarding(let forwardingItem):
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem))
|
actionsSubject.send(.displayMessageForwarding(forwardingItem: forwardingItem))
|
||||||
|
|||||||
+3
@@ -28,6 +28,9 @@ struct PinnedEventsTimelineScreen: View {
|
|||||||
.background(.compound.bgCanvasDefault)
|
.background(.compound.bgCanvasDefault)
|
||||||
.interactiveDismissDisabled()
|
.interactiveDismissDisabled()
|
||||||
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
||||||
|
.sheet(item: $timelineContext.manageMemberViewModel) {
|
||||||
|
ManageRoomMemberSheetView(context: $0.context)
|
||||||
|
}
|
||||||
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
||||||
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
||||||
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
||||||
|
|||||||
@@ -80,31 +80,15 @@ struct RoomMembersListScreenViewStateBindings {
|
|||||||
var searchQuery = ""
|
var searchQuery = ""
|
||||||
/// The current mode the screen is in.
|
/// The current mode the screen is in.
|
||||||
var mode: RoomMembersListScreenMode = .members
|
var mode: RoomMembersListScreenMode = .members
|
||||||
/// A selected member to kick, ban, promote etc.
|
/// A sheet model for the selected member to kick, ban, promote etc.
|
||||||
var memberToManage: RoomMembersListScreenManagementDetails?
|
var manageMemeberViewModel: ManageRoomMemberSheetViewModel?
|
||||||
|
|
||||||
/// Information describing the currently displayed alert.
|
/// Information describing the currently displayed alert.
|
||||||
var alertInfo: AlertInfo<RoomMembersListScreenAlertType>?
|
var alertInfo: AlertInfo<RoomMembersListScreenAlertType>?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about managing a particular room member.
|
|
||||||
struct RoomMembersListScreenManagementDetails: Identifiable {
|
|
||||||
var id: String { member.id }
|
|
||||||
|
|
||||||
/// The member that is being managed.
|
|
||||||
let member: RoomMemberDetails
|
|
||||||
|
|
||||||
/// A management action that can be performed on the member.
|
|
||||||
enum Action { case kick, ban }
|
|
||||||
/// The management actions available for `member`.
|
|
||||||
let actions: [Action]
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RoomMembersListScreenViewAction {
|
enum RoomMembersListScreenViewAction {
|
||||||
case selectMember(RoomMemberDetails)
|
case selectMember(RoomMemberDetails)
|
||||||
case showMemberDetails(RoomMemberDetails)
|
|
||||||
case kickMember(RoomMemberDetails)
|
|
||||||
case banMember(RoomMemberDetails)
|
|
||||||
case unbanMember(RoomMemberDetails)
|
case unbanMember(RoomMemberDetails)
|
||||||
case invite
|
case invite
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-66
@@ -15,8 +15,10 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
private let roomProxy: JoinedRoomProxyProtocol
|
private let roomProxy: JoinedRoomProxyProtocol
|
||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
private let analytics: AnalyticsService
|
private let analytics: AnalyticsService
|
||||||
|
private let mediaProvider: MediaProviderProtocol
|
||||||
|
|
||||||
private var members: [RoomMemberProxyProtocol] = []
|
private var members: [RoomMemberProxyProtocol] = []
|
||||||
|
private var currentUserProxy: RoomMemberProxyProtocol?
|
||||||
|
|
||||||
private var actionsSubject: PassthroughSubject<RoomMembersListScreenViewModelAction, Never> = .init()
|
private var actionsSubject: PassthroughSubject<RoomMembersListScreenViewModelAction, Never> = .init()
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
self.roomProxy = roomProxy
|
self.roomProxy = roomProxy
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
self.analytics = analytics
|
self.analytics = analytics
|
||||||
|
self.mediaProvider = mediaProvider
|
||||||
|
|
||||||
super.init(initialViewState: .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount,
|
super.init(initialViewState: .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount,
|
||||||
bindings: .init(mode: initialMode)),
|
bindings: .init(mode: initialMode)),
|
||||||
@@ -48,34 +51,6 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .selectMember(let member):
|
case .selectMember(let member):
|
||||||
selectMember(member)
|
selectMember(member)
|
||||||
case .showMemberDetails(let member):
|
|
||||||
showMemberDetails(member)
|
|
||||||
case .kickMember(let member):
|
|
||||||
var reason: String?
|
|
||||||
let binding: Binding<String> = .init(get: { reason ?? "" },
|
|
||||||
set: { reason = $0.isBlank ? nil : $0 })
|
|
||||||
state.bindings.alertInfo = .init(id: .kickConfirmation,
|
|
||||||
title: L10n.screenRoomMemberListKickMemberConfirmationTitle,
|
|
||||||
message: L10n.screenRoomMemberListKickMemberConfirmationDescription,
|
|
||||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel) { },
|
|
||||||
secondaryButton: .init(title: L10n.screenRoomMemberListKickMemberConfirmationAction) { [weak self] in Task { await self?.kickMember(member, reason: reason) } },
|
|
||||||
textFields: [.init(placeholder: L10n.commonReason,
|
|
||||||
text: binding,
|
|
||||||
autoCapitalization: .sentences,
|
|
||||||
autoCorrectionDisabled: false)])
|
|
||||||
case .banMember(let member):
|
|
||||||
var reason: String?
|
|
||||||
let binding: Binding<String> = .init(get: { reason ?? "" },
|
|
||||||
set: { reason = $0.isBlank ? nil : $0 })
|
|
||||||
state.bindings.alertInfo = .init(id: .banConfirmation,
|
|
||||||
title: L10n.screenRoomMemberListBanMemberConfirmationTitle,
|
|
||||||
message: L10n.screenRoomMemberListBanMemberConfirmationDescription,
|
|
||||||
primaryButton: .init(title: L10n.actionCancel, role: .cancel) { },
|
|
||||||
secondaryButton: .init(title: L10n.screenRoomMemberListBanMemberConfirmationAction) { [weak self] in Task { await self?.banMember(member, reason: reason) } },
|
|
||||||
textFields: [.init(placeholder: L10n.commonReason,
|
|
||||||
text: binding,
|
|
||||||
autoCapitalization: .sentences,
|
|
||||||
autoCorrectionDisabled: false)])
|
|
||||||
case .unbanMember(let member):
|
case .unbanMember(let member):
|
||||||
Task { await unbanMember(member) }
|
Task { await unbanMember(member) }
|
||||||
case .invite:
|
case .invite:
|
||||||
@@ -118,6 +93,7 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
let members = members.sorted()
|
let members = members.sorted()
|
||||||
let roomMembersDetails = await buildMembersDetails(members: members)
|
let roomMembersDetails = await buildMembersDetails(members: members)
|
||||||
self.members = members
|
self.members = members
|
||||||
|
self.currentUserProxy = members.first { $0.userID == roomProxy.ownUserID }
|
||||||
|
|
||||||
self.state = .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount,
|
self.state = .init(joinedMembersCount: roomProxy.infoPublisher.value.joinedMembersCount,
|
||||||
joinedMembers: roomMembersDetails.joinedMembers,
|
joinedMembers: roomMembersDetails.joinedMembers,
|
||||||
@@ -168,6 +144,12 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func selectMember(_ member: RoomMemberDetails) {
|
private func selectMember(_ member: RoomMemberDetails) {
|
||||||
|
guard let currentUserProxy,
|
||||||
|
currentUserProxy.powerLevel > member.powerLevel else {
|
||||||
|
showMemberDetails(member)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if member.isBanned { // No need to check canBan here, banned users are only shown when it is true.
|
if member.isBanned { // No need to check canBan here, banned users are only shown when it is true.
|
||||||
state.bindings.alertInfo = AlertInfo(id: .unbanConfirmation(member),
|
state.bindings.alertInfo = AlertInfo(id: .unbanConfirmation(member),
|
||||||
title: L10n.screenRoomMemberListManageMemberUnbanTitle,
|
title: L10n.screenRoomMemberListManageMemberUnbanTitle,
|
||||||
@@ -179,16 +161,26 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var actions = [RoomMembersListScreenManagementDetails.Action]()
|
if state.canKickUsers || state.canBanUsers {
|
||||||
if state.canKickUsers, member.role != .administrator {
|
let manageMemeberViewModel = ManageRoomMemberSheetViewModel(member: member,
|
||||||
actions.append(.kick)
|
canKick: state.canKickUsers,
|
||||||
|
canBan: state.canBanUsers,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: userIndicatorController,
|
||||||
|
analyticsService: analytics,
|
||||||
|
mediaProvider: mediaProvider)
|
||||||
|
manageMemeberViewModel.actions.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
switch action {
|
||||||
|
case .dismiss(let shouldShowDetails):
|
||||||
|
state.bindings.manageMemeberViewModel = nil
|
||||||
|
if shouldShowDetails {
|
||||||
|
showMemberDetails(member)
|
||||||
}
|
}
|
||||||
if state.canBanUsers, member.role != .administrator {
|
|
||||||
actions.append(.ban)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if !actions.isEmpty {
|
.store(in: &cancellables)
|
||||||
state.bindings.memberToManage = .init(member: member, actions: actions)
|
state.bindings.manageMemeberViewModel = manageMemeberViewModel
|
||||||
} else {
|
} else {
|
||||||
showMemberDetails(member)
|
showMemberDetails(member)
|
||||||
}
|
}
|
||||||
@@ -200,46 +192,16 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
actionsSubject.send(.selectMember(member))
|
actionsSubject.send(.selectMember(member))
|
||||||
state.bindings.memberToManage = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Member Management
|
// MARK: - Member Management
|
||||||
|
|
||||||
private func kickMember(_ member: RoomMemberDetails, reason: String?) async {
|
|
||||||
let indicatorTitle = L10n.screenRoomMemberListRemovingUser(member.name ?? member.id)
|
|
||||||
showManageMemberIndicator(title: indicatorTitle)
|
|
||||||
|
|
||||||
switch await roomProxy.kickUser(member.id, reason: reason) {
|
|
||||||
case .success:
|
|
||||||
state.bindings.memberToManage = nil
|
|
||||||
hideManageMemberIndicator(title: indicatorTitle)
|
|
||||||
analytics.trackRoomModeration(action: .KickMember, role: nil)
|
|
||||||
case .failure:
|
|
||||||
showManageMemberFailure(title: indicatorTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func banMember(_ member: RoomMemberDetails, reason: String?) async {
|
|
||||||
let indicatorTitle = L10n.screenRoomMemberListBanningUser(member.name ?? member.id)
|
|
||||||
showManageMemberIndicator(title: indicatorTitle)
|
|
||||||
|
|
||||||
switch await roomProxy.banUser(member.id, reason: reason) {
|
|
||||||
case .success:
|
|
||||||
state.bindings.memberToManage = nil
|
|
||||||
hideManageMemberIndicator(title: indicatorTitle)
|
|
||||||
analytics.trackRoomModeration(action: .BanMember, role: nil)
|
|
||||||
case .failure:
|
|
||||||
showManageMemberFailure(title: indicatorTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func unbanMember(_ member: RoomMemberDetails) async {
|
private func unbanMember(_ member: RoomMemberDetails) async {
|
||||||
let indicatorTitle = L10n.screenRoomMemberListUnbanningUser(member.name ?? member.id)
|
let indicatorTitle = L10n.screenRoomMemberListUnbanningUser(member.name ?? member.id)
|
||||||
showManageMemberIndicator(title: indicatorTitle)
|
showManageMemberIndicator(title: indicatorTitle)
|
||||||
|
|
||||||
switch await roomProxy.unbanUser(member.id) {
|
switch await roomProxy.unbanUser(member.id) {
|
||||||
case .success:
|
case .success:
|
||||||
state.bindings.memberToManage = nil
|
|
||||||
hideManageMemberIndicator(title: indicatorTitle)
|
hideManageMemberIndicator(title: indicatorTitle)
|
||||||
analytics.trackRoomModeration(action: .UnbanMember, role: nil)
|
analytics.trackRoomModeration(action: .UnbanMember, role: nil)
|
||||||
case .failure:
|
case .failure:
|
||||||
|
|||||||
-96
@@ -1,96 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2024 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
|
|
||||||
|
|
||||||
struct RoomMembersListManageMemberSheet: View {
|
|
||||||
let member: RoomMemberDetails
|
|
||||||
let actions: [RoomMembersListScreenManagementDetails.Action]
|
|
||||||
|
|
||||||
@ObservedObject var context: RoomMembersListScreenViewModel.Context
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Form {
|
|
||||||
AvatarHeaderView(member: member,
|
|
||||||
avatarSize: .user(on: .memberDetails),
|
|
||||||
mediaProvider: context.mediaProvider) {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
ListRow(label: .default(title: L10n.screenRoomMemberListManageMemberUserInfo,
|
|
||||||
icon: \.userProfileSolid),
|
|
||||||
kind: .button {
|
|
||||||
context.send(viewAction: .showMemberDetails(member))
|
|
||||||
})
|
|
||||||
|
|
||||||
if actions.contains(.kick) {
|
|
||||||
ListRow(label: .default(title: L10n.screenRoomMemberListManageMemberRemove,
|
|
||||||
icon: \.close),
|
|
||||||
kind: .button {
|
|
||||||
context.send(viewAction: .kickMember(member))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if actions.contains(.ban) {
|
|
||||||
ListRow(label: .default(title: L10n.screenRoomMemberListManageMemberBan,
|
|
||||||
icon: \.block,
|
|
||||||
role: .destructive),
|
|
||||||
kind: .button {
|
|
||||||
context.send(viewAction: .banMember(member))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.compoundList()
|
|
||||||
.scrollBounceBehavior(.basedOnSize)
|
|
||||||
.presentationDragIndicator(.visible)
|
|
||||||
.presentationDetents([.large, .fraction(0.54)]) // Maybe find a way to use the ideal height somehow?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RoomMembersListManageMemberSheet_Previews: PreviewProvider, TestablePreview {
|
|
||||||
static let viewModel = RoomMembersListScreenViewModel.mock
|
|
||||||
|
|
||||||
static var previews: some View {
|
|
||||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockDan),
|
|
||||||
actions: [.kick, .ban],
|
|
||||||
context: viewModel.context)
|
|
||||||
.previewDisplayName("Joined")
|
|
||||||
|
|
||||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockBanned[3]),
|
|
||||||
actions: [],
|
|
||||||
context: viewModel.context)
|
|
||||||
.previewDisplayName("Banned")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RoomMembersListManageMemberSheetLive_Previews: PreviewProvider {
|
|
||||||
static let viewModel = RoomMembersListScreenViewModel.mock
|
|
||||||
|
|
||||||
static var previews: some View {
|
|
||||||
Color.clear
|
|
||||||
.sheet(isPresented: .constant(true)) {
|
|
||||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockDan),
|
|
||||||
actions: [.kick, .ban],
|
|
||||||
context: viewModel.context)
|
|
||||||
}
|
|
||||||
.previewDisplayName("Sheet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension RoomMembersListScreenViewModel {
|
|
||||||
static var mock: RoomMembersListScreenViewModel {
|
|
||||||
RoomMembersListScreenViewModel(initialMode: .members,
|
|
||||||
clientProxy: ClientProxyMock(.init()),
|
|
||||||
roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)),
|
|
||||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
|
||||||
analytics: ServiceLocator.shared.analytics)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -47,8 +47,8 @@ struct RoomMembersListScreen: View {
|
|||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||||
.navigationTitle(L10n.commonPeople)
|
.navigationTitle(L10n.commonPeople)
|
||||||
.sheet(item: $context.memberToManage) {
|
.sheet(item: $context.manageMemeberViewModel) {
|
||||||
RoomMembersListManageMemberSheet(member: $0.member, actions: $0.actions, context: context)
|
ManageRoomMemberSheetView(context: $0.context)
|
||||||
}
|
}
|
||||||
.alert(item: $context.alertInfo)
|
.alert(item: $context.alertInfo)
|
||||||
.toolbar { toolbar }
|
.toolbar { toolbar }
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
|||||||
actionsSubject.send(.presentPollForm(mode: mode))
|
actionsSubject.send(.presentPollForm(mode: mode))
|
||||||
case .displayMediaUploadPreviewScreen(let url):
|
case .displayMediaUploadPreviewScreen(let url):
|
||||||
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
||||||
case .tappedOnSenderDetails(userID: let userID):
|
case .displaySenderDetails(userID: let userID):
|
||||||
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
|
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
|
||||||
case .displayMessageForwarding(let forwardingItem):
|
case .displayMessageForwarding(let forwardingItem):
|
||||||
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
|
actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem))
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ struct RoomScreen: View {
|
|||||||
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
||||||
.overlay { loadingIndicator }
|
.overlay { loadingIndicator }
|
||||||
.alert(item: $timelineContext.alertInfo)
|
.alert(item: $timelineContext.alertInfo)
|
||||||
|
.sheet(item: $timelineContext.manageMemberViewModel) {
|
||||||
|
ManageRoomMemberSheetView(context: $0.context)
|
||||||
|
}
|
||||||
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
||||||
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
||||||
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum TimelineViewModelAction {
|
|||||||
case displayLocationPicker
|
case displayLocationPicker
|
||||||
case displayPollForm(mode: PollFormMode)
|
case displayPollForm(mode: PollFormMode)
|
||||||
case displayMediaUploadPreviewScreen(url: URL)
|
case displayMediaUploadPreviewScreen(url: URL)
|
||||||
case tappedOnSenderDetails(userID: String)
|
case displaySenderDetails(userID: String)
|
||||||
case displayMessageForwarding(forwardingItem: MessageForwardingItem)
|
case displayMessageForwarding(forwardingItem: MessageForwardingItem)
|
||||||
case displayMediaPreview(TimelineMediaPreviewViewModel)
|
case displayMediaPreview(TimelineMediaPreviewViewModel)
|
||||||
case displayLocation(body: String, geoURI: GeoURI, description: String?)
|
case displayLocation(body: String, geoURI: GeoURI, description: String?)
|
||||||
@@ -99,6 +99,8 @@ struct TimelineViewState: BindableState {
|
|||||||
var canCurrentUserRedactOthers = false
|
var canCurrentUserRedactOthers = false
|
||||||
var canCurrentUserRedactSelf = false
|
var canCurrentUserRedactSelf = false
|
||||||
var canCurrentUserPin = false
|
var canCurrentUserPin = false
|
||||||
|
var canCurrentUserKick = false
|
||||||
|
var canCurrentUserBan = false
|
||||||
var isViewSourceEnabled: Bool
|
var isViewSourceEnabled: Bool
|
||||||
var hideTimelineMedia: Bool
|
var hideTimelineMedia: Bool
|
||||||
|
|
||||||
@@ -135,7 +137,7 @@ struct TimelineViewStateBindings {
|
|||||||
/// Key is itemID, value is the collapsed state.
|
/// Key is itemID, value is the collapsed state.
|
||||||
var reactionsCollapsed: [TimelineItemIdentifier: Bool]
|
var reactionsCollapsed: [TimelineItemIdentifier: Bool]
|
||||||
|
|
||||||
var alertInfo: AlertInfo<RoomScreenAlertInfoType>?
|
var alertInfo: AlertInfo<TimelineAlertInfoType>?
|
||||||
|
|
||||||
var debugInfo: TimelineItemDebugInfo?
|
var debugInfo: TimelineItemDebugInfo?
|
||||||
|
|
||||||
@@ -144,6 +146,8 @@ struct TimelineViewStateBindings {
|
|||||||
var reactionSummaryInfo: ReactionSummaryInfo?
|
var reactionSummaryInfo: ReactionSummaryInfo?
|
||||||
|
|
||||||
var readReceiptsSummaryInfo: ReadReceiptSummaryInfo?
|
var readReceiptsSummaryInfo: ReadReceiptSummaryInfo?
|
||||||
|
|
||||||
|
var manageMemberViewModel: ManageRoomMemberSheetViewModel?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TimelineItemActionMenuInfo: Equatable, Identifiable {
|
struct TimelineItemActionMenuInfo: Equatable, Identifiable {
|
||||||
@@ -172,7 +176,7 @@ struct ReadReceiptSummaryInfo: Identifiable {
|
|||||||
let id: TimelineItemIdentifier
|
let id: TimelineItemIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RoomScreenAlertInfoType: Hashable {
|
enum TimelineAlertInfoType: Hashable {
|
||||||
case audioRecodingPermissionError
|
case audioRecodingPermissionError
|
||||||
case pollEndConfirmation(String)
|
case pollEndConfirmation(String)
|
||||||
case sendingFailed
|
case sendingFailed
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
actionsSubject.eraseToAnyPublisher()
|
actionsSubject.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var currentUserProxy: RoomMemberProxyProtocol?
|
||||||
|
|
||||||
private var paginateBackwardsTask: Task<Void, Never>?
|
private var paginateBackwardsTask: Task<Void, Never>?
|
||||||
private var paginateForwardsTask: Task<Void, Never>?
|
private var paginateForwardsTask: Task<Void, Never>?
|
||||||
|
|
||||||
@@ -173,7 +175,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
case .handleTimelineItemMenuAction(let itemID, let action):
|
case .handleTimelineItemMenuAction(let itemID, let action):
|
||||||
timelineInteractionHandler.handleTimelineItemMenuAction(action, itemID: itemID)
|
timelineInteractionHandler.handleTimelineItemMenuAction(action, itemID: itemID)
|
||||||
case .tappedOnSenderDetails(userID: let userID):
|
case .tappedOnSenderDetails(userID: let userID):
|
||||||
actionsSubject.send(.tappedOnSenderDetails(userID: userID))
|
handleTappedOnSenderDetails(userID: userID)
|
||||||
case .displayEmojiPicker(let itemID):
|
case .displayEmojiPicker(let itemID):
|
||||||
timelineInteractionHandler.displayEmojiPicker(for: itemID)
|
timelineInteractionHandler.displayEmojiPicker(for: itemID)
|
||||||
case .displayReactionSummary(let itemID, let key):
|
case .displayReactionSummary(let itemID, let key):
|
||||||
@@ -258,6 +260,41 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func handleTappedOnSenderDetails(userID: String) {
|
||||||
|
// We also need to make sure the user is in the joined state, otherwise we just show the details.
|
||||||
|
guard let memberProxy = roomProxy.membersPublisher.value.first(where: { $0.userID == userID && $0.membership == .join }),
|
||||||
|
let currentUserProxy,
|
||||||
|
currentUserProxy.powerLevel > memberProxy.powerLevel else {
|
||||||
|
actionsSubject.send(.displaySenderDetails(userID: userID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.canCurrentUserBan || state.canCurrentUserKick {
|
||||||
|
let member = RoomMemberDetails(withProxy: memberProxy)
|
||||||
|
let manageMemeberViewModel = ManageRoomMemberSheetViewModel(member: member,
|
||||||
|
canKick: state.canCurrentUserKick,
|
||||||
|
canBan: state.canCurrentUserBan,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: userIndicatorController,
|
||||||
|
analyticsService: analyticsService,
|
||||||
|
mediaProvider: mediaProvider)
|
||||||
|
manageMemeberViewModel.actions.sink { [weak self] action in
|
||||||
|
guard let self else { return }
|
||||||
|
switch action {
|
||||||
|
case .dismiss(let shouldShowDetails):
|
||||||
|
state.bindings.manageMemberViewModel = nil
|
||||||
|
if shouldShowDetails {
|
||||||
|
actionsSubject.send(.displaySenderDetails(userID: userID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
state.bindings.manageMemberViewModel = manageMemeberViewModel
|
||||||
|
} else {
|
||||||
|
actionsSubject.send(.displaySenderDetails(userID: userID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func focusLive() {
|
private func focusLive() {
|
||||||
timelineController.focusLive()
|
timelineController.focusLive()
|
||||||
}
|
}
|
||||||
@@ -348,6 +385,9 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
private func updateMembers(_ members: [RoomMemberProxyProtocol]) {
|
private func updateMembers(_ members: [RoomMemberProxyProtocol]) {
|
||||||
state.members = members.reduce(into: [String: RoomMemberState]()) { dictionary, member in
|
state.members = members.reduce(into: [String: RoomMemberState]()) { dictionary, member in
|
||||||
dictionary[member.userID] = RoomMemberState(displayName: member.displayName, avatarURL: member.avatarURL)
|
dictionary[member.userID] = RoomMemberState(displayName: member.displayName, avatarURL: member.avatarURL)
|
||||||
|
if member.userID == roomProxy.ownUserID {
|
||||||
|
currentUserProxy = member
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,6 +409,18 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
} else {
|
} else {
|
||||||
state.canCurrentUserPin = false
|
state.canCurrentUserPin = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if case let .success(value) = await roomProxy.canUserKick(userID: roomProxy.ownUserID) {
|
||||||
|
state.canCurrentUserKick = value
|
||||||
|
} else {
|
||||||
|
state.canCurrentUserKick = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if case let .success(value) = await roomProxy.canUserBan(userID: roomProxy.ownUserID) {
|
||||||
|
state.canCurrentUserBan = value
|
||||||
|
} else {
|
||||||
|
state.canCurrentUserBan = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupSubscriptions() {
|
private func setupSubscriptions() {
|
||||||
@@ -898,7 +950,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
userIndicatorController.retractIndicatorWithId(Constants.focusTimelineToastIndicatorID)
|
userIndicatorController.retractIndicatorWithId(Constants.focusTimelineToastIndicatorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func displayAlert(_ type: RoomScreenAlertInfoType) {
|
private func displayAlert(_ type: TimelineAlertInfoType) {
|
||||||
switch type {
|
switch type {
|
||||||
case .audioRecodingPermissionError:
|
case .audioRecodingPermissionError:
|
||||||
state.bindings.alertInfo = .init(id: type,
|
state.bindings.alertInfo = .init(id: type,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ struct RoomMemberDetails: Identifiable, Hashable {
|
|||||||
|
|
||||||
enum Role { case administrator, moderator, user }
|
enum Role { case administrator, moderator, user }
|
||||||
let role: Role
|
let role: Role
|
||||||
|
let powerLevel: Int
|
||||||
|
|
||||||
func matches(searchQuery: String) -> Bool {
|
func matches(searchQuery: String) -> Bool {
|
||||||
guard !searchQuery.isEmpty else { return true }
|
guard !searchQuery.isEmpty else { return true }
|
||||||
@@ -38,6 +39,7 @@ extension RoomMemberDetails {
|
|||||||
isIgnored = proxy.isIgnored
|
isIgnored = proxy.isIgnored
|
||||||
isBanned = proxy.membership == .ban
|
isBanned = proxy.membership == .ban
|
||||||
role = .init(proxy.role)
|
role = .init(proxy.role)
|
||||||
|
powerLevel = proxy.powerLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -401,6 +401,12 @@ extension PreviewTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testManageRoomMemberSheetView() async throws {
|
||||||
|
for (index, preview) in ManageRoomMemberSheetView_Previews._allPreviews.enumerated() {
|
||||||
|
try await assertSnapshots(matching: preview, step: index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testMapLibreStaticMapView() async throws {
|
func testMapLibreStaticMapView() async throws {
|
||||||
for (index, preview) in MapLibreStaticMapView_Previews._allPreviews.enumerated() {
|
for (index, preview) in MapLibreStaticMapView_Previews._allPreviews.enumerated() {
|
||||||
try await assertSnapshots(matching: preview, step: index)
|
try await assertSnapshots(matching: preview, step: index)
|
||||||
@@ -707,12 +713,6 @@ extension PreviewTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRoomMembersListManageMemberSheet() async throws {
|
|
||||||
for (index, preview) in RoomMembersListManageMemberSheet_Previews._allPreviews.enumerated() {
|
|
||||||
try await assertSnapshots(matching: preview, step: index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRoomMembersListMemberCell() async throws {
|
func testRoomMembersListMemberCell() async throws {
|
||||||
for (index, preview) in RoomMembersListMemberCell_Previews._allPreviews.enumerated() {
|
for (index, preview) in RoomMembersListMemberCell_Previews._allPreviews.enumerated() {
|
||||||
try await assertSnapshots(matching: preview, step: index)
|
try await assertSnapshots(matching: preview, step: index)
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:838a77d7faa429fb769012c1361ddb6ec96aef85fbd3cbf913fdb934e6ed639d
|
||||||
|
size 195727
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:9708620c69e77bc74e461e5a1f5bf5bc843781fe124bfaf3eaa2b24ff8020c64
|
||||||
|
size 197751
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:b36e8a40c54853a34366ba352736da206a6659ae9291bba3d9f338da44f26f0a
|
||||||
|
size 135362
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:1569bcd5c6e626315326b6275b6d069327af935ba9f8971aea27878be26beb8f
|
||||||
|
size 140550
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:1f6d2a023376318b8d75646ac54d71117cd94f5b5b679747a550f9145d450e11
|
||||||
|
size 190338
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:f3053a7e72c09435efb95e99e6c5ade86c7aa2b186f37c2b3a23207113bd0740
|
||||||
|
size 191560
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:94273ef44799d1d926c96451b8c93f41a4262300691f3dc61b93997b8ff49c02
|
||||||
|
size 129508
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:034aba123a2109820e7d15627ba68826769032a2de2045be4ee0bbdf8e12d76c
|
||||||
|
size 131928
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:7a02e69172fc903b7efb96f4f9e9133a95b6c4db94004ca41b65f07279497e92
|
||||||
|
size 189632
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e215c8f632a632fb1f09d98e98fef71fe8fcf94503492078e60a929f4cea6946
|
||||||
|
size 191198
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:680f38ab808dcfd019e0e925ef187033036ff26287cdc495c6242563ed0466fa
|
||||||
|
size 129401
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4d4cfc4fe5d65b69f8930427a54fc55b83aa3f38dc15978c61f79f12776387e2
|
||||||
|
size 134646
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:69d3907f88971cdeb3024488bad0cc0e8023c8eb3f00770d35f1ceb706cee12e
|
|
||||||
size 93594
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:61569c78e79a50ab1f5430326df0007462d83ffd1b87e1bf75846df7fbebab17
|
|
||||||
size 94307
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:eb501a8fc0b0c2399a25c54d489aa3dd948a114fb015045de9c9204962f6510d
|
|
||||||
size 45831
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:c8ceedc2eacd4a377c8b0e54c41bb621bc465660fd33bf5475bbd6ee55f699da
|
|
||||||
size 47942
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:481e986b2ab0b1bb7bf92fbbd14d1a58cf20e4497653d8b230aebb6a023139c1
|
|
||||||
size 196642
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:21f9d12fc11aaacafbb142dee763ac8d68447094aadb3d9c00910e7a5c11334d
|
|
||||||
size 199499
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:973bf92301cda4c122574b1b3d21ca7f30fcdcf427d821a819f4b02f1e7700d2
|
|
||||||
size 136277
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:c6b7be6ae8cc2b73e04b709f5daed55b20df7258de7135f80539158aca860e94
|
|
||||||
size 145835
|
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
//
|
||||||
|
// 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 XCTest
|
||||||
|
|
||||||
|
@testable import ElementX
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class ManageRoomMemberSheetViewModelTests: XCTestCase {
|
||||||
|
private var viewModel: ManageRoomMemberSheetViewModel!
|
||||||
|
private var context: ManageRoomMemberSheetViewModel.Context! {
|
||||||
|
viewModel.context
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKick() async throws {
|
||||||
|
let testReason = "Kick Test"
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(members: [RoomMemberProxyMock.mockAdmin, RoomMemberProxyMock.mockAlice]))
|
||||||
|
let expectation = XCTestExpectation(description: "Kick member")
|
||||||
|
roomProxy.kickUserReasonClosure = { userID, reason in
|
||||||
|
defer { expectation.fulfill() }
|
||||||
|
XCTAssertEqual(userID, RoomMemberProxyMock.mockAlice.userID)
|
||||||
|
XCTAssertEqual(reason, testReason)
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel = ManageRoomMemberSheetViewModel(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockAlice),
|
||||||
|
canKick: true,
|
||||||
|
canBan: true,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
action == .dismiss(shouldShowDetails: false)
|
||||||
|
}
|
||||||
|
context.send(viewAction: .kick)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
context.alertInfo?.textFields?[0].text.wrappedValue = testReason
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
await fulfillment(of: [expectation])
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBan() async throws {
|
||||||
|
let testReason = "Ban Test"
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(members: [RoomMemberProxyMock.mockAdmin, RoomMemberProxyMock.mockAlice]))
|
||||||
|
let expectation = XCTestExpectation(description: "Ban member")
|
||||||
|
roomProxy.banUserReasonClosure = { userID, reason in
|
||||||
|
defer { expectation.fulfill() }
|
||||||
|
XCTAssertEqual(userID, RoomMemberProxyMock.mockAlice.userID)
|
||||||
|
XCTAssertEqual(reason, testReason)
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel = ManageRoomMemberSheetViewModel(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockAlice),
|
||||||
|
canKick: true,
|
||||||
|
canBan: true,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
||||||
|
context.send(viewAction: .ban)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
action == .dismiss(shouldShowDetails: false)
|
||||||
|
}
|
||||||
|
context.alertInfo?.textFields?[0].text.wrappedValue = testReason
|
||||||
|
context.alertInfo?.secondaryButton?.action?()
|
||||||
|
await fulfillment(of: [expectation])
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisplayDetails() async throws {
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init(members: [RoomMemberProxyMock.mockAdmin, RoomMemberProxyMock.mockAlice]))
|
||||||
|
viewModel = ManageRoomMemberSheetViewModel(member: RoomMemberDetails(withProxy: RoomMemberProxyMock.mockAlice),
|
||||||
|
canKick: true,
|
||||||
|
canBan: true,
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
action == .dismiss(shouldShowDetails: true)
|
||||||
|
}
|
||||||
|
context.send(viewAction: .displayDetails)
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
XCTAssertNil(context.alertInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,7 +143,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// Then the member's details should be shown.
|
// Then the member's details should be shown.
|
||||||
try await memberDetailsAction.fulfill()
|
try await memberDetailsAction.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectUserAsAdmin() async throws {
|
func testSelectUserAsAdmin() async throws {
|
||||||
@@ -151,10 +151,10 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
setup(with: .allMembersAsAdmin)
|
setup(with: .allMembersAsAdmin)
|
||||||
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
|
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
|
|
||||||
// When tapping on a user in the list.
|
// When tapping on a user in the list.
|
||||||
deferred = deferFulfillment(context.$viewState) { $0.bindings.memberToManage != nil }
|
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
|
||||||
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .user && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else {
|
guard let user = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .user && $0.member.id != RoomMemberProxyMock.mockMe.userID })?.member else {
|
||||||
XCTFail("Expected to find a regular user.")
|
XCTFail("Expected to find a regular user.")
|
||||||
return
|
return
|
||||||
@@ -163,8 +163,9 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Then member management should be shown for that user.
|
// Then member management should be shown for that user.
|
||||||
XCTAssertEqual(context.memberToManage?.member, user)
|
XCTAssertEqual(context.manageMemeberViewModel?.state.member, user)
|
||||||
XCTAssertEqual(context.memberToManage?.actions, [.kick, .ban])
|
XCTAssertEqual(context.manageMemeberViewModel?.state.canKick, true)
|
||||||
|
XCTAssertEqual(context.manageMemeberViewModel?.state.canBan, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectModeratorAsAdmin() async throws {
|
func testSelectModeratorAsAdmin() async throws {
|
||||||
@@ -172,10 +173,10 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
setup(with: .allMembersAsAdmin)
|
setup(with: .allMembersAsAdmin)
|
||||||
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
|
var deferred = deferFulfillment(context.$viewState) { !$0.visibleInvitedMembers.isEmpty && $0.canKickUsers && $0.canBanUsers }
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
|
|
||||||
// When tapping on a moderator in the list.
|
// When tapping on a moderator in the list.
|
||||||
deferred = deferFulfillment(context.$viewState) { $0.bindings.memberToManage != nil }
|
deferred = deferFulfillment(context.$viewState) { $0.bindings.manageMemeberViewModel != nil }
|
||||||
guard let moderator = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .moderator })?.member else {
|
guard let moderator = viewModel.state.visibleJoinedMembers.first(where: { $0.member.role == .moderator })?.member else {
|
||||||
XCTFail("Expected to find a moderator.")
|
XCTFail("Expected to find a moderator.")
|
||||||
return
|
return
|
||||||
@@ -184,8 +185,9 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
|
|
||||||
// Then member management should be shown for the moderator.
|
// Then member management should be shown for the moderator.
|
||||||
XCTAssertEqual(context.memberToManage?.member, moderator)
|
XCTAssertEqual(context.manageMemeberViewModel?.state.member, moderator)
|
||||||
XCTAssertEqual(context.memberToManage?.actions, [.kick, .ban])
|
XCTAssertEqual(context.manageMemeberViewModel?.state.canKick, true)
|
||||||
|
XCTAssertEqual(context.manageMemeberViewModel?.state.canBan, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectAdminAsAdmin() async throws {
|
func testSelectAdminAsAdmin() async throws {
|
||||||
@@ -204,7 +206,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// Then the administrator's details should be shown.
|
// Then the administrator's details should be shown.
|
||||||
try await memberDetailsAction.fulfill()
|
try await memberDetailsAction.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectOwnMemberAsAdmin() async throws {
|
func testSelectOwnMemberAsAdmin() async throws {
|
||||||
@@ -223,7 +225,7 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// Then your member's details should be shown.
|
// Then your member's details should be shown.
|
||||||
try await memberDetailsAction.fulfill()
|
try await memberDetailsAction.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSelectBannedMember() async throws {
|
func testSelectBannedMember() async throws {
|
||||||
@@ -243,50 +245,10 @@ class RoomMembersListScreenViewModelTests: XCTestCase {
|
|||||||
|
|
||||||
// Then an alert should be shown to unban the user.
|
// Then an alert should be shown to unban the user.
|
||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
XCTAssertNil(context.memberToManage)
|
XCTAssertNil(context.manageMemeberViewModel)
|
||||||
XCTAssertNotNil(context.alertInfo)
|
XCTAssertNotNil(context.alertInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testKickMember() async throws {
|
|
||||||
setup(with: .allMembersAsAdmin)
|
|
||||||
var deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
let expectation = XCTestExpectation(description: "Ban member")
|
|
||||||
roomProxy.kickUserReasonClosure = { _, _ in
|
|
||||||
defer { expectation.fulfill() }
|
|
||||||
return .success(())
|
|
||||||
}
|
|
||||||
|
|
||||||
context.send(viewAction: .kickMember(viewModel.state.visibleJoinedMembers[0].member))
|
|
||||||
|
|
||||||
deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
context.alertInfo?.secondaryButton?.action?()
|
|
||||||
await fulfillment(of: [expectation])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testBanMember() async throws {
|
|
||||||
setup(with: .allMembersAsAdmin)
|
|
||||||
var deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
let expectation = XCTestExpectation(description: "Ban member")
|
|
||||||
roomProxy.banUserReasonClosure = { _, _ in
|
|
||||||
defer { expectation.fulfill() }
|
|
||||||
return .success(())
|
|
||||||
}
|
|
||||||
|
|
||||||
context.send(viewAction: .banMember(viewModel.state.visibleJoinedMembers[0].member))
|
|
||||||
|
|
||||||
deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
|
||||||
try await deferred.fulfill()
|
|
||||||
|
|
||||||
context.alertInfo?.secondaryButton?.action?()
|
|
||||||
await fulfillment(of: [expectation])
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUnbanMember() async throws {
|
func testUnbanMember() async throws {
|
||||||
setup(with: .allMembersAsAdmin)
|
setup(with: .allMembersAsAdmin)
|
||||||
let deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
|
let deferred = deferFulfillment(context.$viewState) { !$0.visibleJoinedMembers.isEmpty }
|
||||||
|
|||||||
@@ -350,6 +350,117 @@ class TimelineViewModelTests: XCTestCase {
|
|||||||
try await deferred.fulfill()
|
try await deferred.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testShowManageUserAsAdmin() async throws {
|
||||||
|
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
|
||||||
|
members: [RoomMemberProxyMock.mockAdmin,
|
||||||
|
RoomMemberProxyMock.mockAlice],
|
||||||
|
ownUserID: RoomMemberProxyMock.mockAdmin.userID)),
|
||||||
|
timelineController: MockTimelineController(),
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||||
|
userIndicatorController: userIndicatorControllerMock,
|
||||||
|
appMediator: AppMediatorMock.default,
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||||
|
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
|
||||||
|
clientProxy: ClientProxyMock(.init()))
|
||||||
|
|
||||||
|
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
|
value.canCurrentUserKick && value.canCurrentUserBan
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
deferred = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
|
value.bindings.manageMemberViewModel != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.context.send(viewAction: .tappedOnSenderDetails(userID: RoomMemberProxyMock.mockAlice.userID))
|
||||||
|
try await deferred.fulfill()
|
||||||
|
|
||||||
|
XCTAssertEqual(viewModel.context.manageMemberViewModel?.state.member.id, RoomMemberProxyMock.mockAlice.userID)
|
||||||
|
XCTAssertEqual(viewModel.context.manageMemberViewModel?.state.canBan, true)
|
||||||
|
XCTAssertEqual(viewModel.context.manageMemberViewModel?.state.canKick, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testShowDetailsForAnAdmin() async throws {
|
||||||
|
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
|
||||||
|
members: [RoomMemberProxyMock.mockAdmin,
|
||||||
|
RoomMemberProxyMock.mockAlice],
|
||||||
|
ownUserID: RoomMemberProxyMock.mockAlice.userID)),
|
||||||
|
timelineController: MockTimelineController(),
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||||
|
userIndicatorController: userIndicatorControllerMock,
|
||||||
|
appMediator: AppMediatorMock.default,
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||||
|
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
|
||||||
|
clientProxy: ClientProxyMock(.init()))
|
||||||
|
|
||||||
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
|
!value.canCurrentUserKick && !value.canCurrentUserBan
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .displaySenderDetails(let userID):
|
||||||
|
return userID == RoomMemberProxyMock.mockAdmin.userID
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.context.send(viewAction: .tappedOnSenderDetails(userID: RoomMemberProxyMock.mockAdmin.userID))
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
|
||||||
|
XCTAssertNil(viewModel.context.manageMemberViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testShowDetailsForABannedUser() async throws {
|
||||||
|
let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "",
|
||||||
|
members: [RoomMemberProxyMock.mockAdmin,
|
||||||
|
RoomMemberProxyMock.mockBanned[0]],
|
||||||
|
ownUserID: RoomMemberProxyMock.mockAdmin.userID)),
|
||||||
|
timelineController: MockTimelineController(),
|
||||||
|
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||||
|
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||||
|
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||||
|
userIndicatorController: userIndicatorControllerMock,
|
||||||
|
appMediator: AppMediatorMock.default,
|
||||||
|
appSettings: ServiceLocator.shared.settings,
|
||||||
|
analyticsService: ServiceLocator.shared.analytics,
|
||||||
|
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||||
|
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
|
||||||
|
clientProxy: ClientProxyMock(.init()))
|
||||||
|
|
||||||
|
let deferredState = deferFulfillment(viewModel.context.$viewState) { value in
|
||||||
|
value.canCurrentUserKick && value.canCurrentUserBan
|
||||||
|
}
|
||||||
|
|
||||||
|
try await deferredState.fulfill()
|
||||||
|
|
||||||
|
let deferredAction = deferFulfillment(viewModel.actions) { action in
|
||||||
|
switch action {
|
||||||
|
case .displaySenderDetails(let userID):
|
||||||
|
return userID == RoomMemberProxyMock.mockBanned[0].userID
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.context.send(viewAction: .tappedOnSenderDetails(userID: RoomMemberProxyMock.mockBanned[0].userID))
|
||||||
|
try await deferredAction.fulfill()
|
||||||
|
|
||||||
|
XCTAssertNil(viewModel.context.manageMemberViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Pins
|
// MARK: - Pins
|
||||||
|
|
||||||
func testPinnedEvents() async throws {
|
func testPinnedEvents() async throws {
|
||||||
|
|||||||
Reference in New Issue
Block a user