Skip to content

Commit

Permalink
#40: Add the login screen from EI.
Browse files Browse the repository at this point in the history
- Remove SSO and replace fallback with OIDC.
  • Loading branch information
pixlwave authored Jun 28, 2022
1 parent cc14f1f commit d74158c
Show file tree
Hide file tree
Showing 35 changed files with 1,315 additions and 393 deletions.
132 changes: 74 additions & 58 deletions ElementX.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@
"settings_timeline_style" = "Timeline Style";
"room_timeline_style_plain_long_description" = "Plain Timeline";
"room_timeline_style_bubbled_long_description" = "Bubbled Timeline";

// MARK: - Authentication

"authentication_login_title" = "Welcome back!";
"authentication_login_forgot_password" = "Forgot password";

"authentication_server_info_title" = "Choose your server to store your data";
"authentication_server_info_matrix_description" = "Join millions for free on the largest public server";
3 changes: 3 additions & 0 deletions ElementX/Sources/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import Foundation

final class BuildSettings {

// MARK: - Servers
static let defaultHomeserverURLString = "https://matrix.org"

// MARK: - Bug report
static let bugReportServiceBaseUrlString = "https://riot.im/bugreports"
Expand Down
8 changes: 8 additions & 0 deletions ElementX/Sources/Generated/Strings+Untranslated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import Foundation
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
extension ElementL10n {
/// Forgot password
public static let authenticationLoginForgotPassword = ElementL10n.tr("Untranslated", "authentication_login_forgot_password")
/// Welcome back!
public static let authenticationLoginTitle = ElementL10n.tr("Untranslated", "authentication_login_title")
/// Join millions for free on the largest public server
public static let authenticationServerInfoMatrixDescription = ElementL10n.tr("Untranslated", "authentication_server_info_matrix_description")
/// Choose your server to store your data
public static let authenticationServerInfoTitle = ElementL10n.tr("Untranslated", "authentication_server_info_title")
/// Bubbled Timeline
public static let roomTimelineStyleBubbledLongDescription = ElementL10n.tr("Untranslated", "room_timeline_style_bubbled_long_description")
/// Plain Timeline
Expand Down
15 changes: 15 additions & 0 deletions ElementX/Sources/Other/Logging/MXLog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ private var logger: SwiftyBeaver.Type = {
logger.error(message, file, function, line: line)
}

public static func failure(_ message: @autoclosure () -> Any, _
file: String = #file,
_ function: String = #function,
line: Int = #line,
context: Any? = nil) {
logger.error(message(), file, function, line: line, context: context)
assertionFailure("\(message())")
}

@available(swift, obsoleted: 5.4)
@objc public static func logFailure(_ message: String, file: String, function: String, line: Int) {
logger.error(message, file, function, line: line)
assertionFailure(message)
}

// MARK: - Private

fileprivate static func configureLogger(_ logger: SwiftyBeaver.Type, withConfiguration configuration: MXLogConfiguration) {
Expand Down
89 changes: 89 additions & 0 deletions ElementX/Sources/Other/SwiftUI/ErrorHandling/AlertInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// Copyright 2021 New Vector Ltd
//
// 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

/// A type that describes an alert to be shown to the user.
///
/// The alert info can be added to the view state bindings and used as an alert's `item`:
/// ```
/// MyView
/// .alert(item: $viewModel.alertInfo) { $0.alert }
/// ```
struct AlertInfo<T: Hashable>: Identifiable {
/// An identifier that can be used to distinguish one error from another.
let id: T
/// The alert's title.
let title: String
/// The alert's message (optional).
var message: String?
/// The alert's primary button title and action. Defaults to an Ok button with no action.
var primaryButton: (title: String, action: (() -> Void)?) = (ElementL10n.ok, nil)
/// The alert's secondary button title and action.
var secondaryButton: (title: String, action: (() -> Void)?)?
}

extension AlertInfo {
/// Initialises the type with the title from an `Error`'s localised description along with the default Ok button.
///
/// Currently this initialiser creates an alert for every error, however in the future it may be updated to filter
/// out some specific errors such as cancellation and networking issues that create too much noise or are
/// indicated to the user using other mechanisms.
init(error: Error) where T == String {
self.init(id: error.localizedDescription,
title: error.localizedDescription)
}

/// Initialises the type with a generic title and message for an unknown error along with the default Ok button.
/// - Parameters:
/// - id: An ID that identifies the error.
/// - error: The Error that occurred.
init(id: T) {
self.id = id
title = ElementL10n.dialogTitleError
message = ElementL10n.unknownError
}
}

extension AlertInfo {
private var messageText: Text? {
guard let message = message else { return nil }
return Text(message)
}

/// Returns a SwiftUI `Alert` created from this alert info, using default button
/// styles for both primary and (if set) secondary buttons.
var alert: Alert {
if let secondaryButton = secondaryButton {
return Alert(title: Text(title),
message: messageText,
primaryButton: alertButton(for: primaryButton),
secondaryButton: alertButton(for: secondaryButton))
} else {
return Alert(title: Text(title),
message: messageText,
dismissButton: alertButton(for: primaryButton))
}
}

private func alertButton(for buttonParameters: (title: String, action: (() -> Void)?)) -> Alert.Button {
guard let action = buttonParameters.action else {
return .default(Text(buttonParameters.title))
}

return .default(Text(buttonParameters.title), action: action)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ final class LabelledActivityIndicatorView: UIView {
static let padding = UIEdgeInsets(top: 20, left: 40, bottom: 15, right: 40)
static let activityIndicatorScale = CGFloat(1.5)
static let cornerRadius: CGFloat = 12.0
static let stackBackgroundOpacity: CGFloat = 0.9
static let stackSpacing: CGFloat = 15
static let backgroundOpacity: CGFloat = 0.5
}

private let stackBackgroundView: UIView = {
let view = UIView()
let view = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
view.layer.cornerRadius = Constants.cornerRadius
view.alpha = Constants.stackBackgroundOpacity
view.backgroundColor = .gray.withAlphaComponent(0.75)
view.clipsToBounds = true
return view
}()

Expand Down Expand Up @@ -67,6 +65,7 @@ final class LabelledActivityIndicatorView: UIView {
private func setup(text: String) {
setupStackView()
label.text = text
label.textColor = .element.primaryContent
}

private func setupStackView() {
Expand Down
21 changes: 18 additions & 3 deletions ElementX/Sources/Other/UserIndicators/RoundedToastView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,27 @@ class RoundedToastView: UIView {

private func setup(viewState: ToastViewState) {

backgroundColor = .gray.withAlphaComponent(0.75)
backgroundColor = .clear
clipsToBounds = true

setupBackgroundMaterial()
setupStackView()
stackView.addArrangedSubview(toastView(for: viewState.style))
stackView.addArrangedSubview(label)
label.text = viewState.label
label.textColor = .element.primaryContent
}

private func setupBackgroundMaterial() {
let material = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
addSubview(material)
material.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
material.topAnchor.constraint(equalTo: topAnchor),
material.bottomAnchor.constraint(equalTo: bottomAnchor),
material.leadingAnchor.constraint(equalTo: leadingAnchor),
material.trailingAnchor.constraint(equalTo: trailingAnchor)
])
}

private func setupStackView() {
Expand All @@ -101,10 +116,10 @@ class RoundedToastView: UIView {
case .loading:
return activityIndicator
case .success:
imageView.image = UIImage(systemName: "checkmark.circle")
imageView.image = UIImage(systemName: "checkmark")
return imageView
case .error:
imageView.image = UIImage(systemName: "x.circle")
imageView.image = UIImage(systemName: "xmark")
return imageView
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ class AuthenticationCoordinator: Coordinator {
switch action {
case .login:
self.showLoginScreen()
case .register:
fatalError("Not implemented")
}
}

Expand All @@ -69,18 +67,19 @@ class AuthenticationCoordinator: Coordinator {
}

private func showLoginScreen() {
let parameters = LoginScreenCoordinatorParameters()
let coordinator = LoginScreenCoordinator(parameters: parameters)
let homeserver = LoginHomeserver(address: BuildSettings.defaultHomeserverURLString)
let parameters = LoginCoordinatorParameters(navigationRouter: navigationRouter, homeserver: homeserver)
let coordinator = LoginCoordinator(parameters: parameters)

coordinator.callback = { [weak self, weak coordinator] action in
guard let self = self, let coordinator = coordinator else {
return
}

switch action {
case .login(let result):
case .login(let username, let password):
Task {
switch await self.login(username: result.username, password: result.password) {
switch await self.login(username: username, password: password) {
case .success(let userSession):
self.delegate?.authenticationCoordinator(self, didLoginWithSession: userSession)
self.remove(childCoordinator: coordinator)
Expand All @@ -90,6 +89,8 @@ class AuthenticationCoordinator: Coordinator {
MXLog.error("Failed logging in user with error: \(error)")
}
}
case .continueWithOIDC:
break
}
}

Expand Down
Loading

0 comments on commit d74158c

Please sign in to comment.