Skip to content

Commit

Permalink
feat: Backend redirect UI - WPB-15224 (#2404)
Browse files Browse the repository at this point in the history
Co-authored-by: El-Fitz <[email protected]>
  • Loading branch information
KaterinaWire and El-Fitz authored Jan 29, 2025
1 parent 868841c commit b4d966a
Show file tree
Hide file tree
Showing 22 changed files with 515 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation

struct BackendEnvironmentInfo: Sendable {

let title: String
let backendURL: URL
let backendWSURL: URL
let blacklistURL: URL
let teamsURL: URL
let accountsURL: URL
let websiteURL: URL

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,17 @@
"authentication.identity.input.field.placeholder" = "Email or SSO code";
"authentication.identity.input.field.title" = "Email or SSO code";
"authentication.identity.input.submit" = "Next";
"authentication.identity.input.terms" = "By pressing on “Next”, you accept Wire’s [Terms and Conditions](%@)";

"switch_backend_confirmation.title" = "Redirect to an on-premises backend?";
"switch_backend_confirmation.message" = "If you proceed, you will be redirected to the following on-premises backend to log in:";
"switch_backend_confirmation.backend_name" = "Backend name:";
"switch_backend_confirmation.backend_url" = "Backend URL:";
"switch_backend_confirmation.backend_wsurl" = "Backend websocket URL:";
"switch_backend_confirmation.blacklist_url" = "Blacklist URL:";
"switch_backend_confirmation.teams_url" = "Teams URL:";
"switch_backend_confirmation.accounts_url" = "Accounts URL:";
"switch_backend_confirmation.website_url" = "Website URL:";
"switch_backend_confirmation.proceed" = "Proceed";
"switch_backend_confirmation.cancel" = "Cancel";
"switch_backend_confirmation.show_details" = "Show details";
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation

package class SwitchBackendConfirmationViewModel {

private typealias Strings = L10n.SwitchBackendConfirmation

// MARK: - State

let items: [ItemUIModel]

private let action: (Event) -> Void

// MARK: - Life cycle

convenience init(
environment: BackendEnvironmentInfo,
action: @escaping (Event) -> Void
) {
self.init(
backendName: environment.title,
backendURL: environment.backendURL.absoluteString,
backendWSURL: environment.backendWSURL.absoluteString,
blacklistURL: environment.blacklistURL.absoluteString,
teamsURL: environment.teamsURL.absoluteString,
accountsURL: environment.accountsURL.absoluteString,
websiteURL: environment.websiteURL.absoluteString,
action: action
)
}

package init(
backendName: String,
backendURL: String,
backendWSURL: String,
blacklistURL: String,
teamsURL: String,
accountsURL: String,
websiteURL: String,
action: @escaping (Event) -> Void
) {
self.action = action
self.items = [
ItemUIModel(title: Strings.backendName, value: backendName, isURL: false),
ItemUIModel(title: Strings.backendUrl, value: backendURL, isURL: true),
ItemUIModel(title: Strings.backendWsurl, value: backendWSURL, isURL: true),
ItemUIModel(title: Strings.blacklistUrl, value: blacklistURL, isURL: true),
ItemUIModel(title: Strings.teamsUrl, value: teamsURL, isURL: true),
ItemUIModel(title: Strings.accountsUrl, value: accountsURL, isURL: true),
ItemUIModel(title: Strings.websiteUrl, value: websiteURL, isURL: true)
]
}

// MARK: - Events

package enum Event {

case didCancel
case didConfirm

}

func handleEvent(_ event: Event) {
action(event)
}

// MARK: - Model

package struct ItemUIModel {
let title: String
let value: String
let isURL: Bool
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import SwiftUI

package struct SwitchBackendConfirmationViewPreview: View {
package init() {}

package var body: some View {
VStack {
SwitchBackendConfirmationView(
viewModel: SwitchBackendConfirmationViewModel(
backendName: "Staging",
backendURL: "www.staging.com",
backendWSURL: "www.ws.staging.com",
blacklistURL: "www.blacklist.staging.com",
teamsURL: "www.teams.staging.com",
accountsURL: "www.accounts.staging.com",
websiteURL: "www.wire.com",
action: { _ in }
)
)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import SwiftUI
import WireDesign

package struct SwitchBackendConfirmationView: View {

// MARK: - Properties

@Environment(\.dismiss) var dismiss
@State private var showFullDetails: Bool = false

private typealias Strings = L10n.SwitchBackendConfirmation

private let viewModel: SwitchBackendConfirmationViewModel

package init(
viewModel: SwitchBackendConfirmationViewModel
) {
self.viewModel = viewModel
}

package var body: some View {
VStack(spacing: 20) {
title
backendDetails
buttons
}
.padding()
.interactiveDismissDisabled()
.background(ColorTheme.Backgrounds.surface.color)
.cornerRadius(16)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(ColorTheme.Backgrounds.surface.color, lineWidth: 1)
)
.fixedSize(horizontal: false, vertical: true)
}

private var title: some View {
Text(Strings.title)
.font(.textStyle(.h2))
.foregroundStyle(Color.primaryText)
.multilineTextAlignment(.center)
.lineLimit(nil)
.minimumScaleFactor(0.8)
}

private var backendDetails: some View {
Group {
if showFullDetails {
fullDetails
.transition(.asymmetric(insertion: .opacity, removal: .opacity))
} else {
shortDetails
.transition(.asymmetric(insertion: .opacity, removal: .opacity))
}
}
.animation(.default, value: showFullDetails)
}

private var contentHeight: CGFloat {
UIScreen.main.bounds.height * 0.4
}

private var fullDetails: some View {
ScrollView {
VStack(spacing: 16) {
Text(Strings.message)
.foregroundStyle(Color.primaryText)
.multilineTextAlignment(.center)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)

ForEach(viewModel.items, id: \.title) { model in
itemView(
title: model.title,
value: model.value,
isURL: model.isURL
)
}
}
}
.frame(maxHeight: contentHeight)
}

private var shortDetails: some View {
ScrollView {
VStack(spacing: 16) {
Text(Strings.message)
.foregroundStyle(Color.primaryText)
.multilineTextAlignment(.center)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
ForEach(viewModel.items.prefix(2), id: \.title) { model in
itemView(
title: model.title,
value: model.value,
isURL: model.isURL
)
}
Button {
withAnimation {
showFullDetails.toggle()
}
} label: {
Text(Strings.showDetails)
.font(.textStyle(.body1))
}
.wireButtonStyle(.link)
}
}
.frame(maxHeight: contentHeight)
}

private func itemView(
title: String,
value: String,
isURL: Bool = false
) -> some View {
VStack {
Text(title)
.foregroundStyle(Color.secondaryText)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
Text(value)
.foregroundStyle(Color.primaryText)
.accessibilityTextContentType(isURL ? .fileSystem : .plain)
}
}

private var buttons: some View {
VStack(spacing: 6) {
cancelButton
proceedButton
}
}

private var cancelButton: some View {
Button {
viewModel.handleEvent(.didCancel)
dismiss()
} label: {
Text(Strings.cancel)
.font(.textStyle(.buttonBig))
}
.wireButtonStyle(.secondary)
}

private var proceedButton: some View {
Button {
viewModel.handleEvent(.didConfirm)
dismiss()
} label: {
Text(Strings.proceed)
.font(.textStyle(.buttonBig))
}
.wireButtonStyle(.primary)
}

}

// MARK: - Previews

#Preview("Regular fonts") {
BackgroundView()
.overlay(
ZStack {
SwitchBackendConfirmationViewPreview()
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
)
}

#Preview("Large fonts") {
BackgroundView()
.overlay(
ZStack {
SwitchBackendConfirmationViewPreview()
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.environment(\.sizeCategory, .accessibilityExtraExtraExtraLarge)
)
}
Loading

0 comments on commit b4d966a

Please sign in to comment.