mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 15:09:31 +02:00
Merge branch 'feature/3616_privacy_on_login' into 'develop'
Feature/3616 privacy on login See merge request bwmessenger/bundesmessenger/bundesmessenger-ios!57
This commit is contained in:
@@ -209,7 +209,7 @@ class BWIBuildSettings: NSObject {
|
||||
|
||||
// Note: Set empty strings to hide the related entry in application settings
|
||||
var applicationCopyrightUrlString = "https://messenger.bwi.de/copyright"
|
||||
var applicationPrivacyPolicyUrlString = ""
|
||||
var applicationPrivacyPolicyUrlString = "https://messenger.bwi.de/datenschutz"
|
||||
var applicationTermsConditionsUrlString = ""
|
||||
|
||||
// MARk: - Matrix permalinks
|
||||
@@ -510,4 +510,7 @@ class BWIBuildSettings: NSObject {
|
||||
|
||||
// one flag for all layout changes to the element login flow
|
||||
var bumLoginFlowLayout = true
|
||||
|
||||
// website for users in public service that want a backend
|
||||
var bumAdvertizementURLString = "https://messenger.bwi.de/ich-will-bum";
|
||||
}
|
||||
|
||||
@@ -545,3 +545,6 @@
|
||||
// MARK - new login flow
|
||||
"authentication_server_selection_login_title" = "Willkommen!";
|
||||
"authentication_login_username" = "Nutzername";
|
||||
"authentication_login_description" = "Mit deinem Konto anmelden";
|
||||
"authentication_dataprivacy_text" = "Hinweise zur Verarbeitung Ihrer Daten und Ihrer Rechte erhalten Sie in unseren ";
|
||||
"authentication_dataprivacy_link" = "Datenschutzhinweisen";
|
||||
|
||||
@@ -413,3 +413,6 @@
|
||||
// MARK: - new login flow
|
||||
"authentication_server_selection_login_title" = "Welcome!";
|
||||
"authentication_login_username" = "Username";
|
||||
"authentication_login_description" = "Login to your account";
|
||||
"authentication_dataprivacy_text" = "You can see our data usage and your rights in our ";
|
||||
"authentication_dataprivacy_link" = "privacy agreement";
|
||||
|
||||
@@ -27,6 +27,18 @@ public class BWIL10n: NSObject {
|
||||
public static var authUserIdPlaceholder: String {
|
||||
return BWIL10n.tr("Bwi", "auth_user_id_placeholder")
|
||||
}
|
||||
/// Datenschutzhinweisen
|
||||
public static var authenticationDataprivacyLink: String {
|
||||
return BWIL10n.tr("Bwi", "authentication_dataprivacy_link")
|
||||
}
|
||||
/// Hinweise zur Verarbeitung Ihrer Daten und Ihrer Rechte erhalten Sie in unseren
|
||||
public static var authenticationDataprivacyText: String {
|
||||
return BWIL10n.tr("Bwi", "authentication_dataprivacy_text")
|
||||
}
|
||||
/// Mit deinem Konto anmelden
|
||||
public static var authenticationLoginDescription: String {
|
||||
return BWIL10n.tr("Bwi", "authentication_login_description")
|
||||
}
|
||||
/// Nutzername
|
||||
public static var authenticationLoginUsername: String {
|
||||
return BWIL10n.tr("Bwi", "authentication_login_username")
|
||||
|
||||
@@ -615,10 +615,7 @@
|
||||
self->userParticipant.mxMember = [roomState.members memberWithUserId:userId];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self handleRoomMember:mxMember];
|
||||
}
|
||||
[self handleRoomMember:mxMember];
|
||||
}
|
||||
|
||||
for (MXRoomThirdPartyInvite *roomThirdPartyInvite in roomThirdPartyInvites)
|
||||
@@ -1004,7 +1001,7 @@
|
||||
modSection = count++;
|
||||
}
|
||||
|
||||
if (userParticipant || actualParticipants.count)
|
||||
if (actualParticipants.count)
|
||||
{
|
||||
participantsSection = count++;
|
||||
}
|
||||
@@ -1031,10 +1028,6 @@
|
||||
else
|
||||
{
|
||||
count = actualParticipants.count;
|
||||
if (userParticipant)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (section == adminSection)
|
||||
@@ -1560,11 +1553,6 @@
|
||||
filteredModParticipants = [NSMutableArray arrayWithArray:modParticipants];
|
||||
filteredAdminParticipants = [NSMutableArray arrayWithArray:adminParticipants];
|
||||
|
||||
// Add the current user if he belongs to the room members.
|
||||
if (userParticipant)
|
||||
{
|
||||
[filteredActualParticipants addObject:userParticipant];
|
||||
}
|
||||
}
|
||||
|
||||
currentSearchText = searchText;
|
||||
|
||||
@@ -49,10 +49,10 @@ struct AuthenticationServerInfoSection: View {
|
||||
.font(theme.fonts.body)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
|
||||
Spacer()
|
||||
|
||||
if !BWIBuildSettings.shared.bumLoginFlowLayout {
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: editAction) {
|
||||
Text(VectorL10n.edit)
|
||||
.font(theme.fonts.body)
|
||||
|
||||
@@ -68,6 +68,7 @@ class AuthenticationService: NSObject {
|
||||
/// The type of client to use during the flow.
|
||||
var clientType: AuthenticationRestClient.Type = MXRestClient.self
|
||||
|
||||
var wellknown: MXWellKnown?
|
||||
// MARK: - Setup
|
||||
|
||||
init(sessionCreator: SessionCreatorProtocol = SessionCreator()) {
|
||||
@@ -236,6 +237,8 @@ class AuthenticationService: NSObject {
|
||||
var identityServerURL: URL?
|
||||
|
||||
if let wellKnown = try? await wellKnown(for: homeserverURL) {
|
||||
self.wellknown = wellKnown
|
||||
|
||||
if let baseURL = URL(string: wellKnown.homeServer.baseUrl) {
|
||||
homeserverURL = baseURL
|
||||
}
|
||||
|
||||
@@ -79,6 +79,13 @@ struct AuthenticationLoginViewState: BindableState {
|
||||
var canSubmit: Bool {
|
||||
hasValidCredentials && !isLoading
|
||||
}
|
||||
|
||||
var dataPrivacyString: String {
|
||||
guard let urlString = AuthenticationService.shared.wellknown?.dataPrivacyURL() else {
|
||||
return BWIBuildSettings.shared.applicationPrivacyPolicyUrlString
|
||||
}
|
||||
return urlString
|
||||
}
|
||||
}
|
||||
|
||||
struct AuthenticationLoginBindings {
|
||||
|
||||
@@ -32,53 +32,70 @@ struct AuthenticationLoginScreen: View {
|
||||
@ObservedObject var viewModel: AuthenticationLoginViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
header
|
||||
.padding(.top, OnboardingMetrics.topPaddingToNavigationBar)
|
||||
.padding(.bottom, 28)
|
||||
|
||||
serverInfo
|
||||
.padding(.leading, 12)
|
||||
.padding(.bottom, 16)
|
||||
|
||||
Rectangle()
|
||||
.fill(theme.colors.quinaryContent)
|
||||
.frame(height: 1)
|
||||
.padding(.bottom, 22)
|
||||
|
||||
if viewModel.viewState.homeserver.showLoginForm {
|
||||
loginForm
|
||||
}
|
||||
VStack {
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
if BWIBuildSettings.shared.bumLoginFlowLayout {
|
||||
ServerIcon(image: Asset.Images.welcomeExperience1, size: OnboardingMetrics.iconSize-18)
|
||||
.padding(.top, OnboardingMetrics.topPaddingToNavigationBar)
|
||||
.padding(.bottom, 16)
|
||||
} else {
|
||||
header
|
||||
.padding(.top, OnboardingMetrics.topPaddingToNavigationBar)
|
||||
.padding(.bottom, 28)
|
||||
}
|
||||
|
||||
serverInfo
|
||||
.padding(.leading, 12)
|
||||
.padding(.bottom, 16)
|
||||
|
||||
Rectangle()
|
||||
.fill(theme.colors.quinaryContent)
|
||||
.frame(height: 1)
|
||||
.padding(.bottom, 22)
|
||||
|
||||
if BWIBuildSettings.shared.bumLoginFlowLayout {
|
||||
loginDescription
|
||||
.padding(.bottom, 22)
|
||||
}
|
||||
|
||||
if viewModel.viewState.homeserver.showLoginForm {
|
||||
loginForm
|
||||
}
|
||||
|
||||
if viewModel.viewState.homeserver.showQRLogin {
|
||||
qrLoginButton
|
||||
}
|
||||
|
||||
if viewModel.viewState.homeserver.showLoginForm, viewModel.viewState.showSSOButtons {
|
||||
Text(VectorL10n.or)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
|
||||
if viewModel.viewState.showSSOButtons {
|
||||
ssoButtons
|
||||
.padding(.top, 16)
|
||||
}
|
||||
if viewModel.viewState.homeserver.showQRLogin {
|
||||
qrLoginButton
|
||||
}
|
||||
|
||||
if viewModel.viewState.homeserver.showLoginForm, viewModel.viewState.showSSOButtons {
|
||||
Text(VectorL10n.or)
|
||||
.foregroundColor(theme.colors.secondaryContent)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
|
||||
if viewModel.viewState.showSSOButtons {
|
||||
ssoButtons
|
||||
.padding(.top, 16)
|
||||
}
|
||||
|
||||
if !viewModel.viewState.homeserver.showLoginForm, !viewModel.viewState.showSSOButtons {
|
||||
fallbackButton
|
||||
if !viewModel.viewState.homeserver.showLoginForm, !viewModel.viewState.showSSOButtons {
|
||||
fallbackButton
|
||||
}
|
||||
}
|
||||
.readableFrame()
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
if BWIBuildSettings.shared.bumLoginFlowLayout {
|
||||
dataPrivacyForm
|
||||
.frame(alignment: .bottom)
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
.readableFrame()
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
.background(theme.colors.background.ignoresSafeArea())
|
||||
.alert(item: $viewModel.alertInfo) { $0.alert }
|
||||
.accentColor(theme.colors.accent)
|
||||
}
|
||||
|
||||
|
||||
/// The header containing a Welcome Back title.
|
||||
var header: some View {
|
||||
Text(VectorL10n.authenticationLoginTitle)
|
||||
@@ -167,6 +184,33 @@ struct AuthenticationLoginScreen: View {
|
||||
.accessibilityIdentifier("fallbackButton")
|
||||
}
|
||||
|
||||
var dataPrivacyForm: some View {
|
||||
Button(action: {
|
||||
let tosURL = URL.init(string: viewModel.viewState.dataPrivacyString)! // add your link here
|
||||
if UIApplication.shared.canOpenURL(tosURL) {
|
||||
UIApplication.shared.open(tosURL)
|
||||
}
|
||||
}, label: {
|
||||
Text(BWIL10n.authenticationDataprivacyText)
|
||||
.font(theme.fonts.footnote)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
+
|
||||
Text(BWIL10n.authenticationDataprivacyLink)
|
||||
.font(theme.fonts.footnote)
|
||||
.foregroundColor(.blue)
|
||||
.underline()
|
||||
})
|
||||
.padding([.horizontal], 20)
|
||||
}
|
||||
|
||||
var loginDescription: some View {
|
||||
HStack(spacing: 0) {
|
||||
Text(BWIL10n.authenticationLoginDescription)
|
||||
.font(theme.fonts.callout)
|
||||
.foregroundColor(theme.colors.primaryContent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the username for a homeserver.
|
||||
func usernameEditingChanged(isEditing: Bool) {
|
||||
guard !isEditing, !viewModel.username.isEmpty else { return }
|
||||
|
||||
@@ -65,7 +65,8 @@ class AuthenticationServerSelectionViewModel: AuthenticationServerSelectionViewM
|
||||
@MainActor func displayInfo(_ message: String, buttonTitle: String, completion: (()->Void)? ) {
|
||||
state.bindings.alertInfo = AlertInfo(id: .openInvalidServerInfo,
|
||||
title: message,
|
||||
primaryButton: (buttonTitle, completion))
|
||||
primaryButton: (buttonTitle, completion),
|
||||
secondaryButton: (VectorL10n.cancel, nil))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
@@ -123,7 +123,7 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
||||
guard protectionService.isValid(homeserverAddress) else {
|
||||
stopLoading()
|
||||
let primaryButtonCompletion: (() -> Void)? = { () in
|
||||
if let url = URL(string: "https://messenger.bwi.de/ich-will-bum") {
|
||||
if let url = URL(string: BWIBuildSettings.shared.bumAdvertizementURLString) {
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,12 @@ struct AuthenticationServerSelectionScreen: View {
|
||||
/// The title, message and icon at the top of the screen.
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
OnboardingIconImage(image: Asset.Images.welcomeExperience1)
|
||||
.padding(.bottom, 8)
|
||||
if BWIBuildSettings.shared.bumLoginFlowLayout {
|
||||
ServerIcon(image: Asset.Images.welcomeExperience1, size: OnboardingMetrics.iconSize)
|
||||
} else {
|
||||
OnboardingIconImage(image: Asset.Images.welcomeExperience1)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
Text(viewModel.viewState.headerTitle)
|
||||
.font(theme.fonts.title2B)
|
||||
|
||||
@@ -24,8 +24,8 @@ struct OnboardingIconImage: View {
|
||||
var body: some View {
|
||||
Image(image.name)
|
||||
.resizable()
|
||||
//.renderingMode(.template)
|
||||
//.foregroundColor(theme.colors.accent)
|
||||
.renderingMode(.template)
|
||||
.foregroundColor(theme.colors.accent)
|
||||
.frame(width: OnboardingMetrics.iconSize, height: OnboardingMetrics.iconSize)
|
||||
.background(Circle().foregroundColor(.white).padding(2))
|
||||
.accessibilityHidden(true)
|
||||
|
||||
65
bwi/CommonUI/ServerIcon.swift
Normal file
65
bwi/CommonUI/ServerIcon.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
struct OnboardingIconImage: View {
|
||||
@Environment(\.theme) private var theme
|
||||
|
||||
let image: ImageAsset
|
||||
|
||||
var body: some View {
|
||||
Image(image.name)
|
||||
.resizable()
|
||||
.renderingMode(.template)
|
||||
.foregroundColor(theme.colors.accent)
|
||||
.frame(width: OnboardingMetrics.iconSize, height: OnboardingMetrics.iconSize)
|
||||
.background(Circle().foregroundColor(.white).padding(2))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingIconImage_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OnboardingIconImage(image: Asset.Images.authenticationEmailIcon)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ServerIcon: View {
|
||||
@Environment(\.theme) private var theme
|
||||
|
||||
let image: ImageAsset
|
||||
let size: CGFloat
|
||||
|
||||
var body: some View {
|
||||
Image(image.name)
|
||||
.resizable()
|
||||
.frame(width: size, height: size * 1.3)
|
||||
.background(Circle().foregroundColor(.white).padding(2))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerIcon_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ServerIcon(image: Asset.Images.authenticationEmailIcon, size: 72)
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,38 @@ public extension MXWellKnown {
|
||||
catch {
|
||||
return .whenTyping
|
||||
}
|
||||
|
||||
return .whenTyping
|
||||
}
|
||||
|
||||
// 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 {
|
||||
let bwi = try WellknownBWI(dict: self.jsonDictionary()["de.bwi"] as! [String : Any])
|
||||
|
||||
if let urlString = bwi.dataPrivacyURL {
|
||||
if URL(string: urlString) != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dataPrivacyURL() -> String? {
|
||||
do {
|
||||
guard let bwiDict = self.jsonDictionary()["de.bwi"] as? [String : Any] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let bwi = try WellknownBWI(dict: bwiDict)
|
||||
return bwi.dataPrivacyURL
|
||||
}
|
||||
catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
bwi/Wellknown/WellknownBWI.swift
Normal file
34
bwi/Wellknown/WellknownBWI.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
/*
|
||||
* 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
|
||||
|
||||
struct WellknownBWI {
|
||||
let dataPrivacyURL: String?
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user