mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-22 15:42:10 +02:00
Merge branch 'develop' into ismail/6181_auth_soft_logout
This commit is contained in:
@@ -563,7 +563,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
}
|
||||
self.setPinCoordinatorBridgePresenter = [[SetPinCoordinatorBridgePresenter alloc] initWithSession:mxSessionArray.firstObject viewMode:SetPinCoordinatorViewModeInactive];
|
||||
self.setPinCoordinatorBridgePresenter.delegate = self;
|
||||
[self.setPinCoordinatorBridgePresenter presentIn:self.window];
|
||||
[self.setPinCoordinatorBridgePresenter presentWithMainAppWindow:self.window];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,12 +663,12 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
{
|
||||
self.setPinCoordinatorBridgePresenter = [[SetPinCoordinatorBridgePresenter alloc] initWithSession:mxSessionArray.firstObject viewMode:SetPinCoordinatorViewModeUnlock];
|
||||
self.setPinCoordinatorBridgePresenter.delegate = self;
|
||||
[self.setPinCoordinatorBridgePresenter presentIn:self.window];
|
||||
[self.setPinCoordinatorBridgePresenter presentWithMainAppWindow:self.window];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.setPinCoordinatorBridgePresenter dismiss];
|
||||
[self.setPinCoordinatorBridgePresenter dismissWithMainAppWindow:self.window];
|
||||
self.setPinCoordinatorBridgePresenter = nil;
|
||||
[self afterAppUnlockedByPin:application];
|
||||
}
|
||||
@@ -4476,7 +4476,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
|
||||
- (void)setPinCoordinatorBridgePresenterDelegateDidComplete:(SetPinCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismiss];
|
||||
[coordinatorBridgePresenter dismissWithMainAppWindow:self.window];
|
||||
self.setPinCoordinatorBridgePresenter = nil;
|
||||
[self afterAppUnlockedByPin:[UIApplication sharedApplication]];
|
||||
}
|
||||
@@ -4490,7 +4490,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
}
|
||||
else
|
||||
{
|
||||
[coordinatorBridgePresenter dismiss];
|
||||
[coordinatorBridgePresenter dismissWithMainAppWindow:self.window];
|
||||
self.setPinCoordinatorBridgePresenter = nil;
|
||||
[self logoutWithConfirmation:NO completion:nil];
|
||||
}
|
||||
|
||||
@@ -149,6 +149,10 @@ extension LegacyAuthenticationCoordinator: AuthenticationServiceDelegate {
|
||||
authenticationViewController.showCustomHomeserver(homeserver, andIdentityServer: identityServer)
|
||||
}
|
||||
}
|
||||
|
||||
func authenticationService(_ service: AuthenticationService, needsPromptFor unrecognizedCertificate: Data?, completion: @escaping (Bool) -> Void) {
|
||||
// Handled internally in AuthenticationViewController
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AuthenticationViewControllerDelegate
|
||||
|
||||
@@ -34,10 +34,6 @@
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Round room image view
|
||||
[_roomAvatar.layer setCornerRadius:_roomAvatar.frame.size.width / 2];
|
||||
_roomAvatar.clipsToBounds = YES;
|
||||
|
||||
// Disable the user interaction on the room avatar.
|
||||
self.roomAvatar.userInteractionEnabled = NO;
|
||||
|
||||
@@ -73,6 +69,9 @@
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
[self.roomAvatar.layer setCornerRadius:self.roomAvatar.frame.size.width / 2.0];
|
||||
[self.roomAvatar setClipsToBounds: YES];
|
||||
}
|
||||
|
||||
- (void)render:(MXKCellData *)cellData
|
||||
|
||||
@@ -223,23 +223,11 @@ typedef enum : NSUInteger {
|
||||
|
||||
@param htmlString the HTLM string to render.
|
||||
@param event the event associated to the string.
|
||||
@param roomState the room state right before the event.
|
||||
@param roomState the room state right before the event. If nil, replies will not get constructed or formatted.
|
||||
@return an attributed string.
|
||||
*/
|
||||
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState;
|
||||
|
||||
/**
|
||||
Render a random html string into an attributed string with the font and the text color
|
||||
that correspond to the passed event.
|
||||
|
||||
@param htmlString the HTLM string to render.
|
||||
@param event the event associated to the string.
|
||||
@param roomState the room state right before the event.
|
||||
@param isEditMode wether string will be used for edition in the composer
|
||||
@return an attributed string.
|
||||
*/
|
||||
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState isEditMode:(BOOL)isEditMode;
|
||||
|
||||
/**
|
||||
Defines the replacement attributed string for a redacted message.
|
||||
|
||||
|
||||
@@ -1750,18 +1750,12 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
|
||||
}
|
||||
|
||||
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState
|
||||
{
|
||||
return [self renderHTMLString:htmlString forEvent:event withRoomState:roomState isEditMode:NO];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)renderHTMLString:(NSString*)htmlString forEvent:(MXEvent*)event withRoomState:(MXRoomState*)roomState isEditMode:(BOOL)isEditMode
|
||||
{
|
||||
NSString *html = htmlString;
|
||||
MXEvent *repliedEvent;
|
||||
|
||||
// Special treatment for "In reply to" message
|
||||
// Note: `isEditMode` fixes an issue where editing a reply would display an "In reply to" span instead of a mention.
|
||||
if (!isEditMode && (event.isReplyEvent || (!RiotSettings.shared.enableThreads && event.isInThread)))
|
||||
if (roomState && (event.isReplyEvent || (!RiotSettings.shared.enableThreads && event.isInThread)))
|
||||
{
|
||||
repliedEvent = [self->mxSession.store eventWithEventId:event.relatesTo.inReplyTo.eventId inRoom:roomState.roomId];
|
||||
if (repliedEvent)
|
||||
|
||||
@@ -181,6 +181,38 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
||||
toPresentable().present(alert, animated: true)
|
||||
}
|
||||
|
||||
/// Prompts the user to trust a certificate by displaying its fingerprint (SHA256).
|
||||
@MainActor private func displayUnrecognizedCertificateAlert(for certificate: Data) async -> Bool {
|
||||
await withCheckedContinuation { continuation in
|
||||
let title = VectorL10n.sslCouldNotVerify
|
||||
let homeserverURLString = VectorL10n.sslHomeserverUrl(authenticationService.state.homeserver.displayableAddress)
|
||||
let fingerprint = VectorL10n.sslFingerprintHash("SHA256")
|
||||
let certificateFingerprint = (certificate as NSData).mx_SHA256AsHexString() ?? VectorL10n.error
|
||||
|
||||
let message = [VectorL10n.sslCertNotTrust,
|
||||
VectorL10n.sslCertNewAccountExpl,
|
||||
homeserverURLString,
|
||||
fingerprint,
|
||||
certificateFingerprint,
|
||||
VectorL10n.sslOnlyAccept]
|
||||
.joined(separator: "\n\n")
|
||||
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
alert.addAction(UIAlertAction(title: VectorL10n.cancel, style: .cancel) { action in
|
||||
continuation.resume(with: .success(false))
|
||||
})
|
||||
|
||||
alert.addAction(UIAlertAction(title: VectorL10n.sslTrust, style: .default) { action in
|
||||
continuation.resume(with: .success(true))
|
||||
})
|
||||
|
||||
// The alert will be encountered on the current stack or when server selection is being presented.
|
||||
let presentingViewController = toPresentable().presentedViewController ?? toPresentable()
|
||||
presentingViewController.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
/// Cancels the registration flow, handing control back to the onboarding coordinator.
|
||||
@MainActor private func cancelRegistration() {
|
||||
authenticationService.reset()
|
||||
@@ -681,6 +713,19 @@ extension AuthenticationCoordinator: SSOAuthenticationPresenterDelegate {
|
||||
|
||||
// MARK: - AuthenticationServiceDelegate
|
||||
extension AuthenticationCoordinator: AuthenticationServiceDelegate {
|
||||
|
||||
func authenticationService(_ service: AuthenticationService, needsPromptFor unrecognizedCertificate: Data?, completion: @escaping (Bool) -> Void) {
|
||||
guard let certificate = unrecognizedCertificate else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
let trusted = await self.displayUnrecognizedCertificateAlert(for: certificate)
|
||||
completion(trusted)
|
||||
}
|
||||
}
|
||||
|
||||
func authenticationService(_ service: AuthenticationService, didReceive ssoLoginToken: String, with transactionID: String) -> Bool {
|
||||
guard let presenter = ssoAuthenticationPresenter, transactionID == ssoTransactionID else {
|
||||
Task { await displayError(message: VectorL10n.errorCommonMessage) }
|
||||
|
||||
@@ -156,7 +156,7 @@ extension RoomDataSource {
|
||||
|
||||
if event.isReply() {
|
||||
let body: String
|
||||
if let newContent = event.content[kMXMessageContentKeyNewContent] as? [String:Any] {
|
||||
if let newContent = event.content[kMXMessageContentKeyNewContent] as? [String: Any] {
|
||||
// Use new content if available.
|
||||
body = newContent["formatted_body"] as? String ?? newContent[kMXMessageBodyKey] as? String ?? ""
|
||||
} else {
|
||||
@@ -167,7 +167,7 @@ extension RoomDataSource {
|
||||
body = replyEventParts?.formattedBodyParts?.replyText ?? replyEventParts?.bodyParts.replyText ?? ""
|
||||
}
|
||||
|
||||
let attributed = eventFormatter.renderHTMLString(body, for: event, with: self.roomState, isEditMode: true)
|
||||
let attributed = eventFormatter.renderHTMLString(body, for: event, with: nil)
|
||||
if let attributed = attributed, #available(iOS 15.0, *) {
|
||||
editableTextMessage = PillsFormatter.insertPills(in: attributed,
|
||||
withSession: self.mxSession,
|
||||
@@ -180,7 +180,7 @@ extension RoomDataSource {
|
||||
}
|
||||
} else {
|
||||
let body: String = event.content["formatted_body"] as? String ?? event.content["body"] as? String ?? ""
|
||||
let attributed = eventFormatter.renderHTMLString(body, for: event, with: self.roomState, isEditMode: true)
|
||||
let attributed = eventFormatter.renderHTMLString(body, for: event, with: nil)
|
||||
if let attributed = attributed, #available(iOS 15.0, *) {
|
||||
editableTextMessage = PillsFormatter.insertPills(in: attributed,
|
||||
withSession: self.mxSession,
|
||||
|
||||
@@ -1077,6 +1077,12 @@ static BOOL _disableLongPressGestureOnEvent;
|
||||
|
||||
NSArray *bubbleComponents = bubbleData.bubbleComponents;
|
||||
|
||||
if (bubbleComponents.count == 1) {
|
||||
return bubbleComponents.firstObject;
|
||||
}
|
||||
|
||||
// The position check below fails for bubble data with a single component when message
|
||||
// bubbles are enabled, thus the early bailout above
|
||||
for (MXKRoomBubbleComponent *component in bubbleComponents)
|
||||
{
|
||||
// Ignore components without display (For example redacted event or state events)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
@objc enum SetPinCoordinatorViewMode: Int {
|
||||
case setPin
|
||||
@@ -49,6 +50,8 @@ final class SetPinCoordinatorBridgePresenter: NSObject {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var pinCoordinatorWindow: UIWindow?
|
||||
|
||||
private let session: MXSession?
|
||||
private var coordinator: SetPinCoordinator?
|
||||
var viewMode: SetPinCoordinatorViewMode {
|
||||
@@ -87,18 +90,23 @@ final class SetPinCoordinatorBridgePresenter: NSObject {
|
||||
self.coordinator = setPinCoordinator
|
||||
}
|
||||
|
||||
func present(in window: UIWindow) {
|
||||
func presentWithMainAppWindow(_ window: UIWindow) {
|
||||
let pinCoordinatorWindow = UIWindow(frame: window.bounds)
|
||||
|
||||
let setPinCoordinator = SetPinCoordinator(session: self.session, viewMode: self.viewMode, pinCodePreferences: .shared)
|
||||
setPinCoordinator.delegate = self
|
||||
guard let view = setPinCoordinator.toPresentable().view else { return }
|
||||
window.addSubview(view)
|
||||
view.leadingAnchor.constraint(equalTo: window.leadingAnchor, constant: 0).isActive = true
|
||||
view.trailingAnchor.constraint(equalTo: window.trailingAnchor, constant: 0).isActive = true
|
||||
view.topAnchor.constraint(equalTo: window.topAnchor, constant: 0).isActive = true
|
||||
view.bottomAnchor.constraint(equalTo: window.bottomAnchor, constant: 0).isActive = true
|
||||
pinCoordinatorWindow.addSubview(view)
|
||||
view.leadingAnchor.constraint(equalTo: pinCoordinatorWindow.leadingAnchor, constant: 0).isActive = true
|
||||
view.trailingAnchor.constraint(equalTo: pinCoordinatorWindow.trailingAnchor, constant: 0).isActive = true
|
||||
view.topAnchor.constraint(equalTo: pinCoordinatorWindow.topAnchor, constant: 0).isActive = true
|
||||
view.bottomAnchor.constraint(equalTo: pinCoordinatorWindow.bottomAnchor, constant: 0).isActive = true
|
||||
|
||||
pinCoordinatorWindow.makeKeyAndVisible()
|
||||
|
||||
setPinCoordinator.start()
|
||||
|
||||
self.pinCoordinatorWindow = pinCoordinatorWindow
|
||||
self.coordinator = setPinCoordinator
|
||||
}
|
||||
|
||||
@@ -106,6 +114,7 @@ final class SetPinCoordinatorBridgePresenter: NSObject {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.toPresentable().dismiss(animated: animated) {
|
||||
self.coordinator = nil
|
||||
|
||||
@@ -115,11 +124,10 @@ final class SetPinCoordinatorBridgePresenter: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
guard let coordinator = self.coordinator else {
|
||||
return
|
||||
}
|
||||
coordinator.toPresentable().view.removeFromSuperview()
|
||||
func dismissWithMainAppWindow(_ window: UIWindow) {
|
||||
window.makeKeyAndVisible()
|
||||
pinCoordinatorWindow = nil
|
||||
coordinator = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,24 +46,30 @@ class URLValidator: NSObject {
|
||||
/// - event: Event containing the link
|
||||
/// - Returns: Validation result
|
||||
static func validateTappedURL(_ url: URL, in event: MXEvent) -> URLValidationResult {
|
||||
if let format = event.content["format"] as? String,
|
||||
let formattedBody = event.content["formatted_body"] as? String {
|
||||
guard let content = event.content else {
|
||||
return .passed
|
||||
}
|
||||
|
||||
if let format = content["format"] as? String,
|
||||
let formattedBody = content["formatted_body"] as? String {
|
||||
if format == kMXRoomMessageFormatHTML {
|
||||
let visibleURL = FormattedBodyParser().getVisibleURL(forURL: url, inFormattedBody: formattedBody)
|
||||
if url != visibleURL {
|
||||
if let visibleURL = FormattedBodyParser().getVisibleURL(forURL: url, inFormattedBody: formattedBody),
|
||||
url != visibleURL {
|
||||
// urls are different, show confirmation alert
|
||||
return .init(shouldShowConfirmationAlert: true,
|
||||
visibleURLString: visibleURL?.absoluteString)
|
||||
visibleURLString: visibleURL.absoluteString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let body = event.content[kMXMessageBodyKey] as? String,
|
||||
body.vc_containsRTLOverride(),
|
||||
body != url.absoluteString {
|
||||
body.vc_containsRTLOverride(),
|
||||
body != url.absoluteString {
|
||||
// we don't know where the url is in the body, assuming visibleString is just a reverse of the url
|
||||
return .init(shouldShowConfirmationAlert: true,
|
||||
visibleURLString: url.absoluteString.vc_reversed())
|
||||
}
|
||||
|
||||
return .passed
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user