Merge branch 'develop' into feature/5365_photos_picker

This commit is contained in:
Arnfried Griesert
2024-02-06 10:21:02 +01:00
30 changed files with 191 additions and 90 deletions

View File

@@ -58,6 +58,9 @@ class AppConfiguration: CommonConfiguration {
// bwi: #4941 deactivate voice broadcast
RiotSettings.shared.enableVoiceBroadcast = BWIBuildSettings.shared.enableFeatureVoiceBroadcastsByDefault
// bwi: #5506 activate use latest avatar
RiotSettings.shared.roomScreenUseOnlyLatestUserAvatarAndName = BWIBuildSettings.shared.enableRoomScreenUseOnlyLatestUserAvatarAndNameByDefault
}

View File

@@ -208,7 +208,8 @@ class BWIBuildSettings: NSObject {
"36a9ec7368bddedd9deb1e2d1c627bd7304865135c9be30b1979659e3ac9ad07",
"dbaf8618e8a2f8d681591dfbcc73243c921c10dec69a2e5ee50bc91ca7dedcda",
"ed1af0fd873ec749f17c3b61ce4e481ab1644c132003f97a9c4e36516325788a",
"081e6ef90ba86102d678756fd13b07ca744340ad4d58a340e1956dca992f18e3"
"081e6ef90ba86102d678756fd13b07ca744340ad4d58a340e1956dca992f18e3",
"40b22592f2417c8031a0c38098c83dd0bfd28dee4c77ed1e9a022556c6ec0ded"
]
// use a different badge color if the user was mentioned in a room
@@ -544,6 +545,9 @@ class BWIBuildSettings: NSObject {
var bwiShowTimelineSettings = false
// #5506 alway use the latest avatar for data privacy
var enableRoomScreenUseOnlyLatestUserAvatarAndNameByDefault = true
// MARK BWI personal state
var bwiPersonalState = false
@@ -715,4 +719,8 @@ class BWIBuildSettings: NSObject {
// MARK: Contacts List
var showContactIdentifierInDetailRow = true
// MARK: Room Participants
// activates swipe gesture action to remove participants from room (RoomParticipantsViewController)
var bwiCanEditRoomParticipants = false
}

View File

@@ -295,7 +295,8 @@ final class RiotSettings: NSObject {
///
/// Note: this is set up from Room perspective, which means that if a user updates their profile after
/// leaving a Room, it will show up the latest profile used in the Room rather than the latest overall.
@UserDefault(key: "roomScreenUseOnlyLatestUserAvatarAndName", defaultValue: BuildSettings.roomScreenUseOnlyLatestUserAvatarAndName, storage: defaults)
// bwi: #5506 alway use the latest avatar
@UserDefault(key: "roomScreenUseOnlyLatestUserAvatarAndName", defaultValue: BWIBuildSettings.shared.enableRoomScreenUseOnlyLatestUserAvatarAndNameByDefault, storage: defaults)
var roomScreenUseOnlyLatestUserAvatarAndName
// MARK: - Room Contextual Menu

View File

@@ -123,7 +123,7 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType, Obse
return alias
} else if BWIBuildSettings.shared.bwiAutoCreateAliasOnRoomCreation {
if let roomName = roomCreationParameters.name {
return self.createAlias(from: roomName)
return MXTools.createAlias(from: roomName)
}
}
@@ -248,14 +248,4 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType, Obse
self.currentOperation?.cancel()
}
// MARK bwi Alias creation
private func createAlias(from roomName: String) -> String {
var alias = roomName.trimmingCharacters(in: .whitespacesAndNewlines).filter { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".contains($0) }
let timeInterval = String(Int(NSDate().timeIntervalSince1970))
alias.append(timeInterval)
return alias
}
}

View File

@@ -1747,7 +1747,7 @@ static NSString *const kRepliedTextPattern = @"<mx-reply>.*<blockquote>.*<br>(.*
{
return nil;
}
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:string];
NSRange wholeString = NSMakeRange(0, str.length);
@@ -1795,7 +1795,12 @@ static NSString *const kRepliedTextPattern = @"<mx-reply>.*<blockquote>.*<br>(.*
{
// body font is the same with the whole string font, no need to change body font
// apply additional treatments
[self postRenderAttributedString:str];
// bwi #5290 remove links from status cells
if (!event.isState) {
[self postRenderAttributedString:str];
}
return str;
}
@@ -1820,6 +1825,7 @@ static NSString *const kRepliedTextPattern = @"<mx-reply>.*<blockquote>.*<br>(.*
[str addAttribute:NSFontAttributeName value:fontForBody range:bodyRange];
// apply additional treatments
[self postRenderAttributedString:str];
return str;
}

