diff --git a/DesignKit/Sources/Colors/ElementColors.swift b/DesignKit/Sources/Colors/ElementColors.swift index 6a2139103..b5a4138c9 100644 --- a/DesignKit/Sources/Colors/ElementColors.swift +++ b/DesignKit/Sources/Colors/ElementColors.swift @@ -49,7 +49,7 @@ public struct ElementColors { public var system: Color { colors.system } @available(swift, obsoleted: 5.0, message: "Use bgCanvasDefault from Compound for backgrounds. For text or icons use textOnSolidPrimary/iconOnSolidPrimary.") public var background: Color { colors.background } - @available(swift, obsoleted: 5.0, message: "Use textActionAccent/iconAccentTertiary from Compound.") + @available(swift, deprecated: 5.0, message: "Use textActionAccent/iconAccentTertiary from Compound.") public var brand: Color { colors.accent } public var contentAndAvatars: [Color] { colors.contentAndAvatars } diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 077502ca6..2fdbfdd09 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -211,6 +211,7 @@ 5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; }; 516534FC5C893D57F169D5A8 /* MapTilerGeocoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33720F7AD25E85E4A84669E8 /* MapTilerGeocoding.swift */; }; 518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; }; + 51C240F4660F7269203A9B3A /* MigrationScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75910F5A36EA8FF9BAD08D18 /* MigrationScreenUITests.swift */; }; 520EEDAFBC778AB0B41F2F53 /* ClientMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADE6170EFE6A161B0A68AB61 /* ClientMock.swift */; }; 5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; }; 53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; }; @@ -253,6 +254,7 @@ 6298AB0906DDD3525CD78C6B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; }; 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; }; 6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F36C5D9B37E50915ECBD3EE /* RoomMemberProxy.swift */; }; + 644AA5001BCC58D7732EB772 /* MigrationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12EDAFB64FA5F6812D54F39A /* MigrationScreenViewModel.swift */; }; 64C373ACCFA26D42BA45CFAD /* HomeScreenInvitesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */; }; 64D05250CEDE8B604119F6E6 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981663D961C94270FA035FD0 /* Alert.swift */; }; 64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1941C8817E6B6971BA4415F5 /* VideoRoomTimelineView.swift */; }; @@ -287,6 +289,7 @@ 6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */; }; 6EC7A40A537CFB3D526A111C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; }; 6F2AB43A1EFAD8A97AF41A15 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; }; + 6F2D5D4F2590310DFAE973E4 /* WaitingDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6D698BFD68B061350553930 /* WaitingDialog.swift */; }; 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */; }; 6FF51EB400DBA0668FC38B97 /* TimelineStartRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */; }; 702694459B649B9D3A3C34F8 /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9212AE02CBDD692C56A879F /* TimelineTableViewController.swift */; }; @@ -402,6 +405,7 @@ 9586E90A447C4896C0CA3A8E /* TimelineItemReplyDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE89A8BD65CCE3FCC925CA14 /* TimelineItemReplyDetails.swift */; }; 962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */; }; 964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; }; + 968823C9DBF3062729413EBF /* MigrationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3DF0BFE5637EA42F5651FE8 /* MigrationScreenCoordinator.swift */; }; 968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; }; 97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; }; 978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; }; @@ -429,6 +433,7 @@ 9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4C37E9F0E53D3DF951AC /* HomeScreenUITests.swift */; }; 9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */; }; 9DD84E014ADFB2DD813022D5 /* RoomDetailsEditScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E5B2CBEF8F96424F095508 /* RoomDetailsEditScreenViewModelTests.swift */; }; + 9DE98D3EC47742A0F9F9EC3C /* MigrationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5685139D0B72BED3503EFCC /* MigrationScreen.swift */; }; 9DF3F6318A4402305F5EB869 /* AnalyticsPromptScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F8002D0392A476D2758B291 /* AnalyticsPromptScreen.swift */; }; 9E838A62918E47BC72D6640D /* UserIndicatorPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB54B4F94686CCF0289B72F /* UserIndicatorPresenter.swift */; }; 9EBDC79CAC9B63A0D626E333 /* LegalInformationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */; }; @@ -497,6 +502,7 @@ B402708F8728DD0DB7C324E2 /* StartChatScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */; }; B444F9C184A377C1B481F07F /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; }; B45F20A1C3F1CE19D5B8BA74 /* InvitesScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F61A0DD8243B395499C99A2 /* InvitesScreenUITests.swift */; }; + B46EBC7B96CCB64FF8E110DC /* MigrationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CD0FAE9EA761DA175D31CC7 /* MigrationScreenModels.swift */; }; B4A0C69370E6008A971463E7 /* BugReportScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C89820BB2B88D4EA28131C /* BugReportScreenViewModelProtocol.swift */; }; B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; }; B5479997ECC516C121E6625E /* LocationMarkerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFECCE59967018204876D0A5 /* LocationMarkerView.swift */; }; @@ -553,6 +559,7 @@ C4FE0E11A907C8999F92D5A8 /* TimelineStartRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */; }; C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; }; C58E305C380D3ADDF7912180 /* StickerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 818695BED971753243FEF897 /* StickerRoomTimelineItem.swift */; }; + C5946E4A3D4295F002F0B3DC /* MigrationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46B59EC4B0C93254089EAACB /* MigrationScreenViewModelTests.swift */; }; C5A07E2D88BE7D51DCECD166 /* LoginScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D0B159AFFBBD8ECFD0E37FA /* LoginScreenModels.swift */; }; C6136E848E55D2C86BF760F5 /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */; }; C67FCC854F3A6FC7A2EC04D0 /* MediaUploadPreviewScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70C86696AC9521F8ED88FBEB /* MediaUploadPreviewScreen.swift */; }; @@ -683,6 +690,7 @@ F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */; }; F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; }; F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; }; + F692D4AF571333C0D785725A /* MigrationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBBC5E7C0F8337D2A46EB2DD /* MigrationScreenViewModelProtocol.swift */; }; F697284B9B5F2C00CFEA3B12 /* EmojiDetectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */; }; F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; }; F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; }; @@ -814,6 +822,7 @@ 1222DB76B917EB8A55365BA5 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 127A57D053CE8C87B5EFB089 /* Consumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumable.swift; sourceTree = ""; }; 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = ""; }; + 12EDAFB64FA5F6812D54F39A /* MigrationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenViewModel.swift; sourceTree = ""; }; 12F1E7F9C2BE8BB751037826 /* WaitlistScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenCoordinator.swift; sourceTree = ""; }; 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = ""; }; 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = ""; }; @@ -945,6 +954,7 @@ 45CDF9A107BFE6C79B58D6B5 /* RoomMembersListScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModelProtocol.swift; sourceTree = ""; }; 45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationCenterProtocol.swift; sourceTree = ""; }; 466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenModels.swift; sourceTree = ""; }; + 46B59EC4B0C93254089EAACB /* MigrationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenViewModelTests.swift; sourceTree = ""; }; 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsAppCoordinator.swift; sourceTree = ""; }; 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = ""; }; 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineView.swift; sourceTree = ""; }; @@ -1001,6 +1011,7 @@ 5AEA0B743847CFA5B3C38EE4 /* RoomMembersListScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenCoordinator.swift; sourceTree = ""; }; 5B8F0ED874DF8C9A51B0AB6F /* SettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenCoordinator.swift; sourceTree = ""; }; 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenCoordinator.swift; sourceTree = ""; }; + 5CD0FAE9EA761DA175D31CC7 /* MigrationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenModels.swift; sourceTree = ""; }; 5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = ""; }; 5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinatorUITests.swift; sourceTree = ""; }; 5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiDetection.swift; sourceTree = ""; }; @@ -1069,6 +1080,7 @@ 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineItemContent.swift; sourceTree = ""; }; 748AE77AC3B0A01223033B87 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 74DD0855F2F76D47E5555082 /* MediaUploadPreviewScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenCoordinator.swift; sourceTree = ""; }; + 75910F5A36EA8FF9BAD08D18 /* MigrationScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenUITests.swift; sourceTree = ""; }; 772334731A8BF8E6D90B194D /* LocationRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineView.swift; sourceTree = ""; }; 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModelProtocol.swift; sourceTree = ""; }; 78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinatorTests.swift; sourceTree = ""; }; @@ -1167,6 +1179,7 @@ A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryPicker.swift; sourceTree = ""; }; A2AC3C656E960E15B5905E05 /* UnsupportedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineView.swift; sourceTree = ""; }; A34A814CBD56230BC74FFCF4 /* MXLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXLogger.swift; sourceTree = ""; }; + A3DF0BFE5637EA42F5651FE8 /* MigrationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenCoordinator.swift; sourceTree = ""; }; A3FBD9C2B9A5479526920399 /* BugReportScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenCoordinator.swift; sourceTree = ""; }; A40C19719687984FD9478FBE /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; A433BE28B40D418237BE37B5 /* ReportContentScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreen.swift; sourceTree = ""; }; @@ -1310,6 +1323,7 @@ D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSourceProxy.swift; sourceTree = ""; }; D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; D54E12B98252F6C527E31FEE /* MediaUploadPreviewScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelProtocol.swift; sourceTree = ""; }; + D5685139D0B72BED3503EFCC /* MigrationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreen.swift; sourceTree = ""; }; D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = ""; }; D5E26C54362206BBDD096D83 /* test_audio.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = test_audio.mp3; sourceTree = ""; }; D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = ""; }; @@ -1358,6 +1372,7 @@ E9D059BFE329BE09B6D96A9F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = ""; }; E9DFC0FBA0FC6FC4DC0FC9FC /* NSESettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESettings.swift; sourceTree = ""; }; EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; + EBBC5E7C0F8337D2A46EB2DD /* MigrationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationScreenViewModelProtocol.swift; sourceTree = ""; }; EC589E641AE46EFB2962534D /* RoomMemberDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModelTests.swift; sourceTree = ""; }; ECB08484CD5D77C9BF97AA78 /* WaitlistScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenUITests.swift; sourceTree = ""; }; ECD5FCBA169B6A82F501CA1B /* AnalyticsSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1386,6 +1401,7 @@ F506C6ADB1E1DA6638078E11 /* UITests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; F5311C989EC15B4C2D699025 /* StaticLocationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenViewModel.swift; sourceTree = ""; }; F57C8022B8A871A1DCD1750A /* UserIndicatorToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorToastView.swift; sourceTree = ""; }; + F6D698BFD68B061350553930 /* WaitingDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingDialog.swift; sourceTree = ""; }; F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.swift; sourceTree = ""; }; F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = ""; }; F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreen.swift; sourceTree = ""; }; @@ -2372,6 +2388,7 @@ AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */, 03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */, 6F6E6EDC4BBF962B2ED595A4 /* MessageForwardingScreenViewModelTests.swift */, + 46B59EC4B0C93254089EAACB /* MigrationScreenViewModelTests.swift */, F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */, 78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */, 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */, @@ -2495,6 +2512,14 @@ path = View; sourceTree = ""; }; + 79A3F9F48E7D5B189A63BACB /* View */ = { + isa = PBXGroup; + children = ( + D5685139D0B72BED3503EFCC /* MigrationScreen.swift */, + ); + path = View; + sourceTree = ""; + }; 79E560F5113ED25D172E550C /* Media */ = { isa = PBXGroup; children = ( @@ -2606,6 +2631,18 @@ path = MediaPickerScreen; sourceTree = ""; }; + 888C38D172A591977316DB1E /* MigrationScreen */ = { + isa = PBXGroup; + children = ( + A3DF0BFE5637EA42F5651FE8 /* MigrationScreenCoordinator.swift */, + 5CD0FAE9EA761DA175D31CC7 /* MigrationScreenModels.swift */, + 12EDAFB64FA5F6812D54F39A /* MigrationScreenViewModel.swift */, + EBBC5E7C0F8337D2A46EB2DD /* MigrationScreenViewModelProtocol.swift */, + 79A3F9F48E7D5B189A63BACB /* View */, + ); + path = MigrationScreen; + sourceTree = ""; + }; 8A9C09B6A392465E03B8D1B1 /* IntegrationTests */ = { isa = PBXGroup; children = ( @@ -2712,6 +2749,7 @@ 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */, 39B6C8690AEA1E49FF1BAF95 /* MediaUploadPreviewScreenUITests.swift */, 59846FA04E1DBBFDD8829C2A /* MessageForwardingScreenUITests.swift */, + 75910F5A36EA8FF9BAD08D18 /* MigrationScreenUITests.swift */, 0C88046D6A070D9827181C4D /* OnboardingUITests.swift */, 4132F882A984ED971338EE9D /* ReportContentScreenUITests.swift */, 122186B7CD1BC46A9C629DD9 /* RoomDetailsEditScreenUITests.swift */, @@ -2768,6 +2806,7 @@ 9F4A1E90C924DE7954BA5005 /* View */ = { isa = PBXGroup; children = ( + F6D698BFD68B061350553930 /* WaitingDialog.swift */, 80C4927D09099497233E9980 /* WaitlistScreen.swift */, ); path = View; @@ -3258,6 +3297,7 @@ 87E2774157D9C4894BCFF3F8 /* MediaPickerScreen */, 23605DD08620BE6558242469 /* MediaUploadPreviewScreen */, 3348D14DBDB54E72FC67E2F3 /* MessageForwardingScreen */, + 888C38D172A591977316DB1E /* MigrationScreen */, 3F38EAC92E2281990E65DAF2 /* OnboardingScreen */, A448A3A8F764174C60CD0CA1 /* Other */, 5970F275D6014548DCED6106 /* ReportContentScreen */, @@ -3915,6 +3955,7 @@ B9A8C34A00D03094C0CF56F3 /* MediaUploadPreviewScreenViewModelTests.swift in Sources */, 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */, F777C6FEE7D106136E2ED2B2 /* MessageForwardingScreenViewModelTests.swift in Sources */, + C5946E4A3D4295F002F0B3DC /* MigrationScreenViewModelTests.swift in Sources */, 4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */, 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */, DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */, @@ -4196,6 +4237,11 @@ 695825D20A761C678809345D /* MessageForwardingScreenModels.swift in Sources */, F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */, C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */, + 9DE98D3EC47742A0F9F9EC3C /* MigrationScreen.swift in Sources */, + 968823C9DBF3062729413EBF /* MigrationScreenCoordinator.swift in Sources */, + B46EBC7B96CCB64FF8E110DC /* MigrationScreenModels.swift in Sources */, + 644AA5001BCC58D7732EB772 /* MigrationScreenViewModel.swift in Sources */, + F692D4AF571333C0D785725A /* MigrationScreenViewModelProtocol.swift in Sources */, 152AE2B8650FB23AFD2E28B9 /* MockAuthenticationServiceProxy.swift in Sources */, EE4F5601356228FF72FC56B6 /* MockClientProxy.swift in Sources */, B659E3A49889E749E3239EA7 /* MockMediaProvider.swift in Sources */, @@ -4439,6 +4485,7 @@ 1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */, 64F43D7390DA2A0AFD6BA911 /* VideoRoomTimelineView.swift in Sources */, 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */, + 6F2D5D4F2590310DFAE973E4 /* WaitingDialog.swift in Sources */, 9AFEE46B03B7E995B3E1A53D /* WaitlistScreen.swift in Sources */, 7C384A8E54A4B60A14CDE8E5 /* WaitlistScreenCoordinator.swift in Sources */, 2F66701B15657A87B4AC3A0A /* WaitlistScreenModels.swift in Sources */, @@ -4467,6 +4514,7 @@ 5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */, 7FB0BDE26838F1A92782D5E1 /* MediaUploadPreviewScreenUITests.swift in Sources */, 6713835120D94BAA8ED7E3E5 /* MessageForwardingScreenUITests.swift in Sources */, + 51C240F4660F7269203A9B3A /* MigrationScreenUITests.swift in Sources */, 6B15FF984906AAFCF9DC4F58 /* OnboardingUITests.swift in Sources */, BA0D3DDCEDD97502DAC4B6E9 /* ReportContentScreenUITests.swift in Sources */, F16109A6F6DF03DA26D59233 /* RoomDetailsEditScreenUITests.swift in Sources */, @@ -4750,7 +4798,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.1.6; + MARKETING_VERSION = 1.1.7; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -4825,7 +4873,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.4; KEYCHAIN_ACCESS_GROUP_IDENTIFIER = "$(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER)"; MACOSX_DEPLOYMENT_TARGET = 13.3; - MARKETING_VERSION = 1.1.6; + MARKETING_VERSION = 1.1.7; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; diff --git a/ElementX.xcodeproj/xcshareddata/xcschemes/ElementX.xcscheme b/ElementX.xcodeproj/xcshareddata/xcschemes/ElementX.xcscheme index 82cbe2450..af399e45d 100644 --- a/ElementX.xcodeproj/xcshareddata/xcschemes/ElementX.xcscheme +++ b/ElementX.xcodeproj/xcshareddata/xcschemes/ElementX.xcscheme @@ -97,6 +97,11 @@ value = "" isEnabled = "NO"> + + = .init() + private var migrationCancellable: AnyCancellable? private let sidebarNavigationStackCoordinator: NavigationStackCoordinator private let detailNavigationStackCoordinator: NavigationStackCoordinator @@ -43,12 +45,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { init(userSession: UserSessionProtocol, navigationSplitCoordinator: NavigationSplitCoordinator, bugReportService: BugReportServiceProtocol, - roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol) { + roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + appSettings: AppSettings) { stateMachine = UserSessionFlowCoordinatorStateMachine() self.userSession = userSession self.navigationSplitCoordinator = navigationSplitCoordinator self.bugReportService = bugReportService self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.appSettings = appSettings sidebarNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) detailNavigationStackCoordinator = NavigationStackCoordinator(navigationSplitCoordinator: navigationSplitCoordinator) @@ -78,7 +82,13 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { } func start() { - stateMachine.processEvent(.start) + if appSettings.migratedAccounts[userSession.userID] != true { + // Show the migration screen for a new account. + stateMachine.processEvent(.startWithMigration) + } else { + // Otherwise go straight to the home screen. + stateMachine.processEvent(.start) + } } func stop() { } @@ -90,13 +100,17 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // MARK: - FlowCoordinatorProtocol func handleAppRoute(_ appRoute: AppRoute, animated: Bool) { + // Tidy up any state before applying the new route. switch stateMachine.state { + case .initial, .migration: + return // Not ready to handle a route. + case .roomList: + break // Nothing to tidy up on the home screen. case .feedbackScreen, .sessionVerificationScreen, .settingsScreen, .startChatScreen, .invitesScreen: navigationSplitCoordinator.setSheetCoordinator(nil, animated: animated) - case .roomList, .initial: - break } - + + // Apply the route. switch appRoute { case .room, .roomDetails, .roomList: roomFlowCoordinator.handleAppRoute(appRoute, animated: animated) @@ -122,6 +136,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { switch (context.fromState, context.event, context.toState) { case (.initial, .start, .roomList): self.presentHomeScreen() + + case (.initial, .startWithMigration, .migration): + self.presentMigrationScreen() // Full screen cover + self.presentHomeScreen() // Have the home screen ready to show underneath + case (.migration, .completeMigration, .roomList): + self.dismissMigrationScreen() case(.roomList, .selectRoom, .roomList): break @@ -179,6 +199,25 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { } } + private func presentMigrationScreen() { + // Listen for the first sync to finish. + migrationCancellable = userSession.clientProxy.callbacks + .receive(on: DispatchQueue.main) + .sink { [weak self] callback in + guard let self, stateMachine.state == .migration, case .receivedSyncUpdate = callback else { return } + migrationCancellable = nil + appSettings.migratedAccounts[userSession.userID] = true + stateMachine.processEvent(.completeMigration) + } + + let coordinator = MigrationScreenCoordinator() + navigationSplitCoordinator.setFullScreenCoverCoordinator(coordinator) + } + + private func dismissMigrationScreen() { + navigationSplitCoordinator.setFullScreenCoverCoordinator(nil) + } + // swiftlint:disable:next cyclomatic_complexity private func presentHomeScreen() { let parameters = HomeScreenCoordinatorParameters(userSession: userSession, diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift index 0f6d5ee75..d3b4efdac 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinatorStateMachine.swift @@ -23,6 +23,9 @@ class UserSessionFlowCoordinatorStateMachine { /// The initial state, used before the coordinator starts case initial + /// Showing the migration screen whilst the proxy performs an initial sync. + case migration + /// Showing the home screen. The `selectedRoomId` represents the timeline shown on the detail panel (if any) case roomList(selectedRoomId: String?) @@ -51,6 +54,11 @@ class UserSessionFlowCoordinatorStateMachine { /// Start the user session flows case start + /// Start the user session flows with a migration screen. + case startWithMigration + /// Request to transition from the migration state to the home screen. + case completeMigration + /// Request presentation for a particular room /// - Parameter roomId:the room identifier case selectRoom(roomId: String) @@ -97,7 +105,9 @@ class UserSessionFlowCoordinatorStateMachine { // swiftlint:disable:next cyclomatic_complexity private func configure() { stateMachine.addRoutes(event: .start, transitions: [.initial => .roomList(selectedRoomId: nil)]) - + stateMachine.addRoutes(event: .startWithMigration, transitions: [.initial => .migration]) + stateMachine.addRoutes(event: .completeMigration, transitions: [.migration => .roomList(selectedRoomId: nil)]) + stateMachine.addRouteMapping { event, fromState, _ in switch (event, fromState) { case (.selectRoom(let roomId), .roomList): diff --git a/ElementX/Sources/Generated/Assets.swift b/ElementX/Sources/Generated/Assets.swift index 2d31bc4db..b802fa44f 100644 --- a/ElementX/Sources/Generated/Assets.swift +++ b/ElementX/Sources/Generated/Assets.swift @@ -40,6 +40,7 @@ internal enum Asset { internal static let locationMarker = ImageAsset(name: "images/location-marker") internal static let locationPin = ImageAsset(name: "images/location-pin") internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message") + internal static let waitingGradient = ImageAsset(name: "images/waiting-gradient") } } // swiftlint:enable identifier_name line_length nesting type_body_length type_name diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index 5e4bb0b04..598cc381c 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -644,6 +644,10 @@ public enum L10n { public static var screenMediaUploadPreviewErrorFailedProcessing: String { return L10n.tr("Localizable", "screen_media_upload_preview_error_failed_processing") } /// Failed uploading media, please try again. public static var screenMediaUploadPreviewErrorFailedSending: String { return L10n.tr("Localizable", "screen_media_upload_preview_error_failed_sending") } + /// This is a one time process, thanks for waiting. + public static var screenMigrationMessage: String { return L10n.tr("Localizable", "screen_migration_message") } + /// Setting up your account. + public static var screenMigrationTitle: String { return L10n.tr("Localizable", "screen_migration_title") } /// Sign in manually public static var screenOnboardingSignInManually: String { return L10n.tr("Localizable", "screen_onboarding_sign_in_manually") } /// Sign in with QR code @@ -832,13 +836,13 @@ public enum L10n { public static func screenWaitlistMessage(_ p1: Any, _ p2: Any) -> String { return L10n.tr("Localizable", "screen_waitlist_message", String(describing: p1), String(describing: p2)) } - /// Welcome to %1$@ + /// Welcome to %1$@! public static func screenWaitlistMessageSuccess(_ p1: Any) -> String { return L10n.tr("Localizable", "screen_waitlist_message_success", String(describing: p1)) } - /// You're on the waitlist! + /// You’re almost there. public static var screenWaitlistTitle: String { return L10n.tr("Localizable", "screen_waitlist_title") } - /// You're in! + /// You're in. public static var screenWaitlistTitleSuccess: String { return L10n.tr("Localizable", "screen_waitlist_title_success") } /// Looks like you’re using a new device. Verify it’s you to access your encrypted messages. public static var sessionVerificationBannerMessage: String { return L10n.tr("Localizable", "session_verification_banner_message") } diff --git a/ElementX/Sources/Other/ScrollViewAdapter.swift b/ElementX/Sources/Other/ScrollViewAdapter.swift index 8e356db42..bdb8d395e 100644 --- a/ElementX/Sources/Other/ScrollViewAdapter.swift +++ b/ElementX/Sources/Other/ScrollViewAdapter.swift @@ -84,9 +84,9 @@ class ScrollViewAdapter: NSObject, UIScrollViewDelegate { let velocity = scrollView.panGestureRecognizer.velocity(in: nil) if velocity.y > Constant.scrollDirectionThreshold { - scrollDirectionSubject.send(.down) - } else if velocity.y < -Constant.scrollDirectionThreshold { scrollDirectionSubject.send(.up) + } else if velocity.y < -Constant.scrollDirectionThreshold { + scrollDirectionSubject.send(.down) } } diff --git a/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitingDialog.swift b/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitingDialog.swift new file mode 100644 index 000000000..0501c6fda --- /dev/null +++ b/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitingDialog.swift @@ -0,0 +1,93 @@ +// +// 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 standardised layout for waiting dialogs shown during the onboarding flow. +struct WaitingDialog: View { + @Environment(\.horizontalSizeClass) private var horizontalSizeClass + @Environment(\.verticalSizeClass) private var verticalSizeClass + + /// Not using the default to allow the gradient to fill edge to edge. + private let horizontalPadding = 16.0 + /// The top padding of the content, used to keep the text below the image. + private var contentTopPadding: CGFloat { + if verticalSizeClass == .compact { + // Reduced value for iPhones in landscape. + return UIConstants.onboardingBreakerScreenTopPadding + } else if horizontalSizeClass == .compact { + // The default value for portrait iPhones. + return 2 * UIConstants.onboardingBreakerScreenTopPadding + } else { + // Larger on iPad specifically for 11" in Landscape. + return 2.7 * UIConstants.onboardingBreakerScreenTopPadding + } + } + + /// 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 { + FullscreenDialog(topPadding: UIConstants.onboardingBreakerScreenTopPadding, horizontalPadding: 0) { + content() + .padding(.top, contentTopPadding) + .padding(.horizontal, horizontalPadding) + } bottomContent: { + bottomContent() + .padding(.horizontal, horizontalPadding) + } + .background { + background + .ignoresSafeArea(edges: [.horizontal, .bottom]) + } + .ignoresSafeArea(edges: [.horizontal, .bottom]) + .environment(\.backgroundStyle, AnyShapeStyle(Color.compound.bgCanvasDefault)) + .environment(\.colorScheme, .dark) + .toolbar(.visible, for: .navigationBar) // Layout consistency in all states. + .toolbarBackground(.hidden, for: .navigationBar) + .preferredColorScheme(.light) // Has no effect on Previews, works fine in-app. + } + + var background: some View { + // Negative spacing to hide a rendering bug that shows the white background. + GeometryReader { geometry in + VStack(spacing: -5) { + Color.white + .frame(maxHeight: UIConstants.onboardingBreakerScreenTopPadding) + + Image(asset: Asset.Images.waitingGradient) + .resizable() + .frame(width: horizontalSizeClass == .compact ? nil : geometry.size.width) // Forced for landscape iPhone and nicer iPad. + .scaledToFit() + .layoutPriority(1) // Force for all landscape layouts. + + Color.compound.bgCanvasDefault + } + } + } +} + +struct WaitingDialog_Previews: PreviewProvider { + static let viewModel = WaitlistScreenViewModel(homeserver: .mockMatrixDotOrg) + + static var previews: some View { + NavigationStack { + WaitlistScreen(context: viewModel.context) + } + } +} diff --git a/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitlistScreen.swift b/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitlistScreen.swift index e5813d668..7f224dfc9 100644 --- a/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitlistScreen.swift +++ b/ElementX/Sources/Screens/Authentication/WaitlistScreen/View/WaitlistScreen.swift @@ -20,42 +20,29 @@ struct WaitlistScreen: View { @ObservedObject var context: WaitlistScreenViewModel.Context var body: some View { - FullscreenDialog(topPadding: UIConstants.iconTopPaddingToNavigationBar) { - header + WaitingDialog { + content } bottomContent: { buttons } - .background() - .environment(\.backgroundStyle, AnyShapeStyle(Color.compound.bgCanvasDefault)) .navigationBarBackButtonHidden() .toolbar { toolbar } - .toolbar(.visible, for: .navigationBar) // Layout consistency in all states. - .overlay { - EffectsView(effect: context.viewState.isWaiting ? .none : .confetti) - .ignoresSafeArea() - .allowsHitTesting(false) - } } /// The main content of the view to be shown in a scroll view. - var header: some View { - VStack(spacing: 8) { - AuthenticationIconImage(image: Image(systemName: context.viewState.iconSymbolName)) - .fontWeight(.semibold) - .padding(.bottom, 8) - - Text(context.viewState.title) - .font(.compound.headingMDBold) + var content: some View { + VStack(spacing: 16) { + Text(context.viewState.title.tinting(".", color: .element.brand)) + .font(.compound.headingXLBold) .multilineTextAlignment(.center) .foregroundColor(.compound.textPrimary) .fixedSize(horizontal: false, vertical: true) Text(context.viewState.message) - .font(.compound.bodyMD) + .font(.compound.bodyLG) .multilineTextAlignment(.center) - .foregroundColor(.compound.textSecondary) + .foregroundColor(.compound.textPrimary) } - .padding(.horizontal, 16) } /// The action buttons shown at the bottom of the view. diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index 5da758002..263b09341 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -26,9 +26,17 @@ struct HomeScreen: View { @ObservedObject var context: HomeScreenViewModel.Context @State private var scrollViewAdapter = ScrollViewAdapter() - @State private var showingBottomToolbar = true + @State private var lastScrollDirection: ScrollViewAdapter.ScrollDirection = .up @State private var isSearching = false + var bottomBarVisibility: Visibility { + if lastScrollDirection == .up, context.viewState.roomListMode == .rooms { + return .automatic + } else { + return .hidden + } + } + var body: some View { ScrollView { if context.viewState.showSessionVerificationBanner { @@ -81,7 +89,7 @@ struct HomeScreen: View { } .onReceive(scrollViewAdapter.scrollDirection) { direction in withAnimation(.elementDefault) { - showingBottomToolbar = (direction == .down) + lastScrollDirection = direction } } .onChange(of: context.viewState.visibleRooms) { _ in @@ -100,7 +108,7 @@ struct HomeScreen: View { actions: leaveRoomAlertActions, message: leaveRoomAlertMessage) .navigationTitle(L10n.screenRoomlistMainSpaceTitle) - .toolbar(showingBottomToolbar ? .automatic : .hidden, for: .bottomBar) + .toolbar(bottomBarVisibility, for: .bottomBar) .toolbar { toolbar } .background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .track(screen: .home) @@ -117,6 +125,8 @@ struct HomeScreen: View { ToolbarItemGroup(placement: .bottomBar) { Spacer() newRoomButton + // Fix position animating on loop by getting caught up in the shimmer effect somehow. + .animation(.noAnimation, value: context.viewState.roomListMode) } } diff --git a/ElementX/Sources/Screens/MigrationScreen/MigrationScreenCoordinator.swift b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenCoordinator.swift new file mode 100644 index 000000000..4a0ba77fb --- /dev/null +++ b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenCoordinator.swift @@ -0,0 +1,33 @@ +// +// 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 + +final class MigrationScreenCoordinator: CoordinatorProtocol { + private var viewModel: MigrationScreenViewModelProtocol + private var cancellables: Set = .init() + + init() { + viewModel = MigrationScreenViewModel() + } + + func start() { } + + func toPresentable() -> AnyView { + AnyView(MigrationScreen(context: viewModel.context)) + } +} diff --git a/ElementX/Sources/Screens/MigrationScreen/MigrationScreenModels.swift b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenModels.swift new file mode 100644 index 000000000..4ae20ba77 --- /dev/null +++ b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenModels.swift @@ -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 Foundation + +enum MigrationScreenViewModelAction { } + +struct MigrationScreenViewState: BindableState { } + +enum MigrationScreenViewAction { } diff --git a/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModel.swift b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModel.swift new file mode 100644 index 000000000..2dab324a1 --- /dev/null +++ b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModel.swift @@ -0,0 +1,32 @@ +// +// 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 MigrationScreenViewModelType = StateStoreViewModel + +class MigrationScreenViewModel: MigrationScreenViewModelType, MigrationScreenViewModelProtocol { + private var actionsSubject: PassthroughSubject = .init() + + var actions: AnyPublisher { + actionsSubject.eraseToAnyPublisher() + } + + init() { + super.init(initialViewState: MigrationScreenViewState()) + } +} diff --git a/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModelProtocol.swift b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModelProtocol.swift new file mode 100644 index 000000000..206c66211 --- /dev/null +++ b/ElementX/Sources/Screens/MigrationScreen/MigrationScreenViewModelProtocol.swift @@ -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 MigrationScreenViewModelProtocol { + var actions: AnyPublisher { get } + var context: MigrationScreenViewModelType.Context { get } +} diff --git a/ElementX/Sources/Screens/MigrationScreen/View/MigrationScreen.swift b/ElementX/Sources/Screens/MigrationScreen/View/MigrationScreen.swift new file mode 100644 index 000000000..f32e3cbc5 --- /dev/null +++ b/ElementX/Sources/Screens/MigrationScreen/View/MigrationScreen.swift @@ -0,0 +1,57 @@ +// +// 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 MigrationScreen: View { + @ObservedObject var context: MigrationScreenViewModel.Context + + var body: some View { + WaitingDialog { + content + } bottomContent: { + EmptyView() + } + .navigationBarBackButtonHidden() + } + + var content: some View { + VStack(spacing: 16) { + ProgressView() + .tint(.compound.iconPrimary) + .padding(.bottom, 4) + + Text(L10n.screenMigrationTitle.tinting(".", color: .element.brand)) + .font(.compound.headingXLBold) + .multilineTextAlignment(.center) + .foregroundColor(.compound.textPrimary) + + Text(L10n.screenMigrationMessage) + .font(.compound.bodyLG) + .multilineTextAlignment(.center) + .foregroundColor(.compound.textPrimary) + } + } +} + +// MARK: - Previews + +struct MigrationScreen_Previews: PreviewProvider { + static let viewModel = MigrationScreenViewModel() + static var previews: some View { + MigrationScreen(context: viewModel.context) + } +} diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index c68b98e1f..35e1491af 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -25,6 +25,8 @@ class UserSessionStore: UserSessionStoreProtocol { /// Whether or not there are sessions in the store. var hasSessions: Bool { !keychainController.restorationTokens().isEmpty } + /// All the user IDs managed by the store. + var userIDs: [String] { keychainController.restorationTokens().map(\.userID) } /// The base directory where all session data is stored. let baseDirectory: URL diff --git a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift index ad229843f..be0eefaad 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStoreProtocol.swift @@ -31,6 +31,9 @@ protocol UserSessionStoreProtocol { /// Whether or not there are sessions in the store. var hasSessions: Bool { get } + /// All the user IDs managed by the store. + var userIDs: [String] { get } + /// Returns the location to store user data for a particular username. var baseDirectory: URL { get } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 8b455bed1..05fd34e1c 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -113,6 +113,9 @@ class MockScreen: Identifiable { analytics: ServiceLocator.shared.analytics)) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator + case .migration: + let coordinator = MigrationScreenCoordinator() + return coordinator case .authenticationFlow: let navigationStackCoordinator = NavigationStackCoordinator() let coordinator = AuthenticationCoordinator(authenticationService: MockAuthenticationServiceProxy(), @@ -326,7 +329,8 @@ class MockScreen: Identifiable { let coordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()), navigationSplitCoordinator: navigationSplitCoordinator, bugReportService: BugReportServiceMock(), - roomTimelineControllerFactory: MockRoomTimelineControllerFactory()) + roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), + appSettings: ServiceLocator.shared.settings) coordinator.start() diff --git a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift index 930484f99..668eb20dc 100644 --- a/ElementX/Sources/UITests/UITestsScreenIdentifier.swift +++ b/ElementX/Sources/UITests/UITestsScreenIdentifier.swift @@ -27,6 +27,7 @@ enum UITestsScreenIdentifier: String { case waitlist case analyticsPrompt case analyticsSettingsScreen + case migration case simpleRegular case simpleUpgrade case home diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index 8eb5453de..bfe64c1f1 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -28,6 +28,9 @@ schemes: - variable: UI_TESTS_SCREEN value: "" isEnabled: false + - variable: RESET_APP_SETTINGS + value: "true" + isEnabled: false test: config: Debug gatherCoverageData: true diff --git a/UITests/Sources/MigrationScreenUITests.swift b/UITests/Sources/MigrationScreenUITests.swift new file mode 100644 index 000000000..1aad59650 --- /dev/null +++ b/UITests/Sources/MigrationScreenUITests.swift @@ -0,0 +1,26 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import ElementX +import XCTest + +@MainActor +class MigrationScreenUITests: XCTestCase { + func testRegularScreen() async throws { + let app = Application.launch(.migration) + try await app.assertScreenshot(.migration) + } +} diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.migration.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.migration.png new file mode 100644 index 000000000..9dcbd556e --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.migration.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be0caf4575522004856575896db9e521b132b64ed60e7973be9af121acbbc6eb +size 705140 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.waitlist.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.waitlist.png index 3b8e3fead..c3cbaac65 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.waitlist.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.waitlist.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04a9042d779769d3d5f7d9c405ee98c09d10e7b29503784822a9eb5d65fe1335 -size 86825 +oid sha256:99710fb79e6a3ec46116249dfa10fa7f76e4d260f55ad918706bde3f78e44abb +size 718895 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.migration.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.migration.png new file mode 100644 index 000000000..a9961ab61 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.migration.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb11ec64209cf5ff6ba8c89883179a2b19b1bffc5f4123e475a8854e1a9a35d0 +size 445100 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.waitlist.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.waitlist.png index dc4f23c94..e80a796b4 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.waitlist.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.waitlist.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:06364eaf85691663137f97860abd42306c83fa134160de1f9274ab427d1a7db9 -size 99246 +oid sha256:fc6669feaa5c72bc5eafa0219895c6d7c8ff3d54e77ef41d5b4947016b206a65 +size 465131 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.migration.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.migration.png new file mode 100644 index 000000000..e0f15d49a --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.migration.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:816e426325a232a5d61d062a2e66b949276ade47fd997553f5ed9d97a9113e4a +size 718647 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.waitlist.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.waitlist.png index 9278c580e..1634d42b3 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.waitlist.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.waitlist.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b20644e2113d4ccc24d03a3d5818cb36086c83abf00b7a8f64e35d1002bae6d -size 108622 +oid sha256:e6e2632eb4a30d943acc2859b517854b2c20aa59289d660c91973670b163e584 +size 746877 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.migration.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.migration.png new file mode 100644 index 000000000..45d901c1c --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.migration.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51acefe26737409fe6a0f420eb3482e9614cf95dfd5b5beb62b27e69c1caf0e4 +size 470957 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.waitlist.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.waitlist.png index a8dafe3f7..d0c34697f 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.waitlist.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.waitlist.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:136e986982afb54c76d94b4e413947f2db0ef194b8c9f02508ac397b5dca2b6c -size 138604 +oid sha256:86605b7eb5f4d9fcd48a9ee0cf4e9e6e66a5b07673369392bc8304411d16078b +size 511376 diff --git a/UnitTests/Sources/MigrationScreenViewModelTests.swift b/UnitTests/Sources/MigrationScreenViewModelTests.swift new file mode 100644 index 000000000..816738d44 --- /dev/null +++ b/UnitTests/Sources/MigrationScreenViewModelTests.swift @@ -0,0 +1,24 @@ +// +// 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 MigrationScreenViewModelTests: XCTestCase { + // Nothing to test, the view model has no mutable state. +} diff --git a/changelog.d/983.change b/changelog.d/983.change new file mode 100644 index 000000000..bdbfc037b --- /dev/null +++ b/changelog.d/983.change @@ -0,0 +1 @@ +Show a migration screen on the first use of the app whilst the proxy does an initial sync. \ No newline at end of file diff --git a/project.yml b/project.yml index be8615176..c7c62a116 100644 --- a/project.yml +++ b/project.yml @@ -29,7 +29,7 @@ settings: APP_NAME: ElementX APP_DISPLAY_NAME: Element X KEYCHAIN_ACCESS_GROUP_IDENTIFIER: $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER) - MARKETING_VERSION: 1.1.6 + MARKETING_VERSION: 1.1.7 CURRENT_PROJECT_VERSION: 1 DEVELOPMENT_TEAM: 7J4U792NQT