Commit e79b903d authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

Account: link new device

Add functionality to fetch user password and display PIN that could
be used to link a new device

Change-Id: Ie985b797af64ebe0de1bea9ac64292e427f5302f
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent 44fb2400
......@@ -86,6 +86,10 @@
0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */; };
0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */; };
0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */; };
0ED2B6FA1F96A075001572F0 /* LinkNewDeviceViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */; };
0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */; };
0ED2B6FE1F96A16C001572F0 /* LinkNewDeviceViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ED2B6FD1F96A16C001572F0 /* LinkNewDeviceViewModel.swift */; };
0EDCC8601F98150500B121D7 /* UIView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDCC85F1F98150500B121D7 /* UIView+Rx.swift */; };
0EDE34C71F868E1200FFA15C /* EditProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE34C61F868E1200FFA15C /* EditProfileViewController.swift */; };
0EDE34C91F8691BB00FFA15C /* EditProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE34C81F8691BB00FFA15C /* EditProfileViewModel.swift */; };
0EE1B54E1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE1B54D1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift */; };
......@@ -312,6 +316,10 @@
0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCellGenerated.xib; sourceTree = "<group>"; };
0EB1A5CE1F8EBE03009923E2 /* DeviceCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DeviceCell.xib; sourceTree = "<group>"; };
0EB1A5D01F8EBE23009923E2 /* DeviceCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceCell.swift; sourceTree = "<group>"; };
0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LinkNewDeviceViewController.storyboard; sourceTree = "<group>"; };
0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceViewController.swift; sourceTree = "<group>"; };
0ED2B6FD1F96A16C001572F0 /* LinkNewDeviceViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceViewModel.swift; sourceTree = "<group>"; };
0EDCC85F1F98150500B121D7 /* UIView+Rx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Rx.swift"; sourceTree = "<group>"; };
0EDE34C61F868E1200FFA15C /* EditProfileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditProfileViewController.swift; sourceTree = "<group>"; };
0EDE34C81F8691BB00FFA15C /* EditProfileViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditProfileViewModel.swift; sourceTree = "<group>"; };
0EE1B54D1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CNContactVCardSerialization+Helpers.swift"; sourceTree = "<group>"; };
......@@ -646,6 +654,7 @@
0586C94A1F684DF600613517 /* UIImage+Helpers.swift */,
0EE1B54D1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift */,
621231F81F880EDF009B86F0 /* UILabel+Ring.swift */,
0EDCC85F1F98150500B121D7 /* UIView+Rx.swift */,
);
path = Extensions;
sourceTree = "<group>";
......@@ -805,6 +814,16 @@
name = DeviceCell;
sourceTree = "<group>";
};
0ED2B6F81F96A048001572F0 /* LinknewDevice */ = {
isa = PBXGroup;
children = (
0ED2B6F91F96A075001572F0 /* LinkNewDeviceViewController.storyboard */,
0ED2B6FB1F96A158001572F0 /* LinkNewDeviceViewController.swift */,
0ED2B6FD1F96A16C001572F0 /* LinkNewDeviceViewModel.swift */,
);
name = LinknewDevice;
sourceTree = "<group>";
};
0EDE34C51F868D2D00FFA15C /* Shared */ = {
isa = PBXGroup;
children = (
......@@ -913,6 +932,7 @@
1A2D18A81F290FBF00B2C785 /* Me */ = {
isa = PBXGroup;
children = (
0ED2B6F81F96A048001572F0 /* LinknewDevice */,
1A2D18D91F2918F300B2C785 /* Me */,
1A2D18AF1F29158700B2C785 /* Detail */,
1A2D18AB1F29149D00B2C785 /* MeCoordinator.swift */,
......@@ -1204,6 +1224,7 @@
1A2D18E61F29197100B2C785 /* MessageAccessoryView.xib in Resources */,
1A2D18F81F292D7200B2C785 /* MessageCellSent.xib in Resources */,
1A2D18F61F292D7200B2C785 /* MessageCellReceived.xib in Resources */,
0ED2B6FA1F96A075001572F0 /* LinkNewDeviceViewController.storyboard in Resources */,
1A2D18EF1F291A0100B2C785 /* MeDetailViewController.storyboard in Resources */,
1A2D18B11F2915B600B2C785 /* SmartlistViewController.storyboard in Resources */,
0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */,
......@@ -1347,6 +1368,7 @@
0EE1B54E1F75ACDE00BA98EE /* CNContactVCardSerialization+Helpers.swift in Sources */,
56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */,
1A3CA32D1F13DA7200283748 /* Chameleon+Ring.swift in Sources */,
0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */,
1ABE07E21F0D924700D36361 /* Strings.swift in Sources */,
621231FB1F8D6FEE009B86F0 /* MessageCell.swift in Sources */,
56AC650E1E85694D00EA1AA9 /* DesignableTextField.swift in Sources */,
......@@ -1362,6 +1384,7 @@
1A2D18AA1F29131900B2C785 /* ConversationsCoordinator.swift in Sources */,
043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */,
1A2041861F1EA19600C08435 /* CreateAccountViewController.swift in Sources */,
0EDCC8601F98150500B121D7 /* UIView+Rx.swift in Sources */,
1A2D18C21F29180700B2C785 /* AccountCredentialsModel.swift in Sources */,
1A2D18FF1F29352D00B2C785 /* MeViewModel.swift in Sources */,
62A88D391F6C323500F8AB18 /* PresenceAdapter.mm in Sources */,
......@@ -1375,6 +1398,7 @@
1A5DC0281F3564AA0075E8EF /* MessageModel.swift in Sources */,
56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */,
1A2041911F1FD46300C08435 /* DesignableView.swift in Sources */,
0ED2B6FE1F96A16C001572F0 /* LinkNewDeviceViewModel.swift in Sources */,
1A3D28A91F0EBF0200B524EE /* UIView+Ring.swift in Sources */,
1A2041881F1EA1EA00C08435 /* CreateAccountViewModel.swift in Sources */,
62E55B6D1F758E6F00D3FEF4 /* String+Helpers.swift in Sources */,
......
......@@ -79,6 +79,11 @@ enum StoryboardScene {
static let initialScene = InitialSceneType<Ring.LinkDeviceViewController>(storyboard: LinkDeviceViewController.self)
}
enum LinkNewDeviceViewController: StoryboardType {
static let storyboardName = "LinkNewDeviceViewController"
static let initialScene = InitialSceneType<Ring.LinkNewDeviceViewController>(storyboard: LinkNewDeviceViewController.self)
}
enum MeDetailViewController: StoryboardType {
static let storyboardName = "MeDetailViewController"
......
......@@ -82,6 +82,21 @@ enum L10n {
static let ok = L10n.tr("Localizable", "global.ok")
}
enum Linkdevice {
/// An error occured during the export
static let defaultError = L10n.tr("Localizable", "linkDevice.defaultError")
/// To complete the process, you need to open Ring on the new device and choose the option "Link this device to an account." Your pin is valid for 10 minutes
static let explanationMessage = L10n.tr("Localizable", "linkDevice.explanationMessage")
/// Verifying
static let hudMessage = L10n.tr("Localizable", "linkDevice.hudMessage")
/// A network error occured during the export
static let networkError = L10n.tr("Localizable", "linkDevice.networkError")
/// The password you entered does not unlock this account
static let passwordError = L10n.tr("Localizable", "linkDevice.passwordError")
/// Link new device
static let title = L10n.tr("Localizable", "linkDevice.title")
}
enum Smartlist {
/// Conversations
static let conversations = L10n.tr("Localizable", "smartlist.conversations")
......
......@@ -144,4 +144,11 @@ extension UIView {
return UIColor.clear
}
public func convertViewToImage() -> UIImage? {
UIGraphicsBeginImageContext(self.bounds.size)
self.drawHierarchy(in: self.bounds, afterScreenUpdates: false)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import RxSwift
import RxCocoa
extension Reactive where Base : UIView {
//show view with animation and hide without
public var isVisible: AnyObserver<Bool> {
return UIBindingObserver(UIElement: self.base) { view, hidden in
if hidden == true {
view.isHidden = true
view.alpha = 0
} else {
UIView.animate(withDuration: 0.3, delay: 0.5, options: .curveEaseOut,
animations: {view.alpha = 1},
completion: { _ in view.isHidden = false
})
}
}.asObserver()
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="2dj-eG-xeW">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Link New Device View Controller-->
<scene sceneID="D9S-FB-I4T">
<objects>
<viewController extendedLayoutIncludesOpaqueBars="YES" modalPresentationStyle="currentContext" id="2dj-eG-xeW" customClass="LinkNewDeviceViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="eZL-Om-fvc"/>
<viewControllerLayoutGuide type="bottom" id="3JQ-jM-bhq"/>
</layoutGuides>
<view key="view" opaque="NO" contentMode="scaleToFill" id="hfz-3u-S0l">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="bAU-MT-5Ok">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<edgeInsets key="layoutMargins" top="0.0" left="0.0" bottom="0.0" right="0.0"/>
</imageView>
<visualEffectView opaque="NO" alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tkx-Tz-Dro">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="hhH-X0-zM1">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<blurEffect style="dark"/>
</visualEffectView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DUK-26-PaO">
<rect key="frame" x="62.5" y="140" width="250" height="250"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="EjG-nq-d2Y" userLabel="Gradient View" customClass="DesignableView" customModule="Ring" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="250" height="250"/>
<color key="backgroundColor" red="0.2274509804" green="0.75294117650000003" blue="0.82352941180000006" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="displayP3"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="color" keyPath="gradientEndColor">
<color key="value" red="0.2274509804" green="0.75294117650000003" blue="0.82352941180000006" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="color" keyPath="gradientStartColor">
<color key="value" red="0.2274509804" green="0.75294117650000003" blue="0.82352941180000006" alpha="0.0" colorSpace="custom" customColorSpace="displayP3"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Enter Password" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="jXW-gy-tZg">
<rect key="frame" x="5" y="45.5" width="239.5" height="30"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="0.0"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="5" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="geP-pG-uQ9">
<rect key="frame" x="10" y="124.5" width="230" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hEG-05-68q">
<rect key="frame" x="10" y="210" width="60" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="6vg-8H-tbE"/>
<constraint firstAttribute="width" constant="60" id="fhV-en-uNc"/>
</constraints>
<state key="normal" title="Cancel"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
</userDefinedRuntimeAttributes>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Link Another Device" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ees-Ul-zym">
<rect key="frame" x="48" y="15" width="154" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="7" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oSv-Hz-eMn">
<rect key="frame" x="10" y="70" width="230" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Pin" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BDQ-0Z-HqM">
<rect key="frame" x="112.5" y="30" width="24" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="85j-fk-tVM"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tLd-d1-Onw">
<rect key="frame" x="180" y="210" width="60" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="3tg-dw-XKp"/>
<constraint firstAttribute="width" constant="60" id="qra-Ny-3lQ"/>
</constraints>
<state key="normal" title="Ok"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
</userDefinedRuntimeAttributes>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="hEG-05-68q" secondAttribute="bottom" constant="10" id="26g-iR-A7F"/>
<constraint firstItem="jXW-gy-tZg" firstAttribute="top" secondItem="Ees-Ul-zym" secondAttribute="bottom" constant="10" id="2mf-Or-SVI"/>
<constraint firstItem="BDQ-0Z-HqM" firstAttribute="top" secondItem="DUK-26-PaO" secondAttribute="top" constant="30" id="5ia-DN-cAf"/>
<constraint firstItem="jXW-gy-tZg" firstAttribute="centerX" secondItem="DUK-26-PaO" secondAttribute="centerX" id="BPA-ZA-NCX"/>
<constraint firstAttribute="trailing" secondItem="geP-pG-uQ9" secondAttribute="trailing" constant="10" id="E3A-Q3-yZL"/>
<constraint firstAttribute="height" constant="250" id="Eyi-7Y-VZh"/>
<constraint firstItem="Ees-Ul-zym" firstAttribute="centerX" secondItem="DUK-26-PaO" secondAttribute="centerX" id="GcQ-ZN-Kf7"/>
<constraint firstItem="BDQ-0Z-HqM" firstAttribute="centerX" secondItem="DUK-26-PaO" secondAttribute="centerX" id="KRt-k3-GCR"/>
<constraint firstItem="geP-pG-uQ9" firstAttribute="centerX" secondItem="DUK-26-PaO" secondAttribute="centerX" id="LCp-tV-nme"/>
<constraint firstItem="geP-pG-uQ9" firstAttribute="centerY" secondItem="DUK-26-PaO" secondAttribute="centerY" id="Msz-Fp-afW"/>
<constraint firstItem="oSv-Hz-eMn" firstAttribute="leading" secondItem="DUK-26-PaO" secondAttribute="leading" constant="10" id="RNO-ft-fcR"/>
<constraint firstItem="oSv-Hz-eMn" firstAttribute="centerX" secondItem="DUK-26-PaO" secondAttribute="centerX" id="WMe-wm-YT4"/>
<constraint firstItem="Ees-Ul-zym" firstAttribute="top" secondItem="DUK-26-PaO" secondAttribute="top" constant="15" id="XQl-Ux-PCv"/>
<constraint firstItem="EjG-nq-d2Y" firstAttribute="leading" secondItem="DUK-26-PaO" secondAttribute="leading" id="a2W-5K-EdM"/>
<constraint firstItem="geP-pG-uQ9" firstAttribute="top" relation="greaterThanOrEqual" secondItem="DUK-26-PaO" secondAttribute="top" constant="20" id="aGj-RT-SSc"/>
<constraint firstAttribute="trailing" secondItem="EjG-nq-d2Y" secondAttribute="trailing" id="dAZ-J3-qAs"/>
<constraint firstItem="EjG-nq-d2Y" firstAttribute="top" secondItem="DUK-26-PaO" secondAttribute="top" id="gEg-rk-8ue"/>
<constraint firstAttribute="bottom" secondItem="tLd-d1-Onw" secondAttribute="bottom" constant="10" id="hL2-NM-eUa"/>
<constraint firstAttribute="trailing" secondItem="tLd-d1-Onw" secondAttribute="trailing" constant="10" id="hVG-0q-T18"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="geP-pG-uQ9" secondAttribute="bottom" constant="20" id="hnT-KZ-nNk"/>
<constraint firstItem="oSv-Hz-eMn" firstAttribute="top" secondItem="BDQ-0Z-HqM" secondAttribute="bottom" constant="10" id="iaf-dp-0vU"/>
<constraint firstItem="jXW-gy-tZg" firstAttribute="leading" secondItem="DUK-26-PaO" secondAttribute="leading" constant="5" id="kZq-mB-2ed"/>
<constraint firstAttribute="bottom" secondItem="EjG-nq-d2Y" secondAttribute="bottom" id="oLU-wf-KNH"/>
<constraint firstItem="geP-pG-uQ9" firstAttribute="leading" secondItem="DUK-26-PaO" secondAttribute="leading" constant="10" id="sHR-7a-bbd"/>
<constraint firstAttribute="width" constant="250" id="u8k-6x-tBz"/>
<constraint firstAttribute="trailing" secondItem="oSv-Hz-eMn" secondAttribute="trailing" constant="10" id="uWL-yZ-BJL"/>
<constraint firstItem="hEG-05-68q" firstAttribute="leading" secondItem="DUK-26-PaO" secondAttribute="leading" constant="10" id="ulo-GW-WFO"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="10"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</view>
</subviews>
<constraints>
<constraint firstItem="DUK-26-PaO" firstAttribute="top" secondItem="eZL-Om-fvc" secondAttribute="bottom" priority="750" constant="120" id="63X-b8-559"/>
<constraint firstItem="bAU-MT-5Ok" firstAttribute="top" secondItem="hfz-3u-S0l" secondAttribute="top" id="6F2-uC-0HH"/>
<constraint firstItem="3JQ-jM-bhq" firstAttribute="top" relation="greaterThanOrEqual" secondItem="DUK-26-PaO" secondAttribute="bottom" constant="30" id="aev-7M-xMF"/>
<constraint firstItem="Tkx-Tz-Dro" firstAttribute="leading" secondItem="hfz-3u-S0l" secondAttribute="leading" id="e0Q-BA-Grx"/>
<constraint firstItem="DUK-26-PaO" firstAttribute="centerX" secondItem="hfz-3u-S0l" secondAttribute="centerX" id="fBl-cM-hBw"/>
<constraint firstItem="bAU-MT-5Ok" firstAttribute="leading" secondItem="hfz-3u-S0l" secondAttribute="leading" id="h3g-hC-dSs"/>
<constraint firstAttribute="trailing" secondItem="bAU-MT-5Ok" secondAttribute="trailing" id="kqj-mu-ZoR"/>
<constraint firstItem="Tkx-Tz-Dro" firstAttribute="top" secondItem="hfz-3u-S0l" secondAttribute="top" id="nZp-x2-ycz"/>
<constraint firstItem="3JQ-jM-bhq" firstAttribute="top" secondItem="Tkx-Tz-Dro" secondAttribute="bottom" id="oAL-K8-Riy"/>
<constraint firstItem="3JQ-jM-bhq" firstAttribute="top" secondItem="bAU-MT-5Ok" secondAttribute="bottom" id="pVT-Xj-NJ0"/>
<constraint firstAttribute="trailing" secondItem="Tkx-Tz-Dro" secondAttribute="trailing" id="v9B-h1-RtS"/>
</constraints>
</view>
<connections>
<outlet property="background" destination="bAU-MT-5Ok" id="neq-1u-Pct"/>
<outlet property="cancelButton" destination="hEG-05-68q" id="bfe-iV-QmP"/>
<outlet property="containerView" destination="DUK-26-PaO" id="Oyr-48-9CT"/>
<outlet property="errorMessage" destination="geP-pG-uQ9" id="fKA-HW-yAi"/>
<outlet property="explanationMessage" destination="oSv-Hz-eMn" id="1Cn-eN-jvD"/>
<outlet property="okButton" destination="tLd-d1-Onw" id="tBs-Mr-ZPa"/>
<outlet property="passwordField" destination="jXW-gy-tZg" id="nMH-ce-BCQ"/>
<outlet property="pinLabel" destination="BDQ-0Z-HqM" id="Lto-kY-5AQ"/>
<outlet property="titleLable" destination="Ees-Ul-zym" id="7uo-rl-0rV"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mtT-LS-iO8" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="132" y="145.27736131934034"/>
</scene>
</scenes>
</document>
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import Foundation
import Reusable
import RxSwift
import PKHUD
class LinkNewDeviceViewController: UIViewController, StoryboardBased, ViewModelBased {
@IBOutlet weak var titleLable: UILabel!
@IBOutlet weak var passwordField: UITextField!
@IBOutlet weak var okButton: UIButton!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var pinLabel: UILabel!
@IBOutlet weak var explanationMessage: UILabel!
@IBOutlet weak var errorMessage: UILabel!
@IBOutlet weak var background: UIImageView!
@IBOutlet weak var containerView: UIView!
var viewModel: LinkNewDeviceViewModel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
self.background.image = self.view.convertViewToImage()
self.applyL10n()
// initial state
self.viewModel.isInitialState
.bind(to: self.titleLable.rx.isHidden)
.addDisposableTo(self.disposeBag)
self.viewModel.isInitialState.bind(to: self.passwordField.rx.isHidden)
.addDisposableTo(self.disposeBag)
self.viewModel.isInitialState.bind(to: self.cancelButton.rx.isHidden)
.addDisposableTo(self.disposeBag)
// error state
self.viewModel.isErrorState.bind(to: self.errorMessage.rx.isVisible)
.addDisposableTo(self.disposeBag)
// success state
self.viewModel.isSuccessState
.bind(to: self.explanationMessage.rx.isVisible)
.addDisposableTo(self.disposeBag)
self.viewModel.isSuccessState
.bind(to: self.pinLabel.rx.isVisible)
.addDisposableTo(self.disposeBag)
passwordField.rx.text
.map({!$0!.isEmpty})
.shareReplay(1)
.bind(to: okButton.rx.isEnabled)
.addDisposableTo(self.disposeBag)
self.viewModel.observableState
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] (state) in
switch state {
case .generatingPin:
self?.showProgress()
case .success(let pin):
self?.pinLabel.text = pin
self?.hideHud()
case .error(let pinError):
self?.errorMessage.text = pinError.description
self?.hideHud()
default:
break
}
}).addDisposableTo(self.disposeBag)
cancelButton.rx.tap.subscribe(onNext: { [unowned self] in
self.dismiss(animated: true, completion: nil)
}).disposed(by: disposeBag)
okButton.rx.tap.subscribe(onNext: { [unowned self] in
if !self.passwordField.isHidden {
self.viewModel.linkDevice(with: self.passwordField.text)
self.passwordField.text = ""
} else if !self.errorMessage.isHidden {
self.viewModel.refresh()
} else {
self.dismiss(animated: true, completion: nil)
}
}).disposed(by: disposeBag)
super.viewDidLoad()
}
private func showProgress() {
HUD.show(.labeledProgress(title: L10n.Linkdevice.hudMessage, subtitle: nil), onView: self.containerView)
}
private func hideHud() {
HUD.hide(animated: true)
}
private func applyL10n() {
self.titleLable.text = self.viewModel.linkDeviceTitleTitle
self.explanationMessage.text = self.viewModel.explanationMessage
}
}
/*
* Copyright (C) 2017 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import Foundation
import RxSwift
import RxDataSources
enum ExportAccountResponse: Int {
case success = 0
case wrongPassword = 1
case networkProblem = 2
}
enum PinError {
case passwordError
case networkError
case defaultError
var description: String {
switch self {
case .passwordError:
return L10n.Linkdevice.passwordError
case .networkError:
return L10n.Linkdevice.networkError
case .defaultError:
return L10n.Linkdevice.defaultError
}
}
}
enum GeneratingPinState {
case initial
case generatingPin
case success(pin: String)
case error(error: PinError)
var rawValue: String {
switch self {
case .initial:
return "INITIAL"
case .generatingPin:
return "GENERATING_PIN"
case .success:
return "SUCCESS"
case .error:
return "ERROR"
}
}
func isStateOfType(type: String) -> Bool {
return self.rawValue == type
}
}
class LinkNewDeviceViewModel: ViewModel, Stateable {
// MARK: - Rx Stateable
private let stateSubject = PublishSubject <State>()
lazy var state: Observable<State> = {
return self.stateSubject.asObservable()
}()
private let generatingState = Variable(GeneratingPinState.initial)
lazy var observableState: Observable <GeneratingPinState> = {
return self.generatingState.asObservable()
}()
lazy var isInitialState: Observable<Bool> = {
return self.observableState.map { state in