MESSENGER-4846 add federate toggle for room creation

This commit is contained in:
JanNiklas Grabowski
2023-12-07 12:15:16 +01:00
parent d446aaf24e
commit 7849724638
10 changed files with 229 additions and 3 deletions

View File

@@ -43,7 +43,7 @@ when String # specific MatrixSDK released version
$matrixSDKVersionSpec = $matrixSDKVersion
end
$matrixSDKVersionSpec = { :git => 'https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk', :tag => 'v2.12.0' }
$matrixSDKVersionSpec = { :git => 'https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk', :tag => 'v2.13.0-RC01' }
# Method to import the MatrixSDK
def import_MatrixSDK

View File

@@ -414,6 +414,12 @@
"user_avatar_view_accessibility_label" = "Profilbild";
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Zum Forfahren gib deinen Wiederherstellungsschlüssel ein.";
"create_room_type_federated" = "Für eine Föderation zulassen";
"create_room_type_federated_subtext" = "(Raum kann von externen Organisationen mitgenutzt werden)";
"create_room_deactivate_federation_for_room_error_title" = "Föderation aktiv";
"create_room_deactivate_federation_for_room_error_text" = "Die Föderation konnte nicht deaktiviert werden, bitte versuche es später erneut.";
"create_room_deactivate_federation_alert_dismiss_button" = "Ok";
// Mark: - Voice Messages
"voice_message_release_to_send" = "Zur Aufnahme halten, zum Senden loslassen";

View File

@@ -326,6 +326,12 @@
// Mark: - Room Creation
"room_intro_cell_information_dm_sentence1_part1" = "This is the beginning of your direct message history with ";
"create_room_type_federated" = "Allow federation";
"create_room_type_federated_subtext" = "(External organizations can join the room)";
"create_room_deactivate_federation_for_room_error_title" = "Federation active";
"create_room_deactivate_federation_for_room_error_text" = "The federation could not be disabled, please try again later.";
"create_room_deactivate_federation_alert_dismiss_button" = "Ok";
// MARK: - Notification Times

View File

