Skip to content
Snippets Groups Projects
Commit d28b7488 authored by Thibault Wittemberg's avatar Thibault Wittemberg
Browse files

project: conform to a swifty way of coding

This commit:
- adapts code to a more Swifty way of coding
- uses previously added frameworks to make the code "type safer"

Change-Id: I5332e2843e82cac4f4f9af79714993863ef5963d
parent 69981855
No related branches found
No related tags found
No related merge requests found
Showing
with 276 additions and 206 deletions
disabled_rules: # rule identifiers to exclude from running disabled_rules: # rule identifiers to exclude from running
- todo
opt_in_rules: # some rules are only opt-in opt_in_rules: # some rules are only opt-in
- empty_count - empty_count
......
This diff is collapsed.
...@@ -19,8 +19,9 @@ ...@@ -19,8 +19,9 @@
*/ */
import UIKit import UIKit
import Reusable
class AccountTableViewCell: UITableViewCell { class AccountTableViewCell: UITableViewCell, NibReusable {
// MARK: - Properties // MARK: - Properties
@IBOutlet weak var activeSwitch: UISwitch! @IBOutlet weak var activeSwitch: UISwitch!
...@@ -29,15 +30,6 @@ class AccountTableViewCell: UITableViewCell { ...@@ -29,15 +30,6 @@ class AccountTableViewCell: UITableViewCell {
var account: AccountModel! var account: AccountModel!
// MARK: - UITableViewCell
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
// MARK: - Actions // MARK: - Actions
@IBAction func switchAccountState(_ sender: UISwitch) { @IBAction func switchAccountState(_ sender: UISwitch) {
// account.isEnabled = sender.isOn // account.isEnabled = sender.isOn
......
...@@ -140,17 +140,11 @@ class CreateRingAccountViewModel { ...@@ -140,17 +140,11 @@ class CreateRingAccountViewModel {
.asObservable().map ({ status in .asObservable().map ({ status in
switch status { switch status {
case .lookingUp: case .lookingUp:
return NSLocalizedString("LookingForUsernameAvailability", return L10n.Createaccount.lookingForUsernameAvailability.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
case .invalid: case .invalid:
return NSLocalizedString("InvalidUsername", return L10n.Createaccount.invalidUsername.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
case .alreadyTaken: case .alreadyTaken:
return NSLocalizedString("UsernameAlreadyTaken", return L10n.Createaccount.usernameAlreadyTaken.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
default: default:
return "" return ""
} }
...@@ -181,7 +175,7 @@ class CreateRingAccountViewModel { ...@@ -181,7 +175,7 @@ class CreateRingAccountViewModel {
//Loookup name request observer //Loookup name request observer
self.username.asObservable().subscribe(onNext: { [unowned self] username in self.username.asObservable().subscribe(onNext: { [unowned self] username in
self.nameService.lookupName(withAccount: "", nameserver: "", name: username) self.nameService.lookupName(withAccount: "", nameserver: "", name: username)
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
//Name registration observer //Name registration observer
self.accountService self.accountService
...@@ -200,7 +194,7 @@ class CreateRingAccountViewModel { ...@@ -200,7 +194,7 @@ class CreateRingAccountViewModel {
name: self.username.value) name: self.username.value)
} }
}) })
.addDisposableTo(disposeBag) .disposed(by: disposeBag)
//Account creation state observer //Account creation state observer
self.accountService self.accountService
...@@ -215,7 +209,7 @@ class CreateRingAccountViewModel { ...@@ -215,7 +209,7 @@ class CreateRingAccountViewModel {
} }
}, onError: { _ in }, onError: { _ in
self.accountCreationState.onError(AccountCreationError.unknown) self.accountCreationState.onError(AccountCreationError.unknown)
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
} }
} }
...@@ -238,34 +232,22 @@ extension AccountCreationError: LocalizedError { ...@@ -238,34 +232,22 @@ extension AccountCreationError: LocalizedError {
var title: String { var title: String {
switch self { switch self {
case .generic: case .generic:
return NSLocalizedString("AccountCannotBeFoundTitle", return L10n.Alerts.accountCannotBeFoundTitle.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
case .network: case .network:
return NSLocalizedString("AccountNoNetworkTitle", return L10n.Alerts.accountNoNetworkTitle.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
default: default:
return NSLocalizedString("AccountDefaultErrorTitle", return L10n.Alerts.accountDefaultErrorTitle.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
} }
} }
var message: String { var message: String {
switch self { switch self {
case .generic: case .generic:
return NSLocalizedString("AcountCannotBeFoundMessage", return L10n.Alerts.accountDefaultErrorMessage.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
case .network: case .network:
return NSLocalizedString("AccountNoNetworkMessage", return L10n.Alerts.accountNoNetworkMessage.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
default: default:
return NSLocalizedString("AccountDefaultErrorMessage", return L10n.Alerts.accountDefaultErrorMessage.smartString
tableName: LocalizedStringTableNames.walkthrough,
comment: "")
} }
} }
} }
...@@ -20,9 +20,16 @@ ...@@ -20,9 +20,16 @@
import Foundation import Foundation
/** public enum Durations {
Time interval between TextField events in seconds case textFieldThrottlingDuration
*/ case alertFlashDuration
let textFieldThrottlingDuration = 0.5
let alertFlashDuration = 1.0 var value: Double {
switch self {
case .textFieldThrottlingDuration:
return 0.5
case .alertFlashDuration:
return 1.0
}
}
}
...@@ -13,64 +13,83 @@ private class RingStringsBundleToken {} ...@@ -13,64 +13,83 @@ private class RingStringsBundleToken {}
// swiftlint:disable valid_docs // swiftlint:disable valid_docs
enum L10n { enum L10n {
enum Alerts {
/// Account Added /// Account Added
static let accountAddedTitle = L10n.tr("AccountAddedTitle") static let accountAddedTitle = L10n.tr("alerts.accountAddedTitle")
/// Can't find account /// Can't find account
static let accountCannotBeFoundTitle = L10n.tr("AccountCannotBeFoundTitle") static let accountCannotBeFoundTitle = L10n.tr("alerts.accountCannotBeFoundTitle")
/// The account couldn't be created. /// The account couldn't be created.
static let accountDefaultErrorMessage = L10n.tr("AccountDefaultErrorMessage") static let accountDefaultErrorMessage = L10n.tr("alerts.accountDefaultErrorMessage")
/// Unknown error /// Unknown error
static let accountDefaultErrorTitle = L10n.tr("AccountDefaultErrorTitle") static let accountDefaultErrorTitle = L10n.tr("alerts.accountDefaultErrorTitle")
/// Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity. /// Could not add account because Ring couldn't connect to the distributed network. Check your device connectivity.
static let accountNoNetworkMessage = L10n.tr("AccountNoNetworkMessage") static let accountNoNetworkMessage = L10n.tr("alerts.accountNoNetworkMessage")
/// Can't connect to the network /// Can't connect to the network
static let accountNoNetworkTitle = L10n.tr("AccountNoNetworkTitle") static let accountNoNetworkTitle = L10n.tr("alerts.accountNoNetworkTitle")
/// Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct. /// Account couldn't be found on the Ring network. Make sure it was exported on Ring from an existing device, and that provided credentials are correct.
static let acountCannotBeFoundMessage = L10n.tr("AcountCannotBeFoundMessage") static let acountCannotBeFoundMessage = L10n.tr("alerts.acountCannotBeFoundMessage")
}
enum Createaccount {
/// Choose strong password you will remember to protect your Ring account. /// Choose strong password you will remember to protect your Ring account.
static let chooseStrongPassword = L10n.tr("ChooseStrongPassword") static let chooseStrongPassword = L10n.tr("createAccount.chooseStrongPassword")
/// Conversations
static let conversations = L10n.tr("Conversations")
/// Create a Ring account
static let createAccount = L10n.tr("CreateAccount")
/// Create your Ring account /// Create your Ring account
static let createAccountFormTitle = L10n.tr("CreateAccountFormTitle") static let createAccountFormTitle = L10n.tr("createAccount.createAccountFormTitle")
/// Enter new username /// Enter new username
static let enterNewUsernamePlaceholder = L10n.tr("EnterNewUsernamePlaceholder") static let enterNewUsernamePlaceholder = L10n.tr("createAccount.enterNewUsernamePlaceholder")
/// Home
static let homeTabBarTitle = L10n.tr("HomeTabBarTitle")
/// Invalid username /// Invalid username
static let invalidUsername = L10n.tr("InvalidUsername") static let invalidUsername = L10n.tr("createAccount.invalidUsername")
/// Link this device to an account /// Loading...
static let linkDeviceButton = L10n.tr("LinkDeviceButton") static let loading = L10n.tr("createAccount.loading")
/// Looking for username availability... /// Looking for username availability...
static let lookingForUsernameAvailability = L10n.tr("LookingForUsernameAvailability") static let lookingForUsernameAvailability = L10n.tr("createAccount.lookingForUsernameAvailability")
/// New Password /// New Password
static let newPasswordPlaceholder = L10n.tr("NewPasswordPlaceholder") static let newPasswordPlaceholder = L10n.tr("createAccount.newPasswordPlaceholder")
/// No results
static let noResults = L10n.tr("NoResults")
/// 6 characters minimum /// 6 characters minimum
static let passwordCharactersNumberError = L10n.tr("PasswordCharactersNumberError") static let passwordCharactersNumberError = L10n.tr("createAccount.passwordCharactersNumberError")
/// Passwords do not match /// Passwords do not match
static let passwordNotMatchingError = L10n.tr("PasswordNotMatchingError") static let passwordNotMatchingError = L10n.tr("createAccount.passwordNotMatchingError")
/// Register public username (experimental) /// Register public username (experimental)
static let registerPublicUsername = L10n.tr("RegisterPublicUsername") static let registerPublicUsername = L10n.tr("createAccount.registerPublicUsername")
/// Repeat new password /// Repeat new password
static let repeatPasswordPlaceholder = L10n.tr("RepeatPasswordPlaceholder") static let repeatPasswordPlaceholder = L10n.tr("createAccount.repeatPasswordPlaceholder")
/// Searching...
static let searching = L10n.tr("Searching")
/// User found
static let userFound = L10n.tr("UserFound")
/// Username already taken /// Username already taken
static let usernameAlreadyTaken = L10n.tr("UsernameAlreadyTaken") static let usernameAlreadyTaken = L10n.tr("createAccount.usernameAlreadyTaken")
/// Adding account /// Adding account
static let waitCreateAccountTitle = L10n.tr("WaitCreateAccountTitle") static let waitCreateAccountTitle = L10n.tr("createAccount.waitCreateAccountTitle")
}
enum Global {
/// Home
static let homeTabBarTitle = L10n.tr("global.homeTabBarTitle")
/// Ok
static let ok = L10n.tr("global.ok")
}
enum Smartlist {
/// Conversations
static let conversations = L10n.tr("smartlist.conversations")
/// No results
static let noResults = L10n.tr("smartlist.noResults")
/// Searching...
static let searching = L10n.tr("smartlist.searching")
/// User found
static let userFound = L10n.tr("smartlist.userFound")
/// Yesterday
static let yesterday = L10n.tr("smartlist.yesterday")
}
enum Welcome {
/// Create a Ring account
static let createAccount = L10n.tr("welcome.createAccount")
/// Link this device to an account
static let linkDeviceButton = L10n.tr("welcome.linkDeviceButton")
/// A Ring account allows you to reach people securely in peer to peer through fully distributed network /// A Ring account allows you to reach people securely in peer to peer through fully distributed network
static let welcomeText = L10n.tr("WelcomeText") static let text = L10n.tr("welcome.text")
/// Welcome to Ring /// Welcome to Ring
static let welcomeTitle = L10n.tr("WelcomeTitle") static let title = L10n.tr("welcome.title")
/// Yesterday }
static let yesterday = L10n.tr("Yesterday")
} }
struct LocalizableString { struct LocalizableString {
......
...@@ -39,7 +39,7 @@ class ContactHelper { ...@@ -39,7 +39,7 @@ class ContactHelper {
} else { } else {
userName.value = lookupNameResponse.address userName.value = lookupNameResponse.address
} }
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
nameService.lookupAddress(withAccount: "", nameserver: "", address: ringId) nameService.lookupAddress(withAccount: "", nameserver: "", address: ringId)
......
...@@ -73,7 +73,7 @@ class ContactViewModel { ...@@ -73,7 +73,7 @@ class ContactViewModel {
} else { } else {
self.userName.value = lookupNameResponse.address self.userName.value = lookupNameResponse.address
} }
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
nameService.lookupAddress(withAccount: "", nameserver: "", address: self.contact.ringId) nameService.lookupAddress(withAccount: "", nameserver: "", address: self.contact.ringId)
} }
......
...@@ -20,8 +20,9 @@ ...@@ -20,8 +20,9 @@
import UIKit import UIKit
import RxSwift import RxSwift
import Reusable
class ConversationCell: UITableViewCell { class ConversationCell: UITableViewCell, NibReusable {
@IBOutlet weak var profileImage: UIImageView! @IBOutlet weak var profileImage: UIImageView!
@IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var nameLabel: UILabel!
...@@ -30,11 +31,6 @@ class ConversationCell: UITableViewCell { ...@@ -30,11 +31,6 @@ class ConversationCell: UITableViewCell {
@IBOutlet weak var lastMessageDateLabel: UILabel! @IBOutlet weak var lastMessageDateLabel: UILabel!
@IBOutlet weak var lastMessagePreviewLabel: UILabel! @IBOutlet weak var lastMessagePreviewLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated) super.setSelected(selected, animated: animated)
self.newMessagesIndicator.backgroundColor = UIColor.red self.newMessagesIndicator.backgroundColor = UIColor.red
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait"> <device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ConversationCellId" rowHeight="76" id="KGk-i7-Jjw" customClass="ConversationCell" customModule="Ring" customModuleProvider="target"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="76" id="KGk-i7-Jjw" customClass="ConversationCell" customModule="Ring" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="358" height="76"/> <rect key="frame" x="0.0" y="0.0" width="358" height="76"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="358" height="76"/> <rect key="frame" x="0.0" y="0.0" width="358" height="75.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="pFB-Jn-TNP"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="pFB-Jn-TNP">
......
...@@ -71,7 +71,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { ...@@ -71,7 +71,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate {
} }
func setupUI() { func setupUI() {
self.viewModel?.userName.asObservable().bind(to: self.navigationItem.rx.title).addDisposableTo(disposeBag) self.viewModel?.userName.asObservable().bind(to: self.navigationItem.rx.title).disposed(by: disposeBag)
self.tableView.contentInset.bottom = messageAccessoryView.frame.size.height self.tableView.contentInset.bottom = messageAccessoryView.frame.size.height
self.tableView.scrollIndicatorInsets.bottom = messageAccessoryView.frame.size.height self.tableView.scrollIndicatorInsets.bottom = messageAccessoryView.frame.size.height
...@@ -97,22 +97,21 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { ...@@ -97,22 +97,21 @@ class ConversationViewController: UIViewController, UITextFieldDelegate {
self.tableView.separatorStyle = .none self.tableView.separatorStyle = .none
//Register cell //Register cell
self.tableView.register(UINib.init(nibName: "MessageCell", bundle: nil), self.tableView.register(cellType: MessageCell.self)
forCellReuseIdentifier: "MessageCellId")
//Bind the TableView to the ViewModel //Bind the TableView to the ViewModel
self.viewModel?.messages self.viewModel?.messages
.bind(to: tableView.rx.items(cellIdentifier: "MessageCellId", .bind(to: tableView.rx.items(cellIdentifier: "MessageCell",
cellType: MessageCell.self)) { _, messageViewModel, cell in cellType: MessageCell.self)) { _, messageViewModel, cell in
cell.messageLabel.text = messageViewModel.content cell.messageLabel.text = messageViewModel.content
cell.bubblePosition = messageViewModel.bubblePosition() cell.bubblePosition = messageViewModel.bubblePosition()
}.addDisposableTo(disposeBag) }.disposed(by: disposeBag)
//Scroll to bottom when reloaded //Scroll to bottom when reloaded
self.tableView.rx.methodInvoked(#selector(UITableView.reloadData)).subscribe(onNext: { _ in self.tableView.rx.methodInvoked(#selector(UITableView.reloadData)).subscribe(onNext: { _ in
self.scrollToBottomIfNeed() self.scrollToBottomIfNeed()
self.updateBottomOffset() self.updateBottomOffset()
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
} }
fileprivate func updateBottomOffset() { fileprivate func updateBottomOffset() {
...@@ -153,7 +152,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { ...@@ -153,7 +152,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate {
} }
lazy var messageAccessoryView: MessageAccessoryView = { lazy var messageAccessoryView: MessageAccessoryView = {
return MessageAccessoryView.instanceFromNib() return MessageAccessoryView.loadFromNib()
}() }()
func setupBindings() { func setupBindings() {
...@@ -162,7 +161,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate { ...@@ -162,7 +161,7 @@ class ConversationViewController: UIViewController, UITextFieldDelegate {
self.messageAccessoryView.messageTextField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { _ in self.messageAccessoryView.messageTextField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { _ in
self.viewModel?.sendMessage(withContent: self.messageAccessoryView.messageTextField.text!) self.viewModel?.sendMessage(withContent: self.messageAccessoryView.messageTextField.text!)
self.messageAccessoryView.messageTextField.text = "" self.messageAccessoryView.messageTextField.text = ""
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
} }
// Avoid the keyboard to be hidden when the Send button is touched // Avoid the keyboard to be hidden when the Send button is touched
......
...@@ -89,7 +89,7 @@ class ConversationViewModel { ...@@ -89,7 +89,7 @@ class ConversationViewModel {
self.log.error("Realm persistence with error: \(error)") self.log.error("Realm persistence with error: \(error)")
} }
}).addDisposableTo(self.disposeBag) }).disposed(by: self.disposeBag)
return tmp return tmp
} }
...@@ -134,7 +134,7 @@ class ConversationViewModel { ...@@ -134,7 +134,7 @@ class ConversationViewModel {
if todayDay == day && todayMonth == month && todayYear == year { if todayDay == day && todayMonth == month && todayYear == year {
return hourFormatter.string(from: lastMessageDate) return hourFormatter.string(from: lastMessageDate)
} else if day == todayDay - 1 { } else if day == todayDay - 1 {
return NSLocalizedString("Yesterday", tableName: "Smartlist", comment: "") return L10n.Smartlist.yesterday.smartString
} else if todayYear == year && todayWeekOfYear == weekOfYear { } else if todayYear == year && todayWeekOfYear == weekOfYear {
return lastMessageDate.dayOfWeek() return lastMessageDate.dayOfWeek()
} else { } else {
...@@ -158,7 +158,7 @@ class ConversationViewModel { ...@@ -158,7 +158,7 @@ class ConversationViewModel {
.subscribe(onCompleted: { .subscribe(onCompleted: {
let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!) let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!)
self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: (self.conversation.recipient?.ringId)!) self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: (self.conversation.recipient?.ringId)!)
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
} }
fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) { fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) {
...@@ -167,7 +167,7 @@ class ConversationViewModel { ...@@ -167,7 +167,7 @@ class ConversationViewModel {
.subscribe(onCompleted: { [unowned self] in .subscribe(onCompleted: { [unowned self] in
self.log.debug("Message saved") self.log.debug("Message saved")
}) })
.addDisposableTo(disposeBag) .disposed(by: disposeBag)
} }
func setMessagesAsRead() { func setMessagesAsRead() {
...@@ -175,7 +175,7 @@ class ConversationViewModel { ...@@ -175,7 +175,7 @@ class ConversationViewModel {
.setMessagesAsRead(forConversation: self.conversation) .setMessagesAsRead(forConversation: self.conversation)
.subscribe(onCompleted: { [unowned self] in .subscribe(onCompleted: { [unowned self] in
self.log.debug("Message set as read") self.log.debug("Message set as read")
}).addDisposableTo(disposeBag) }).disposed(by: disposeBag)
} }
fileprivate var unreadMessagesCount: Int { fileprivate var unreadMessagesCount: Int {
......
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Thibault Wittemberg <thibault.wittemberg@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
extension Bool {
func toString() -> String {
return self ? "true" : "false"
}
}
...@@ -22,22 +22,13 @@ import Foundation ...@@ -22,22 +22,13 @@ import Foundation
extension String { extension String {
func toBool() -> Bool? { func toBool() -> Bool? {
switch self { switch self.lowercased() {
case "True", "true", "yes", "1": case "true", "yes", "1":
return true return true
case "False", "false", "no", "0": case "false", "no", "0":
return false return false
default: default:
return nil return nil
} }
} }
} }
extension Bool {
func toString() -> String {
if self == true {
return "true"
}
return "false"
}
}
/* /*
* Copyright (C) 2017 Savoir-faire Linux Inc. * Copyright (C) 2016 Savoir-faire Linux Inc.
* *
* Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com> * Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,20 +18,14 @@ ...@@ -18,20 +18,14 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
import Foundation
import UIKit import UIKit
class RoundedTextField: UITextField { extension UIColor {
required init?(coder aDecoder: NSCoder) { static let ringMain = UIColor(colorLiteralRed: 10.0/255.0,
super.init(coder: aDecoder) green: 116.0/255.0,
self.layer.borderColor = UIColor.white.cgColor blue: 137.0/255.0,
self.layer.borderWidth = 1.0 alpha: 1.0)
self.clipsToBounds = true
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.frame.size.height / 2.0
}
} }
/* /*
* Copyright (C) 2016 Savoir-faire Linux Inc. * Copyright (C) 2016 Savoir-faire Linux Inc.
* *
* Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com> * Author: Thibault Wittemberg <thibault.wittemberg@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -18,21 +18,58 @@ ...@@ -18,21 +18,58 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
import Foundation
import UIKit import UIKit
class RoundedButton: UIButton { extension UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//Button layout @IBInspectable
self.layer.borderColor = self.backgroundColor?.cgColor var cornerRadius: CGFloat {
self.layer.borderWidth = 1.0 get {
return self.layer.cornerRadius
}
set {
self.clipsToBounds = true self.clipsToBounds = true
self.layer.cornerRadius = 15.0 self.layer.cornerRadius = newValue
self.contentEdgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) }
}
@IBInspectable
var roundedCorners: Bool {
get {
return self.cornerRadius == self.frame.height / 2
}
set {
if newValue {
self.cornerRadius = self.frame.height / 2
} else {
self.cornerRadius = 0
}
}
}
//Text colors @IBInspectable
self.setTitleColor(UIColor.white, for: .normal) var borderWidth: CGFloat {
self.setTitleColor(UIColor.gray, for: .disabled) get {
return self.layer.borderWidth
} }
set {
self.layer.borderWidth = newValue
}
}
@IBInspectable
var borderColor: UIColor {
get {
return UIColor(cgColor: self.layer.borderColor ?? UIColor.clear.cgColor)
}
set {
self.layer.borderColor = newValue.cgColor
}
}
} }
...@@ -26,7 +26,7 @@ class MainTabBarViewController: UITabBarController { ...@@ -26,7 +26,7 @@ class MainTabBarViewController: UITabBarController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
UITabBarItem.appearance() UITabBarItem.appearance()
.setTitleTextAttributes( [NSForegroundColorAttributeName: Colors.ringMainColor], for: .selected) .setTitleTextAttributes( [NSForegroundColorAttributeName: UIColor.ringMain], for: .selected)
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
......
...@@ -19,16 +19,10 @@ ...@@ -19,16 +19,10 @@
*/ */
import UIKit import UIKit
import Reusable
class MessageAccessoryView: UIView { class MessageAccessoryView: UIView, NibLoadable {
@IBOutlet weak var messageTextField: UITextField! @IBOutlet weak var messageTextField: UITextField!
class func instanceFromNib() -> MessageAccessoryView {
guard let view = UINib(nibName: "MessageAccessoryView", bundle: nil).instantiate(withOwner: nil, options: nil).first as? MessageAccessoryView else {
fatalError("The view you are trying to instantiate is not a MessageAccessoryView")
}
return view
}
} }
...@@ -19,13 +19,14 @@ ...@@ -19,13 +19,14 @@
*/ */
import UIKit import UIKit
import Reusable
enum BubblePosition { enum BubblePosition {
case received case received
case sent case sent
} }
class MessageCell: UITableViewCell { class MessageCell: UITableViewCell, NibReusable {
@IBOutlet weak var bubble: UIView! @IBOutlet weak var bubble: UIView!
@IBOutlet weak var messageLabel: UILabel! @IBOutlet weak var messageLabel: UILabel!
...@@ -44,7 +45,7 @@ class MessageCell: UITableViewCell { ...@@ -44,7 +45,7 @@ class MessageCell: UITableViewCell {
self.containerLeadingConstraint.priority = 1 self.containerLeadingConstraint.priority = 1
self.minimumLeadingConstraint.priority = 999 self.minimumLeadingConstraint.priority = 999
self.bubble.backgroundColor = Colors.ringMainColor self.bubble.backgroundColor = UIColor.ringMain
self.messageLabel.textColor = UIColor.white self.messageLabel.textColor = UIColor.white
} else { } else {
self.minimumLeadingConstraint.priority = 1 self.minimumLeadingConstraint.priority = 1
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16E195" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait"> <device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/> <adaptation id="fullscreen"/>
</device> </device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="MessageCellId" rowHeight="60" id="KGk-i7-Jjw" customClass="MessageCell" customModule="Ring" customModuleProvider="target"> <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="60" id="KGk-i7-Jjw" customClass="MessageCell" customModule="Ring" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="510" height="47"/> <rect key="frame" x="0.0" y="0.0" width="510" height="47"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="510" height="46"/> <rect key="frame" x="0.0" y="0.0" width="510" height="46.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kZJ-Ay-LTR"> <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kZJ-Ay-LTR">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment