// /* * 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 SwiftUI /// Helper class for making our SwiftUI view available to ObjectiveC @objcMembers class RoomFederationDecisionSheet: NSObject { private var decisionView: RoomFederationDecisionView? @Published var theme: Theme override init() { theme = ThemeService.shared().theme } func makeViewController(room: MXRoom, roomAvatarImage: UIImage) -> UIViewController { registerThemeServiceDidChangeThemeNotification() self.decisionView = RoomFederationDecisionView(theme: Binding(get: { return self.theme }, set: { newTheme in self.theme = newTheme }), room: room, roomAvatarImage: roomAvatarImage) return UIHostingController(rootView: decisionView) } private func registerThemeServiceDidChangeThemeNotification() { NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) } @objc private func themeDidChange() { self.theme = ThemeService.shared().theme } } // bwi: #5304 struct RoomFederationDecisionView: View { @Environment(\.dismiss) var dismissView @State var isUpdatingServerACLs: Bool = false @Binding var theme: Theme @State var showAlert: Bool = false // show error alert when value is false @State var showSuccessAlert: Bool = false var room: MXRoom @State var pendingRequest: MXHTTPOperation? var roomAvatarImage: UIImage let imageStackScale = 0.30 let imageStackShift = 5.0 let outerLineWith = 5.0 let innerLineWith = 1.0 let spacing = 16.0 var body: some View { GeometryReader { geo in ScrollView(.vertical) { VStack(spacing: spacing) { VStack(spacing: spacing) { Spacer() // Federation image with room avatar ZStack(alignment: .center) { Circle() .fill(Color(theme.colors.quinaryContent)) .padding(outerLineWith) .offset(x:((getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale) / imageStackShift)) Image(uiImage: roomAvatarImage) .resizable() .clipShape(Circle()) .padding(innerLineWith) .background(Color(theme.colors.quinaryContent)) .clipShape(Circle()) .padding(outerLineWith) .background(Color(theme.colors.background)) .clipShape(Circle()) .offset(x:-((getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale) / imageStackShift)) } .frame(width: getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale, height: getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale) // Federation info text textStack } if isUpdatingServerACLs { ProgressView() } // Federation actions buttonStack } .frame(minHeight: geo.size.height) } .interactiveDismissDisabled() .frame(width: geo.size.width, height: geo.size.height) .background(Color(theme.colors.background)) .alert(isPresented: $showAlert) { if (showSuccessAlert) { return Alert(title: Text(BWIL10n.roomAdminFederationDecisionSetFederationSuccessAlertTitle), message: nil, dismissButton: .default(Text(BWIL10n.roomAdminFederationDecisionSetFederationAlertOkButton), action: { closeView() })) } else { return Alert(title: Text(BWIL10n.roomAdminFederationDecisionSetFederationErrorAlertTitle), message: Text(BWIL10n.roomAdminFederationDecisionSetFederationErrorAlertText), dismissButton: .default(Text(BWIL10n.roomAdminFederationDecisionSetFederationAlertOkButton), action: { closeView() })) } } } } func getImageSize(width: CGFloat, height: CGFloat) -> CGFloat { return (width > height ? height : width) } var textStack: some View { LazyVStack(spacing: spacing) { Text(BWIL10n.roomAdminFederationDecisionSheetTitle(room.displayName ?? "")) .font(.system(size: 24).bold()) .multilineTextAlignment(.center) .lineLimit(nil) .foregroundColor(Color(theme.colors.primaryContent)) Text(BWIL10n.roomAdminFederationDecisionSheetText) .font(Font(theme.fonts.subheadline)) .multilineTextAlignment(.center) .lineLimit(nil) .foregroundColor(Color(theme.colors.secondaryContent)) } .padding(spacing) } var buttonStack: some View { VStack(spacing: spacing) { Spacer() Button { setServerACL(isFederated: true) } label: { Text(BWIL10n.roomAdminFederationDecisionSheetActivateFederationButton) } .buttonStyle(SecondaryActionButtonStyle()) .accessibilityIdentifier("federationDecisionSheetActivateFederationButton") .disabled(isUpdatingServerACLs) Button { setServerACL(isFederated: false) } label: { Text(BWIL10n.roomAdminFederationDecisionSheetDeactivateFederationButton) } .buttonStyle(PrimaryActionButtonStyle()) .accessibilityIdentifier("federationDecisionSheetDeactivateFederationButton") .disabled(isUpdatingServerACLs) } .padding(spacing) } 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 // #5578 show alert on success if room is federated if (response.isSuccess) { // bwi #5393 tracking, use the buildSettig here to avoid admin calculation if not necessary if BWIBuildSettings.shared.bwiMatomoEnabled { BWIAnalyticsHelper().getRoomAdminCount(room: room) { (adminCount) in BWIAnalytics.sharedTracker.trackEvent(category: "Federation", action: "ShowSettingFederateRoom", name: isFederated ? "accept" : "decline", number: NSNumber(value: adminCount)) } } if (isFederated) { showSuccessAlert = true showAlert = true } else { closeView() } } else { // show error alert showSuccessAlert = false showAlert = true } } } func closeView() { if let pendingRequest = pendingRequest { pendingRequest.cancel() } dismissView() } }