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:
Doug 2023-05-31 18:39:53 +01:00 committed by GitHub
parent f792aafe8d
commit dcc4ea5e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 338 additions and 370 deletions

View File

@ -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;

View File

@ -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
}
}

View 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)
}
}
}
}

View File

@ -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) {

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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)
}
}
}
}

View File

@ -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])
}
}
}

View File

@ -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

View 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
View File

@ -0,0 +1 @@
Bump the minimum supported iOS version to 16.4.

View File

@ -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