diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index ff8650701..be0ed4128 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -26,7 +26,7 @@ 072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */; }; 0B1F80C2BF7D223159FBA82C /* ImageAnonymizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6045E825AE900A92D61FEFF0 /* ImageAnonymizerTests.swift */; }; 0C38C3E771B472E27295339D /* SessionVerificationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB9A17AC512A7EF4B106E5 /* SessionVerificationModels.swift */; }; - 0E8C480700870BB34A2A360F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; + 0E8C480700870BB34A2A360F /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4346F63D53A346271577FD9C /* AppAuth */; }; 0EA6537A07E2DC882AEA5962 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 187853A7E643995EE49FAD43 /* Localizable.stringsdict */; }; 0ED951768EC443A8728DE1D7 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */; }; 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; }; @@ -66,9 +66,9 @@ 290FDB0FFDC2F1DDF660343E /* TestMeasurementParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4048041C1A6B20CB97FD18 /* TestMeasurementParser.swift */; }; 297CD0A27C87B0C50FF192EE /* RoomTimelineViewFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEE384418EB1FEDFA62C9CD0 /* RoomTimelineViewFactoryProtocol.swift */; }; 29AEE68A604940180AB9EBFF /* MockRoomSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6BDAC8895AB2B77B47703AE /* MockRoomSummary.swift */; }; - 29EE1791E0AFA1ABB7F23D2F /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; }; + 29EE1791E0AFA1ABB7F23D2F /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */; }; 2A90D9F91A836E30B7D78838 /* MXLogObjcWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 54E438DBCBDC7A41B95DDDD9 /* MXLogObjcWrapper.m */; }; - 2BA59D0AEFB4B82A2EC2A326 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 04C28663564E008DB32B5972 /* Introspect */; }; + 2BA59D0AEFB4B82A2EC2A326 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; }; 2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; }; 2C0CE61E5DC177938618E0B1 /* RootRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90733775209F4D4D366A268F /* RootRouterType.swift */; }; 2E59008365E01F0AFB3A6B24 /* ImageRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BF686BA36D0C2FA3C63DFDF /* ImageRoomMessage.swift */; }; @@ -81,7 +81,7 @@ 3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; }; 313382FC5D38064EAAA35CB2 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D1CC633517D695FEC54208 /* FileManager.swift */; }; 33B4E59D408AE6E02323EE41 /* NoticeRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDA364DFFC3AC71C4771251 /* NoticeRoomMessage.swift */; }; - 33CAC1226DFB8B5D8447D286 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; }; + 33CAC1226DFB8B5D8447D286 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 3853B78FB8531B83936C5DA6 /* SwiftState */; }; 344AF4CBB6D8786214878642 /* NavigationRouterStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D5F812E5AD6DC786DBC9B /* NavigationRouterStoreProtocol.swift */; }; 34966D4C1C2C6D37FE3F7F50 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD2D50A7EAA4FC78417730E /* SettingsCoordinator.swift */; }; 352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */; }; @@ -92,6 +92,7 @@ 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; }; 388FD50AC66E9E684DDFA9D8 /* ServerSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5D2C0950F8196232D88045C /* ServerSelectionScreen.swift */; }; 38C76D586404C1FDED095F3A /* LoginModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B01468022EC826CB2FD2C0 /* LoginModels.swift */; }; + 3A64A93A651A3CB8774ADE8E /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; }; 3B770CB4DED51CC362C66D47 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4990FDBDA96B88E214F92F48 /* SettingsModels.swift */; }; 3C549A0BF39F8A854D45D9FD /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; }; 3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */; }; @@ -108,7 +109,7 @@ 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; }; 4669804D0369FBED4E8625D1 /* ToastViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4470B8CB654B097D807AA713 /* ToastViewPresenter.swift */; }; 490E606044B18985055FF690 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E29F98CF0E960689A410E3 /* SettingsUITests.swift */; }; - 492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; }; + 492274DA6691EE985C2FCCAA /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; }; 499A26EB06C97E48C27A2DB9 /* BuildSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F87116470221880017CF522 /* BuildSettings.swift */; }; 49E9B99CB6A275C7744351F0 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D58333B377888012740101 /* LoginViewModel.swift */; }; 49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; }; @@ -147,7 +148,7 @@ 6647430A45B4A8E692909A8F /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */; }; 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; 67E391A2E00709FB41903B36 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6920A4869821BF72FFC58842 /* MockMediaProvider.swift */; }; - 6832733838C57A7D3FE8FEB5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; }; + 6832733838C57A7D3FE8FEB5 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; }; 684BDE198AE5AA1392288A73 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CE6D4FF64C9A3C18619224 /* SplashScreen.swift */; }; 68AC3C84E2B438036B174E30 /* EmoteRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */; }; 69BCBB4FB2DC3D61A28D3FD8 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */; }; @@ -162,6 +163,7 @@ 706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; }; 72F6E890820FF606A7E276C8 /* SplashScreenPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24A534A4619D8FEFB6439FCC /* SplashScreenPageView.swift */; }; 7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; }; + 744C029EB6C43429926A0499 /* AnalyticsPromptViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */; }; 74604ACFDBE7F54260E7B617 /* ApplicationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */; }; 755727E0B756430DFFEC4732 /* SessionVerificationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF05DA24F71B455E8EFEBC3B /* SessionVerificationViewModelTests.swift */; }; 758BF44CA565AB0AB84F2185 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7109E709A7738E6BCC4553E6 /* Localizable.strings */; }; @@ -185,6 +187,7 @@ 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; }; 7FA4227B2BAAA71560252866 /* UserIndicatorDismissal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D1532B5D9FB0C8461A1453 /* UserIndicatorDismissal.swift */; }; 7FED310F6AB7A70CBFB7C8A3 /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C483956FA3D665E3842E319A /* SettingsScreen.swift */; }; + 8024BE37156FF0A95A7A3465 /* AnalyticsPromptUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */; }; 80E04BE80A89A78FBB4863BB /* UserIndicatorViewPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193FB285430D3956B6E61E4D /* UserIndicatorViewPresentable.swift */; }; 83E5054739949181CA981193 /* LoginCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD667C4BB98CF4F3FE2CE3B0 /* LoginCoordinator.swift */; }; 85AFBB433AD56704A880F8A0 /* FramePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */; }; @@ -212,9 +215,10 @@ 97189E495F0E47805D1868DB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; 9738F894DB1BD383BE05767A /* ElementSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1027BB9A852F445B7623897F /* ElementSettings.swift */; }; 978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; }; + 97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; }; 989029A28C9E2F828AD6658A /* AppIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 16DC8C5B2991724903F1FA6A /* AppIcon.pdf */; }; 992F5E750F5030C4BA2D0D03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; }; - 99ED42B8F8D6BFB1DBCF4C45 /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4346F63D53A346271577FD9C /* AppAuth */; }; + 99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = D661CAB418C075A94306A792 /* AnalyticsEvents */; }; 9AC5F8142413862A9E3A2D98 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 0DD568A494247444A4B56031 /* Kingfisher */; }; 9B8DE1D424E37581C7D99CCC /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC7CCC6DE5FA623E31BA8546 /* RoomTimelineControllerProtocol.swift */; }; 9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; }; @@ -230,11 +234,12 @@ A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; }; A32517FB1CA0BBCE2BC75249 /* BugReportCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6C07DA7D3FF193F7419F55 /* BugReportCoordinator.swift */; }; A371629728E597C5FCA3C2B2 /* Analytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73FC861755C6388F62B9280A /* Analytics.swift */; }; - A4E885358D7DD5A072A06824 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 3853B78FB8531B83936C5DA6 /* SwiftState */; }; + A4E885358D7DD5A072A06824 /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = CCE5BF78B125320CBF3BB834 /* PostHog */; }; A50849766F056FD1DB942DEA /* AlertInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EEB64CC6F3DF5B68736A6B4 /* AlertInfo.swift */; }; A5C8F013ED9FB8AA6FEE18A7 /* InfoPlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A901D95158B02CA96C79C7F /* InfoPlist.swift */; }; A5EC21A071F58FC1229C20D0 /* MemberDetailsProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09747989908EC5E4AA29F844 /* MemberDetailsProviderProtocol.swift */; }; A636D4900E0D98ED91536482 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF3EDF23226895776553F04A /* AppCoordinator.swift */; }; + A663FE6704CB500EBE782AE1 /* AnalyticsPromptCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4DE1CF8F5EFD353B1A5E36F /* AnalyticsPromptCoordinator.swift */; }; A6DEC1ADEC8FEEC206A0FA37 /* AttributedStringBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72F37B5DA798C9AE436F2C2C /* AttributedStringBuilderProtocol.swift */; }; A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; }; A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; }; @@ -245,16 +250,18 @@ ABF3FAB234AD3565B214309B /* TimelineSenderAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */; }; ACF094CF3BF02DBFA6DFDE60 /* AuthenticationCoordinatorUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */; }; B037C365CF8A58A0D149A2DB /* AuthenticationIconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97755C01C3971474EFAD5367 /* AuthenticationIconImage.swift */; }; - B245583C63F8F90357B87FAE /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */; }; + B245583C63F8F90357B87FAE /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 04C28663564E008DB32B5972 /* Introspect */; }; B3357B00F1AA930E54F76609 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; }; B3FDB1D9CF40777695DBBD1D /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9AB74614131D6706894E0C /* AppCoordinatorStateMachine.swift */; }; B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; }; B5111BAF5F601C139EBBD8BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01C4C7DB37597D7D8379511A /* Assets.xcassets */; }; B6DA66EFC13A90846B625836 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 91DE43B8815918E590912DDA /* InfoPlist.strings */; }; B6DF6B6FA8734B70F9BF261E /* BlurHashDecode.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */; }; + B6F92EBE04D4AABF30B9E73A /* AnalyticsPromptModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8BA82CF99D843FEF680E91 /* AnalyticsPromptModels.swift */; }; B80C4FABB5529DF12436FFDA /* AppIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 16DC8C5B2991724903F1FA6A /* AppIcon.pdf */; }; B94368839BDB69172E28E245 /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111B698739E3410E2CDB7144 /* MXLog.swift */; }; BB01CC19C3D3322308D1B2CF /* ServerSelectionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 167521635A1CC27624FCEB7F /* ServerSelectionViewModel.swift */; }; + BB4C6F362F75933DDDE30F3E /* InfoPlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A901D95158B02CA96C79C7F /* InfoPlist.swift */; }; BB6B0B91CE11E06330017000 /* SessionVerificationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC1A01C3A745BDF1D3697D3 /* SessionVerificationScreen.swift */; }; BCC3EDB7AD0902797CB4BBC2 /* MXLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = EF188681D6B6068CFAEAFC3F /* MXLogger.m */; }; BCEC41FB1F2BB663183863E4 /* LoginServerInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D379E13DD9D987470A3C70C /* LoginServerInfoSection.swift */; }; @@ -267,10 +274,11 @@ C4F69156C31A447FEFF2A47C /* DTHTMLElement+AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E508AB0EDEE017FF4F6F8D1 /* DTHTMLElement+AttributedStringBuilder.swift */; }; C55A44C99F64A479ABA85B46 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */; }; C76892321558E75101E68ED6 /* ReadableFrameModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */; }; + C7B251DC896C0867C51B616D /* AnalyticsPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 541542F5AC323709D8563458 /* AnalyticsPrompt.swift */; }; C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */; }; CB137BFB3E083C33E398A6CB /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 5986E300FC849DEAB2EE7AEB /* Introspect */; }; CB326BAB54E9B68658909E36 /* Benchmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EAD710A2C16EFF7C3EA16F /* Benchmark.swift */; }; - CB498F4E27AA0545DCEF0F6F /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; }; + CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; CC736DA1AA8F8B9FD8785009 /* ScreenshotDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5C4AF6E3885730CD560311C /* ScreenshotDetector.swift */; }; CD6A72B65D3B6076F4045C30 /* PHGPostHogConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */; }; CE1694C7BB93C3311524EF28 /* Untranslated.strings in Resources */ = {isa = PBXBuildFile; fileRef = D2F7194F440375338F8E2487 /* Untranslated.strings */; }; @@ -293,9 +301,11 @@ DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */; }; DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */; }; DFF7D6A6C26DDD40D00AE579 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = F012CB5EE3F2B67359F6CC52 /* target.yml */; }; + E01373F2043E76393A0CE073 /* AnalyticsPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11B74ACE8D71747E1044A9C /* AnalyticsPromptViewModel.swift */; }; E0A4DCA633D174EB43AD599F /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; }; E1DF24D085572A55C9758A2D /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; }; E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; }; + E47CD939D8480657D4B706C6 /* AnalyticsPromptCheckmarkItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA7B2E9CC5DC3B76ADC35A43 /* AnalyticsPromptCheckmarkItem.swift */; }; E481C8FDCB6C089963C95344 /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; }; E5895C74615CBE8462FB840F /* SessionVerificationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCF86010A0A719A9A50EEC59 /* SessionVerificationCoordinator.swift */; }; E81EEC1675F2371D12A880A3 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61ADFB893DEF81E58DF3FAB9 /* MockRoomTimelineController.swift */; }; @@ -310,6 +320,7 @@ EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; }; EF99A92701E401C4CD5ADC50 /* SplashScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE978A6118C131D7F2A04B3 /* SplashScreenModels.swift */; }; F040ABFEB0A2B142D948BA12 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; }; + F0F82C3C848C865C3098AA52 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; }; F2DD8661B5C0BA2BB526FA6C /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */; }; F4C3FEDB1B3A05376A1723A3 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4427F9E0571B4E6E048A2B /* KeychainController.swift */; }; F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; }; @@ -492,6 +503,7 @@ 529513218340CC8419273165 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; 534A5C8FCDE2CBC50266B9F2 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = gl; path = gl.lproj/Localizable.stringsdict; sourceTree = ""; }; 536E72DCBEEC4A1FE66CFDCE /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; + 541542F5AC323709D8563458 /* AnalyticsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPrompt.swift; sourceTree = ""; }; 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; 54E438DBCBDC7A41B95DDDD9 /* MXLogObjcWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLogObjcWrapper.m; sourceTree = ""; }; 55BC11560C8A2598964FFA4C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = ""; }; @@ -616,6 +628,7 @@ A00C7A331B72C0F05C00392F /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = ""; }; A05707BF550D770168A406DB /* LoginViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelTests.swift; sourceTree = ""; }; A0A20AE75FF4FF35B1FF6CA7 /* MockServerSelectionScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockServerSelectionScreenState.swift; sourceTree = ""; }; + A11B74ACE8D71747E1044A9C /* AnalyticsPromptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptViewModel.swift; sourceTree = ""; }; A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationStateMachineTests.swift; sourceTree = ""; }; A1ED7E89865201EE7D53E6DA /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = ""; }; A2B6433F516F1E6DFA0E2D89 /* vls */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vls; path = vls.lproj/Localizable.strings; sourceTree = ""; }; @@ -631,6 +644,7 @@ A8D1CC633517D695FEC54208 /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = ""; }; A9873374E72AA53260AE90A2 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = ""; }; AA19C32BD97F45847724E09A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Untranslated.strings; sourceTree = ""; }; + AA8BA82CF99D843FEF680E91 /* AnalyticsPromptModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptModels.swift; sourceTree = ""; }; AAC9344689121887B74877AF /* UnitTests.xctest */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.cfbundle; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AACE9B8E1A4AE79A7E2914F6 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = ""; }; AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = ""; }; @@ -642,6 +656,7 @@ AE225C66978648AA4AF37B45 /* te */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = te; path = te.lproj/Localizable.strings; sourceTree = ""; }; AE5DDBEBBA17973ED4638823 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = ""; }; AF05352F28D4E7336228E9F4 /* ActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorView.swift; sourceTree = ""; }; + AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptUITests.swift; sourceTree = ""; }; AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = ""; }; AFABDF2E19D349DAAAC18C65 /* RoundedToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedToastView.swift; sourceTree = ""; }; B08CBE1E670690ECF11C2C6A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = eu; path = eu.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -653,6 +668,7 @@ B4173A48FD8542CD4AD3645C /* NavigationRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = ""; }; B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemContextMenu.swift; sourceTree = ""; }; B4C18FAAD59AE7F1462D817E /* SessionVerificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModel.swift; sourceTree = ""; }; + B4DE1CF8F5EFD353B1A5E36F /* AnalyticsPromptCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptCoordinator.swift; sourceTree = ""; }; B516212D9FE785DDD5E490D1 /* BugReportModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportModels.swift; sourceTree = ""; }; B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = ""; }; B64F3A3D0DF86ED5A241AB05 /* ActivityIndicatorView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ActivityIndicatorView.xib; sourceTree = ""; }; @@ -666,6 +682,7 @@ B83CB897B183BF3C33715F55 /* bn-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "bn-IN"; path = "bn-IN.lproj/Localizable.stringsdict"; sourceTree = ""; }; B8A56EA2A5AE726F445CB2E3 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = eo; path = eo.lproj/Localizable.stringsdict; sourceTree = ""; }; B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; }; + BA7B2E9CC5DC3B76ADC35A43 /* AnalyticsPromptCheckmarkItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptCheckmarkItem.swift; sourceTree = ""; }; BB3073CCD77D906B330BC1D6 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; BC9B05D6B293A039EB963CA7 /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/Localizable.strings; sourceTree = ""; }; BE6C10032A77AE7DC5AA4C50 /* MessageComposerTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposerTextField.swift; sourceTree = ""; }; @@ -684,6 +701,7 @@ C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = ""; }; C91A6BC1A54CDB598EE2A81B /* UserIndicatorQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorQueue.swift; sourceTree = ""; }; C95ADE8D9527523572532219 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = ""; }; + C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptViewModelProtocol.swift; sourceTree = ""; }; C9F395A2E917115C7AAF7F34 /* SplashViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = ""; }; CAAE4A709C0A2144C103AA0F /* ang */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ang; path = ang.lproj/Localizable.strings; sourceTree = ""; }; CACA846B3E3E9A521D98B178 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -787,16 +805,18 @@ buildActionMask = 2147483647; files = ( 2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */, - 99ED42B8F8D6BFB1DBCF4C45 /* AppAuth in Frameworks */, - 0E8C480700870BB34A2A360F /* DTCoreText in Frameworks */, - CB498F4E27AA0545DCEF0F6F /* KeychainAccess in Frameworks */, - 6832733838C57A7D3FE8FEB5 /* Kingfisher in Frameworks */, - 2BA59D0AEFB4B82A2EC2A326 /* Introspect in Frameworks */, - B245583C63F8F90357B87FAE /* SwiftyBeaver in Frameworks */, - A4E885358D7DD5A072A06824 /* SwiftState in Frameworks */, - 29EE1791E0AFA1ABB7F23D2F /* GZIP in Frameworks */, - 33CAC1226DFB8B5D8447D286 /* Sentry in Frameworks */, - 492274DA6691EE985C2FCCAA /* SnapshotTesting in Frameworks */, + 99ED42B8F8D6BFB1DBCF4C45 /* AnalyticsEvents in Frameworks */, + 0E8C480700870BB34A2A360F /* AppAuth in Frameworks */, + CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */, + 6832733838C57A7D3FE8FEB5 /* KeychainAccess in Frameworks */, + 2BA59D0AEFB4B82A2EC2A326 /* Kingfisher in Frameworks */, + B245583C63F8F90357B87FAE /* Introspect in Frameworks */, + A4E885358D7DD5A072A06824 /* PostHog in Frameworks */, + 29EE1791E0AFA1ABB7F23D2F /* SwiftyBeaver in Frameworks */, + 33CAC1226DFB8B5D8447D286 /* SwiftState in Frameworks */, + 492274DA6691EE985C2FCCAA /* GZIP in Frameworks */, + F0F82C3C848C865C3098AA52 /* Sentry in Frameworks */, + 3A64A93A651A3CB8774ADE8E /* SnapshotTesting in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -915,6 +935,15 @@ path = ViewModel; sourceTree = ""; }; + 1EEF62E8C0F7826C50264951 /* View */ = { + isa = PBXGroup; + children = ( + 541542F5AC323709D8563458 /* AnalyticsPrompt.swift */, + BA7B2E9CC5DC3B76ADC35A43 /* AnalyticsPromptCheckmarkItem.swift */, + ); + path = View; + sourceTree = ""; + }; 24FD174C31912A5FACFEAFB5 /* SupportingFiles */ = { isa = PBXGroup; children = ( @@ -1402,6 +1431,7 @@ 951A66D15CD44C0EACE4A951 /* Sources */ = { isa = PBXGroup; children = ( + AF11DD57D9FACF2A757AB024 /* AnalyticsPromptUITests.swift */, 7D0CBC76C80E04345E11F2DB /* Application.swift */, 5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */, C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */, @@ -1633,6 +1663,18 @@ path = SessionVerification; sourceTree = ""; }; + E0EEBB2F7AA1BB36FC08F606 /* AnalyticsPrompt */ = { + isa = PBXGroup; + children = ( + B4DE1CF8F5EFD353B1A5E36F /* AnalyticsPromptCoordinator.swift */, + AA8BA82CF99D843FEF680E91 /* AnalyticsPromptModels.swift */, + A11B74ACE8D71747E1044A9C /* AnalyticsPromptViewModel.swift */, + C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */, + 1EEF62E8C0F7826C50264951 /* View */, + ); + path = AnalyticsPrompt; + sourceTree = ""; + }; E2DA161C142B7AB8CC40F752 /* Animation */ = { isa = PBXGroup; children = ( @@ -1644,6 +1686,7 @@ E59565F441830B19DBAE567C /* Screens */ = { isa = PBXGroup; children = ( + E0EEBB2F7AA1BB36FC08F606 /* AnalyticsPrompt */, E74CD7681375AD2EAA34D66B /* Authentication */, 4009BE2E791C16AC6EE39A7E /* BugReport */, B53CA9BECD3F97805E1432D0 /* HomeScreen */, @@ -1779,11 +1822,13 @@ name = UITests; packageProductDependencies = ( B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */, + D661CAB418C075A94306A792 /* AnalyticsEvents */, 4346F63D53A346271577FD9C /* AppAuth */, 36B7FC232711031AA2B0D188 /* DTCoreText */, 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */, 50009897F60FAE7D63EF5E5B /* Kingfisher */, 04C28663564E008DB32B5972 /* Introspect */, + CCE5BF78B125320CBF3BB834 /* PostHog */, A981A4CA233FB5C13B9CA690 /* SwiftyBeaver */, 3853B78FB8531B83936C5DA6 /* SwiftState */, 1BCD21310B997A6837B854D6 /* GZIP */, @@ -2171,6 +2216,12 @@ A371629728E597C5FCA3C2B2 /* Analytics.swift in Sources */, F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */, 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */, + C7B251DC896C0867C51B616D /* AnalyticsPrompt.swift in Sources */, + E47CD939D8480657D4B706C6 /* AnalyticsPromptCheckmarkItem.swift in Sources */, + A663FE6704CB500EBE782AE1 /* AnalyticsPromptCoordinator.swift in Sources */, + B6F92EBE04D4AABF30B9E73A /* AnalyticsPromptModels.swift in Sources */, + E01373F2043E76393A0CE073 /* AnalyticsPromptViewModel.swift in Sources */, + 744C029EB6C43429926A0499 /* AnalyticsPromptViewModelProtocol.swift in Sources */, 3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */, EC4C31963E755EEC77BD778C /* AnalyticsSettings.swift in Sources */, A636D4900E0D98ED91536482 /* AppCoordinator.swift in Sources */, @@ -2390,12 +2441,15 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 97CECF91D68235F1D13598D7 /* AnalyticsConfiguration.swift in Sources */, + 8024BE37156FF0A95A7A3465 /* AnalyticsPromptUITests.swift in Sources */, 7405B4824D45BA7C3D943E76 /* Application.swift in Sources */, ACF094CF3BF02DBFA6DFDE60 /* AuthenticationCoordinatorUITests.swift in Sources */, 7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */, 499A26EB06C97E48C27A2DB9 /* BuildSettings.swift in Sources */, 94D0F36A87E596A93C0C178A /* Bundle.swift in Sources */, 9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */, + BB4C6F362F75933DDDE30F3E /* InfoPlist.swift in Sources */, 5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */, 2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */, 77FACC29F98FE2E65BBB6A5F /* ServerSelectionUITests.swift in Sources */, @@ -3202,6 +3256,16 @@ package = 80B898A3AD2AC63F3ABFC218 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */; productName = MatrixRustSDK; }; + CCE5BF78B125320CBF3BB834 /* PostHog */ = { + isa = XCSwiftPackageProductDependency; + package = 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */; + productName = PostHog; + }; + D661CAB418C075A94306A792 /* AnalyticsEvents */ = { + isa = XCSwiftPackageProductDependency; + package = AC3475112CA40C2C6E78D1EB /* XCRemoteSwiftPackageReference "matrix-analytics-events" */; + productName = AnalyticsEvents; + }; D82E84F90358CC1118E6034B /* Introspect */ = { isa = XCSwiftPackageProductDependency; package = 9A472EE0218FE7DCF5283429 /* XCRemoteSwiftPackageReference "SwiftUI-Introspect" */; diff --git a/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/AnalyticsTick.pdf b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/AnalyticsTick.pdf new file mode 100644 index 000000000..9696208e3 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/AnalyticsTick.pdf @@ -0,0 +1,123 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 1.000000 -1.000000 cm +0.049479 0.742188 0.545395 scn +10.000000 23.000000 m +3.947715 23.000000 -1.000000 18.052284 -1.000000 12.000000 c +1.000000 12.000000 l +1.000000 16.947716 5.052285 21.000000 10.000000 21.000000 c +10.000000 23.000000 l +h +-1.000000 12.000000 m +-1.000000 5.947716 3.947715 1.000000 10.000000 1.000000 c +10.000000 3.000000 l +5.052285 3.000000 1.000000 7.052285 1.000000 12.000000 c +-1.000000 12.000000 l +h +10.000000 1.000000 m +16.052284 1.000000 21.000000 5.947716 21.000000 12.000000 c +19.000000 12.000000 l +19.000000 7.052285 14.947715 3.000000 10.000000 3.000000 c +10.000000 1.000000 l +h +21.000000 12.000000 m +21.000000 18.052284 16.052284 23.000000 10.000000 23.000000 c +10.000000 21.000000 l +14.947715 21.000000 19.000000 16.947716 19.000000 12.000000 c +21.000000 12.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 5.545532 4.655060 cm +0.049479 0.742188 0.545395 scn +0.717378 6.153159 m +0.332610 6.549356 -0.300487 6.558620 -0.696684 6.173852 c +-1.092881 5.789084 -1.102146 5.155987 -0.717378 4.759790 c +0.717378 6.153159 l +h +3.257576 2.102139 m +2.540198 1.405455 l +2.728505 1.211555 2.987285 1.102139 3.257576 1.102139 c +3.527867 1.102139 3.786646 1.211555 3.974954 1.405455 c +3.257576 2.102139 l +h +11.626469 9.284243 m +12.011237 9.680439 12.001972 10.313537 11.605776 10.698304 c +11.209579 11.083073 10.576482 11.073808 10.191713 10.677610 c +11.626469 9.284243 l +h +-0.717378 4.759790 m +2.540198 1.405455 l +3.974954 2.798823 l +0.717378 6.153159 l +-0.717378 4.759790 l +h +3.974954 1.405455 m +11.626469 9.284243 l +10.191713 10.677610 l +2.540198 2.798823 l +3.974954 1.405455 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 1679 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 22.000000 22.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001769 00000 n +0000001792 00000 n +0000001965 00000 n +0000002039 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2098 +%%EOF \ No newline at end of file diff --git a/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/Contents.json new file mode 100644 index 000000000..459589593 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsCheckmark.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "AnalyticsTick.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/AnalyticsLogo.pdf b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/AnalyticsLogo.pdf new file mode 100644 index 000000000..096c22d4b --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/AnalyticsLogo.pdf @@ -0,0 +1,641 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /ExtGState << /E2 << /ca 0.400000 >> + /E1 << /ca 0.400000 >> + >> >> + /BBox [ 0.000000 0.000000 119.000000 93.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 14.875000 -0.000015 cm +0.049479 0.742188 0.545395 scn +0.000000 44.521278 m +0.000000 69.109695 20.036579 89.042557 44.625000 89.042557 c +44.625000 89.042557 l +69.213417 89.042557 89.250000 69.109695 89.250000 44.521278 c +89.250000 44.521278 l +89.250000 19.932861 69.213417 0.000000 44.625000 0.000000 c +44.625000 0.000000 l +20.036579 0.000000 0.000000 19.932861 0.000000 44.521278 c +0.000000 44.521278 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 52.227402 46.594299 cm +1.000000 1.000000 1.000000 scn +0.000000 20.730219 m +0.000000 22.447567 1.395428 23.839752 3.116776 23.839752 c +14.592426 23.839752 23.895279 14.558517 23.895279 3.109533 c +23.895279 1.392185 22.499851 0.000000 20.778503 0.000000 c +19.057156 0.000000 17.661728 1.392185 17.661728 3.109533 c +17.661728 11.123821 11.149731 17.620686 3.116776 17.620686 c +1.395428 17.620686 0.000000 19.012871 0.000000 20.730219 c +h +f* +n +Q +q +-1.000000 0.000000 -0.000000 -1.000000 66.772202 42.448242 cm +1.000000 1.000000 1.000000 scn +0.000000 20.730206 m +0.000000 22.447552 1.395429 23.839737 3.116779 23.839737 c +14.592443 23.839737 23.895306 14.558502 23.895306 3.109520 c +23.895306 1.392172 22.499878 -0.000011 20.778526 -0.000011 c +19.057177 -0.000011 17.661749 1.392172 17.661749 3.109520 c +17.661749 11.123808 11.149744 17.620672 3.116779 17.620672 c +1.395429 17.620672 0.000000 19.012857 0.000000 20.730206 c +h +f* +n +Q +q +-0.000000 1.000000 -1.000000 -0.000000 57.366512 37.265598 cm +1.000000 1.000000 1.000000 scn +0.000000 20.722975 m +0.000000 22.444323 1.392186 23.839752 3.109534 23.839752 c +14.558520 23.839752 23.839758 14.536892 23.839758 3.061234 c +23.839758 1.339886 22.447573 -0.055544 20.730225 -0.055544 c +19.012875 -0.055544 17.620689 1.339886 17.620689 3.061234 c +17.620689 11.094195 11.123824 17.606197 3.109534 17.606197 c +1.392186 17.606197 0.000000 19.001625 0.000000 20.722975 c +h +f* +n +Q +q +-0.000000 -1.000000 1.000000 -0.000000 61.633194 51.777008 cm +1.000000 1.000000 1.000000 scn +0.000000 20.722975 m +0.000000 22.444324 1.392186 23.839752 3.109534 23.839752 c +14.558520 23.839752 23.839758 14.536893 23.839758 3.061237 c +23.839758 1.339888 22.447573 -0.055540 20.730225 -0.055540 c +19.012875 -0.055540 17.620689 1.339888 17.620689 3.061237 c +17.620689 11.094196 11.123824 17.606197 3.109534 17.606197 c +1.392186 17.606197 0.000000 19.001627 0.000000 20.722975 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.123089 16.695465 cm +1.000000 1.000000 1.000000 scn +10.448288 0.000008 m +11.057861 0.000008 11.560491 0.448122 11.646045 1.077618 c +12.576446 7.617959 13.464069 8.514189 19.795069 9.229038 c +20.436726 9.303724 20.917965 9.826525 20.917965 10.434681 c +20.917965 11.053506 20.447418 11.554968 19.805763 11.640323 c +13.506846 12.461866 12.694082 13.262072 11.646045 19.802414 c +11.539103 20.431910 11.057861 20.869354 10.448288 20.869354 c +9.849410 20.869354 9.346780 20.431910 9.250531 19.791744 c +8.330826 13.251402 7.443202 12.355172 1.112203 11.640323 c +0.470547 11.565637 0.000000 11.053506 0.000000 10.434681 c +0.000000 9.826525 0.459853 9.314394 1.112203 9.229038 c +7.411120 8.354148 8.191800 7.607290 9.250531 1.066948 c +9.368169 0.437452 9.860105 0.000008 10.448288 0.000008 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.123089 15.195465 cm +0.049479 0.742188 0.545395 scn +11.646045 2.577618 m +10.903506 2.683247 l +10.902877 2.678619 l +11.646045 2.577618 l +h +19.795069 10.729038 m +19.879219 9.983770 l +19.881781 9.984068 l +19.795069 10.729038 l +h +19.805763 13.140323 m +19.904661 13.883777 l +19.902761 13.884024 l +19.805763 13.140323 l +h +11.646045 21.302414 m +12.386630 21.421087 l +12.385450 21.428030 l +11.646045 21.302414 l +h +9.250531 21.291744 m +8.508834 21.403259 l +8.507838 21.396183 l +9.250531 21.291744 l +h +1.112203 13.140323 m +1.028052 13.885592 l +1.025491 13.885293 l +1.112203 13.140323 l +h +1.112203 10.729038 m +1.215387 11.471931 l +1.209505 11.472700 l +1.112203 10.729038 l +h +9.250531 2.566948 m +8.510169 2.447100 l +8.511623 2.438120 l +8.513294 2.429176 l +9.250531 2.566948 l +h +10.448288 0.750008 m +11.450765 0.750008 12.255557 1.493191 12.389213 2.476614 c +10.902877 2.678619 l +10.865425 2.403053 10.664957 2.250008 10.448288 2.250008 c +10.448288 0.750008 l +h +12.388570 2.471989 m +12.620602 4.103085 12.844038 5.334888 13.138243 6.288948 c +13.429995 7.235054 13.776924 7.858273 14.225985 8.307880 c +15.140809 9.223818 16.666159 9.620979 19.879219 9.983774 c +19.710920 11.474303 l +16.592981 11.122249 14.509018 10.713870 13.164680 9.367895 c +12.484159 8.686545 12.039045 7.814714 11.704848 6.730967 c +11.373105 5.655174 11.136688 4.322321 10.903521 2.683245 c +12.388570 2.471989 l +h +19.881781 9.984068 m +20.873766 10.099530 21.667965 10.918526 21.667965 11.934681 c +20.167965 11.934681 l +20.167965 11.734525 19.999683 11.507918 19.708359 11.474010 c +19.881781 9.984068 l +h +21.667965 11.934681 m +21.667965 12.966555 20.881077 13.753887 19.904659 13.883774 c +19.706867 12.396872 l +20.013762 12.356048 20.167965 12.140457 20.167965 11.934681 c +21.667965 11.934681 l +h +19.902761 13.884024 m +18.330524 14.089085 17.151228 14.287123 16.235826 14.561028 c +15.331247 14.831694 14.731587 15.163220 14.286853 15.607450 c +13.840208 16.053589 13.492528 16.670565 13.191763 17.611713 c +12.888441 18.560867 12.648228 19.788363 12.386598 21.421082 c +10.905493 21.183746 l +11.167881 19.546295 11.422276 18.221136 11.762950 17.155106 c +12.106180 16.081072 12.551881 15.220337 13.226795 14.546188 c +13.903622 13.870131 14.753702 13.438795 15.805837 13.123979 c +16.847151 12.812399 18.131544 12.602333 19.708765 12.396622 c +19.902761 13.884024 l +h +12.385450 21.428030 m +12.223795 22.379583 11.461336 23.119354 10.448288 23.119354 c +10.448288 21.619354 l +10.654386 21.619354 10.854410 21.484234 10.906639 21.176800 c +12.385450 21.428030 l +h +10.448288 23.119354 m +9.455997 23.119354 8.656921 22.387987 8.508867 21.403254 c +9.992196 21.180237 l +10.036638 21.475830 10.242823 21.619354 10.448288 21.619354 c +10.448288 23.119354 l +h +8.507838 21.396183 m +8.278477 19.765110 8.056973 18.533388 7.764218 17.579443 c +7.473907 16.633463 7.127907 16.010515 6.679636 15.561167 c +5.766263 14.645599 4.241331 14.248406 1.028053 13.885587 c +1.196352 12.395059 l +4.314074 12.747088 6.398453 13.155437 7.741569 14.501781 c +8.421542 15.183390 8.865580 16.055490 9.198210 17.139366 c +9.528394 18.215273 9.762733 19.548208 9.993224 21.187307 c +8.507838 21.396183 l +h +1.025491 13.885293 m +0.028613 13.769261 -0.750000 12.956783 -0.750000 11.934681 c +0.750000 11.934681 l +0.750000 12.150229 0.912482 12.362013 1.198914 12.395352 c +1.025491 13.885293 l +h +-0.750000 11.934681 m +-0.750000 10.922595 0.016880 10.115961 1.014900 9.985377 c +1.209505 11.472700 l +0.902826 11.512827 0.750000 11.730454 0.750000 11.934681 c +-0.750000 11.934681 l +h +1.009022 9.986170 m +2.582546 9.767614 3.760892 9.563175 4.675031 9.286510 c +5.578484 9.013078 6.174878 8.682938 6.616406 8.241908 c +7.059544 7.799271 7.404263 7.187467 7.703608 6.250257 c +8.005574 5.304842 8.245770 4.080437 8.510169 2.447100 c +9.990894 2.686794 l +9.725928 4.323629 9.471515 5.645210 9.132493 6.706642 c +8.790851 7.776280 8.348207 8.632186 7.676467 9.303166 c +7.003119 9.975756 6.157125 10.405144 5.109545 10.722197 c +4.072651 11.036015 2.791318 11.253017 1.215384 11.471908 c +1.009022 9.986170 l +h +8.513294 2.429176 m +8.688872 1.489628 9.455215 0.750008 10.448288 0.750008 c +10.448288 2.250008 l +10.264993 2.250008 10.047464 2.385277 9.987769 2.704720 c +8.513294 2.429176 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 72.573807 58.608093 cm +1.000000 1.000000 1.000000 scn +8.619835 -0.000011 m +9.122732 -0.000011 9.537402 0.373419 9.607985 0.897997 c +10.375565 6.348283 11.107853 7.095141 16.330927 7.690849 c +16.860292 7.753087 17.257317 8.188755 17.257317 8.695551 c +17.257317 9.211239 16.869114 9.629124 16.339748 9.700253 c +11.143145 10.384872 10.472614 11.051710 9.607985 16.501997 c +9.519756 17.026575 9.122732 17.391113 8.619835 17.391113 c +8.125761 17.391113 7.711091 17.026575 7.631686 16.493105 c +6.872929 11.042820 6.140640 10.295961 0.917567 9.700253 c +0.388201 9.638015 0.000000 9.211239 0.000000 8.695551 c +0.000000 8.188755 0.379379 7.761978 0.917567 7.690849 c +6.114172 6.961774 6.758233 6.339392 7.631686 0.889107 c +7.728737 0.364527 8.134583 -0.000011 8.619835 -0.000011 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 72.573807 57.608093 cm +0.049479 0.742188 0.545395 scn +9.607985 1.897997 m +9.112861 1.967726 l +9.112450 1.964672 l +9.607985 1.897997 l +h +16.330927 8.690849 m +16.387587 8.194067 l +16.389311 8.194269 l +16.330927 8.690849 l +h +16.339748 10.700253 m +16.406334 11.195801 l +16.405056 11.195970 l +16.339748 10.700253 l +h +9.607985 17.501997 m +10.101830 17.580339 l +10.101059 17.584925 l +9.607985 17.501997 l +h +7.631686 17.493105 m +7.137113 17.566721 l +7.136462 17.562048 l +7.631686 17.493105 l +h +0.917567 10.700253 m +0.860907 11.197035 l +0.859183 11.196833 l +0.917567 10.700253 l +h +0.917567 8.690849 m +0.987038 9.186015 l +0.983079 9.186539 l +0.917567 8.690849 l +h +7.631686 1.889107 m +7.137843 1.809963 l +7.140029 1.798147 l +7.631686 1.889107 l +h +8.619835 0.499989 m +9.388696 0.499989 10.001672 1.074373 10.103518 1.831324 c +9.112450 1.964672 l +9.073133 1.672462 8.856770 1.499989 8.619835 1.499989 c +8.619835 0.499989 l +h +10.103099 1.828268 m +10.294624 3.188222 10.480069 4.223436 10.725984 5.028956 c +10.970345 5.829387 11.264832 6.369568 11.654185 6.763333 c +12.442908 7.560994 13.744701 7.892640 16.387587 8.194070 c +16.274267 9.187629 l +13.694078 8.893350 12.018190 8.553713 10.943106 7.466446 c +10.400556 6.917747 10.041607 6.212054 9.769561 5.320940 c +9.499068 4.434915 9.305134 3.332915 9.112870 1.967726 c +10.103099 1.828268 l +h +16.389311 8.194269 m +17.155991 8.284410 17.757317 8.920855 17.757317 9.695551 c +16.757317 9.695551 l +16.757317 9.456655 16.564592 9.221766 16.272543 9.187428 c +16.389311 8.194269 l +h +17.757317 9.695551 m +17.757317 10.482604 17.162529 11.094193 16.406334 11.195800 c +16.273165 10.204706 l +16.575699 10.164056 16.757317 9.939874 16.757317 9.695551 c +17.757317 9.695551 l +h +16.405056 11.195970 m +15.107601 11.366901 14.126670 11.532858 13.361839 11.764020 c +12.604449 11.992933 12.090017 12.277006 11.704502 12.665974 c +11.317406 13.056538 11.022324 13.591100 10.770527 14.386979 c +10.517109 15.187984 10.317721 16.219311 10.101810 17.580338 c +9.114160 17.423656 l +9.330563 16.059540 9.539227 14.963654 9.817105 14.085340 c +10.096603 13.201900 10.456060 12.505035 10.994250 11.962027 c +11.534022 11.417421 12.215625 11.065775 13.072525 10.806786 c +13.921983 10.550046 14.973595 10.375915 16.274443 10.204536 c +16.405056 11.195970 l +h +10.101059 17.584925 m +9.977288 18.320837 9.395552 18.891113 8.619835 18.891113 c +8.619835 17.891113 l +8.849913 17.891113 9.062225 17.732313 9.114909 17.419067 c +10.101059 17.584925 l +h +8.619835 18.891113 m +7.859531 18.891113 7.250215 18.326427 7.137135 17.566717 c +8.126238 17.419493 l +8.171968 17.726725 8.391990 17.891113 8.619835 17.891113 c +8.619835 18.891113 l +h +7.136462 17.562048 m +6.947139 16.202108 6.763302 15.166948 6.518592 14.361504 c +6.275430 13.561155 5.981721 13.021152 5.592998 12.627558 c +4.805452 11.830145 3.503936 11.498478 0.860908 11.197033 c +0.974226 10.203474 l +3.554271 10.497736 5.230436 10.837353 6.304493 11.924868 c +6.846570 12.473737 7.204643 13.179608 7.475406 14.070805 c +7.744622 14.956905 7.936855 16.058958 8.126910 17.424164 c +7.136462 17.562048 l +h +0.859183 11.196833 m +0.089259 11.106312 -0.500000 10.475979 -0.500000 9.695551 c +0.500000 9.695551 l +0.500000 9.946500 0.687143 10.169718 0.975950 10.203673 c +0.859183 11.196833 l +h +-0.500000 9.695551 m +-0.500000 8.923503 0.079786 8.297226 0.852054 8.195160 c +0.983079 9.186539 l +0.678971 9.226731 0.500000 9.454006 0.500000 9.695551 c +-0.500000 9.695551 l +h +0.848098 8.195699 m +2.146421 8.013546 3.126409 7.842214 3.889960 7.608789 c +4.646159 7.377612 5.157835 7.094736 5.540681 6.708459 c +5.924912 6.320785 6.217544 5.790504 6.468155 4.997945 c +6.720429 4.200129 6.919805 3.171420 7.137986 1.809986 c +8.125387 1.968225 l +7.906841 3.331934 7.698164 4.424881 7.421624 5.299438 c +7.143422 6.179253 6.786478 6.872063 6.250936 7.412404 c +5.714010 7.954142 5.035716 8.304206 4.182313 8.565100 c +3.336262 8.823746 2.287016 9.003614 0.987036 9.186000 c +0.848098 8.195699 l +h +7.140029 1.798147 m +7.274719 1.070122 7.860904 0.499989 8.619835 0.499989 c +8.619835 1.499989 l +8.408263 1.499989 8.182755 1.658932 8.123343 1.980066 c +7.140029 1.798147 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 107.227104 75.129669 cm +0.049479 0.742188 0.545395 scn +5.619252 -0.000010 m +5.947090 -0.000010 6.217412 0.238985 6.263425 0.574716 c +6.763808 4.062898 7.241186 4.540888 10.646096 4.922141 c +10.991189 4.961974 11.250008 5.240800 11.250008 5.565150 c +11.250008 5.895190 10.996941 6.162637 10.651848 6.208159 c +7.264192 6.646316 6.827075 7.073092 6.263425 10.561275 c +6.205909 10.897006 5.947090 11.130310 5.619252 11.130310 c +5.297166 11.130310 5.026845 10.897006 4.975080 10.555585 c +4.480448 7.067402 4.003070 6.589413 0.598160 6.208159 c +0.253068 6.168327 0.000000 5.895190 0.000000 5.565150 c +0.000000 5.240800 0.247316 4.967664 0.598160 4.922141 c +3.985816 4.455533 4.405678 4.057208 4.975080 0.569025 c +5.038347 0.233294 5.302918 -0.000010 5.619252 -0.000010 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 103.008408 84.868683 cm +0.049479 0.742188 0.545395 scn +3.160831 -0.000001 m +3.345240 -0.000001 3.497297 0.134433 3.523178 0.323282 c +3.804644 2.285384 4.073170 2.554254 5.988433 2.768708 c +6.182547 2.791114 6.328133 2.947954 6.328133 3.130401 c +6.328133 3.316049 6.185782 3.466487 5.991668 3.492094 c +4.086111 3.738557 3.840232 3.978619 3.523178 5.940721 c +3.490826 6.129570 3.345240 6.260803 3.160831 6.260803 c +2.979658 6.260803 2.827601 6.129570 2.798484 5.937521 c +2.520254 3.975418 2.251728 3.706549 0.336465 3.492094 c +0.142351 3.469688 0.000000 3.316049 0.000000 3.130401 c +0.000000 2.947954 0.139115 2.794315 0.336465 2.768708 c +2.242023 2.506241 2.478195 2.282184 2.798484 0.320081 c +2.834072 0.131233 2.982893 -0.000001 3.160831 -0.000001 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 103.711479 69.564484 cm +0.049479 0.742188 0.545395 scn +3.863233 0.000004 m +4.088622 0.000004 4.274468 0.164312 4.306101 0.395127 c +4.650115 2.793253 4.978312 3.121871 7.319186 3.383983 c +7.556437 3.411368 7.734375 3.603061 7.734375 3.826052 c +7.734375 4.052955 7.560391 4.236824 7.323141 4.268121 c +4.994129 4.569354 4.693611 4.862762 4.306101 7.260888 c +4.266560 7.491703 4.088622 7.652100 3.863233 7.652100 c +3.641799 7.652100 3.455953 7.491703 3.420365 7.256976 c +3.080306 4.858850 2.752109 4.530232 0.411235 4.268121 c +0.173984 4.240736 0.000000 4.052955 0.000000 3.826052 c +0.000000 3.603061 0.170030 3.415280 0.411235 3.383983 c +2.740246 3.063190 3.028902 2.789341 3.420365 0.391215 c +3.463861 0.160400 3.645753 0.000004 3.863233 0.000004 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 -0.070938 78.607880 cm +0.049479 0.742188 0.545395 scn +5.268045 -0.000012 m +5.575393 -0.000012 5.828820 0.224045 5.871957 0.538792 c +6.341066 3.808963 6.788608 4.257079 9.980709 4.614503 c +10.304233 4.651846 10.546875 4.913247 10.546875 5.217325 c +10.546875 5.526737 10.309625 5.777468 9.986101 5.820146 c +6.810176 6.230918 6.400379 6.631021 5.871957 9.901192 c +5.818036 10.215940 5.575393 10.434662 5.268045 10.434662 c +4.966090 10.434662 4.712663 10.215940 4.664135 9.895857 c +4.200418 6.625686 3.752876 6.177571 0.560775 5.820146 c +0.237251 5.782803 0.000000 5.526737 0.000000 5.217325 c +0.000000 4.913247 0.231859 4.657181 0.560775 4.614503 c +3.736700 4.177058 4.130321 3.803629 4.664135 0.533457 c +4.723447 0.218710 4.971482 -0.000012 5.268045 -0.000012 c +h +f +n +Q +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 9.772858 88.346924 cm +0.049479 0.742188 0.545395 scn +2.458421 -0.000008 m +2.601850 -0.000008 2.720115 0.104552 2.740246 0.251434 c +2.959164 1.777514 3.168016 1.986634 4.657663 2.153433 c +4.808641 2.170859 4.921874 2.292846 4.921874 2.434749 c +4.921874 2.579142 4.811157 2.696150 4.660179 2.716066 c +3.178081 2.907759 2.986843 3.094474 2.740246 4.620554 c +2.715083 4.767436 2.601850 4.869507 2.458421 4.869507 c +2.317508 4.869507 2.199242 4.767436 2.176596 4.618064 c +1.960194 3.091985 1.751342 2.882864 0.261695 2.716066 c +0.110717 2.698639 0.000000 2.579142 0.000000 2.434749 c +0.000000 2.292846 0.108201 2.173349 0.261695 2.153433 c +1.743793 1.949291 1.927482 1.775024 2.176596 0.248944 c +2.204275 0.102062 2.320024 -0.000008 2.458421 -0.000008 c +h +f +n +Q +q +/E2 gs +1.000000 0.000000 -0.000000 1.000000 13.288479 82.086121 cm +0.049479 0.742188 0.545395 scn +2.458421 -0.000008 m +2.601850 -0.000008 2.720116 0.104552 2.740247 0.251434 c +2.959164 1.777514 3.168017 1.986634 4.657664 2.153433 c +4.808642 2.170859 4.921875 2.292846 4.921875 2.434749 c +4.921875 2.579142 4.811158 2.696150 4.660181 2.716066 c +3.178082 2.907759 2.986844 3.094474 2.740247 4.620554 c +2.715084 4.767436 2.601850 4.869507 2.458421 4.869507 c +2.317509 4.869507 2.199243 4.767436 2.176596 4.618064 c +1.960195 3.091985 1.751342 2.882864 0.261695 2.716066 c +0.110717 2.698639 0.000000 2.579142 0.000000 2.434749 c +0.000000 2.292846 0.108201 2.173349 0.261695 2.153433 c +1.743793 1.949291 1.927483 1.775024 2.176596 0.248944 c +2.204276 0.102062 2.320025 -0.000008 2.458421 -0.000008 c +h +f +n +Q + +endstream +endobj + +2 0 obj + 17245 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 119.000000 93.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 93.000000 m +119.000000 93.000000 l +119.000000 0.000000 l +0.000000 0.000000 l +0.000000 93.000000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 234 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 119.000000 93.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000017630 00000 n +0000017654 00000 n +0000018137 00000 n +0000018159 00000 n +0000018457 00000 n +0000018559 00000 n +0000018580 00000 n +0000018754 00000 n +0000018828 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +18888 +%%EOF \ No newline at end of file diff --git a/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/Contents.json new file mode 100644 index 000000000..7d49fc335 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/Images/Analytics/AnalyticsLogo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "AnalyticsLogo.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/ElementX/Resources/Assets.xcassets/Images/Analytics/Contents.json b/ElementX/Resources/Assets.xcassets/Images/Analytics/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/Images/Analytics/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Sources/AppCoordinator.swift b/ElementX/Sources/AppCoordinator.swift index 13bb000d6..9fa891215 100644 --- a/ElementX/Sources/AppCoordinator.swift +++ b/ElementX/Sources/AppCoordinator.swift @@ -286,6 +286,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator { private func presentSettingsScreen() { let parameters = SettingsCoordinatorParameters(navigationRouter: navigationRouter, + userSession: userSession, bugReportService: bugReportService) let coordinator = SettingsCoordinator(parameters: parameters) coordinator.callback = { [weak self] action in diff --git a/ElementX/Sources/Generated/Assets.swift b/ElementX/Sources/Generated/Assets.swift index 3ba8d0efa..3fa5ac4a2 100644 --- a/ElementX/Sources/Generated/Assets.swift +++ b/ElementX/Sources/Generated/Assets.swift @@ -23,6 +23,8 @@ internal typealias AssetImageTypeAlias = ImageAsset.Image // swiftlint:disable identifier_name line_length nesting type_body_length type_name internal enum Asset { internal enum Images { + internal static let analyticsCheckmark = ImageAsset(name: "Images/AnalyticsCheckmark") + internal static let analyticsLogo = ImageAsset(name: "Images/AnalyticsLogo") internal static let serverSelectionIcon = ImageAsset(name: "Images/Server Selection Icon") internal static let splashScreenPage1 = ImageAsset(name: "Images/Splash Screen Page 1") internal static let splashScreenPage2 = ImageAsset(name: "Images/Splash Screen Page 2") diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptCoordinator.swift b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptCoordinator.swift new file mode 100644 index 000000000..a73968070 --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptCoordinator.swift @@ -0,0 +1,73 @@ +// +// Copyright 2021 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 AnalyticsPromptCoordinatorParameters { + /// The user session to use if analytics are enabled. + let userSession: UserSessionProtocol +} + +final class AnalyticsPromptCoordinator: Coordinator, Presentable { + // MARK: - Properties + + // MARK: Private + + private let parameters: AnalyticsPromptCoordinatorParameters + private let analyticsPromptHostingController: UIViewController + private var analyticsPromptViewModel: AnalyticsPromptViewModel + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var callback: (@MainActor () -> Void)? + + // MARK: - Setup + + init(parameters: AnalyticsPromptCoordinatorParameters) { + self.parameters = parameters + + let viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL) + + let view = AnalyticsPrompt(context: viewModel.context) + analyticsPromptViewModel = viewModel + analyticsPromptHostingController = UIHostingController(rootView: view) + } + + // MARK: - Public + + func start() { + MXLog.debug("Did start.") + + analyticsPromptViewModel.callback = { [weak self] result in + MXLog.debug("AnalyticsPromptViewModel did complete with result: \(result).") + + guard let self = self else { return } + + switch result { + case .enable: + Analytics.shared.optIn(with: self.parameters.userSession) + self.callback?() + case .disable: + Analytics.shared.optOut() + self.callback?() + } + } + } + + func toPresentable() -> UIViewController { analyticsPromptHostingController } +} diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptModels.swift b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptModels.swift new file mode 100644 index 000000000..aa1e245e7 --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptModels.swift @@ -0,0 +1,63 @@ +// +// Copyright 2021 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 AnalyticsPromptViewAction { + /// Enable analytics. + case enable + /// Disable analytics. + case disable +} + +enum AnalyticsPromptViewModelAction { + /// Enable analytics. + case enable + /// Disable analytics. + case disable +} + +struct AnalyticsPromptViewState: BindableState { + /// Attributed strings created from localized HTML. + let strings = AnalyticsPromptStrings() +} + +/// A collection of strings for the UI that need to be parsed from HTML +struct AnalyticsPromptStrings { + let optInContent: AttributedString + let point1 = AttributedStringBuilder().fromHTML(ElementL10n.analyticsOptInListItem1) ?? AttributedString(ElementL10n.analyticsOptInListItem1) + let point2 = AttributedStringBuilder().fromHTML(ElementL10n.analyticsOptInListItem2) ?? AttributedString(ElementL10n.analyticsOptInListItem2) + + init() { + // Create the opt in content with a placeholder. + let linkPlaceholder = "{link}" + var optInContent = AttributedString(ElementL10n.analyticsOptInContent(ElementInfoPlist.cfBundleName, linkPlaceholder)) + + guard let range = optInContent.range(of: linkPlaceholder) else { + self.optInContent = AttributedString(ElementL10n.analyticsOptInContent(ElementInfoPlist.cfBundleName, + ElementL10n.analyticsOptInContentLink)) + MXLog.failure("Failed to add a link attribute to the opt in content.") + return + } + + // Replace the placeholder with a link. + var link = AttributedString(ElementL10n.analyticsOptInContentLink) + link.link = BuildSettings.analyticsConfiguration.termsURL + optInContent.replaceSubrange(range, with: link) + + self.optInContent = optInContent + } +} diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModel.swift b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModel.swift new file mode 100644 index 000000000..5a7254cfc --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModel.swift @@ -0,0 +1,51 @@ +// +// Copyright 2021 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 AnalyticsPromptViewModelType = StateStoreViewModel + +class AnalyticsPromptViewModel: AnalyticsPromptViewModelType, AnalyticsPromptViewModelProtocol { + // MARK: - Properties + + // MARK: Private + + private let termsURL: URL + + // MARK: Public + + var callback: (@MainActor (AnalyticsPromptViewModelAction) -> Void)? + + // MARK: - Setup + + /// Initialize a view model with the specified prompt type and app display name. + init(termsURL: URL) { + self.termsURL = termsURL + super.init(initialViewState: AnalyticsPromptViewState()) + } + + // MARK: - Public + + override func process(viewAction: AnalyticsPromptViewAction) async { + switch viewAction { + case .enable: + callback?(.enable) + case .disable: + callback?(.disable) + } + } +} diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModelProtocol.swift b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModelProtocol.swift new file mode 100644 index 000000000..483b764c7 --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/AnalyticsPromptViewModelProtocol.swift @@ -0,0 +1,23 @@ +// +// Copyright 2021 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 + +@MainActor +protocol AnalyticsPromptViewModelProtocol { + var callback: (@MainActor (AnalyticsPromptViewModelAction) -> Void)? { get set } + var context: AnalyticsPromptViewModelType.Context { get } +} diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPrompt.swift b/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPrompt.swift new file mode 100644 index 000000000..989a153d8 --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPrompt.swift @@ -0,0 +1,130 @@ +// +// Copyright 2021 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 prompt that asks the user whether they would like to enable Analytics or not. +struct AnalyticsPrompt: View { + // MARK: - Properties + + // MARK: Private + + private let horizontalPadding: CGFloat = 16 + + // MARK: Public + + @ObservedObject var context: AnalyticsPromptViewModel.Context + + // MARK: Views + + var body: some View { + GeometryReader { geometry in + ScrollView { + Spacer() + .frame(height: UIConstants.spacerHeight(in: geometry)) + + mainContent + .readableFrame() + .padding(.horizontal, horizontalPadding) + .padding(.top, UIConstants.onboardingBreakerScreenTopPadding) + .padding(.bottom, 8) + } + .safeAreaInset(edge: .bottom) { + VStack { + buttons + .readableFrame() + .padding(.horizontal, horizontalPadding) + .padding(.bottom, UIConstants.actionButtonBottomPadding) + + Spacer() + .frame(height: UIConstants.spacerHeight(in: geometry)) + } + .padding(.top, 8) + .background(Color.element.background.ignoresSafeArea()) + } + .background(Color.element.background.ignoresSafeArea()) + } + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) + } + + /// The main content of the screen that is shown inside the scroll view. + private var mainContent: some View { + VStack { + Image(uiImage: Asset.Images.analyticsLogo.image) + .padding(.bottom, 25) + + Text(ElementL10n.analyticsOptInTitle(ElementInfoPlist.cfBundleName)) + .font(.element.title2B) + .multilineTextAlignment(.center) + .foregroundColor(.element.primaryContent) + .padding(.bottom, 2) + + Text(context.viewState.strings.optInContent) + .font(.element.body) + .multilineTextAlignment(.center) + .foregroundColor(.element.secondaryContent) + + Divider() + .background(Color.element.quinaryContent) + .padding(.vertical, 28) + + checkmarkList + } + } + + /// The list of re-assurances about analytics. + private var checkmarkList: some View { + VStack(alignment: .leading) { + AnalyticsPromptCheckmarkItem(attributedString: context.viewState.strings.point1) + AnalyticsPromptCheckmarkItem(attributedString: context.viewState.strings.point1) + AnalyticsPromptCheckmarkItem(string: ElementL10n.analyticsOptInListItem3) + } + .fixedSize(horizontal: false, vertical: true) + .font(.element.body) + .foregroundColor(.element.secondaryContent) + .frame(maxWidth: .infinity) + } + + /// The stack of enable/disable buttons. + private var buttons: some View { + VStack { + Button { context.send(viewAction: .enable) } label: { + Text(ElementL10n.actionEnable) + .font(.element.bodySB) + } + .buttonStyle(.elementAction(.xLarge)) + .accessibilityIdentifier("enableButton") + + Button { context.send(viewAction: .disable) } label: { + Text(ElementL10n.actionNotNow) + .font(.element.bodySB) + .padding(12) + } + .accessibilityIdentifier("disableButton") + } + } +} + +// MARK: - Previews + +struct AnalyticsPrompt_Previews: PreviewProvider { + static let viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL) + static var previews: some View { + AnalyticsPrompt(context: viewModel.context) + .tint(.element.accent) + } +} diff --git a/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPromptCheckmarkItem.swift b/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPromptCheckmarkItem.swift new file mode 100644 index 000000000..891995f07 --- /dev/null +++ b/ElementX/Sources/Screens/AnalyticsPrompt/View/AnalyticsPromptCheckmarkItem.swift @@ -0,0 +1,60 @@ +// +// Copyright 2021 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 AnalyticsPromptCheckmarkItem: View { + // MARK: - Properties + + // MARK: Private + + private let attributedString: AttributedString + + // MARK: - Setup + + init(attributedString: AttributedString) { + self.attributedString = attributedString + } + + init(string: String) { + attributedString = AttributedString(string) + } + + // MARK: - Views + + var body: some View { + Label { Text(attributedString) } icon: { + Image(uiImage: Asset.Images.analyticsCheckmark.image) + .foregroundColor(.element.accent) + } + } +} + +// MARK: - Previews + +struct AnalyticsPromptCheckmarkItem_Previews: PreviewProvider { + static let strings = AnalyticsPromptStrings() + + static var previews: some View { + VStack(alignment: .leading) { + AnalyticsPromptCheckmarkItem(attributedString: strings.point1) + AnalyticsPromptCheckmarkItem(attributedString: strings.point2) + AnalyticsPromptCheckmarkItem(attributedString: AttributedString("This is a short string.")) + AnalyticsPromptCheckmarkItem(attributedString: AttributedString("This is a very long string that will be used to test the layout over multiple lines of text to ensure everything is correct.")) + } + .padding() + } +} diff --git a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift index 3fe8b365b..a5d94e3f6 100644 --- a/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift +++ b/ElementX/Sources/Screens/Authentication/AuthenticationCoordinator.swift @@ -51,6 +51,7 @@ class AuthenticationCoordinator: Coordinator, Presentable { // MARK: - Private + /// Shows the splash screen as the root view in the navigation stack. private func showSplashScreen() { let coordinator = SplashScreenCoordinator() @@ -129,6 +130,23 @@ class AuthenticationCoordinator: Coordinator, Presentable { } } + private func showAnalyticsPrompt(with userSession: UserSessionProtocol) { + let parameters = AnalyticsPromptCoordinatorParameters(userSession: userSession) + let coordinator = AnalyticsPromptCoordinator(parameters: parameters) + + coordinator.callback = { [weak self] in + guard let self = self else { return } + self.delegate?.authenticationCoordinator(self, didLoginWithSession: userSession) + } + + coordinator.start() + add(childCoordinator: coordinator) + + navigationRouter.setRootModule(coordinator, hideNavigationBar: true, animated: true) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } + /// Show a blocking activity indicator. private func startLoading() { activityIndicator = indicatorPresenter.present(.loading(label: ElementL10n.loading, isInteractionBlocking: true)) diff --git a/ElementX/Sources/Screens/Authentication/UIConstants.swift b/ElementX/Sources/Screens/Authentication/UIConstants.swift index e4b19d0a8..59e4b5358 100644 --- a/ElementX/Sources/Screens/Authentication/UIConstants.swift +++ b/ElementX/Sources/Screens/Authentication/UIConstants.swift @@ -25,6 +25,9 @@ struct UIConstants { /// The padding used between the footer and the bottom of the view. static let actionButtonBottomPadding: CGFloat = 24 + /// The padding used to the top of the view for breaker screens that don't have a navigation bar. + static let onboardingBreakerScreenTopPadding: CGFloat = 80 + /// 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) diff --git a/ElementX/Sources/Screens/Settings/SettingsCoordinator.swift b/ElementX/Sources/Screens/Settings/SettingsCoordinator.swift index e08b789a9..67bed04d1 100644 --- a/ElementX/Sources/Screens/Settings/SettingsCoordinator.swift +++ b/ElementX/Sources/Screens/Settings/SettingsCoordinator.swift @@ -18,6 +18,7 @@ import SwiftUI struct SettingsCoordinatorParameters { let navigationRouter: NavigationRouterType + let userSession: UserSessionProtocol let bugReportService: BugReportServiceProtocol } @@ -60,6 +61,8 @@ final class SettingsCoordinator: Coordinator, Presentable { guard let self = self else { return } MXLog.debug("SettingsViewModel did complete with result: \(result).") switch result { + case .toggleAnalytics: + self.toggleAnalytics() case .reportBug: self.presentBugReportScreen() case .crash: @@ -82,17 +85,12 @@ final class SettingsCoordinator: Coordinator, Presentable { // MARK: - Private - /// Show an activity indicator whilst loading. - /// - Parameters: - /// - label: The label to show on the indicator. - /// - isInteractionBlocking: Whether the indicator should block any user interaction. - private func startLoading(label: String = ElementL10n.loading, isInteractionBlocking: Bool = true) { - loadingIndicator = indicatorPresenter.present(.loading(label: label, isInteractionBlocking: isInteractionBlocking)) - } - - /// Hide the currently displayed activity indicator. - private func stopLoading() { - loadingIndicator = nil + private func toggleAnalytics() { + if ElementSettings.shared.enableAnalytics { + Analytics.shared.optOut() + } else { + Analytics.shared.optIn(with: parameters.userSession) + } } private func presentBugReportScreen() { @@ -114,6 +112,19 @@ final class SettingsCoordinator: Coordinator, Presentable { self.remove(childCoordinator: coordinator) } } + + /// Show an activity indicator whilst loading. + /// - Parameters: + /// - label: The label to show on the indicator. + /// - isInteractionBlocking: Whether the indicator should block any user interaction. + private func startLoading(label: String = ElementL10n.loading, isInteractionBlocking: Bool = true) { + loadingIndicator = indicatorPresenter.present(.loading(label: label, isInteractionBlocking: isInteractionBlocking)) + } + + /// Hide the currently displayed activity indicator. + private func stopLoading() { + loadingIndicator = nil + } /// Show success indicator private func showSuccess(label: String) { diff --git a/ElementX/Sources/Screens/Settings/SettingsModels.swift b/ElementX/Sources/Screens/Settings/SettingsModels.swift index ad9d49a0a..ae1ead79f 100644 --- a/ElementX/Sources/Screens/Settings/SettingsModels.swift +++ b/ElementX/Sources/Screens/Settings/SettingsModels.swift @@ -21,6 +21,7 @@ import Foundation // MARK: View model enum SettingsViewModelAction { + case toggleAnalytics case reportBug case crash case logout @@ -32,9 +33,12 @@ struct SettingsViewState: BindableState { var bindings: SettingsViewStateBindings } -struct SettingsViewStateBindings { } +struct SettingsViewStateBindings { + var enableAnalytics = ElementSettings.shared.enableAnalytics +} enum SettingsViewAction { + case toggleAnalytics case reportBug case crash case logout diff --git a/ElementX/Sources/Screens/Settings/SettingsViewModel.swift b/ElementX/Sources/Screens/Settings/SettingsViewModel.swift index 4488ec51b..86c2d90e6 100644 --- a/ElementX/Sources/Screens/Settings/SettingsViewModel.swift +++ b/ElementX/Sources/Screens/Settings/SettingsViewModel.swift @@ -40,6 +40,8 @@ class SettingsViewModel: SettingsViewModelType, SettingsViewModelProtocol { override func process(viewAction: SettingsViewAction) async { switch viewAction { + case .toggleAnalytics: + callback?(.toggleAnalytics) case .reportBug: callback?(.reportBug) case .crash: diff --git a/ElementX/Sources/Screens/Settings/View/SettingsScreen.swift b/ElementX/Sources/Screens/Settings/View/SettingsScreen.swift index 49ded3b82..47a35eebf 100644 --- a/ElementX/Sources/Screens/Settings/View/SettingsScreen.swift +++ b/ElementX/Sources/Screens/Settings/View/SettingsScreen.swift @@ -31,43 +31,14 @@ struct SettingsScreen: View { var body: some View { Form { - Section { - Button { context.send(viewAction: .reportBug) } label: { - Text(ElementL10n.sendBugReport) - } - .foregroundColor(Color.element.primaryContent) - .accessibilityIdentifier("reportBugButton") - - if BuildSettings.settingsCrashButtonVisible { - Button("Crash the app", - role: .destructive) { context.send(viewAction: .crash) - } - - .accessibilityIdentifier("crashButton") - } - } - .listRowBackground(rowBackgroundColor) + analyticsSection + .listRowBackground(rowBackgroundColor) userInterfaceSection + .listRowBackground(rowBackgroundColor) - Section { - Button { showingLogoutConfirmation = true } label: { - Text(ElementL10n.actionSignOut) - } - .frame(maxWidth: .infinity) - .foregroundColor(Color.element.primaryContent) - .accessibilityIdentifier("logoutButton") - .confirmationDialog(ElementL10n.actionSignOutConfirmationSimple, - isPresented: $showingLogoutConfirmation, - titleVisibility: .visible) { - Button(ElementL10n.actionSignOut, - role: .destructive) { context.send(viewAction: .logout) - } - } - } footer: { - versionText - } - .listRowBackground(rowBackgroundColor) + logoutSection + .listRowBackground(rowBackgroundColor) } .introspectTableView { tableView in tableView.backgroundColor = .clear @@ -87,11 +58,28 @@ struct SettingsScreen: View { private var rowBackgroundColor: Color { colorScheme == .light ? .element.background : .element.system } + + private var analyticsSection: some View { + Section(ElementL10n.settingsAnalytics) { + Button { context.send(viewAction: .reportBug) } label: { + Text(ElementL10n.sendBugReport) + } + .foregroundColor(.element.primaryContent) + .accessibilityIdentifier("reportBugButton") + + if BuildSettings.settingsCrashButtonVisible { + Button("Crash the app", + role: .destructive) { context.send(viewAction: .crash) + } + .accessibilityIdentifier("crashButton") + } + } + } @ViewBuilder private var userInterfaceSection: some View { if BuildSettings.settingsShowTimelineStyle { - Section(header: Text(ElementL10n.settingsUserInterface)) { + Section(ElementL10n.settingsUserInterface) { Picker(ElementL10n.settingsTimelineStyle, selection: $settings.timelineStyle) { ForEach(TimelineStyle.allCases, id: \.self) { style in Text(style.description) @@ -100,7 +88,26 @@ struct SettingsScreen: View { } .accessibilityIdentifier("timelineStylePicker") } - .listRowBackground(rowBackgroundColor) + } + } + + private var logoutSection: some View { + Section { + Button { showingLogoutConfirmation = true } label: { + Text(ElementL10n.actionSignOut) + } + .frame(maxWidth: .infinity) + .foregroundColor(.element.primaryContent) + .accessibilityIdentifier("logoutButton") + .confirmationDialog(ElementL10n.actionSignOutConfirmationSimple, + isPresented: $showingLogoutConfirmation, + titleVisibility: .visible) { + Button(ElementL10n.actionSignOut, + role: .destructive) { context.send(viewAction: .logout) + } + } + } footer: { + versionText } } } diff --git a/ElementX/Sources/Services/Analytics/Analytics.swift b/ElementX/Sources/Services/Analytics/Analytics.swift index 9cf2f1a44..ca045c6a3 100644 --- a/ElementX/Sources/Services/Analytics/Analytics.swift +++ b/ElementX/Sources/Services/Analytics/Analytics.swift @@ -56,14 +56,14 @@ class Analytics { // MARK: - Public - /// Opts in to analytics tracking with the supplied session. - /// - Parameter session: An optional session to use to when reading/generating the analytics ID. + /// Opts in to analytics tracking with the supplied user session. + /// - Parameter userSession: The user session to use to when reading/generating the analytics ID. /// The session will be ignored if not running. - func optIn(with session: UserSessionProtocol) { + func optIn(with userSession: UserSessionProtocol) { ElementSettings.shared.enableAnalytics = true startIfEnabled() - Task { await useAnalyticsSettings(from: session) } + Task { await useAnalyticsSettings(from: userSession) } } /// Stops analytics tracking and calls `reset` to clear any IDs and event queues. @@ -95,16 +95,16 @@ class Analytics { // MXLogger.setBuildVersion(ElementInfoPlist.cfBundleShortVersionString) } - /// Use the analytics settings from the supplied session to configure analytics. + /// Use the analytics settings from the supplied user session to configure analytics. /// For now this is only used for (pseudonymous) identification. - /// - Parameter session: The session to read analytics settings from. - func useAnalyticsSettings(from session: UserSessionProtocol) async { + /// - Parameter userSession: The user session to read analytics settings from. + func useAnalyticsSettings(from userSession: UserSessionProtocol) async { guard ElementSettings.shared.enableAnalytics, !ElementSettings.shared.isIdentifiedForAnalytics else { return } - let service = AnalyticsService(session: session) + let service = AnalyticsService(userSession: userSession) self.service = service switch await service.settings() { diff --git a/ElementX/Sources/Services/Analytics/AnalyticsService.swift b/ElementX/Sources/Services/Analytics/AnalyticsService.swift index 745ec3fb0..2991a344a 100644 --- a/ElementX/Sources/Services/Analytics/AnalyticsService.swift +++ b/ElementX/Sources/Services/Analytics/AnalyticsService.swift @@ -17,7 +17,7 @@ import Foundation enum AnalyticsServiceError: Error { - /// The session supplied to the service does not have a state of `MXSessionStateRunning`. + /// The user session supplied to the service does not have a state of `MXSessionStateRunning`. case sessionIsNotRunning /// The service failed to get or update the analytics settings event from the user's account data. case accountDataFailure @@ -25,12 +25,12 @@ enum AnalyticsServiceError: Error { /// A service responsible for handling the `im.vector.analytics` event from the user's account data. class AnalyticsService { - let session: UserSessionProtocol + let userSession: UserSessionProtocol - /// Creates an analytics service with the supplied session. - /// - Parameter session: The session to use when reading analytics settings from account data. - init(session: UserSessionProtocol) { - self.session = session + /// Creates an analytics service with the supplied user session. + /// - Parameter userSession: The user session to use when reading analytics settings from account data. + init(userSession: UserSessionProtocol) { + self.userSession = userSession } /// The analytics settings for the current user. Calling this method will check whether the settings already @@ -41,12 +41,12 @@ class AnalyticsService { func settings() async -> Result { // Only use the session if it is running otherwise we could wipe out an existing analytics ID. fatalWithoutUnreachableCodeWarning() -// guard session.state == .running else { +// guard userSession.state == .running else { // MXLog.warning("Aborting attempt to read analytics settings. The session may not be up-to-date.") // return .failure(.sessionIsNotRunning) // } - let result: Result = await session.clientProxy.accountDataEvent(type: AnalyticsSettings.eventType) + let result: Result = await userSession.clientProxy.accountDataEvent(type: AnalyticsSettings.eventType) switch result { case .failure: return .failure(.accountDataFailure) @@ -57,7 +57,7 @@ class AnalyticsService { } let newSettings = AnalyticsSettings.new(currentEvent: settings) - switch await session.clientProxy.setAccountData(content: newSettings, type: AnalyticsSettings.eventType) { + switch await userSession.clientProxy.setAccountData(content: newSettings, type: AnalyticsSettings.eventType) { case .failure: MXLog.warning("Failed to update analytics settings.") return .failure(.accountDataFailure) diff --git a/ElementX/Sources/UITestScreenIdentifier.swift b/ElementX/Sources/UITestScreenIdentifier.swift index 05f5842c4..17bbe7a6d 100644 --- a/ElementX/Sources/UITestScreenIdentifier.swift +++ b/ElementX/Sources/UITestScreenIdentifier.swift @@ -21,6 +21,7 @@ enum UITestScreenIdentifier: String { case serverSelection case serverSelectionNonModal case authenticationFlow + case analyticsPrompt case simpleRegular case simpleUpgrade case settings diff --git a/ElementX/Sources/UITestsAppCoordinator.swift b/ElementX/Sources/UITestsAppCoordinator.swift index cd7363699..f2fedd7bd 100644 --- a/ElementX/Sources/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITestsAppCoordinator.swift @@ -76,6 +76,9 @@ class MockScreen: Identifiable { case .serverSelectionNonModal: return ServerSelectionCoordinator(parameters: .init(authenticationService: MockAuthenticationServiceProxy(), hasModalPresentation: false)) + case .analyticsPrompt: + return AnalyticsPromptCoordinator(parameters: .init(userSession: MockUserSession(clientProxy: MockClientProxy(userIdentifier: "@mock:client.com"), + mediaProvider: MockMediaProvider()))) case .authenticationFlow: return AuthenticationCoordinator(authenticationService: MockAuthenticationServiceProxy(), navigationRouter: navigationRouter) @@ -85,6 +88,8 @@ class MockScreen: Identifiable { return TemplateCoordinator(parameters: .init(promptType: .upgrade)) case .settings: return SettingsCoordinator(parameters: .init(navigationRouter: navigationRouter, + userSession: MockUserSession(clientProxy: MockClientProxy(userIdentifier: "@mock:client.com"), + mediaProvider: MockMediaProvider()), bugReportService: MockBugReportService())) case .bugReport: return BugReportCoordinator(parameters: .init(bugReportService: MockBugReportService(), diff --git a/UITests/Sources/AnalyticsPromptUITests.swift b/UITests/Sources/AnalyticsPromptUITests.swift new file mode 100644 index 000000000..93fd70812 --- /dev/null +++ b/UITests/Sources/AnalyticsPromptUITests.swift @@ -0,0 +1,27 @@ +// +// Copyright 2021 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 + +class AnalyticsPromptUITests: XCTestCase { + /// Verify that the prompt is displayed correctly. + func testAnalyticsPrompt() { + let app = Application.launch() + app.goToScreenWithIdentifier(.analyticsPrompt) + app.assertScreenshot(.analyticsPrompt) + } +} diff --git a/UITests/SupportingFiles/target.yml b/UITests/SupportingFiles/target.yml index ba62ac69e..47d9d4d40 100644 --- a/UITests/SupportingFiles/target.yml +++ b/UITests/SupportingFiles/target.yml @@ -29,6 +29,8 @@ targets: - target: ElementX - package: MatrixRustSDK linkType: static + - package: AnalyticsEvents + linkType: static - package: AppAuth linkType: static - package: DTCoreText @@ -39,6 +41,8 @@ targets: linkType: static - package: Introspect linkType: static + - package: PostHog + linkType: static - package: SwiftyBeaver linkType: static - package: SwiftState @@ -76,8 +80,10 @@ targets: - path: ../../Tools/Scripts/Templates/SimpleScreenExample/Tests/UI - path: ../../ElementX/Sources/BuildSettings.swift - path: ../../ElementX/Sources/Screens/RoomScreen/View/Style/TimelineStyle.swift + - path: ../../ElementX/Sources/Services/Analytics/AnalyticsConfiguration.swift - path: ../../ElementX/Sources/UITestScreenIdentifier.swift - path: ../../ElementX/Sources/Generated/Strings.swift - path: ../../ElementX/Sources/Generated/Strings+Untranslated.swift + - path: ../../ElementX/Sources/Generated/InfoPlist.swift - path: ../../ElementX/Resources - path: ../../ElementX/Sources/Other/Extensions/Bundle.swift