Add a TracingHook. (#4345)
This commit is contained in:
@@ -254,6 +254,7 @@
|
||||
2D0E3983288E2D35613AD681 /* SecureBackupControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */; };
|
||||
2D2D8A53B35BE8D8A01449C6 /* PinnedEventsBannerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FA38E813BE14149F173F461 /* PinnedEventsBannerStateTests.swift */; };
|
||||
2D38D39B1789B91AE69F477F /* PhotoLibraryManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD955A0380C287C418F1A74D /* PhotoLibraryManagerMock.swift */; };
|
||||
2D45A04699BB6BA3B3A0CB9A /* TracingHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A95C9B8299A36A6495DECA6 /* TracingHook.swift */; };
|
||||
2DA27D78560D5F79B917E163 /* AudioConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44E35AA87F49503E7B3BF6E /* AudioConverter.swift */; };
|
||||
2DD9D0FE7CB5CFC80D071451 /* AppLockScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */; };
|
||||
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; };
|
||||
@@ -391,6 +392,7 @@
|
||||
494970EA811FE4D93AC68482 /* SettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FE8A35B4AD5256F4B562274 /* SettingsScreenViewModelTests.swift */; };
|
||||
4949C8C12669D1B5E082366E /* QRCodeLoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */; };
|
||||
49500BBA1CD65A5AE252D970 /* RoomDirectorySearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */; };
|
||||
49BBEC46D523BF6A41400048 /* URLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB34956C87731AB094DB33A /* URLTests.swift */; };
|
||||
4A4110369DBB79E4A314F415 /* ComposerToolbarViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0618820D26F9871A4BBB40E /* ComposerToolbarViewModelProtocol.swift */; };
|
||||
4A618590DEB72C4F186BFED4 /* UserSessionFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C99FDEEB71173C4C6FA2734C /* UserSessionFlowCoordinator.swift */; };
|
||||
4A8287E5281B44A8754BE509 /* SessionVerificationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */; };
|
||||
@@ -434,6 +436,7 @@
|
||||
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */; };
|
||||
5100F53E6884A15F9BA07CC3 /* AttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */; };
|
||||
510C4EDF826CA9C6CEEC6C95 /* ManageRoomMemberSheetViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A34D9BCA1A7D9A56E1EAF1D /* ManageRoomMemberSheetViewModel.swift */; };
|
||||
51263FC33CC961A14F5D6BCA /* TracingHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A95C9B8299A36A6495DECA6 /* TracingHook.swift */; };
|
||||
5139F4BD5A5DF6F8D11A9BDE /* NotificationPermissionsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D0BA44B1838E65B507B277 /* NotificationPermissionsScreen.swift */; };
|
||||
513AF15E0E84711B80D04B1B /* ReportRoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C3E9684DCE6B66BD0B5DF67 /* ReportRoomScreenViewModelTests.swift */; };
|
||||
51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; };
|
||||
@@ -1159,6 +1162,7 @@
|
||||
DF8F1211F2B0B56F0FCCA5C2 /* CertificateValidatorHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3865AD7B7249C939D7C69C33 /* CertificateValidatorHook.swift */; };
|
||||
DFD5AA8688A34C72D48AF3B1 /* StaticLocationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5311C989EC15B4C2D699025 /* StaticLocationScreenViewModel.swift */; };
|
||||
DFF7D6A6C26DDD40D00AE579 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = F012CB5EE3F2B67359F6CC52 /* target.yml */; };
|
||||
E010DDE938032D3B8E84CC35 /* TracingHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A95C9B8299A36A6495DECA6 /* TracingHook.swift */; };
|
||||
E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E625B0EB2F86B37C14EF7E6 /* SettingsScreenViewModel.swift */; };
|
||||
E0C167D41A48EDB30B447DE3 /* VoiceMessageRecordingComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A5C3F7C9C1DA10CAEC6A98 /* VoiceMessageRecordingComposer.swift */; };
|
||||
E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */; };
|
||||
@@ -1650,6 +1654,7 @@
|
||||
29A953B6C0C431DBF4DD00B4 /* RoomSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummary.swift; sourceTree = "<group>"; };
|
||||
2A2BB38DF61F5100B8723112 /* TimelineMediaPreviewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaPreviewModels.swift; sourceTree = "<group>"; };
|
||||
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
||||
2A95C9B8299A36A6495DECA6 /* TracingHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracingHook.swift; sourceTree = "<group>"; };
|
||||
2AB2C848BB9A7A9B618B7B89 /* TextBasedRoomTimelineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBasedRoomTimelineTests.swift; sourceTree = "<group>"; };
|
||||
2ADF12A50186B75C68017B61 /* DeclineAndBlockScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeclineAndBlockScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
2AE807361805463F5AEDD1CA /* VoiceMessagePreviewComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessagePreviewComposer.swift; sourceTree = "<group>"; };
|
||||
@@ -1720,6 +1725,7 @@
|
||||
39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = "<group>"; };
|
||||
3A12D3D8138F1B71AFA7C858 /* CompletionSuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionService.swift; sourceTree = "<group>"; };
|
||||
3A21027F05874B1BCC3E452B /* InvitedRoomProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitedRoomProxyMock.swift; sourceTree = "<group>"; };
|
||||
3AB34956C87731AB094DB33A /* URLTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLTests.swift; sourceTree = "<group>"; };
|
||||
3AD253E7EFF88F308D644272 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SAS.strings"; sourceTree = "<group>"; };
|
||||
3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = "<group>"; };
|
||||
3B927A399A5418DA40A5CA15 /* StartChatScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenTests.swift; sourceTree = "<group>"; };
|
||||
@@ -3306,6 +3312,7 @@
|
||||
8B89D6C760E8CAE29CA28FB1 /* CompoundHook.swift */,
|
||||
D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */,
|
||||
B343C5255FB408DDE853CFDF /* RoomScreenHook.swift */,
|
||||
2A95C9B8299A36A6495DECA6 /* TracingHook.swift */,
|
||||
);
|
||||
path = Hooks;
|
||||
sourceTree = "<group>";
|
||||
@@ -4479,6 +4486,7 @@
|
||||
5C1F000589F2CEE6B03ECFAB /* TimelineMediaPreviewViewModelTests.swift */,
|
||||
6509708F54FC883604DFDC95 /* TimelineViewModelTests.swift */,
|
||||
76310030C831D4610A705603 /* URLComponentsTests.swift */,
|
||||
3AB34956C87731AB094DB33A /* URLTests.swift */,
|
||||
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */,
|
||||
2429224EB0EEA34D35CE9249 /* UserIndicatorControllerTests.swift */,
|
||||
BA241DEEF7C8A7181C0AEDC9 /* UserPreferenceTests.swift */,
|
||||
@@ -6976,6 +6984,7 @@
|
||||
CE4B342F9DD747CF4BEDB5AB /* TestablePreview.swift in Sources */,
|
||||
AC3C3D6D4AD31F13EE987390 /* TraceLogPack.swift in Sources */,
|
||||
B81840E45D8746A4692DA774 /* Tracing.swift in Sources */,
|
||||
2D45A04699BB6BA3B3A0CB9A /* TracingHook.swift in Sources */,
|
||||
DDB47D29C6865669288BF87C /* UIFont+AttributedStringBuilder.m in Sources */,
|
||||
45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */,
|
||||
281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */,
|
||||
@@ -7109,6 +7118,7 @@
|
||||
8E650379587C31D7912ED67B /* UNNotification+Creator.swift in Sources */,
|
||||
AF33B9044498211C3D82F1E1 /* UNTextInputNotificationResponse+Creator.swift in Sources */,
|
||||
20C16A3F718802B0E4A19C83 /* URLComponentsTests.swift in Sources */,
|
||||
49BBEC46D523BF6A41400048 /* URLTests.swift in Sources */,
|
||||
8D3E1FADD78E72504DE0E402 /* UserAgentBuilderTests.swift in Sources */,
|
||||
E313BDD2B8813144139B2E00 /* UserDiscoveryServiceTest.swift in Sources */,
|
||||
A1DF0E1E526A981ED6D5DF44 /* UserIndicatorControllerTests.swift in Sources */,
|
||||
@@ -7163,6 +7173,7 @@
|
||||
6E03A710799E6C65C0AB36BC /* TargetConfiguration.swift in Sources */,
|
||||
A5FD8284744E2FECFC842FC1 /* TraceLogPack.swift in Sources */,
|
||||
89DF67AECBF9D0EE0DDB7737 /* Tracing.swift in Sources */,
|
||||
51263FC33CC961A14F5D6BCA /* TracingHook.swift in Sources */,
|
||||
03BD83E8BDD23AE059802E0D /* UITestsScreenIdentifier.swift in Sources */,
|
||||
26252AA9AED64010788F4C26 /* UIView.swift in Sources */,
|
||||
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */,
|
||||
@@ -8032,6 +8043,7 @@
|
||||
B3D8AA9988F8A000B162DCB5 /* TombstonedAvatarImage.swift in Sources */,
|
||||
6E44638FDF7D4B0F80EFA7EA /* TraceLogPack.swift in Sources */,
|
||||
126CBCF5B0145FA1377C1316 /* Tracing.swift in Sources */,
|
||||
E010DDE938032D3B8E84CC35 /* TracingHook.swift in Sources */,
|
||||
298F9EC30E918F12AB7F1EE8 /* TypingIndicatorView.swift in Sources */,
|
||||
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
|
||||
A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */,
|
||||
|
||||
@@ -39,6 +39,11 @@ class AppHooks: AppHooksProtocol {
|
||||
}
|
||||
#endif
|
||||
|
||||
private(set) var tracingHook: TracingHookProtocol = DefaultTracingHook()
|
||||
func registerTracingHook(_ hook: TracingHookProtocol) {
|
||||
tracingHook = hook
|
||||
}
|
||||
|
||||
private(set) var clientBuilderHook: ClientBuilderHookProtocol = DefaultClientBuilderHook()
|
||||
func registerClientBuilderHook(_ hook: ClientBuilderHookProtocol) {
|
||||
clientBuilderHook = hook
|
||||
|
||||
@@ -18,7 +18,7 @@ protocol RemoteSettingsHookProtocol {
|
||||
func updateCache(using client: ClientProtocol) async
|
||||
func reset(_ appSettings: CommonSettingsProtocol)
|
||||
#endif
|
||||
func loadCache(forHomeserver: String, applyingTo appSettings: CommonSettingsProtocol)
|
||||
func loadCache(forHomeserver homeserver: String, applyingTo appSettings: CommonSettingsProtocol)
|
||||
}
|
||||
|
||||
struct DefaultRemoteSettingsHook: RemoteSettingsHookProtocol {
|
||||
@@ -51,7 +51,7 @@ struct DefaultRemoteSettingsHook: RemoteSettingsHookProtocol {
|
||||
func reset(_ appSettings: any CommonSettingsProtocol) { }
|
||||
#endif
|
||||
|
||||
func loadCache(forHomeserver: String, applyingTo appSettings: CommonSettingsProtocol) { }
|
||||
func loadCache(forHomeserver homeserver: String, applyingTo appSettings: CommonSettingsProtocol) { }
|
||||
}
|
||||
|
||||
private struct ElementWellKnown: Decodable {
|
||||
|
||||
16
ElementX/Sources/AppHooks/Hooks/TracingHook.swift
Normal file
16
ElementX/Sources/AppHooks/Hooks/TracingHook.swift
Normal file
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import MatrixRustSDK
|
||||
|
||||
protocol TracingHookProtocol {
|
||||
func update(_ configuration: TracingConfiguration, with rageshakeURL: RemotePreference<RageshakeConfiguration>)
|
||||
}
|
||||
|
||||
struct DefaultTracingHook: TracingHookProtocol {
|
||||
func update(_ configuration: TracingConfiguration, with rageshakeURL: RemotePreference<RageshakeConfiguration>) { }
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
private let stateMachine: AppCoordinatorStateMachine
|
||||
private let navigationRootCoordinator: NavigationRootCoordinator
|
||||
private let userSessionStore: UserSessionStoreProtocol
|
||||
private let targetConfiguration: Target.Configuration
|
||||
private let targetConfiguration: Target.ConfigurationResult
|
||||
private let appMediator: AppMediator
|
||||
private let appSettings: AppSettings
|
||||
private let appDelegate: AppDelegate
|
||||
@@ -77,7 +77,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
targetConfiguration = Target.mainApp.configure(logLevel: appSettings.logLevel,
|
||||
traceLogPacks: appSettings.traceLogPacks,
|
||||
sentryURL: appSettings.bugReportSentryRustURL)
|
||||
sentryURL: appSettings.bugReportSentryRustURL,
|
||||
rageshakeURL: appSettings.bugReportRageshakeURL,
|
||||
appHooks: appHooks)
|
||||
|
||||
let appName = InfoPlistReader.main.bundleDisplayName
|
||||
let appVersion = InfoPlistReader.main.bundleShortVersionString
|
||||
|
||||
@@ -12,7 +12,7 @@ import Combine
|
||||
///
|
||||
/// Unlike ``UserPreference``, this type of setting isn't settable by the user, nor is the
|
||||
/// remote value persisted between app launches.
|
||||
struct RemotePreference<T: Equatable> {
|
||||
class RemotePreference<T: Equatable> {
|
||||
private let defaultValue: T
|
||||
private let subject: CurrentValueSubject<T, Never>
|
||||
var publisher: CurrentValuePublisher<T, Never> { subject.asCurrentValuePublisher() }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
@@ -32,10 +33,14 @@ enum Target: String {
|
||||
|
||||
/// Configures the target with logging and an appropriate runtime.
|
||||
///
|
||||
/// Returns a `Configuration` which should be stored to
|
||||
/// Returns a `ConfigurationResult` which should be stored to
|
||||
/// a) detect whether the platform is already configured.
|
||||
/// b) reconfigure the platform if necessary.
|
||||
func configure(logLevel: LogLevel, traceLogPacks: Set<TraceLogPack>, sentryURL: URL?) -> Configuration {
|
||||
/// b) automatically reconfigure the platform as necessary.
|
||||
func configure(logLevel: LogLevel,
|
||||
traceLogPacks: Set<TraceLogPack>,
|
||||
sentryURL: URL?,
|
||||
rageshakeURL: RemotePreference<RageshakeConfiguration>,
|
||||
appHooks: AppHooks) -> ConfigurationResult {
|
||||
let tracingConfiguration = Tracing.buildConfiguration(logLevel: logLevel,
|
||||
traceLogPacks: traceLogPacks,
|
||||
currentTarget: rawValue,
|
||||
@@ -54,14 +59,21 @@ enum Target: String {
|
||||
|
||||
MXLog.configure(currentTarget: rawValue)
|
||||
|
||||
return Configuration(tracingConfiguration: tracingConfiguration)
|
||||
let hookCancellable = rageshakeURL.publisher
|
||||
.sink { _ in
|
||||
appHooks.tracingHook.update(tracingConfiguration, with: rageshakeURL)
|
||||
}
|
||||
|
||||
return ConfigurationResult(hookCancellable: hookCancellable)
|
||||
}
|
||||
|
||||
/// Represents the configuration that was applied by ``configure(logLevel:traceLogPacks:sentryURL:)``.
|
||||
struct Configuration {
|
||||
/// The configuration applied when calling ``configure(logLevel:traceLogPacks:sentryURL:)``.
|
||||
///
|
||||
/// **Note:** This is immutable and won't be updated to reflect further changes.
|
||||
let tracingConfiguration: TracingConfiguration
|
||||
/// The result of calling ``configure(logLevel:traceLogPacks:sentryURL:)``.
|
||||
/// This must be stored - see the docs on the configure method to learn more.
|
||||
struct ConfigurationResult {
|
||||
private let hookCancellable: AnyCancellable
|
||||
|
||||
init(hookCancellable: AnyCancellable) {
|
||||
self.hookCancellable = hookCancellable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ extension ClientSDKMock {
|
||||
slidingSyncVersionReturnValue = configuration.slidingSyncVersion
|
||||
userIdServerNameThrowableError = MockError.generic
|
||||
serverReturnValue = "https://\(configuration.serverAddress)"
|
||||
homeserverReturnValue = configuration.homeserverURL
|
||||
urlForOidcOidcConfigurationPromptLoginHintDeviceIdReturnValue = OAuthAuthorizationDataSDKMock(configuration: configuration)
|
||||
loginUsernamePasswordInitialDeviceNameDeviceIdClosure = { username, password, _, _ in
|
||||
guard username == configuration.validCredentials.username,
|
||||
|
||||
@@ -7,15 +7,9 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
public init(stringLiteral value: StaticString) {
|
||||
guard let url = URL(string: "\(value)") else {
|
||||
fatalError("The static string used to create this URL is invalid")
|
||||
}
|
||||
|
||||
self = url
|
||||
}
|
||||
// MARK: - Custom URLs
|
||||
|
||||
extension URL {
|
||||
/// The URL of the primary app group container.
|
||||
static var appGroupContainerDirectory: URL {
|
||||
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: InfoPlistReader.main.appGroupIdentifier) else {
|
||||
@@ -104,6 +98,45 @@ extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: Mocks
|
||||
|
||||
static var mockMXCAudio: URL { "mxc://matrix.org/1234567890AuDiO" }
|
||||
static var mockMXCFile: URL { "mxc://matrix.org/1234567890FiLe" }
|
||||
static var mockMXCImage: URL { "mxc://matrix.org/1234567890ImAgE" }
|
||||
static var mockMXCVideo: URL { "mxc://matrix.org/1234567890ViDeO" }
|
||||
static var mockMXCAvatar: URL { "mxc://matrix.org/1234567890AvAtAr" }
|
||||
static var mockMXCUserAvatar: URL { "mxc://matrix.org/1234567890AvAtArUsEr" }
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
public init(stringLiteral value: StaticString) {
|
||||
guard let url = URL(string: "\(value)") else {
|
||||
fatalError("The static string used to create this URL is invalid")
|
||||
}
|
||||
|
||||
self = url
|
||||
}
|
||||
|
||||
/// Sanitises the URL for use as the name of a directory.
|
||||
func asDirectoryName() -> String {
|
||||
absoluteString.asURLDirectoryName()
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
/// Assumes that the string is a URL and sanitises it for use as the name of a directory.
|
||||
func asURLDirectoryName() -> String {
|
||||
replacingOccurrences(of: "https://", with: "")
|
||||
.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
.replacing(/[:\/\p{C}]/, with: "-")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Phishing Confirmation URL
|
||||
|
||||
extension URL {
|
||||
static let confirmationScheme = "confirm"
|
||||
|
||||
var requiresConfirmation: Bool {
|
||||
@@ -117,15 +150,6 @@ extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
}
|
||||
return ConfirmURLParameters(queryItems: queryItems)
|
||||
}
|
||||
|
||||
// MARK: Mocks
|
||||
|
||||
static var mockMXCAudio: URL { "mxc://matrix.org/1234567890AuDiO" }
|
||||
static var mockMXCFile: URL { "mxc://matrix.org/1234567890FiLe" }
|
||||
static var mockMXCImage: URL { "mxc://matrix.org/1234567890ImAgE" }
|
||||
static var mockMXCVideo: URL { "mxc://matrix.org/1234567890ViDeO" }
|
||||
static var mockMXCAvatar: URL { "mxc://matrix.org/1234567890AvAtAr" }
|
||||
static var mockMXCUserAvatar: URL { "mxc://matrix.org/1234567890AvAtArUsEr" }
|
||||
}
|
||||
|
||||
struct ConfirmURLParameters {
|
||||
|
||||
Submodule Enterprise updated: 2333bf9890...6db7fe89bd
@@ -5,6 +5,7 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import MatrixRustSDK
|
||||
import UserNotifications
|
||||
|
||||
@@ -28,7 +29,7 @@ import UserNotifications
|
||||
// notification.
|
||||
|
||||
class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
private static var targetConfiguration: Target.Configuration?
|
||||
private static var targetConfiguration: Target.ConfigurationResult?
|
||||
private let settings: CommonSettingsProtocol = AppSettings()
|
||||
private let appHooks: AppHooks
|
||||
|
||||
@@ -36,6 +37,8 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
private let keychainController = KeychainController(service: .sessions,
|
||||
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
// We can make the whole NSE a MainActor after https://github.com/swiftlang/swift-evolution/blob/main/proposals/0371-isolated-synchronous-deinit.md
|
||||
// otherwise we wouldn't be able to log the tag in the deinit.
|
||||
deinit {
|
||||
@@ -50,8 +53,12 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
if Self.targetConfiguration == nil {
|
||||
Self.targetConfiguration = Target.nse.configure(logLevel: settings.logLevel,
|
||||
traceLogPacks: settings.traceLogPacks,
|
||||
sentryURL: nil)
|
||||
sentryURL: nil,
|
||||
rageshakeURL: settings.bugReportRageshakeURL,
|
||||
appHooks: appHooks)
|
||||
}
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest,
|
||||
|
||||
@@ -81,6 +81,7 @@ targets:
|
||||
- path: ../Sources
|
||||
- path: ../SupportingFiles
|
||||
- path: ../../ElementX/Sources/AppHooks/AppHooks.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientBuilderHook.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
||||
- path: ../../Secrets/Secrets.swift
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import IntentsUI
|
||||
import SwiftUI
|
||||
|
||||
class ShareExtensionViewController: UIViewController {
|
||||
private static var targetConfiguration: Target.Configuration?
|
||||
private static var targetConfiguration: Target.ConfigurationResult?
|
||||
private let appSettings: CommonSettingsProtocol = AppSettings()
|
||||
private var appHooks: AppHooks!
|
||||
|
||||
private let keychainController = KeychainController(service: .sessions,
|
||||
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||
|
||||
private var cancellables: Set<AnyCancellable> = []
|
||||
|
||||
private let hostingController = UIHostingController(rootView: ShareExtensionView())
|
||||
|
||||
override func viewDidLoad() {
|
||||
@@ -27,7 +30,9 @@ class ShareExtensionViewController: UIViewController {
|
||||
if Self.targetConfiguration == nil {
|
||||
Self.targetConfiguration = Target.shareExtension.configure(logLevel: appSettings.logLevel,
|
||||
traceLogPacks: appSettings.traceLogPacks,
|
||||
sentryURL: nil)
|
||||
sentryURL: nil,
|
||||
rageshakeURL: appSettings.bugReportRageshakeURL,
|
||||
appHooks: appHooks)
|
||||
}
|
||||
|
||||
addChild(hostingController)
|
||||
|
||||
@@ -81,6 +81,7 @@ targets:
|
||||
- path: ../SupportingFiles
|
||||
- path: ../../ElementX/Sources/ShareExtension
|
||||
- path: ../../ElementX/Sources/AppHooks/AppHooks.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/TracingHook.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/ClientBuilderHook.swift
|
||||
- path: ../../ElementX/Sources/AppHooks/Hooks/RemoteSettingsHook.swift
|
||||
- path: ../../Secrets/Secrets.swift
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import XCTest
|
||||
|
||||
class LoggingTests: XCTestCase {
|
||||
static var targetConfiguration: Target.Configuration?
|
||||
static var targetConfiguration: Target.ConfigurationResult?
|
||||
|
||||
private enum Constants {
|
||||
static let genericFailure = "Test failed"
|
||||
@@ -25,7 +25,11 @@ class LoggingTests: XCTestCase {
|
||||
XCTAssertTrue(Tracing.logFiles.isEmpty)
|
||||
|
||||
if Self.targetConfiguration == nil {
|
||||
Self.targetConfiguration = Target.tests.configure(logLevel: .info, traceLogPacks: [], sentryURL: nil)
|
||||
Self.targetConfiguration = Target.tests.configure(logLevel: .info,
|
||||
traceLogPacks: [],
|
||||
sentryURL: nil,
|
||||
rageshakeURL: ServiceLocator.shared.settings.bugReportRageshakeURL,
|
||||
appHooks: AppHooks())
|
||||
}
|
||||
|
||||
// There is something weird with Rust logging where the file writing handle doesn't
|
||||
@@ -180,7 +184,11 @@ class LoggingTests: XCTestCase {
|
||||
|
||||
// When logging that value
|
||||
if Self.targetConfiguration == nil {
|
||||
Self.targetConfiguration = Target.tests.configure(logLevel: .info, traceLogPacks: [], sentryURL: nil)
|
||||
Self.targetConfiguration = Target.tests.configure(logLevel: .info,
|
||||
traceLogPacks: [],
|
||||
sentryURL: nil,
|
||||
rageshakeURL: ServiceLocator.shared.settings.bugReportRageshakeURL,
|
||||
appHooks: AppHooks())
|
||||
}
|
||||
|
||||
MXLog.info(textMessage)
|
||||
|
||||
44
UnitTests/Sources/URLTests.swift
Normal file
44
UnitTests/Sources/URLTests.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
class URLTests: XCTestCase {
|
||||
func testURLDirectoryName() {
|
||||
let url: URL = "https://matrix.example.com/foo/bar/"
|
||||
let directoryName = url.asDirectoryName()
|
||||
XCTAssertEqual(directoryName, "matrix.example.com-foo-bar")
|
||||
createDirectory(with: directoryName)
|
||||
}
|
||||
|
||||
func testComplexURLDirectoryName() {
|
||||
let url: URL = "https://us%3Aer:pa%40%3Ass@[2001:db8:85a3::8a2e:370:7334]:8443/..//folder/./fi%20le(1).html;p=1;q=2"
|
||||
let directoryName = url.asDirectoryName()
|
||||
XCTAssertEqual(directoryName, "us%3Aer-pa%40%3Ass@[2001-db8-85a3--8a2e-370-7334]-8443-..--folder-.-fi%20le(1).html;p=1;q=2")
|
||||
createDirectory(with: directoryName)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
func createDirectory(with directoryName: String) {
|
||||
let url = URL.temporaryDirectory.appending(path: directoryName)
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
XCTFail("Invalid file path: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
guard FileManager.default.directoryExists(at: url) else {
|
||||
XCTFail("Invalid file path")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user