mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:11:45 +00:00
Resetting the app after deletion and cleanup settings (#372)
* Fixes #361 - Reset everything if the app has been deleted since the last run * Cleaned up and renamed the ElementSettings to ApplicationSettings. Removed its singleton and moved it to the service locator. * Moved the ApplicationSettings to the Application folder * Merged together the BuildSettings and ApplicationSettings * Reset the UserDefaults too when resetting the application state * Rename ServiceLocator.applicationSettings to just settings * Fix indentation * Rename ApplicationSettings to AppSettings * Various tweaks following code review * Fix unit tests
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
0F3F2FDD4021A25A0D57F801 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885D8C42DD17625B5261BEFF /* MediaProvider.swift */; };
|
0F3F2FDD4021A25A0D57F801 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885D8C42DD17625B5261BEFF /* MediaProvider.swift */; };
|
||||||
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */; };
|
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */; };
|
||||||
1281625B25371BE53D36CB3A /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */; };
|
1281625B25371BE53D36CB3A /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */; };
|
||||||
|
12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
|
||||||
132D241B09F9044711FD70A5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; };
|
132D241B09F9044711FD70A5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; };
|
||||||
13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */; };
|
13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */; };
|
||||||
13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; };
|
13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; };
|
||||||
@@ -86,7 +87,6 @@
|
|||||||
28410F3DE89C2C44E4F75C92 /* MockBugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E7BF8F7BB1021F889C6483 /* MockBugReportService.swift */; };
|
28410F3DE89C2C44E4F75C92 /* MockBugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0E7BF8F7BB1021F889C6483 /* MockBugReportService.swift */; };
|
||||||
290FDB0FFDC2F1DDF660343E /* TestMeasurementParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */; };
|
290FDB0FFDC2F1DDF660343E /* TestMeasurementParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */; };
|
||||||
297CD0A27C87B0C50FF192EE /* RoomTimelineViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */; };
|
297CD0A27C87B0C50FF192EE /* RoomTimelineViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */; };
|
||||||
29E20505F321071E8375F99B /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B3B811C2B900F12C6F695 /* BuildSettings.swift */; };
|
|
||||||
29EE1791E0AFA1ABB7F23D2F /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */; };
|
29EE1791E0AFA1ABB7F23D2F /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */; };
|
||||||
2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */; };
|
2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */; };
|
||||||
2B9AEEC12B1BBE5BD61D0F5E /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
2B9AEEC12B1BBE5BD61D0F5E /* UserSessionFlowCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3429142FE11930422E7CC1A0 /* UserSessionFlowCoordinatorStateMachine.swift */; };
|
||||||
@@ -269,6 +269,7 @@
|
|||||||
91DFCB641FBA03EE2DA0189E /* FilePreviewScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB27E1BE894F9F9F0134372 /* FilePreviewScreen.swift */; };
|
91DFCB641FBA03EE2DA0189E /* FilePreviewScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB27E1BE894F9F9F0134372 /* FilePreviewScreen.swift */; };
|
||||||
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
||||||
92B95779840CD749117B3615 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */; };
|
92B95779840CD749117B3615 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */; };
|
||||||
|
9377D6BE1511E1529EA1662B /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
|
||||||
93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */; };
|
93875ADD456142D20823ED24 /* ServerSelectionViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */; };
|
||||||
93BA4A81B6D893271101F9F0 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; };
|
93BA4A81B6D893271101F9F0 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 531CE4334AC5CA8DFF6AEB84 /* DTCoreText */; };
|
||||||
9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; };
|
9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; };
|
||||||
@@ -277,7 +278,6 @@
|
|||||||
964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; };
|
964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; };
|
||||||
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
||||||
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
||||||
9738F894DB1BD383BE05767A /* ElementSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1027BB9A852F445B7623897F /* ElementSettings.swift */; };
|
|
||||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
|
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
|
||||||
97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; };
|
97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; };
|
||||||
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */; };
|
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */; };
|
||||||
@@ -369,7 +369,6 @@
|
|||||||
C8E82786DE1B6A400DA9BA25 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 289FA233E896FBC5956C67E0 /* RoomTimelineItemProperties.swift */; };
|
C8E82786DE1B6A400DA9BA25 /* RoomTimelineItemProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 289FA233E896FBC5956C67E0 /* RoomTimelineItemProperties.swift */; };
|
||||||
C94A6048C654B01163AE1BF1 /* VideoPlayerViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5282B7A2DCD076AD2CF27F46 /* VideoPlayerViewModelProtocol.swift */; };
|
C94A6048C654B01163AE1BF1 /* VideoPlayerViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5282B7A2DCD076AD2CF27F46 /* VideoPlayerViewModelProtocol.swift */; };
|
||||||
CA45758F08DF42D41D8A4B29 /* FilePreviewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */; };
|
CA45758F08DF42D41D8A4B29 /* FilePreviewViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */; };
|
||||||
CA9558C0B40C1EE2AD00124A /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 263B3B811C2B900F12C6F695 /* BuildSettings.swift */; };
|
|
||||||
CB137BFB3E083C33E398A6CB /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
|
CB137BFB3E083C33E398A6CB /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; };
|
||||||
CB326BAB54E9B68658909E36 /* Benchmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */; };
|
CB326BAB54E9B68658909E36 /* Benchmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */; };
|
||||||
CB498F4E27AA0545DCEF0F6F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; };
|
CB498F4E27AA0545DCEF0F6F /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4003BC24B24C9E63D3304177 /* DeviceKit */; };
|
||||||
@@ -533,7 +532,6 @@
|
|||||||
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = "<group>"; };
|
0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionStore.swift; sourceTree = "<group>"; };
|
||||||
0EE9EAF0309A2A1D67D8FAF5 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
0EE9EAF0309A2A1D67D8FAF5 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = sv; path = sv.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
0F52BF30D12BA3BD3D3DBB8F /* ServerSelectionViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelProtocol.swift; sourceTree = "<group>"; };
|
0F52BF30D12BA3BD3D3DBB8F /* ServerSelectionViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
1027BB9A852F445B7623897F /* ElementSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementSettings.swift; sourceTree = "<group>"; };
|
|
||||||
1059E2AE7878CF7820592637 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
1059E2AE7878CF7820592637 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactory.swift; sourceTree = "<group>"; };
|
105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactory.swift; sourceTree = "<group>"; };
|
||||||
105D16E7DB0CCE9526612BDD /* bn-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "bn-IN"; path = "bn-IN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
105D16E7DB0CCE9526612BDD /* bn-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "bn-IN"; path = "bn-IN.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
@@ -583,7 +581,6 @@
|
|||||||
24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
2583416C8974272ADBADDBE1 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-TW"; path = "zh-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
2583416C8974272ADBADDBE1 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "zh-TW"; path = "zh-TW.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
|
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
|
||||||
263B3B811C2B900F12C6F695 /* BuildSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildSettings.swift; sourceTree = "<group>"; };
|
|
||||||
26C4D226FCD20BAC53F1E092 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/Localizable.strings; sourceTree = "<group>"; };
|
26C4D226FCD20BAC53F1E092 /* ml */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ml; path = ml.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
|
27A1AD6389A4659AF0CEAE62 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = "<group>"; };
|
||||||
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
||||||
@@ -836,6 +833,7 @@
|
|||||||
AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = "<group>"; };
|
AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
AB785716B9212C093704E767 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; };
|
AB785716B9212C093704E767 /* EmojiPickerHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerHeaderView.swift; sourceTree = "<group>"; };
|
||||||
AB8E75B9CB6C78BE8D09B1AF /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
|
AB8E75B9CB6C78BE8D09B1AF /* OnboardingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreen.swift; sourceTree = "<group>"; };
|
||||||
|
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||||
ACA11F7F50A4A3887A18CA5A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
ACA11F7F50A4A3887A18CA5A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewProvider.swift; sourceTree = "<group>"; };
|
ACB6C5E4950B6C9842F35A38 /* RoomTimelineViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewProvider.swift; sourceTree = "<group>"; };
|
||||||
AD378D580A41E42560C60E9C /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
AD378D580A41E42560C60E9C /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -1978,7 +1976,7 @@
|
|||||||
077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */,
|
077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */,
|
||||||
C75EF87651B00A176AB08E97 /* AppDelegate.swift */,
|
C75EF87651B00A176AB08E97 /* AppDelegate.swift */,
|
||||||
CA89A2DD51B6BBE1DA55E263 /* Application.swift */,
|
CA89A2DD51B6BBE1DA55E263 /* Application.swift */,
|
||||||
263B3B811C2B900F12C6F695 /* BuildSettings.swift */,
|
AC3F82523D6F48B926D6AF68 /* AppSettings.swift */,
|
||||||
B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */,
|
B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */,
|
||||||
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */,
|
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */,
|
||||||
780F74C73E826685A9DB289B /* Navigation */,
|
780F74C73E826685A9DB289B /* Navigation */,
|
||||||
@@ -2075,7 +2073,6 @@
|
|||||||
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */,
|
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */,
|
||||||
49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */,
|
49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */,
|
||||||
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
|
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
|
||||||
1027BB9A852F445B7623897F /* ElementSettings.swift */,
|
|
||||||
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
|
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
|
||||||
6A580295A56B55A856CC4084 /* InfoPlistReader.swift */,
|
6A580295A56B55A856CC4084 /* InfoPlistReader.swift */,
|
||||||
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
|
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
|
||||||
@@ -2808,6 +2805,7 @@
|
|||||||
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */,
|
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */,
|
||||||
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */,
|
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */,
|
||||||
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */,
|
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */,
|
||||||
|
12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */,
|
||||||
9462C62798F47E39DCC182D2 /* Application.swift in Sources */,
|
9462C62798F47E39DCC182D2 /* Application.swift in Sources */,
|
||||||
74604ACFDBE7F54260E7B617 /* ApplicationProtocol.swift in Sources */,
|
74604ACFDBE7F54260E7B617 /* ApplicationProtocol.swift in Sources */,
|
||||||
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */,
|
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */,
|
||||||
@@ -2830,7 +2828,6 @@
|
|||||||
172E6E9A612ADCF10A62CF13 /* BugReportServiceProtocol.swift in Sources */,
|
172E6E9A612ADCF10A62CF13 /* BugReportServiceProtocol.swift in Sources */,
|
||||||
86C2E93920FD15AD17E193A9 /* BugReportViewModel.swift in Sources */,
|
86C2E93920FD15AD17E193A9 /* BugReportViewModel.swift in Sources */,
|
||||||
187E18F21EF4DA244E436E58 /* BugReportViewModelProtocol.swift in Sources */,
|
187E18F21EF4DA244E436E58 /* BugReportViewModelProtocol.swift in Sources */,
|
||||||
CA9558C0B40C1EE2AD00124A /* BuildSettings.swift in Sources */,
|
|
||||||
E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */,
|
E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */,
|
||||||
6A0E7551E0D1793245F34CDD /* ClientError.swift in Sources */,
|
6A0E7551E0D1793245F34CDD /* ClientError.swift in Sources */,
|
||||||
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */,
|
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */,
|
||||||
@@ -2841,7 +2838,6 @@
|
|||||||
1CF18DE71D5D23C61BD88852 /* DebugScreen.swift in Sources */,
|
1CF18DE71D5D23C61BD88852 /* DebugScreen.swift in Sources */,
|
||||||
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */,
|
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */,
|
||||||
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */,
|
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */,
|
||||||
9738F894DB1BD383BE05767A /* ElementSettings.swift in Sources */,
|
|
||||||
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */,
|
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */,
|
||||||
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */,
|
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */,
|
||||||
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */,
|
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */,
|
||||||
@@ -3081,10 +3077,10 @@
|
|||||||
files = (
|
files = (
|
||||||
97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */,
|
97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */,
|
||||||
8024BE37156FF0A95A7A3465 /* AnalyticsPromptUITests.swift in Sources */,
|
8024BE37156FF0A95A7A3465 /* AnalyticsPromptUITests.swift in Sources */,
|
||||||
|
9377D6BE1511E1529EA1662B /* AppSettings.swift in Sources */,
|
||||||
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */,
|
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */,
|
||||||
ACF094CF3BF02DBFA6DFDE60 /* AuthenticationCoordinatorUITests.swift in Sources */,
|
ACF094CF3BF02DBFA6DFDE60 /* AuthenticationCoordinatorUITests.swift in Sources */,
|
||||||
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
||||||
29E20505F321071E8375F99B /* BuildSettings.swift in Sources */,
|
|
||||||
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */,
|
||||||
E3C328EF20C4B3326D263BCD /* FileManager.swift in Sources */,
|
E3C328EF20C4B3326D263BCD /* FileManager.swift in Sources */,
|
||||||
9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */,
|
9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */,
|
||||||
|
|||||||
@@ -52,23 +52,30 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
navigationRootCoordinator = NavigationRootCoordinator()
|
navigationRootCoordinator = NavigationRootCoordinator()
|
||||||
|
|
||||||
|
Self.setupServiceLocator(navigationRootCoordinator: navigationRootCoordinator)
|
||||||
|
Self.setupLogging()
|
||||||
|
|
||||||
stateMachine = AppCoordinatorStateMachine()
|
stateMachine = AppCoordinatorStateMachine()
|
||||||
|
|
||||||
bugReportService = BugReportService(withBaseURL: BuildSettings.bugReportServiceBaseURL, sentryURL: BuildSettings.bugReportSentryURL)
|
bugReportService = BugReportService(withBaseURL: ServiceLocator.shared.settings.bugReportServiceBaseURL, sentryURL: ServiceLocator.shared.settings.bugReportSentryURL)
|
||||||
|
|
||||||
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
|
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
|
||||||
|
|
||||||
ServiceLocator.shared.register(userNotificationController: UserNotificationController(rootCoordinator: navigationRootCoordinator))
|
|
||||||
|
|
||||||
backgroundTaskService = UIKitBackgroundTaskService {
|
backgroundTaskService = UIKitBackgroundTaskService {
|
||||||
UIApplication.shared
|
UIApplication.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
userSessionStore = UserSessionStore(backgroundTaskService: backgroundTaskService)
|
userSessionStore = UserSessionStore(backgroundTaskService: backgroundTaskService)
|
||||||
|
|
||||||
setupStateMachine()
|
// Reset everything if the app has been deleted since the previous run
|
||||||
|
if !ServiceLocator.shared.settings.hasAppLaunchedOnce {
|
||||||
|
AppSettings.reset()
|
||||||
|
userSessionStore.reset()
|
||||||
|
ServiceLocator.shared.settings.hasAppLaunchedOnce = true
|
||||||
|
}
|
||||||
|
|
||||||
setupLogging()
|
setupStateMachine()
|
||||||
|
|
||||||
Bundle.elementFallbackLanguage = "en"
|
Bundle.elementFallbackLanguage = "en"
|
||||||
|
|
||||||
@@ -91,7 +98,12 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func setupLogging() {
|
private static func setupServiceLocator(navigationRootCoordinator: NavigationRootCoordinator) {
|
||||||
|
ServiceLocator.shared.register(userNotificationController: UserNotificationController(rootCoordinator: navigationRootCoordinator))
|
||||||
|
ServiceLocator.shared.register(appSettings: AppSettings())
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func setupLogging() {
|
||||||
let loggerConfiguration = MXLogConfiguration()
|
let loggerConfiguration = MXLogConfiguration()
|
||||||
loggerConfiguration.maxLogFilesCount = 10
|
loggerConfiguration.maxLogFilesCount = 10
|
||||||
|
|
||||||
@@ -270,7 +282,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func configureNotificationManager() {
|
private func configureNotificationManager() {
|
||||||
guard BuildSettings.enableNotifications else {
|
guard ServiceLocator.shared.settings.enableNotifications else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard notificationManager == nil else {
|
guard notificationManager == nil else {
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2022 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// Store Element specific app settings.
|
||||||
|
final class AppSettings: ObservableObject {
|
||||||
|
private enum UserDefaultsKeys: String {
|
||||||
|
case hasAppLaunchedOnce
|
||||||
|
case timelineStyle
|
||||||
|
case enableAnalytics
|
||||||
|
case isIdentifiedForAnalytics
|
||||||
|
case slidingSyncProxyBaseURLString
|
||||||
|
case enableInAppNotifications
|
||||||
|
case pusherProfileTag
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var suiteName: String = InfoPlistReader.target.appGroupIdentifier
|
||||||
|
|
||||||
|
/// UserDefaults to be used on reads and writes.
|
||||||
|
private static var store: UserDefaults! = UserDefaults(suiteName: suiteName)
|
||||||
|
|
||||||
|
static func reset() {
|
||||||
|
store.removePersistentDomain(forName: suiteName)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func configureWithSuiteName(_ name: String) {
|
||||||
|
suiteName = name
|
||||||
|
|
||||||
|
guard let userDefaults = UserDefaults(suiteName: name) else {
|
||||||
|
fatalError("Fail to load shared UserDefaults")
|
||||||
|
}
|
||||||
|
|
||||||
|
store = userDefaults
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Application
|
||||||
|
|
||||||
|
/// Simple flag to check if app has been deleted between runs.
|
||||||
|
/// Used to clear data stored in the shared container and keychain
|
||||||
|
@AppStorage(UserDefaultsKeys.hasAppLaunchedOnce.rawValue, store: store)
|
||||||
|
var hasAppLaunchedOnce = false
|
||||||
|
|
||||||
|
let defaultHomeserverAddress = "matrix.org"
|
||||||
|
|
||||||
|
// MARK: - Notifications
|
||||||
|
|
||||||
|
var pusherAppId: String {
|
||||||
|
#if DEBUG
|
||||||
|
InfoPlistReader.target.baseBundleIdentifier + ".ios.dev"
|
||||||
|
#else
|
||||||
|
InfoPlistReader.target.baseBundleIdentifier + ".ios.prod"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
let pushGatewayBaseURL = URL(staticString: "https://matrix.org/_matrix/push/v1/notify")
|
||||||
|
|
||||||
|
let enableNotifications = false
|
||||||
|
|
||||||
|
// MARK: - Bug report
|
||||||
|
|
||||||
|
let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
|
||||||
|
let bugReportSentryURL = URL(staticString: "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44")
|
||||||
|
// Use the name allocated by the bug report server
|
||||||
|
let bugReportApplicationId = "riot-ios"
|
||||||
|
let bugReportUISIId = "element-auto-uisi"
|
||||||
|
let bugReportGHLabels = ["Element-X"]
|
||||||
|
|
||||||
|
// MARK: - Analytics
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
/// The configuration to use for analytics during development. Set `isEnabled` to false to disable analytics in debug builds.
|
||||||
|
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
|
||||||
|
let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
|
||||||
|
host: "https://posthog.element.dev",
|
||||||
|
apiKey: "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN",
|
||||||
|
termsURL: URL(staticString: "https://element.io/cookie-policy"))
|
||||||
|
#else
|
||||||
|
/// The configuration to use for analytics. Set `isEnabled` to false to disable analytics.
|
||||||
|
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
|
||||||
|
let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
|
||||||
|
host: "https://posthog.hss.element.io",
|
||||||
|
apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
|
||||||
|
termsURL: URL(staticString: "https://element.io/cookie-policy"))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Whether the user has already been shown the PostHog analytics prompt.
|
||||||
|
var hasSeenAnalyticsPrompt: Bool {
|
||||||
|
Self.store.object(forKey: UserDefaultsKeys.enableAnalytics.rawValue) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `true` when the user has opted in to send analytics.
|
||||||
|
@AppStorage(UserDefaultsKeys.enableAnalytics.rawValue, store: store)
|
||||||
|
var enableAnalytics = false
|
||||||
|
|
||||||
|
/// Indicates if the device has already called identify for this session to PostHog.
|
||||||
|
/// This is separate to `enableAnalytics` as logging out leaves analytics
|
||||||
|
/// enabled, but requires the next account to be identified separately.
|
||||||
|
@AppStorage(UserDefaultsKeys.isIdentifiedForAnalytics.rawValue, store: store)
|
||||||
|
var isIdentifiedForAnalytics = false
|
||||||
|
|
||||||
|
// MARK: - Room Screen
|
||||||
|
|
||||||
|
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
|
||||||
|
var timelineStyle = TimelineStyle.bubbles
|
||||||
|
|
||||||
|
// MARK: - Client
|
||||||
|
|
||||||
|
@AppStorage(UserDefaultsKeys.slidingSyncProxyBaseURLString.rawValue, store: store)
|
||||||
|
var slidingSyncProxyBaseURLString = "https://slidingsync.lab.element.dev"
|
||||||
|
|
||||||
|
// MARK: - Notifications
|
||||||
|
|
||||||
|
@AppStorage(UserDefaultsKeys.enableInAppNotifications.rawValue, store: store)
|
||||||
|
var enableInAppNotifications = true
|
||||||
|
|
||||||
|
/// Tag describing which set of device specific rules a pusher executes.
|
||||||
|
@AppStorage(UserDefaultsKeys.pusherProfileTag.rawValue, store: store)
|
||||||
|
var pusherProfileTag: String?
|
||||||
|
|
||||||
|
// MARK: - Other
|
||||||
|
|
||||||
|
let permalinkBaseURL = URL(staticString: "https://matrix.to")
|
||||||
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
final class BuildSettings {
|
|
||||||
// MARK: - Bundle Settings
|
|
||||||
|
|
||||||
static var pusherAppId: String {
|
|
||||||
#if DEBUG
|
|
||||||
InfoPlistReader.target.baseBundleIdentifier + ".ios.dev"
|
|
||||||
#else
|
|
||||||
InfoPlistReader.target.baseBundleIdentifier + ".ios.prod"
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Servers
|
|
||||||
|
|
||||||
static let defaultHomeserverAddress = "matrix.org"
|
|
||||||
static let defaultSlidingSyncProxyBaseURLString = "https://slidingsync.lab.element.dev"
|
|
||||||
static let pushGatewayBaseURL = URL(staticString: "https://matrix.org/_matrix/push/v1/notify")
|
|
||||||
|
|
||||||
// MARK: - Bug report
|
|
||||||
|
|
||||||
static let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
|
|
||||||
static let bugReportSentryURL = URL(staticString: "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44")
|
|
||||||
// Use the name allocated by the bug report server
|
|
||||||
static let bugReportApplicationId = "riot-ios"
|
|
||||||
static let bugReportUISIId = "element-auto-uisi"
|
|
||||||
|
|
||||||
static let bugReportGHLabels = ["Element-X"]
|
|
||||||
|
|
||||||
// MARK: - Analytics
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
/// The configuration to use for analytics during development. Set `isEnabled` to false to disable analytics in debug builds.
|
|
||||||
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
|
|
||||||
static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
|
|
||||||
host: "https://posthog.element.dev",
|
|
||||||
apiKey: "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN",
|
|
||||||
termsURL: URL(staticString: "https://element.io/cookie-policy"))
|
|
||||||
#else
|
|
||||||
/// The configuration to use for analytics. Set `isEnabled` to false to disable analytics.
|
|
||||||
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
|
|
||||||
static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
|
|
||||||
host: "https://posthog.hss.element.io",
|
|
||||||
apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
|
|
||||||
termsURL: URL(staticString: "https://element.io/cookie-policy"))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - Settings screen
|
|
||||||
|
|
||||||
static let settingsCrashButtonVisible = true
|
|
||||||
static let settingsShowTimelineStyle = true
|
|
||||||
|
|
||||||
// MARK: - Room screen
|
|
||||||
|
|
||||||
static let defaultRoomTimelineStyle: TimelineStyle = .bubbles
|
|
||||||
|
|
||||||
// MARK: - Other
|
|
||||||
|
|
||||||
static var permalinkBaseURL = URL(staticString: "https://matrix.to")
|
|
||||||
|
|
||||||
// MARK: - Notifications
|
|
||||||
|
|
||||||
static let enableNotifications = false
|
|
||||||
}
|
|
||||||
@@ -26,4 +26,10 @@ class ServiceLocator {
|
|||||||
func register(userNotificationController: UserNotificationControllerProtocol) {
|
func register(userNotificationController: UserNotificationControllerProtocol) {
|
||||||
self.userNotificationController = userNotificationController
|
self.userNotificationController = userNotificationController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private(set) var settings: AppSettings!
|
||||||
|
|
||||||
|
func register(appSettings: AppSettings) {
|
||||||
|
settings = appSettings
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
/// Store Element specific app settings.
|
|
||||||
final class ElementSettings: ObservableObject {
|
|
||||||
// MARK: - Constants
|
|
||||||
|
|
||||||
public enum UserDefaultsKeys: String {
|
|
||||||
case timelineStyle
|
|
||||||
case enableAnalytics
|
|
||||||
case isIdentifiedForAnalytics
|
|
||||||
case slidingSyncProxyBaseURLString
|
|
||||||
case enableInAppNotifications
|
|
||||||
case pusherProfileTag
|
|
||||||
}
|
|
||||||
|
|
||||||
static let shared = ElementSettings()
|
|
||||||
|
|
||||||
/// UserDefaults to be used on reads and writes.
|
|
||||||
static var store: UserDefaults {
|
|
||||||
guard let userDefaults = UserDefaults(suiteName: InfoPlistReader.target.appGroupIdentifier) else {
|
|
||||||
fatalError("Fail to load shared UserDefaults")
|
|
||||||
}
|
|
||||||
return userDefaults
|
|
||||||
}
|
|
||||||
|
|
||||||
private init() {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Analytics
|
|
||||||
|
|
||||||
/// Whether the user has already been shown the PostHog analytics prompt.
|
|
||||||
var hasSeenAnalyticsPrompt: Bool {
|
|
||||||
Self.store.object(forKey: UserDefaultsKeys.enableAnalytics.rawValue) != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `true` when the user has opted in to send analytics.
|
|
||||||
@AppStorage(UserDefaultsKeys.enableAnalytics.rawValue, store: store)
|
|
||||||
var enableAnalytics = false
|
|
||||||
|
|
||||||
/// Indicates if the device has already called identify for this session to PostHog.
|
|
||||||
/// This is separate to `enableAnalytics` as logging out leaves analytics
|
|
||||||
/// enabled, but requires the next account to be identified separately.
|
|
||||||
@AppStorage(UserDefaultsKeys.isIdentifiedForAnalytics.rawValue, store: store)
|
|
||||||
var isIdentifiedForAnalytics = false
|
|
||||||
|
|
||||||
// MARK: - Room Screen
|
|
||||||
|
|
||||||
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
|
|
||||||
var timelineStyle = BuildSettings.defaultRoomTimelineStyle
|
|
||||||
|
|
||||||
// MARK: - Client
|
|
||||||
|
|
||||||
@AppStorage(UserDefaultsKeys.slidingSyncProxyBaseURLString.rawValue, store: store)
|
|
||||||
var slidingSyncProxyBaseURLString = BuildSettings.defaultSlidingSyncProxyBaseURLString
|
|
||||||
|
|
||||||
// MARK: - Notifications
|
|
||||||
|
|
||||||
@AppStorage(UserDefaultsKeys.enableInAppNotifications.rawValue, store: store)
|
|
||||||
var enableInAppNotifications = true
|
|
||||||
|
|
||||||
@AppStorage(UserDefaultsKeys.pusherProfileTag.rawValue, store: store)
|
|
||||||
/// Tag describing which set of device specific rules a pusher executes.
|
|
||||||
var pusherProfileTag: String?
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ enum PermalinkBuilder {
|
|||||||
throw PermalinkBuilderError.invalidUserIdentifier
|
throw PermalinkBuilderError.invalidUserIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(userIdentifier)"
|
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(userIdentifier)"
|
||||||
|
|
||||||
guard let url = URL(string: urlString) else {
|
guard let url = URL(string: urlString) else {
|
||||||
throw PermalinkBuilderError.failedConstructingURL
|
throw PermalinkBuilderError.failedConstructingURL
|
||||||
@@ -75,7 +75,7 @@ enum PermalinkBuilder {
|
|||||||
throw PermalinkBuilderError.failedAddingPercentEncoding
|
throw PermalinkBuilderError.failedAddingPercentEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(roomId)/\(eventId)"
|
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(roomId)/\(eventId)"
|
||||||
|
|
||||||
guard let url = URL(string: urlString) else {
|
guard let url = URL(string: urlString) else {
|
||||||
throw PermalinkBuilderError.failedConstructingURL
|
throw PermalinkBuilderError.failedConstructingURL
|
||||||
@@ -91,7 +91,7 @@ enum PermalinkBuilder {
|
|||||||
throw PermalinkBuilderError.failedAddingPercentEncoding
|
throw PermalinkBuilderError.failedAddingPercentEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(identifier)"
|
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(identifier)"
|
||||||
|
|
||||||
guard let url = URL(string: urlString) else {
|
guard let url = URL(string: urlString) else {
|
||||||
throw PermalinkBuilderError.failedConstructingURL
|
throw PermalinkBuilderError.failedConstructingURL
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ final class AnalyticsPromptCoordinator: CoordinatorProtocol {
|
|||||||
init(parameters: AnalyticsPromptCoordinatorParameters) {
|
init(parameters: AnalyticsPromptCoordinatorParameters) {
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
|
|
||||||
viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL)
|
viewModel = AnalyticsPromptViewModel(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ struct AnalyticsPromptStrings {
|
|||||||
|
|
||||||
// Replace the placeholder with a link.
|
// Replace the placeholder with a link.
|
||||||
var link = AttributedString(ElementL10n.analyticsOptInContentLink)
|
var link = AttributedString(ElementL10n.analyticsOptInContentLink)
|
||||||
link.link = BuildSettings.analyticsConfiguration.termsURL
|
link.link = ServiceLocator.shared.settings.analyticsConfiguration.termsURL
|
||||||
optInContent.replaceSubrange(range, with: link)
|
optInContent.replaceSubrange(range, with: link)
|
||||||
|
|
||||||
self.optInContent = optInContent
|
self.optInContent = optInContent
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ struct AnalyticsPrompt: View {
|
|||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct AnalyticsPrompt_Previews: PreviewProvider {
|
struct AnalyticsPrompt_Previews: PreviewProvider {
|
||||||
static let viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL)
|
static let viewModel = AnalyticsPromptViewModel(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
AnalyticsPrompt(context: viewModel.context)
|
AnalyticsPrompt(context: viewModel.context)
|
||||||
.tint(.element.accent)
|
.tint(.element.accent)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class AuthenticationCoordinator: CoordinatorProtocol {
|
|||||||
private func startAuthentication() async {
|
private func startAuthentication() async {
|
||||||
startLoading()
|
startLoading()
|
||||||
|
|
||||||
switch await authenticationService.configure(for: BuildSettings.defaultHomeserverAddress) {
|
switch await authenticationService.configure(for: ServiceLocator.shared.settings.defaultHomeserverAddress) {
|
||||||
case .success:
|
case .success:
|
||||||
stopLoading()
|
stopLoading()
|
||||||
showLoginScreen()
|
showLoginScreen()
|
||||||
|
|||||||
+3
-3
@@ -23,7 +23,7 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
|
|||||||
|
|
||||||
init(homeserverAddress: String, isModallyPresented: Bool) {
|
init(homeserverAddress: String, isModallyPresented: Bool) {
|
||||||
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress,
|
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress,
|
||||||
slidingSyncProxyAddress: ElementSettings.shared.slidingSyncProxyBaseURLString)
|
slidingSyncProxyAddress: ServiceLocator.shared.settings.slidingSyncProxyBaseURLString)
|
||||||
|
|
||||||
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
|
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
|
||||||
isModallyPresented: isModallyPresented))
|
isModallyPresented: isModallyPresented))
|
||||||
@@ -33,8 +33,8 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .confirm:
|
case .confirm:
|
||||||
if !state.bindings.slidingSyncProxyAddress.isEmpty,
|
if !state.bindings.slidingSyncProxyAddress.isEmpty,
|
||||||
state.bindings.slidingSyncProxyAddress != ElementSettings.shared.slidingSyncProxyBaseURLString {
|
state.bindings.slidingSyncProxyAddress != ServiceLocator.shared.settings.slidingSyncProxyBaseURLString {
|
||||||
ElementSettings.shared.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
|
ServiceLocator.shared.settings.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol {
|
|||||||
let result = try await bugReportService.submitBugReport(text: context.reportText,
|
let result = try await bugReportService.submitBugReport(text: context.reportText,
|
||||||
includeLogs: context.sendingLogsEnabled,
|
includeLogs: context.sendingLogsEnabled,
|
||||||
includeCrashLog: true,
|
includeCrashLog: true,
|
||||||
githubLabels: BuildSettings.bugReportGHLabels,
|
githubLabels: ServiceLocator.shared.settings.bugReportGHLabels,
|
||||||
files: files)
|
files: files)
|
||||||
MXLog.info("SubmitBugReport succeeded, result: \(result.reportUrl)")
|
MXLog.info("SubmitBugReport succeeded, result: \(result.reportUrl)")
|
||||||
callback?(.submitFinished)
|
callback?(.submitFinished)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RoomScreen: View {
|
struct RoomScreen: View {
|
||||||
@ObservedObject private var settings = ElementSettings.shared
|
@ObservedObject private var settings = ServiceLocator.shared.settings
|
||||||
@ObservedObject var context: RoomScreenViewModel.Context
|
@ObservedObject var context: RoomScreenViewModel.Context
|
||||||
@State private var showReactionsMenuForItemId = ""
|
@State private var showReactionsMenuForItemId = ""
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ enum TimelineStyle: String, CaseIterable {
|
|||||||
// MARK: - Environment
|
// MARK: - Environment
|
||||||
|
|
||||||
private struct TimelineStyleKey: EnvironmentKey {
|
private struct TimelineStyleKey: EnvironmentKey {
|
||||||
static let defaultValue = BuildSettings.defaultRoomTimelineStyle
|
static let defaultValue = TimelineStyle.bubbles
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EnvironmentValues {
|
extension EnvironmentValues {
|
||||||
|
|||||||
@@ -50,8 +50,6 @@ final class SettingsCoordinator: CoordinatorProtocol {
|
|||||||
self.toggleAnalytics()
|
self.toggleAnalytics()
|
||||||
case .reportBug:
|
case .reportBug:
|
||||||
self.presentBugReportScreen()
|
self.presentBugReportScreen()
|
||||||
case .crash:
|
|
||||||
self.parameters.bugReportService.crash()
|
|
||||||
case .logout:
|
case .logout:
|
||||||
self.callback?(.logout)
|
self.callback?(.logout)
|
||||||
}
|
}
|
||||||
@@ -67,7 +65,7 @@ final class SettingsCoordinator: CoordinatorProtocol {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func toggleAnalytics() {
|
private func toggleAnalytics() {
|
||||||
if ElementSettings.shared.enableAnalytics {
|
if ServiceLocator.shared.settings.enableAnalytics {
|
||||||
Analytics.shared.optOut()
|
Analytics.shared.optOut()
|
||||||
} else {
|
} else {
|
||||||
Analytics.shared.optIn(with: parameters.userSession)
|
Analytics.shared.optIn(with: parameters.userSession)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ enum SettingsViewModelAction {
|
|||||||
case close
|
case close
|
||||||
case toggleAnalytics
|
case toggleAnalytics
|
||||||
case reportBug
|
case reportBug
|
||||||
case crash
|
|
||||||
case logout
|
case logout
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,13 +32,12 @@ struct SettingsViewState: BindableState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsViewStateBindings {
|
struct SettingsViewStateBindings {
|
||||||
var enableAnalytics = ElementSettings.shared.enableAnalytics
|
var enableAnalytics = ServiceLocator.shared.settings.enableAnalytics
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SettingsViewAction {
|
enum SettingsViewAction {
|
||||||
case close
|
case close
|
||||||
case toggleAnalytics
|
case toggleAnalytics
|
||||||
case reportBug
|
case reportBug
|
||||||
case crash
|
|
||||||
case logout
|
case logout
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ class SettingsViewModel: SettingsViewModelType, SettingsViewModelProtocol {
|
|||||||
callback?(.toggleAnalytics)
|
callback?(.toggleAnalytics)
|
||||||
case .reportBug:
|
case .reportBug:
|
||||||
callback?(.reportBug)
|
callback?(.reportBug)
|
||||||
case .crash:
|
|
||||||
callback?(.crash)
|
|
||||||
case .logout:
|
case .logout:
|
||||||
callback?(.logout)
|
callback?(.logout)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import SwiftUI
|
|||||||
struct SettingsScreen: View {
|
struct SettingsScreen: View {
|
||||||
@State private var showingLogoutConfirmation = false
|
@State private var showingLogoutConfirmation = false
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
@ObservedObject private var settings = ElementSettings.shared
|
@ObservedObject private var settings = ServiceLocator.shared.settings
|
||||||
|
|
||||||
@ScaledMetric private var avatarSize = AvatarSize.user(on: .settings).value
|
@ScaledMetric private var avatarSize = AvatarSize.user(on: .settings).value
|
||||||
@ScaledMetric private var menuIconSize = 30.0
|
@ScaledMetric private var menuIconSize = 30.0
|
||||||
@@ -110,30 +110,20 @@ struct SettingsScreen: View {
|
|||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.foregroundColor(.element.primaryContent)
|
.foregroundColor(.element.primaryContent)
|
||||||
.accessibilityIdentifier("reportBugButton")
|
.accessibilityIdentifier("reportBugButton")
|
||||||
|
|
||||||
if BuildSettings.settingsCrashButtonVisible {
|
|
||||||
Button("Crash app",
|
|
||||||
role: .destructive) { context.send(viewAction: .crash)
|
|
||||||
}
|
|
||||||
.listRowInsets(listRowInsets)
|
|
||||||
.accessibilityIdentifier("crashButton")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var userInterfaceSection: some View {
|
private var userInterfaceSection: some View {
|
||||||
if BuildSettings.settingsShowTimelineStyle {
|
Section {
|
||||||
Section {
|
Picker(ElementL10n.settingsTimelineStyle, selection: $settings.timelineStyle) {
|
||||||
Picker(ElementL10n.settingsTimelineStyle, selection: $settings.timelineStyle) {
|
ForEach(TimelineStyle.allCases, id: \.self) { style in
|
||||||
ForEach(TimelineStyle.allCases, id: \.self) { style in
|
Text(style.description)
|
||||||
Text(style.description)
|
.tag(style)
|
||||||
.tag(style)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.listRowInsets(listRowInsets)
|
|
||||||
.accessibilityIdentifier("timelineStylePicker")
|
|
||||||
}
|
}
|
||||||
|
.listRowInsets(listRowInsets)
|
||||||
|
.accessibilityIdentifier("timelineStylePicker")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ class Analytics {
|
|||||||
/// Whether to show the user the analytics opt in prompt.
|
/// Whether to show the user the analytics opt in prompt.
|
||||||
var shouldShowAnalyticsPrompt: Bool {
|
var shouldShowAnalyticsPrompt: Bool {
|
||||||
// Only show the prompt once, and when analytics are enabled in BuildSettings.
|
// Only show the prompt once, and when analytics are enabled in BuildSettings.
|
||||||
!ElementSettings.shared.hasSeenAnalyticsPrompt && BuildSettings.analyticsConfiguration.isEnabled
|
!ServiceLocator.shared.settings.hasSeenAnalyticsPrompt && ServiceLocator.shared.settings.analyticsConfiguration.isEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opts in to analytics tracking with the supplied user session.
|
/// Opts in to analytics tracking with the supplied user session.
|
||||||
/// - Parameter userSession: The user session to use to when reading/generating the analytics ID.
|
/// - Parameter userSession: The user session to use to when reading/generating the analytics ID.
|
||||||
/// The session will be ignored if not running.
|
/// The session will be ignored if not running.
|
||||||
func optIn(with userSession: UserSessionProtocol) {
|
func optIn(with userSession: UserSessionProtocol) {
|
||||||
ElementSettings.shared.enableAnalytics = true
|
ServiceLocator.shared.settings.enableAnalytics = true
|
||||||
startIfEnabled()
|
startIfEnabled()
|
||||||
|
|
||||||
Task { await useAnalyticsSettings(from: userSession) }
|
Task { await useAnalyticsSettings(from: userSession) }
|
||||||
@@ -64,7 +64,7 @@ class Analytics {
|
|||||||
|
|
||||||
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
|
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
|
||||||
func optOut() {
|
func optOut() {
|
||||||
ElementSettings.shared.enableAnalytics = false
|
ServiceLocator.shared.settings.enableAnalytics = false
|
||||||
|
|
||||||
// The order is important here. PostHog ignores the reset if stopped.
|
// The order is important here. PostHog ignores the reset if stopped.
|
||||||
reset()
|
reset()
|
||||||
@@ -76,7 +76,7 @@ class Analytics {
|
|||||||
|
|
||||||
/// Starts the analytics client if the user has opted in, otherwise does nothing.
|
/// Starts the analytics client if the user has opted in, otherwise does nothing.
|
||||||
func startIfEnabled() {
|
func startIfEnabled() {
|
||||||
guard ElementSettings.shared.enableAnalytics, !isRunning else { return }
|
guard ServiceLocator.shared.settings.enableAnalytics, !isRunning else { return }
|
||||||
|
|
||||||
client.start()
|
client.start()
|
||||||
// monitoringClient.start()
|
// monitoringClient.start()
|
||||||
@@ -96,8 +96,8 @@ class Analytics {
|
|||||||
/// - Parameter userSession: The user session to read analytics settings from.
|
/// - Parameter userSession: The user session to read analytics settings from.
|
||||||
func useAnalyticsSettings(from userSession: UserSessionProtocol) async {
|
func useAnalyticsSettings(from userSession: UserSessionProtocol) async {
|
||||||
guard
|
guard
|
||||||
ElementSettings.shared.enableAnalytics,
|
ServiceLocator.shared.settings.enableAnalytics,
|
||||||
!ElementSettings.shared.isIdentifiedForAnalytics
|
!ServiceLocator.shared.settings.isIdentifiedForAnalytics
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
let service = AnalyticsService(userSession: userSession)
|
let service = AnalyticsService(userSession: userSession)
|
||||||
@@ -122,7 +122,7 @@ class Analytics {
|
|||||||
// monitoringClient.reset()
|
// monitoringClient.reset()
|
||||||
|
|
||||||
MXLog.debug("Reset.")
|
MXLog.debug("Reset.")
|
||||||
ElementSettings.shared.isIdentifiedForAnalytics = false
|
ServiceLocator.shared.settings.isIdentifiedForAnalytics = false
|
||||||
|
|
||||||
// Stop collecting crash logs
|
// Stop collecting crash logs
|
||||||
// MXLogger.logCrashes(false)
|
// MXLogger.logCrashes(false)
|
||||||
@@ -147,7 +147,7 @@ class Analytics {
|
|||||||
|
|
||||||
client.identify(id: id)
|
client.identify(id: id)
|
||||||
MXLog.debug("Identified.")
|
MXLog.debug("Identified.")
|
||||||
ElementSettings.shared.isIdentifiedForAnalytics = true
|
ServiceLocator.shared.settings.isIdentifiedForAnalytics = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Capture an event in the `client`.
|
/// Capture an event in the `client`.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import PostHog
|
|||||||
|
|
||||||
extension PHGPostHogConfiguration {
|
extension PHGPostHogConfiguration {
|
||||||
static var standard: PHGPostHogConfiguration? {
|
static var standard: PHGPostHogConfiguration? {
|
||||||
let analyticsConfiguration = BuildSettings.analyticsConfiguration
|
let analyticsConfiguration = ServiceLocator.shared.settings.analyticsConfiguration
|
||||||
guard analyticsConfiguration.isEnabled else { return nil }
|
guard analyticsConfiguration.isEnabled else { return nil }
|
||||||
|
|
||||||
let postHogConfiguration = PHGPostHogConfiguration(apiKey: analyticsConfiguration.apiKey, host: analyticsConfiguration.host)
|
let postHogConfiguration = PHGPostHogConfiguration(apiKey: analyticsConfiguration.apiKey, host: analyticsConfiguration.host)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
|
|||||||
private let authenticationService: AuthenticationService
|
private let authenticationService: AuthenticationService
|
||||||
private let userSessionStore: UserSessionStoreProtocol
|
private let userSessionStore: UserSessionStoreProtocol
|
||||||
|
|
||||||
private(set) var homeserver = LoginHomeserver(address: BuildSettings.defaultHomeserverAddress, loginMode: .unknown)
|
private(set) var homeserver = LoginHomeserver(address: ServiceLocator.shared.settings.defaultHomeserverAddress, loginMode: .unknown)
|
||||||
|
|
||||||
init(userSessionStore: UserSessionStoreProtocol) {
|
init(userSessionStore: UserSessionStoreProtocol) {
|
||||||
self.userSessionStore = userSessionStore
|
self.userSessionStore = userSessionStore
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class BugReportService: BugReportServiceProtocol {
|
|||||||
|
|
||||||
init(withBaseURL baseURL: URL,
|
init(withBaseURL baseURL: URL,
|
||||||
sentryURL: URL,
|
sentryURL: URL,
|
||||||
applicationId: String = BuildSettings.bugReportApplicationId,
|
applicationId: String = ServiceLocator.shared.settings.bugReportApplicationId,
|
||||||
session: URLSession = .shared) {
|
session: URLSession = .shared) {
|
||||||
self.baseURL = baseURL
|
self.baseURL = baseURL
|
||||||
self.sentryURL = sentryURL
|
self.sentryURL = sentryURL
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
|
|
||||||
await Task.dispatch(on: clientQueue) {
|
await Task.dispatch(on: clientQueue) {
|
||||||
do {
|
do {
|
||||||
let slidingSyncBuilder = try client.slidingSync().homeserver(url: ElementSettings.shared.slidingSyncProxyBaseURLString)
|
let slidingSyncBuilder = try client.slidingSync().homeserver(url: ServiceLocator.shared.settings.slidingSyncProxyBaseURLString)
|
||||||
|
|
||||||
let slidingSyncView = try SlidingSyncViewBuilder()
|
let slidingSyncView = try SlidingSyncViewBuilder()
|
||||||
.timelineLimit(limit: 10)
|
.timelineLimit(limit: 10)
|
||||||
|
|||||||
@@ -89,12 +89,12 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
do {
|
do {
|
||||||
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
|
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
|
||||||
kind: .http,
|
kind: .http,
|
||||||
appId: BuildSettings.pusherAppId,
|
appId: ServiceLocator.shared.settings.pusherAppId,
|
||||||
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
|
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
|
||||||
deviceDisplayName: UIDevice.current.name,
|
deviceDisplayName: UIDevice.current.name,
|
||||||
profileTag: pusherProfileTag(),
|
profileTag: pusherProfileTag(),
|
||||||
lang: Bundle.preferredLanguages.first ?? "en",
|
lang: Bundle.preferredLanguages.first ?? "en",
|
||||||
url: BuildSettings.pushGatewayBaseURL.absoluteString,
|
url: ServiceLocator.shared.settings.pushGatewayBaseURL.absoluteString,
|
||||||
format: .eventIdOnly,
|
format: .eventIdOnly,
|
||||||
defaultPayload: [
|
defaultPayload: [
|
||||||
"aps": [
|
"aps": [
|
||||||
@@ -113,7 +113,7 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func pusherProfileTag() -> String {
|
private func pusherProfileTag() -> String {
|
||||||
if let currentTag = ElementSettings.shared.pusherProfileTag {
|
if let currentTag = ServiceLocator.shared.settings.pusherProfileTag {
|
||||||
return currentTag
|
return currentTag
|
||||||
}
|
}
|
||||||
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
@@ -122,7 +122,7 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
|
|||||||
return String(chars[chars.index(chars.startIndex, offsetBy: offset)])
|
return String(chars[chars.index(chars.startIndex, offsetBy: offset)])
|
||||||
}.joined()
|
}.joined()
|
||||||
|
|
||||||
ElementSettings.shared.pusherProfileTag = newTag
|
ServiceLocator.shared.settings.pusherProfileTag = newTag
|
||||||
return newTag
|
return newTag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ 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 ElementSettings.shared.enableInAppNotifications else {
|
guard ServiceLocator.shared.settings.enableInAppNotifications else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
guard let delegate else {
|
guard let delegate else {
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
MXLog.debug("Setup base directory at: \(baseDirectory)")
|
MXLog.debug("Setup base directory at: \(baseDirectory)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deletes all data stored in the shared container and keychain
|
||||||
|
func reset() {
|
||||||
|
try? FileManager.default.removeItem(at: baseDirectory)
|
||||||
|
keychainController.removeAllRestorationTokens()
|
||||||
|
}
|
||||||
|
|
||||||
func restoreUserSession() async -> Result<UserSession, UserSessionStoreError> {
|
func restoreUserSession() async -> Result<UserSession, UserSessionStoreError> {
|
||||||
let availableCredentials = keychainController.restorationTokens()
|
let availableCredentials = keychainController.restorationTokens()
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ enum UserSessionStoreError: Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protocol UserSessionStoreProtocol {
|
protocol UserSessionStoreProtocol {
|
||||||
|
/// Deletes all data stored in the shared container and keychain
|
||||||
|
func reset()
|
||||||
|
|
||||||
/// Whether or not there are sessions in the store.
|
/// Whether or not there are sessions in the store.
|
||||||
var hasSessions: Bool { get }
|
var hasSessions: Bool { get }
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ class UITestsAppCoordinator: AppCoordinatorProtocol {
|
|||||||
mockScreens = UITestScreenIdentifier.allCases.map { MockScreen(id: $0, navigationRootCoordinator: navigationRootCoordinator) }
|
mockScreens = UITestScreenIdentifier.allCases.map { MockScreen(id: $0, navigationRootCoordinator: navigationRootCoordinator) }
|
||||||
|
|
||||||
ServiceLocator.shared.register(userNotificationController: MockUserNotificationController())
|
ServiceLocator.shared.register(userNotificationController: MockUserNotificationController())
|
||||||
|
|
||||||
|
AppSettings.configureWithSuiteName("io.element.elementx.uitests")
|
||||||
|
AppSettings.reset()
|
||||||
|
ServiceLocator.shared.register(appSettings: AppSettings())
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
|||||||
@@ -26,12 +26,9 @@ class SettingsUITests: XCTestCase {
|
|||||||
let reportBugButton = app.buttons["reportBugButton"]
|
let reportBugButton = app.buttons["reportBugButton"]
|
||||||
XCTAssert(reportBugButton.exists)
|
XCTAssert(reportBugButton.exists)
|
||||||
XCTAssertEqual(reportBugButton.label, ElementL10n.sendBugReport)
|
XCTAssertEqual(reportBugButton.label, ElementL10n.sendBugReport)
|
||||||
XCTAssertEqual(app.buttons["crashButton"].exists, BuildSettings.settingsCrashButtonVisible)
|
|
||||||
|
|
||||||
XCTAssertEqual(app.buttons["timelineStylePicker"].exists, BuildSettings.settingsShowTimelineStyle)
|
XCTAssertTrue(app.buttons["timelineStylePicker"].exists)
|
||||||
if BuildSettings.settingsShowTimelineStyle {
|
XCTAssertTrue(app.staticTexts[ElementL10n.settingsTimelineStyle].exists)
|
||||||
XCTAssertTrue(app.staticTexts[ElementL10n.settingsTimelineStyle].exists)
|
|
||||||
}
|
|
||||||
|
|
||||||
let logoutButton = app.buttons["logoutButton"]
|
let logoutButton = app.buttons["logoutButton"]
|
||||||
XCTAssert(logoutButton.exists)
|
XCTAssert(logoutButton.exists)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:27d058762e6eafc940c71b8e4edeef26f6ccad34e086ad6a0f0a30252848485f
|
oid sha256:0ab7f75524ce702d1d0344884aad7bafa821b7cdd8215c28aff7ddcf0f5d184e
|
||||||
size 93290
|
size 90671
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:97980472cdf36a0d07109fc066bd8184013b0a99cbbd164e82dbfb0a20cbc09f
|
oid sha256:79163533dc0069a51c0309ad4cc5291a324457949d69155954fa2c8529de9478
|
||||||
size 115670
|
size 109653
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:cbbb771047c24948ad5b38308273fc1262667bdaafcf0bac61704b3186f66f1d
|
oid sha256:32ddcc37817a668eb87e0670e6b0fc35de66df19e0e12904c10532f0bc4172c5
|
||||||
size 93992
|
size 91443
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:063feeb819831472533b47c11eb8a69924843fea616ef3f56651274fd8f970c9
|
oid sha256:5e30413bbd9e9268085b4564ab646f25a69a262285d68ec2477eba7dd68c3653
|
||||||
size 115397
|
size 111343
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:df1a88376fa6e083729329fcf221093c4704d900de8acc40600a61b7fd812a2c
|
oid sha256:9cbc095e0d4f194b01ac4b560315b8914100302ad33e8d7a2d2c81ea6280ecb7
|
||||||
size 94430
|
size 91866
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:d4b3f25e56c50b01e3ce87c4bd0537e752664e23b7b761a5bdb1e2b88ed4dc35
|
oid sha256:a44a61f155c7d0559982a70fa1ffd1a84828e8a8a9f9998ea6cb05210f4e8798
|
||||||
size 117371
|
size 113408
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ targets:
|
|||||||
- "**/__Snapshots__/**"
|
- "**/__Snapshots__/**"
|
||||||
- path: ../SupportingFiles
|
- path: ../SupportingFiles
|
||||||
- path: ../../Tools/Scripts/Templates/SimpleScreenExample/Tests/UI
|
- path: ../../Tools/Scripts/Templates/SimpleScreenExample/Tests/UI
|
||||||
- path: ../../ElementX/Sources/Application/BuildSettings.swift
|
- path: ../../ElementX/Sources/Application/AppSettings.swift
|
||||||
- path: ../../ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyle.swift
|
- path: ../../ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyle.swift
|
||||||
- path: ../../ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift
|
- path: ../../ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift
|
||||||
- path: ../../ElementX/Sources/UITests/UITestScreenIdentifier.swift
|
- path: ../../ElementX/Sources/UITests/UITestScreenIdentifier.swift
|
||||||
|
|||||||
@@ -19,10 +19,16 @@ import AnalyticsEvents
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class AnalyticsTests: XCTestCase {
|
class AnalyticsTests: XCTestCase {
|
||||||
|
private var applicationSettings: AppSettings!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
AppSettings.configureWithSuiteName("io.element.elementx.unitests")
|
||||||
|
AppSettings.reset()
|
||||||
|
applicationSettings = AppSettings()
|
||||||
|
}
|
||||||
|
|
||||||
func testAnalyticsPromptNewUser() {
|
func testAnalyticsPromptNewUser() {
|
||||||
// Given a fresh install of the app (without PostHog analytics having been set).
|
// Given a fresh install of the app (without PostHog analytics having been set).
|
||||||
ElementSettings.store.removeObject(forKey: ElementSettings.UserDefaultsKeys.enableAnalytics.rawValue)
|
|
||||||
|
|
||||||
// When the user is prompted for analytics.
|
// When the user is prompted for analytics.
|
||||||
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
||||||
|
|
||||||
@@ -32,7 +38,7 @@ class AnalyticsTests: XCTestCase {
|
|||||||
|
|
||||||
func testAnalyticsPromptUserDeclinedPostHog() {
|
func testAnalyticsPromptUserDeclinedPostHog() {
|
||||||
// Given an existing install of the app where the user previously declined PostHog
|
// Given an existing install of the app where the user previously declined PostHog
|
||||||
ElementSettings.shared.enableAnalytics = false
|
applicationSettings.enableAnalytics = false
|
||||||
|
|
||||||
// When the user is prompted for analytics
|
// When the user is prompted for analytics
|
||||||
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
||||||
@@ -43,7 +49,7 @@ class AnalyticsTests: XCTestCase {
|
|||||||
|
|
||||||
func testAnalyticsPromptUserAcceptedPostHog() {
|
func testAnalyticsPromptUserAcceptedPostHog() {
|
||||||
// Given an existing install of the app where the user previously accepted PostHog
|
// Given an existing install of the app where the user previously accepted PostHog
|
||||||
ElementSettings.shared.enableAnalytics = true
|
applicationSettings.enableAnalytics = true
|
||||||
|
|
||||||
// When the user is prompted for analytics
|
// When the user is prompted for analytics
|
||||||
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
let showPrompt = Analytics.shared.shouldShowAnalyticsPrompt
|
||||||
|
|||||||
@@ -18,12 +18,20 @@
|
|||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class PermalinkBuilderTests: XCTestCase {
|
class PermalinkBuilderTests: XCTestCase {
|
||||||
|
private var appSettings: AppSettings!
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
AppSettings.configureWithSuiteName("io.element.elementx.unitests")
|
||||||
|
AppSettings.reset()
|
||||||
|
appSettings = AppSettings()
|
||||||
|
}
|
||||||
|
|
||||||
func testUserIdentifierPermalink() {
|
func testUserIdentifierPermalink() {
|
||||||
let userId = "@abcdefghijklmnopqrstuvwxyz1234567890._-=/:matrix.org"
|
let userId = "@abcdefghijklmnopqrstuvwxyz1234567890._-=/:matrix.org"
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let permalink = try PermalinkBuilder.permalinkTo(userIdentifier: userId)
|
let permalink = try PermalinkBuilder.permalinkTo(userIdentifier: userId)
|
||||||
XCTAssertEqual(permalink, URL(string: "\(BuildSettings.permalinkBaseURL)/#/\(userId)"))
|
XCTAssertEqual(permalink, URL(string: "\(appSettings.permalinkBaseURL)/#/\(userId)"))
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("User identifier must be valid: \(error)")
|
XCTFail("User identifier must be valid: \(error)")
|
||||||
}
|
}
|
||||||
@@ -43,7 +51,7 @@ class PermalinkBuilderTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
let permalink = try PermalinkBuilder.permalinkTo(roomIdentifier: roomId)
|
let permalink = try PermalinkBuilder.permalinkTo(roomIdentifier: roomId)
|
||||||
XCTAssertEqual(permalink, URL(string: "\(BuildSettings.permalinkBaseURL)/#/!abcdefghijklmnopqrstuvwxyz1234567890%3Amatrix.org"))
|
XCTAssertEqual(permalink, URL(string: "\(appSettings.permalinkBaseURL)/#/!abcdefghijklmnopqrstuvwxyz1234567890%3Amatrix.org"))
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("Room identifier must be valid: \(error)")
|
XCTFail("Room identifier must be valid: \(error)")
|
||||||
}
|
}
|
||||||
@@ -63,7 +71,7 @@ class PermalinkBuilderTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
let permalink = try PermalinkBuilder.permalinkTo(roomAlias: roomAlias)
|
let permalink = try PermalinkBuilder.permalinkTo(roomAlias: roomAlias)
|
||||||
XCTAssertEqual(permalink, URL(string: "\(BuildSettings.permalinkBaseURL)/#/%23abcdefghijklmnopqrstuvwxyz-_.1234567890%3Amatrix.org"))
|
XCTAssertEqual(permalink, URL(string: "\(appSettings.permalinkBaseURL)/#/%23abcdefghijklmnopqrstuvwxyz-_.1234567890%3Amatrix.org"))
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("Room alias must be valid: \(error)")
|
XCTFail("Room alias must be valid: \(error)")
|
||||||
}
|
}
|
||||||
@@ -84,7 +92,7 @@ class PermalinkBuilderTests: XCTestCase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
let permalink = try PermalinkBuilder.permalinkTo(eventIdentifier: eventId, roomIdentifier: roomId)
|
let permalink = try PermalinkBuilder.permalinkTo(eventIdentifier: eventId, roomIdentifier: roomId)
|
||||||
XCTAssertEqual(permalink, URL(string: "\(BuildSettings.permalinkBaseURL)/#/!abcdefghijklmnopqrstuvwxyz1234567890%3Amatrix.org/%24abcdefghijklmnopqrstuvwxyz1234567890"))
|
XCTAssertEqual(permalink, URL(string: "\(appSettings.permalinkBaseURL)/#/!abcdefghijklmnopqrstuvwxyz1234567890%3Amatrix.org/%24abcdefghijklmnopqrstuvwxyz1234567890"))
|
||||||
} catch {
|
} catch {
|
||||||
XCTFail("Room and event identifiers must be valid: \(error)")
|
XCTFail("Room and event identifiers must be valid: \(error)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,15 +56,4 @@ class SettingsViewModelTests: XCTestCase {
|
|||||||
await Task.yield()
|
await Task.yield()
|
||||||
XCTAssert(correctResult)
|
XCTAssert(correctResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCrash() async throws {
|
|
||||||
var correctResult = false
|
|
||||||
viewModel.callback = { result in
|
|
||||||
correctResult = result == .crash
|
|
||||||
}
|
|
||||||
|
|
||||||
context.send(viewAction: .crash)
|
|
||||||
await Task.yield()
|
|
||||||
XCTAssert(correctResult)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user