Commit 32ec4d2d authored by Kateryna Kostiuk's avatar Kateryna Kostiuk

account: support multiple accounts

Change-Id: I45091f153873900b8285bcc0ae8f941171f4a100
parent 5e8d8b81
......@@ -124,6 +124,10 @@
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */; };
0E68571120238546008B0717 /* ConversationNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E68571020238546008B0717 /* ConversationNavigation.swift */; };
0E6949791FA7E71C0029B60A /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6949781FA7E71C0029B60A /* BaseViewController.swift */; };
0E6F544D223BFE3E00ECC3CE /* DisposableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6F544C223BFE3E00ECC3CE /* DisposableCell.swift */; };
0E6F544F223C0ED600ECC3CE /* AccountPickerAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6F544E223C0ED600ECC3CE /* AccountPickerAdapter.swift */; };
0E6F5451223C3C4F00ECC3CE /* AccountItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E6F5450223C3C4F00ECC3CE /* AccountItemView.xib */; };
0E6F5453223C3C7500ECC3CE /* AccountItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6F5452223C3C7500ECC3CE /* AccountItemView.swift */; };
0E72374A20460320006B0C7D /* ProfileHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E72374920460320006B0C7D /* ProfileHeaderView.xib */; };
0E7CF4DB20164B6700CD967D /* ButtonsContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DA20164B6700CD967D /* ButtonsContainerView.swift */; };
0E7CF4DD20165BFB00CD967D /* ButtonsContainerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DC20165BFB00CD967D /* ButtonsContainerViewModel.swift */; };
......@@ -435,6 +439,10 @@
0E63F1F3202907090001F248 /* Ring.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ring.entitlements; sourceTree = "<group>"; };
0E68571020238546008B0717 /* ConversationNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationNavigation.swift; sourceTree = "<group>"; };
0E6949781FA7E71C0029B60A /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
0E6F544C223BFE3E00ECC3CE /* DisposableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposableCell.swift; sourceTree = "<group>"; };
0E6F544E223C0ED600ECC3CE /* AccountPickerAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AccountPickerAdapter.swift; path = Ring/Features/Conversations/SmartList/Cells/AccountPickerAdapter.swift; sourceTree = SOURCE_ROOT; };
0E6F5450223C3C4F00ECC3CE /* AccountItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountItemView.xib; sourceTree = "<group>"; };
0E6F5452223C3C7500ECC3CE /* AccountItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountItemView.swift; sourceTree = "<group>"; };
0E72374920460320006B0C7D /* ProfileHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ProfileHeaderView.xib; sourceTree = "<group>"; };
0E7CF4DA20164B6700CD967D /* ButtonsContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsContainerView.swift; sourceTree = "<group>"; };
0E7CF4DC20165BFB00CD967D /* ButtonsContainerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonsContainerViewModel.swift; sourceTree = "<group>"; };
......@@ -1137,6 +1145,7 @@
0E20E4C22031FEFF0087C868 /* BlockListCell */,
0EAA9DAD2029F054005E245C /* Proxy */,
0E5AFE0B1F8EBC1E0040D539 /* DeviceCell */,
0E6F544C223BFE3E00ECC3CE /* DisposableCell.swift */,
);
name = Cells;
sourceTree = "<group>";
......@@ -1393,8 +1402,11 @@
1A2D18F91F292DA000B2C785 /* Cells */ = {
isa = PBXGroup;
children = (
0E6F544E223C0ED600ECC3CE /* AccountPickerAdapter.swift */,
1A2D18FA1F292DAD00B2C785 /* ConversationCell.swift */,
1A2D18FB1F292DAD00B2C785 /* ConversationCell.xib */,
0E6F5450223C3C4F00ECC3CE /* AccountItemView.xib */,
0E6F5452223C3C7500ECC3CE /* AccountItemView.swift */,
);
name = Cells;
path = ../SmartList/Cells;
......@@ -1704,6 +1716,7 @@
623660AA20092081002598C1 /* src in Resources */,
1A2D18B11F2915B600B2C785 /* SmartlistViewController.storyboard in Resources */,
0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */,
0E6F5451223C3C4F00ECC3CE /* AccountItemView.xib in Resources */,
0E4909751FEAC943005CAA50 /* CallViewController.storyboard in Resources */,
0E438A96204F318200402900 /* AccountHeader.xib in Resources */,
62AD58462056D8EC00AF0701 /* MessageCellDataTransferSent.xib in Resources */,
......@@ -1853,6 +1866,7 @@
04399AAC1D1C304300E99CD9 /* AccountAdapter.mm in Sources */,
0E68571120238546008B0717 /* ConversationNavigation.swift in Sources */,
0E49096C1FEAB225005CAA50 /* CallsAdapterDelegate.swift in Sources */,
0E6F544F223C0ED600ECC3CE /* AccountPickerAdapter.swift in Sources */,
1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */,
1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */,
1A2D18C71F29180700B2C785 /* DeviceModel.swift in Sources */,
......@@ -1906,6 +1920,7 @@
1A3CA32D1F13DA7200283748 /* Chameleon+Ring.swift in Sources */,
0ED2B6FC1F96A158001572F0 /* LinkNewDeviceViewController.swift in Sources */,
1ABE07E21F0D924700D36361 /* Strings.swift in Sources */,
0E6F544D223BFE3E00ECC3CE /* DisposableCell.swift in Sources */,
0E9D84491FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift in Sources */,
621231FB1F8D6FEE009B86F0 /* MessageCell.swift in Sources */,
56AC650E1E85694D00EA1AA9 /* DesignableTextField.swift in Sources */,
......@@ -1997,6 +2012,7 @@
1A2D18A21F27A6D600B2C785 /* LinkDeviceViewModel.swift in Sources */,
564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */,
0E35C10C2077DFF000BBA5E3 /* NotificationCell.swift in Sources */,
0E6F5453223C3C7500ECC3CE /* AccountItemView.swift in Sources */,
0E8E9A0520483E1200DA8E8B /* TitleView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......
......@@ -89,7 +89,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
fileprivate let disposeBag = DisposeBag()
// swiftlint:disable function_body_length
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ignore sigpipe
......@@ -149,42 +148,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
nameService: self.nameService,
dataTransferService: self.dataTransferService,
callService: self.callService)
self.accountService
.sharedResponseStream
.filter({ (event) in
return event.eventType == ServiceEventType.registrationStateChanged &&
event.getEventInput(ServiceEventInput.registrationState) == Registered
})
.subscribe(onNext: { [unowned self] _ in
if let currentAccount = self.accountService.currentAccount, !currentAccount.onBoarded {
currentAccount.onBoarded = true
// make sure video is enabled
let accountDetails = self.accountService.getAccountDetails(fromAccountId: currentAccount.id)
accountDetails.set(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.videoEnabled), withValue: "true")
// set ringtone path
DispatchQueue.main.async { [unowned self] in
self.accountService.setRingtonePath(forAccountId: currentAccount.id)
}
// check if push notifications are enabled in the config
let notificationsEnabled = accountDetails.get(withConfigKeyModel: ConfigKeyModel(withKey: ConfigKey.devicePushToken)).isEmpty ? false : true
if notificationsEnabled {
self.registerVoipNotifications()
}
//in case if application was open when incoming call launched the push notifications
// reimit new call signal to show incoming call alert
self.callService.checkForIncomingCall()
}
})
.disposed(by: disposeBag)
self.window?.rootViewController = self.appCoordinator.rootViewController
self.window?.makeKeyAndVisible()
self.accountService.initialAccountsLoading().subscribe(onCompleted: {
//set selected account if exists
self.appCoordinator.start()
if let selectedAccountId = UserDefaults.standard.string(forKey: self.accountService.selectedAccountID),
let account = self.accountService.getAccount(fromAccountId: selectedAccountId) {
self.accountService.currentAccount = account
}
guard let currentAccount = self.accountService.currentAccount else {
self.log.error("Can't get current account!")
return
......@@ -197,6 +170,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
self.conversationManager?
.prepareConversationsForAccount(accountId: currentAccount.id, accountUri: ringID)
}
self.reloadDataFor(account: currentAccount)
if self.accountService.proxyEnabled() {
self.registerVoipNotifications()
} else {
self.unregisterVoipNotifications()
}
// reimit new call signal to show incoming call alert
self.callService.checkForIncomingCall()
}, onError: { _ in
self.appCoordinator.start()
let time = DispatchTime.now() + 1
......@@ -204,17 +185,30 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
self.appCoordinator.showDatabaseError()
}
}).disposed(by: self.disposeBag)
self.accountService.currentAccountChanged
.subscribe(onNext: { account in
guard let currentAccount = account else {return}
self.reloadDataFor(account: currentAccount)
}).disposed(by: self.disposeBag)
self.voipRegistry.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(registerVoipNotifications),
name: NSNotification.Name(rawValue: NotificationName.enablePushNotifications.rawValue),
object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(unregisterVoipNotifications),
name: NSNotification.Name(rawValue: NotificationName.disablePushNotifications.rawValue),
object: nil)
self.clearBadgeNumber()
return true
}
func reloadDataFor(account: AccountModel) {
self.contactsService.loadContacts(withAccount: account)
self.contactsService.loadContactRequests(withAccount: account)
self.presenceService.subscribeBuddies(withAccount: account, withContacts: self.contactsService.contacts.value)
if let ringID = AccountModelHelper(withAccount: account).ringId {
self.conversationManager?
.prepareConversationsForAccount(accountId: account.id, accountUri: ringID)
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
self.log.warning("entering background")
}
......@@ -288,13 +282,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
@objc private func registerVoipNotifications() {
self.requestNotificationAuthorization()
self.voipRegistry.desiredPushTypes = Set([PKPushType.voIP])
if self.voipRegistry.desiredPushTypes == nil {
self.voipRegistry.desiredPushTypes = Set([PKPushType.voIP])
}
}
@objc private func unregisterVoipNotifications() {
self.voipRegistry.desiredPushTypes = nil
private func unregisterVoipNotifications() {
self.voipRegistry.desiredPushTypes = nil
self.accountService.savePushToken(token: "")
self.accountService.setPushNotificationToken(token: "")
self.accountService.updatePushTokenForCurrentAccount(token: "")
}
private func requestNotificationAuthorization() {
......@@ -415,8 +411,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
extension AppDelegate: PKPushRegistryDelegate {
func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
self.accountService.savePushToken(token: "")
self.accountService.setPushNotificationToken(token: "")
self.accountService.updatePushTokenForCurrentAccount(token: "")
}
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {
......@@ -426,7 +422,7 @@ extension AppDelegate: PKPushRegistryDelegate {
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
if type == PKPushType.voIP {
let deviceTokenString = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
self.accountService.updatePushTokenForCurrentAccount(token: deviceTokenString)
self.accountService.savePushToken(token: deviceTokenString)
self.accountService.setPushNotificationToken(token: deviceTokenString)
}
}
......
......@@ -43,6 +43,8 @@ internal enum L10n {
internal static let namePlaceholder = L10n.tr("Localizable", "accountPage.namePlaceholder")
/// Your device won't receive notifications when proxy is disabled
internal static let noProxyExplanationLabel = L10n.tr("Localizable", "accountPage.noProxyExplanationLabel")
/// Other
internal static let other = L10n.tr("Localizable", "accountPage.other")
/// Provide proxy address
internal static let proxyAddressAlert = L10n.tr("Localizable", "accountPage.proxyAddressAlert")
/// In order to receive notifications, please enable proxy
......@@ -51,6 +53,12 @@ internal enum L10n {
internal static let proxyDisabledAlertTitle = L10n.tr("Localizable", "accountPage.proxyDisabledAlertTitle")
/// Proxy address
internal static let proxyPaceholder = L10n.tr("Localizable", "accountPage.proxyPaceholder")
/// Remove
internal static let removeAccountButton = L10n.tr("Localizable", "accountPage.removeAccountButton")
/// By clicking "Remove" you will remove this account on this device! This action can not be undone. Also, your registered name can be lost.
internal static let removeAccountMessage = L10n.tr("Localizable", "accountPage.removeAccountMessage")
/// Remove account
internal static let removeAccountTitle = L10n.tr("Localizable", "accountPage.removeAccountTitle")
/// Revoke
internal static let revokeDeviceButton = L10n.tr("Localizable", "accountPage.revokeDeviceButton")
/// Are you sure you want to revoke this device? This action could not be undone.
......@@ -330,6 +338,10 @@ internal enum L10n {
}
internal enum Smartlist {
/// Accounts
internal static let accountsTitle = L10n.tr("Localizable", "smartlist.accountsTitle")
/// + Add Account
internal static let addAccountButton = L10n.tr("Localizable", "smartlist.addAccountButton")
/// Be sure cellular access is granted in your settings
internal static let cellularAccess = L10n.tr("Localizable", "smartlist.cellularAccess")
/// Conversations
......
......@@ -30,7 +30,8 @@ import RxSwift
/// - allSet: everything is set, the app should display its main interface
public enum AppState: State {
case initialLoading
case needToOnboard
case needToOnboard(animated: Bool, isFirstAccount: Bool)
case addAccount
case allSet
}
......@@ -49,6 +50,7 @@ final class AppCoordinator: Coordinator, StateableResponsive {
var rootViewController: UIViewController {
return self.navigationController
}
var parentCoordinator: Coordinator?
var childCoordinators = [Coordinator]()
// MARK: -
......@@ -79,10 +81,12 @@ final class AppCoordinator: Coordinator, StateableResponsive {
switch state {
case .initialLoading:
self.showInitialLoading()
case .needToOnboard:
self.showWalkthrough()
case .needToOnboard(let animated, let isFirstAccount):
self.showWalkthrough(animated: animated, isAccountFirst: isFirstAccount)
case .allSet:
self.showMainInterface()
case .addAccount:
self.showWalkthrough(animated: false, isAccountFirst: false)
}
}).disposed(by: self.disposeBag)
}
......@@ -98,7 +102,7 @@ final class AppCoordinator: Coordinator, StateableResponsive {
/// Handles the switch between the three supported screens.
private func dispatchApplication() {
if self.injectionBag.accountService.accounts.isEmpty {
self.stateSubject.onNext(AppState.needToOnboard)
self.stateSubject.onNext(AppState.needToOnboard(animated: true, isFirstAccount: true))
} else {
self.stateSubject.onNext(AppState.allSet)
}
......@@ -119,8 +123,10 @@ final class AppCoordinator: Coordinator, StateableResponsive {
}
/// Presents the walkthrough as a popup with a fade effect
private func showWalkthrough () {
private func showWalkthrough (animated: Bool, isAccountFirst: Bool) {
let walkthroughCoordinator = WalkthroughCoordinator(with: self.injectionBag)
walkthroughCoordinator.isAccountFirst = isAccountFirst
walkthroughCoordinator.withAnimations = animated
walkthroughCoordinator.start()
self.addChildCoordinator(childCoordinator: walkthroughCoordinator)
......@@ -134,6 +140,7 @@ final class AppCoordinator: Coordinator, StateableResponsive {
walkthroughCoordinator?.stateSubject.dispose()
self?.removeChildCoordinator(childCoordinator: walkthroughCoordinator)
self?.dispatchApplication()
self?.tabBarViewController.selectedIndex = 0
}).disposed(by: self.disposeBag)
}
......@@ -144,9 +151,13 @@ final class AppCoordinator: Coordinator, StateableResponsive {
}
let conversationsCoordinator = ConversationsCoordinator(with: self.injectionBag)
conversationsCoordinator.parentCoordinator = self
let contactRequestsCoordinator = ContactRequestsCoordinator(with: self.injectionBag)
contactRequestsCoordinator.parentCoordinator = self
let meCoordinator = MeCoordinator(with: self.injectionBag)
meCoordinator.parentCoordinator = self
self.tabBarViewController.tabBar.tintColor = UIColor.jamiMain
self.tabBarViewController.view.backgroundColor = UIColor.white
self.tabBarViewController.viewControllers = [conversationsCoordinator.rootViewController,
contactRequestsCoordinator.rootViewController,
......
......@@ -231,6 +231,10 @@ class DBManager {
}
}
func removeDBForAccount(accountId: String) {
self.dbConnections.removeDBForAccount(account: accountId)
}
// swiftlint:disable:next function_parameter_count
func saveMessage(for accountId: String, with contactUri: String,
message: MessageModel, incoming: Bool,
......
......@@ -41,6 +41,18 @@ class EditProfileViewController: UIViewController, UITextFieldDelegate, UIImageP
override func viewDidLoad() {
super.viewDidLoad()
self.model.profileImage
.bind(to: self.profileImageView.rx.image)
.disposed(by: disposeBag)
self.model.profileName
.bind(to: self.profileName.rx.text)
.disposed(by: disposeBag)
//Binds the keyboard Send button action to the ViewModel
self.profileName.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { [unowned self] _ in
self.model.updateName(self.profileName.text!)
}).disposed(by: disposeBag)
}
override func viewWillAppear(_ animated: Bool) {
......@@ -53,22 +65,9 @@ class EditProfileViewController: UIViewController, UITextFieldDelegate, UIImageP
profileName.returnKeyType = .done
profileName.autocorrectionType = .no
self.model.profileImage
.bind(to: self.profileImageView.rx.image)
.disposed(by: disposeBag)
self.model.profileName
.bind(to: self.profileName.rx.text)
.disposed(by: disposeBag)
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
profileImageView.isUserInteractionEnabled = true
profileImageView.addGestureRecognizer(tapGestureRecognizer)
//Binds the keyboard Send button action to the ViewModel
self.profileName.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { [unowned self] _ in
self.model.updateName(self.profileName.text!)
}).disposed(by: disposeBag)
}
func resetProfileName() {
......
......@@ -32,31 +32,36 @@ class EditProfileViewModel {
let accountService: AccountsService
lazy var profileImage: Observable<UIImage?> = { [unowned self] in
guard let account = self.accountService.currentAccount else {
return Observable.just(defaultImage)
}
return self.profileService.getAccountProfile(accountId: account.id)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01, execute: {
if let account = self.accountService.currentAccount {
self.profileService.getAccountProfile(accountId: account.id)
.take(1)
.subscribe(onNext: { profile in
self.profileForCurrentAccount.onNext(profile)
}).disposed(by: self.disposeBag)
}
})
return profileForCurrentAccount.share()
.map({ profile in
if let photo = profile.photo,
let data = NSData(base64Encoded: photo,
options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? {
options: NSData.Base64DecodingOptions
.ignoreUnknownCharacters) as Data? {
self.image = UIImage(data: data)
return UIImage(data: data)
return UIImage(data: data)!
}
return self.defaultImage
return UIImage(named: "add_avatar")!
})
}()
var profileForCurrentAccount = PublishSubject<AccountProfile>()
lazy var profileName: Observable<String?> = { [unowned self] in
guard let account = self.accountService.currentAccount
else {
return Observable.just("")
}
return self.profileService.getAccountProfile(accountId: account.id)
return profileForCurrentAccount.share()
.map({ profile in
if let alias = profile.alias, !alias.isEmpty {
self.name = alias
return alias
if let name = profile.alias {
self.name = name
return name
}
return ""
})
......@@ -65,6 +70,19 @@ class EditProfileViewModel {
init(profileService: ProfilesService, accountService: AccountsService) {
self.profileService = profileService
self.accountService = accountService
self.accountService.currentAccountChanged
.subscribe(onNext: { [unowned self] account in
if let selectedAccount = account {
self.updateProfileInfoFor(accountId: selectedAccount.id)
}
}).disposed(by: self.disposeBag)
}
func updateProfileInfoFor(accountId: String) {
self.profileService.getAccountProfile(accountId: accountId)
.subscribe(onNext: { [unowned self] profile in
self.profileForCurrentAccount.onNext(profile)
}).disposed(by: self.disposeBag)
}
func saveProfile() {
......
......@@ -191,4 +191,61 @@ extension UIImage {
UIGraphicsEndImageContext()
return newImage
}
func drawText(text: String, backgroundColor: UIColor, textColor: UIColor, size: CGSize) -> UIImage? {
//Setups up the font attributes that will be later used to dictate how the text should be drawn
let textFont = UIFont.systemFont(ofSize: 20, weight: .semibold)
let textFontAttributes = [
NSAttributedStringKey.font: textFont,
NSAttributedStringKey.foregroundColor: textColor]
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
backgroundColor.setFill()
UIRectFill(rect)
//Put the image into a rectangle as large as the original image.
self.draw(in: rect)
// Our drawing bounds
let textSize = text.size(withAttributes: [NSAttributedStringKey.font: textFont])
let textRect = CGRect(x: rect.size.width/2 - textSize.width/2, y: rect.size.height/2 - textSize.height/2,
width: textSize.width, height: textSize.height)
text.draw(in: textRect, withAttributes: textFontAttributes)
let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func drawBackground(color: UIColor, size: CGSize) -> UIImage? {
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
class func defaultJamiAvatarFor(profileName: String?, account: AccountModel) -> UIImage {
let image = UIImage(asset: Asset.icContactPicture)!
.withAlignmentRectInsets(UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4))
var name: String? = (profileName != nil) ? profileName :
!account.registeredName.isEmpty ?
account.registeredName : nil
if let userNameData = UserDefaults.standard.dictionary(forKey: registeredNamesKey),
let accountName = userNameData[account.id] as? String,
!accountName.isEmpty {
name = accountName
}
guard let username = name else {return image}
let scanner = Scanner(string: username.toMD5HexString().prefixString())
var index: UInt64 = 0
if scanner.scanHexInt64(&index) {
let fbaBGColor = avatarColors[Int(index)]
if !username.isSHA1() && !username.isEmpty {
if let avatar = image.drawText(text: username.prefixString().capitalized, backgroundColor: fbaBGColor, textColor: UIColor.white, size: CGSize(width: 40, height: 40)) {
return avatar
}
}
}
return image
}
}
......@@ -30,6 +30,7 @@ class ContactRequestsCoordinator: Coordinator, StateableResponsive, Conversation
}
var childCoordinators = [Coordinator]()
var parentCoordinator: Coordinator?
private let navigationViewController = BaseViewController(with: TabBarItemType.contactRequest)
let injectionBag: InjectionBag
......@@ -41,9 +42,16 @@ class ContactRequestsCoordinator: Coordinator, StateableResponsive, Conversation
required init (with injectionBag: InjectionBag) {
self.injectionBag = injectionBag
self.contactService = injectionBag.contactsService
self.navigationViewController.viewModel = ContactRequestTabBarItem(with: self.injectionBag)
self.navigationViewController.viewModel =
ContactRequestTabBarItem(with: self.injectionBag)
self.addLockFlags()
self.callbackPlaceCall()
self.injectionBag.accountService
.currentAccountChanged
.subscribe(onNext: {[unowned self] _ in
self.navigationViewController.viewModel =
ContactRequestTabBarItem(with: self.injectionBag)
}).disposed(by: self.disposeBag)
}
func addLockFlags() {
presentingVC[VCType.contact.rawValue] = false
......
......@@ -31,6 +31,7 @@ class ConversationsCoordinator: Coordinator, StateableResponsive, ConversationNa
}
var childCoordinators = [Coordinator]()
var parentCoordinator: Coordinator?
private let navigationViewController = BaseViewController(with: TabBarItemType.chat)
let injectionBag: InjectionBag
......@@ -49,6 +50,16 @@ class ConversationsCoordinator: Coordinator, StateableResponsive, ConversationNa
self.conversationService = injectionBag.conversationsService
self.addLockFlags()
self.stateSubject.subscribe(onNext: { [unowned self] (state) in
guard let state = state as? ConversationState else { return }
switch state {
case .createNewAccount:
self.createNewAccount()
default:
break
}
}).disposed(by: self.disposeBag)
self.callService.newCall.asObservable()
.map({ call in
return call
......@@ -59,6 +70,12 @@ class ConversationsCoordinator: Coordinator, StateableResponsive, ConversationNa
self.navigationViewController.viewModel = ChatTabBarItemViewModel(with: self.injectionBag)
self.callbackPlaceCall()
NotificationCenter.default.addObserver(self, selector: #selector(self.incomingCall(_:)), name: NSNotification.Name(NotificationName.answerCallFromNotifications.rawValue), object: nil)
self.accountService.currentAccountChanged
.subscribe(onNext: {[unowned self] _ in
self.navigationViewController.viewModel =
ChatTabBarItemViewModel(with: self.injectionBag)
}).disposed(by: self.disposeBag)
}
@objc func incomingCall(_ notification: NSNotification) {
......@@ -69,6 +86,12 @@ class ConversationsCoordinator: Coordinator, StateableResponsive, ConversationNa
self.answerIncomingCall(call: call)
}
func createNewAccount() {
if let parent = self.parentCoordinator as? AppCoordinator {
parent.stateSubject.onNext(AppState.addAccount)
}
}
func puchConversation(participantId: String) {
let conversationViewModel = ConversationViewModel(with: self.injectionBag)
guard let account = accountService.currentAccount else {
......
/*
* Copyright (C) 2019 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
class AccountItemView: UIView {
@IBOutlet var containerView: UIView!
@IBOutlet weak var avatarView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
func commonInit() {
Bundle.main.loadNibNamed("AccountItemView", owner: self, options: nil)
addSubview(containerView)
containerView.frame = self.bounds
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AccountItemView" customModule="Ring" customModuleProvider="target">
<connections>
<outlet property="avatarView" destination="1tO-cV-0xx" id="NvX-rb-pwF"/>
<outlet property="containerView" destination="ZAR-pP-XYM" id="9C5-cI-mFe"/>
<outlet property="nameLabel" destination="tuV-pF-WQA" id="lXf-rB-tMK"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="ZAR-pP-XYM">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="1tO-cV-0xx">
<rect key="frame" x="10" y="10" width="40" height="40"/>
<color key="tintColor" red="0.1215686275" green="0.28627450980000002" blue="0.4431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="aCk-LH-mUF"/>
<constraint firstAttribute="width" constant="40" id="yky-Kb-wdU"/>
</constraints>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="boolean" keyPath="roundedCorners" value="YES"/>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="20"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tuV-pF-WQA">
<rect key="frame" x="60" y="19.5" width="0.0" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="Zjs-hB-YBW"/>