@@ -719,10 +719,30 @@ public class BWIL10n: NSObject {
public static var create: String {
return BWIL10n.tr("Bwi", "create")
}
/// Ok
public static var createRoomDeactivateFederationAlertDismissButton: String {
return BWIL10n.tr("Bwi", "create_room_deactivate_federation_alert_dismiss_button")
}
/// Die Föderation konnte nicht deaktiviert werden, bitte versuche es später erneut.
public static var createRoomDeactivateFederationForRoomErrorText: String {
return BWIL10n.tr("Bwi", "create_room_deactivate_federation_for_room_error_text")
}
/// Föderation aktiv
public static var createRoomDeactivateFederationForRoomErrorTitle: String {
return BWIL10n.tr("Bwi", "create_room_deactivate_federation_for_room_error_title")
}
/// Thema
public static var createRoomPlaceholderTopic: String {
return BWIL10n.tr("Bwi", "create_room_placeholder_topic")
}
/// Für eine Föderation zulassen
public static var createRoomTypeFederated: String {
return BWIL10n.tr("Bwi", "create_room_type_federated")
}
/// (Raum kann von externen Organisationen mitgenutzt werden)
public static var createRoomTypeFederatedSubtext: String {
return BWIL10n.tr("Bwi", "create_room_type_federated_subtext")
}
/// Verifizierung abgebrochen. Du kannst sie erneut starten.
public static var deviceVerificationCancelled: String {
return BWIL10n.tr("Bwi", "device_verification_cancelled")

View File

@@ -22,6 +22,7 @@ import UIKit
enum EnterNewRoomDetailsViewAction {
case loadData
case chooseAvatar(sourceView: UIView)
case updateServerACLRule // bwi: #4846
case removeAvatar
case cancel
case create

View File

@@ -35,6 +35,7 @@ final class EnterNewRoomDetailsViewController: UIViewController {
static let roomTopicMaximumNumberOfChars = 250
static let chooseAvatarTableViewCellHeight: CGFloat = 140
static let textViewTableViewCellHeight: CGFloat = 150
static let bwiToggleWithLabelAndSubLabelCellReuseIdentifier = "bwiToggleWithLabelAndSubLabelCellReuseIdentifier" // bwi: #4846
}
// MARK: - Properties
@@ -74,11 +75,13 @@ final class EnterNewRoomDetailsViewController: UIViewController {
case textField(tag: Int, placeholder: String?, delegate: UITextFieldDelegate?)
case textView(tag: Int, placeholder: String?, delegate: UITextViewDelegate?)
case withSwitch(isOn: Bool, onValueChanged: ((UISwitch) -> Void)?)
case withSwitchWithSubText(onValueChanged: ((Bool) -> Void)?) // bwi: #4846
}
private struct Row {
var type: RowType
var text: String?
var subText: String? // bwi: #4846
var accessoryType: UITableViewCell.AccessoryType = .none
var action: (() -> Void)?
}
@@ -169,12 +172,24 @@ final class EnterNewRoomDetailsViewController: UIViewController {
}
}
}
// bwi: #4846 add serverACLRule on roomcreation
let row_4_3 = Row(type: .withSwitchWithSubText(onValueChanged: { [weak self] newToggleValue in
guard let self = self else {
return
}
viewModel.process(viewAction: .updateServerACLRule)
}), text: BWIL10n.createRoomTypeFederated, subText: BWIL10n.createRoomTypeFederatedSubtext)
let rows: [Row]
switch viewModel.actionType {
case .createAndAddToSpace:
rows = [row_4_0, row_4_1, row_4_2]
case .createOnly:
rows = [row_4_0, row_4_2]
// bwi: #4846 add serverACLRule on roomcreation
if BWIBuildSettings.shared.isFederationEnabled {
rows = [row_4_0, row_4_2, row_4_3]
} else {
rows = [row_4_0, row_4_2]
}
}
var footer: String
switch viewModel.roomCreationParameters.joinRule {
@@ -348,6 +363,8 @@ final class EnterNewRoomDetailsViewController: UIViewController {
mainTableView.register(cellType: TextFieldTableViewCell.self)
mainTableView.register(cellType: TextViewTableViewCell.self)
mainTableView.register(headerFooterViewType: TextViewTableViewHeaderFooterView.self)
// bwi: #4846 add serverACLRule on roomcreation
mainTableView.register(TableViewCellWithLabelSubLabelAndSwitch<TableViewCellWithLabelSubLabelAndSwitchView>.self, forCellReuseIdentifier: Constants.bwiToggleWithLabelAndSubLabelCellReuseIdentifier)
mainTableView.sectionHeaderHeight = UITableView.automaticDimension
mainTableView.estimatedSectionHeaderHeight = 50
mainTableView.sectionFooterHeight = UITableView.automaticDimension
@@ -490,6 +507,22 @@ extension EnterNewRoomDetailsViewController: UITableViewDataSource {
cell.mxkSwitchTrailingConstraint.constant = 15
cell.update(theme: theme)
return cell
// bwi: #4846 add toggle with subtext for federation
case .withSwitchWithSubText(let onValueChanged):
guard let cell = tableView.dequeueReusableCell(withIdentifier: Constants.bwiToggleWithLabelAndSubLabelCellReuseIdentifier, for: indexPath) as? TableViewCellWithLabelSubLabelAndSwitch<TableViewCellWithLabelSubLabelAndSwitchView> else {
return UITableViewCell()
}
cell.selectionStyle = .none
if let viewModel = viewModel as? EnterNewRoomDetailsViewModel {
cell.setupView(parent: self, rootView: TableViewCellWithLabelSubLabelAndSwitchView(viewModel: viewModel, toggleText: row.text ?? "", subText: row.subText ?? "", onValueChanged: { newToggleValue in
onValueChanged?(newToggleValue)
}))
} else {
return UITableViewCell()
}
return cell
}
}
@@ -600,6 +633,20 @@ extension EnterNewRoomDetailsViewController: EnterNewRoomDetailsViewModelViewDel
func enterNewRoomDetailsViewModel(_ viewModel: EnterNewRoomDetailsViewModelType, didUpdateViewState viewSate: EnterNewRoomDetailsViewState) {
self.render(viewState: viewSate)
}
// bwi: #4846 add serverACLRule on roomcreation
func showDeactivateFederationForRoomErrorAlert() {
let alert = UIAlertController(title: BWIL10n.createRoomDeactivateFederationForRoomErrorTitle,
message: BWIL10n.createRoomDeactivateFederationForRoomErrorText,
preferredStyle: .alert)
let closeAction = UIAlertAction(title: BWIL10n.createRoomDeactivateFederationAlertDismissButton, style: .default, handler: { _ in
})
alert.addAction(closeAction)
present(alert, animated: true, completion: nil)
}
}
// MARK: - UITextFieldDelegate

View File

@@ -18,7 +18,7 @@
import Foundation
final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType, ObservableObject {
// MARK: - Properties
@@ -36,6 +36,9 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
weak var coordinatorDelegate: EnterNewRoomDetailsViewModelCoordinatorDelegate?
var roomCreationParameters: RoomCreationParameters = RoomCreationParameters()
// bwi: #4846 add serverACLRule on roomcreation
@Published var serverACLToggleValue: Bool = false
private(set) var viewState: EnterNewRoomDetailsViewState {
didSet {
self.viewDelegate?.enterNewRoomDetailsViewModel(self, didUpdateViewState: viewState)
@@ -54,6 +57,11 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
roomCreationParameters.isEncrypted = session.vc_homeserverConfiguration().encryption.isE2EEByDefaultEnabled && RiotSettings.shared.roomCreationScreenRoomIsEncrypted
roomCreationParameters.joinRule = RiotSettings.shared.roomCreationScreenRoomIsPublic ? .public : .private
viewState = .loaded
// bwi: #4846 add serverACLRule on roomcreation
if BWIBuildSettings.shared.isFederationEnabled {
updateServerACLRule()
}
}
deinit {
@@ -75,6 +83,9 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
self.coordinatorDelegate?.enterNewRoomDetailsViewModelDidCancel(self)
case .create:
self.createRoom()
// bwi: #4846 add serverACLRule on roomcreation
case .updateServerACLRule:
self.updateServerACLRule()
}
}
@@ -129,6 +140,7 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
parentRoomId: parentSpace?.spaceId,
aliasLocalPart: fixRoomAlias(alias: roomCreationParameters.address),
isEncrypted: roomCreationParameters.isEncrypted,
serverACLRule: roomCreationParameters.serverACLRule,
completion: { response in
switch response {
case .success(let room):
@@ -203,6 +215,25 @@ final class EnterNewRoomDetailsViewModel: EnterNewRoomDetailsViewModelType {
}
}
}
// bwi: #4846 add serverACLRule on roomcreation
private func updateServerACLRule() {
var allowedServers = [String]()
if serverACLToggleValue {
// Room is federated, allow other user from other servers to join the room
allowedServers.append("*")
} else {
if let host = URLComponents(string: "something")?.host {
// Room is not federated, only user from the same homeserver can join the room
allowedServers.append(host)
} else {
serverACLToggleValue = true
self.viewDelegate?.showDeactivateFederationForRoomErrorAlert()
}
}
roomCreationParameters.serverACLRule = MXRoomServerACLRule(allow: allowedServers)
}
private func cancelOperations() {
self.currentOperation?.cancel()

View File

@@ -20,6 +20,7 @@ import Foundation
protocol EnterNewRoomDetailsViewModelViewDelegate: AnyObject {
func enterNewRoomDetailsViewModel(_ viewModel: EnterNewRoomDetailsViewModelType, didUpdateViewState viewSate: EnterNewRoomDetailsViewState)
func showDeactivateFederationForRoomErrorAlert() // bwi: #4846
}
protocol EnterNewRoomDetailsViewModelCoordinatorDelegate: AnyObject {

View File

@@ -38,6 +38,7 @@ struct RoomCreationParameters {
}
}
}
var serverACLRule: MXRoomServerACLRule? // bwi: #4846
var showInDirectory: Bool = false
var isRoomSuggested: Bool = false

View File

@@ -0,0 +1,113 @@
//
/*
* Copyright (c) 2023 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 SwiftUI
import Foundation
/// Helper class for making our SwiftUI view available to ObjectiveC
@objcMembers class TableViewCellWithLabelSubLabelAndSwitch<Content: View>: MXKTableViewCell {
private var parentViewController: UIViewController?
private let hostingController = UIHostingController<Content?>(rootView: nil)
private var theme: Theme!
override init!(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public func setupView(parent: UIViewController, rootView: Content) {
self.parentViewController = parent
hostingController.rootView = rootView
let shouldControllerMove = hostingController.parent != parent
if shouldControllerMove {
removeController()
parent.addChild(hostingController)
}
if !self.contentView.subviews.contains(hostingController.view) {
self.contentView.addSubview(hostingController.view)
self.contentView.vc_addSubViewMatchingParentSafeArea(hostingController.view)
}
if shouldControllerMove {
hostingController.didMove(toParent: parent)
}
registerThemeServiceDidChangeThemeNotification()
update(theme: ThemeService.shared().theme)
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
update(theme: ThemeService.shared().theme)
}
private func update(theme: Theme) {
self.theme = theme
hostingController.view.backgroundColor = theme.headerBackgroundColor
}
deinit {
removeController()
}
private func removeController() {
hostingController.willMove(toParent: nil)
hostingController.view.removeFromSuperview()
hostingController.removeFromParent()
parentViewController = nil
}
}
struct TableViewCellWithLabelSubLabelAndSwitchView: View {
@ObservedObject var viewModel: EnterNewRoomDetailsViewModel
var toggleText: String
var subText: String
@Environment(\.theme) private var theme
var onValueChanged: ((_: Bool) -> Void)?
var body: some View {
VStack(alignment: .leading) {
HStack {
Toggle(isOn: $viewModel.serverACLToggleValue) {
Text(toggleText)
.lineLimit(nil)
}
.toggleStyle(SwitchToggleStyle(tint: Color(ThemeService.shared().theme.tintColor)))
.onChange(of: viewModel.serverACLToggleValue) { newToggleValue in
onValueChanged?(newToggleValue)
}
}
Text(subText)
.font(.system(size: 15))
.foregroundColor(theme.colors.secondaryContent)
.lineLimit(nil)
}
.padding([.leading, .trailing], 16)
.padding([.top, .bottom], 8)
}
}