Merge branch 'feature/5706_show_federation_info' into 'develop'

Feature/5706 show federation info

See merge request bwmessenger/bundesmessenger/bundesmessenger-ios!325
This commit is contained in:
JanNiklas Grabowski
2024-03-04 10:00:50 +00:00
14 changed files with 274 additions and 25 deletions

View File

@@ -38,7 +38,7 @@ extension BWIBuildSettings {
ignoreBlockingMaintenance = true
bwiUseWellKnownPrivacyPolicyLink = true
showChangeProfilePictureHint = true
showFederationIntroduction = true
showFederationIntroduction = false
itunesAppLink = "https://apps.apple.com/de/app/bundesmessenger/id1616866351"
avoidServerSelectionOnAppConfig = true

View File

@@ -52,8 +52,10 @@
"bwi_settings_developer_unrestrict_user" = "Nutzereinschränkung aufheben";
"bwi_settings_developer_unmark_banner" = "Banner als nicht gelesen markieren";
"bwi_settings_developer_introduce_federation" = "Föderation";
"bwi_settings_developer_introduce_federation_preview" = "Vorschau zeigen";
"bwi_settings_developer_introduce_federation_reset" = "Account Data Flag urücksetzen";
"bwi_settings_developer_introduce_federation_preview" = "Introduction zeigen";
"bwi_settings_developer_introduce_federation_reset" = "Account Data Flag für Introduction zurücksetzen";
"bwi_settings_developer_announcement_federation_preview" = "Announcement zeigen";
"bwi_settings_developer_announcement_federation_reset" = "Account Data Flag für Announcement zurücksetzen";
"bwi_settings_developer_well_known" = "Well-Known";
"bwi_settings_developer_capabilities" = "Capabilities";
"bwi_settings_developer_maintenance" = "Maintenance";
@@ -713,6 +715,11 @@
"event_formatter_acl_collapsed" = "%tu Föderationseinstellungen wurden geändert.";
"event_formatter_acl_and_other_collapsed" = "%tu Föderations und weitere Einstellungen geändert.";
"federation_announcement_title" = "Ankündigung";
"federation_announcement_text" = "Ab Mai 2024 ist die **Föderation** zwischen verschiedenen Organisationen möglich. Dies bietet eine übergreifende sichere Kommunikation.\n\nDetails findest du in der Datenschutzerklärung.";
"federation_announcement_sub_text" = "Wenn du dein Profilbild vorweg ändern möchtest, gehe zu den Einstellungen.";
"federation_announcement_button" = "Verstanden";
"introduce_federation_screen1_title" = "Föderation";
"introduce_federation_screen1_description" = "Übergreifende sichere Kommunikation zwischen verschiedenen Organisationen";
"introduce_federation_screen2_title" = "Kennzeichnung";

View File

@@ -53,8 +53,10 @@
"bwi_settings_developer_unrestrict_user" = "Remove user restriction";
"bwi_settings_developer_unmark_banner" = "Unmark Feature Banner";
"bwi_settings_developer_introduce_federation" = "Federation";
"bwi_settings_developer_introduce_federation_preview" = "Show preview";
"bwi_settings_developer_introduce_federation_reset" = "Reset account data flag";
"bwi_settings_developer_introduce_federation_preview" = "Show introduction preview";
"bwi_settings_developer_introduce_federation_reset" = "Reset account data flag for introduction";
"bwi_settings_developer_announcement_federation_preview" = "Show announcement preview";
"bwi_settings_developer_announcement_federation_reset" = "Reset account data flag for announcement";
"bwi_settings_developer_well_known" = "Well-Known";
"bwi_settings_developer_capabilities" = "Capabilities";
"bwi_settings_developer_maintenance" = "Maintenance";
@@ -626,6 +628,11 @@
"event_formatter_acl_collapsed" = "%tu Federation settings changed.";
"event_formatter_acl_and_other_collapsed" = "%tu Federation and other settings changed.";
"federation_announcement_title" = "Announcement";
"federation_announcement_text" = "The **Federation** will start in May 2024. This enables secure communication across different organizations.\n\nDetails can be found in the privacy policies.";
"federation_announcement_sub_text" = "If you want to change your profile picture, go to the settings.";
"federation_announcement_button" = "Understood";
"introduce_federation_screen1_title" = "Federation";
"introduce_federation_screen1_description" = "Secure communication across different organizations";
"introduce_federation_screen2_title" = "Characterization";
@@ -633,4 +640,3 @@
"introduce_federation_screen3_title" = "Individual settings";
"introduce_federation_screen3_description" = "Decide as an admin which room should be authorized for federation.";
"introduce_federation_start" = "Understood";

View File

@@ -539,6 +539,14 @@ public class BWIL10n: NSObject {
public static var bwiSettingsDeveloper: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer")
}
/// Announcement zeigen
public static var bwiSettingsDeveloperAnnouncementFederationPreview: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_announcement_federation_preview")
}
/// Account Data Flag für Announcement zurücksetzen
public static var bwiSettingsDeveloperAnnouncementFederationReset: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_announcement_federation_reset")
}
/// MDM Config: MSG Demo
public static var bwiSettingsDeveloperApplyAppConfig: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_apply_app_config")
@@ -555,6 +563,18 @@ public class BWIL10n: NSObject {
public static var bwiSettingsDeveloperCreateNewPersonalNotesRoom: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_create_new_personal_notes_room")
}
/// Föderation
public static var bwiSettingsDeveloperIntroduceFederation: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_introduce_federation")
}
/// Introduction zeigen
public static var bwiSettingsDeveloperIntroduceFederationPreview: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_introduce_federation_preview")
}
/// Account Data Flag für Introduction zurücksetzen
public static var bwiSettingsDeveloperIntroduceFederationReset: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_introduce_federation_reset")
}
/// Key Backup
public static var bwiSettingsDeveloperKeyBackup: String {
return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup")
@@ -851,6 +871,22 @@ public class BWIL10n: NSObject {
public static var eventFormatterAclDisallowFederation: String {
return BWIL10n.tr("Bwi", "event_formatter_acl_disallow_federation")
}
/// Verstanden
public static var federationAnnouncementButton: String {
return BWIL10n.tr("Bwi", "federation_announcement_button")
}
/// Wenn du dein Profilbild vorweg ändern möchtest, gehe zu den Einstellungen.
public static var federationAnnouncementSubText: String {
return BWIL10n.tr("Bwi", "federation_announcement_sub_text")
}
/// Ab Mai 2024 ist die **Föderation** zwischen verschiedenen Organisationen möglich. Dies bietet eine übergreifende sichere Kommunikation.\n\nDetails findest du in der Datenschutzerklärung.
public static var federationAnnouncementText: String {
return BWIL10n.tr("Bwi", "federation_announcement_text")
}
/// Ankündigung
public static var federationAnnouncementTitle: String {
return BWIL10n.tr("Bwi", "federation_announcement_title")
}
/// Notizen ausblenden
public static var homeContextMenuPersonalNotes: String {
return BWIL10n.tr("Bwi", "home_context_menu_personal_notes")
@@ -871,6 +907,34 @@ public class BWIL10n: NSObject {
public static var integrityAlertTitle: String {
return BWIL10n.tr("Bwi", "integrity_alert_title")
}
/// Übergreifende sichere Kommunikation zwischen verschiedenen Organisationen
public static var introduceFederationScreen1Description: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen1_description")
}
/// Föderation
public static var introduceFederationScreen1Title: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen1_title")
}
/// Föderierte Personen und Räume erkennst du am Symbol mit den zwei sich überschneidenden Kreisen.
public static var introduceFederationScreen2Description: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen2_description")
}
/// Kennzeichnung
public static var introduceFederationScreen2Title: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen2_title")
}
/// Bestimme als Admin, welche Räume für eine Föderation zugelassen sind.
public static var introduceFederationScreen3Description: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen3_description")
}
/// Individuelle Einstellung
public static var introduceFederationScreen3Title: String {
return BWIL10n.tr("Bwi", "introduce_federation_screen3_title")
}
/// Verstanden
public static var introduceFederationStart: String {
return BWIL10n.tr("Bwi", "introduce_federation_start")
}
/// Einladen
public static var invite: String {
return BWIL10n.tr("Bwi", "invite")
@@ -1171,10 +1235,6 @@ public class BWIL10n: NSObject {
public static var roomAdminFederationDecisionSheetDeactivateFederationButton: String {
return BWIL10n.tr("Bwi", "room_admin_federation_decision_sheet_deactivate_federation_button")
}
/// Später erinnern
public static var roomAdminFederationDecisionSheetRemindLaterButton: String {
return BWIL10n.tr("Bwi", "room_admin_federation_decision_sheet_remind_later_button")
}
/// Hierdurch kann der Raum von externen Organisationen mitgenutzt werden. Dies kann nachträglich in den Einstellungen geändert werden.
public static var roomAdminFederationDecisionSheetText: String {
return BWIL10n.tr("Bwi", "room_admin_federation_decision_sheet_text")

View File

@@ -302,7 +302,7 @@ class RiotSharedSettings: NSObject {
return session.setAccountData(notificationsDict, forType: "de.bwi.notifications", success: success, failure: failure)
}
// MARK: - Private
private func getAccountData(forEventType eventType: String) -> [String: Any]? {
return session.accountData.accountData(forEventType: eventType) as? [String: Any]

View File

@@ -713,13 +713,9 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol {
if BWIBuildSettings.shared.bwiMatomoEnabled && BWIAnalytics.sharedTracker.needsToShowPromt() {
self.allChatsViewController.bwiPresentMatomoConsentAlert()
} else {
// bwi: 5706 show federation announcement promt
self.allChatsViewController.bwiCheckForFederationAnnouncementPromt()
// bwi: 5660 introduce federation
self.bwiShowFederationIntroduction()
}
}
private func bwiShowFederationIntroduction() {
if BWIBuildSettings.shared.showFederationIntroduction {
self.allChatsViewController.presentFederationIntroductionSheet()
}
}

View File

@@ -325,6 +325,10 @@ class AllChatsViewController: HomeViewController {
}
func presentFederationIntroductionSheet() {
guard BWIBuildSettings.shared.showFederationIntroduction else {
return
}
guard BWIBuildSettings.shared.isFederationEnabled else {
return
}
@@ -497,6 +501,8 @@ class AllChatsViewController: HomeViewController {
style: .default,
handler: { action in
BWIAnalytics.sharedTracker.running = false
// bwi: 5706 show federation announcement promt
self.bwiCheckForFederationAnnouncementPromt()
// bwi: 5660 introduce federation
self.presentFederationIntroductionSheet()
}))
@@ -506,6 +512,8 @@ class AllChatsViewController: HomeViewController {
handler: { action in
BWIAnalytics.sharedTracker.running = true
BWIAnalytics.sharedTracker.trackBwiValue(0, "General", "ConsentGiven", "popup")
// bwi: 5706 show federation announcement promt
self.bwiCheckForFederationAnnouncementPromt()
// bwi: 5660 introduce federation
self.presentFederationIntroductionSheet()
}))
@@ -513,6 +521,24 @@ class AllChatsViewController: HomeViewController {
self.present(alert, animated: true)
}
// bwi: 5706 show federation announcement promt
func bwiCheckForFederationAnnouncementPromt() {
guard self.mainSession.homeserverWellknown.shouldShowFederationAnnouncement() else {
return
}
let notificationService = BWIAccountNotificationService(mxSession: self.mainSession)
guard notificationService.showFederationAnnouncementFlag() else {
return
}
let viewController = UIHostingController(rootView: FederationAnnouncementView().environmentObject(BWIThemeService.shared))
viewController.isModalInPresentation = true
present(viewController, animated: true) {
notificationService.setShowFederationAnnouncementFlag(false)
}
}
// MARK: - Actions
@objc private func showSpaceSelectorAction(sender: AnyObject) {

View File

@@ -208,4 +208,5 @@ extension UserDefaults
func externalUrlScheme() -> String? {
return appConfig.externalUrlScheme
}
}

View File

@@ -37,6 +37,7 @@ struct DeveloperSettingsView: View {
@State private var showAlertBirthdayCampaign = false
@State private var permalinkPrefix: String? = UserDefaults.standard.string(forKey: "bwi_permalink_prefix")
@State private var showIntroduceFederation = false
@State private var showAnnouncementFederation = false
var body: some View {
Form {
@@ -83,6 +84,23 @@ struct DeveloperSettingsView: View {
}
}
SwiftUI.Section(header: Text(BWIL10n.bwiSettingsDeveloperIntroduceFederation)) {
Button(action: { showAnnouncementFederation = true }) {
Text(BWIL10n.bwiSettingsDeveloperAnnouncementFederationPreview)
.foregroundColor(Color(themeService.theme.tintColor))
.font(.system(size: 17))
}
.sheet(isPresented: $showAnnouncementFederation) {
FederationAnnouncementView()
.interactiveDismissDisabled()
.environmentObject(BWIThemeService.shared)
}
if let session = session {
Button(action: { BWIAccountNotificationService(mxSession: session).setShowFederationAnnouncementFlag(true) }) {
Text(BWIL10n.bwiSettingsDeveloperAnnouncementFederationReset)
.foregroundColor(Color(themeService.theme.tintColor))
.font(.system(size: 17))
}
}
Button(action: { showIntroduceFederation = true }) {
Text(BWIL10n.bwiSettingsDeveloperIntroduceFederationPreview)
.foregroundColor(Color(themeService.theme.tintColor))

View File

@@ -0,0 +1,106 @@
//
/*
* 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
struct FederationAnnouncementView: View {
@Environment(\.dismiss) var dismissView
@EnvironmentObject var themeService: BWIThemeService
let imageStackScale = 0.30
let imageStackShift = 5.0
let outerLineWith = 5.0
let innerLineWith = 1.0
let spacing = 16.0
var announcementText: AttributedString {
do {
return try AttributedString(markdown: (BWIL10n.federationAnnouncementText), options: AttributedString.MarkdownParsingOptions(interpretedSyntax: .inlineOnlyPreservingWhitespace))
} catch {
return AttributedString(BWIL10n.federationAnnouncementText)
}
}
var body: some View {
GeometryReader { geo in
ScrollView(.vertical) {
VStack(spacing: spacing) {
Spacer()
ZStack(alignment: .center) {
Circle()
.fill(Color(themeService.theme.colors.quinaryContent))
.padding(outerLineWith)
.offset(x:((getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale) / imageStackShift))
Image(uiImage: Asset.SharedImages.loginFlowLogo.image)
.resizable()
.aspectRatio(contentMode: .fill)
.clipShape(Circle())
.padding(innerLineWith)
.background(Color(themeService.theme.colors.quinaryContent))
.clipShape(Circle())
.padding(outerLineWith)
.background(Color(themeService.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)
LazyVStack(spacing: spacing) {
Text(BWIL10n.federationAnnouncementTitle)
.font(.system(size: 24).bold())
.multilineTextAlignment(.center)
.lineLimit(nil)
.foregroundColor(Color(themeService.theme.colors.primaryContent))
Text(announcementText)
.font(Font(themeService.theme.fonts.subheadline))
.multilineTextAlignment(.center)
.lineLimit(nil)
.foregroundColor(Color(themeService.theme.colors.primaryContent))
Text(BWIL10n.federationAnnouncementSubText)
.font(Font(themeService.theme.fonts.subheadline))
.multilineTextAlignment(.center)
.lineLimit(nil)
.foregroundColor(Color(themeService.theme.colors.secondaryContent))
}
.padding(spacing)
Spacer()
Button {
dismissView()
} label: {
Text(BWIL10n.federationAnnouncementButton)
}
.buttonStyle(PrimaryActionButtonStyle())
.accessibilityIdentifier("federationAnnouncementCloseButton")
.padding(spacing)
}
.frame(minHeight: geo.size.height)
}
.interactiveDismissDisabled()
.frame(width: geo.size.width, height: geo.size.height)
.background(Color(themeService.theme.colors.background))
}
}
func getImageSize(width: CGFloat, height: CGFloat) -> CGFloat {
return (width > height ? height : width)
}
}

View File

@@ -62,7 +62,8 @@ struct RoomFederationDecisionView: View {
var roomAvatarImage: UIImage
let imageStackScale = 0.30
let imageStackShift = 5.0
let lineWith = 7.0
let outerLineWith = 5.0
let innerLineWith = 1.0
let spacing = 16.0
@@ -76,14 +77,17 @@ struct RoomFederationDecisionView: View {
ZStack(alignment: .center) {
Circle()
.fill(Color(theme.colors.quinaryContent))
.overlay(Circle()
.stroke(Color(theme.colors.background), lineWidth: lineWith))
.padding(outerLineWith)
.offset(x:((getImageSize(width: geo.size.width, height: geo.size.height) * imageStackScale) / imageStackShift))
Image(uiImage: roomAvatarImage)
.resizable()
.clipShape(Circle())
.overlay(Circle()
.stroke(Color(theme.colors.background), lineWidth: lineWith))
.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)
@@ -124,7 +128,7 @@ struct RoomFederationDecisionView: View {
var textStack: some View {
VStack(spacing: spacing) {
LazyVStack(spacing: spacing) {
Text(BWIL10n.roomAdminFederationDecisionSheetTitle(room.displayName ?? ""))
.font(.system(size: 24).bold())
.multilineTextAlignment(.center)

View File

@@ -48,12 +48,12 @@ import Foundation
if let federationAnnouncementVal = dict[AccountNotifications.CodingKeys.federationAnnouncement.rawValue] as? Bool {
federationAnnouncement = federationAnnouncementVal
} else {
federationAnnouncement = false
federationAnnouncement = true
}
if let federationIntroductionVal = dict[AccountNotifications.CodingKeys.federationIntroduction.rawValue] as? Bool {
federationIntroduction = federationIntroductionVal
} else {
federationIntroduction = false
federationIntroduction = true
}
}
}
@@ -85,6 +85,7 @@ import Foundation
session.setAccountData(notificationsDict, forType: AccountDataTypes.notifications, success: nil, failure: nil)
}
// bwi: 5706 show federation announcement promt
func showFederationAnnouncementFlag() -> Bool {
guard let notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) as? [String: Any] else {
return true
@@ -94,6 +95,7 @@ import Foundation
return notifications.federationAnnouncement
}
// bwi: 5706 show federation announcement promt
func setShowFederationAnnouncementFlag(_ value: Bool) {
var notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) ?? [:]
notificationsDict[AccountNotifications.CodingKeys.federationAnnouncement.rawValue] = value

View File

@@ -75,6 +75,24 @@ public extension MXWellKnown {
return false
}
}
@objc func shouldShowFederationAnnouncement() -> Bool {
do {
guard let bwiDict = self.jsonDictionary()["de.bwi"] as? [String : Any] else {
return false
}
let bwi = try WellknownBWI(dict: bwiDict)
if let federation = bwi.federation {
return federation.showAnnouncement ?? false
} else {
return false
}
}
catch {
return false
}
}
// returns true if there is a valid url or no url, only the case "is url" but not valid is wrong
@objc func isValidDataPrivacyURL() -> Bool {

View File

@@ -36,8 +36,13 @@ struct WellknownBWI: Decodable {
}
struct WellknownFederation: Decodable {
// show announcement (1 screen)
let showAnnouncement: Bool?
// show federation tutorial (3 screens)
let showIntroduction: Bool?
// enables the federation feature for the app - show federation views, activate invitation rules, show federation settings
let enable: Bool?
enum CodingKeys: String, CodingKey {