mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Authentication Layouts (#994)
* Remove unused code in OnboardingScreen. * Add FullscreenDialog layout and use in auth flow. Bump minimum OS to 16.4/13.3. * Add dynamic layout based on dynamic type size.
This commit is contained in:
parent
f792aafe8d
commit
dcc4ea5e85
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 51;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -50,7 +50,6 @@
|
||||
12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
|
||||
13853973A5E24374FCEDE8A3 /* RedactedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */; };
|
||||
13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */; };
|
||||
14132418A748C988B85B025E /* OnboardingPageIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */; };
|
||||
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */; };
|
||||
14E99D27628B1A6F0CB46FEA /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */; };
|
||||
152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; };
|
||||
@ -193,6 +192,7 @@
|
||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874A1842477895F199567BD7 /* TimelineView.swift */; };
|
||||
501304F26B52DF7024011B6C /* EmojiMartJSONLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF9E3E6A23180EC05F06460 /* EmojiMartJSONLoaderTests.swift */; };
|
||||
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; };
|
||||
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
|
||||
520EEDAFBC778AB0B41F2F53 /* ClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE6170EFE6A161B0A68AB61 /* ClientMock.swift */; };
|
||||
5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; };
|
||||
@ -358,6 +358,7 @@
|
||||
90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */; };
|
||||
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
||||
913134A9302FD13139372A2F /* AnalyticsSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93B3513E60591237A49EE102 /* AnalyticsSettingsScreenCoordinator.swift */; };
|
||||
915241C42A274FA80087BF83 /* FullscreenDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 915241C32A274FA80087BF83 /* FullscreenDialog.swift */; };
|
||||
9219640F4D980CFC5FE855AD /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 536E72DCBEEC4A1FE66CFDCE /* target.yml */; };
|
||||
92B95779840CD749117B3615 /* EmojiMartStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */; };
|
||||
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */; };
|
||||
@ -616,7 +617,6 @@
|
||||
F16109A6F6DF03DA26D59233 /* RoomDetailsEditScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 122186B7CD1BC46A9C629DD9 /* RoomDetailsEditScreenUITests.swift */; };
|
||||
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.swift */; };
|
||||
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
||||
F257F964493A9CD02A6F720C /* OnboardingPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF2717AB91060260E5F4781 /* OnboardingPageView.swift */; };
|
||||
F32B271F60531BE92C6E62A1 /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 612EF972F2A1800682D32C5E /* StickerRoomTimelineView.swift */; };
|
||||
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; };
|
||||
F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; };
|
||||
@ -722,7 +722,6 @@
|
||||
07E65E613F057697A1A0BC03 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = "<group>"; };
|
||||
086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = "<group>"; };
|
||||
086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentViewModelTests.swift; sourceTree = "<group>"; };
|
||||
09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageIndicator.swift; sourceTree = "<group>"; };
|
||||
095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementToggleStyle.swift; sourceTree = "<group>"; };
|
||||
099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = "<group>"; };
|
||||
@ -750,7 +749,7 @@
|
||||
1222DB76B917EB8A55365BA5 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
127A57D053CE8C87B5EFB089 /* Consumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumable.swift; sourceTree = "<group>"; };
|
||||
127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = "<group>"; };
|
||||
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
||||
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
||||
130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = "<group>"; };
|
||||
13673F95EBA78D40C09CCE35 /* MockUserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserIndicatorController.swift; sourceTree = "<group>"; };
|
||||
13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
@ -778,7 +777,6 @@
|
||||
1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreen.swift; sourceTree = "<group>"; };
|
||||
1D56469A9EE0CFA2B7BA9760 /* SessionVerificationControllerProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationControllerProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenUITests.swift; sourceTree = "<group>"; };
|
||||
1DF2717AB91060260E5F4781 /* OnboardingPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageView.swift; sourceTree = "<group>"; };
|
||||
1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@ -830,6 +828,7 @@
|
||||
35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = "<group>"; };
|
||||
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = "<group>"; };
|
||||
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
||||
37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringTests.swift; sourceTree = "<group>"; };
|
||||
39001365B76B89983FDB7AD8 /* EmojiMartJSONLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartJSONLoader.swift; sourceTree = "<group>"; };
|
||||
3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBackgroundTaskService.swift; sourceTree = "<group>"; };
|
||||
398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = "<group>"; };
|
||||
@ -865,7 +864,7 @@
|
||||
46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsAppCoordinator.swift; sourceTree = "<group>"; };
|
||||
47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DesignKit; path = DesignKit; sourceTree = SOURCE_ROOT; };
|
||||
478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DesignKit; sourceTree = SOURCE_ROOT; };
|
||||
4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramePreferenceKey.swift; sourceTree = "<group>"; };
|
||||
47E6DD75A81D07CD91997D8C /* SettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = "<group>"; };
|
||||
@ -1012,7 +1011,7 @@
|
||||
8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineProvider.swift; sourceTree = "<group>"; };
|
||||
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||
8E1BBA73B611EDEEA6E20E05 /* InvitesScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenModels.swift; sourceTree = "<group>"; };
|
||||
8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUITests.swift; sourceTree = "<group>"; };
|
||||
8F61A0DD8243B395499C99A2 /* InvitesScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenUITests.swift; sourceTree = "<group>"; };
|
||||
@ -1020,6 +1019,7 @@
|
||||
8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
||||
8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = "<group>"; };
|
||||
90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
915241C32A274FA80087BF83 /* FullscreenDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenDialog.swift; sourceTree = "<group>"; };
|
||||
923485F85E1D765EF9D20E88 /* UserProfileCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileCell.swift; sourceTree = "<group>"; };
|
||||
92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthenticationPresenter.swift; sourceTree = "<group>"; };
|
||||
92B45A6B13D32A131FCA4EFF /* FilePreviewScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -1110,7 +1110,7 @@
|
||||
B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemContextMenu.swift; sourceTree = "<group>"; };
|
||||
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = "<group>"; };
|
||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
||||
B7DBA101D643B31E813F3AC1 /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = "<group>"; };
|
||||
@ -1177,7 +1177,7 @@
|
||||
CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = "<group>"; };
|
||||
CECF45B5E8E795666B8C5013 /* SettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenModels.swift; sourceTree = "<group>"; };
|
||||
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxy.swift; sourceTree = "<group>"; };
|
||||
D06A27D9C70E0DCC1E199163 /* OnboardingBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundView.swift; sourceTree = "<group>"; };
|
||||
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
||||
@ -1243,7 +1243,7 @@
|
||||
ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
||||
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = "<group>"; };
|
||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = "<group>"; };
|
||||
ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@ -2218,6 +2218,7 @@
|
||||
C687844F60BFF532D49A994C /* AnalyticsTests.swift */,
|
||||
893777A4997BBDB68079D4F5 /* ArrayTests.swift */,
|
||||
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */,
|
||||
37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */,
|
||||
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */,
|
||||
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */,
|
||||
7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */,
|
||||
@ -2372,8 +2373,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D06A27D9C70E0DCC1E199163 /* OnboardingBackgroundView.swift */,
|
||||
09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */,
|
||||
1DF2717AB91060260E5F4781 /* OnboardingPageView.swift */,
|
||||
AB8E75B9CB6C78BE8D09B1AF /* OnboardingScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
@ -2930,6 +2929,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */,
|
||||
915241C32A274FA80087BF83 /* FullscreenDialog.swift */,
|
||||
4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */,
|
||||
398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */,
|
||||
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */,
|
||||
@ -3634,6 +3634,7 @@
|
||||
890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */,
|
||||
3EC698F80DDEEFA273857841 /* ArrayTests.swift in Sources */,
|
||||
90DF83A6A347F7EE7EDE89EE /* AttributedStringBuilderTests.swift in Sources */,
|
||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */,
|
||||
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */,
|
||||
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */,
|
||||
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */,
|
||||
@ -3858,6 +3859,7 @@
|
||||
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */,
|
||||
B796A25F282C0A340D1B9C12 /* ImageRoomTimelineItemContent.swift in Sources */,
|
||||
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */,
|
||||
915241C42A274FA80087BF83 /* FullscreenDialog.swift in Sources */,
|
||||
B6048166B4AA4CEFEA9B77A6 /* InfoPlistReader.swift in Sources */,
|
||||
C4D2BCAA54E2C62B94B24AF4 /* InviteUsersScreen.swift in Sources */,
|
||||
E27C4D1A1F8BB77CA790B403 /* InviteUsersScreenCoordinator.swift in Sources */,
|
||||
@ -3931,8 +3933,6 @@
|
||||
48FE5F0E3921146DBF4E61E7 /* OnboardingBackgroundView.swift in Sources */,
|
||||
2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */,
|
||||
5D7960B32C350FA93F48D02B /* OnboardingModels.swift in Sources */,
|
||||
14132418A748C988B85B025E /* OnboardingPageIndicator.swift in Sources */,
|
||||
F257F964493A9CD02A6F720C /* OnboardingPageView.swift in Sources */,
|
||||
7F64FA937B95924B3A44EC12 /* OnboardingScreen.swift in Sources */,
|
||||
CE7148E80F09B7305E026AC6 /* OnboardingViewModel.swift in Sources */,
|
||||
992477AB8E3F3C36D627D32E /* OnboardingViewModelProtocol.swift in Sources */,
|
||||
@ -4443,9 +4443,9 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
||||
MARKETING_VERSION = 1.1.2;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
@ -4517,9 +4517,9 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)";
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.3;
|
||||
MARKETING_VERSION = 1.1.2;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
|
@ -49,4 +49,18 @@ extension AttributedString {
|
||||
replacement.link = url
|
||||
replaceSubrange(range, with: replacement)
|
||||
}
|
||||
|
||||
/// Returns a new attributed string, created by replacing any hard coded `UIFont` with
|
||||
/// a simple presentation intent. This allows simple formatting to respond to Dynamic Type.
|
||||
///
|
||||
/// Currently only supports regular and bold weights.
|
||||
func replacingFontWithPresentationIntent() -> AttributedString {
|
||||
var newValue = self
|
||||
for run in newValue.runs {
|
||||
guard let font = run.uiKit.font else { continue }
|
||||
newValue[run.range].inlinePresentationIntent = font.fontDescriptor.symbolicTraits.contains(.traitBold) ? .stronglyEmphasized : nil
|
||||
newValue[run.range].uiKit.font = nil
|
||||
}
|
||||
return newValue
|
||||
}
|
||||
}
|
||||
|
161
ElementX/Sources/Other/SwiftUI/Layout/FullscreenDialog.swift
Normal file
161
ElementX/Sources/Other/SwiftUI/Layout/FullscreenDialog.swift
Normal file
@ -0,0 +1,161 @@
|
||||
//
|
||||
// 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 layout that separates the main content of a screen from the buttons shown at
|
||||
/// the bottom for a dialogs that fill the entire screen. On larger devices (iPad/Mac),
|
||||
/// the height is constrained to keep the content relatively close to the buttons. If
|
||||
/// the content overflows the space available, it will become scrollable.
|
||||
///
|
||||
/// The background color behind the buttons is read from the `backgroundStyle`
|
||||
/// environment value, so make sure to set this to match the screen's background.
|
||||
struct FullscreenDialog<Content: View, BottomContent: View>: View {
|
||||
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
|
||||
|
||||
/// Padding applied to the top of the content automatically. Use `UIConstants` for preset values.
|
||||
var topPadding: CGFloat = UIConstants.titleTopPaddingToNavigationBar
|
||||
/// Padding applied to the content and buttons automatically
|
||||
var horizontalPadding: CGFloat = 16
|
||||
/// The spacing between the content and the buttons.
|
||||
var spacing: CGFloat = 16
|
||||
|
||||
/// The main content shown at the top of the layout.
|
||||
@ViewBuilder var content: () -> Content
|
||||
/// The content shown at the bottom of the layout.
|
||||
@ViewBuilder var bottomContent: () -> BottomContent
|
||||
|
||||
var body: some View {
|
||||
if dynamicTypeSize < .accessibility1 {
|
||||
standardLayout
|
||||
} else {
|
||||
accessibilityLayout
|
||||
}
|
||||
}
|
||||
|
||||
/// A layout where the content scrolls with the bottom content overlaid. Used with regular font sizes.
|
||||
var standardLayout: some View {
|
||||
GeometryReader { geometry in
|
||||
ScrollView {
|
||||
VStack {
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
|
||||
content()
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, topPadding)
|
||||
}
|
||||
}
|
||||
.scrollBounceBehavior(.basedOnSize)
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
VStack {
|
||||
bottomContent()
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, spacing)
|
||||
.padding(.bottom, UIConstants.actionButtonBottomPadding)
|
||||
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
}
|
||||
.background()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A continuously scrolling layout used for accessibility font sizes.
|
||||
var accessibilityLayout: some View {
|
||||
GeometryReader { geometry in
|
||||
ScrollView {
|
||||
VStack {
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
|
||||
content()
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, topPadding)
|
||||
|
||||
Spacer(minLength: spacing)
|
||||
|
||||
bottomContent()
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.bottom, UIConstants.actionButtonBottomPadding)
|
||||
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
}
|
||||
.frame(minHeight: geometry.size.height)
|
||||
}
|
||||
.scrollBounceBehavior(.basedOnSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FullscreenDialog_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
FullscreenDialog(topPadding: UIConstants.iconTopPaddingToNavigationBar) {
|
||||
content
|
||||
} bottomContent: {
|
||||
buttons
|
||||
}
|
||||
.background()
|
||||
.environment(\.backgroundStyle, AnyShapeStyle(Color.element.background))
|
||||
}
|
||||
|
||||
private static var content: some View {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemName: "globe")
|
||||
.font(.system(size: 50))
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.padding()
|
||||
.background(Color.compound.bgSubtlePrimary, in: Circle())
|
||||
.padding(.bottom, 8)
|
||||
Text("Hello, World")
|
||||
.font(.compound.headingLG)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
Text("I am a subtitle")
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
.padding(.bottom)
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Label("We care about you", systemImage: "person")
|
||||
Label("Environmentally focussed", systemImage: "leaf")
|
||||
Label("All of the options", systemImage: "wrench")
|
||||
Label("Fun to use", systemImage: "logo.xbox")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static var buttons: some View {
|
||||
VStack(spacing: 16) {
|
||||
Button { } label: {
|
||||
Text("Continue")
|
||||
.font(.compound.bodyLGSemibold)
|
||||
}
|
||||
.buttonStyle(.elementAction(.xLarge))
|
||||
|
||||
Button { } label: {
|
||||
Text("More options")
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(14)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -38,8 +38,8 @@ struct AnalyticsPromptScreenViewState: BindableState {
|
||||
/// A collection of strings for the UI that need to be parsed from HTML
|
||||
struct AnalyticsPromptScreenStrings {
|
||||
let optInContent: AttributedString
|
||||
let point1 = AttributedStringBuilder().fromHTML(L10n.screenAnalyticsPromptDataUsage) ?? AttributedString(L10n.screenAnalyticsPromptDataUsage)
|
||||
let point2 = AttributedStringBuilder().fromHTML(L10n.screenAnalyticsPromptThirdPartySharing) ?? AttributedString(L10n.screenAnalyticsPromptThirdPartySharing)
|
||||
let point1 = AttributedStringBuilder().fromHTML(L10n.screenAnalyticsPromptDataUsage)?.replacingFontWithPresentationIntent() ?? AttributedString(L10n.screenAnalyticsPromptDataUsage)
|
||||
let point2 = AttributedStringBuilder().fromHTML(L10n.screenAnalyticsPromptThirdPartySharing)?.replacingFontWithPresentationIntent() ?? AttributedString(L10n.screenAnalyticsPromptThirdPartySharing)
|
||||
let point3 = L10n.screenAnalyticsPromptSettings
|
||||
|
||||
init(termsURL: URL) {
|
||||
|
@ -18,37 +18,16 @@ import SwiftUI
|
||||
|
||||
/// A prompt that asks the user whether they would like to enable Analytics or not.
|
||||
struct AnalyticsPromptScreen: View {
|
||||
private let horizontalPadding: CGFloat = 16
|
||||
|
||||
@ObservedObject var context: AnalyticsPromptScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ScrollView {
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
|
||||
mainContent
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.top, UIConstants.onboardingBreakerScreenTopPadding)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
VStack {
|
||||
buttons
|
||||
.readableFrame()
|
||||
.padding(.horizontal, horizontalPadding)
|
||||
.padding(.bottom, UIConstants.actionButtonBottomPadding)
|
||||
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.background(Color.element.background.ignoresSafeArea())
|
||||
}
|
||||
.background(Color.element.background.ignoresSafeArea())
|
||||
FullscreenDialog(topPadding: UIConstants.onboardingBreakerScreenTopPadding) {
|
||||
mainContent
|
||||
} bottomContent: {
|
||||
buttons
|
||||
}
|
||||
.background()
|
||||
.environment(\.backgroundStyle, AnyShapeStyle(Color.element.background))
|
||||
.navigationBarHidden(true)
|
||||
.navigationBarBackButtonHidden(true)
|
||||
}
|
||||
@ -95,7 +74,7 @@ struct AnalyticsPromptScreen: View {
|
||||
|
||||
/// The stack of enable/disable buttons.
|
||||
private var buttons: some View {
|
||||
VStack {
|
||||
VStack(spacing: 16) {
|
||||
Button { context.send(viewAction: .enable) } label: {
|
||||
Text(L10n.actionEnable)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
@ -106,7 +85,7 @@ struct AnalyticsPromptScreen: View {
|
||||
Button { context.send(viewAction: .disable) } label: {
|
||||
Text(L10n.actionNotNow)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(12)
|
||||
.padding(14)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.analyticsPromptScreen.notNow)
|
||||
}
|
||||
|
@ -20,20 +20,13 @@ struct ServerConfirmationScreen: View {
|
||||
@ObservedObject var context: ServerConfirmationScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
FullscreenDialog(topPadding: UIConstants.iconTopPaddingToNavigationBar) {
|
||||
header
|
||||
.padding(.top, UIConstants.iconTopPaddingToNavigationBar)
|
||||
.padding(.horizontal, 16)
|
||||
.readableFrame()
|
||||
}
|
||||
.background(Color.element.background.ignoresSafeArea())
|
||||
.safeAreaInset(edge: .bottom) {
|
||||
} bottomContent: {
|
||||
buttons
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 16)
|
||||
.readableFrame()
|
||||
.background(Color.element.background.ignoresSafeArea())
|
||||
}
|
||||
.background()
|
||||
.environment(\.backgroundStyle, AnyShapeStyle(Color.element.background))
|
||||
.introspectViewController { viewController in
|
||||
guard let window = viewController.view.window else { return }
|
||||
context.send(viewAction: .updateWindow(window))
|
||||
@ -62,7 +55,7 @@ struct ServerConfirmationScreen: View {
|
||||
|
||||
/// The action buttons shown at the bottom of the view.
|
||||
var buttons: some View {
|
||||
VStack(spacing: 24) {
|
||||
VStack(spacing: 16) {
|
||||
Button { context.send(viewAction: .confirm) } label: {
|
||||
Text(L10n.actionContinue)
|
||||
}
|
||||
@ -72,7 +65,7 @@ struct ServerConfirmationScreen: View {
|
||||
Button { context.send(viewAction: .changeServer) } label: {
|
||||
Text(L10n.screenServerConfirmationChangeServer)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(.vertical, 14)
|
||||
.padding(14)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.serverConfirmationScreen.changeServer)
|
||||
}
|
||||
|
@ -33,24 +33,7 @@ enum OnboardingViewModelAction {
|
||||
case login
|
||||
}
|
||||
|
||||
struct OnboardingViewState: BindableState {
|
||||
/// An array containing all content of the carousel pages
|
||||
let content: [OnboardingPageContent]
|
||||
var bindings: OnboardingBindings
|
||||
|
||||
init() {
|
||||
content = [
|
||||
OnboardingPageContent(title: L10n.screenOnboardingWelcomeTitle.tinting(".", color: .element.accent),
|
||||
message: L10n.screenOnboardingWelcomeSubtitle(InfoPlistReader.main.bundleDisplayName),
|
||||
image: Asset.Images.onboardingAppLogo)
|
||||
]
|
||||
bindings = OnboardingBindings()
|
||||
}
|
||||
}
|
||||
|
||||
struct OnboardingBindings {
|
||||
var pageIndex = 0
|
||||
}
|
||||
struct OnboardingViewState: BindableState { }
|
||||
|
||||
enum OnboardingViewAction {
|
||||
case login
|
||||
|
@ -1,45 +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 SwiftUI
|
||||
|
||||
struct OnboardingPageIndicator: View {
|
||||
/// The number of pages that are shown.
|
||||
let pageCount: Int
|
||||
/// The index of the current page
|
||||
let pageIndex: Int
|
||||
|
||||
internal init(pageCount: Int, pageIndex: Int) {
|
||||
self.pageCount = pageCount
|
||||
|
||||
// Clamp the page index to handle the hidden page.
|
||||
if pageIndex == -1 {
|
||||
self.pageIndex = pageCount - 1
|
||||
} else {
|
||||
self.pageIndex = pageIndex % pageCount
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
ForEach(0..<pageCount, id: \.self) { index in
|
||||
Circle()
|
||||
.frame(width: 8, height: 8)
|
||||
.foregroundColor(index == pageIndex ? .accentColor : .element.quaternaryContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,69 +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 SwiftUI
|
||||
|
||||
struct OnboardingPageView: View {
|
||||
/// The content that this page should display.
|
||||
let content: OnboardingPageContent
|
||||
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if verticalSizeClass == .regular {
|
||||
Spacer()
|
||||
|
||||
Image(content.image.name)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.padding(60)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Spacer()
|
||||
|
||||
Text(content.title)
|
||||
.font(.compound.headingLGBold)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
.multilineTextAlignment(.center)
|
||||
Text(content.message)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal, 16)
|
||||
.readableFrame()
|
||||
}
|
||||
}
|
||||
|
||||
struct OnboardingPage_Previews: PreviewProvider {
|
||||
static let content = OnboardingViewState().content
|
||||
static var previews: some View {
|
||||
ForEach(0..<content.count, id: \.self) { index in
|
||||
OnboardingPageView(content: content[index])
|
||||
}
|
||||
}
|
||||
}
|
@ -19,16 +19,7 @@ import SwiftUI
|
||||
|
||||
/// The screen shown at the beginning of the onboarding flow.
|
||||
struct OnboardingScreen: View {
|
||||
@Environment(\.layoutDirection) private var layoutDirection
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
private var isLeftToRight: Bool { layoutDirection == .leftToRight }
|
||||
private var pageCount: Int { context.viewState.content.count }
|
||||
|
||||
/// A timer to automatically animate the pages.
|
||||
@State private var pageTimer: Timer?
|
||||
/// The amount of offset to apply when a drag gesture is in progress.
|
||||
@State private var dragOffset: CGFloat = .zero
|
||||
|
||||
@ObservedObject var context: OnboardingViewModel.Context
|
||||
|
||||
@ -38,55 +29,66 @@ struct OnboardingScreen: View {
|
||||
.accessibilityHidden(true)
|
||||
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
VStack(alignment: .leading) {
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
|
||||
// The main content of the carousel
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
// Add a hidden page at the start of the carousel duplicating the content of the last page
|
||||
OnboardingPageView(content: context.viewState.content[pageCount - 1])
|
||||
.frame(width: geometry.size.width)
|
||||
.accessibilityIdentifier(A11yIdentifiers.onboardingScreen.hidden)
|
||||
|
||||
ForEach(0..<pageCount, id: \.self) { index in
|
||||
OnboardingPageView(content: context.viewState.content[index])
|
||||
.frame(width: geometry.size.width)
|
||||
}
|
||||
}
|
||||
.offset(x: pageOffset(in: geometry))
|
||||
|
||||
Spacer()
|
||||
|
||||
if pageCount > 1 {
|
||||
OnboardingPageIndicator(pageCount: pageCount, pageIndex: context.pageIndex)
|
||||
.frame(width: geometry.size.width)
|
||||
.padding(.bottom)
|
||||
}
|
||||
|
||||
buttons
|
||||
.frame(width: geometry.size.width)
|
||||
.padding(.bottom, UIConstants.actionButtonBottomPadding)
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16)
|
||||
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
.gesture(
|
||||
DragGesture()
|
||||
.onChanged(handleDragGestureChange)
|
||||
.onEnded { handleDragGestureEnded($0, viewSize: geometry.size) }
|
||||
)
|
||||
VStack(alignment: .leading) {
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
|
||||
content
|
||||
.frame(width: geometry.size.width)
|
||||
.accessibilityIdentifier(A11yIdentifiers.onboardingScreen.hidden)
|
||||
|
||||
Spacer()
|
||||
|
||||
buttons
|
||||
.frame(width: geometry.size.width)
|
||||
.padding(.bottom, UIConstants.actionButtonBottomPadding)
|
||||
.padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16)
|
||||
|
||||
Spacer()
|
||||
.frame(height: UIConstants.spacerHeight(in: geometry))
|
||||
}
|
||||
.frame(maxHeight: .infinity)
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
.onAppear(perform: startTimer)
|
||||
.onDisappear(perform: stopTimer)
|
||||
}
|
||||
}
|
||||
|
||||
var content: some View {
|
||||
VStack {
|
||||
if verticalSizeClass == .regular {
|
||||
Spacer()
|
||||
|
||||
Image(Asset.Images.onboardingAppLogo.name)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.padding(60)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Spacer()
|
||||
|
||||
Text(L10n.screenOnboardingWelcomeTitle)
|
||||
.font(.compound.headingLGBold)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
.multilineTextAlignment(.center)
|
||||
Text(L10n.screenOnboardingWelcomeSubtitle(InfoPlistReader.main.bundleDisplayName))
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.padding()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding(.bottom)
|
||||
.padding(.horizontal, 16)
|
||||
.readableFrame()
|
||||
}
|
||||
|
||||
/// The main action buttons.
|
||||
var buttons: some View {
|
||||
VStack(spacing: 12) {
|
||||
@ -99,103 +101,6 @@ struct OnboardingScreen: View {
|
||||
.padding(.horizontal, verticalSizeClass == .compact ? 128 : 24)
|
||||
.readableFrame()
|
||||
}
|
||||
|
||||
// MARK: - Animation
|
||||
|
||||
/// Starts the animation timer for an automatic carousel effect.
|
||||
private func startTimer() {
|
||||
guard pageTimer == nil, pageCount > 1 else { return }
|
||||
|
||||
pageTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
|
||||
if context.pageIndex == pageCount - 1 {
|
||||
showHiddenPage()
|
||||
|
||||
withElementAnimation(.easeInOut(duration: 0.7)) {
|
||||
showNextPage()
|
||||
}
|
||||
} else {
|
||||
withElementAnimation(.easeInOut(duration: 0.7)) {
|
||||
showNextPage()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Stops the animation timer for manual interaction.
|
||||
private func stopTimer() {
|
||||
guard let pageTimer else { return }
|
||||
|
||||
self.pageTimer = nil
|
||||
pageTimer.invalidate()
|
||||
}
|
||||
|
||||
private func showNextPage() {
|
||||
// Wrap back round to the first page index when reaching the end.
|
||||
context.pageIndex = (context.pageIndex + 1) % context.viewState.content.count
|
||||
}
|
||||
|
||||
private func showPreviousPage() {
|
||||
// Prevent the hidden page at index -1 from being shown.
|
||||
context.pageIndex = max(0, context.pageIndex - 1)
|
||||
}
|
||||
|
||||
private func showHiddenPage() {
|
||||
// Hidden page for a nicer animation when looping back to the start.
|
||||
context.pageIndex = -1
|
||||
}
|
||||
|
||||
/// The offset to apply to the `HStack` of pages.
|
||||
private func pageOffset(in geometry: GeometryProxy) -> CGFloat {
|
||||
(CGFloat(context.pageIndex + 1) * -geometry.size.width) + dragOffset
|
||||
}
|
||||
|
||||
// MARK: - Gestures
|
||||
|
||||
/// Whether or not a drag gesture is valid or not.
|
||||
/// - Parameter width: The gesture's translation width.
|
||||
/// - Returns: `true` if there is another page to drag to.
|
||||
private func shouldSwipeForTranslation(_ width: CGFloat) -> Bool {
|
||||
if context.pageIndex == 0 {
|
||||
return isLeftToRight ? width < 0 : width > 0
|
||||
} else if context.pageIndex == pageCount - 1 {
|
||||
return isLeftToRight ? width > 0 : width < 0
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Updates the `dragOffset` based on the gesture's value.
|
||||
/// - Parameter drag: The drag gesture value to handle.
|
||||
private func handleDragGestureChange(_ drag: DragGesture.Value) {
|
||||
guard shouldSwipeForTranslation(drag.translation.width) else { return }
|
||||
|
||||
stopTimer()
|
||||
|
||||
// Animate the change over a few frames to smooth out any stuttering.
|
||||
withElementAnimation(.linear(duration: 0.05)) {
|
||||
dragOffset = isLeftToRight ? drag.translation.width : -drag.translation.width
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the drag offset and informs the view model to switch to another page if necessary.
|
||||
/// - Parameter viewSize: The size of the view in which the gesture took place.
|
||||
private func handleDragGestureEnded(_ drag: DragGesture.Value, viewSize: CGSize) {
|
||||
guard shouldSwipeForTranslation(drag.predictedEndTranslation.width) else {
|
||||
// Reset the offset just in case.
|
||||
withElementAnimation { dragOffset = 0 }
|
||||
return
|
||||
}
|
||||
|
||||
withElementAnimation(.easeInOut(duration: 0.2)) {
|
||||
if drag.predictedEndTranslation.width < -viewSize.width / 2 {
|
||||
showNextPage()
|
||||
} else if drag.predictedEndTranslation.width > viewSize.width / 2 {
|
||||
showPreviousPage()
|
||||
}
|
||||
|
||||
dragOffset = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.analyticsPrompt.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.analyticsPrompt.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.serverConfirmationLogin.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.serverConfirmationLogin.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.serverConfirmationRegister.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.serverConfirmationRegister.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.analyticsPrompt.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.analyticsPrompt.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.serverConfirmationLogin.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.serverConfirmationLogin.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.serverConfirmationRegister.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.serverConfirmationRegister.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.analyticsPrompt.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.analyticsPrompt.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.serverConfirmationLogin.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.serverConfirmationLogin.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.serverConfirmationRegister.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.serverConfirmationRegister.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.analyticsPrompt.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.analyticsPrompt.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.serverConfirmationLogin.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.serverConfirmationLogin.png
(Stored with Git LFS)
Binary file not shown.
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.serverConfirmationRegister.png
(Stored with Git LFS)
BIN
UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.serverConfirmationRegister.png
(Stored with Git LFS)
Binary file not shown.
46
UnitTests/Sources/AttributedStringTests.swift
Normal file
46
UnitTests/Sources/AttributedStringTests.swift
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
@testable import ElementX
|
||||
import XCTest
|
||||
|
||||
class AttributedStringTests: XCTestCase {
|
||||
func testReplacingFontWithPresentationIntent() {
|
||||
// Given a string parsed from HTML that contains specific fixed size fonts.
|
||||
let boldString = "Bold"
|
||||
guard let originalString = AttributedStringBuilder().fromHTML("Normal <b>\(boldString)</b> Normal.") else {
|
||||
XCTFail("The attributed string should be built from the HTML.")
|
||||
return
|
||||
}
|
||||
originalString.runs.forEach { XCTAssertNotNil($0.uiKit.font, "The original runs should all have a UIFont.") }
|
||||
|
||||
// When replacing the font with a presentation intent.
|
||||
let string = originalString.replacingFontWithPresentationIntent()
|
||||
|
||||
// Then the font should be removed with an inline presentation intent applied to the bold text.
|
||||
for run in string.runs {
|
||||
XCTAssertNil(run.uiKit.font, "The UIFont should have been removed.")
|
||||
XCTAssertNil(run.font, "No font should be in the run at all.")
|
||||
|
||||
let substring = string[run.range]
|
||||
if String(substring.characters) == boldString {
|
||||
XCTAssertEqual(run.inlinePresentationIntent, .stronglyEmphasized, "The bold string should be bold.")
|
||||
} else {
|
||||
XCTAssertNil(run.presentationIntent, "The rest should be plain.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
changelog.d/pr-994.api
Normal file
1
changelog.d/pr-994.api
Normal file
@ -0,0 +1 @@
|
||||
Bump the minimum supported iOS version to 16.4.
|
@ -9,8 +9,8 @@ options:
|
||||
groupSortPosition: bottom
|
||||
createIntermediateGroups: true
|
||||
deploymentTarget:
|
||||
iOS: "16.0"
|
||||
macOS: "13.0"
|
||||
iOS: "16.4"
|
||||
macOS: "13.3"
|
||||
groupOrdering:
|
||||
- order: [ElementX, UnitTests, UITests, IntegrationTests, Tools]
|
||||
- pattern: ElementX
|
||||
|
Loading…
x
Reference in New Issue
Block a user