mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Add AppLockSetupPINScreen. (#1930)
This commit is contained in:
parent
ceee6b1853
commit
5356a56a00
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
000765812BABB81F5174C601 /* AppLockSetupPINScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEDC38E389B89BCF5C1AFD4A /* AppLockSetupPINScreenUITests.swift */; };
|
||||
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C070FD43DC6BF4E50217965A /* LocalizationTests.swift */; };
|
||||
0180C44B997EDA8D21F883AC /* RoomNotificationSettingsCustomSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.swift */; };
|
||||
0206016CCEF6EF9365916768 /* AppLockSettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33284693F54382F46CFD2EDD /* AppLockSettingsScreenViewModelProtocol.swift */; };
|
||||
@ -64,6 +65,7 @@
|
||||
0ED691ADC9C2EA457E7A9427 /* FormattingToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE449DFBA7CC863EEB2FD2A /* FormattingToolbar.swift */; };
|
||||
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
|
||||
0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; };
|
||||
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */; };
|
||||
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */; };
|
||||
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; };
|
||||
119AE9A3FC6E0606C1146528 /* NotificationSettingsEditScreenRoomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97F8963B14EB0AF3940DDBF /* NotificationSettingsEditScreenRoomCell.swift */; };
|
||||
@ -133,6 +135,7 @@
|
||||
24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */; };
|
||||
25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */; };
|
||||
256D76972BA3254F7CB7F88B /* LocationAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */; };
|
||||
25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */; };
|
||||
266C4DF893F2947DCCEF327B /* InvitesScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC14E5209C262530E19BC4C1 /* InvitesScreenViewModelTests.swift */; };
|
||||
2689D22EF1D10D22B0A4DAEA /* NotificationContentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */; };
|
||||
273AB64B9A26B61C51858867 /* AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73A07BAEDD74C48795A996A /* AsyncSequence.swift */; };
|
||||
@ -269,6 +272,7 @@
|
||||
4BAB8222DBA0B4207D1223E0 /* NotificationSettingsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */; };
|
||||
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */; };
|
||||
4BB51476A29E7E27BC14EA22 /* UserDetailsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022E6BD64CB4610B9C95FC02 /* UserDetailsEditScreenViewModel.swift */; };
|
||||
4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */; };
|
||||
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; };
|
||||
4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; };
|
||||
4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */; };
|
||||
@ -282,6 +286,7 @@
|
||||
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */; };
|
||||
4FFDC274824F7CC0BBDF581E /* BugReportScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */; };
|
||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874A1842477895F199567BD7 /* TimelineView.swift */; };
|
||||
50381244BA280451771BE3ED /* PINTextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF13BFD415CA84B1272E94F8 /* PINTextFieldTests.swift */; };
|
||||
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; };
|
||||
516534FC5C893D57F169D5A8 /* MapTilerGeocoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33720F7AD25E85E4A84669E8 /* MapTilerGeocoding.swift */; };
|
||||
@ -401,6 +406,7 @@
|
||||
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
|
||||
71B62C48B8079D49F3FBC845 /* ExpiringTaskRunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */; };
|
||||
71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; };
|
||||
733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */; };
|
||||
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */; };
|
||||
73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */; };
|
||||
@ -468,6 +474,7 @@
|
||||
8421FFCD5360A15D170922A8 /* ProgressMaskModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79A1D75C7C52CD14A327CC90 /* ProgressMaskModifier.swift */; };
|
||||
84226AD2E1F1FBC965F3B09E /* UnitTestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8E19C4645D3F5F9FB02355 /* UnitTestsAppCoordinator.swift */; };
|
||||
8478992479B296C45150208F /* AppLockScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */; };
|
||||
847DE3A7EB9FCA2C429C6E85 /* PINTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1D4A6D451F43A03CACD01D /* PINTextField.swift */; };
|
||||
84CAE3E96D93194DA06B9194 /* CallScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9AD6AE5FC868962F090740 /* CallScreenViewModelProtocol.swift */; };
|
||||
84EFCB95F9DA2979C8042B26 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
|
||||
8544657DEEE717ED2E22E382 /* RoomNotificationSettingsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */; };
|
||||
@ -543,6 +550,7 @@
|
||||
964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; };
|
||||
968823C9DBF3062729413EBF /* MigrationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DF0BFE5637EA42F5651FE8 /* MigrationScreenCoordinator.swift */; };
|
||||
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
||||
9696ECAFB4F0C079C5C2A526 /* AppLockSetupPINScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */; };
|
||||
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
|
||||
97969EF0B9C412CD38E5CA93 /* AppLockScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4005D82E9D27BAF006A8FE1 /* AppLockScreenViewModel.swift */; };
|
||||
@ -835,6 +843,7 @@
|
||||
E45C9FA22BC13B477FD3B4AC /* EmojiDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */; };
|
||||
E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; };
|
||||
E49F74BD93230BDEFFE5EA51 /* RoomNotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D295F0081084F38DB20893 /* RoomNotificationSettingsScreenViewModelTests.swift */; };
|
||||
E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */; };
|
||||
E4BAEED438A843D7B01D8069 /* CompletionSuggestionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F421E51DF00377DE1A01354 /* CompletionSuggestionView.swift */; };
|
||||
E570117376826665640F0CFD /* SessionVerificationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16CAF20C9AC874A210E2DCF /* SessionVerificationScreenViewModelProtocol.swift */; };
|
||||
E571163060CBE87D82CE24FD /* NSESettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9DFC0FBA0FC6FC4DC0FC9FC /* NSESettings.swift */; };
|
||||
@ -1097,6 +1106,7 @@
|
||||
1E508AB0EDEE017FF4F6F8D1 /* DTHTMLElement+AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DTHTMLElement+AttributedStringBuilder.swift"; sourceTree = "<group>"; };
|
||||
1F2529D434C750ED78ADF1ED /* UserAgentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilder.swift; sourceTree = "<group>"; };
|
||||
1F7C6DDBB5D12F6EF6A3D6E1 /* CollapsibleReactionLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleReactionLayout.swift; sourceTree = "<group>"; };
|
||||
1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||
201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = "<group>"; };
|
||||
20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -1190,6 +1200,7 @@
|
||||
3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = "<group>"; };
|
||||
3CCE3636E3D01477C8B2E9D0 /* ReportContentScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenModels.swift; sourceTree = "<group>"; };
|
||||
3CFD5EB0B0EEA4549FB49784 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
|
||||
3D1D4A6D451F43A03CACD01D /* PINTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PINTextField.swift; sourceTree = "<group>"; };
|
||||
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; };
|
||||
3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
|
||||
@ -1492,6 +1503,7 @@
|
||||
97CE98208321C4D66E363612 /* ShimmerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShimmerModifier.swift; sourceTree = "<group>"; };
|
||||
981663D961C94270FA035FD0 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
||||
9873076F224E4CE09D8BD47D /* TemplateScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenUITests.swift; sourceTree = "<group>"; };
|
||||
989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
|
||||
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
|
||||
@ -1510,6 +1522,7 @@
|
||||
9F3450F4C32D73532DBBC1A2 /* OnboardingScreenBackgroundImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingScreenBackgroundImage.swift; sourceTree = "<group>"; };
|
||||
9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineController.swift; sourceTree = "<group>"; };
|
||||
A00C7A331B72C0F05C00392F /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
A05707BF550D770168A406DB /* LoginViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelTests.swift; sourceTree = "<group>"; };
|
||||
A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = "<group>"; };
|
||||
A0A01AECFF54281CF35909A6 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
|
||||
@ -1565,6 +1578,7 @@
|
||||
AE40D4A5DD857AC16EED945A /* URLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSession.swift; sourceTree = "<group>"; };
|
||||
AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancellableTask.swift; sourceTree = "<group>"; };
|
||||
AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
AEDC38E389B89BCF5C1AFD4A /* AppLockSetupPINScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenUITests.swift; sourceTree = "<group>"; };
|
||||
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
AF042B0FB2EE88977C91E330 /* portrait_test_image.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = portrait_test_image.jpg; sourceTree = "<group>"; };
|
||||
AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptUITests.swift; sourceTree = "<group>"; };
|
||||
@ -1581,6 +1595,7 @@
|
||||
B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationDialog.swift; sourceTree = "<group>"; };
|
||||
B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = "<group>"; };
|
||||
B4005D82E9D27BAF006A8FE1 /* AppLockScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenModels.swift; sourceTree = "<group>"; };
|
||||
B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
B48B7AD4908C5C374517B892 /* MapAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapAssets.xcassets; sourceTree = "<group>"; };
|
||||
B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Analytics+SwiftUI.swift"; sourceTree = "<group>"; };
|
||||
@ -1593,6 +1608,7 @@
|
||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
||||
B70A50C41C5871B4DB905E7E /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsCustomSectionView.swift; sourceTree = "<group>"; };
|
||||
B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
B7AE92E7BFF71797BDE1D261 /* MapTilerStyleBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyleBuilder.swift; sourceTree = "<group>"; };
|
||||
B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsSignalling.swift; sourceTree = "<group>"; };
|
||||
B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
@ -1706,6 +1722,7 @@
|
||||
D38391154120264910D19528 /* PollMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollMock.swift; sourceTree = "<group>"; };
|
||||
D39D7F513A36C9C1951DB44C /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = "<group>"; };
|
||||
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
||||
D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreen.swift; sourceTree = "<group>"; };
|
||||
D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSourceProxy.swift; sourceTree = "<group>"; };
|
||||
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
D529B976F8B2AA654D923422 /* VoiceMessageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
@ -1788,6 +1805,7 @@
|
||||
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
||||
EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = "<group>"; };
|
||||
EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEUserSession.swift; sourceTree = "<group>"; };
|
||||
EF13BFD415CA84B1272E94F8 /* PINTextFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PINTextFieldTests.swift; sourceTree = "<group>"; };
|
||||
EF1593DD87F974F8509BB619 /* ElementAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementAnimations.swift; sourceTree = "<group>"; };
|
||||
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
|
||||
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; };
|
||||
@ -2144,6 +2162,7 @@
|
||||
children = (
|
||||
3AD37D7DDF9904587601239D /* AppLockScreen */,
|
||||
CE39C9B97963CC30AB0859E5 /* AppLockSettingsScreen */,
|
||||
570026F1BA71A2D167652E48 /* AppLockSetupPINScreen */,
|
||||
);
|
||||
path = AppLock;
|
||||
sourceTree = "<group>";
|
||||
@ -2813,6 +2832,18 @@
|
||||
path = Other;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
570026F1BA71A2D167652E48 /* AppLockSetupPINScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */,
|
||||
B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */,
|
||||
A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */,
|
||||
B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */,
|
||||
AC24CD8D1E51188678C6AE07 /* View */,
|
||||
);
|
||||
path = AppLockSetupPINScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
593C7129C5927E25AD8B688F /* FlowCoordinators */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3536,6 +3567,7 @@
|
||||
7D0CBC76C80E04345E11F2DB /* Application.swift */,
|
||||
349C633291427A0F29C28C54 /* AppLockScreenUITests.swift */,
|
||||
5F088B61525099A48909743B /* AppLockSettingsScreenUITests.swift */,
|
||||
AEDC38E389B89BCF5C1AFD4A /* AppLockSetupPINScreenUITests.swift */,
|
||||
5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */,
|
||||
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */,
|
||||
1D8866FE1CCCF10305FCACBC /* CallScreenUITests.swift */,
|
||||
@ -3607,7 +3639,9 @@
|
||||
AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */,
|
||||
DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */,
|
||||
B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */,
|
||||
989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */,
|
||||
4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */,
|
||||
EF13BFD415CA84B1272E94F8 /* PINTextFieldTests.swift */,
|
||||
);
|
||||
path = AppLock;
|
||||
sourceTree = "<group>";
|
||||
@ -3791,6 +3825,15 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AC24CD8D1E51188678C6AE07 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */,
|
||||
3D1D4A6D451F43A03CACD01D /* PINTextField.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
ACF39CFC617601C942702CDC /* Media */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4949,6 +4992,7 @@
|
||||
8478992479B296C45150208F /* AppLockScreenViewModelTests.swift in Sources */,
|
||||
77693820498ABF3508814D49 /* AppLockServiceTests.swift in Sources */,
|
||||
73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */,
|
||||
25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */,
|
||||
0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */,
|
||||
EA78A7512AFB1E5451744EB1 /* AppRouteURLParserTests.swift in Sources */,
|
||||
3EC698F80DDEEFA273857841 /* ArrayTests.swift in Sources */,
|
||||
@ -5001,6 +5045,7 @@
|
||||
C11939FDC40716C4387275A4 /* NotificationSettingsEditScreenViewModelTests.swift in Sources */,
|
||||
E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */,
|
||||
0C26A1588B17DCDE5F490FE3 /* OnboardingScreenViewModelTests.swift in Sources */,
|
||||
50381244BA280451771BE3ED /* PINTextFieldTests.swift in Sources */,
|
||||
27E9263DA75E266690A37EB1 /* PermalinkBuilderTests.swift in Sources */,
|
||||
3982E60F9C126437D5E488A3 /* PillContextTests.swift in Sources */,
|
||||
D415764645491F10344FC6AC /* Publisher.swift in Sources */,
|
||||
@ -5104,6 +5149,11 @@
|
||||
0AD81E04A8C024C09B7AEAC5 /* AppLockSettingsScreenModels.swift in Sources */,
|
||||
A7BEE8216B4B12BE4C0F2C3F /* AppLockSettingsScreenViewModel.swift in Sources */,
|
||||
0206016CCEF6EF9365916768 /* AppLockSettingsScreenViewModelProtocol.swift in Sources */,
|
||||
733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */,
|
||||
9696ECAFB4F0C079C5C2A526 /* AppLockSetupPINScreenCoordinator.swift in Sources */,
|
||||
E4B07FF075C99D04D9AF792D /* AppLockSetupPINScreenModels.swift in Sources */,
|
||||
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */,
|
||||
4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */,
|
||||
EF890DEF0479E66548F2BA23 /* AppLockTimer.swift in Sources */,
|
||||
355B11D08CE0CEF97A813236 /* AppRoutes.swift in Sources */,
|
||||
12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */,
|
||||
@ -5403,6 +5453,7 @@
|
||||
4714991754A08B58B4D7ED85 /* OnboardingScreenViewModelProtocol.swift in Sources */,
|
||||
804C15D8ADE0EA7A5268F58A /* OverridableAvatarImage.swift in Sources */,
|
||||
CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */,
|
||||
847DE3A7EB9FCA2C429C6E85 /* PINTextField.swift in Sources */,
|
||||
7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */,
|
||||
764AFCC225B044CF5F9B41E5 /* PaginationIndicatorRoomTimelineView.swift in Sources */,
|
||||
80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */,
|
||||
@ -5707,6 +5758,7 @@
|
||||
BF675964C9159F718589C36A /* AnalyticsSettingsScreenUITests.swift in Sources */,
|
||||
F05516474DB42369FD976CEF /* AppLockScreenUITests.swift in Sources */,
|
||||
61C345258DD392477E79A3B5 /* AppLockSettingsScreenUITests.swift in Sources */,
|
||||
000765812BABB81F5174C601 /* AppLockSetupPINScreenUITests.swift in Sources */,
|
||||
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */,
|
||||
ACF094CF3BF02DBFA6DFDE60 /* AuthenticationCoordinatorUITests.swift in Sources */,
|
||||
7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */,
|
||||
|
@ -77,6 +77,7 @@
|
||||
"action_start_verification" = "Start verification";
|
||||
"action_static_map_load" = "Tap to load map";
|
||||
"action_take_photo" = "Take photo";
|
||||
"action_try_again" = "Try again";
|
||||
"action_view_source" = "View source";
|
||||
"action_yes" = "Yes";
|
||||
"action.edit_poll" = "Edit poll";
|
||||
@ -273,6 +274,13 @@
|
||||
"screen_app_lock_settings_remove_pin" = "Remove PIN";
|
||||
"screen_app_lock_settings_remove_pin_alert_message" = "Are you sure you want to remove PIN?";
|
||||
"screen_app_lock_settings_remove_pin_alert_title" = "Remove PIN?";
|
||||
"screen_app_lock_setup_choose_pin" = "Choose PIN";
|
||||
"screen_app_lock_setup_confirm_pin" = "Confirm PIN";
|
||||
"screen_app_lock_setup_pin_blacklisted_dialog_content" = "You cannot choose this as your PIN code for security reasons";
|
||||
"screen_app_lock_setup_pin_blacklisted_dialog_title" = "Choose a different PIN";
|
||||
"screen_app_lock_setup_pin_context" = "Lock %1$@ to add extra security to your chats.\n\nChoose something memorable. If you forget this PIN, you will be logged out of the app.";
|
||||
"screen_app_lock_setup_pin_mismatch_dialog_content" = "Please enter the same PIN twice";
|
||||
"screen_app_lock_setup_pin_mismatch_dialog_title" = "PINs don't match";
|
||||
"screen_app_lock_signout_alert_message" = "You’ll need to re-login and create a new PIN to proceed";
|
||||
"screen_app_lock_signout_alert_title" = "You are being signed out";
|
||||
"screen_app_lock_subtitle" = "You have 3 attempts to unlock";
|
||||
|
@ -174,6 +174,8 @@ public enum L10n {
|
||||
public static var actionStaticMapLoad: String { return L10n.tr("Localizable", "action_static_map_load") }
|
||||
/// Take photo
|
||||
public static var actionTakePhoto: String { return L10n.tr("Localizable", "action_take_photo") }
|
||||
/// Try again
|
||||
public static var actionTryAgain: String { return L10n.tr("Localizable", "action_try_again") }
|
||||
/// View source
|
||||
public static var actionViewSource: String { return L10n.tr("Localizable", "action_view_source") }
|
||||
/// Yes
|
||||
@ -664,6 +666,24 @@ public enum L10n {
|
||||
public static var screenAppLockSettingsRemovePinAlertMessage: String { return L10n.tr("Localizable", "screen_app_lock_settings_remove_pin_alert_message") }
|
||||
/// Remove PIN?
|
||||
public static var screenAppLockSettingsRemovePinAlertTitle: String { return L10n.tr("Localizable", "screen_app_lock_settings_remove_pin_alert_title") }
|
||||
/// Choose PIN
|
||||
public static var screenAppLockSetupChoosePin: String { return L10n.tr("Localizable", "screen_app_lock_setup_choose_pin") }
|
||||
/// Confirm PIN
|
||||
public static var screenAppLockSetupConfirmPin: String { return L10n.tr("Localizable", "screen_app_lock_setup_confirm_pin") }
|
||||
/// You cannot choose this as your PIN code for security reasons
|
||||
public static var screenAppLockSetupPinBlacklistedDialogContent: String { return L10n.tr("Localizable", "screen_app_lock_setup_pin_blacklisted_dialog_content") }
|
||||
/// Choose a different PIN
|
||||
public static var screenAppLockSetupPinBlacklistedDialogTitle: String { return L10n.tr("Localizable", "screen_app_lock_setup_pin_blacklisted_dialog_title") }
|
||||
/// Lock %1$@ to add extra security to your chats.
|
||||
///
|
||||
/// Choose something memorable. If you forget this PIN, you will be logged out of the app.
|
||||
public static func screenAppLockSetupPinContext(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_app_lock_setup_pin_context", String(describing: p1))
|
||||
}
|
||||
/// Please enter the same PIN twice
|
||||
public static var screenAppLockSetupPinMismatchDialogContent: String { return L10n.tr("Localizable", "screen_app_lock_setup_pin_mismatch_dialog_content") }
|
||||
/// PINs don't match
|
||||
public static var screenAppLockSetupPinMismatchDialogTitle: String { return L10n.tr("Localizable", "screen_app_lock_setup_pin_mismatch_dialog_title") }
|
||||
/// You’ll need to re-login and create a new PIN to proceed
|
||||
public static var screenAppLockSignoutAlertMessage: String { return L10n.tr("Localizable", "screen_app_lock_signout_alert_message") }
|
||||
/// You are being signed out
|
||||
|
@ -154,6 +154,27 @@ class AppLockServiceMock: AppLockServiceProtocol {
|
||||
return setupPINCodeReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - validate
|
||||
|
||||
var validateCallsCount = 0
|
||||
var validateCalled: Bool {
|
||||
return validateCallsCount > 0
|
||||
}
|
||||
var validateReceivedPinCode: String?
|
||||
var validateReceivedInvocations: [String] = []
|
||||
var validateReturnValue: Result<Void, AppLockServiceError>!
|
||||
var validateClosure: ((String) -> Result<Void, AppLockServiceError>)?
|
||||
|
||||
func validate(_ pinCode: String) -> Result<Void, AppLockServiceError> {
|
||||
validateCallsCount += 1
|
||||
validateReceivedPinCode = pinCode
|
||||
validateReceivedInvocations.append(pinCode)
|
||||
if let validateClosure = validateClosure {
|
||||
return validateClosure(pinCode)
|
||||
} else {
|
||||
return validateReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - disable
|
||||
|
||||
var disableCallsCount = 0
|
||||
|
@ -60,6 +60,7 @@ struct AppLockScreen: View {
|
||||
}
|
||||
.font(.compound.bodyMDSemibold)
|
||||
}
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.alert(item: $context.alertInfo)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import SwiftUI
|
||||
|
||||
struct AppLockSetupPINScreenCoordinatorParameters {
|
||||
/// Whether the screen should start in create or unlock mode.
|
||||
/// Specifying confirm here will raise a fatal error.
|
||||
let initialMode: AppLockSetupPINScreenMode
|
||||
let appLockService: AppLockServiceProtocol
|
||||
}
|
||||
|
||||
enum AppLockSetupPINScreenCoordinatorAction {
|
||||
/// The user cancelled PIN entry.
|
||||
case cancel
|
||||
/// The user succeeded PIN entry.
|
||||
case complete
|
||||
}
|
||||
|
||||
final class AppLockSetupPINScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: AppLockSetupPINScreenCoordinatorParameters
|
||||
private var viewModel: AppLockSetupPINScreenViewModelProtocol
|
||||
private let actionsSubject: PassthroughSubject<AppLockSetupPINScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var actions: AnyPublisher<AppLockSetupPINScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: AppLockSetupPINScreenCoordinatorParameters) {
|
||||
guard parameters.initialMode != .confirm else { fatalError(".confirm is an invalid initial mode") }
|
||||
|
||||
self.parameters = parameters
|
||||
viewModel = AppLockSetupPINScreenViewModel(initialMode: parameters.initialMode,
|
||||
appLockService: parameters.appLockService)
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.actions.sink { [weak self] action in
|
||||
MXLog.info("Coordinator: received view model action: \(action)")
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .complete:
|
||||
break
|
||||
case .cancel:
|
||||
break
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(AppLockSetupPINScreen(context: viewModel.context))
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
enum AppLockSetupPINScreenViewModelAction {
|
||||
/// The user succeeded PIN entry.
|
||||
case complete
|
||||
/// The user cancelled PIN entry.
|
||||
case cancel
|
||||
}
|
||||
|
||||
enum AppLockSetupPINScreenMode {
|
||||
/// Creating a new PIN.
|
||||
case create
|
||||
/// Confirming the new PIN.
|
||||
case confirm
|
||||
/// Unlocking with the current PIN.
|
||||
case unlock
|
||||
}
|
||||
|
||||
struct AppLockSetupPINScreenViewState: BindableState {
|
||||
/// The current mode that the screen is in.
|
||||
var mode: AppLockSetupPINScreenMode
|
||||
|
||||
var title: String {
|
||||
switch mode {
|
||||
case .create: return L10n.screenAppLockSetupChoosePin
|
||||
case .confirm: return L10n.screenAppLockSetupConfirmPin
|
||||
case .unlock: return L10n.commonEnterYourPin
|
||||
}
|
||||
}
|
||||
|
||||
var subtitle: String? {
|
||||
switch mode {
|
||||
case .create, .confirm: return L10n.screenAppLockSetupPinContext(InfoPlistReader.main.bundleDisplayName)
|
||||
case .unlock: return nil
|
||||
}
|
||||
}
|
||||
|
||||
var bindings: AppLockSetupPINScreenViewStateBindings
|
||||
}
|
||||
|
||||
struct AppLockSetupPINScreenViewStateBindings {
|
||||
var pinCode: String
|
||||
var alertInfo: AlertInfo<AppLockSetupPINScreenAlertType>?
|
||||
}
|
||||
|
||||
enum AppLockSetupPINScreenAlertType {
|
||||
/// The user entered a weak PIN and it has been rejected.
|
||||
case weakPIN
|
||||
/// The user entered the wrong PIN when confirming the creation.
|
||||
case pinMismatch
|
||||
/// An error occurred setting the PIN code in the App Lock service.
|
||||
case failedToSetPIN
|
||||
}
|
||||
|
||||
enum AppLockSetupPINScreenViewAction {
|
||||
/// Confirm the entered PIN.
|
||||
case submitPINCode
|
||||
/// Stop entering a PIN.
|
||||
case cancel
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import SwiftUI
|
||||
|
||||
typealias AppLockSetupPINScreenViewModelType = StateStoreViewModel<AppLockSetupPINScreenViewState, AppLockSetupPINScreenViewAction>
|
||||
|
||||
class AppLockSetupPINScreenViewModel: AppLockSetupPINScreenViewModelType, AppLockSetupPINScreenViewModelProtocol {
|
||||
private let appLockService: AppLockServiceProtocol
|
||||
private var actionsSubject: PassthroughSubject<AppLockSetupPINScreenViewModelAction, Never> = .init()
|
||||
|
||||
/// The PIN entered by the user in `.create` mode, used for confirmation.
|
||||
var newPIN: String?
|
||||
|
||||
var actions: AnyPublisher<AppLockSetupPINScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(initialMode: AppLockSetupPINScreenMode, appLockService: AppLockServiceProtocol) {
|
||||
self.appLockService = appLockService
|
||||
super.init(initialViewState: AppLockSetupPINScreenViewState(mode: initialMode, bindings: .init(pinCode: "")))
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: AppLockSetupPINScreenViewAction) {
|
||||
MXLog.info("View model: received view action: \(viewAction)")
|
||||
|
||||
switch viewAction {
|
||||
case .submitPINCode:
|
||||
submitPINCode()
|
||||
case .cancel:
|
||||
actionsSubject.send(.cancel)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Handle the entered PIN code.
|
||||
private func submitPINCode() {
|
||||
switch state.mode {
|
||||
case .create:
|
||||
createPIN()
|
||||
case .confirm:
|
||||
confirmPIN()
|
||||
case .unlock:
|
||||
unlock()
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a PIN input from the create mode. Transitions to confirmation if valid.
|
||||
private func createPIN() {
|
||||
let pinCode = state.bindings.pinCode
|
||||
if case let .failure(error) = appLockService.validate(pinCode) {
|
||||
MXLog.warning("PIN rejected: \(error)")
|
||||
handleError(.weakPIN)
|
||||
return
|
||||
}
|
||||
|
||||
newPIN = pinCode
|
||||
state.mode = .confirm
|
||||
state.bindings.pinCode = ""
|
||||
}
|
||||
|
||||
/// Handles a PIN input from the confirm mode. Stores the pin if it matches.
|
||||
private func confirmPIN() {
|
||||
let pinCode = state.bindings.pinCode
|
||||
guard pinCode == newPIN else {
|
||||
MXLog.warning("PIN mismatch.")
|
||||
handleError(.pinMismatch)
|
||||
return
|
||||
}
|
||||
|
||||
if case let .failure(error) = appLockService.setupPINCode(pinCode) {
|
||||
MXLog.warning("Failed to set PIN: \(error)")
|
||||
if case .keychainError = error {
|
||||
handleError(.failedToSetPIN)
|
||||
return
|
||||
} else {
|
||||
handleError(.weakPIN) // Shouldn't really happen but just in case.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
actionsSubject.send(.complete)
|
||||
}
|
||||
|
||||
/// Handles a PIN input for the unlock mode.
|
||||
private func unlock() {
|
||||
guard appLockService.unlock(with: state.bindings.pinCode) else {
|
||||
// show an error
|
||||
// https://www.figma.com/file/0MMNu7cTOzLOlWb7ctTkv3?node-id=13067:107631&mode=dev#591068578
|
||||
state.bindings.pinCode = ""
|
||||
return
|
||||
}
|
||||
|
||||
actionsSubject.send(.complete)
|
||||
}
|
||||
|
||||
private func handleError(_ error: AppLockSetupPINScreenAlertType) {
|
||||
switch error {
|
||||
case .weakPIN:
|
||||
state.bindings.alertInfo = .init(id: error,
|
||||
title: L10n.screenAppLockSetupPinBlacklistedDialogTitle,
|
||||
message: L10n.screenAppLockSetupPinBlacklistedDialogContent,
|
||||
primaryButton: .init(title: L10n.actionOk) { self.state.bindings.pinCode = "" })
|
||||
case .pinMismatch:
|
||||
state.bindings.alertInfo = .init(id: error,
|
||||
title: L10n.screenAppLockSetupPinMismatchDialogTitle,
|
||||
message: L10n.screenAppLockSetupPinMismatchDialogContent,
|
||||
primaryButton: .init(title: L10n.actionTryAgain) { self.state.bindings.pinCode = "" })
|
||||
case .failedToSetPIN:
|
||||
state.bindings.alertInfo = .init(id: error)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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 Combine
|
||||
|
||||
@MainActor
|
||||
protocol AppLockSetupPINScreenViewModelProtocol {
|
||||
var actions: AnyPublisher<AppLockSetupPINScreenViewModelAction, Never> { get }
|
||||
var context: AppLockSetupPINScreenViewModelType.Context { get }
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
//
|
||||
// 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 Compound
|
||||
import SwiftUI
|
||||
|
||||
/// The screen shown to unlock the App Lock settings or to create a new PIN and enable the feature.
|
||||
struct AppLockSetupPINScreen: View {
|
||||
@ObservedObject var context: AppLockSetupPINScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 48) {
|
||||
header
|
||||
|
||||
PINTextField(pinCode: $context.pinCode,
|
||||
isSecure: context.viewState.mode == .unlock)
|
||||
.onChange(of: context.pinCode) { newValue in
|
||||
guard newValue.count == 4 else { return }
|
||||
context.send(viewAction: .submitPINCode)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, UIConstants.iconTopPaddingToNavigationBar)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
}
|
||||
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(image: Image(systemSymbol: .lock))
|
||||
.symbolVariant(.fill)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(context.viewState.title)
|
||||
.font(.compound.headingMDBold)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
|
||||
if let subtitle = context.viewState.subtitle {
|
||||
Text(subtitle)
|
||||
.font(.compound.bodyMD)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct AppLockSetupPINScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let service = AppLockService(keychainController: KeychainControllerMock(),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
static let createViewModel = AppLockSetupPINScreenViewModel(initialMode: .create, appLockService: service)
|
||||
static let confirmViewModel = AppLockSetupPINScreenViewModel(initialMode: .confirm, appLockService: service)
|
||||
static let unlockViewModel = AppLockSetupPINScreenViewModel(initialMode: .unlock, appLockService: service)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
AppLockSetupPINScreen(context: createViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Create")
|
||||
|
||||
NavigationStack {
|
||||
AppLockSetupPINScreen(context: confirmViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Confirm")
|
||||
|
||||
NavigationStack {
|
||||
AppLockSetupPINScreen(context: unlockViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Unlock")
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
//
|
||||
// Copyright 2023 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 SwiftUI
|
||||
|
||||
/// A text field that enables secure entry of a numerical PIN code.
|
||||
/// The view itself handles validation and the base text field type.
|
||||
struct PINTextField: View {
|
||||
@Binding var pinCode: String
|
||||
var isSecure = false
|
||||
|
||||
var body: some View {
|
||||
textField
|
||||
.textFieldStyle(PINTextFieldStyle(pinCode: pinCode, isSecure: isSecure))
|
||||
.keyboardType(.numberPad)
|
||||
.onChange(of: pinCode) { newValue in
|
||||
let sanitized = sanitize(newValue)
|
||||
if sanitized != newValue {
|
||||
MXLog.warning("PIN code input sanitized.")
|
||||
pinCode = sanitized
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var textField: some View {
|
||||
if isSecure {
|
||||
SecureField("", text: $pinCode)
|
||||
} else {
|
||||
TextField("", text: $pinCode)
|
||||
}
|
||||
}
|
||||
|
||||
func sanitize(_ pinCode: String) -> String {
|
||||
var sanitized = pinCode
|
||||
if sanitized.count > 4 { sanitized = String(pinCode.prefix(4)) }
|
||||
return sanitized.filter(\.isNumber)
|
||||
}
|
||||
}
|
||||
|
||||
/// A text field style for displaying individual digits of a PIN code.
|
||||
private struct PINTextFieldStyle: TextFieldStyle {
|
||||
@FocusState private var isFocussed
|
||||
|
||||
let pinCode: String
|
||||
let isSecure: Bool
|
||||
|
||||
public func _body(configuration: TextField<_Label>) -> some View {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(0..<4) { index in
|
||||
PINDigitField(digit: digit(index))
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
configuration
|
||||
.focused($isFocussed)
|
||||
.opacity(0.0)
|
||||
}
|
||||
.onTapGesture { isFocussed = true }
|
||||
}
|
||||
|
||||
func digit(_ index: Int) -> Character? {
|
||||
guard pinCode.count > index else { return nil }
|
||||
let stringIndex = pinCode.index(pinCode.startIndex, offsetBy: index)
|
||||
return isSecure ? "●" : pinCode[stringIndex]
|
||||
}
|
||||
}
|
||||
|
||||
/// A single digit shown within the text field style.
|
||||
private struct PINDigitField: View {
|
||||
let digit: Character?
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let digit {
|
||||
RoundedRectangle(cornerRadius: 8, style: .continuous)
|
||||
.fill(Color.compound.bgSubtlePrimary)
|
||||
Text(String(digit))
|
||||
.font(.compound.headingMDBold)
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: 8, style: .continuous)
|
||||
.inset(by: 0.5)
|
||||
.stroke(Color.compound.iconPrimary, lineWidth: 1)
|
||||
}
|
||||
}
|
||||
.frame(width: 48, height: 48)
|
||||
}
|
||||
}
|
||||
|
||||
struct PINTextField_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
PreviewWrapper(pinCode: "", isSecure: false)
|
||||
PreviewWrapper(pinCode: "12", isSecure: false)
|
||||
PreviewWrapper(pinCode: "1234", isSecure: false)
|
||||
.padding(.bottom)
|
||||
|
||||
PreviewWrapper(pinCode: "", isSecure: true)
|
||||
PreviewWrapper(pinCode: "12", isSecure: true)
|
||||
PreviewWrapper(pinCode: "1234", isSecure: true)
|
||||
}
|
||||
}
|
||||
|
||||
struct PreviewWrapper: View {
|
||||
@State var pinCode = ""
|
||||
let isSecure: Bool
|
||||
|
||||
var body: some View {
|
||||
PINTextField(pinCode: $pinCode, isSecure: isSecure)
|
||||
}
|
||||
}
|
||||
}
|
@ -47,8 +47,8 @@ class AppLockService: AppLockServiceProtocol {
|
||||
}
|
||||
|
||||
func setupPINCode(_ pinCode: String) -> Result<Void, AppLockServiceError> {
|
||||
guard validate(pinCode) else { return .failure(.invalidPIN) }
|
||||
guard !appSettings.appLockPINCodeBlockList.contains(pinCode) else { return .failure(.weakPIN) }
|
||||
let result = validate(pinCode)
|
||||
guard case .success = result else { return result }
|
||||
|
||||
do {
|
||||
try keychainController.setPINCode(pinCode)
|
||||
@ -59,6 +59,12 @@ class AppLockService: AppLockServiceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func validate(_ pinCode: String) -> Result<Void, AppLockServiceError> {
|
||||
guard pinCode.count == 4, pinCode.allSatisfy(\.isNumber) else { return .failure(.invalidPIN) }
|
||||
guard !appSettings.appLockPINCodeBlockList.contains(pinCode) else { return .failure(.weakPIN) }
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func disable() {
|
||||
biometricUnlockEnabled = false
|
||||
keychainController.removePINCode()
|
||||
@ -94,11 +100,6 @@ class AppLockService: AppLockServiceProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that a provided PIN code is long enough and only contains digits.
|
||||
private func validate(_ pinCode: String) -> Bool {
|
||||
pinCode.count == 4 && pinCode.allSatisfy(\.isNumber)
|
||||
}
|
||||
|
||||
/// Shared logic for completing an unlock via a PIN or biometry.
|
||||
private func completeUnlock() -> Bool {
|
||||
timer.registerUnlock()
|
||||
|
@ -36,6 +36,8 @@ protocol AppLockServiceProtocol: AnyObject {
|
||||
|
||||
/// Sets the user's PIN code used to unlock the app.
|
||||
func setupPINCode(_ pinCode: String) -> Result<Void, AppLockServiceError>
|
||||
/// Validates the supplied PIN code is long enough, only contains digits and isn't a weak choice.
|
||||
func validate(_ pinCode: String) -> Result<Void, AppLockServiceError>
|
||||
/// Disables the App Lock feature, removing the user's stored PIN code.
|
||||
func disable()
|
||||
|
||||
|
@ -161,6 +161,13 @@ class MockScreen: Identifiable {
|
||||
let appLockService = AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings)
|
||||
let coordinator = AppLockScreenCoordinator(parameters: .init(appLockService: appLockService))
|
||||
return coordinator
|
||||
case .appLockSetupFlow:
|
||||
// Use the flow coordinator once more screens are added and remove the settings screen below.
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let appLockService = AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings)
|
||||
let coordinator = AppLockSetupPINScreenCoordinator(parameters: .init(initialMode: .create, appLockService: appLockService))
|
||||
navigationStackCoordinator.setRootCoordinator(coordinator)
|
||||
return navigationStackCoordinator
|
||||
case .appLockSettingsScreen:
|
||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||
let appLockService = AppLockServiceMock.mock(biometryType: .faceID)
|
||||
|
@ -30,6 +30,7 @@ enum UITestsScreenIdentifier: String {
|
||||
case migration
|
||||
case templateScreen
|
||||
case appLockScreen
|
||||
case appLockSetupFlow
|
||||
case appLockSettingsScreen
|
||||
case home
|
||||
case settings
|
||||
|
26
UITests/Sources/AppLockSetupPINScreenUITests.swift
Normal file
26
UITests/Sources/AppLockSetupPINScreenUITests.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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 ElementX
|
||||
import XCTest
|
||||
|
||||
@MainActor
|
||||
class AppLockSetupUITests: XCTestCase {
|
||||
func testScreen() async throws {
|
||||
let app = Application.launch(.appLockSetupFlow)
|
||||
try await app.assertScreenshot(.appLockSetupFlow, step: 0)
|
||||
}
|
||||
}
|
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.appLockSetupFlow-0.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,96 @@
|
||||
//
|
||||
// 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 XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
class AppLockSetupPINScreenViewModelTests: XCTestCase {
|
||||
var appLockService: AppLockService!
|
||||
var keychainController: KeychainControllerMock!
|
||||
var viewModel: AppLockSetupPINScreenViewModelProtocol!
|
||||
|
||||
var context: AppLockSetupPINScreenViewModelType.Context { viewModel.context }
|
||||
|
||||
override func setUp() {
|
||||
AppSettings.reset()
|
||||
keychainController = KeychainControllerMock()
|
||||
appLockService = AppLockService(keychainController: keychainController, appSettings: AppSettings())
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
AppSettings.reset()
|
||||
}
|
||||
|
||||
func testCreatePIN() async throws {
|
||||
viewModel = AppLockSetupPINScreenViewModel(initialMode: .create, appLockService: appLockService)
|
||||
XCTAssertEqual(context.viewState.mode, .create, "The mode should start as creation.")
|
||||
|
||||
let createDeferred = deferFulfillment(context.$viewState, message: "A valid PIN needs confirming.") { $0.mode == .confirm }
|
||||
context.pinCode = "2023"
|
||||
context.send(viewAction: .submitPINCode)
|
||||
try await createDeferred.fulfill()
|
||||
XCTAssertEqual(context.viewState.mode, .confirm, "The mode should transition to confirmation.")
|
||||
|
||||
let confirmDeferred = deferFulfillment(viewModel.actions, message: "The screen should be finished.") { $0 == .complete }
|
||||
context.pinCode = "2023"
|
||||
context.send(viewAction: .submitPINCode)
|
||||
try await confirmDeferred.fulfill()
|
||||
}
|
||||
|
||||
func testCreateWeakPIN() async throws {
|
||||
viewModel = AppLockSetupPINScreenViewModel(initialMode: .create, appLockService: appLockService)
|
||||
XCTAssertEqual(context.viewState.mode, .create, "The mode should start as creation.")
|
||||
XCTAssertNil(context.alertInfo, "There shouldn't be an alert to begin with.")
|
||||
|
||||
context.pinCode = "0000"
|
||||
context.send(viewAction: .submitPINCode)
|
||||
|
||||
XCTAssertEqual(context.alertInfo?.id, .weakPIN, "The weak PIN should be rejected.")
|
||||
XCTAssertEqual(context.viewState.mode, .create, "The mode shouldn't transition after an invalid PIN code.")
|
||||
}
|
||||
|
||||
func testCreatePINMismatch() async throws {
|
||||
viewModel = AppLockSetupPINScreenViewModel(initialMode: .create, appLockService: appLockService)
|
||||
XCTAssertEqual(context.viewState.mode, .create, "The mode should start as creation.")
|
||||
XCTAssertNil(context.alertInfo, "There shouldn't be an alert to begin with.")
|
||||
|
||||
let createDeferred = deferFulfillment(context.$viewState, message: "A valid PIN needs confirming.") { $0.mode == .confirm }
|
||||
context.pinCode = "2023"
|
||||
context.send(viewAction: .submitPINCode)
|
||||
try await createDeferred.fulfill()
|
||||
XCTAssertEqual(context.viewState.mode, .confirm, "The mode should transition to confirmation.")
|
||||
XCTAssertNil(context.alertInfo, "There shouldn't be an alert after a valid initial PIN.")
|
||||
|
||||
context.pinCode = "2024"
|
||||
context.send(viewAction: .submitPINCode)
|
||||
|
||||
XCTAssertEqual(context.alertInfo?.id, .pinMismatch, "A PIN mismatch should be rejected.")
|
||||
}
|
||||
|
||||
func testUnlock() async throws {
|
||||
viewModel = AppLockSetupPINScreenViewModel(initialMode: .unlock, appLockService: appLockService)
|
||||
let pinCode = "2023"
|
||||
keychainController.pinCodeReturnValue = pinCode
|
||||
keychainController.containsPINCodeReturnValue = true
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions, message: "The screen should be finished.") { $0 == .complete }
|
||||
context.pinCode = pinCode
|
||||
context.send(viewAction: .submitPINCode)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
}
|
31
UnitTests/Sources/AppLock/PINTextFieldTests.swift
Normal file
31
UnitTests/Sources/AppLock/PINTextFieldTests.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright 2023 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 XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
class PINTextFieldTests: XCTestCase {
|
||||
func testSanitize() {
|
||||
let textField = PINTextField(pinCode: .constant(""))
|
||||
XCTAssertEqual(textField.sanitize("2"), "2")
|
||||
XCTAssertEqual(textField.sanitize("2023"), "2023")
|
||||
XCTAssertEqual(textField.sanitize("20233"), "2023")
|
||||
XCTAssertEqual(textField.sanitize("20x"), "20")
|
||||
XCTAssertEqual(textField.sanitize("20!"), "20")
|
||||
XCTAssertEqual(textField.sanitize("boop"), "")
|
||||
}
|
||||
}
|
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Confirm.png
(Stored with Git LFS)
Normal file
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Confirm.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Create.png
(Stored with Git LFS)
Normal file
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Create.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Unlock.png
(Stored with Git LFS)
Normal file
BIN
UnitTests/__Snapshots__/PreviewTests/test_appLockSetupPINScreen.Unlock.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
UnitTests/__Snapshots__/PreviewTests/test_pINTextField.1.png
(Stored with Git LFS)
Normal file
BIN
UnitTests/__Snapshots__/PreviewTests/test_pINTextField.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
1
changelog.d/pr-1930.wip
Normal file
1
changelog.d/pr-1930.wip
Normal file
@ -0,0 +1 @@
|
||||
Add PIN entry screen for creating/accessing PIN settings.
|
Loading…
x
Reference in New Issue
Block a user