Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Sign in / Register
Toggle navigation
J
jami-client-ios
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Insights
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
27
Issues
27
List
Boards
Labels
Milestones
Security & Compliance
Security & Compliance
Dependency List
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
savoirfairelinux
jami-client-ios
Commits
32ec4d2d
Commit
32ec4d2d
authored
Feb 25, 2019
by
Kateryna Kostiuk
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
account: support multiple accounts
Change-Id: I45091f153873900b8285bcc0ae8f941171f4a100
parent
5e8d8b81
Changes
31
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
1106 additions
and
530 deletions
+1106
-530
Ring/Ring.xcodeproj/project.pbxproj
Ring/Ring.xcodeproj/project.pbxproj
+16
-0
Ring/Ring/AppDelegate.swift
Ring/Ring/AppDelegate.swift
+37
-41
Ring/Ring/Constants/Generated/Strings.swift
Ring/Ring/Constants/Generated/Strings.swift
+12
-0
Ring/Ring/Coordinators/AppCoordinator.swift
Ring/Ring/Coordinators/AppCoordinator.swift
+16
-5
Ring/Ring/Database/DBManager.swift
Ring/Ring/Database/DBManager.swift
+4
-0
Ring/Ring/EditProfileViewController.swift
Ring/Ring/EditProfileViewController.swift
+12
-13
Ring/Ring/EditProfileViewModel.swift
Ring/Ring/EditProfileViewModel.swift
+33
-15
Ring/Ring/Extensions/UIImage+Helpers.swift
Ring/Ring/Extensions/UIImage+Helpers.swift
+57
-0
Ring/Ring/Features/ContactRequests/ContactRequestsCoordinator.swift
...Features/ContactRequests/ContactRequestsCoordinator.swift
+9
-1
Ring/Ring/Features/Conversations/ConversationsCoordinator.swift
...ing/Features/Conversations/ConversationsCoordinator.swift
+23
-0
Ring/Ring/Features/Conversations/SmartList/Cells/AccountItemView.swift
...tures/Conversations/SmartList/Cells/AccountItemView.swift
+44
-0
Ring/Ring/Features/Conversations/SmartList/Cells/AccountItemView.xib
...eatures/Conversations/SmartList/Cells/AccountItemView.xib
+63
-0
Ring/Ring/Features/Conversations/SmartList/Cells/AccountPickerAdapter.swift
.../Conversations/SmartList/Cells/AccountPickerAdapter.swift
+105
-0
Ring/Ring/Features/Conversations/SmartList/SmartlistViewController.swift
...res/Conversations/SmartList/SmartlistViewController.swift
+120
-1
Ring/Ring/Features/Conversations/SmartList/SmartlistViewModel.swift
...Features/Conversations/SmartList/SmartlistViewModel.swift
+182
-87
Ring/Ring/Features/Me/Me/DisposableCell.swift
Ring/Ring/Features/Me/Me/DisposableCell.swift
+30
-0
Ring/Ring/Features/Me/Me/MeViewController.swift
Ring/Ring/Features/Me/Me/MeViewController.swift
+33
-103
Ring/Ring/Features/Me/Me/MeViewModel.swift
Ring/Ring/Features/Me/Me/MeViewModel.swift
+150
-158
Ring/Ring/Features/Me/MeCoordinator.swift
Ring/Ring/Features/Me/MeCoordinator.swift
+12
-1
Ring/Ring/Features/Walkthrough/CreateAccount/CreateAccountViewModel.swift
...es/Walkthrough/CreateAccount/CreateAccountViewModel.swift
+2
-0
Ring/Ring/Features/Walkthrough/WalkthroughCoordinator.swift
Ring/Ring/Features/Walkthrough/WalkthroughCoordinator.swift
+5
-0
Ring/Ring/Features/Walkthrough/Welcome/WelcomeViewController.swift
.../Features/Walkthrough/Welcome/WelcomeViewController.swift
+22
-2
Ring/Ring/Features/Walkthrough/Welcome/WelcomeViewModel.swift
.../Ring/Features/Walkthrough/Welcome/WelcomeViewModel.swift
+8
-0
Ring/Ring/Models/AccountModel.swift
Ring/Ring/Models/AccountModel.swift
+21
-2
Ring/Ring/Protocols/ConversationNavigation.swift
Ring/Ring/Protocols/ConversationNavigation.swift
+3
-0
Ring/Ring/Protocols/Coordinator.swift
Ring/Ring/Protocols/Coordinator.swift
+3
-0
Ring/Ring/Resources/en.lproj/Localizable.strings
Ring/Ring/Resources/en.lproj/Localizable.strings
+6
-0
Ring/Ring/Services/AccountsService.swift
Ring/Ring/Services/AccountsService.swift
+73
-99
Ring/Ring/Services/ContactsService.swift
Ring/Ring/Services/ContactsService.swift
+2
-0
Ring/Ring/Services/NameService.swift
Ring/Ring/Services/NameService.swift
+2
-2
Ring/Ring/TabBar/BaseViewController.swift
Ring/Ring/TabBar/BaseViewController.swift
+1
-0
No files found.
Ring/Ring.xcodeproj/project.pbxproj
View file @
32ec4d2d
...
...
@@ -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
;
...
...
Ring/Ring/AppDelegate.swift
View file @
32ec4d2d
...
...
@@ -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
)
}
}
...
...
Ring/Ring/Constants/Generated/Strings.swift
View file @
32ec4d2d
...
...
@@ -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
...
...
Ring/Ring/Coordinators/AppCoordinator.swift
View file @
32ec4d2d
...
...
@@ -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
,
...
...
Ring/Ring/Database/DBManager.swift
View file @
32ec4d2d
...
...
@@ -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
,
...
...
Ring/Ring/EditProfileViewController.swift
View file @
32ec4d2d
...
...
@@ -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
()
{
...
...
Ring/Ring/EditProfileViewModel.swift
View file @
32ec4d2d
...
...
@@ -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
()
{
...
...
Ring/Ring/Extensions/UIImage+Helpers.swift
View file @
32ec4d2d
...
...
@@ -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
}
}
Ring/Ring/Features/ContactRequests/ContactRequestsCoordinator.swift
View file @
32ec4d2d
...
...
@@ -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
...
...
Ring/Ring/Features/Conversations/ConversationsCoordinator.swift
View file @
32ec4d2d
...
...
@@ -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
{
...
...
Ring/Ring/Features/Conversations/SmartList/Cells/AccountItemView.swift
0 → 100644
View file @
32ec4d2d
/*
* 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
}
}
Ring/Ring/Features/Conversations/SmartList/Cells/AccountItemView.xib
0 → 100644
View file @
32ec4d2d
<?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"
/>