Commit 2ff3a26a authored by Quentin Muret's avatar Quentin Muret Committed by Kateryna Kostiuk

UI/UX: refactor welcome and account creation view

- refactor UI / UX
- New launchScreen
- add enable notification switch (on by default)
- add encrypt my local account switch (off by default)
- manage correctly the bottom offset of the scroll view (when
  the keyboard is shown or dismissed)
- we can now dismiss the keyboard when tipping outside and
  scroll it manually
- adapt the UI for all devices
- the UI texts are now changing dynamically regarding the
  language of the device

Change-Id: I4fecb7bc5bfda5585ce7a4c83ad3a64a2e6b246c
Reviewed-by: Kateryna Kostiuk<kateryna.kostiuk@savoirfairelinux.com>
parent 67c37f47
......@@ -9,5 +9,5 @@ github "ashleymills/Reachability.swift" "v4.3.0"
github "gskbyte/GSKStretchyHeaderView" "1.0.4"
github "optonaut/ActiveLabel.swift" "1.0.1"
github "pkluz/PKHUD" "5.2.0"
github "realm/realm-cocoa" "v3.11.1"
github "realm/realm-cocoa" "v3.12.0"
github "stephencelis/SQLite.swift" "0.11.5"
......@@ -40,6 +40,7 @@ internal enum Asset {
internal static let enableSpeakerphone = ImageAsset(name: "enable_speakerphone")
internal static let fallbackAvatar = ImageAsset(name: "fallback_avatar")
internal static let icContactPicture = ImageAsset(name: "ic_contact_picture")
internal static let jamiIcon = ImageAsset(name: "jamiIcon")
internal static let leftArrow = ImageAsset(name: "left_arrow")
internal static let moreSettings = ImageAsset(name: "more_settings")
internal static let pauseCall = ImageAsset(name: "pause_call")
......
......@@ -128,25 +128,35 @@ internal enum L10n {
}
internal enum CreateAccount {
/// Encrypt my account
internal static let chooseAPassword = L10n.tr("Localizable", "createAccount.ChooseAPassword")
/// Choose strong password you will remember to protect your Ring account.
internal static let chooseStrongPassword = L10n.tr("Localizable", "createAccount.chooseStrongPassword")
/// Create your Ring account
/// Create your account
internal static let createAccountFormTitle = L10n.tr("Localizable", "createAccount.createAccountFormTitle")
/// username
/// Notifications
internal static let enableNotifications = L10n.tr("Localizable", "createAccount.EnableNotifications")
/// Username
internal static let enterNewUsernamePlaceholder = L10n.tr("Localizable", "createAccount.enterNewUsernamePlaceholder")
/// invalid username
internal static let invalidUsername = L10n.tr("Localizable", "createAccount.invalidUsername")
/// Loading
internal static let loading = L10n.tr("Localizable", "createAccount.loading")
/// looking for username availability
/// looking for availability…
internal static let lookingForUsernameAvailability = L10n.tr("Localizable", "createAccount.lookingForUsernameAvailability")
/// password
/// Password
internal static let newPasswordPlaceholder = L10n.tr("Localizable", "createAccount.newPasswordPlaceholder")
/// 6 characters minimum
internal static let passwordCharactersNumberError = L10n.tr("Localizable", "createAccount.passwordCharactersNumberError")
/// Choose a password to encrypt your local account. Don’t forget it or you will not be able to recover your account
internal static let passwordInformation = L10n.tr("Localizable", "createAccount.PasswordInformation")
/// passwords do not match
internal static let passwordNotMatchingError = L10n.tr("Localizable", "createAccount.passwordNotMatchingError")
/// confirm password
/// (Recommended)
internal static let recommended = L10n.tr("Localizable", "createAccount.Recommended")
/// Register a username
internal static let registerAUsername = L10n.tr("Localizable", "createAccount.RegisterAUsername")
/// Confirm password
internal static let repeatPasswordPlaceholder = L10n.tr("Localizable", "createAccount.repeatPasswordPlaceholder")
/// username already taken
internal static let usernameAlreadyTaken = L10n.tr("Localizable", "createAccount.usernameAlreadyTaken")
......@@ -250,13 +260,13 @@ internal enum L10n {
}
internal enum Welcome {
/// Create a Ring account
/// Create a Jami account
internal static let createAccount = L10n.tr("Localizable", "welcome.createAccount")
/// Link this device to an account
internal static let linkDevice = L10n.tr("Localizable", "welcome.linkDevice")
/// Ring is a free and universal communication platform which preserves the users' privacy and freedoms
internal static let text = L10n.tr("Localizable", "welcome.text")
/// Welcome to Ring
/// Welcome to Jami !
internal static let title = L10n.tr("Localizable", "welcome.title")
}
}
......
......@@ -34,8 +34,8 @@ extension UIColor {
self.init(red: (hex >> 16) & 0xff, green: (hex >> 8) & 0xff, blue: hex & 0xff, alpha: alpha)
}
static let ringMain = UIColor(red: 54, green: 125, blue: 156, alpha: 1.0)
static let ringSecondary = UIColor(red: 0, green: 76, blue: 96, alpha: 1.0)
static let ringMain = UIColor(hex: 0x017CBD, alpha: 1.0) // jami style
static let ringSecondary = UIColor(hex: 0x1F4971, alpha: 1.0) // jami style
static let ringMainLight = UIColor(red: 0, green: 76, blue: 96, alpha: 1.0)
static let ringMsgCellEmoji = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
static let ringMsgCellSent = UIColor(red: 58, green: 192, blue: 210, alpha: 1.0)
......@@ -52,4 +52,7 @@ extension UIColor {
static let ringSuccess = UIColor(hex: 0x00b20b, alpha: 1.0)
static let ringFailure = UIColor(hex: 0xf00000, alpha: 1.0)
static let ringWarning = UIColor.orange
static let jamiButtonLight = UIColor(hex: 0x285F97, alpha: 1.0)
static let jamiButtonDark = UIColor(hex: 0x0F2643, alpha: 1.0)
}
......@@ -151,4 +151,51 @@ extension UIView {
UIGraphicsEndImageContext()
return image
}
func applyGradient(with colours: [UIColor], locations: [NSNumber]? = nil) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colours.map { $0.cgColor }
gradient.locations = locations
self.layer.insertSublayer(gradient, at: 0)
}
func applyGradient(with colours: [UIColor], gradient orientation: GradientOrientation) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colours.map { $0.cgColor }
gradient.startPoint = orientation.startPoint
gradient.endPoint = orientation.endPoint
self.layer.insertSublayer(gradient, at: 0)
}
}
typealias GradientPoints = (startPoint: CGPoint, endPoint: CGPoint)
enum GradientOrientation {
case topRightBottomLeft
case topLeftBottomRight
case horizontal
case vertical
var startPoint : CGPoint {
return points.startPoint
}
var endPoint : CGPoint {
return points.endPoint
}
var points : GradientPoints {
switch self {
case .topRightBottomLeft:
return (CGPoint(x: 0.0,y: 1.0), CGPoint(x: 1.0,y: 0.0))
case .topLeftBottomRight:
return (CGPoint(x: 0.0,y: 0.0), CGPoint(x: 1,y: 1))
case .horizontal:
return (CGPoint(x: 0.0,y: 0.5), CGPoint(x: 1.0,y: 0.5))
case .vertical:
return (CGPoint(x: 0.0,y: 0.0), CGPoint(x: 0.0,y: 1.0))
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="COY-AX-993">
<device id="retina4_7" orientation="portrait">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="COY-AX-993">
<device id="retina5_9" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
......@@ -19,56 +18,23 @@
<viewControllerLayoutGuide type="bottom" id="yiq-Ru-NtI"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="aRx-1z-bhh">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="RING" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mT3-yy-Ins">
<rect key="frame" x="162" y="320.5" width="51" height="26.5"/>
<fontDescription key="fontDescription" type="system" pointSize="22"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="background_ring" translatesAutoresizingMaskIntoConstraints="NO" id="0jH-Qw-sbt">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vx3-sT-ceQ" userLabel="Gradient View">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" verticalHuggingPriority="251" image="ring_logo" translatesAutoresizingMaskIntoConstraints="NO" id="noP-kw-7tM">
<rect key="frame" x="92" y="302" width="191" height="63"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</imageView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="T5f-Vn-hsM">
<rect key="frame" x="169.5" y="373" width="37" height="37"/>
<constraints>
<constraint firstAttribute="height" constant="37" id="OXD-aw-oL2"/>
<constraint firstAttribute="width" constant="37" id="W9J-rm-eGu"/>
</constraints>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.2274509804" green="0.75294117650000003" blue="0.82352941180000006" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="displayP3"/>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" verticalHuggingPriority="251" image="jamiIcon" translatesAutoresizingMaskIntoConstraints="NO" id="noP-kw-7tM">
<rect key="frame" x="137.66666666666666" y="358.66666666666669" width="100" height="95"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="T5f-Vn-hsM" firstAttribute="top" secondItem="noP-kw-7tM" secondAttribute="bottom" constant="8" id="9DO-PZ-5no"/>
<constraint firstItem="T5f-Vn-hsM" firstAttribute="centerX" secondItem="vx3-sT-ceQ" secondAttribute="centerX" id="LBl-Ca-4f2"/>
<constraint firstItem="noP-kw-7tM" firstAttribute="centerY" secondItem="vx3-sT-ceQ" secondAttribute="centerY" id="OjY-lE-ODE"/>
<constraint firstItem="noP-kw-7tM" firstAttribute="centerX" secondItem="vx3-sT-ceQ" secondAttribute="centerX" id="q4D-DQ-coq"/>
<constraint firstAttribute="width" secondItem="noP-kw-7tM" secondAttribute="height" multiplier="20:19" id="90r-Az-47a"/>
<constraint firstAttribute="width" constant="100" id="u8R-gO-yAc"/>
</constraints>
</view>
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="0jH-Qw-sbt" firstAttribute="leading" secondItem="aRx-1z-bhh" secondAttribute="leading" id="7DO-cX-ZHu"/>
<constraint firstAttribute="bottom" secondItem="vx3-sT-ceQ" secondAttribute="bottom" id="Dbq-On-bgN"/>
<constraint firstItem="mT3-yy-Ins" firstAttribute="centerX" secondItem="aRx-1z-bhh" secondAttribute="centerX" id="TUh-hS-ILM"/>
<constraint firstItem="0jH-Qw-sbt" firstAttribute="top" secondItem="aRx-1z-bhh" secondAttribute="top" id="Ttd-hs-cxC"/>
<constraint firstAttribute="trailing" secondItem="0jH-Qw-sbt" secondAttribute="trailing" id="bQI-fo-zjq"/>
<constraint firstItem="mT3-yy-Ins" firstAttribute="centerY" secondItem="aRx-1z-bhh" secondAttribute="centerY" id="hcy-ws-hgV"/>
<constraint firstAttribute="trailing" secondItem="vx3-sT-ceQ" secondAttribute="trailing" id="iio-7B-a2J"/>
<constraint firstItem="vx3-sT-ceQ" firstAttribute="top" secondItem="aRx-1z-bhh" secondAttribute="top" id="ltY-hm-fnP"/>
<constraint firstItem="yiq-Ru-NtI" firstAttribute="top" secondItem="0jH-Qw-sbt" secondAttribute="bottom" id="vPl-N3-Vvw"/>
<constraint firstItem="vx3-sT-ceQ" firstAttribute="leading" secondItem="aRx-1z-bhh" secondAttribute="leading" id="yoR-WM-6bH"/>
<constraint firstItem="noP-kw-7tM" firstAttribute="centerY" secondItem="aRx-1z-bhh" secondAttribute="centerY" id="YcT-vx-Xtv"/>
<constraint firstItem="noP-kw-7tM" firstAttribute="centerX" secondItem="aRx-1z-bhh" secondAttribute="centerX" id="gPC-Oy-B1H"/>
</constraints>
<viewLayoutGuide key="safeArea" id="vc6-gh-ZiE"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="u5I-td-CmB" userLabel="First Responder" sceneMemberID="firstResponder"/>
......@@ -77,7 +43,6 @@
</scene>
</scenes>
<resources>
<image name="background_ring" width="750" height="1334"/>
<image name="ring_logo" width="191" height="63"/>
<image name="jamiIcon" width="200" height="190"/>
</resources>
</document>
......@@ -33,27 +33,49 @@ class CreateAccountViewController: UIViewController, StoryboardBased, ViewModelB
self.registerUsernameHeightConstraintConstant = registerUsernameHeightConstraint.constant
}
}
@IBOutlet weak var backgroundNavigationBarHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var choosePasswordViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var scrollViewBottomConstraint: NSLayoutConstraint!
@IBOutlet weak var usernameSwitch: UISwitch!
@IBOutlet weak var passwordSwitch: UISwitch!
@IBOutlet weak var notificationsSwitch: UISwitch!
@IBOutlet weak var registerUsernameView: UIView!
@IBOutlet weak var registerPasswordView: UIView!
@IBOutlet weak var registerUsernameLabel: UILabel!
@IBOutlet weak var recommendedLabel: UILabel!
@IBOutlet weak var registerUsernameErrorLabel: UILabel!
@IBOutlet weak var passwordTextField: DesignableTextField!
@IBOutlet weak var confirmPasswordTextField: DesignableTextField!
@IBOutlet weak var passwordErrorLabel: UILabel!
@IBOutlet weak var usernameTextField: DesignableTextField!
@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var chooseAPasswordLabel: UILabel!
@IBOutlet weak var passwordInfoLabel: UILabel!
@IBOutlet weak var enableNotificationsLabel: UILabel!
// MARK: members
private let disposeBag = DisposeBag()
var viewModel: CreateAccountViewModel!
var registerUsernameHeightConstraintConstant: CGFloat = 0.0
@IBOutlet weak var containerViewBottomConstraint: NSLayoutConstraint!
var keyboardDismissTapRecognizer: UITapGestureRecognizer!
var isKeyboardOpened: Bool = false
// MARK: functions
override func viewDidLoad() {
super.viewDidLoad()
// L10n
self.applyL10n()
super.viewDidLoad()
self.view.layoutIfNeeded()
// Style
self.scrollView.alwaysBounceHorizontal = false
self.scrollView.alwaysBounceVertical = true
self.createAccountButton.applyGradient(with: [UIColor.jamiButtonLight, UIColor.jamiButtonDark], gradient: .horizontal)
let device = UIDevice.modelName
self.backgroundNavigationBarHeightConstraint.constant = UIApplication.shared.statusBarFrame.height
self.usernameTextField.tintColor = UIColor.ringSecondary
self.passwordTextField.tintColor = UIColor.ringSecondary
self.confirmPasswordTextField.tintColor = UIColor.ringSecondary
// Bind ViewModel to View
self.bindViewModelToView()
......@@ -63,11 +85,55 @@ class CreateAccountViewController: UIViewController, StoryboardBased, ViewModelB
// handle keyboard
self.adaptToKeyboardState(for: self.scrollView, with: self.disposeBag)
keyboardDismissTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.statusBarStyle = .default
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillAppear(withNotification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillDisappear(withNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func setContentInset() {
if !self.isKeyboardOpened {
self.containerViewBottomConstraint.constant = -20
return
}
let device = UIDevice.modelName
switch device {
case "iPhone X", "iPhone XS", "iPhone XS Max", "iPhone XR" :
self.containerViewBottomConstraint.constant = 100
default :
self.containerViewBottomConstraint.constant = 70
}
}
@objc func dismissKeyboard() {
self.isKeyboardOpened = false
self.becomeFirstResponder()
view.removeGestureRecognizer(keyboardDismissTapRecognizer)
}
@objc func keyboardWillAppear(withNotification: NSNotification){
self.isKeyboardOpened = true
self.view.addGestureRecognizer(keyboardDismissTapRecognizer)
self.setContentInset()
}
@objc func keyboardWillDisappear(withNotification: NSNotification){
view.removeGestureRecognizer(keyboardDismissTapRecognizer)
self.setContentInset()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
override var canBecomeFirstResponder: Bool {
return true
}
private func applyL10n() {
......@@ -76,6 +142,11 @@ class CreateAccountViewController: UIViewController, StoryboardBased, ViewModelB
self.usernameTextField.placeholder = self.viewModel.usernameTitle
self.passwordTextField.placeholder = self.viewModel.passwordTitle
self.confirmPasswordTextField.placeholder = self.viewModel.confirmPasswordTitle
self.registerUsernameLabel.text = self.viewModel.registerAUserNameTitle
self.chooseAPasswordLabel.text = self.viewModel.chooseAPasswordTitle
self.passwordInfoLabel.text = self.viewModel.passwordInfoTitle
self.enableNotificationsLabel.text = self.viewModel.enableNotificationsTitle
self.recommendedLabel.text = self.viewModel.recommendedTitle
}
private func bindViewModelToView() {
......@@ -86,18 +157,37 @@ class CreateAccountViewController: UIViewController, StoryboardBased, ViewModelB
UIView.animate(withDuration: 0.3, animations: {
if isOn {
self.registerUsernameHeightConstraint.constant = self.registerUsernameHeightConstraintConstant
self.registerUsernameView.alpha = 1.0
DispatchQueue.global(qos: .background).async {
usleep(300000)
DispatchQueue.main.async {
UIView.animate(withDuration: 0.3, animations: {
self.registerUsernameView.alpha = 1.0
})
}
}
} else {
self.registerUsernameHeightConstraint.constant = 0
self.registerUsernameView.alpha = 0.0
}
self.setContentInset()
self.view.layoutIfNeeded()
})
}).disposed(by: self.disposeBag)
// handle Create Account Button state
self.viewModel.canAskForAccountCreation.bind(to: self.createAccountButton.rx.isEnabled).disposed(by: self.disposeBag)
self.viewModel.canAskForAccountCreation.subscribe(onNext: { [weak self] enable in
if enable {
DispatchQueue.main.async {
self?.createAccountButton.alpha = 1
self?.createAccountButton.isEnabled = true
}
} else {
DispatchQueue.main.async {
self?.createAccountButton.alpha = 0.6
self?.createAccountButton.isEnabled = false
}
}
}).disposed(by: self.disposeBag)
// handle password error
self.viewModel.passwordValidationState.map { $0.isValidated }
......@@ -136,9 +226,40 @@ class CreateAccountViewController: UIViewController, StoryboardBased, ViewModelB
}).disposed(by: self.disposeBag)
}
private func managePasswordSwitch(isOn: Bool) {
UIView.animate(withDuration: 0.3, animations: { [weak self] in
if isOn {
guard let height = self?.passwordInfoLabel.frame.height else {return}
self?.registerPasswordView.isHidden = false
self?.choosePasswordViewHeightConstraint.constant = 133 + height
self?.view.layoutIfNeeded()
DispatchQueue.global(qos: .background).async {
usleep(300000)
DispatchQueue.main.async {
UIView.animate(withDuration: 0.3, animations: {
self?.registerPasswordView.alpha = 1.0
})
}
}
} else {
self?.choosePasswordViewHeightConstraint.constant = 0
self?.registerPasswordView.alpha = 0.0
self?.passwordTextField.text = ""
self?.confirmPasswordTextField.text = ""
self?.passwordErrorLabel.isHidden = true
}
self?.setContentInset()
self?.view.layoutIfNeeded()
})
}
private func bindViewToViewModel() {
// Bind View Outlets to ViewModel
self.usernameSwitch.rx.isOn.bind(to: self.viewModel.registerUsername).disposed(by: self.disposeBag)
self.passwordSwitch.rx.isOn.subscribe(onNext: { [weak self] isOn in
self?.managePasswordSwitch(isOn: isOn)
}).disposed(by: self.disposeBag)
self.notificationsSwitch.rx.isOn.bind(to: self.viewModel.notificationSwitch).disposed(by: self.disposeBag)
self.usernameTextField.rx.text.orEmpty.throttle(3, scheduler: MainScheduler.instance).distinctUntilChanged().bind(to: self.viewModel.username).disposed(by: self.disposeBag)
self.passwordTextField.rx.text.orEmpty.bind(to: self.viewModel.password).disposed(by: self.disposeBag)
self.confirmPasswordTextField.rx.text.orEmpty.bind(to: self.viewModel.confirmPassword).disposed(by: self.disposeBag)
......
......@@ -153,6 +153,11 @@ class CreateAccountViewModel: Stateable, ViewModel {
let usernameTitle = L10n.CreateAccount.enterNewUsernamePlaceholder
let passwordTitle = L10n.CreateAccount.newPasswordPlaceholder
let confirmPasswordTitle = L10n.CreateAccount.repeatPasswordPlaceholder
let registerAUserNameTitle = L10n.CreateAccount.registerAUsername
let chooseAPasswordTitle = L10n.CreateAccount.chooseAPassword
let passwordInfoTitle = L10n.CreateAccount.passwordInformation
let enableNotificationsTitle = L10n.CreateAccount.enableNotifications
let recommendedTitle = L10n.CreateAccount.recommended
// MARK: - Low level services
private let accountService: AccountsService
......@@ -167,6 +172,7 @@ class CreateAccountViewModel: Stateable, ViewModel {
let password = Variable<String>("")
let confirmPassword = Variable<String>("")
let registerUsername = Variable<Bool>(true)
let notificationSwitch = Variable<Bool>(true)
lazy var passwordValidationState: Observable<PasswordValidationState> = {
return Observable.combineLatest(self.password.asObservable(), self.confirmPassword.asObservable())
{ (password: String, confirmPassword: String) -> PasswordValidationState in
......@@ -281,6 +287,15 @@ class CreateAccountViewModel: Stateable, ViewModel {
func createAccount() {
self.accountCreationState.value = .started
self.accountService.addRingAccount(withUsername: self.username.value,
password: self.password.value)
password: self.password.value, enable: self.notificationSwitch.value)
self.enablePushNotifications(enable: self.notificationSwitch.value)
}
func enablePushNotifications(enable: Bool) {
if enable {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: NotificationName.enablePushNotifications.rawValue), object: nil)
return
}
NotificationCenter.default.post(name: NSNotification.Name(rawValue: NotificationName.disablePushNotifications.rawValue), object: nil)
}
}
......@@ -32,13 +32,18 @@ class WelcomeViewController: UIViewController, StoryboardBased, ViewModelBased {
@IBOutlet weak var linkDeviceButton: DesignableButton!
@IBOutlet weak var createAccountButton: DesignableButton!
// MARK: constraints
@IBOutlet weak var ringLogoBottomConstraint: NSLayoutConstraint!
// MARK: members
private let disposeBag = DisposeBag()
// MARK: functions
override func viewDidLoad() {
super.viewDidLoad()
self.initialAnimation()
self.createAccountButton.applyGradient(with: [UIColor.jamiButtonLight, UIColor.jamiButtonDark], gradient: .horizontal)
self.linkDeviceButton.applyGradient(with: [UIColor.jamiButtonLight, UIColor.jamiButtonDark], gradient: .horizontal)
// Bind ViewModel to View
self.viewModel.welcomeText.bind(to: self.welcomeTextLabel.rx.text).disposed(by: self.disposeBag)
self.viewModel.createAccount.bind(to: self.createAccountButton.rx.title(for: .normal)).disposed(by: self.disposeBag)
......@@ -54,10 +59,27 @@ class WelcomeViewController: UIViewController, StoryboardBased, ViewModelBased {
}).disposed(by: self.disposeBag)
}
func initialAnimation() {
DispatchQueue.global(qos: .background).async {
sleep(1)
DispatchQueue.main.async { [weak self] in
self?.ringLogoBottomConstraint.constant = -72
UIView.animate(withDuration: 0.5, animations: {
self?.ringLogoBottomConstraint.constant = -200
self?.welcomeTextLabel.alpha = 1
self?.createAccountButton.alpha = 1
self?.linkDeviceButton.alpha = 1
self?.view.layoutIfNeeded()
})
}
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.statusBarStyle = .default
self.navigationController?.navigationBar.tintColor = UIColor.ringMain
self.navigationController?.navigationBar.tintColor = UIColor.ringSecondary
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
......
......@@ -30,7 +30,7 @@ class WelcomeViewModel: Stateable, ViewModel {
}()
// MARK: - Rx Singles for L10n
let welcomeText = Observable<String>.of(L10n.Welcome.text)
let welcomeText = Observable<String>.of(L10n.Welcome.title)
let createAccount = Observable<String>.of(L10n.Welcome.createAccount)
let linkDevice = Observable<String>.of(L10n.Welcome.linkDevice)
......
{
"images" : [
{
"idiom" : "universal",
"filename" : "jami.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "jami-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "jami-2.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_0" orientation="portrait">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina5_9" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
......@@ -18,37 +18,22 @@
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="background_ring" translatesAutoresizingMaskIntoConstraints="NO" id="QbZ-1j-Ebd">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0tt-6B-dHo" userLabel="Gradient View">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" verticalHuggingPriority="251" image="ring_logo" translatesAutoresizingMaskIntoConstraints="NO" id="kP1-oe-ZEx">
<rect key="frame" x="64.5" y="252.5" width="191" height="63"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.2274509804" green="0.75294117650000003" blue="0.82352941180000006" alpha="0.34999999999999998" colorSpace="custom" customColorSpace="displayP3"/>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" verticalHuggingPriority="251" image="jamiIcon" translatesAutoresizingMaskIntoConstraints="NO" id="kP1-oe-ZEx">
<rect key="frame" x="137.66666666666666" y="358.66666666666669" width="100" height="95"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kP1-oe-ZEx" firstAttribute="centerX" secondItem="0tt-6B-dHo" secondAttribute="centerX" id="54e-9c-yxP"/>
<constraint firstItem="kP1-oe-ZEx" firstAttribute="centerY" secondItem="0tt-6B-dHo" secondAttribute="centerY" id="fXn-9R-frB"/>
<constraint firstAttribute="width" secondItem="kP1-oe-ZEx" secondAttribute="height" multiplier="20:19" id="5Bg-Lw-vZJ"/>
<constraint firstAttribute="width" constant="100" id="Vc4-VQ-EsM"/>
</constraints>
</view>
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="0tt-6B-dHo" secondAttribute="bottom" id="Bwv-uE-mOw"/>
<constraint firstItem="QbZ-1j-Ebd" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="GdO-YK-ig4"/>
<constraint firstItem="xb3-aO-Qok" firstAttribute="top" secondItem="QbZ-1j-Ebd" secondAttribute="bottom" id="OrU-s1-c96"/>
<constraint firstItem="0tt-6B-dHo" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="WEG-5j-2f3"/>
<constraint firstItem="0tt-6B-dHo" firstAttribute="top" secondItem="Ze5-6b-2t3" secondAttribute="top" id="h0e-hO-zEy"/>
<constraint firstAttribute="trailing" secondItem="0tt-6B-dHo" secondAttribute="trailing" id="hq4-6q-gx1"/>
<constraint firstItem="QbZ-1j-Ebd" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leading" id="mB5-R8-Vf7"/>
<constraint firstAttribute="trailing" secondItem="QbZ-1j-Ebd" secondAttribute="trailing" id="n4Y-q4-dRf"/>
<constraint firstItem="kP1-oe-ZEx" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="ksh-ub-gUe"/>
<constraint firstItem="kP1-oe-ZEx" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="soi-Xo-HWk"/>
</constraints>
</view>
</viewController>
......@@ -58,7 +43,6 @@
</scene>
</scenes>
<resources>
<image name="background_ring" width="750" height="1334"/>
<image name="ring_logo" width="191" height="63"/>
<image name="jamiIcon" width="200" height="190"/>
</resources>
</document>
......@@ -40,28 +40,33 @@
// Walkthrough
//Welcome Screen
"welcome.title" = "Welcome to Ring";
"welcome.title" = "Welcome to Jami !";
"welcome.text" = "Ring is a free and universal communication platform which preserves the users' privacy and freedoms";
"welcome.linkDevice" = "Link this device to an account";
"welcome.createAccount" = "Create a Ring account";
"welcome.createAccount" = "Create a Jami account";
//Creation Profile Screen
"createProfile.skipCreateProfile" = "Skip";
"createProfile.profileCreated" = "Next";