mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 06:58:28 +02:00
Move string formatting to Tools.
Revert contacts tracking from MatrixKit. Final tweaks before PR.
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -29,9 +29,6 @@ vendor/
|
||||
#
|
||||
Pods/
|
||||
|
||||
# Never commit auto-generated secrets even if pods are checked in
|
||||
Pods/CocoaPodsKeys/
|
||||
|
||||
## Ignore project files as we generate them with xcodegen (https://github.com/yonaskolb/XcodeGen)
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
|
||||
@@ -166,10 +166,12 @@ final class BuildSettings: NSObject {
|
||||
|
||||
// MARK: - Analytics
|
||||
#warning("Testing environment.")
|
||||
// Optional host for PostHog analytics. Set to nil to disable analytics.
|
||||
/// Host to use for PostHog analytics. Set to nil to disable analytics.
|
||||
static let analyticsHost: String? = "https://posthog-poc.lab.element.dev"
|
||||
// Public key for submitting analytics. Set to nil to disable analytics.
|
||||
/// Public key for submitting analytics. Set to nil to disable analytics.
|
||||
static let analyticsKey: String? = "rs-pJjsYJTuAkXJfhaMmPUNBhWliDyTKLOOxike6ck8"
|
||||
/// The URL to open with more information about analytics terms.
|
||||
static let analyticsTermsURL = URL(string: "https://element.io/cookie-policy")!
|
||||
|
||||
|
||||
// MARK: - Bug report
|
||||
|
||||
@@ -299,4 +299,4 @@ DEPENDENCIES
|
||||
xcode-install
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.31
|
||||
2.2.28
|
||||
|
||||
5
Podfile
5
Podfile
@@ -69,8 +69,8 @@ abstract_target 'RiotPods' do
|
||||
|
||||
# PostHog for analytics
|
||||
pod 'PostHog', '~> 1.4.4'
|
||||
# pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift'
|
||||
pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'
|
||||
pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift'
|
||||
# pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'
|
||||
|
||||
# Remove warnings from "bad" pods
|
||||
pod 'OLMKit', :inhibit_warnings => true
|
||||
@@ -129,6 +129,7 @@ abstract_target 'RiotPods' do
|
||||
|
||||
end
|
||||
|
||||
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
|
||||
|
||||
48
Podfile.lock
48
Podfile.lock
@@ -57,29 +57,16 @@ PODS:
|
||||
- LoggerAPI (1.9.200):
|
||||
- Logging (~> 1.1)
|
||||
- Logging (1.4.0)
|
||||
- MatrixKit (0.16.10):
|
||||
- Down (~> 0.11.0)
|
||||
- DTCoreText (~> 1.6.25)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.16.10)
|
||||
- MatrixSDK (= 0.20.10)
|
||||
- MatrixKit/Core (0.16.10):
|
||||
- Down (~> 0.11.0)
|
||||
- DTCoreText (~> 1.6.25)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.20.10)
|
||||
- MatrixSDK (0.20.10):
|
||||
- MatrixSDK/Core (= 0.20.10)
|
||||
- MatrixSDK/Core (0.20.10):
|
||||
- MatrixSDK (0.20.13):
|
||||
- MatrixSDK/Core (= 0.20.13)
|
||||
- MatrixSDK/Core (0.20.13):
|
||||
- AFNetworking (~> 4.0.0)
|
||||
- GZIP (~> 1.3.0)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.2.5)
|
||||
- Realm (= 10.16.0)
|
||||
- SwiftyBeaver (= 1.9.5)
|
||||
- MatrixSDK/JingleCallStack (0.20.10):
|
||||
- MatrixSDK/JingleCallStack (0.20.13):
|
||||
- JitsiMeetSDK (= 3.10.2)
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.2.5):
|
||||
@@ -115,19 +102,22 @@ PODS:
|
||||
- ZXingObjC/All (3.6.5)
|
||||
|
||||
DEPENDENCIES:
|
||||
- AnalyticsEvents (from `../matrix-analytics-events/AnalyticsEvents.podspec`)
|
||||
- AnalyticsEvents (from `https://github.com/matrix-org/matrix-analytics-events.git`, branch `release/swift`)
|
||||
- DGCollectionViewLeftAlignFlowLayout (~> 1.0.4)
|
||||
- Down (~> 0.11.0)
|
||||
- DSWaveformImage (~> 6.1.1)
|
||||
- DTCoreText (~> 1.6.25)
|
||||
- ffmpeg-kit-ios-audio (~> 4.5)
|
||||
- FLEX (~> 4.5.0)
|
||||
- FlowCommoniOS (~> 1.12.0)
|
||||
- GBDeviceInfo (~> 6.6.0)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- Introspect (~> 0.1)
|
||||
- KeychainAccess (~> 4.2.2)
|
||||
- KTCenterFlowLayout (~> 1.3.1)
|
||||
- MatrixKit (= 0.16.10)
|
||||
- MatrixSDK
|
||||
- MatrixSDK/JingleCallStack
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.20.13)
|
||||
- MatrixSDK/JingleCallStack (= 0.20.13)
|
||||
- OLMKit
|
||||
- PostHog (~> 1.4.4)
|
||||
- ReadMoreTextView (~> 3.0.1)
|
||||
@@ -167,7 +157,6 @@ SPEC REPOS:
|
||||
- libPhoneNumber-iOS
|
||||
- LoggerAPI
|
||||
- Logging
|
||||
- MatrixKit
|
||||
- MatrixSDK
|
||||
- OLMKit
|
||||
- PostHog
|
||||
@@ -186,11 +175,17 @@ SPEC REPOS:
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
AnalyticsEvents:
|
||||
:path: "../matrix-analytics-events/AnalyticsEvents.podspec"
|
||||
:branch: release/swift
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
AnalyticsEvents:
|
||||
:commit: aac06956d45cb86ea2bbd7a21b20b14ba8899fcf
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce
|
||||
AnalyticsEvents: 27b0d074e839d2d354d12ae679930e373cba5f45
|
||||
AnalyticsEvents: 333bf47d67dc628fadd29ce887b7ac93d8bd6e05
|
||||
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
|
||||
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
|
||||
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
|
||||
@@ -214,8 +209,7 @@ SPEC CHECKSUMS:
|
||||
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
|
||||
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
|
||||
Logging: beeb016c9c80cf77042d62e83495816847ef108b
|
||||
MatrixKit: c3f0bb056ceeb015e2f1688543ac4dbcf88bef2f
|
||||
MatrixSDK: 0e2ed8fc6f004cac4b4ab46f038a86fe49ce4007
|
||||
MatrixSDK: 945f082654830d7ae3a6e1e068b6dc22b2eae932
|
||||
OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5
|
||||
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
|
||||
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
|
||||
@@ -231,6 +225,6 @@ SPEC CHECKSUMS:
|
||||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 9819df47b1ebbcea325ba9f24baa92878f3d0efe
|
||||
PODFILE CHECKSUM: f4ad67860350a28588e177245d1d0aff0fdcf186
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
||||
@@ -165,13 +165,6 @@ extension Analytics {
|
||||
func trackIdentityServerAccepted(_ accepted: Bool) {
|
||||
// Do we still want to track this?
|
||||
}
|
||||
|
||||
/// Track whether the user granted or rejected access to the device contacts.
|
||||
/// **Note** This method isn't currently implemented.
|
||||
/// - Parameter granted: Whether access was granted.
|
||||
func trackContactsAccessGranted(_ granted: Bool) {
|
||||
// Do we still want to track this?
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - MXAnalyticsDelegate
|
||||
@@ -217,4 +210,9 @@ extension Analytics: MXAnalyticsDelegate {
|
||||
let event = AnalyticsEvent.JoinedRoom(isDM: isDM, roomSize: roomSize)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
/// **Note** This method isn't currently implemented.
|
||||
func trackContactsAccessGranted(_ granted: Bool) {
|
||||
// Do we still want to track this?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
protocol DictionaryConvertible: Encodable {
|
||||
var dictionary: [String: Any] { get }
|
||||
}
|
||||
|
||||
extension DictionaryConvertible {
|
||||
var dictionary: [String: Any] {
|
||||
let mirror = Mirror(reflecting: self)
|
||||
let dict: [String: Any] = Dictionary(uniqueKeysWithValues: mirror.children.compactMap { (label: String?, value: Any) in
|
||||
guard let label = label else { return nil }
|
||||
|
||||
// Handle standard types such as String/Int/Bool
|
||||
if let value = value as? NSCoding {
|
||||
return (label, value)
|
||||
}
|
||||
|
||||
// AnalyticsEvent enums
|
||||
if let value = value as? CustomStringConvertible {
|
||||
return (label, value.description)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return dict
|
||||
}
|
||||
}
|
||||
@@ -59,5 +59,4 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
|
||||
postHog?.screen(event.screenName.rawValue, properties: event.properties)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -101,14 +101,12 @@ final class RiotSettings: NSObject {
|
||||
|
||||
// MARK: Other
|
||||
|
||||
/// Whether the user has both seen the Matomo analytics prompt and declined it.
|
||||
/// This is used to prevent users who previously opted out from being asked again.
|
||||
/// Whether the user was previously shown the Matomo analytics prompt.
|
||||
var hasSeenAnalyticsPrompt: Bool {
|
||||
RiotSettings.defaults.object(forKey: UserDefaultsKeys.enableAnalytics) != nil
|
||||
}
|
||||
|
||||
/// Whether the user has both seen the Matomo analytics prompt and declined it.
|
||||
/// This is used to prevent users who previously opted out from being asked again.
|
||||
var hasDeclinedMatomoAnalytics: Bool {
|
||||
RiotSettings.defaults.object(forKey: UserDefaultsKeys.matomoAnalytics) != nil && !RiotSettings.defaults.bool(forKey: UserDefaultsKeys.matomoAnalytics)
|
||||
}
|
||||
@@ -119,11 +117,13 @@ final class RiotSettings: NSObject {
|
||||
RiotSettings.defaults.bool(forKey: UserDefaultsKeys.matomoAnalytics)
|
||||
}
|
||||
|
||||
/// Indicates if the device has already called identify for this session to PostHog.
|
||||
/// `true` when the user has opted in to send analytics.
|
||||
@UserDefault(key: UserDefaultsKeys.enableAnalytics, defaultValue: false, storage: defaults)
|
||||
var enableAnalytics
|
||||
|
||||
/// Indicates if the device has already called identify for this session to PostHog.
|
||||
/// This is separate to `enableAnalytics` as logging out will leave analytics
|
||||
/// enabled but reset identification.
|
||||
@UserDefault(key: "isIdentifiedForAnalytics", defaultValue: false, storage: defaults)
|
||||
var isIdentifiedForAnalytics
|
||||
|
||||
|
||||
@@ -648,7 +648,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
MXLogDebug(@"[AppDelegate] afterAppUnlockedByPin");
|
||||
|
||||
// Check if there is crash log to send
|
||||
if (Analytics.shared.isRunning)
|
||||
if (RiotSettings.shared.enableAnalytics)
|
||||
{
|
||||
[self checkExceptionToReport];
|
||||
}
|
||||
|
||||
@@ -883,7 +883,7 @@ manualChangeMessageForVideo:(NSString*)manualChangeMessageForVideo
|
||||
// Request address book access
|
||||
[[CNContactStore new] requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
|
||||
[Analytics.shared trackContactsAccessGranted:granted];
|
||||
[MXSDKOptions.sharedInstance.analyticsDelegate trackContactsAccessGranted:granted];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
|
||||
@@ -59,4 +59,14 @@
|
||||
*/
|
||||
+ (NSAttributedString *)setTextColorAlpha:(CGFloat)alpha inAttributedString:(NSAttributedString*)attributedString;
|
||||
|
||||
/** Builds an attributed string from a string containing html.
|
||||
@param htmlString The html string to use.
|
||||
@param allowedTags The html tags that should be allowed.
|
||||
|
||||
Note: It is recommended to include "p" and "body" tags in
|
||||
`allowedTags` as these are often added when parsing.
|
||||
*/
|
||||
+ (NSAttributedString * _Nonnull)attributedStringFromHTML:(NSString * _Nonnull)htmlString
|
||||
withAllowedTags:(NSArray<NSString *> * _Nonnull)allowedTags;
|
||||
|
||||
@end
|
||||
|
||||
@@ -138,4 +138,41 @@
|
||||
return string;
|
||||
}
|
||||
|
||||
+ (NSAttributedString *)attributedStringFromHTML:(NSString *)htmlString withAllowedTags:(NSArray<NSString *> *)allowedTags
|
||||
{
|
||||
UIFont *font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
|
||||
|
||||
// Do some sanitisation before finalizing the string
|
||||
DTHTMLAttributedStringBuilderWillFlushCallback sanitizeCallback = ^(DTHTMLElement *element) {
|
||||
[element sanitizeWith:allowedTags bodyFont:font imageHandler:nil];
|
||||
};
|
||||
|
||||
NSDictionary *options = @{
|
||||
DTUseiOS6Attributes: @(YES), // Enable it to be able to display the attributed string in a UITextView
|
||||
DTDefaultFontFamily: font.familyName,
|
||||
DTDefaultFontName: font.fontName,
|
||||
DTDefaultFontSize: @(font.pointSize),
|
||||
DTDefaultLinkDecoration: @(NO),
|
||||
DTWillFlushBlockCallBack: sanitizeCallback
|
||||
};
|
||||
|
||||
// Do not use the default HTML renderer of NSAttributedString because this method
|
||||
// runs on the UI thread which we want to avoid because renderHTMLString is called
|
||||
// most of the time from a background thread.
|
||||
// Use DTCoreText HTML renderer instead.
|
||||
// Using DTCoreText, which renders static string, helps to avoid code injection attacks
|
||||
// that could happen with the default HTML renderer of NSAttributedString which is a
|
||||
// webview.
|
||||
NSAttributedString *string = [[NSAttributedString alloc] initWithHTMLData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:options documentAttributes:NULL];
|
||||
|
||||
// Apply additional treatments
|
||||
string = [MXKTools removeDTCoreTextArtifacts:string];
|
||||
|
||||
if (!string) {
|
||||
return [[NSAttributedString alloc] initWithString:htmlString];
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
35
Riot/Utils/Tools.swift
Normal file
35
Riot/Utils/Tools.swift
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
extension Tools {
|
||||
/// Builds an attributed string by replacing a `%@` placeholder with the supplied link text and URL.
|
||||
/// - Parameters:
|
||||
/// - string: The string to be formatted.
|
||||
/// - link: The link text to be inserted.
|
||||
/// - url: The URL to be linked to.
|
||||
/// - Returns: An attributed string.
|
||||
static func format(_ string: String, with link: String, using url: URL) -> NSAttributedString {
|
||||
let baseString = NSMutableAttributedString(string: string)
|
||||
let attributedLink = NSAttributedString(string: link, attributes: [.link: url])
|
||||
|
||||
let linkRange = (baseString.string as NSString).range(of: "%@")
|
||||
baseString.replaceCharacters(in: linkRange, with: attributedLink)
|
||||
|
||||
return baseString
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,3 @@ extension AnalyticsPromptType: Identifiable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSAttributedString.Key {
|
||||
static let analyticsPromptTermsTextLink = NSAttributedString.Key("TermsTextLink")
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@ class AnalyticsPromptViewModel: AnalyticsPromptViewModelType {
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
let termsURL: URL
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@@ -37,7 +39,8 @@ class AnalyticsPromptViewModel: AnalyticsPromptViewModelType {
|
||||
// MARK: - Setup
|
||||
|
||||
/// Initialize a view model with the specified prompt type and app display name.
|
||||
init(promptType: AnalyticsPromptType, strings: AnalyticsPromptStringsProtocol) {
|
||||
init(promptType: AnalyticsPromptType, strings: AnalyticsPromptStringsProtocol, termsURL: URL) {
|
||||
self.termsURL = termsURL
|
||||
super.init(initialViewState: AnalyticsPromptViewState(promptType: promptType, strings: strings))
|
||||
}
|
||||
|
||||
@@ -70,7 +73,6 @@ class AnalyticsPromptViewModel: AnalyticsPromptViewModelType {
|
||||
|
||||
/// Open the service terms link.
|
||||
private func openTermsURL() {
|
||||
guard let url = URL(string: "https://element.io/cookie-policy") else { return }
|
||||
UIApplication.shared.open(url)
|
||||
UIApplication.shared.open(termsURL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ final class AnalyticsPromptCoordinator: Coordinator {
|
||||
promptType = .newUser(termsString: strings.termsNewUser)
|
||||
}
|
||||
|
||||
let viewModel = AnalyticsPromptViewModel(promptType: promptType, strings: strings)
|
||||
let viewModel = AnalyticsPromptViewModel(promptType: promptType, strings: strings, termsURL: BuildSettings.analyticsTermsURL)
|
||||
|
||||
let view = AnalyticsPrompt(viewModel: viewModel.context)
|
||||
_analyticsPromptViewModel = viewModel
|
||||
|
||||
@@ -20,52 +20,14 @@ import DTCoreText
|
||||
struct AnalyticsPromptStrings: AnalyticsPromptStringsProtocol {
|
||||
let appDisplayName = AppInfo.current.displayName
|
||||
|
||||
let point1: NSAttributedString
|
||||
let point2: NSAttributedString
|
||||
let point1 = Tools.attributedString(fromHTML: VectorL10n.analyticsPromptPoint1, withAllowedTags: ["b", "p"])
|
||||
let point2 = Tools.attributedString(fromHTML: VectorL10n.analyticsPromptPoint2, withAllowedTags: ["b", "p"])
|
||||
|
||||
let termsNewUser: NSAttributedString
|
||||
let termsUpgrade: NSAttributedString
|
||||
|
||||
init() {
|
||||
self.point1 = Self.parse(VectorL10n.analyticsPromptPoint1)
|
||||
self.point2 = Self.parse(VectorL10n.analyticsPromptPoint2)
|
||||
|
||||
self.termsNewUser = Self.attach(VectorL10n.analyticsPromptTermsLinkNewUser,
|
||||
to: VectorL10n.analyticsPromptTermsNewUser("%@"))
|
||||
self.termsUpgrade = Self.attach(VectorL10n.analyticsPromptTermsLinkUpgrade,
|
||||
to: VectorL10n.analyticsPromptTermsUpgrade("%@"))
|
||||
}
|
||||
|
||||
static func parse(_ htmlString: String) -> NSAttributedString {
|
||||
// Do some sanitisation before finalizing the string
|
||||
// let sanitizeCallback: DTHTMLAttributedStringBuilderWillFlushCallback = { element in
|
||||
// element?.sanitize(with: ["b"], bodyFont: .systemFont(ofSize: UIFont.systemFontSize), imageHandler: nil)
|
||||
// print("Hello")
|
||||
// }
|
||||
|
||||
let options: [String: Any] = [
|
||||
DTUseiOS6Attributes: true, // Enable it to be able to display the attributed string in a UITextView
|
||||
DTDefaultLinkDecoration: false,
|
||||
// DTWillFlushBlockCallBack: sanitizeCallback
|
||||
]
|
||||
|
||||
guard let attributedString = NSAttributedString(htmlData: htmlString.data(using: .utf8),
|
||||
options: options,
|
||||
documentAttributes: nil) else {
|
||||
return NSAttributedString(string: htmlString)
|
||||
}
|
||||
|
||||
return MXKTools.removeDTCoreTextArtifacts(attributedString)
|
||||
}
|
||||
|
||||
static func attach(_ link: String, to terms: String) -> NSAttributedString {
|
||||
let baseString = NSMutableAttributedString(string: terms)
|
||||
let linkRange = (baseString.string as NSString).range(of: "%@")
|
||||
let formattedLink = NSAttributedString(string: VectorL10n.analyticsPromptTermsLinkNewUser,
|
||||
attributes: [.analyticsPromptTermsTextLink: true])
|
||||
baseString.replaceCharacters(in: linkRange, with: formattedLink)
|
||||
|
||||
return baseString
|
||||
}
|
||||
let termsNewUser = Tools.format(VectorL10n.analyticsPromptTermsNewUser("%@"),
|
||||
with: VectorL10n.analyticsPromptTermsLinkNewUser,
|
||||
using: BuildSettings.analyticsTermsURL)
|
||||
let termsUpgrade = Tools.format(VectorL10n.analyticsPromptTermsUpgrade("%@"),
|
||||
with: VectorL10n.analyticsPromptTermsLinkUpgrade,
|
||||
using: BuildSettings.analyticsTermsURL)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,8 @@ enum MockAnalyticsPromptScreenState: MockScreenState, CaseIterable {
|
||||
promptType = analyticsPromptType
|
||||
}
|
||||
let viewModel = AnalyticsPromptViewModel(promptType: promptType,
|
||||
strings: MockAnalyticsPromptStrings())
|
||||
strings: MockAnalyticsPromptStrings(),
|
||||
termsURL: URL(string: "https://element.io/cookie-policy")!)
|
||||
|
||||
return (
|
||||
[promptType, viewModel],
|
||||
|
||||
@@ -40,12 +40,12 @@ struct MockAnalyticsPromptStrings: AnalyticsPromptStringsProtocol {
|
||||
self.point2 = point2
|
||||
|
||||
let termsNewUser = NSMutableAttributedString(string: "You can read all our terms ")
|
||||
termsNewUser.append(NSAttributedString(string: "here", attributes: [.analyticsPromptTermsTextLink: true]))
|
||||
termsNewUser.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!]))
|
||||
termsNewUser.append(NSAttributedString(string: "."))
|
||||
self.termsNewUser = termsNewUser
|
||||
|
||||
let termsUpgrade = NSMutableAttributedString(string: "Read all our terms ")
|
||||
termsUpgrade.append(NSAttributedString(string: "here", attributes: [.analyticsPromptTermsTextLink: true]))
|
||||
termsUpgrade.append(NSAttributedString(string: "here", attributes: [.link: URL(string: "https://element.io/cookie-policy")!]))
|
||||
termsUpgrade.append(NSAttributedString(string: ". Is that OK?"))
|
||||
self.termsUpgrade = termsUpgrade
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ struct AnalyticsPromptTermsText: View {
|
||||
let string = attributedString.string as NSString
|
||||
|
||||
attributedString.enumerateAttributes(in: range, options: []) { attributes, range, stop in
|
||||
let isLink = attributes.keys.contains(.analyticsPromptTermsTextLink)
|
||||
let isLink = attributes.keys.contains(.link)
|
||||
components.append(StringComponent(string: string.substring(with: range), isLink: isLink))
|
||||
}
|
||||
|
||||
|
||||
1
changelog.d/5035.change
Normal file
1
changelog.d/5035.change
Normal file
@@ -0,0 +1 @@
|
||||
Analytics: Replace Matomo with PostHog.
|
||||
Reference in New Issue
Block a user