mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
Move any visible account settings into AccountSettings. (#6308)
* Remove the now unused setting to disable in-app notifications. * Move visible account settings into AccountSettings. Have ignored enableNotifications for now as that will require reworking the NotificationManager. * Allow different sounds to be selected for each account. * Move defaultNotificationTone into AccountSettings.
This commit is contained in:
@@ -81,6 +81,7 @@
|
|||||||
09693596AA3CA4E598C0D2F1 /* ThreadTimelineScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73FEE625AB52042049DB9268 /* ThreadTimelineScreenCoordinator.swift */; };
|
09693596AA3CA4E598C0D2F1 /* ThreadTimelineScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73FEE625AB52042049DB9268 /* ThreadTimelineScreenCoordinator.swift */; };
|
||||||
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; };
|
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; };
|
||||||
09D3D7D115318CAD131B4FE7 /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57084488B03BDB33C7B7CA0E /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift */; };
|
09D3D7D115318CAD131B4FE7 /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57084488B03BDB33C7B7CA0E /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift */; };
|
||||||
|
09EBBB7F7384C5D5C03BC324 /* NSEAccountSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 038DAB8462278EA64753315D /* NSEAccountSettings.swift */; };
|
||||||
0AB4FF7645175FF1F6E942CC /* DeferredFulfillment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B91AD590B0B40718A0AA0C61 /* DeferredFulfillment.swift */; };
|
0AB4FF7645175FF1F6E942CC /* DeferredFulfillment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B91AD590B0B40718A0AA0C61 /* DeferredFulfillment.swift */; };
|
||||||
0ACAA31FD0399CEEBA3ECC21 /* UserDetailsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85149F56BA333619900E2410 /* UserDetailsEditScreenViewModelProtocol.swift */; };
|
0ACAA31FD0399CEEBA3ECC21 /* UserDetailsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85149F56BA333619900E2410 /* UserDetailsEditScreenViewModelProtocol.swift */; };
|
||||||
0AD8EF040A60D62F488C18B5 /* KnockRequestProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */; };
|
0AD8EF040A60D62F488C18B5 /* KnockRequestProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F957320D0EB7D7B4E30C79D /* KnockRequestProxyMock.swift */; };
|
||||||
@@ -188,6 +189,7 @@
|
|||||||
1AE4532D92E6ED42BFE87064 /* JoinRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A566F8F2C27B99E1FB80C69B /* JoinRule.swift */; };
|
1AE4532D92E6ED42BFE87064 /* JoinRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = A566F8F2C27B99E1FB80C69B /* JoinRule.swift */; };
|
||||||
1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; };
|
1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; };
|
||||||
1AF4A82B4332CAD2DB3EB5DA /* TopBannerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78483F0143E185EDC6ECD741 /* TopBannerModifier.swift */; };
|
1AF4A82B4332CAD2DB3EB5DA /* TopBannerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78483F0143E185EDC6ECD741 /* TopBannerModifier.swift */; };
|
||||||
|
1AF6BFB40515EB31B9485665 /* NSENotificationTone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060DA1A559FE85CF65ADFE07 /* NSENotificationTone.swift */; };
|
||||||
1B2DADC008EE211AF1DA5292 /* NotificationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */; };
|
1B2DADC008EE211AF1DA5292 /* NotificationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */; };
|
||||||
1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */; };
|
1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */; };
|
||||||
1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */; };
|
1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */; };
|
||||||
@@ -523,6 +525,7 @@
|
|||||||
51263FC33CC961A14F5D6BCA /* TracingHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A95C9B8299A36A6495DECA6 /* TracingHook.swift */; };
|
51263FC33CC961A14F5D6BCA /* TracingHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A95C9B8299A36A6495DECA6 /* TracingHook.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 */; };
|
||||||
|
5152FF71D3AE01A1ADF0FC62 /* NSEAccountSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 038DAB8462278EA64753315D /* NSEAccountSettings.swift */; };
|
||||||
51A075328D68FB4AE65C43B5 /* SearchServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD6FFE581238BD30B53BA0A /* SearchServiceProxy.swift */; };
|
51A075328D68FB4AE65C43B5 /* SearchServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD6FFE581238BD30B53BA0A /* SearchServiceProxy.swift */; };
|
||||||
51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; };
|
51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; };
|
||||||
51DBB7D725080FDAA4AB1412 /* UserSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE997F400545F3265935B53 /* UserSettingsTests.swift */; };
|
51DBB7D725080FDAA4AB1412 /* UserSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE997F400545F3265935B53 /* UserSettingsTests.swift */; };
|
||||||
@@ -723,6 +726,7 @@
|
|||||||
709A9B52FC26B4CB86B8B020 /* EncryptedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F06F70B9C433BAD4BC6B9F5 /* EncryptedRoomTimelineView.swift */; };
|
709A9B52FC26B4CB86B8B020 /* EncryptedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F06F70B9C433BAD4BC6B9F5 /* EncryptedRoomTimelineView.swift */; };
|
||||||
70A4C56E4E6C60A3124631FC /* ModerationAndSafetySettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8465F252997056A1E094204E /* ModerationAndSafetySettingsScreenModels.swift */; };
|
70A4C56E4E6C60A3124631FC /* ModerationAndSafetySettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8465F252997056A1E094204E /* ModerationAndSafetySettingsScreenModels.swift */; };
|
||||||
70B83D44043293B4B77440B9 /* PollFormScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */; };
|
70B83D44043293B4B77440B9 /* PollFormScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */; };
|
||||||
|
70D5D5E3ED6C3AD2449B382E /* NSENotificationTone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 060DA1A559FE85CF65ADFE07 /* NSENotificationTone.swift */; };
|
||||||
715FB2D768E8919979EACD71 /* StopButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A786DE47C0DBCE0CD163193 /* StopButton.swift */; };
|
715FB2D768E8919979EACD71 /* StopButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A786DE47C0DBCE0CD163193 /* StopButton.swift */; };
|
||||||
71643093F87153F633A1B025 /* ThreadDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA4F09CB613C54FDC73AE6A /* ThreadDecorator.swift */; };
|
71643093F87153F633A1B025 /* ThreadDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DA4F09CB613C54FDC73AE6A /* ThreadDecorator.swift */; };
|
||||||
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
|
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
|
||||||
@@ -749,7 +753,6 @@
|
|||||||
754602A7B2AAD443C4228ED4 /* ElementCall in Frameworks */ = {isa = PBXBuildFile; productRef = 99570A04BD7A395538321E94 /* ElementCall */; };
|
754602A7B2AAD443C4228ED4 /* ElementCall in Frameworks */ = {isa = PBXBuildFile; productRef = 99570A04BD7A395538321E94 /* ElementCall */; };
|
||||||
755395927DDD6EBDDA5E217A /* SettingsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */; };
|
755395927DDD6EBDDA5E217A /* SettingsFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */; };
|
||||||
755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */; };
|
755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */; };
|
||||||
756BD0B80C1AF2A6DF0786CB /* CommonSettings+NotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7A66212DC7D097886BD01F /* CommonSettings+NotificationName.swift */; };
|
|
||||||
756EA0D663261889EF64E6D4 /* VoiceMessageRecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */; };
|
756EA0D663261889EF64E6D4 /* VoiceMessageRecordingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E9CBF577B9711CFBB4FA40D /* VoiceMessageRecordingView.swift */; };
|
||||||
7573D682F089205F7F1D96CF /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; };
|
7573D682F089205F7F1D96CF /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; };
|
||||||
757862045774A0F458357E19 /* RoomThreadListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544B9262E1BC6F489C03FFFA /* RoomThreadListScreen.swift */; };
|
757862045774A0F458357E19 /* RoomThreadListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 544B9262E1BC6F489C03FFFA /* RoomThreadListScreen.swift */; };
|
||||||
@@ -885,7 +888,6 @@
|
|||||||
88C8B87A26D6B4E731F2B5B7 /* SpacesTabFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4930A2674306A2DC9C92009D /* SpacesTabFlowCoordinator.swift */; };
|
88C8B87A26D6B4E731F2B5B7 /* SpacesTabFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4930A2674306A2DC9C92009D /* SpacesTabFlowCoordinator.swift */; };
|
||||||
88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */; };
|
88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */; };
|
||||||
88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.swift */; };
|
88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.swift */; };
|
||||||
88FF7B41DF76E22E6835F3C3 /* CommonSettings+NotificationName.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB7A66212DC7D097886BD01F /* CommonSettings+NotificationName.swift */; };
|
|
||||||
890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C687844F60BFF532D49A994C /* AnalyticsTests.swift */; };
|
890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C687844F60BFF532D49A994C /* AnalyticsTests.swift */; };
|
||||||
89198AE2649DD77673D5793B /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
|
89198AE2649DD77673D5793B /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
|
||||||
8944548A684F1C837CEC47F4 /* RoomMembersListScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */; };
|
8944548A684F1C837CEC47F4 /* RoomMembersListScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */; };
|
||||||
@@ -970,6 +972,7 @@
|
|||||||
9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; };
|
9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; };
|
||||||
94871CA883F44022086FE750 /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98784280D98C852727BE0111 /* AuthenticationStartLogo.swift */; };
|
94871CA883F44022086FE750 /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98784280D98C852727BE0111 /* AuthenticationStartLogo.swift */; };
|
||||||
94A65DD8A353DF112EBEF67A /* SessionVerificationControllerProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D56469A9EE0CFA2B7BA9760 /* SessionVerificationControllerProxyProtocol.swift */; };
|
94A65DD8A353DF112EBEF67A /* SessionVerificationControllerProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D56469A9EE0CFA2B7BA9760 /* SessionVerificationControllerProxyProtocol.swift */; };
|
||||||
|
94B6EDC626DBECADF727879C /* UserSessionHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9773983346838AABFAA914D /* UserSessionHook.swift */; };
|
||||||
94C2B531B96493B68B976E5F /* SpaceServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A56746AD99ABDA97189E1 /* SpaceServiceProxy.swift */; };
|
94C2B531B96493B68B976E5F /* SpaceServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 226A56746AD99ABDA97189E1 /* SpaceServiceProxy.swift */; };
|
||||||
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
|
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
|
||||||
94D9172C4B91FC6971C668CE /* LeaveSpaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE4B7AD74E402BBC46A801A /* LeaveSpaceView.swift */; };
|
94D9172C4B91FC6971C668CE /* LeaveSpaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE4B7AD74E402BBC46A801A /* LeaveSpaceView.swift */; };
|
||||||
@@ -1061,6 +1064,7 @@
|
|||||||
A2357AA4A188BC37085BC6F0 /* EditRoomAddressScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5FDFAA04174CC99FB66391C /* EditRoomAddressScreenViewModel.swift */; };
|
A2357AA4A188BC37085BC6F0 /* EditRoomAddressScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5FDFAA04174CC99FB66391C /* EditRoomAddressScreenViewModel.swift */; };
|
||||||
A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
|
A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
|
||||||
A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
|
A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
|
||||||
|
A30AAE44E872674D77B5D972 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; };
|
||||||
A32384E3D85CA65342D3A908 /* TimelineMediaPreviewRedactConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75FE3F524B575D53787868C /* TimelineMediaPreviewRedactConfirmationView.swift */; };
|
A32384E3D85CA65342D3A908 /* TimelineMediaPreviewRedactConfirmationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75FE3F524B575D53787868C /* TimelineMediaPreviewRedactConfirmationView.swift */; };
|
||||||
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; };
|
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; };
|
||||||
A36AD251013402EDBD666C75 /* AppMediatorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAC027034248429A438886B /* AppMediatorMock.swift */; };
|
A36AD251013402EDBD666C75 /* AppMediatorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAC027034248429A438886B /* AppMediatorMock.swift */; };
|
||||||
@@ -1260,6 +1264,7 @@
|
|||||||
C486DA7A3E05BAA1E389D280 /* AuthenticationStartScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F69820F717FF11C6309916 /* AuthenticationStartScreenCoordinator.swift */; };
|
C486DA7A3E05BAA1E389D280 /* AuthenticationStartScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F69820F717FF11C6309916 /* AuthenticationStartScreenCoordinator.swift */; };
|
||||||
C49FCC766673006B6D299F1C /* RoomDetailsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */; };
|
C49FCC766673006B6D299F1C /* RoomDetailsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */; };
|
||||||
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */; };
|
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2E9B841EE4878283ECDB554 /* InviteUsersScreen.swift */; };
|
||||||
|
C4E035CCFC3C457ED2F07528 /* UserSessionHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9773983346838AABFAA914D /* UserSessionHook.swift */; };
|
||||||
C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */; };
|
C4E0D03DF88242697545A9B7 /* UserIndicatorController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */; };
|
||||||
C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */; };
|
C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */; };
|
||||||
C525F6C2892CB0640E776B3D /* OAuthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */; };
|
C525F6C2892CB0640E776B3D /* OAuthConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 002DE5E9625122F37DE5F020 /* OAuthConfiguration.swift */; };
|
||||||
@@ -1806,6 +1811,7 @@
|
|||||||
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
||||||
0367E0AF1163F42292D788BD /* StartChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatTests.swift; sourceTree = "<group>"; };
|
0367E0AF1163F42292D788BD /* StartChatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatTests.swift; sourceTree = "<group>"; };
|
||||||
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
||||||
|
038DAB8462278EA64753315D /* NSEAccountSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEAccountSettings.swift; sourceTree = "<group>"; };
|
||||||
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = "<group>"; };
|
044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = "<group>"; };
|
||||||
@@ -1826,6 +1832,7 @@
|
|||||||
05A3E8741D199CD1A37F4CBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
05A3E8741D199CD1A37F4CBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||||
05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = "<group>"; };
|
05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = "<group>"; };
|
05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
060DA1A559FE85CF65ADFE07 /* NSENotificationTone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSENotificationTone.swift; sourceTree = "<group>"; };
|
||||||
06775BFC4D00C62B7E1353A8 /* SettingsScreenUserStatusPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUserStatusPickerView.swift; sourceTree = "<group>"; };
|
06775BFC4D00C62B7E1353A8 /* SettingsScreenUserStatusPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUserStatusPickerView.swift; sourceTree = "<group>"; };
|
||||||
0685156EB62D7E243F097CFC /* ServerSelectionScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
0685156EB62D7E243F097CFC /* ServerSelectionScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
06B098A612DCB5A7358EECD5 /* DeveloperOptionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenModels.swift; sourceTree = "<group>"; };
|
06B098A612DCB5A7358EECD5 /* DeveloperOptionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
@@ -2815,7 +2822,6 @@
|
|||||||
AB07F03461023BC39C730922 /* PhishingDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhishingDetector.swift; sourceTree = "<group>"; };
|
AB07F03461023BC39C730922 /* PhishingDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhishingDetector.swift; sourceTree = "<group>"; };
|
||||||
AB26D5444A4A7E095222DE8B /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
AB26D5444A4A7E095222DE8B /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
AB389C38BD41EB3E47092CFB /* AccessibilityTests.xctestplan */ = {isa = PBXFileReference; path = AccessibilityTests.xctestplan; sourceTree = "<group>"; };
|
AB389C38BD41EB3E47092CFB /* AccessibilityTests.xctestplan */ = {isa = PBXFileReference; path = AccessibilityTests.xctestplan; sourceTree = "<group>"; };
|
||||||
AB7A66212DC7D097886BD01F /* CommonSettings+NotificationName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommonSettings+NotificationName.swift"; sourceTree = "<group>"; };
|
|
||||||
AB7D79FA27D094146E2B5C9D /* TimelineMediaContentScanningFailureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaContentScanningFailureView.swift; sourceTree = "<group>"; };
|
AB7D79FA27D094146E2B5C9D /* TimelineMediaContentScanningFailureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaContentScanningFailureView.swift; sourceTree = "<group>"; };
|
||||||
ABF84AA68B2B7584D9275769 /* VoiceMessageTrashButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageTrashButton.swift; sourceTree = "<group>"; };
|
ABF84AA68B2B7584D9275769 /* VoiceMessageTrashButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageTrashButton.swift; sourceTree = "<group>"; };
|
||||||
AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModelTests.swift; sourceTree = "<group>"; };
|
AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
@@ -5832,11 +5838,11 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
6F054DE7D47849687662C9D9 /* BootDetectionManager.swift */,
|
6F054DE7D47849687662C9D9 /* BootDetectionManager.swift */,
|
||||||
AB7A66212DC7D097886BD01F /* CommonSettings+NotificationName.swift */,
|
|
||||||
D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */,
|
D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */,
|
||||||
BB6ED50FE104992419310EEB /* NotificationHandler.swift */,
|
BB6ED50FE104992419310EEB /* NotificationHandler.swift */,
|
||||||
27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */,
|
27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */,
|
||||||
6C9651CD1066F239C7739240 /* NSEUserSession.swift */,
|
6C9651CD1066F239C7739240 /* NSEUserSession.swift */,
|
||||||
|
C7EA0DC1683A468E57E933C4 /* Extensions */,
|
||||||
10450069C1510FAFAEABB4AA /* NotificationItemProxy */,
|
10450069C1510FAFAEABB4AA /* NotificationItemProxy */,
|
||||||
);
|
);
|
||||||
path = Sources;
|
path = Sources;
|
||||||
@@ -6834,6 +6840,15 @@
|
|||||||
path = SecurityAndPrivacyScreen;
|
path = SecurityAndPrivacyScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C7EA0DC1683A468E57E933C4 /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
038DAB8462278EA64753315D /* NSEAccountSettings.swift */,
|
||||||
|
060DA1A559FE85CF65ADFE07 /* NSENotificationTone.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C844840F3DD48A154C65AE0C /* View */ = {
|
C844840F3DD48A154C65AE0C /* View */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -8366,7 +8381,6 @@
|
|||||||
DA6E57CB533213D438001A31 /* ClientFactory.swift in Sources */,
|
DA6E57CB533213D438001A31 /* ClientFactory.swift in Sources */,
|
||||||
9AF73FA23C86B45801EE2949 /* ClientFactoryHook.swift in Sources */,
|
9AF73FA23C86B45801EE2949 /* ClientFactoryHook.swift in Sources */,
|
||||||
039FE1757084092146C0B616 /* ClientFactoryProtocol.swift in Sources */,
|
039FE1757084092146C0B616 /* ClientFactoryProtocol.swift in Sources */,
|
||||||
88FF7B41DF76E22E6835F3C3 /* CommonSettings+NotificationName.swift in Sources */,
|
|
||||||
211B5F524E851178EE549417 /* CurrentValuePublisher.swift in Sources */,
|
211B5F524E851178EE549417 /* CurrentValuePublisher.swift in Sources */,
|
||||||
24A75F72EEB7561B82D726FD /* Date.swift in Sources */,
|
24A75F72EEB7561B82D726FD /* Date.swift in Sources */,
|
||||||
FE9A5D4715C7AB68682C030C /* Dictionary.swift in Sources */,
|
FE9A5D4715C7AB68682C030C /* Dictionary.swift in Sources */,
|
||||||
@@ -8392,6 +8406,8 @@
|
|||||||
9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */,
|
9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */,
|
||||||
7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */,
|
7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */,
|
||||||
E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */,
|
E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */,
|
||||||
|
09EBBB7F7384C5D5C03BC324 /* NSEAccountSettings.swift in Sources */,
|
||||||
|
70D5D5E3ED6C3AD2449B382E /* NSENotificationTone.swift in Sources */,
|
||||||
4CB30D9BD72CA3FEEFAA0793 /* NSEUserSession.swift in Sources */,
|
4CB30D9BD72CA3FEEFAA0793 /* NSEUserSession.swift in Sources */,
|
||||||
1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */,
|
1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */,
|
||||||
4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */,
|
4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */,
|
||||||
@@ -8434,6 +8450,7 @@
|
|||||||
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */,
|
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */,
|
||||||
34357B287357BC0B9715DD51 /* UserAgentBuilder.swift in Sources */,
|
34357B287357BC0B9715DD51 /* UserAgentBuilder.swift in Sources */,
|
||||||
90F7C2C3DF76B3B00B58CB57 /* UserDefaultsProtocol.swift in Sources */,
|
90F7C2C3DF76B3B00B58CB57 /* UserDefaultsProtocol.swift in Sources */,
|
||||||
|
94B6EDC626DBECADF727879C /* UserSessionHook.swift in Sources */,
|
||||||
D61FCF6ABC1490F630E76AEC /* UserSettings.swift in Sources */,
|
D61FCF6ABC1490F630E76AEC /* UserSettings.swift in Sources */,
|
||||||
AF75E5CF932D80EAD578E02E /* VolatileUserDefaults.swift in Sources */,
|
AF75E5CF932D80EAD578E02E /* VolatileUserDefaults.swift in Sources */,
|
||||||
);
|
);
|
||||||
@@ -8682,6 +8699,7 @@
|
|||||||
DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */,
|
DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */,
|
||||||
5AA81A4E2D40A32A9E7F71F2 /* ShareExtensionView.swift in Sources */,
|
5AA81A4E2D40A32A9E7F71F2 /* ShareExtensionView.swift in Sources */,
|
||||||
5AC5CD6D893073EE4D9A277E /* ShareExtensionViewController.swift in Sources */,
|
5AC5CD6D893073EE4D9A277E /* ShareExtensionViewController.swift in Sources */,
|
||||||
|
A30AAE44E872674D77B5D972 /* Strings.swift in Sources */,
|
||||||
6E03A710799E6C65C0AB36BC /* TargetConfiguration.swift in Sources */,
|
6E03A710799E6C65C0AB36BC /* TargetConfiguration.swift in Sources */,
|
||||||
A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */,
|
A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */,
|
||||||
89DF67AECBF9D0EE0DDB7737 /* Tracing.swift in Sources */,
|
89DF67AECBF9D0EE0DDB7737 /* Tracing.swift in Sources */,
|
||||||
@@ -8691,6 +8709,7 @@
|
|||||||
26252AA9AED64010788F4C26 /* UIView.swift in Sources */,
|
26252AA9AED64010788F4C26 /* UIView.swift in Sources */,
|
||||||
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */,
|
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */,
|
||||||
9F51563FBDD578F37020C852 /* UserDefaultsProtocol.swift in Sources */,
|
9F51563FBDD578F37020C852 /* UserDefaultsProtocol.swift in Sources */,
|
||||||
|
C4E035CCFC3C457ED2F07528 /* UserSessionHook.swift in Sources */,
|
||||||
452507BCE97B3EA0F24E206E /* UserSettings.swift in Sources */,
|
452507BCE97B3EA0F24E206E /* UserSettings.swift in Sources */,
|
||||||
0B30DC7D9D445C405ADCB5FA /* VolatileUserDefaults.swift in Sources */,
|
0B30DC7D9D445C405ADCB5FA /* VolatileUserDefaults.swift in Sources */,
|
||||||
);
|
);
|
||||||
@@ -8895,7 +8914,6 @@
|
|||||||
78A3D84BA47DAC69B4D0A34C /* CollapsibleRoomTimelineView.swift in Sources */,
|
78A3D84BA47DAC69B4D0A34C /* CollapsibleRoomTimelineView.swift in Sources */,
|
||||||
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */,
|
0DC815CA24E1BD7F408F37D3 /* CollapsibleTimelineItem.swift in Sources */,
|
||||||
663E198678778F7426A9B27D /* Collection.swift in Sources */,
|
663E198678778F7426A9B27D /* Collection.swift in Sources */,
|
||||||
756BD0B80C1AF2A6DF0786CB /* CommonSettings+NotificationName.swift in Sources */,
|
|
||||||
24B7CD41342C143117ADA768 /* Comparable.swift in Sources */,
|
24B7CD41342C143117ADA768 /* Comparable.swift in Sources */,
|
||||||
19DF5600A7F547B22DD7872A /* CompletionSuggestionService.swift in Sources */,
|
19DF5600A7F547B22DD7872A /* CompletionSuggestionService.swift in Sources */,
|
||||||
EAB3C1F0BC7F671ED8BDF82D /* CompletionSuggestionServiceProtocol.swift in Sources */,
|
EAB3C1F0BC7F671ED8BDF82D /* CompletionSuggestionServiceProtocol.swift in Sources */,
|
||||||
@@ -9240,6 +9258,8 @@
|
|||||||
5846723185B444D52E077874 /* ModerationAndSafetySettingsScreenViewModel.swift in Sources */,
|
5846723185B444D52E077874 /* ModerationAndSafetySettingsScreenViewModel.swift in Sources */,
|
||||||
7C8F9E3AF56E7FF5B338DBFA /* ModerationAndSafetySettingsScreenViewModelProtocol.swift in Sources */,
|
7C8F9E3AF56E7FF5B338DBFA /* ModerationAndSafetySettingsScreenViewModelProtocol.swift in Sources */,
|
||||||
A88A0C11EB4231EE2989C659 /* MultiAccountAnnouncementView.swift in Sources */,
|
A88A0C11EB4231EE2989C659 /* MultiAccountAnnouncementView.swift in Sources */,
|
||||||
|
5152FF71D3AE01A1ADF0FC62 /* NSEAccountSettings.swift in Sources */,
|
||||||
|
1AF6BFB40515EB31B9485665 /* NSENotificationTone.swift in Sources */,
|
||||||
E938F7A45D6A3DBBE6789A03 /* NSEUserSession.swift in Sources */,
|
E938F7A45D6A3DBBE6789A03 /* NSEUserSession.swift in Sources */,
|
||||||
551E03A935A6B73158A1AB4A /* NSEUserSessionMock.swift in Sources */,
|
551E03A935A6B73158A1AB4A /* NSEUserSessionMock.swift in Sources */,
|
||||||
F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */,
|
F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */,
|
||||||
@@ -10530,7 +10550,7 @@
|
|||||||
IS_NIGHTLY_BUILD = NO;
|
IS_NIGHTLY_BUILD = NO;
|
||||||
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)";
|
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.5;
|
MACOSX_DEPLOYMENT_TARGET = 14.5;
|
||||||
MARKETING_VERSION = 26.10.1;
|
MARKETING_VERSION = 26.10.2;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCTION_APP_NAME = Element;
|
PRODUCTION_APP_NAME = Element;
|
||||||
@@ -10612,7 +10632,7 @@
|
|||||||
IS_NIGHTLY_BUILD = NO;
|
IS_NIGHTLY_BUILD = NO;
|
||||||
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)";
|
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.5;
|
MACOSX_DEPLOYMENT_TARGET = 14.5;
|
||||||
MARKETING_VERSION = 26.10.1;
|
MARKETING_VERSION = 26.10.2;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ final nonisolated class AppHooks: AppHooksProtocol {
|
|||||||
@AppHook(default: DefaultOAuthPresenterHook())
|
@AppHook(default: DefaultOAuthPresenterHook())
|
||||||
var oAuthPresenterHook: OAuthPresenterHookProtocol
|
var oAuthPresenterHook: OAuthPresenterHookProtocol
|
||||||
|
|
||||||
@AppHook(default: DefaultUserSessionHook())
|
|
||||||
var userSessionHook: UserSessionHookProtocol
|
|
||||||
|
|
||||||
@AppHook(default: DefaultRoomScreenHook())
|
@AppHook(default: DefaultRoomScreenHook())
|
||||||
var roomScreenHook: RoomScreenHookProtocol
|
var roomScreenHook: RoomScreenHookProtocol
|
||||||
|
|
||||||
@@ -64,6 +61,9 @@ final nonisolated class AppHooks: AppHooksProtocol {
|
|||||||
|
|
||||||
@AppHook(default: DefaultRemoteSettingsHook())
|
@AppHook(default: DefaultRemoteSettingsHook())
|
||||||
var remoteSettingsHook: RemoteSettingsHookProtocol
|
var remoteSettingsHook: RemoteSettingsHookProtocol
|
||||||
|
|
||||||
|
@AppHook(default: DefaultUserSessionHook())
|
||||||
|
var userSessionHook: UserSessionHookProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated protocol AppHooksProtocol: Sendable {
|
nonisolated protocol AppHooksProtocol: Sendable {
|
||||||
|
|||||||
@@ -8,9 +8,19 @@
|
|||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
nonisolated protocol UserSessionHookProtocol: Sendable {
|
nonisolated protocol UserSessionHookProtocol: Sendable {
|
||||||
|
#if IS_MAIN_APP
|
||||||
func configure(with userSession: UserSessionProtocol?) async
|
func configure(with userSession: UserSessionProtocol?) async
|
||||||
|
#endif
|
||||||
|
|
||||||
|
func configure(_ userSettings: UserSettings, forHomeserver homeserver: String) -> UserSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated struct DefaultUserSessionHook: UserSessionHookProtocol {
|
nonisolated struct DefaultUserSessionHook: UserSessionHookProtocol {
|
||||||
|
#if IS_MAIN_APP
|
||||||
func configure(with userSession: UserSessionProtocol?) async { }
|
func configure(with userSession: UserSessionProtocol?) async { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
func configure(_ userSettings: UserSettings, forHomeserver homeserver: String) -> UserSettings {
|
||||||
|
userSettings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,10 +467,28 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
MXLog.info("Migrating to version 25.07.4, log files have been moved.")
|
MXLog.info("Migrating to version 25.07.4, log files have been moved.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldVersion < Version(26, 10, 2) {
|
||||||
|
MXLog.info("Migrating to version 26.10.2, moving notification sounds.")
|
||||||
|
let soundsDirectory = URL.libraryDirectory.appending(component: "Sounds", directoryHint: .isDirectory)
|
||||||
|
|
||||||
|
// Sounds are now referenced directly, the duplicate file can go.
|
||||||
|
try? FileManager.default.removeItem(at: soundsDirectory.appending(component: "currentAlert.caf"))
|
||||||
|
|
||||||
|
let oldCustomTonesLocation = soundsDirectory.appending(component: "AvailableSounds", directoryHint: .isDirectory)
|
||||||
|
if FileManager.default.fileExists(atPath: oldCustomTonesLocation.path(percentEncoded: false)) {
|
||||||
|
do {
|
||||||
|
try FileManager.default.moveItem(at: oldCustomTonesLocation, to: URL.customTonesDirectory)
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed moving custom notification sounds: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Store the old version to run additional migrations on the user session once it has been set up.
|
// Store the old version to run additional migrations on the user session once it has been set up.
|
||||||
userSessionMigrationsOldVersion = oldVersion
|
userSessionMigrationsOldVersion = oldVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next cyclomatic_complexity
|
||||||
private func performUserSessionMigrations(_ userSession: UserSessionProtocol) async {
|
private func performUserSessionMigrations(_ userSession: UserSessionProtocol) async {
|
||||||
guard let oldVersion = userSessionMigrationsOldVersion else { return }
|
guard let oldVersion = userSessionMigrationsOldVersion else { return }
|
||||||
|
|
||||||
@@ -515,6 +533,23 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
|||||||
userSession.userSettings.account.migrateAppSettingsValue(\.searchBreadcrumbsKey)
|
userSession.userSettings.account.migrateAppSettingsValue(\.searchBreadcrumbsKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldVersion < Version(26, 10, 2) {
|
||||||
|
MXLog.info("Migrating to version 26.10.2, moving visible account settings.")
|
||||||
|
userSession.userSettings.account.migrateAppSettingsValue(\.showAllRoomListActivityKey)
|
||||||
|
userSession.userSettings.account.migrateAppSettingsValue(\.selectedNotificationToneKey)
|
||||||
|
userSession.userSettings.account.migrateAppSettingsValue(\.sharePresenceKey)
|
||||||
|
|
||||||
|
// Make sure system notification sounds are available in the new directory.
|
||||||
|
if let selectedNotificationTone = userSession.userSettings.account.selectedNotificationTone {
|
||||||
|
do {
|
||||||
|
MXLog.info("Copying the selected system notification sound.")
|
||||||
|
try NotificationToneManager.copySystemToneIfNeeded(selectedNotificationTone)
|
||||||
|
} catch {
|
||||||
|
MXLog.error("Failed migrating the selected notification sound: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
userSessionMigrationsOldVersion = nil
|
userSessionMigrationsOldVersion = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,23 +14,34 @@ final nonisolated class AccountSettings: @unchecked Sendable {
|
|||||||
private let store: AccountUserDefaults
|
private let store: AccountUserDefaults
|
||||||
private let userID: String
|
private let userID: String
|
||||||
|
|
||||||
// MARK: Accounts
|
// MARK: Session Activity
|
||||||
|
|
||||||
/// When this account was last selected, used to order the accounts most recently selected first.
|
/// When this account was last selected, used to order the accounts most recently selected first.
|
||||||
@UserPreference
|
@UserPreference
|
||||||
var lastSelectedDate: Date?
|
var lastSelectedDate: Date?
|
||||||
|
|
||||||
// MARK: Session
|
|
||||||
|
|
||||||
@UserPreference(defaultValue: false)
|
@UserPreference(defaultValue: false)
|
||||||
var hasRunIdentityConfirmationOnboarding: Bool
|
var hasRunIdentityConfirmationOnboarding: Bool
|
||||||
|
|
||||||
// MARK: Search
|
|
||||||
|
|
||||||
/// The queries the user searched for and the rooms they opened from the results, most recent first.
|
/// The queries the user searched for and the rooms they opened from the results, most recent first.
|
||||||
@UserPreference(defaultValue: [SearchBreadcrumb]())
|
@UserPreference(defaultValue: [SearchBreadcrumb]())
|
||||||
var searchBreadcrumbs: [SearchBreadcrumb]
|
var searchBreadcrumbs: [SearchBreadcrumb]
|
||||||
|
|
||||||
|
// MARK: Visible Preferences
|
||||||
|
|
||||||
|
@UserPreference(defaultValue: true)
|
||||||
|
var showAllRoomListActivity: Bool
|
||||||
|
|
||||||
|
/// The tone played when the user hasn't selected one, which the homeserver can configure remotely.
|
||||||
|
let defaultNotificationTone: RemotePreference<NotificationTone> = .init(.default)
|
||||||
|
|
||||||
|
/// The sound played when delivering noisy notifications. If nil, use ``defaultNotificationTone``.
|
||||||
|
@UserPreference
|
||||||
|
var selectedNotificationTone: NotificationTone?
|
||||||
|
|
||||||
|
@UserPreference(defaultValue: true)
|
||||||
|
var sharePresence: Bool
|
||||||
|
|
||||||
init(userID: String, store: UserDefaultsProtocol) {
|
init(userID: String, store: UserDefaultsProtocol) {
|
||||||
self.store = .init(userID: userID, store: store)
|
self.store = .init(userID: userID, store: store)
|
||||||
self.userID = userID
|
self.userID = userID
|
||||||
|
|||||||
@@ -235,9 +235,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
|||||||
@UserPreference(defaultValue: true)
|
@UserPreference(defaultValue: true)
|
||||||
var enableNotifications: Bool
|
var enableNotifications: Bool
|
||||||
|
|
||||||
@UserPreference(defaultValue: true)
|
|
||||||
var enableInAppNotifications: Bool
|
|
||||||
|
|
||||||
@UserPreference(defaultValue: false)
|
@UserPreference(defaultValue: false)
|
||||||
var hideQuietNotificationAlerts: Bool
|
var hideQuietNotificationAlerts: Bool
|
||||||
|
|
||||||
@@ -253,10 +250,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
|||||||
@UserPreference(defaultValue: 0)
|
@UserPreference(defaultValue: 0)
|
||||||
var lastKnownBadgeCount: Int
|
var lastKnownBadgeCount: Int
|
||||||
|
|
||||||
/// The sound played when delivering noisy notifications. If nil, use the ElementX default
|
|
||||||
@UserPreference
|
|
||||||
var selectedNotificationTone: NotificationTone?
|
|
||||||
|
|
||||||
// MARK: - Logging
|
// MARK: - Logging
|
||||||
|
|
||||||
@UserPreference(defaultValue: LogLevel.info)
|
@UserPreference(defaultValue: LogLevel.info)
|
||||||
@@ -331,11 +324,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
|||||||
@UserPreference(defaultValue: false)
|
@UserPreference(defaultValue: false)
|
||||||
var liveLocationDisclaimerDisplayed: Bool
|
var liveLocationDisclaimerDisplayed: Bool
|
||||||
|
|
||||||
// MARK: - Home Screen
|
|
||||||
|
|
||||||
@UserPreference(defaultValue: true)
|
|
||||||
var showAllRoomListActivity: Bool
|
|
||||||
|
|
||||||
// MARK: - Room Screen
|
// MARK: - Room Screen
|
||||||
|
|
||||||
@UserPreference(defaultValue: AppBuildType.current == .debug)
|
@UserPreference(defaultValue: AppBuildType.current == .debug)
|
||||||
@@ -382,11 +370,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
|||||||
/// The MapTiler configuration used to build map URLs, which defaults to the bundled one.
|
/// The MapTiler configuration used to build map URLs, which defaults to the bundled one.
|
||||||
private(set) var mapTilerConfiguration = RemotePreference(AppSettings.bundledMapTilerConfiguration)
|
private(set) var mapTilerConfiguration = RemotePreference(AppSettings.bundledMapTilerConfiguration)
|
||||||
|
|
||||||
// MARK: - Presence
|
|
||||||
|
|
||||||
@UserPreference(defaultValue: true)
|
|
||||||
var sharePresence: Bool
|
|
||||||
|
|
||||||
// MARK: - Feature Flags
|
// MARK: - Feature Flags
|
||||||
|
|
||||||
/// Others
|
/// Others
|
||||||
|
|||||||
@@ -8692,6 +8692,11 @@ nonisolated class MediaProviderMock: MediaProviderProtocol, @unchecked Sendable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
nonisolated class NSEUserSessionMock: NSEUserSessionProtocol, @unchecked Sendable {
|
nonisolated class NSEUserSessionMock: NSEUserSessionProtocol, @unchecked Sendable {
|
||||||
|
var userSettings: UserSettings {
|
||||||
|
get { return underlyingUserSettings }
|
||||||
|
set(value) { underlyingUserSettings = value }
|
||||||
|
}
|
||||||
|
nonisolated(unsafe) var underlyingUserSettings: UserSettings!
|
||||||
nonisolated(unsafe) var inviteAvatarsVisibilityCallsCount = 0
|
nonisolated(unsafe) var inviteAvatarsVisibilityCallsCount = 0
|
||||||
var inviteAvatarsVisibilityCalled: Bool {
|
var inviteAvatarsVisibilityCalled: Bool {
|
||||||
return inviteAvatarsVisibilityCallsCount > 0
|
return inviteAvatarsVisibilityCallsCount > 0
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import MatrixRustSDK
|
import MatrixRustSDK
|
||||||
|
|
||||||
struct NSEUserSessionMockConfiguration {
|
struct NSEUserSessionMockConfiguration {
|
||||||
|
var userSettings = UserSettings.mock()
|
||||||
var inviteAvatarsVisibility = InviteAvatars.on
|
var inviteAvatarsVisibility = InviteAvatars.on
|
||||||
var mediaPreviewVisibility = MediaPreviews.on
|
var mediaPreviewVisibility = MediaPreviews.on
|
||||||
var threadsEnabled = true
|
var threadsEnabled = true
|
||||||
@@ -17,6 +18,7 @@ extension NSEUserSessionMock {
|
|||||||
convenience init(_ configuration: NSEUserSessionMockConfiguration) {
|
convenience init(_ configuration: NSEUserSessionMockConfiguration) {
|
||||||
self.init()
|
self.init()
|
||||||
|
|
||||||
|
underlyingUserSettings = configuration.userSettings
|
||||||
underlyingInviteAvatarsVisibility = configuration.inviteAvatarsVisibility
|
underlyingInviteAvatarsVisibility = configuration.inviteAvatarsVisibility
|
||||||
underlyingMediaPreviewVisibility = configuration.mediaPreviewVisibility
|
underlyingMediaPreviewVisibility = configuration.mediaPreviewVisibility
|
||||||
threadsEnabled = configuration.threadsEnabled
|
threadsEnabled = configuration.threadsEnabled
|
||||||
|
|||||||
@@ -103,6 +103,16 @@ nonisolated extension URL {
|
|||||||
return systemRoot.appending(components: "System", "Library", "Audio", "UISounds", directoryHint: .isDirectory)
|
return systemRoot.appending(components: "System", "Library", "Audio", "UISounds", directoryHint: .isDirectory)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
/// The directory holding notification tones imported by the user, inside the Library/Sounds directory searched by the system.
|
||||||
|
static var customTonesDirectory: URL {
|
||||||
|
libraryDirectory.appending(components: "Sounds", "Custom", directoryHint: .isDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The directory holding copies of selected system tones, inside the Library/Sounds directory searched by the system.
|
||||||
|
static var systemTonesDirectory: URL {
|
||||||
|
libraryDirectory.appending(components: "Sounds", "System", directoryHint: .isDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
var globalProxy: String? {
|
var globalProxy: String? {
|
||||||
guard let proxySettings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() else {
|
guard let proxySettings = CFNetworkCopySystemProxySettings()?.takeRetainedValue() else {
|
||||||
MXLog.error("Failed retrieving proxy settings")
|
MXLog.error("Failed retrieving proxy settings")
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
.weakAssign(to: \.state.selectedRoomID, on: self)
|
.weakAssign(to: \.state.selectedRoomID, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
userSettings.app.showAllRoomListActivityPublisher
|
userSettings.account.showAllRoomListActivityPublisher
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
self?.updateRooms()
|
self?.updateRooms()
|
||||||
@@ -277,7 +277,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
case .success:
|
case .success:
|
||||||
analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
|
analyticsService.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
|
||||||
|
|
||||||
if case .failure(let error) = await roomProxy.markAsRead(receiptType: userSettings.app.sharePresence ? .read : .readPrivate) {
|
if case .failure(let error) = await roomProxy.markAsRead(receiptType: userSettings.account.sharePresence ? .read : .readPrivate) {
|
||||||
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
|
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
|
||||||
}
|
}
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
@@ -395,7 +395,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
|
|
||||||
for summary in roomSummaryProvider.roomListPublisher.value {
|
for summary in roomSummaryProvider.roomListPublisher.value {
|
||||||
let room = HomeScreenRoom(summary: summary,
|
let room = HomeScreenRoom(summary: summary,
|
||||||
showAllActivity: userSettings.app.showAllRoomListActivity,
|
showAllActivity: userSettings.account.showAllRoomListActivity,
|
||||||
seenInvites: seenInvites)
|
seenInvites: seenInvites)
|
||||||
rooms.append(room)
|
rooms.append(room)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
Task {
|
Task {
|
||||||
// When navigating away from the room, we need to mark the room as both read
|
// When navigating away from the room, we need to mark the room as both read
|
||||||
// and fully read for Synapse to clear this room from the app's badge count.
|
// and fully read for Synapse to clear this room from the app's badge count.
|
||||||
_ = await roomProxy.markAsRead(receiptType: userSettings.app.sharePresence ? .read : .readPrivate)
|
_ = await roomProxy.markAsRead(receiptType: userSettings.account.sharePresence ? .read : .readPrivate)
|
||||||
_ = await roomProxy.markAsRead(receiptType: .fullyRead)
|
_ = await roomProxy.markAsRead(receiptType: .fullyRead)
|
||||||
}
|
}
|
||||||
// Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier.
|
// Work around QLPreviewController dismissal issues, see the InteractiveQuickLookModifier.
|
||||||
|
|||||||
+2
-2
@@ -32,8 +32,8 @@ struct ModerationAndSafetySettingsScreenViewStateBindings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sharePresence: Bool {
|
var sharePresence: Bool {
|
||||||
get { userSettings.app.sharePresence }
|
get { userSettings.account.sharePresence }
|
||||||
set { userSettings.app.sharePresence = newValue }
|
set { userSettings.account.sharePresence = newValue }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -39,10 +39,10 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
|
|||||||
self.notificationToneManager = notificationToneManager
|
self.notificationToneManager = notificationToneManager
|
||||||
|
|
||||||
let bindings = NotificationSettingsScreenViewStateBindings(enableNotifications: userSettings.app.enableNotifications,
|
let bindings = NotificationSettingsScreenViewStateBindings(enableNotifications: userSettings.app.enableNotifications,
|
||||||
showAllRoomListActivity: userSettings.app.showAllRoomListActivity)
|
showAllRoomListActivity: userSettings.account.showAllRoomListActivity)
|
||||||
super.init(initialViewState: NotificationSettingsScreenViewState(bindings: bindings,
|
super.init(initialViewState: NotificationSettingsScreenViewState(bindings: bindings,
|
||||||
isModallyPresented: isModallyPresented,
|
isModallyPresented: isModallyPresented,
|
||||||
selectedAlertTone: userSettings.app.selectedNotificationTone ?? NotificationToneManager.defaultElementXMessageTone,
|
selectedAlertTone: userSettings.account.selectedNotificationTone ?? userSettings.account.defaultNotificationTone.publisher.value,
|
||||||
availableCustomTones: notificationToneManager.customTones(),
|
availableCustomTones: notificationToneManager.customTones(),
|
||||||
// macos lacks default sounds and its sandbox Sounds directory is immutable
|
// macos lacks default sounds and its sandbox Sounds directory is immutable
|
||||||
customToneSelectionEnabled: !ProcessInfo.processInfo.isiOSAppOnMac))
|
customToneSelectionEnabled: !ProcessInfo.processInfo.isiOSAppOnMac))
|
||||||
@@ -52,8 +52,8 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
|
|||||||
.weakAssign(to: \.state.bindings.enableNotifications, on: self)
|
.weakAssign(to: \.state.bindings.enableNotifications, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
userSettings.app.selectedNotificationTonePublisher
|
userSettings.account.selectedNotificationTonePublisher
|
||||||
.map { $0 ?? NotificationToneManager.defaultElementXMessageTone }
|
.map { $0 ?? userSettings.account.defaultNotificationTone.publisher.value }
|
||||||
.weakAssign(to: \.state.selectedAlertTone, on: self)
|
.weakAssign(to: \.state.selectedAlertTone, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
|
|||||||
}
|
}
|
||||||
Task { await enableInvitations(state.bindings.invitationsEnabled) }
|
Task { await enableInvitations(state.bindings.invitationsEnabled) }
|
||||||
case .showAllRoomListActivityChanged:
|
case .showAllRoomListActivityChanged:
|
||||||
userSettings.app.showAllRoomListActivity = state.bindings.showAllRoomListActivity
|
userSettings.account.showAllRoomListActivity = state.bindings.showAllRoomListActivity
|
||||||
case .close:
|
case .close:
|
||||||
actionsSubject.send(.close)
|
actionsSubject.send(.close)
|
||||||
case .fixConfigurationMismatchTapped:
|
case .fixConfigurationMismatchTapped:
|
||||||
@@ -302,7 +302,7 @@ class NotificationSettingsScreenViewModel: NotificationSettingsScreenViewModelTy
|
|||||||
try notificationToneManager.deleteCustomTone(tone)
|
try notificationToneManager.deleteCustomTone(tone)
|
||||||
|
|
||||||
if tone == state.selectedAlertTone {
|
if tone == state.selectedAlertTone {
|
||||||
userSettings.app.selectedNotificationTone = nil
|
userSettings.account.selectedNotificationTone = nil
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
MXLog.error("Error deleting alert tone \(tone.label): \(error)")
|
MXLog.error("Error deleting alert tone \(tone.label): \(error)")
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
case .voiceMessage(let voiceMessageAction):
|
case .voiceMessage(let voiceMessageAction):
|
||||||
processVoiceMessageAction(voiceMessageAction)
|
processVoiceMessageAction(voiceMessageAction)
|
||||||
case .contentChanged(let isEmpty):
|
case .contentChanged(let isEmpty):
|
||||||
guard userSettings.app.sharePresence else {
|
guard userSettings.account.sharePresence else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -508,7 +508,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
|
|
||||||
roomProxy.typingMembersPublisher
|
roomProxy.typingMembersPublisher
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.filter { [weak self] _ in self?.userSettings.app.sharePresence ?? false }
|
.filter { [weak self] _ in self?.userSettings.account.sharePresence ?? false }
|
||||||
.weakAssign(to: \.state.typingMembers, on: self)
|
.weakAssign(to: \.state.typingMembers, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func setupUserSettingsSubscriptions() {
|
private func setupUserSettingsSubscriptions() {
|
||||||
userSettings.app.sharePresencePublisher
|
userSettings.account.sharePresencePublisher
|
||||||
.weakAssign(to: \.state.showReadReceipts, on: self)
|
.weakAssign(to: \.state.showReadReceipts, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ nonisolated struct NotificationTone: Hashable, Comparable, Codable {
|
|||||||
case appLibrary
|
case appLibrary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The default bundled message tone.
|
||||||
|
static let `default`: NotificationTone = .createBundledSound(label: L10n.screenNotificationSettingsSoundElementDefault, filename: "message.caf")
|
||||||
|
|
||||||
/// Creates a tone backed by a file in the system sounds directory.
|
/// Creates a tone backed by a file in the system sounds directory.
|
||||||
static func createSystemSound(label: String?, filename: String, systemSoundsSubdirectory: [String] = []) -> NotificationTone {
|
static func createSystemSound(label: String?, filename: String, systemSoundsSubdirectory: [String] = []) -> NotificationTone {
|
||||||
NotificationTone(labelOverride: label, storageLocationRoot: .system, relativePath: systemSoundsSubdirectory + [filename])
|
NotificationTone(labelOverride: label, storageLocationRoot: .system, relativePath: systemSoundsSubdirectory + [filename])
|
||||||
|
|||||||
@@ -31,26 +31,40 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
|
|
||||||
private let userSettings: UserSettings
|
private let userSettings: UserSettings
|
||||||
|
|
||||||
/// The default Element X bundled message tone.
|
|
||||||
static let defaultElementXMessageTone: NotificationTone = .createBundledSound(label: L10n.screenNotificationSettingsSoundElementDefault,
|
|
||||||
filename: "message.caf")
|
|
||||||
|
|
||||||
/// All default tones (system + Element X), sorted by name.
|
/// All default tones (system + Element X), sorted by name.
|
||||||
static let allDefaultAlerts: [NotificationTone] = (defaultSystemAlerts + defaultElementXAlerts).sorted()
|
static let allDefaultAlerts: [NotificationTone] = (defaultSystemAlerts + defaultBundledAlerts).sorted()
|
||||||
|
|
||||||
/// Filename of the active tone file used by the notification service.
|
/// The name the notification service uses to find the tone's file in the app bundle or Library/Sounds.
|
||||||
static let selectedToneFilename = "currentAlert.caf"
|
///
|
||||||
|
/// Note: Names with a subdirectory aren't documented for `UNNotificationSound(named:)` but do work.
|
||||||
|
static func soundName(for tone: NotificationTone) -> String {
|
||||||
|
switch tone.storageLocationRoot {
|
||||||
|
case .appBundle:
|
||||||
|
tone.filename
|
||||||
|
case .appLibrary:
|
||||||
|
"\(URL.customTonesDirectory.lastPathComponent)/\(tone.filename)"
|
||||||
|
case .system:
|
||||||
|
"\(URL.systemTonesDirectory.lastPathComponent)/\(tone.filename)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Directory where user-imported custom tones are stored.
|
/// Copies a system tone into Library/Sounds, as the system sounds directory isn't searched for notification sounds.
|
||||||
nonisolated static let libraryLocation = URL.libraryDirectory.appending(components: "Sounds", "AvailableSounds", directoryHint: .isDirectory)
|
static func copySystemToneIfNeeded(_ tone: NotificationTone) throws {
|
||||||
|
guard tone.storageLocationRoot == .system else { return }
|
||||||
|
|
||||||
|
let copyLocation = URL.systemTonesDirectory.appending(component: tone.filename)
|
||||||
|
guard (try? copyLocation.checkResourceIsReachable()) != true else { return }
|
||||||
|
|
||||||
|
try FileManager.default.createDirectory(at: URL.systemTonesDirectory, withIntermediateDirectories: true)
|
||||||
|
try FileManager.default.copyItem(at: toneLocation(for: tone), to: copyLocation)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates the manager and ensures required library directories exist.
|
/// Creates the manager and ensures required library directories exist.
|
||||||
init(userSettings: UserSettings) {
|
init(userSettings: UserSettings) {
|
||||||
self.userSettings = userSettings
|
self.userSettings = userSettings
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try FileManager.default.createDirectory(at: NotificationToneManager.libraryLocation, withIntermediateDirectories: true)
|
try FileManager.default.createDirectory(at: URL.customTonesDirectory, withIntermediateDirectories: true)
|
||||||
try FileManager.default.createDirectory(at: Self.selectedToneLocation.deletingLastPathComponent(), withIntermediateDirectories: true)
|
|
||||||
} catch {
|
} catch {
|
||||||
// Don't crash on a recoverable file system error. The underlying problem (directory creation
|
// Don't crash on a recoverable file system error. The underlying problem (directory creation
|
||||||
// failing on launch, e.g. a background launch before the container is writable) is acknowledged
|
// failing on launch, e.g. a background launch before the container is writable) is acknowledged
|
||||||
@@ -60,29 +74,17 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the given tone as the active notification alert tone.
|
/// Sets the given tone as the active notification alert tone.
|
||||||
///
|
|
||||||
/// Copies the tone's audio file to `selectedToneLocation` and persists the selection in app settings.
|
|
||||||
func setSelectedTone(_ alertTone: NotificationTone) throws -> URL {
|
func setSelectedTone(_ alertTone: NotificationTone) throws -> URL {
|
||||||
do {
|
try Self.copySystemToneIfNeeded(alertTone)
|
||||||
try? FileManager.default.removeItem(at: Self.selectedToneLocation)
|
userSettings.account.selectedNotificationTone = alertTone
|
||||||
let toneLocation = Self.toneLocation(for: alertTone)
|
return Self.toneLocation(for: alertTone)
|
||||||
try FileManager.default.copyItem(at: toneLocation, to: Self.selectedToneLocation)
|
|
||||||
userSettings.app.selectedNotificationTone = alertTone
|
|
||||||
return Self.selectedToneLocation
|
|
||||||
} catch {
|
|
||||||
if (try? Self.selectedToneLocation.checkResourceIsReachable()) != true {
|
|
||||||
// make sure the selected tone is reset if there's no custom tone present
|
|
||||||
userSettings.app.selectedNotificationTone = nil
|
|
||||||
}
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all user-imported CAF tones from the library directory, sorted by name.
|
/// Returns all user-imported CAF tones from the library directory, sorted by name.
|
||||||
func customTones() -> [NotificationTone] {
|
func customTones() -> [NotificationTone] {
|
||||||
let availableFiles = try? FileManager
|
let availableFiles = try? FileManager
|
||||||
.default
|
.default
|
||||||
.contentsOfDirectory(at: NotificationToneManager.libraryLocation, includingPropertiesForKeys: nil)
|
.contentsOfDirectory(at: URL.customTonesDirectory, includingPropertiesForKeys: nil)
|
||||||
|
|
||||||
return (availableFiles ?? [])
|
return (availableFiles ?? [])
|
||||||
.compactMap {
|
.compactMap {
|
||||||
@@ -100,7 +102,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
@discardableResult
|
@discardableResult
|
||||||
func addNewToneToLibrary(from sourceURL: URL) throws -> URL {
|
func addNewToneToLibrary(from sourceURL: URL) throws -> URL {
|
||||||
let baseName = sourceURL.deletingPathExtension().lastPathComponent
|
let baseName = sourceURL.deletingPathExtension().lastPathComponent
|
||||||
let outputURL = NotificationToneManager.libraryLocation.appending(component: baseName).appendingPathExtension("caf")
|
let outputURL = URL.customTonesDirectory.appending(component: baseName).appendingPathExtension("caf")
|
||||||
|
|
||||||
guard (try? outputURL.checkResourceIsReachable()) != true else {
|
guard (try? outputURL.checkResourceIsReachable()) != true else {
|
||||||
throw ManagerError.fileAlreadyExists
|
throw ManagerError.fileAlreadyExists
|
||||||
@@ -119,7 +121,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
/// - Throws: `DeletionError.notACustomTone` if the tone is not stored in the library directory.
|
/// - Throws: `DeletionError.notACustomTone` if the tone is not stored in the library directory.
|
||||||
func deleteCustomTone(_ alertTone: NotificationTone) throws {
|
func deleteCustomTone(_ alertTone: NotificationTone) throws {
|
||||||
let toneLocation = Self.toneLocation(for: alertTone)
|
let toneLocation = Self.toneLocation(for: alertTone)
|
||||||
guard toneLocation.deletingLastPathComponent() == NotificationToneManager.libraryLocation else {
|
guard toneLocation.deletingLastPathComponent() == URL.customTonesDirectory else {
|
||||||
throw ManagerError.notACustomTone
|
throw ManagerError.notACustomTone
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,9 +174,6 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
MXLog.info("Converted \(sourceURL.path(percentEncoded: false)) to caf")
|
MXLog.info("Converted \(sourceURL.path(percentEncoded: false)) to caf")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// File URL of the active tone copied/linked for use by the system.
|
|
||||||
private static let selectedToneLocation = NotificationToneManager.libraryLocation.deletingLastPathComponent().appending(component: selectedToneFilename)
|
|
||||||
|
|
||||||
/// Pre-defined iOS system tones available for selection, sorted by name.
|
/// Pre-defined iOS system tones available for selection, sorted by name.
|
||||||
private static let defaultSystemAlerts: [NotificationTone] = [
|
private static let defaultSystemAlerts: [NotificationTone] = [
|
||||||
.createSystemSound(label: L10n.screenNotificationSettingsSoundSystemTriToneIos,
|
.createSystemSound(label: L10n.screenNotificationSettingsSoundSystemTriToneIos,
|
||||||
@@ -256,9 +255,9 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
}
|
}
|
||||||
.sorted()
|
.sorted()
|
||||||
|
|
||||||
/// Element X bundled tones available for selection, sorted by name.
|
/// Bundled tones available for selection, sorted by name.
|
||||||
private static let defaultElementXAlerts: [NotificationTone] = [
|
private static let defaultBundledAlerts: [NotificationTone] = [
|
||||||
defaultElementXMessageTone,
|
.default,
|
||||||
.createBundledSound(label: L10n.screenNotificationSettingsSoundElementFade,
|
.createBundledSound(label: L10n.screenNotificationSettingsSoundElementFade,
|
||||||
filename: "sound_01.caf")
|
filename: "sound_01.caf")
|
||||||
].sorted()
|
].sorted()
|
||||||
@@ -280,7 +279,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
|||||||
case .appBundle:
|
case .appBundle:
|
||||||
root = Self.bundledLocation
|
root = Self.bundledLocation
|
||||||
case .appLibrary:
|
case .appLibrary:
|
||||||
root = Self.libraryLocation
|
root = URL.customTonesDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
return tone.relativePath.reduce(root) {
|
return tone.relativePath.reduce(root) {
|
||||||
|
|||||||
@@ -235,9 +235,6 @@ final class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
extension NotificationManager: UNUserNotificationCenterDelegate {
|
extension NotificationManager: UNUserNotificationCenterDelegate {
|
||||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||||
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
||||||
guard appSettings.enableInAppNotifications else {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
guard let delegate else {
|
guard let delegate else {
|
||||||
return [.badge, .sound, .list, .banner]
|
return [.badge, .sound, .list, .banner]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ final class PresenceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var desiredPresence: ClientProxyPresence {
|
private var desiredPresence: ClientProxyPresence {
|
||||||
switch (isForegroundActive, userSettings.app.sharePresence) {
|
switch (isForegroundActive, userSettings.account.sharePresence) {
|
||||||
case (true, true):
|
case (true, true):
|
||||||
.online
|
.online
|
||||||
case (true, false):
|
case (true, false):
|
||||||
@@ -68,7 +68,7 @@ final class PresenceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func observeSharePresence() {
|
private func observeSharePresence() {
|
||||||
userSettings.app.sharePresencePublisher
|
userSettings.account.sharePresencePublisher
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
self?.reportCurrentState()
|
self?.reportCurrentState()
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class TimelineController: TimelineControllerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
|
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
|
||||||
let receiptType: MatrixRustSDK.ReceiptType = userSettings.app.sharePresence ? .read : .readPrivate
|
let receiptType: MatrixRustSDK.ReceiptType = userSettings.account.sharePresence ? .read : .readPrivate
|
||||||
|
|
||||||
guard let eventID = itemID.eventID else {
|
guard let eventID = itemID.eventID else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
return .failure(.unknownAccount)
|
return .failure(.unknownAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
let userSettings = appSettings.userSettings(for: credentials.userID)
|
let userSettings = appHooks.userSessionHook.configure(appSettings.userSettings(for: credentials.userID),
|
||||||
|
forHomeserver: credentials.restorationToken.session.homeserverUrl)
|
||||||
|
|
||||||
switch await restorePreviousLogin(credentials, userSettings: userSettings) {
|
switch await restorePreviousLogin(credentials, userSettings: userSettings) {
|
||||||
case .success(let clientProxy):
|
case .success(let clientProxy):
|
||||||
@@ -81,7 +82,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
do {
|
do {
|
||||||
let session = try client.session()
|
let session = try client.session()
|
||||||
let userID = try client.userId()
|
let userID = try client.userId()
|
||||||
let userSettings = appSettings.userSettings(for: userID)
|
let userSettings = appHooks.userSessionHook.configure(appSettings.userSettings(for: userID), forHomeserver: client.homeserver())
|
||||||
let clientProxy = try await setupProxyForClient(client, userSettings: userSettings)
|
let clientProxy = try await setupProxyForClient(client, userSettings: userSettings)
|
||||||
|
|
||||||
keychainController.setRestorationToken(RestorationToken(session: session,
|
keychainController.setRestorationToken(RestorationToken(session: session,
|
||||||
|
|||||||
+1
-1
Submodule Enterprise updated: 8ff9415f26...191c56f2bb
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2026 Element Creations Ltd.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
||||||
// Please see LICENSE files in the repository root for full details.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UserNotifications
|
|
||||||
|
|
||||||
nonisolated extension AppSettings {
|
|
||||||
/// The sound name to use in outgoing notifications.
|
|
||||||
/// Falls back to the default Element X tone if no custom tone has been selected.
|
|
||||||
var notificationSoundName: UNNotificationSoundName {
|
|
||||||
if selectedNotificationTone == nil {
|
|
||||||
return UNNotificationSoundName("message.caf")
|
|
||||||
} else {
|
|
||||||
return UNNotificationSoundName(NotificationToneManager.selectedToneFilename)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `UNNotificationSound` built from `notificationSoundName`, ready to attach to a notification content object.
|
|
||||||
var notificationSound: UNNotificationSound {
|
|
||||||
UNNotificationSound(named: notificationSoundName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2026 Element Creations Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
// Please see LICENSE files in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UserNotifications
|
||||||
|
|
||||||
|
nonisolated extension AccountSettings {
|
||||||
|
/// The sound to attach to outgoing notifications, falling back to the default tone if no tone has been selected.
|
||||||
|
var notificationSound: UNNotificationSound {
|
||||||
|
(selectedNotificationTone ?? defaultNotificationTone.publisher.value).notificationSound
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2026 Element Creations Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||||
|
// Please see LICENSE files in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UserNotifications
|
||||||
|
|
||||||
|
nonisolated extension NotificationTone {
|
||||||
|
/// A `UNNotificationSound` for this tone, ready to attach to a notification content object.
|
||||||
|
var notificationSound: UNNotificationSound {
|
||||||
|
UNNotificationSound(named: UNNotificationSoundName(NotificationToneManager.soundName(for: self)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import MatrixRustSDK
|
|||||||
|
|
||||||
// sourcery: AutoMockable
|
// sourcery: AutoMockable
|
||||||
nonisolated protocol NSEUserSessionProtocol {
|
nonisolated protocol NSEUserSessionProtocol {
|
||||||
|
var userSettings: UserSettings { get }
|
||||||
var inviteAvatarsVisibility: InviteAvatars { get async }
|
var inviteAvatarsVisibility: InviteAvatars { get async }
|
||||||
var mediaPreviewVisibility: MediaPreviews { get async }
|
var mediaPreviewVisibility: MediaPreviews { get async }
|
||||||
var threadsEnabled: Bool { get }
|
var threadsEnabled: Bool { get }
|
||||||
@@ -59,7 +60,8 @@ final nonisolated class NSEUserSession: NSEUserSessionProtocol {
|
|||||||
appSettings: AppSettings,
|
appSettings: AppSettings,
|
||||||
appHooks: AppHooks) async throws {
|
appHooks: AppHooks) async throws {
|
||||||
userID = credentials.userID
|
userID = credentials.userID
|
||||||
userSettings = appSettings.userSettings(for: credentials.userID)
|
userSettings = appHooks.userSessionHook.configure(appSettings.userSettings(for: credentials.userID),
|
||||||
|
forHomeserver: credentials.restorationToken.session.homeserverUrl)
|
||||||
|
|
||||||
baseClient = try await clientFactory.makeNSEClient(credentials: credentials,
|
baseClient = try await clientFactory.makeNSEClient(credentials: credentials,
|
||||||
roomID: roomID,
|
roomID: roomID,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import Version
|
|||||||
|
|
||||||
nonisolated struct NotificationContentBuilder {
|
nonisolated struct NotificationContentBuilder {
|
||||||
let messageEventStringBuilder: RoomMessageEventStringBuilder
|
let messageEventStringBuilder: RoomMessageEventStringBuilder
|
||||||
let notificationSoundName: UNNotificationSoundName
|
|
||||||
let userSession: NSEUserSessionProtocol
|
let userSession: NSEUserSessionProtocol
|
||||||
|
|
||||||
/// Process the given notification item proxy
|
/// Process the given notification item proxy
|
||||||
@@ -50,7 +49,7 @@ nonisolated struct NotificationContentBuilder {
|
|||||||
notificationContent.threadIdentifier = threadIdentifier.replacingOccurrences(of: "@", with: "")
|
notificationContent.threadIdentifier = threadIdentifier.replacingOccurrences(of: "@", with: "")
|
||||||
|
|
||||||
MXLog.info("isNoisy: \(notificationItem.isNoisy)")
|
MXLog.info("isNoisy: \(notificationItem.isNoisy)")
|
||||||
notificationContent.sound = notificationItem.isNoisy ? .init(named: notificationSoundName) : nil
|
notificationContent.sound = notificationItem.isNoisy ? userSession.userSettings.account.notificationSound : nil
|
||||||
|
|
||||||
switch notificationItem.event {
|
switch notificationItem.event {
|
||||||
case .none:
|
case .none:
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ nonisolated class NotificationHandler {
|
|||||||
style: .plain)
|
style: .plain)
|
||||||
|
|
||||||
notificationContentBuilder = NotificationContentBuilder(messageEventStringBuilder: eventStringBuilder,
|
notificationContentBuilder = NotificationContentBuilder(messageEventStringBuilder: eventStringBuilder,
|
||||||
notificationSoundName: userSession.userSettings.app.notificationSoundName,
|
|
||||||
userSession: userSession)
|
userSession: userSession)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ actor NotificationServiceExtensionActor {
|
|||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.body = L10n.notificationReceivedWhileOfflineIos
|
content.body = L10n.notificationReceivedWhileOfflineIos
|
||||||
content.sound = appSettings.notificationSound
|
content.sound = NotificationTone.default.notificationSound // Use the default sound as missed notifications could be for any account.
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: Self.receivedWhileOfflineNotificationID, content: content, trigger: nil)
|
let request = UNNotificationRequest(identifier: Self.receivedWhileOfflineNotificationID, content: content, trigger: nil)
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request)
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ targets:
|
|||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientFactoryHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientFactoryHook.swift
|
||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
||||||
|
- path: ../../ElementX/Sources/AppHooks/Hooks/UserSessionHook.swift
|
||||||
- path: ../../Components/Secrets/Secrets.swift
|
- path: ../../Components/Secrets/Secrets.swift
|
||||||
- path: ../../ElementX/Sources/Application/Settings
|
- path: ../../ElementX/Sources/Application/Settings
|
||||||
- path: ../../ElementX/Sources/Application/TargetConfiguration.swift
|
- path: ../../ElementX/Sources/Application/TargetConfiguration.swift
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class ShareExtensionViewController: UIViewController {
|
|||||||
if let credentials = keychainController.restorationTokens().first {
|
if let credentials = keychainController.restorationTokens().first {
|
||||||
let homeserverURL = credentials.restorationToken.session.homeserverUrl
|
let homeserverURL = credentials.restorationToken.session.homeserverUrl
|
||||||
appHooks.remoteSettingsHook.loadCache(forHomeserver: homeserverURL, applyingTo: appSettings)
|
appHooks.remoteSettingsHook.loadCache(forHomeserver: homeserverURL, applyingTo: appSettings)
|
||||||
|
_ = appHooks.userSessionHook.configure(appSettings.userSettings(for: credentials.userID), forHomeserver: homeserverURL)
|
||||||
} else {
|
} else {
|
||||||
// We should really show a different state when there isn't a logged in user, but for now this is fine.
|
// We should really show a different state when there isn't a logged in user, but for now this is fine.
|
||||||
MXLog.error("Not logged in, launching app to show the authentication flow.")
|
MXLog.error("Not logged in, launching app to show the authentication flow.")
|
||||||
|
|||||||
@@ -89,9 +89,11 @@ targets:
|
|||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientFactoryHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientFactoryHook.swift
|
||||||
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
||||||
|
- path: ../../ElementX/Sources/AppHooks/Hooks/UserSessionHook.swift
|
||||||
- path: ../../Components/Secrets/Secrets.swift
|
- path: ../../Components/Secrets/Secrets.swift
|
||||||
- path: ../../ElementX/Sources/Application/Settings
|
- path: ../../ElementX/Sources/Application/Settings
|
||||||
- path: ../../ElementX/Sources/Application/TargetConfiguration.swift
|
- path: ../../ElementX/Sources/Application/TargetConfiguration.swift
|
||||||
|
- path: ../../ElementX/Sources/Generated/Strings.swift
|
||||||
- path: ../../ElementX/Sources/Services/AlertTones/NotificationTone.swift
|
- path: ../../ElementX/Sources/Services/AlertTones/NotificationTone.swift
|
||||||
- path: ../../ElementX/Sources/Services/Keychain/KeychainController.swift
|
- path: ../../ElementX/Sources/Services/Keychain/KeychainController.swift
|
||||||
- path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift
|
- path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Tidy
|
||||||
|
|
||||||
|
Follow-ups that aren't worth their own PR yet. Do each item in one go, not piecemeal.
|
||||||
|
|
||||||
|
- **Enterprise hook tests should use `EnterpriseESSServiceMock(configuration:)` instead of the on-disk cache.** Today `EnterpriseCreateRoomScreenHookTests`, `EnterpriseSecurityAndPrivacyScreenHookTests`, `EnterpriseUserSessionHookTests` and parts of `EnterpriseRemoteSettingsHookTests` seed a real `EnterpriseESSService` by writing an `EnterpriseESSConfiguration` to `URL.cachedESSConfigurationFile(for:)`, and each wipes `.enterpriseApplicationSupportBaseDirectory` in `init`. Passing the mock removes the disk dependency and the shared-directory coupling. Switch all of them together so the pattern stays consistent, and leave `EnterpriseESSServiceTests` alone since the cache is what it tests.
|
||||||
@@ -23,7 +23,6 @@ nonisolated struct NotificationContentBuilderTests {
|
|||||||
style: .plain)
|
style: .plain)
|
||||||
mediaProvider = MediaProviderMock(.init())
|
mediaProvider = MediaProviderMock(.init())
|
||||||
notificationContentBuilder = await NotificationContentBuilder(messageEventStringBuilder: stringBuilder,
|
notificationContentBuilder = await NotificationContentBuilder(messageEventStringBuilder: stringBuilder,
|
||||||
notificationSoundName: UNNotificationSoundName("message.caf"),
|
|
||||||
userSession: NSEUserSessionMock(.init()))
|
userSession: NSEUserSessionMock(.init()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -411,29 +411,29 @@ struct NotificationSettingsScreenViewModelTests {
|
|||||||
// Given a tone and a manager that persists the selection to app settings
|
// Given a tone and a manager that persists the selection to app settings
|
||||||
let tone = NotificationTone.createBundledSound(label: "Test", filename: "test.caf")
|
let tone = NotificationTone.createBundledSound(label: "Test", filename: "test.caf")
|
||||||
toneManager.setSelectedToneClosure = { [userSettings] selectedTone in
|
toneManager.setSelectedToneClosure = { [userSettings] selectedTone in
|
||||||
userSettings.app.selectedNotificationTone = selectedTone
|
userSettings.account.selectedNotificationTone = selectedTone
|
||||||
return NotificationToneManager.libraryLocation
|
return URL.customTonesDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the user selects that tone
|
// When the user selects that tone
|
||||||
context.send(viewAction: .selectAlertTone(tone))
|
context.send(viewAction: .selectAlertTone(tone))
|
||||||
|
|
||||||
// Then it is reflected in app settings
|
// Then it is reflected in app settings
|
||||||
#expect(userSettings.app.selectedNotificationTone == tone)
|
#expect(userSettings.account.selectedNotificationTone == tone)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func deletingActiveToneResetsSelection() {
|
func deletingActiveToneResetsSelection() {
|
||||||
// Given the active tone is a custom tone
|
// Given the active tone is a custom tone
|
||||||
let customTone = NotificationTone.createCustomUserSound(filename: "custom.caf")
|
let customTone = NotificationTone.createCustomUserSound(filename: "custom.caf")
|
||||||
userSettings.app.selectedNotificationTone = customTone
|
userSettings.account.selectedNotificationTone = customTone
|
||||||
toneManager.customTonesReturnValue = [customTone]
|
toneManager.customTonesReturnValue = [customTone]
|
||||||
|
|
||||||
// When that tone is deleted
|
// When that tone is deleted
|
||||||
context.send(viewAction: .deleteCustomAlertTones([customTone]))
|
context.send(viewAction: .deleteCustomAlertTones([customTone]))
|
||||||
|
|
||||||
// Then the selection is cleared, falling back to the default
|
// Then the selection is cleared, falling back to the default
|
||||||
#expect(userSettings.app.selectedNotificationTone == nil)
|
#expect(userSettings.account.selectedNotificationTone == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -441,14 +441,14 @@ struct NotificationSettingsScreenViewModelTests {
|
|||||||
// Given a custom tone is active and a different custom tone also exists
|
// Given a custom tone is active and a different custom tone also exists
|
||||||
let activeTone = NotificationTone.createCustomUserSound(filename: "active.caf")
|
let activeTone = NotificationTone.createCustomUserSound(filename: "active.caf")
|
||||||
let otherTone = NotificationTone.createCustomUserSound(filename: "other.caf")
|
let otherTone = NotificationTone.createCustomUserSound(filename: "other.caf")
|
||||||
userSettings.app.selectedNotificationTone = activeTone
|
userSettings.account.selectedNotificationTone = activeTone
|
||||||
toneManager.customTonesReturnValue = [activeTone, otherTone]
|
toneManager.customTonesReturnValue = [activeTone, otherTone]
|
||||||
|
|
||||||
// When the non-active tone is deleted
|
// When the non-active tone is deleted
|
||||||
context.send(viewAction: .deleteCustomAlertTones([otherTone]))
|
context.send(viewAction: .deleteCustomAlertTones([otherTone]))
|
||||||
|
|
||||||
// Then the active selection is unchanged
|
// Then the active selection is unchanged
|
||||||
#expect(userSettings.app.selectedNotificationTone == activeTone)
|
#expect(userSettings.account.selectedNotificationTone == activeTone)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -476,7 +476,7 @@ struct NotificationSettingsScreenViewModelTests {
|
|||||||
func deleteCustomToneFailurePreservesSelection() {
|
func deleteCustomToneFailurePreservesSelection() {
|
||||||
// Given the active tone is a custom tone and deletion will fail
|
// Given the active tone is a custom tone and deletion will fail
|
||||||
let customTone = NotificationTone.createCustomUserSound(filename: "custom.caf")
|
let customTone = NotificationTone.createCustomUserSound(filename: "custom.caf")
|
||||||
userSettings.app.selectedNotificationTone = customTone
|
userSettings.account.selectedNotificationTone = customTone
|
||||||
toneManager.deleteCustomToneThrowableError = NSError(domain: "test", code: 1)
|
toneManager.deleteCustomToneThrowableError = NSError(domain: "test", code: 1)
|
||||||
toneManager.customTonesReturnValue = [customTone]
|
toneManager.customTonesReturnValue = [customTone]
|
||||||
|
|
||||||
@@ -484,14 +484,14 @@ struct NotificationSettingsScreenViewModelTests {
|
|||||||
context.send(viewAction: .deleteCustomAlertTones([customTone]))
|
context.send(viewAction: .deleteCustomAlertTones([customTone]))
|
||||||
|
|
||||||
// Then the selection is unchanged
|
// Then the selection is unchanged
|
||||||
#expect(userSettings.app.selectedNotificationTone == customTone)
|
#expect(userSettings.account.selectedNotificationTone == customTone)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func importingCustomToneRefreshesAvailableList() async throws {
|
func importingCustomToneRefreshesAvailableList() async throws {
|
||||||
// Given a tone file ready to import
|
// Given a tone file ready to import
|
||||||
let importedTone = NotificationTone.createCustomUserSound(filename: "imported.caf")
|
let importedTone = NotificationTone.createCustomUserSound(filename: "imported.caf")
|
||||||
let location = NotificationToneManager.libraryLocation.appending(component: importedTone.filename)
|
let location = URL.customTonesDirectory.appending(component: importedTone.filename)
|
||||||
toneManager.addNewToneToLibraryFromReturnValue = location
|
toneManager.addNewToneToLibraryFromReturnValue = location
|
||||||
toneManager.customTonesReturnValue = [importedTone]
|
toneManager.customTonesReturnValue = [importedTone]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
import Testing
|
import Testing
|
||||||
|
|
||||||
/// These tests exercise `NotificationToneManager` against the real app sandbox filesystem.
|
/// These tests exercise `NotificationToneManager` against the real app sandbox filesystem.
|
||||||
/// Because all tests share a single `libraryLocation` directory on disk, they are inherently
|
/// Because all tests share a single `customTonesDirectory` on disk, they are inherently
|
||||||
/// incapable of running in parallel — files written by one test would corrupt the state of
|
/// incapable of running in parallel — files written by one test would corrupt the state of
|
||||||
/// another. This project runs test in serial regardless, but this is just to document this is a specific case
|
/// another. This project runs test in serial regardless, but this is just to document this is a specific case
|
||||||
/// relying on that behavior.
|
/// relying on that behavior.
|
||||||
@@ -56,8 +56,8 @@ struct NotificationToneManagerTests {
|
|||||||
@Test
|
@Test
|
||||||
func customTonesFiltersToCAFOnly() throws {
|
func customTonesFiltersToCAFOnly() throws {
|
||||||
// Given both CAF and non-CAF files written to the library directory
|
// Given both CAF and non-CAF files written to the library directory
|
||||||
let cafURL = NotificationToneManager.libraryLocation.appending(component: "\(UUID().uuidString).caf")
|
let cafURL = URL.customTonesDirectory.appending(component: "\(UUID().uuidString).caf")
|
||||||
let mp3URL = NotificationToneManager.libraryLocation.appending(component: "\(UUID().uuidString).mp3")
|
let mp3URL = URL.customTonesDirectory.appending(component: "\(UUID().uuidString).mp3")
|
||||||
defer {
|
defer {
|
||||||
try? FileManager.default.removeItem(at: cafURL)
|
try? FileManager.default.removeItem(at: cafURL)
|
||||||
try? FileManager.default.removeItem(at: mp3URL)
|
try? FileManager.default.removeItem(at: mp3URL)
|
||||||
@@ -79,7 +79,7 @@ struct NotificationToneManagerTests {
|
|||||||
func addingDuplicateToneThrowsFileAlreadyExists() async throws {
|
func addingDuplicateToneThrowsFileAlreadyExists() async throws {
|
||||||
// Given a CAF file that has already been imported into the library
|
// Given a CAF file that has already been imported into the library
|
||||||
let sourceURL = URL.temporaryDirectory.appending(component: "\(UUID().uuidString).caf")
|
let sourceURL = URL.temporaryDirectory.appending(component: "\(UUID().uuidString).caf")
|
||||||
let importedURL = NotificationToneManager.libraryLocation
|
let importedURL = URL.customTonesDirectory
|
||||||
.appending(component: sourceURL.deletingPathExtension().lastPathComponent)
|
.appending(component: sourceURL.deletingPathExtension().lastPathComponent)
|
||||||
.appendingPathExtension("caf")
|
.appendingPathExtension("caf")
|
||||||
defer {
|
defer {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func serviceSeededActiveWhileSharingReportsOnline() async throws {
|
func serviceSeededActiveWhileSharingReportsOnline() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
|
|
||||||
let deferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
let deferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
||||||
let service = makeService(initialApplicationState: .active)
|
let service = makeService(initialApplicationState: .active)
|
||||||
@@ -53,7 +53,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func serviceSeededBackgroundedWhileSharingReportsUnavailable() async throws {
|
func serviceSeededBackgroundedWhileSharingReportsUnavailable() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
|
|
||||||
let deferred = deferFulfillment(sendsSubject) { $0.presence == .unavailable }
|
let deferred = deferFulfillment(sendsSubject) { $0.presence == .unavailable }
|
||||||
let service = makeService(initialApplicationState: .background)
|
let service = makeService(initialApplicationState: .background)
|
||||||
@@ -66,7 +66,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func serviceSeededActiveWhileNotSharingReportsOffline() async throws {
|
func serviceSeededActiveWhileNotSharingReportsOffline() async throws {
|
||||||
userSettings.app.sharePresence = false
|
userSettings.account.sharePresence = false
|
||||||
|
|
||||||
let deferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
let deferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
||||||
let service = makeService(initialApplicationState: .active)
|
let service = makeService(initialApplicationState: .active)
|
||||||
@@ -79,7 +79,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func becomingActiveNotificationWhileSharingReportsOnlineImmediately() async throws {
|
func becomingActiveNotificationWhileSharingReportsOnlineImmediately() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
let service = makeService(initialApplicationState: .inactive)
|
let service = makeService(initialApplicationState: .inactive)
|
||||||
|
|
||||||
let deferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
let deferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
||||||
@@ -93,7 +93,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func resigningActiveNotificationWhileSharingReportsUnavailableImmediately() async throws {
|
func resigningActiveNotificationWhileSharingReportsUnavailableImmediately() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
||||||
let service = makeService(initialApplicationState: .active)
|
let service = makeService(initialApplicationState: .active)
|
||||||
_ = try await initialDeferred.fulfill()
|
_ = try await initialDeferred.fulfill()
|
||||||
@@ -109,19 +109,19 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func togglingSharingWhileActiveDrivesOnlineAndOffline() async throws {
|
func togglingSharingWhileActiveDrivesOnlineAndOffline() async throws {
|
||||||
userSettings.app.sharePresence = false
|
userSettings.account.sharePresence = false
|
||||||
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
||||||
let service = makeService(initialApplicationState: .active)
|
let service = makeService(initialApplicationState: .active)
|
||||||
_ = try await initialDeferred.fulfill()
|
_ = try await initialDeferred.fulfill()
|
||||||
|
|
||||||
let onlineDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
let onlineDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
let online = try await onlineDeferred.fulfill()
|
let online = try await onlineDeferred.fulfill()
|
||||||
#expect(online.presence == .online)
|
#expect(online.presence == .online)
|
||||||
#expect(online.sendImmediately)
|
#expect(online.sendImmediately)
|
||||||
|
|
||||||
let offlineDeferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
let offlineDeferred = deferFulfillment(sendsSubject) { $0.presence == .offline }
|
||||||
userSettings.app.sharePresence = false
|
userSettings.account.sharePresence = false
|
||||||
let offline = try await offlineDeferred.fulfill()
|
let offline = try await offlineDeferred.fulfill()
|
||||||
#expect(offline.presence == .offline)
|
#expect(offline.presence == .offline)
|
||||||
#expect(offline.sendImmediately)
|
#expect(offline.sendImmediately)
|
||||||
@@ -131,7 +131,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func repeatingTheSameStateSendsOnlyOnce() async throws {
|
func repeatingTheSameStateSendsOnlyOnce() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
|
|
||||||
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
let initialDeferred = deferFulfillment(sendsSubject) { $0.presence == .online }
|
||||||
let service = makeService(initialApplicationState: .active)
|
let service = makeService(initialApplicationState: .active)
|
||||||
@@ -148,7 +148,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func failedImmediateSendIsNotRetriedUntilTheDesiredPresenceChanges() async throws {
|
func failedImmediateSendIsNotRetriedUntilTheDesiredPresenceChanges() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
clientProxy.configurePresenceSendImmediatelyClosure = { [sendsSubject] presence, sendImmediately in
|
clientProxy.configurePresenceSendImmediatelyClosure = { [sendsSubject] presence, sendImmediately in
|
||||||
sendsSubject.send((presence, sendImmediately))
|
sendsSubject.send((presence, sendImmediately))
|
||||||
return presence == .online ? .failure(.forbiddenAccess) : .success(())
|
return presence == .online ? .failure(.forbiddenAccess) : .success(())
|
||||||
@@ -173,7 +173,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func newerTransitionWinsWhenAnEarlierSendIsInFlight() async throws {
|
func newerTransitionWinsWhenAnEarlierSendIsInFlight() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
|
|
||||||
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream<Void>.makeStream()
|
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream<Void>.makeStream()
|
||||||
let (resumeOnlineSendStream, resumeOnlineSendContinuation) = AsyncStream<Void>.makeStream()
|
let (resumeOnlineSendStream, resumeOnlineSendContinuation) = AsyncStream<Void>.makeStream()
|
||||||
@@ -206,7 +206,7 @@ struct PresenceServiceTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
func deinitCancelsPendingSend() async throws {
|
func deinitCancelsPendingSend() async throws {
|
||||||
userSettings.app.sharePresence = true
|
userSettings.account.sharePresence = true
|
||||||
|
|
||||||
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream.makeStream(of: Void.self)
|
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream.makeStream(of: Void.self)
|
||||||
let (onlineFinishedStream, onlineFinishedContinuation) = AsyncStream.makeStream(of: Void.self)
|
let (onlineFinishedStream, onlineFinishedContinuation) = AsyncStream.makeStream(of: Void.self)
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ settings:
|
|||||||
ENABLE_BITCODE: false
|
ENABLE_BITCODE: false
|
||||||
APP_NAME: ElementX
|
APP_NAME: ElementX
|
||||||
KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)"
|
KEYCHAIN_ACCESS_GROUP_IDENTIFIER: "$(DEVELOPMENT_TEAM).$(BASE_BUNDLE_IDENTIFIER)"
|
||||||
MARKETING_VERSION: 26.10.1
|
MARKETING_VERSION: 26.10.2
|
||||||
CURRENT_PROJECT_VERSION: 1
|
CURRENT_PROJECT_VERSION: 1
|
||||||
SUPPORTS_MACCATALYST: false
|
SUPPORTS_MACCATALYST: false
|
||||||
ARCHS: arm64
|
ARCHS: arm64
|
||||||
|
|||||||
Reference in New Issue
Block a user