View File

@@ -1151,9 +1151,13 @@
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == participantsSection || indexPath.section == invitedSection)
// bwi: #5671 deactivate swipe gesture - remove participant from room
if (BWIBuildSettings.shared.bwiCanEditRoomParticipants)
{
return YES;
if (indexPath.section == participantsSection || indexPath.section == invitedSection)
{
return YES;
}
}
return NO;
}

View File

@@ -1709,6 +1709,9 @@ BOOL reloadToggleCell = false;
if (serverACLRule)
{
// bwi: #5383 set alias
[mxRoom setAliasIfNeededWithRoomName:mxRoom.summary.displayName];
NSDictionary *content = [mxRoom createServerACLContentWithServerACL:serverACLRule];
pendingOperation = [mxRoom sendStateEventOfType:kMXEventTypeStringRoomServerACL content:content stateKey:nil success:^(NSString *eventId) {

View File

@@ -47,6 +47,13 @@
}
}
- (void)setupMessageTextView {
[super setupMessageTextView];
// bwi #5290 remove links from status cells
self.messageTextView.dataDetectorTypes = UIDataDetectorTypeNone;
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -35,7 +35,6 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW">
<rect key="frame" x="515" y="3" width="70" height="31"/>
@@ -46,7 +45,7 @@
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="stw-MD-khQ">
<rect key="frame" x="7" y="3" width="586" height="28"/>
<rect key="frame" x="8" y="3" width="584" height="28"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</subviews>

View File

@@ -1,12 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -16,10 +13,10 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<rect key="frame" x="0.0" y="0.0" width="600" height="69.5"/>
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="df5-x1-Zhy">
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="df5-x1-Zhy">
<rect key="frame" x="531" y="3" width="54" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<state key="normal" title="collapse"/>
@@ -28,7 +25,7 @@
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dJl-MG-cbs">
<rect key="frame" x="55" y="35" width="530" height="1"/>
<rect key="frame" x="55" y="38" width="530" height="1"/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="Jyb-5H-rlc"/>
@@ -44,7 +41,7 @@
</constraints>
</view>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="HTH-5n-MSU">
<rect key="frame" x="71" y="39" width="102" height="30.5"/>
<rect key="frame" x="79" y="39" width="102" height="31"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="MessageTextView"/>
<constraints>
@@ -53,10 +50,9 @@
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW">
<rect key="frame" x="515" y="42" width="70" height="27.5"/>
<rect key="frame" x="515" y="42" width="70" height="28"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="BubbleInfoContainer"/>
<constraints>
@@ -64,7 +60,7 @@
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="stw-MD-khQ">
<rect key="frame" x="0.0" y="0.0" width="600" height="79.5"/>
<rect key="frame" x="8" y="3" width="584" height="64"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</subviews>
@@ -108,6 +104,7 @@
<outlet property="pictureViewTopConstraint" destination="2Ih-ga-N9s" id="rpQ-bN-moE"/>
<outlet property="separatorView" destination="dJl-MG-cbs" id="Vnp-K7-DEh"/>
</connections>
<point key="canvasLocation" x="-218" y="-79"/>
</tableViewCell>
</objects>
</document>

View File

@@ -58,6 +58,7 @@ targets:
- path: ../bwi/MatomoAnalytics/DecryptedEvent.swift
- path: ../bwi/MatomoAnalytics/E2EEError.swift
- path: ../bwi/URLScheme/CustomURLSchemeHelper.swift
- path: ../bwi/Extentions/MXTools+Alias.swift
- path: ../bwi/Federation/MXRoom+Federation.swift
- path: ../Riot/Managers/Settings/RiotSettings.swift
- path: ../Config/BuildSettings.swift

View File

@@ -40,11 +40,11 @@ enum MockAuthenticationChoosePasswordScreenState: MockScreenState, CaseIterable
case .emptyPassword:
viewModel = AuthenticationChoosePasswordViewModel()
case .enteredInvalidPassword:
viewModel = AuthenticationChoosePasswordViewModel(password: "1234")
viewModel = AuthenticationChoosePasswordViewModel(password: "1234") //NOSONAR Password for mock Unittests
case .enteredValidPassword:
viewModel = AuthenticationChoosePasswordViewModel(password: "12345678")
viewModel = AuthenticationChoosePasswordViewModel(password: "12345678") //NOSONAR Password for mock Unittests
case .enteredValidPasswordAndSignoutAllDevicesChecked:
viewModel = AuthenticationChoosePasswordViewModel(password: "12345678", signoutAllDevices: true)
viewModel = AuthenticationChoosePasswordViewModel(password: "12345678", signoutAllDevices: true) //NOSONAR Password for mock Unittests
}
// can simulate service and viewModel actions here if needs be.

View File

@@ -71,7 +71,7 @@ class AuthenticationLoginViewModelTests: XCTestCase {
// When entering a password without a username.
context.username = ""
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
// Then the credentials should be considered invalid.
XCTAssertFalse(context.viewState.hasValidCredentials, "The credentials should be invalid.")
@@ -85,7 +85,7 @@ class AuthenticationLoginViewModelTests: XCTestCase {
// When entering a username and an 8-character password.
context.username = "bob"
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
// Then the credentials should be considered valid.
XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid when the username and password are valid.")
@@ -94,7 +94,7 @@ class AuthenticationLoginViewModelTests: XCTestCase {
@MainActor func testLoadingServer() {
// Given a form with valid credentials.
context.username = "bob"
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid.")
XCTAssertTrue(context.viewState.canSubmit, "The form should be valid to submit.")
XCTAssertFalse(context.viewState.isLoading, "The view shouldn't start in a loading state.")

View File

@@ -150,7 +150,7 @@ import XCTest
// When entering a password of 7 characters without a username.
context.username = ""
context.password = "1234567"
context.password = "1234567" //NOSONAR Password for Unittests
// Then the credentials should remain invalid.
XCTAssertTrue(context.viewState.isPasswordInvalid, "A 7-character password should be invalid.")
@@ -168,7 +168,7 @@ import XCTest
// When entering a password of 8 characters without a username.
context.username = ""
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
// Then the password should be valid but the credentials should still be invalid.
XCTAssertFalse(context.viewState.isPasswordInvalid, "An 8-character password should be valid.")
@@ -204,7 +204,7 @@ import XCTest
// When entering a username and password and encountering a username error
context.username = "bob"
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
let errorMessage = "Username unavailable"
viewModel.displayError(.usernameUnavailable(errorMessage))
@@ -225,7 +225,7 @@ import XCTest
// When entering a username and an 8-character password.
context.username = "bob"
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
// Then the credentials should be considered valid.
XCTAssertFalse(context.viewState.isPasswordInvalid, "An 8-character password should be valid.")
@@ -236,7 +236,7 @@ import XCTest
@MainActor func testLoadingServer() {
// Given a form with valid credentials.
context.username = "bob"
context.password = "12345678"
context.password = "12345678" //NOSONAR Password for Unittests
XCTAssertTrue(context.viewState.hasValidCredentials, "The credentials should be valid.")
XCTAssertTrue(context.viewState.canSubmit, "The form should be valid to submit.")
XCTAssertFalse(context.viewState.isLoading, "The view shouldn't start in a loading state.")

View File

@@ -51,7 +51,7 @@ enum MockAuthenticationSoftLogoutScreenState: MockScreenState, CaseIterable {
viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials,
homeserver: .mockMatrixDotOrg,
keyBackupNeeded: true,
password: "12345678")
password: "12345678") //NOSONAR Password for mock Unittests
case .ssoOnly:
viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials,
homeserver: .mockEnterpriseSSO,

View File

@@ -40,16 +40,16 @@ enum MockChangePasswordScreenState: MockScreenState, CaseIterable {
case .allEmpty:
viewModel = ChangePasswordViewModel()
case .cannotSubmit:
viewModel = ChangePasswordViewModel(oldPassword: "12345678",
newPassword1: "87654321")
viewModel = ChangePasswordViewModel(oldPassword: "12345678", //NOSONAR Password for mock Unittests
newPassword1: "87654321") //NOSONAR Password for mock Unittests
case .canSubmit:
viewModel = ChangePasswordViewModel(oldPassword: "12345678",
newPassword1: "87654321",
newPassword2: "87654321")
viewModel = ChangePasswordViewModel(oldPassword: "12345678", //NOSONAR Password for mock Unittests
newPassword1: "87654321", //NOSONAR Password for mock Unittests
newPassword2: "87654321") //NOSONAR Password for mock Unittests
case .canSubmitAndSignoutAllDevicesChecked:
viewModel = ChangePasswordViewModel(oldPassword: "12345678",
newPassword1: "87654321",
newPassword2: "87654321",
viewModel = ChangePasswordViewModel(oldPassword: "12345678", //NOSONAR Password for mock Unittests
newPassword1: "87654321", //NOSONAR Password for mock Unittests
newPassword2: "87654321", //NOSONAR Password for mock Unittests
signoutAllDevices: true)
}

View File

@@ -32,9 +32,9 @@ class ChangePasswordViewModelTests: XCTestCase {
}
@MainActor func testValidState() async {
let viewModel = ChangePasswordViewModel(oldPassword: "12345678",
newPassword1: "87654321",
newPassword2: "87654321",
let viewModel = ChangePasswordViewModel(oldPassword: "12345678", //NOSONAR Password for mock Unittests
newPassword1: "87654321", //NOSONAR Password for mock Unittests
newPassword2: "87654321", //NOSONAR Password for mock Unittests
signoutAllDevices: true)
let context = viewModel.context

View File

@@ -106,7 +106,7 @@ extension UserSessionInfo {
name: "Element Mobile: iOS",
deviceType: .mobile,
verificationState: verificationState,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: hasTimestamp ? Date().timeIntervalSince1970 : nil,
applicationName: "Element iOS",
applicationVersion: "1.9.14",

View File

@@ -146,7 +146,7 @@ struct UserSessionCardViewPreview: View {
name: "iOS",
deviceType: .mobile,
verificationState: verificationState,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",

View File

@@ -90,7 +90,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: nil,
applicationVersion: nil,
@@ -106,7 +106,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -122,7 +122,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "Firefox on Windows",
deviceType: .web,
verificationState: .verified,
lastSeenIP: "2.0.0.2",
lastSeenIP: "2.0.0.2", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 9_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -138,7 +138,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "Android",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "3.0.0.3",
lastSeenIP: "3.0.0.3", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 10_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -157,7 +157,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: nil,
applicationVersion: nil,
@@ -173,7 +173,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .unverified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -192,7 +192,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .verified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: nil,
applicationVersion: nil,
@@ -208,7 +208,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -227,7 +227,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 500_000,
applicationName: nil,
applicationVersion: nil,
@@ -243,7 +243,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -259,7 +259,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "Firefox on Windows",
deviceType: .web,
verificationState: .verified,
lastSeenIP: "2.0.0.2",
lastSeenIP: "2.0.0.2", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 9_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -275,7 +275,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "Android",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "3.0.0.3",
lastSeenIP: "3.0.0.3", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 10_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -291,7 +291,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 11_000_000,
applicationName: nil,
applicationVersion: nil,
@@ -307,7 +307,7 @@ enum MockUserOtherSessionsScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 20_000_000,
applicationName: nil,
applicationVersion: nil,

View File

@@ -377,7 +377,7 @@ class UserOtherSessionsViewModelTests: XCTestCase {
name: "iOS",
deviceType: .mobile,
verificationState: verificationState,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
applicationName: nil,
applicationVersion: nil,

View File

@@ -45,7 +45,7 @@ enum MockUserSessionDetailsScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",

View File

@@ -55,7 +55,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
name: "iOS",
deviceType: .mobile,
verificationState: state,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
@@ -73,7 +73,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: state,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
applicationName: "Element MacOS",
applicationVersion: "1.0.0",
@@ -91,7 +91,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
applicationName: "Element MacOS",
applicationVersion: "1.0.0",
@@ -109,7 +109,7 @@ enum MockUserSessionOverviewScreenState: MockScreenState, CaseIterable {
name: "macOS",
deviceType: .desktop,
verificationState: .verified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 130_000,
applicationName: "Element MacOS",
applicationVersion: "1.0.0",

View File

@@ -90,7 +90,7 @@ class UserSessionOverviewViewModelTests: XCTestCase {
name: "iOS",
deviceType: .mobile,
verificationState: .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
applicationName: "Element iOS",
applicationVersion: "1.9.7",

View File

@@ -87,7 +87,7 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
name: "iOS",
deviceType: .mobile,
verificationState: mode == .currentSessionVerified ? .verified : .unverified,
lastSeenIP: "10.0.0.10",
lastSeenIP: "10.0.0.10", //NOSONAR mocking ip for testing
lastSeenTimestamp: nil,
applicationName: "Element iOS",
applicationVersion: "1.0.0",
@@ -106,7 +106,7 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
name: "macOS verified: \(verified) active: \(active)",
deviceType: .desktop,
verificationState: verified ? .verified : .unverified,
lastSeenIP: "1.0.0.1",
lastSeenIP: "1.0.0.1", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 8_000_000,
applicationName: "Element MacOS",
applicationVersion: "1.0.0",
@@ -122,7 +122,7 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
name: "Firefox on Windows verified: \(verified) active: \(active)",
deviceType: .web,
verificationState: verified ? .verified : .unverified,
lastSeenIP: "2.0.0.2",
lastSeenIP: "2.0.0.2", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 100,
applicationName: "Element Web",
applicationVersion: "1.0.0",
@@ -138,7 +138,7 @@ class MockUserSessionsOverviewService: UserSessionsOverviewServiceProtocol {
name: "Android verified: \(verified) active: \(active)",
deviceType: .mobile,
verificationState: verified ? .verified : .unverified,
lastSeenIP: "3.0.0.3",
lastSeenIP: "3.0.0.3", //NOSONAR mocking ip for testing
lastSeenTimestamp: Date().timeIntervalSince1970 - 10,
applicationName: "Element Android",
applicationVersion: "1.0.0",

View File

@@ -0,0 +1,30 @@
//
/*
* Copyright (c) 2024 BWI GmbH
*
* 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 MXTools {
static func createAlias(from roomName: String) -> String {
var alias = roomName.trimmingCharacters(in: .whitespacesAndNewlines).filter { "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".contains($0) }
let timeInterval = String(Int(NSDate().timeIntervalSince1970))
alias.append(timeInterval)
return alias
}
}

View File

@@ -145,4 +145,45 @@ import MatrixSDK
return content
}
// #5383 Check if no canonical alias exists, create alias from name and timestamp and add as canonical alias
func setAliasIfNeeded(roomName: String?) {
Task {
do {
var needsAlias: Bool = false
let state = try await state()
// Check if canonical exists
if let canonicalAlias = state.canonicalAlias {
if canonicalAlias.isEmpty {
needsAlias = true
} else {
needsAlias = false
}
} else {
needsAlias = true
}
if needsAlias {
if let roomName = roomName {
if !roomName.isEmpty {
// Create new alias
var newAlias = MXTools.createAlias(from: roomName)
// Generate local alias
newAlias = MXTools.fullLocalAlias(from: newAlias, with: mxSession)
// Add local alias
addAlias(newAlias) { [self] response in
if response.isSuccess {
// Set new alias as canonical alias
setCanonicalAlias(newAlias) { response in
return
}
}
return
}
}
}
}
} catch { return }
}
}
}

View File

@@ -120,7 +120,7 @@ struct RoomFederationDecisionView: View {
.lineLimit(nil)
.foregroundColor(Color(theme.colors.primaryContent))
Text(BWIL10n.roomAdminFederationDecisionSheetText)
.font(.system(size: 12))
.font(Font(theme.fonts.subheadline))
.multilineTextAlignment(.center)
.lineLimit(nil)
.foregroundColor(Color(theme.colors.secondaryContent))
@@ -163,6 +163,8 @@ struct RoomFederationDecisionView: View {
func setServerACL(isFederated: Bool) {
let content: [String:Any] = room.createServerACL(isFederated: isFederated)
isUpdatingServerACLs = true
// #5383 set alias
room.setAliasIfNeeded(roomName: room.displayName)
pendingRequest = room.sendStateEvent(MXEventType.roomServerACL, content: content, stateKey: "") { response in
isUpdatingServerACLs = false
if response.isSuccess {

View File

@@ -102,11 +102,19 @@ extension PersonalNotesDefaultService : PersonalNotesService {
func createPersonalNotesRoomIfNeeded() {
self.fetchRoomID() { (roomId, error) in
if let mxerror = error as? MXError {
if let errcode = mxerror.userInfo["errcode"] as? String {
if errcode != kMXErrCodeStringNotFound {
// request error, don't create room
return
// bwi #5740 don't create room after connection error that is not translated to mxerror
if let error = error {
if MXError.isMXError(error) {
if let mxError = MXError(nsError: error) {
if let errcode = mxError.userInfo["errcode"] as? String {
if errcode != kMXErrCodeStringNotFound {
// request error, don't create room
return
}
} else {
return
}
}
} else {
return

View File

@@ -6,6 +6,7 @@ sonar.projectName=BundesMessenger iOS
sonar.sources=Riot,RiotSwiftUI,RiotNSE,RiotShareExtension,DesignKit,bwi,CommonKit,Config
sonar.tests=RiotTests
sonar.exclusions=RiotSwiftUI/**/Test/Unit/**,RiotSwiftUI/**/Mock*,Riot/Assets/js/**
# disable obj-c scans
sonar.c.file.suffixes=-