Welcome Screen (#1259)

* the screen renders and changed the analytics checkmark to be more generic

* welcome screen

* transition

* tests... TODO

* changelog

* changed assets and added multilineTextAlignment

* better naming

* fix for missing after migration but has a weird presentaiton bug

* fix

* icon color
This commit is contained in:
Mauro 2023-07-05 19:55:24 +02:00 committed by GitHub
parent 9ebc57e1cf
commit 00636d98da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 501 additions and 49 deletions

View File

@ -43,7 +43,7 @@
0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; };
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */; };
10516CF20E8B5852F4C444FD /* AnalyticsPromptScreenCheckmarkItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1253D3E9395A0493DB944B9 /* AnalyticsPromptScreenCheckmarkItem.swift */; };
10516CF20E8B5852F4C444FD /* RoundedLabelItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1253D3E9395A0493DB944B9 /* RoundedLabelItem.swift */; };
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; };
126EE01D8BEAEF26105D83C5 /* RoomDetailsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */; };
12C867E85E6D12EEDFD0B127 /* CustomStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96C4762F8D6112E43117DB2F /* CustomStringConvertible.swift */; };
@ -56,6 +56,7 @@
152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65C2B80DD0BF6F10BB5FA922 /* MockAuthenticationServiceProxy.swift */; };
155063E980E763D4910EA3CF /* Analytics+SwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */; };
1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4549FCB53F43DB0B278374BC /* TemplateScreen.swift */; };
15705159D584B189BC48CE50 /* WelcomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AC2812E6AB8C477A199E565 /* WelcomeScreen.swift */; };
157E5FDDF419C0B2CA7E2C28 /* TimelineItemBubbledStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98A2932515EA11D3DD8A3506 /* TimelineItemBubbledStylerView.swift */; };
158A2D528CC78C4E7A8ED608 /* MockRoomTimelineControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71556206CD5E8B1F53F07178 /* MockRoomTimelineControllerFactory.swift */; };
15D867E638BFD0E5E71DB1EF /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AFEF3AC64B1358083F76B8B /* List.swift */; };
@ -85,6 +86,7 @@
1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */; };
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */; };
2185C1F6724C78FFF355D6FA /* WelcomeScreenScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */; };
2198B4458AFF69102BBCC370 /* RoomTimelineItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7D3C686C214470F4911618 /* RoomTimelineItemViewModel.swift */; };
21BF2B7CEDFE3CA67C5355AD /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = C733D11B421CFE3A657EF230 /* test_image.png */; };
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
@ -149,6 +151,7 @@
36AD4DD4C798E22584ED3200 /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; };
36CD6E11B37396E14F032CB6 /* Flow in Frameworks */ = {isa = PBXBuildFile; productRef = 4D7E6BFC89715FC3CF0349D0 /* Flow */; };
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */; };
383055C6ABE5BE058CEE1DDB /* WelcomeScreenScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57FE5EF0AFFE360C66420AAE /* WelcomeScreenScreenCoordinator.swift */; };
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
38896D54D6D675534E606195 /* RoomTimelineControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */; };
3910D3A2EF98587C0E7B9CCB /* EmojiMartEmoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11F7F3CF7E70518BD7D25E04 /* EmojiMartEmoji.swift */; };
@ -333,6 +336,7 @@
7E3C34BC10936AD4F77975F4 /* EmojiMartJSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39001365B76B89983FDB7AD8 /* EmojiMartJSONLoader.swift */; };
7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */; };
7ECF12D5DCD69F67BD3E3842 /* RoomTimelineControllerFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FE0CDF1FFA92EA7EE17B0B /* RoomTimelineControllerFactoryProtocol.swift */; };
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851EF6258DF8B7EF129DC3AC /* WelcomeScreenScreenViewModelTests.swift */; };
7F08F4BC1312075E2B5EAEFA /* AuthenticationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */; };
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; };
7F64FA937B95924B3A44EC12 /* OnboardingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB8E75B9CB6C78BE8D09B1AF /* OnboardingScreen.swift */; };
@ -401,6 +405,7 @@
9408CE8B8865C0C8DD4C9869 /* NoticeRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */; };
9462C62798F47E39DCC182D2 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA89A2DD51B6BBE1DA55E263 /* Application.swift */; };
94A65DD8A353DF112EBEF67A /* SessionVerificationControllerProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D56469A9EE0CFA2B7BA9760 /* SessionVerificationControllerProxyProtocol.swift */; };
94CEF587A3994A36A46D8334 /* WelcomeScreenScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7101698791B321A76F552804 /* WelcomeScreenScreenViewModelProtocol.swift */; };
94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD371B60E07A5324B9507EF /* AnalyticsSettingsScreenCoordinator.swift */; };
9586E90A447C4896C0CA3A8E /* TimelineItemReplyDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */; };
@ -536,6 +541,7 @@
BB784A02BADB03C820617A46 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90A55430639712CFACA34F43 /* TextRoomTimelineItem.swift */; };
BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC1D382565A4E9CAC2F14EA /* MediaFileHandleProxy.swift */; };
BD203FC6A7AE7637EA003643 /* RoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABDE6F66532CBEB0E016F94 /* RoomProxyMock.swift */; };
BD2BF1EC73FFB0C01552ECDA /* WelcomeScreenScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0FB782CE6176A5D2C082EC5D /* WelcomeScreenScreenModels.swift */; };
BD6D98676111DA8FC2BE4908 /* InvitesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86873A768B13069BB5CAECF6 /* InvitesScreenViewModelProtocol.swift */; };
BD782053BE4C3D2F0BDE5699 /* ServiceLocator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */; };
BDA68E8D95B2B24B28825B8B /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */; };
@ -618,6 +624,7 @@
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; };
DB079D1929B5A5F52D207C83 /* RoomDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */; };
DC08ADC41E792086A340A8B3 /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */; };
DC1BB5EE5F4D9B6A1F98A77A /* WelcomeScreenScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */; };
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */; };
DE0BBA736557B42BC0DA6CBF /* TimelineEventProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00B62EE933FC3D5651AF4607 /* TimelineEventProxy.swift */; };
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */; };
@ -814,6 +821,7 @@
0F19DBE940499D3E3DD405D8 /* RoomMemberDetailsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenUITests.swift; sourceTree = "<group>"; };
0F5567A7EF6F2AB9473236F6 /* DocumentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentPicker.swift; sourceTree = "<group>"; };
0FA60F848D1C14F873F9621A /* RoomMemberDetailsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenCoordinator.swift; sourceTree = "<group>"; };
0FB782CE6176A5D2C082EC5D /* WelcomeScreenScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenModels.swift; sourceTree = "<group>"; };
1059E2AE7878CF7820592637 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactory.swift; sourceTree = "<group>"; };
10CC626F97AD70FF0420C115 /* RoomSummaryProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProviderProtocol.swift; sourceTree = "<group>"; };
@ -1009,6 +1017,7 @@
57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsConsentState.swift; sourceTree = "<group>"; };
57EAAF82432B0B53881CF826 /* AudioRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineItem.swift; sourceTree = "<group>"; };
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLocator.swift; sourceTree = "<group>"; };
57FE5EF0AFFE360C66420AAE /* WelcomeScreenScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenCoordinator.swift; sourceTree = "<group>"; };
584A61D9C459FAFEF038A7C0 /* Section.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Section.swift; sourceTree = "<group>"; };
58C2527813FDAE23E72A9063 /* AnalyticsSettingsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModelTests.swift; sourceTree = "<group>"; };
592A35163B0749C66BFD6186 /* MapLibreStaticMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreStaticMapView.swift; sourceTree = "<group>"; };
@ -1075,6 +1084,7 @@
6FC5015B9634698BDB8701AF /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
7023EB4F3B7C7D1FBA68638B /* TimelineItemDebugView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemDebugView.swift; sourceTree = "<group>"; };
70C86696AC9521F8ED88FBEB /* MediaUploadPreviewScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreen.swift; sourceTree = "<group>"; };
7101698791B321A76F552804 /* WelcomeScreenScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModelProtocol.swift; sourceTree = "<group>"; };
713B48DBF65DE4B0DD445D66 /* ReportContentScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenViewModelProtocol.swift; sourceTree = "<group>"; };
71556206CD5E8B1F53F07178 /* MockRoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
71A7D4DDEEE5D2CA0C8D63CD /* SoftLogoutScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreen.swift; sourceTree = "<group>"; };
@ -1115,6 +1125,7 @@
84816E0D2F34E368BF64FA60 /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = "<group>"; };
84A87D0471D438A233C2CF4A /* RoomMemberDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenViewModel.swift; sourceTree = "<group>"; };
84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunnerTests.swift; sourceTree = "<group>"; };
851EF6258DF8B7EF129DC3AC /* WelcomeScreenScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModelTests.swift; sourceTree = "<group>"; };
854BCEAF2A832176FAACD2CB /* SplashScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenCoordinator.swift; sourceTree = "<group>"; };
85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = "<group>"; };
86873A768B13069BB5CAECF6 /* InvitesScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModelProtocol.swift; sourceTree = "<group>"; };
@ -1125,6 +1136,7 @@
893777A4997BBDB68079D4F5 /* ArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayTests.swift; sourceTree = "<group>"; };
8977176AB534AA41630395BC /* LegalInformationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
897DF5E9A70CE05A632FC8AF /* UTType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTType.swift; sourceTree = "<group>"; };
8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenUITests.swift; sourceTree = "<group>"; };
8AFCE895ECFFA53FEE64D62B /* MediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoader.swift; sourceTree = "<group>"; };
8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenModels.swift; sourceTree = "<group>"; };
8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItemContent.swift; sourceTree = "<group>"; };
@ -1165,6 +1177,7 @@
9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomFlowCoordinator.swift; sourceTree = "<group>"; };
9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
9A68BCE6438873D2661D93D0 /* BugReportServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceProtocol.swift; sourceTree = "<group>"; };
9AC2812E6AB8C477A199E565 /* WelcomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreen.swift; sourceTree = "<group>"; };
9B06663F7858E45882E63471 /* StaticLocationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreen.swift; sourceTree = "<group>"; };
9B65A314DF40B6BBF775C2BC /* AnalyticsPromptScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCoordinator.swift; sourceTree = "<group>"; };
9B663BE498BB39EADC24025D /* SettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenModels.swift; sourceTree = "<group>"; };
@ -1352,7 +1365,7 @@
DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = "<group>"; };
E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerAuthorization.swift; sourceTree = "<group>"; };
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
E1253D3E9395A0493DB944B9 /* AnalyticsPromptScreenCheckmarkItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenCheckmarkItem.swift; sourceTree = "<group>"; };
E1253D3E9395A0493DB944B9 /* RoundedLabelItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedLabelItem.swift; sourceTree = "<group>"; };
E18CF12478983A5EB390FB26 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreen.swift; sourceTree = "<group>"; };
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSize.swift; sourceTree = "<group>"; };
@ -1427,6 +1440,7 @@
FC2D505742FDA21FCDC4C18A /* AudioRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineView.swift; sourceTree = "<group>"; };
FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorController.swift; sourceTree = "<group>"; };
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModel.swift; sourceTree = "<group>"; };
FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsScreen.swift; sourceTree = "<group>"; };
FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -1554,7 +1568,7 @@
isa = PBXGroup;
children = (
5F8002D0392A476D2758B291 /* AnalyticsPromptScreen.swift */,
E1253D3E9395A0493DB944B9 /* AnalyticsPromptScreenCheckmarkItem.swift */,
E1253D3E9395A0493DB944B9 /* RoundedLabelItem.swift */,
);
path = View;
sourceTree = "<group>";
@ -2427,6 +2441,7 @@
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */,
BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */,
C796FC1DFDBCDD5573D0360F /* WaitlistScreenViewModelTests.swift */,
851EF6258DF8B7EF129DC3AC /* WelcomeScreenScreenViewModelTests.swift */,
53280D2292E6C9C7821773FD /* UserSession */,
70C5B842301AC281DF374E41 /* Extensions */,
7583EAC171059A86B767209F /* MediaProvider */,
@ -2712,6 +2727,18 @@
path = View;
sourceTree = "<group>";
};
93F4D089C78719B688D576ED /* WelcomeScreenScreen */ = {
isa = PBXGroup;
children = (
57FE5EF0AFFE360C66420AAE /* WelcomeScreenScreenCoordinator.swift */,
0FB782CE6176A5D2C082EC5D /* WelcomeScreenScreenModels.swift */,
FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */,
7101698791B321A76F552804 /* WelcomeScreenScreenViewModelProtocol.swift */,
C5B85A56479F1382EA5A9913 /* View */,
);
path = WelcomeScreenScreen;
sourceTree = "<group>";
};
9413F680ECDFB2B0DDB0DEF2 /* Packages */ = {
isa = PBXGroup;
children = (
@ -2774,6 +2801,7 @@
DA2AEC1AB349A341FE13DEC1 /* StartChatScreenUITests.swift */,
F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */,
ECB08484CD5D77C9BF97AA78 /* WaitlistScreenUITests.swift */,
8AB10FA6570DD08B3966C159 /* WelcomeScreenScreenUITests.swift */,
);
path = Sources;
sourceTree = "<group>";
@ -3154,6 +3182,14 @@
path = CreateRoom;
sourceTree = "<group>";
};
C5B85A56479F1382EA5A9913 /* View */ = {
isa = PBXGroup;
children = (
9AC2812E6AB8C477A199E565 /* WelcomeScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
CA15BB3F6C62B35AE2C281A9 /* Provider */ = {
isa = PBXGroup;
children = (
@ -3321,6 +3357,7 @@
3153FCA3F4B0E88B16D99D12 /* SessionVerificationScreen */,
70B74A432C241E56A7ACE610 /* Settings */,
EC4545C7E37E8294D3FE6800 /* StartChatScreen */,
93F4D089C78719B688D576ED /* WelcomeScreenScreen */,
);
path = Screens;
sourceTree = "<group>";
@ -4010,6 +4047,7 @@
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
99F8DA4CCC6772EE5FE68E24 /* ViewModelContext.swift in Sources */,
FB9A1DD83EF641A75ABBCE69 /* WaitlistScreenViewModelTests.swift in Sources */,
7F02063FB3D1C3E5601471A1 /* WelcomeScreenScreenViewModelTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -4037,7 +4075,7 @@
8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */,
EDC1031A7CFB3406A9DA3175 /* AnalyticsLocationType.swift in Sources */,
9DF3F6318A4402305F5EB869 /* AnalyticsPromptScreen.swift in Sources */,
10516CF20E8B5852F4C444FD /* AnalyticsPromptScreenCheckmarkItem.swift in Sources */,
10516CF20E8B5852F4C444FD /* RoundedLabelItem.swift in Sources */,
5F28C9146694B381BB82E18C /* AnalyticsPromptScreenCoordinator.swift in Sources */,
496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */,
0AA0477E063E72B786A983CF /* AnalyticsPromptScreenViewModel.swift in Sources */,
@ -4507,6 +4545,11 @@
2F66701B15657A87B4AC3A0A /* WaitlistScreenModels.swift in Sources */,
CF3827071B0BC9638BD44F5D /* WaitlistScreenViewModel.swift in Sources */,
B717A820BE02C6FE2CB53F6E /* WaitlistScreenViewModelProtocol.swift in Sources */,
15705159D584B189BC48CE50 /* WelcomeScreen.swift in Sources */,
383055C6ABE5BE058CEE1DDB /* WelcomeScreenScreenCoordinator.swift in Sources */,
BD2BF1EC73FFB0C01552ECDA /* WelcomeScreenScreenModels.swift in Sources */,
DC1BB5EE5F4D9B6A1F98A77A /* WelcomeScreenScreenViewModel.swift in Sources */,
94CEF587A3994A36A46D8334 /* WelcomeScreenScreenViewModelProtocol.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -4549,6 +4592,7 @@
84EFCB95F9DA2979C8042B26 /* UITestsSignalling.swift in Sources */,
B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */,
2DA90E38FF4E696825810C1A /* WaitlistScreenUITests.swift in Sources */,
2185C1F6724C78FFF355D6FA /* WelcomeScreenScreenUITests.swift in Sources */,
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@ -279,7 +279,7 @@
"screen_room_details_encryption_enabled_subtitle" = "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them.";
"screen_room_details_encryption_enabled_title" = "Message encryption enabled";
"screen_room_details_invite_people_title" = "Invite people";
"screen_room_details_notification_title" = "Notification";
"screen_room_details_notification_title" = "Notifications";
"screen_room_details_room_name_label" = "Room name";
"screen_room_details_share_room_title" = "Share room";
"screen_room_details_updating_room" = "Updating room…";
@ -336,6 +336,12 @@
"screen_waitlist_message_success" = "Welcome to %1$@!";
"screen_waitlist_title" = "Youre almost there.";
"screen_waitlist_title_success" = "You're in.";
"screen_welcome_bullet_1" = "Calls, location sharing, search and more will be added later this year.";
"screen_welcome_bullet_2" = "Message history for encrypted rooms wont be available in this update.";
"screen_welcome_bullet_3" = "Wed love to hear from you, let us know what you think via the settings page.";
"screen_welcome_button" = "Let's go!";
"screen_welcome_subtitle" = "Heres what you need to know:";
"screen_welcome_title" = "Welcome to %1$@!";
"session_verification_banner_message" = "Looks like youre using a new device. Verify with another device to access your encrypted messages moving forwards.";
"session_verification_banner_title" = "Verify its you";
"settings_rageshake" = "Rageshake";

View File

@ -33,6 +33,7 @@ final class AppSettings {
case readReceiptsEnabled
case locationEventsEnabled
case shareLocationEnabled
case hasShownWelcomeScreen
}
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
@ -89,6 +90,9 @@ final class AppSettings {
/// The task identifier used for background app refresh. Also used in main target's the Info.plist
let backgroundAppRefreshTaskIdentifier = "io.element.elementx.background.refresh"
@UserPreference(key: UserDefaultsKeys.hasShownWelcomeScreen, defaultValue: false, storageType: .userDefaults(store))
var hasShownWelcomeScreen: Bool
// MARK: - Authentication
@ -119,7 +123,7 @@ final class AppSettings {
/// proxy that it is the first sync (or that an upgrade on the backend will involve a slower sync).
@UserPreference(key: UserDefaultsKeys.migratedAccounts, defaultValue: [:], storageType: .userDefaults(store))
var migratedAccounts: [String: Bool]
// MARK: - Notifications
var pusherAppId: String {

View File

@ -37,7 +37,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
private let sidebarNavigationStackCoordinator: NavigationStackCoordinator
private let detailNavigationStackCoordinator: NavigationStackCoordinator
private let selectedRoomSubject = CurrentValueSubject<String?, Never>(nil)
var callback: ((UserSessionFlowCoordinatorAction) -> Void)?
@ -85,6 +85,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
if appSettings.migratedAccounts[userSession.userID] != true {
// Show the migration screen for a new account.
stateMachine.processEvent(.startWithMigration)
} else if !appSettings.hasShownWelcomeScreen {
stateMachine.processEvent(.startWithWelcomeScreen)
} else {
// Otherwise go straight to the home screen.
stateMachine.processEvent(.start)
@ -106,7 +108,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
return // Not ready to handle a route.
case .roomList:
break // Nothing to tidy up on the home screen.
case .feedbackScreen, .sessionVerificationScreen, .settingsScreen, .startChatScreen, .invitesScreen:
case .feedbackScreen, .sessionVerificationScreen, .settingsScreen, .startChatScreen, .invitesScreen, .welcomeScreen:
navigationSplitCoordinator.setSheetCoordinator(nil, animated: animated)
}
@ -135,13 +137,21 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let animated = (context.userInfo as? UserSessionFlowCoordinatorStateMachine.EventUserInfo)?.animated ?? true
switch (context.fromState, context.event, context.toState) {
case (.initial, .start, .roomList):
self.presentHomeScreen()
presentHomeScreen()
case (.initial, .startWithMigration, .migration):
self.presentMigrationScreen() // Full screen cover
self.presentHomeScreen() // Have the home screen ready to show underneath
presentMigrationScreen() // Full screen cover
presentHomeScreen() // Have the home screen ready to show underneath
case (.migration, .completeMigration, .roomList):
self.dismissMigrationScreen()
dismissMigrationScreen()
case (.initial, .startWithWelcomeScreen, .welcomeScreen):
presentHomeScreen()
presentWelcomeScreen()
case (.roomList, .presentWelcomeScreen, .welcomeScreen):
presentWelcomeScreen()
case (.welcomeScreen, .dismissedWelcomeScreen, .roomList):
break
case(.roomList, .selectRoom, .roomList):
break
@ -154,27 +164,27 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
break
case (.roomList, .showSessionVerificationScreen, .sessionVerificationScreen):
self.presentSessionVerification(animated: animated)
presentSessionVerification(animated: animated)
case (.sessionVerificationScreen, .dismissedSessionVerificationScreen, .roomList):
break
case (.roomList, .showSettingsScreen, .settingsScreen):
self.presentSettingsScreen(animated: animated)
presentSettingsScreen(animated: animated)
case (.settingsScreen, .dismissedSettingsScreen, .roomList):
break
case (.roomList, .feedbackScreen, .feedbackScreen):
self.presentFeedbackScreen(animated: animated)
presentFeedbackScreen(animated: animated)
case (.feedbackScreen, .dismissedFeedbackScreen, .roomList):
break
case (.roomList, .showStartChatScreen, .startChatScreen):
self.presentStartChat(animated: animated)
presentStartChat(animated: animated)
case (.startChatScreen, .dismissedStartChatScreen, .roomList):
break
case (.roomList, .showInvitesScreen, .invitesScreen):
self.presentInvitesList(animated: animated)
presentInvitesList(animated: animated)
case (.invitesScreen, .showInvitesScreen, .invitesScreen):
break
case (.invitesScreen, .closedInvitesScreen, .roomList):
@ -216,6 +226,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
private func dismissMigrationScreen() {
navigationSplitCoordinator.setFullScreenCoverCoordinator(nil)
// Not sure why but the full screen closure dismissal closure doesn't seem to work properly
// And not using the DispatchQueue.main results in the the screen getting presented as full screen too.
if !appSettings.hasShownWelcomeScreen {
DispatchQueue.main.async {
self.stateMachine.processEvent(.presentWelcomeScreen)
}
}
}
// swiftlint:disable:next cyclomatic_complexity
@ -257,6 +275,21 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
sidebarNavigationStackCoordinator.setRootCoordinator(coordinator)
}
private func presentWelcomeScreen() {
let welcomeScreenCoordinator = WelcomeScreenScreenCoordinator()
welcomeScreenCoordinator.actions.sink { [weak self] action in
switch action {
case .dismiss:
self?.navigationSplitCoordinator.setSheetCoordinator(nil)
}
}
.store(in: &cancellables)
navigationSplitCoordinator.setSheetCoordinator(welcomeScreenCoordinator) { [weak self] in
self?.stateMachine.processEvent(.dismissedWelcomeScreen)
}
}
// MARK: Settings

View File

@ -25,6 +25,9 @@ class UserSessionFlowCoordinatorStateMachine {
/// Showing the migration screen whilst the proxy performs an initial sync.
case migration
/// Showing the welcome screen.
case welcomeScreen
/// Showing the home screen. The `selectedRoomId` represents the timeline shown on the detail panel (if any)
case roomList(selectedRoomId: String?)
@ -53,11 +56,15 @@ class UserSessionFlowCoordinatorStateMachine {
enum Event: EventType {
/// Start the user session flows
case start
/// Starts the user session flows with the welcome screen
case startWithWelcomeScreen
/// Start the user session flows with a migration screen.
case startWithMigration
/// Request to transition from the migration state to the home screen.
case completeMigration
case presentWelcomeScreen
case dismissedWelcomeScreen
/// Request presentation for a particular room
/// - Parameter roomId:the room identifier
@ -106,8 +113,10 @@ class UserSessionFlowCoordinatorStateMachine {
private func configure() {
stateMachine.addRoutes(event: .start, transitions: [.initial => .roomList(selectedRoomId: nil)])
stateMachine.addRoutes(event: .startWithMigration, transitions: [.initial => .migration])
stateMachine.addRoutes(event: .startWithWelcomeScreen, transitions: [.initial => .welcomeScreen])
stateMachine.addRoutes(event: .completeMigration, transitions: [.migration => .roomList(selectedRoomId: nil)])
stateMachine.addRoutes(event: .dismissedWelcomeScreen, transitions: [.welcomeScreen => .roomList(selectedRoomId: nil)])
stateMachine.addRouteMapping { event, fromState, _ in
switch (event, fromState) {
case (.selectRoom(let roomId), .roomList):
@ -146,6 +155,9 @@ class UserSessionFlowCoordinatorStateMachine {
case (.closedInvitesScreen, .invitesScreen(let selectedRoomId)):
return .roomList(selectedRoomId: selectedRoomId)
case (.presentWelcomeScreen, .roomList):
return .welcomeScreen
default:
return nil

View File

@ -704,7 +704,7 @@ public enum L10n {
public static var screenRoomDetailsInvitePeopleTitle: String { return L10n.tr("Localizable", "screen_room_details_invite_people_title") }
/// Leave room
public static var screenRoomDetailsLeaveRoomTitle: String { return L10n.tr("Localizable", "screen_room_details_leave_room_title") }
/// Notification
/// Notifications
public static var screenRoomDetailsNotificationTitle: String { return L10n.tr("Localizable", "screen_room_details_notification_title") }
/// People
public static var screenRoomDetailsPeopleTitle: String { return L10n.tr("Localizable", "screen_room_details_people_title") }
@ -850,6 +850,20 @@ public enum L10n {
public static var screenWaitlistTitle: String { return L10n.tr("Localizable", "screen_waitlist_title") }
/// You're in.
public static var screenWaitlistTitleSuccess: String { return L10n.tr("Localizable", "screen_waitlist_title_success") }
/// Calls, location sharing, search and more will be added later this year.
public static var screenWelcomeBullet1: String { return L10n.tr("Localizable", "screen_welcome_bullet_1") }
/// Message history for encrypted rooms wont be available in this update.
public static var screenWelcomeBullet2: String { return L10n.tr("Localizable", "screen_welcome_bullet_2") }
/// Wed love to hear from you, let us know what you think via the settings page.
public static var screenWelcomeBullet3: String { return L10n.tr("Localizable", "screen_welcome_bullet_3") }
/// Let's go!
public static var screenWelcomeButton: String { return L10n.tr("Localizable", "screen_welcome_button") }
/// Heres what you need to know:
public static var screenWelcomeSubtitle: String { return L10n.tr("Localizable", "screen_welcome_subtitle") }
/// Welcome to %1$@!
public static func screenWelcomeTitle(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_welcome_title", String(describing: p1))
}
/// Looks like youre using a new device. Verify with another device to access your encrypted messages moving forwards.
public static var sessionVerificationBannerMessage: String { return L10n.tr("Localizable", "session_verification_banner_message") }
/// Verify its you

View File

@ -58,17 +58,32 @@ struct AnalyticsPromptScreen: View {
.foregroundColor(.compound.textSecondary)
}
}
@ViewBuilder
private var checkMark: some View {
Image(systemName: "checkmark.circle")
.symbolVariant(.fill)
.symbolRenderingMode(.palette)
.foregroundStyle(Color.compound.iconAccentTertiary, Color.compound.textOnSolidPrimary)
}
/// The list of re-assurances about analytics.
private var checkmarkList: some View {
VStack(alignment: .leading, spacing: 4) {
AnalyticsPromptScreenCheckmarkItem(title: context.viewState.strings.point1, listPosition: .top)
AnalyticsPromptScreenCheckmarkItem(title: context.viewState.strings.point2, listPosition: .middle)
AnalyticsPromptScreenCheckmarkItem(title: context.viewState.strings.point3, listPosition: .bottom)
checkMarkItem(title: context.viewState.strings.point1, position: .top)
checkMarkItem(title: context.viewState.strings.point2, position: .middle)
checkMarkItem(title: context.viewState.strings.point3, position: .bottom)
}
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity)
}
@ViewBuilder
private func checkMarkItem(title: String, position: ListPosition) -> some View {
RoundedLabelItem(title: title, listPosition: position) {
checkMark
}
}
/// The stack of enable/disable buttons.
private var buttons: some View {

View File

@ -16,33 +16,31 @@
import SwiftUI
struct AnalyticsPromptScreenCheckmarkItem: View {
/// Represents the position of a checkmark item in a list.
enum ListPosition {
case top, middle, bottom
/// The corners that should be rounded for this position.
var roundedCorners: UIRectCorner {
switch self {
case .top:
return [.topLeft, .topRight]
case .middle:
return []
case .bottom:
return [.bottomLeft, .bottomRight]
}
/// Represents the position of a checkmark item in a list.
enum ListPosition {
case top, middle, bottom
/// The corners that should be rounded for this position.
var roundedCorners: UIRectCorner {
switch self {
case .top:
return [.topLeft, .topRight]
case .middle:
return []
case .bottom:
return [.bottomLeft, .bottomRight]
}
}
}
struct RoundedLabelItem<Icon: View>: View {
let title: String
let listPosition: ListPosition
let iconContent: () -> Icon
var body: some View {
Label { Text(title) } icon: {
Image(systemName: "checkmark.circle")
.symbolVariant(.fill)
.symbolRenderingMode(.palette)
.foregroundStyle(Color.compound.iconAccentTertiary, Color.compound.textOnSolidPrimary)
iconContent()
}
.labelStyle(CheckmarkLabelStyle())
.padding(.horizontal, 20)
@ -68,14 +66,32 @@ private struct CheckmarkLabelStyle: LabelStyle {
struct AnalyticsPromptScreenCheckmarkItem_Previews: PreviewProvider {
static let strings = AnalyticsPromptScreenStrings(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
@ViewBuilder
static var testImage1: some View {
Image(systemName: "circle")
}
@ViewBuilder
static var testImage2: some View {
Image(systemName: "square")
}
static var previews: some View {
VStack(alignment: .leading, spacing: 4) {
AnalyticsPromptScreenCheckmarkItem(title: strings.point1, listPosition: .top)
AnalyticsPromptScreenCheckmarkItem(title: strings.point2, listPosition: .middle)
AnalyticsPromptScreenCheckmarkItem(title: "This is a short string.", listPosition: .middle)
AnalyticsPromptScreenCheckmarkItem(title: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.",
listPosition: .bottom)
RoundedLabelItem(title: strings.point1, listPosition: .top) {
testImage1
}
RoundedLabelItem(title: strings.point2, listPosition: .middle) {
testImage2
}
RoundedLabelItem(title: "This is a short string.", listPosition: .middle) {
testImage1
}
RoundedLabelItem(title: "This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.",
listPosition: .bottom) {
testImage2
}
}
.padding()
}

View File

@ -29,7 +29,9 @@ struct UIConstants {
/// The padding used to the top of the view for breaker screens that don't have a navigation bar.
static let onboardingBreakerScreenTopPadding: CGFloat = 80
static let welcomeScreenTopPadding: CGFloat = 122
/// The height to use for top/bottom spacers to pad the views to fit the `maxContentHeight`.
static func spacerHeight(in geometry: GeometryProxy) -> CGFloat {
max(0, (geometry.size.height - maxContentHeight) / 2)

View File

@ -0,0 +1,108 @@
//
// 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 WelcomeScreen: View {
@ScaledMetric var iconSize = 20
@ObservedObject var context: WelcomeScreenScreenViewModel.Context
var body: some View {
FullscreenDialog(topPadding: UIConstants.welcomeScreenTopPadding) {
mainContent
} bottomContent: {
button
}
.background(OnboardingBackgroundImage())
.environment(\.backgroundStyle, AnyShapeStyle(Color.clear))
.onAppear {
context.send(viewAction: .appeared)
}
}
@ViewBuilder
private var mainContent: some View {
VStack(spacing: 42) {
header
list
}
}
@ViewBuilder
private var header: some View {
VStack(spacing: 32) {
Image(asset: Asset.Images.launchLogo)
.resizable()
.scaledToFit()
.frame(width: 118, height: 118)
.accessibilityHidden(true)
title
}
}
@ViewBuilder
private var title: some View {
VStack(spacing: 12) {
Text(context.viewState.title)
.font(Font.compound.headingLGBold)
.foregroundColor(Color.compound.textPrimary)
.multilineTextAlignment(.center)
Text(context.viewState.subtitle)
.font(Font.compound.bodyMD)
.foregroundColor(Color.compound.textPrimary)
.multilineTextAlignment(.center)
}
}
private var list: some View {
VStack(alignment: .leading, spacing: 4) {
RoundedLabelItem(title: context.viewState.bullet1, listPosition: .top) {
Image(systemName: "exclamationmark.transmission")
.foregroundColor(.compound.iconSecondary)
}
RoundedLabelItem(title: context.viewState.bullet2, listPosition: .middle) {
Image(systemName: "lock")
.foregroundColor(.compound.iconSecondary)
}
RoundedLabelItem(title: context.viewState.bullet3, listPosition: .bottom) {
Image(systemName: "plus.bubble")
.foregroundColor(.compound.iconSecondary)
}
}
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity)
}
@ViewBuilder
private var button: some View {
Button {
context.send(viewAction: .doneTapped)
} label: {
Text(context.viewState.buttonTitle)
}
.buttonStyle(.elementAction(.xLarge))
}
}
// MARK: - Previews
struct WelcomeScreen_Previews: PreviewProvider {
static let viewModel = WelcomeScreenScreenViewModel()
static var previews: some View {
WelcomeScreen(context: viewModel.context)
}
}

View File

@ -0,0 +1,51 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import SwiftUI
enum WelcomeScreenScreenCoordinatorAction {
case dismiss
}
final class WelcomeScreenScreenCoordinator: CoordinatorProtocol {
private var viewModel: WelcomeScreenScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<WelcomeScreenScreenCoordinatorAction, Never> = .init()
private var cancellables: Set<AnyCancellable> = .init()
var actions: AnyPublisher<WelcomeScreenScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init() {
viewModel = WelcomeScreenScreenViewModel()
}
func start() {
viewModel.actions.sink { [weak self] action in
guard let self else { return }
switch action {
case .dismiss:
actionsSubject.send(.dismiss)
}
}
.store(in: &cancellables)
}
func toPresentable() -> AnyView {
AnyView(WelcomeScreen(context: viewModel.context))
}
}

View File

@ -0,0 +1,35 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
enum WelcomeScreenScreenViewModelAction {
case dismiss
}
struct WelcomeScreenScreenViewState: BindableState {
let title = L10n.screenWelcomeTitle(InfoPlistReader.main.bundleDisplayName)
let subtitle = L10n.screenWelcomeSubtitle
let bullet1 = L10n.screenWelcomeBullet1
let bullet2 = L10n.screenWelcomeBullet2
let bullet3 = L10n.screenWelcomeBullet3
let buttonTitle = L10n.screenWelcomeButton
}
enum WelcomeScreenScreenViewAction {
case doneTapped
case appeared
}

View File

@ -0,0 +1,45 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import SwiftUI
typealias WelcomeScreenScreenViewModelType = StateStoreViewModel<WelcomeScreenScreenViewState, WelcomeScreenScreenViewAction>
class WelcomeScreenScreenViewModel: WelcomeScreenScreenViewModelType, WelcomeScreenScreenViewModelProtocol {
let appSettings: AppSettings
private var actionsSubject: PassthroughSubject<WelcomeScreenScreenViewModelAction, Never> = .init()
var actions: AnyPublisher<WelcomeScreenScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(appSettings: AppSettings = ServiceLocator.shared.settings) {
self.appSettings = appSettings
super.init(initialViewState: WelcomeScreenScreenViewState())
}
// MARK: - Public
override func process(viewAction: WelcomeScreenScreenViewAction) {
switch viewAction {
case .doneTapped:
actionsSubject.send(.dismiss)
case .appeared:
appSettings.hasShownWelcomeScreen = true
}
}
}

View File

@ -0,0 +1,23 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
@MainActor
protocol WelcomeScreenScreenViewModelProtocol {
var actions: AnyPublisher<WelcomeScreenScreenViewModelAction, Never> { get }
var context: WelcomeScreenScreenViewModelType.Context { get }
}

View File

@ -0,0 +1,21 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import ElementX
import XCTest
@MainActor
class WelcomeScreenScreenUITests: XCTestCase { }

View File

@ -0,0 +1,22 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import XCTest
@testable import ElementX
@MainActor
class WelcomeScreenScreenViewModelTests: XCTestCase { }

View File

@ -0,0 +1 @@
Added a welcome screen that will appear only once.