diff --git a/Config/BWIBuildSettings.swift b/Config/BWIBuildSettings.swift index 2bb5cd468..5ba0acedd 100644 --- a/Config/BWIBuildSettings.swift +++ b/Config/BWIBuildSettings.swift @@ -727,4 +727,8 @@ class BWIBuildSettings: NSObject { // MARK: Profile picture hint // (only when federation is enabled) var showChangeProfilePictureHint = false + + // MARK: Federation Introduction + // (only when federation is enabled in the client and well-known flag is set) + var showFederationIntroduction = false } diff --git a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift index 53dc7c8e4..9623079a4 100644 --- a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift +++ b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift @@ -38,7 +38,8 @@ extension BWIBuildSettings { ignoreBlockingMaintenance = true bwiUseWellKnownPrivacyPolicyLink = true showChangeProfilePictureHint = true - + showFederationIntroduction = true + itunesAppLink = "https://apps.apple.com/de/app/bundesmessenger/id1616866351" avoidServerSelectionOnAppConfig = true enableFeatureWYSIWYGByDefault = true diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_dark.imageset/Contents.json new file mode 100644 index 000000000..a19a54922 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_dark.imageset/Contents.json @@ -0,0 +1,20 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_light.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_light.imageset/Contents.json new file mode 100644 index 000000000..6fe332016 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_1_light.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Contents.json new file mode 100644 index 000000000..b78772d84 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Kennzeichnung_BuM_dark_mobile.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Kennzeichnung_BuM_dark_mobile.svg b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Kennzeichnung_BuM_dark_mobile.svg new file mode 100644 index 000000000..260a32857 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_dark.imageset/Kennzeichnung_BuM_dark_mobile.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Contents.json new file mode 100644 index 000000000..ee0238965 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Kennzeichnung_BuM_light_mobile.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Kennzeichnung_BuM_light_mobile.svg b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Kennzeichnung_BuM_light_mobile.svg new file mode 100644 index 000000000..c46be4fe7 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_2_light.imageset/Kennzeichnung_BuM_light_mobile.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Apple iPhone 11 White (1).png b/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Apple iPhone 11 White (1).png new file mode 100644 index 000000000..3b743cfda Binary files /dev/null and b/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Apple iPhone 11 White (1).png differ diff --git a/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Contents.json new file mode 100644 index 000000000..c70125781 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/introduce_federation_3.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Apple iPhone 11 White (1).png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" + } +} diff --git a/Riot/Assets/de.lproj/Bwi.strings b/Riot/Assets/de.lproj/Bwi.strings index 4f8e9217c..65e4e3355 100644 --- a/Riot/Assets/de.lproj/Bwi.strings +++ b/Riot/Assets/de.lproj/Bwi.strings @@ -51,6 +51,9 @@ "bwi_settings_developer_restrict_user" = "Nutzer einschränken"; "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_well_known" = "Well-Known"; "bwi_settings_developer_capabilities" = "Capabilities"; "bwi_settings_developer_maintenance" = "Maintenance"; @@ -703,3 +706,11 @@ "room_participants_invite_prompt_server_acl_for_room_not_configured_text" = "Du kannst noch keine Personen aus einer föderierten Organisation einladen, da die Freigabe hierfür noch nicht erteilt wurde. Gib dem Admin Bescheid, dass die Einstellung getroffen werden muss."; "room_participants_invite_prompt_server_acl_loading_error_text" = "Die Person kann aktuell nicht eingeladen werden, bitte versuche es später erneut."; +"introduce_federation_screen1_title" = "Föderation"; +"introduce_federation_screen1_description" = "Übergreifende sichere Kommunikation zwischen verschiedenen Organisationen"; +"introduce_federation_screen2_title" = "Kennzeichnung"; +"introduce_federation_screen2_description" = "Föderierte Personen und Räume erkennst du am Symbol mit den zwei sich überschneidenden Kreisen."; +"introduce_federation_screen3_title" = "Individuelle Einstellung"; +"introduce_federation_screen3_description" = "Bestimme als Admin, welche Räume für eine Föderation zugelassen sind."; +"introduce_federation_start" = "Verstanden"; + diff --git a/Riot/Assets/en.lproj/Bwi.strings b/Riot/Assets/en.lproj/Bwi.strings index 07bfd376a..c762bd067 100644 --- a/Riot/Assets/en.lproj/Bwi.strings +++ b/Riot/Assets/en.lproj/Bwi.strings @@ -52,6 +52,9 @@ "bwi_settings_developer_restrict_user" = "Restrict user"; "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_well_known" = "Well-Known"; "bwi_settings_developer_capabilities" = "Capabilities"; "bwi_settings_developer_maintenance" = "Maintenance"; @@ -616,3 +619,10 @@ "room_participants_invite_prompt_server_acl_for_room_not_configured_text" = "You cannot yet invite people from a federated organization, as this has not yet been approved. Let the admin know that the setting needs to be made."; "room_participants_invite_prompt_server_acl_loading_error_text" = "You cannot invite this user at the moment, please try again later."; +"introduce_federation_screen1_title" = "Federation"; +"introduce_federation_screen1_description" = "Secure communication across different organizations"; +"introduce_federation_screen2_title" = "Characterization"; +"introduce_federation_screen2_description" = "The symbol of overlaping circles helps to identify federated people."; +"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"; diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 260029cca..6a1631a90 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -55,6 +55,11 @@ internal class Asset: NSObject { internal static let federationPillEnBumLight = ImageAsset(name: "federation_pill_en_bum_light") internal static let fileAttachmentIcon = ImageAsset(name: "file_attachment_icon") internal static let fileScanInfected = ImageAsset(name: "file_scan_infected") + internal static let introduceFederation1Dark = ImageAsset(name: "introduce_federation_1_dark") + internal static let introduceFederation1Light = ImageAsset(name: "introduce_federation_1_light") + internal static let introduceFederation2Dark = ImageAsset(name: "introduce_federation_2_dark") + internal static let introduceFederation2Light = ImageAsset(name: "introduce_federation_2_light") + internal static let introduceFederation3 = ImageAsset(name: "introduce_federation_3") internal static let newFeatures = ImageAsset(name: "new_features") internal static let qrcodeViewfinder = ImageAsset(name: "qrcode_viewfinder") internal static let roomFederatedBumIconDark = ImageAsset(name: "room_federated_bum_icon_dark") diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index cebaf0abb..1336e8ee9 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -712,6 +712,15 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { private func bwiCheckForMatomoPromt() { if BWIBuildSettings.shared.bwiMatomoEnabled && BWIAnalytics.sharedTracker.needsToShowPromt() { self.allChatsViewController.bwiPresentMatomoConsentAlert() + } else { + // bwi: 5660 introduce federation + self.bwiShowFederationIntroduction() + } + } + + private func bwiShowFederationIntroduction() { + if BWIBuildSettings.shared.showFederationIntroduction { + self.allChatsViewController.presentFederationIntroductionSheet() } } diff --git a/Riot/Modules/Home/AllChats/AllChatsViewController.swift b/Riot/Modules/Home/AllChats/AllChatsViewController.swift index b827f8e8e..2c0028ed0 100644 --- a/Riot/Modules/Home/AllChats/AllChatsViewController.swift +++ b/Riot/Modules/Home/AllChats/AllChatsViewController.swift @@ -17,6 +17,7 @@ // swiftlint:disable file_length import UIKit +import SwiftUI import Reusable protocol AllChatsViewControllerDelegate: AnyObject { @@ -321,8 +322,27 @@ class AllChatsViewController: HomeViewController { } AppDelegate.theDelegate().checkAppVersion() - + } + + func presentFederationIntroductionSheet() { + guard BWIBuildSettings.shared.isFederationEnabled else { + return + } + guard self.mainSession.homeserverWellknown.shouldShowFederationIntroduction() else { + return + } + + let notificationService = BWIAccountNotificationService(mxSession: self.mainSession) + guard notificationService.showFederationIntroductionFlag() else { + return + } + + let viewController = UIHostingController(rootView: IntroduceFederationView().environmentObject(BWIThemeService.shared)) + viewController.isModalInPresentation = true + present(viewController, animated: true) { + notificationService.setShowFederationIntroductionFlag(false) + } } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -477,6 +497,8 @@ class AllChatsViewController: HomeViewController { style: .default, handler: { action in BWIAnalytics.sharedTracker.running = false + // bwi: 5660 introduce federation + self.presentFederationIntroductionSheet() })) alert.addAction(UIAlertAction(title: BWIL10n.bwiAnalyticsAlertOkButton, @@ -484,6 +506,8 @@ class AllChatsViewController: HomeViewController { handler: { action in BWIAnalytics.sharedTracker.running = true BWIAnalytics.sharedTracker.trackBwiValue(0, "General", "ConsentGiven", "popup") + // bwi: 5660 introduce federation + self.presentFederationIntroductionSheet() })) self.present(alert, animated: true) diff --git a/bwi/DeveloperSettings/DeveloperSettingsView.swift b/bwi/DeveloperSettings/DeveloperSettingsView.swift index 25a790b1e..b5932983b 100644 --- a/bwi/DeveloperSettings/DeveloperSettingsView.swift +++ b/bwi/DeveloperSettings/DeveloperSettingsView.swift @@ -23,7 +23,7 @@ import SwiftUI @available(iOS 14.0, *) class func makeViewController(session: MXSession) -> UIViewController { - return UIHostingController(rootView: DeveloperSettingsView(session: session)) + return UIHostingController(rootView: DeveloperSettingsView(session: session).environmentObject(BWIThemeService.shared)) } } @@ -32,16 +32,18 @@ import SwiftUI struct DeveloperSettingsView: View { let session: MXSession? + @EnvironmentObject var themeService: BWIThemeService @State private var showAlert = false @State private var showAlertBirthdayCampaign = false @State private var permalinkPrefix: String? = UserDefaults.standard.string(forKey: "bwi_permalink_prefix") + @State private var showIntroduceFederation = false var body: some View { Form { SwiftUI.Section { Button(action: { showAlert = createNewPersonalNotesRoom(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperCreateNewPersonalNotesRoom) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } .alert(isPresented: $showAlert) { @@ -50,7 +52,7 @@ struct DeveloperSettingsView: View { Button(action: { showAlert = resetMatomoInfoScreen(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperResetMatomoInfo) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } .alert(isPresented: $showAlert) { @@ -58,7 +60,7 @@ struct DeveloperSettingsView: View { } Button(action: { showAlertBirthdayCampaign = resetBirthdayCampaignScreen(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperSettingsResetBirthdayBanner) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } .alert(isPresented: $showAlertBirthdayCampaign) { @@ -66,20 +68,39 @@ struct DeveloperSettingsView: View { } Button(action: { _ = restrictUser(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperRestrictUser) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } Button(action: { _ = unrestrictUser(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperUnrestrictUser) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } Button(action: { _ = unmarkBannerVersion(mxSession: session) }) { Text(BWIL10n.bwiSettingsDeveloperUnmarkBanner) - .foregroundColor(Color(ThemeService.shared().theme.tintColor)) + .foregroundColor(Color(themeService.theme.tintColor)) .font(.system(size: 17)) } } + SwiftUI.Section(header: Text(BWIL10n.bwiSettingsDeveloperIntroduceFederation)) { + Button(action: { showIntroduceFederation = true }) { + Text(BWIL10n.bwiSettingsDeveloperIntroduceFederationPreview) + .foregroundColor(Color(themeService.theme.tintColor)) + .font(.system(size: 17)) + } + .sheet(isPresented: $showIntroduceFederation) { + IntroduceFederationView() + .interactiveDismissDisabled() + .environmentObject(BWIThemeService.shared) + } + if let session = session { + Button(action: { BWIAccountNotificationService(mxSession: session).setShowFederationIntroductionFlag(true) }) { + Text(BWIL10n.bwiSettingsDeveloperIntroduceFederationReset) + .foregroundColor(Color(themeService.theme.tintColor)) + .font(.system(size: 17)) + } + } + } if BWIBuildSettings.shared.permalinkPrefixSettings && !BWIBuildSettings.shared.permalinkPrefixes.isEmpty { SwiftUI.Section(header: Text(BWIL10n.settingsPermalinkPrefixPickerTitle)) { Picker("", selection: $permalinkPrefix) { @@ -129,12 +150,12 @@ struct DeveloperSettingsView: View { } -@available(iOS 14.0, *) -struct DeveloperSettingsView_Previews: PreviewProvider { - static var previews: some View { - DeveloperSettingsView(session: nil) - } -} +//@available(iOS 14.0, *) +//struct DeveloperSettingsView_Previews: PreviewProvider { +// static var previews: some View { +// DeveloperSettingsView(session: nil) +// } +//} // MARK: - Button Actions @@ -153,7 +174,7 @@ fileprivate func resetMatomoInfoScreen(mxSession: MXSession?) -> Bool { guard let mxSession = mxSession else { return false } - BWIAnalyticsAccountDataService(mxSession: mxSession) + _ = BWIAnalyticsAccountDataService(mxSession: mxSession) return true } diff --git a/bwi/IntroduceFederation/IntroduceFederationScreen1.swift b/bwi/IntroduceFederation/IntroduceFederationScreen1.swift new file mode 100644 index 000000000..103bf36ff --- /dev/null +++ b/bwi/IntroduceFederation/IntroduceFederationScreen1.swift @@ -0,0 +1,61 @@ +// +/* + * 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 + +struct IntroduceFederationScreen1: View { + @EnvironmentObject var themeService: BWIThemeService + + var body: some View { + ZStack(alignment: .top) { + Color(themeService.theme.backgroundColor) + + VStack(spacing: 26) { + ZStack { + Color(hex: 0xE3E8F0) + .frame(width: IntroduceFederationView.imageSize, height: IntroduceFederationView.imageSize) + .clipShape(Circle()) + .padding(.leading, 50) + Image(uiImage: Asset.SharedImages.loginFlowLogo.image) + .resizable() + .aspectRatio(contentMode: .fill) + .clipShape(Circle()) + .padding(1) // inner border size + .background(Color(hex: 0xE3E8F0)) + .clipShape(Circle()) + .padding(5) // outer border size + .background(Color(themeService.theme.backgroundColor)) + .clipShape(Circle()) + .frame(width: IntroduceFederationView.imageSize + 12, height: IntroduceFederationView.imageSize + 12) + .padding(.trailing, 50) + } + .padding(.bottom, 42) + Text(BWIL10n.introduceFederationScreen1Title) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 28, weight: .semibold)) + .multilineTextAlignment(.center) + Text(BWIL10n.introduceFederationScreen1Description) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 17)) + .multilineTextAlignment(.center) + Spacer() + } + .padding(.horizontal) + .padding(.top, IntroduceFederationView.topConstraintTitle - IntroduceFederationView.imageSize - 12) + } + } +} diff --git a/bwi/IntroduceFederation/IntroduceFederationScreen2.swift b/bwi/IntroduceFederation/IntroduceFederationScreen2.swift new file mode 100644 index 000000000..1243d5f99 --- /dev/null +++ b/bwi/IntroduceFederation/IntroduceFederationScreen2.swift @@ -0,0 +1,51 @@ +// +/* + * 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 + +struct IntroduceFederationScreen2: View { + @EnvironmentObject var themeService: BWIThemeService + + var body: some View { + ZStack(alignment: .top) { + Color(themeService.theme.backgroundColor) + + VStack(spacing: 26) { + Image(uiImage: getImage(darkmode: themeService.isCurrentThemeDark)) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: IntroduceFederationView.imageSize + 12) + .padding(.bottom, 42) + Text(BWIL10n.introduceFederationScreen2Title) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 28, weight: .semibold)) + .multilineTextAlignment(.center) + Text(BWIL10n.introduceFederationScreen2Description) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 17)) + .multilineTextAlignment(.center) + Spacer() + } + .padding(.horizontal) + .padding(.top, IntroduceFederationView.topConstraintTitle - IntroduceFederationView.imageSize - 12) + } + } + + private func getImage(darkmode: Bool) -> UIImage { + return darkmode ? Asset.Images.introduceFederation2Dark.image : Asset.Images.introduceFederation2Light.image + } +} diff --git a/bwi/IntroduceFederation/IntroduceFederationScreen3.swift b/bwi/IntroduceFederation/IntroduceFederationScreen3.swift new file mode 100644 index 000000000..dbd20f05f --- /dev/null +++ b/bwi/IntroduceFederation/IntroduceFederationScreen3.swift @@ -0,0 +1,52 @@ +// +/* + * 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 + +struct IntroduceFederationScreen3: View { + @EnvironmentObject var themeService: BWIThemeService + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack(spacing: 26) { + Image(uiImage: Asset.Images.introduceFederation3.image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: IntroduceFederationView.topConstraintTitle) + .padding(.bottom, 42) + Text(BWIL10n.introduceFederationScreen3Title) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 28, weight: .semibold)) + .multilineTextAlignment(.center) + Text(BWIL10n.introduceFederationScreen3Description) + .foregroundColor(Color(themeService.theme.textPrimaryColor)) + .font(.system(size: 17)) + .multilineTextAlignment(.center) + Spacer() + Button(action: onConfirm) { + Text(BWIL10n.introduceFederationStart) + } + .buttonStyle(PrimaryActionButtonStyle()) + .padding(.bottom, 80) + } + .padding(.horizontal) + } + + private func onConfirm() { + dismiss() + } +} diff --git a/bwi/IntroduceFederation/IntroduceFederationView.swift b/bwi/IntroduceFederation/IntroduceFederationView.swift new file mode 100644 index 000000000..3e716006b --- /dev/null +++ b/bwi/IntroduceFederation/IntroduceFederationView.swift @@ -0,0 +1,99 @@ +// +/* + * 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 + +enum VisibleScreen { + case screen1 + case screen2 + case screen3 +} + +struct IntroduceFederationView: View { + static let imageSize: CGFloat = 108 + static let topConstraintTitle: CGFloat = 390 + @EnvironmentObject var themeService: BWIThemeService + @State var visibleScreen = VisibleScreen.screen1 + + var body: some View { + TabView(selection: $visibleScreen) { + IntroduceFederationScreen1().tag(VisibleScreen.screen1).navigationBarHidden(true) + IntroduceFederationScreen2().tag(VisibleScreen.screen2).navigationBarHidden(true) + IntroduceFederationScreen3().tag(VisibleScreen.screen3).navigationBarHidden(true) + } + .tabViewStyle(.page(indexDisplayMode: .always)) + .background { + Color(themeService.theme.backgroundColor) + .edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/) + } + .overlay(alignment: .bottom) { + HStack { + if visibleScreen != .screen1 { + Button(action: presentPreviousScreen) { + Image(systemName: "arrow.left") + .foregroundColor(Color(themeService.theme.textSecondaryColor)) + .padding() + } + } + Spacer() + if visibleScreen != .screen3 { + Button(action: presentNextScreen) { + Image(systemName: "arrow.right") + .foregroundColor(Color(themeService.theme.textSecondaryColor)) + .padding() + } + } + } + .padding() + } + .onAppear { + setupIndicatorColors() + } + } + + private func setupIndicatorColors() { + UIPageControl.appearance().currentPageIndicatorTintColor = themeService.theme.textPrimaryColor + UIPageControl.appearance().pageIndicatorTintColor = themeService.theme.textSecondaryColor + } + + private func presentNextScreen() { + withAnimation { + switch visibleScreen { + case .screen1: + visibleScreen = .screen2 + case .screen2: + visibleScreen = .screen3 + case .screen3: + break + } + } + } + + private func presentPreviousScreen() { + withAnimation { + switch visibleScreen { + case .screen1: + break + case .screen2: + visibleScreen = .screen1 + case .screen3: + visibleScreen = .screen2 + } + } + } + +} diff --git a/bwi/Tools/BWIAccountNotificationService.swift b/bwi/Tools/BWIAccountNotificationService.swift index 57029b41c..f839a0bb0 100644 --- a/bwi/Tools/BWIAccountNotificationService.swift +++ b/bwi/Tools/BWIAccountNotificationService.swift @@ -24,13 +24,17 @@ import Foundation enum CodingKeys: String, CodingKey { case analyticsPromt = "should_show_analytics_consent_dialog" case birthdayPromt = "should_show_ios_birthday_notification" + case federationAnnouncement = "should_show_federation_announcement" + case federationIntroduction = "should_show_federation_introduction" } let analyticsPromt: Bool let birthdayPromt: Bool + let federationAnnouncement: Bool + let federationIntroduction: Bool init(_ dict: [String: Any]) { - + if let analyticsVal = dict[AccountNotifications.CodingKeys.analyticsPromt.rawValue] as? Bool { analyticsPromt = analyticsVal } else { @@ -41,6 +45,16 @@ import Foundation } else { birthdayPromt = false } + if let federationAnnouncementVal = dict[AccountNotifications.CodingKeys.federationAnnouncement.rawValue] as? Bool { + federationAnnouncement = federationAnnouncementVal + } else { + federationAnnouncement = false + } + if let federationIntroductionVal = dict[AccountNotifications.CodingKeys.federationIntroduction.rawValue] as? Bool { + federationIntroduction = federationIntroductionVal + } else { + federationIntroduction = false + } } } @@ -70,4 +84,37 @@ import Foundation session.setAccountData(notificationsDict, forType: AccountDataTypes.notifications, success: nil, failure: nil) } + + func showFederationAnnouncementFlag() -> Bool { + guard let notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) as? [String: Any] else { + return true + } + + let notifications = AccountNotifications(notificationsDict) + return notifications.federationAnnouncement + } + + func setShowFederationAnnouncementFlag(_ value: Bool) { + var notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) ?? [:] + notificationsDict[AccountNotifications.CodingKeys.federationAnnouncement.rawValue] = value + + session.setAccountData(notificationsDict, forType: AccountDataTypes.notifications, success: nil, failure: nil) + } + + func showFederationIntroductionFlag() -> Bool { + guard let notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) as? [String: Any] else { + return true + } + + let notifications = AccountNotifications(notificationsDict) + return notifications.federationIntroduction + } + + func setShowFederationIntroductionFlag(_ value: Bool) { + var notificationsDict = session.accountData.accountData(forEventType: AccountDataTypes.notifications) ?? [:] + notificationsDict[AccountNotifications.CodingKeys.federationIntroduction.rawValue] = value + + session.setAccountData(notificationsDict, forType: AccountDataTypes.notifications, success: nil, failure: nil) + } + } diff --git a/bwi/Tools/BWIThemeService.swift b/bwi/Tools/BWIThemeService.swift new file mode 100644 index 000000000..f83199099 --- /dev/null +++ b/bwi/Tools/BWIThemeService.swift @@ -0,0 +1,48 @@ +// +/* + * Copyright (c) 2022 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 +import Combine + +class BWIThemeService: ObservableObject { + static let shared = BWIThemeService() + @Published var themeId: String? + @Published var theme: Theme + @Published var isCurrentThemeDark: Bool + var cancellable: Cancellable? + + private init() { + let service = ThemeService.shared() + themeId = service.themeId + theme = service.theme + isCurrentThemeDark = service.isCurrentThemeDark() + + cancellable = NotificationCenter.default + .publisher(for: .themeServiceDidChangeTheme) + .receive(on: DispatchQueue.main) + .sink() { _ in + self.themeDidChange() + } + } + + private func themeDidChange() { + let service = ThemeService.shared() + themeId = service.themeId + theme = service.theme + isCurrentThemeDark = service.isCurrentThemeDark() + } +} diff --git a/bwi/Wellknown/Wellknown+Bwi.swift b/bwi/Wellknown/Wellknown+Bwi.swift index ae290aada..4a6565701 100644 --- a/bwi/Wellknown/Wellknown+Bwi.swift +++ b/bwi/Wellknown/Wellknown+Bwi.swift @@ -66,6 +66,16 @@ public extension MXWellKnown { } } + // returns true if the federation introduction screen should be presented to the new users + @objc func shouldShowFederationIntroduction() -> Bool { + do { + let bwi = try WellknownBWI(dict: self.jsonDictionary()["de.bwi"] as! [String : Any]) + return bwi.federation?.showIntroduction ?? 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 { do { diff --git a/bwi/Wellknown/WellknownBWI.swift b/bwi/Wellknown/WellknownBWI.swift index 6b166a20a..3ff5e0898 100644 --- a/bwi/Wellknown/WellknownBWI.swift +++ b/bwi/Wellknown/WellknownBWI.swift @@ -17,20 +17,32 @@ import Foundation -struct WellknownBWI { +struct WellknownBWI: Decodable { let dataPrivacyURL: String? let imprintURL: String? + let federation: WellknownFederation? init(dict: [String: Any]) throws { let jsonData = try JSONSerialization.data(withJSONObject: dict, options: []) let decoder = JSONDecoder() self = try decoder.decode(Self.self, from: jsonData) } -} - -extension WellknownBWI: Decodable { + enum CodingKeys: String, CodingKey { case dataPrivacyURL = "data_privacy_url" case imprintURL = "imprint_url" + case federation + } +} + +struct WellknownFederation: Decodable { + let showAnnouncement: Bool? + let showIntroduction: Bool? + let enable: Bool? + + enum CodingKeys: String, CodingKey { + case showAnnouncement = "show_announcement" + case showIntroduction = "show_introduction" + case enable } }