Commit 8dfb5d62 authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

UI/UX: add contact page

Change-Id: I068ba7f45d88a44edbcbb4a6b241c4569c93fa40
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent ec8e54db
......@@ -8,3 +8,4 @@ github "andreamazz/AMPopTip"
github "ashleymills/Reachability.swift"
github "stephencelis/SQLite.swift" ~> 0.11.4
github "optonaut/ActiveLabel.swift"
github "gskbyte/GSKStretchyHeaderView"
......@@ -6,7 +6,8 @@ github "SwiftyBeaver/SwiftyBeaver" "1.4.3"
github "ViccAlexander/Chameleon" "2.2.0"
github "andreamazz/AMPopTip" "3.0.2"
github "ashleymills/Reachability.swift" "v4.1.0"
github "gskbyte/GSKStretchyHeaderView" "1.0.4"
github "optonaut/ActiveLabel.swift" "0.8.0"
github "pkluz/PKHUD" "5.0.0"
github "realm/realm-cocoa" "v3.0.1"
github "stephencelis/SQLite.swift" "0.11.4"
github "optonaut/ActiveLabel.swift" "0.8.0"
......@@ -110,6 +110,11 @@
0E3697A1203235EA009A68CA /* BannedContactItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3697A0203235EA009A68CA /* BannedContactItem.swift */; };
0E3697A8203243D3009A68CA /* BannedContactCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3697A6203243D3009A68CA /* BannedContactCell.swift */; };
0E3697A9203243D3009A68CA /* BannedContactCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E3697A7203243D3009A68CA /* BannedContactCell.xib */; };
0E3BD4242044776300A50DDF /* ContactViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E3BD4232044776300A50DDF /* ContactViewController.storyboard */; };
0E3BD4262044778100A50DDF /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3BD4252044778100A50DDF /* ContactViewModel.swift */; };
0E3BD4282044784000A50DDF /* ContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3BD4272044784000A50DDF /* ContactViewController.swift */; };
0E3BD4322044A41D00A50DDF /* GSKStretchyHeaderView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3BD4312044A41D00A50DDF /* GSKStretchyHeaderView.framework */; };
0E3BD4362044B39F00A50DDF /* ProfileHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3BD4352044B39F00A50DDF /* ProfileHeaderView.swift */; };
0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E403F801F7D797300C80BC2 /* MessageCellGenerated.swift */; };
0E403F831F7D79B000C80BC2 /* MessageCellGenerated.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */; };
0E44B62F202B9DE40060F71B /* LocalNotificationsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44B62E202B9DE40060F71B /* LocalNotificationsHelper.swift */; };
......@@ -122,11 +127,13 @@
0E4909751FEAC943005CAA50 /* CallViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0E4909741FEAC943005CAA50 /* CallViewController.storyboard */; };
0E49097A1FEAC9E1005CAA50 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4909791FEAC9E1005CAA50 /* CallViewController.swift */; };
0E49097C1FEACA4B005CAA50 /* CallViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */; };
0E68571120238546008B0717 /* CallMakeable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E68571020238546008B0717 /* CallMakeable.swift */; };
0E68571120238546008B0717 /* ConversationNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E68571020238546008B0717 /* ConversationNavigation.swift */; };
0E6949791FA7E71C0029B60A /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E6949781FA7E71C0029B60A /* BaseViewController.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 */; };
0E7CF4DF2017918300CD967D /* ButtonsContainerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E7CF4DE2017918300CD967D /* ButtonsContainerView.xib */; };
0E8E9A0520483E1200DA8E8B /* TitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8E9A0420483E1200DA8E8B /* TitleView.swift */; };
0E983E6E1FC77C3E0082103E /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E983E6D1FC77C3E0082103E /* ConversationModel.swift */; };
0E9D84491FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */; };
0E9D844B1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E9D844A1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift */; };
......@@ -391,6 +398,11 @@
0E3697A0203235EA009A68CA /* BannedContactItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannedContactItem.swift; sourceTree = "<group>"; };
0E3697A6203243D3009A68CA /* BannedContactCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BannedContactCell.swift; sourceTree = "<group>"; };
0E3697A7203243D3009A68CA /* BannedContactCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BannedContactCell.xib; sourceTree = "<group>"; };
0E3BD4232044776300A50DDF /* ContactViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ContactViewController.storyboard; sourceTree = "<group>"; };
0E3BD4252044778100A50DDF /* ContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = "<group>"; };
0E3BD4272044784000A50DDF /* ContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewController.swift; sourceTree = "<group>"; };
0E3BD4312044A41D00A50DDF /* GSKStretchyHeaderView.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GSKStretchyHeaderView.framework; path = Carthage/Build/iOS/GSKStretchyHeaderView.framework; sourceTree = "<group>"; };
0E3BD4352044B39F00A50DDF /* ProfileHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderView.swift; sourceTree = "<group>"; };
0E403F801F7D797300C80BC2 /* MessageCellGenerated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCellGenerated.swift; sourceTree = "<group>"; };
0E403F821F7D79B000C80BC2 /* MessageCellGenerated.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCellGenerated.xib; sourceTree = "<group>"; };
0E44B62E202B9DE40060F71B /* LocalNotificationsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationsHelper.swift; sourceTree = "<group>"; };
......@@ -405,11 +417,13 @@
0E4909791FEAC9E1005CAA50 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = "<group>"; };
0E49097B1FEACA4B005CAA50 /* CallViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewModel.swift; sourceTree = "<group>"; };
0E63F1F3202907090001F248 /* Ring.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ring.entitlements; sourceTree = "<group>"; };
0E68571020238546008B0717 /* CallMakeable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMakeable.swift; 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>"; };
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>"; };
0E7CF4DE2017918300CD967D /* ButtonsContainerView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ButtonsContainerView.xib; sourceTree = "<group>"; };
0E8E9A0420483E1200DA8E8B /* TitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleView.swift; sourceTree = "<group>"; };
0E983E6D1FC77C3E0082103E /* ConversationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; };
0E9D84481FA7DA6A00C561EB /* ChatTabBarItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatTabBarItemViewModel.swift; sourceTree = "<group>"; };
0E9D844A1FA7DBAA00C561EB /* ContactRequestTabBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestTabBarItem.swift; sourceTree = "<group>"; };
......@@ -616,6 +630,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
0E3BD4322044A41D00A50DDF /* GSKStretchyHeaderView.framework in Frameworks */,
0E4909611FE97A94005CAA50 /* ActiveLabel.framework in Frameworks */,
0E0FF1A71FC38070003898C2 /* SQLite.framework in Frameworks */,
5C093F011FB495830011D90E /* Differentiator.framework in Frameworks */,
......@@ -728,6 +743,7 @@
02AED8171DD4C4B000F740BA /* Frameworks */ = {
isa = PBXGroup;
children = (
0E3BD4312044A41D00A50DDF /* GSKStretchyHeaderView.framework */,
0E4909601FE97A94005CAA50 /* ActiveLabel.framework */,
0E0FF1A61FC38070003898C2 /* SQLite.framework */,
5C093F001FB495830011D90E /* Differentiator.framework */,
......@@ -1033,6 +1049,19 @@
name = BlockList;
sourceTree = "<group>";
};
0E3BD4222044770000A50DDF /* Contact */ = {
isa = PBXGroup;
children = (
0E3BD4232044776300A50DDF /* ContactViewController.storyboard */,
0E3BD4252044778100A50DDF /* ContactViewModel.swift */,
0E3BD4272044784000A50DDF /* ContactViewController.swift */,
0E3BD4352044B39F00A50DDF /* ProfileHeaderView.swift */,
0E72374920460320006B0C7D /* ProfileHeaderView.xib */,
0E8E9A0420483E1200DA8E8B /* TitleView.swift */,
);
path = Contact;
sourceTree = "<group>";
};
0E44B62D202B9DC40060F71B /* Helpers */ = {
isa = PBXGroup;
children = (
......@@ -1117,6 +1146,7 @@
1A0C4EBC1F1D48AA00550433 /* Features */ = {
isa = PBXGroup;
children = (
0E3BD4222044770000A50DDF /* Contact */,
5CE66F721FBF765D00EE9291 /* InitialLoading */,
0E4909711FEAC822005CAA50 /* Calls */,
0E9D84471FA7D9EC00C561EB /* TabBar */,
......@@ -1198,7 +1228,7 @@
1A2041791F1E547F00C08435 /* Stateable.swift */,
1A20418C1F1EABCC00C08435 /* StateableResponsive.swift */,
1A20418E1F1EAC0E00C08435 /* Coordinator.swift */,
0E68571020238546008B0717 /* CallMakeable.swift */,
0E68571020238546008B0717 /* ConversationNavigation.swift */,
);
path = Protocols;
sourceTree = "<group>";
......@@ -1596,6 +1626,7 @@
0EBCAA4E202E60F000E2A545 /* default.wav in Resources */,
1A2D18E61F29197100B2C785 /* MessageAccessoryView.xib in Resources */,
1A2D18F81F292D7200B2C785 /* MessageCellSent.xib in Resources */,
0E3BD4242044776300A50DDF /* ContactViewController.storyboard in Resources */,
1A2D18F61F292D7200B2C785 /* MessageCellReceived.xib in Resources */,
0ED2B6FA1F96A075001572F0 /* LinkNewDeviceViewController.storyboard in Resources */,
1A2D18EF1F291A0100B2C785 /* MeDetailViewController.storyboard in Resources */,
......@@ -1615,6 +1646,7 @@
1A0C4EDA1F1D4B1B00550433 /* WelcomeViewController.storyboard in Resources */,
5CE66F751FBF769B00EE9291 /* InitialLoadingViewController.storyboard in Resources */,
1A5DC03E1F35678D0075E8EF /* ContactRequestsViewController.storyboard in Resources */,
0E72374A20460320006B0C7D /* ProfileHeaderView.xib in Resources */,
0EB1A5CF1F8EBE03009923E2 /* DeviceCell.xib in Resources */,
1A2D18B31F2915C500B2C785 /* ConversationViewController.storyboard in Resources */,
0E7CF4DF2017918300CD967D /* ButtonsContainerView.xib in Resources */,
......@@ -1662,6 +1694,7 @@
"$(SRCROOT)/Carthage/Build/iOS/Differentiator.framework",
"$(SRCROOT)/Carthage/Build/iOS/SQLite.framework",
"$(SRCROOT)/Carthage/Build/iOS/ActiveLabel.framework",
"$(SRCROOT)/Carthage/Build/iOS/GSKStretchyHeaderView.framework",
);
name = "⚙️ Copy Frameworks";
outputPaths = (
......@@ -1678,7 +1711,8 @@
"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/AMPopTip.framework",
"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Differentiator.framework",
"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/SQLite.framework",
"(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ActiveLabel.framework",
"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ActiveLabel.framework",
"$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/GSKStretchyHeaderView.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
......@@ -1741,7 +1775,7 @@
0E3697A8203243D3009A68CA /* BannedContactCell.swift in Sources */,
1A2D18F71F292D7200B2C785 /* MessageCellSent.swift in Sources */,
04399AAC1D1C304300E99CD9 /* AccountAdapter.mm in Sources */,
0E68571120238546008B0717 /* CallMakeable.swift in Sources */,
0E68571120238546008B0717 /* ConversationNavigation.swift in Sources */,
0E49096C1FEAB225005CAA50 /* CallsAdapterDelegate.swift in Sources */,
1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */,
1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */,
......@@ -1761,10 +1795,12 @@
1A5DC0241F3564360075E8EF /* ContactRequestModel.swift in Sources */,
0E4909701FEAC1C6005CAA50 /* CallModel.swift in Sources */,
1A5DC03F1F35678D0075E8EF /* ContactRequestsViewController.swift in Sources */,
0E3BD4282044784000A50DDF /* ContactViewController.swift in Sources */,
62006E04203F4DD6003C3197 /* UITextField+Helpers.swift in Sources */,
1A20418B1F1EA58A00C08435 /* ViewModelBased.swift in Sources */,
1A5DC01E1F355DA70075E8EF /* ContactsAdapterDelegate.swift in Sources */,
0E983E6E1FC77C3E0082103E /* ConversationModel.swift in Sources */,
0E3BD4362044B39F00A50DDF /* ProfileHeaderView.swift in Sources */,
1A20418D1F1EABCC00C08435 /* StateableResponsive.swift in Sources */,
1A0C4EE51F1D67DF00550433 /* WalkthroughCoordinator.swift in Sources */,
1A2D18DD1F29192D00B2C785 /* MessableBubble.swift in Sources */,
......@@ -1804,6 +1840,7 @@
0E403F811F7D797300C80BC2 /* MessageCellGenerated.swift in Sources */,
1A2041821F1E906B00C08435 /* CreateProfileViewModel.swift in Sources */,
1A0C4EE31F1D673600550433 /* InjectionBag.swift in Sources */,
0E3BD4262044778100A50DDF /* ContactViewModel.swift in Sources */,
564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */,
0E36979920322D46009A68CA /* BlockListViewController.swift in Sources */,
1A2D18AA1F29131900B2C785 /* ConversationsCoordinator.swift in Sources */,
......@@ -1870,6 +1907,7 @@
1A5DC0301F3565AE0075E8EF /* SmartlistViewController.swift in Sources */,
1A2D18A21F27A6D600B2C785 /* LinkDeviceViewModel.swift in Sources */,
564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */,
0E8E9A0520483E1200DA8E8B /* TitleView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -40,3 +40,4 @@
#import "CallsAdapter.h"
#import <PushKit/PushKit.h>
#import <UserNotifications/UserNotifications.h>
#import <GSKStretchyHeaderView/GSKStretchyHeaderView.h>
......@@ -54,6 +54,7 @@ enum Asset {
static let backgroundRing = ImageAsset(name: "background_ring")
static let blockIcon = ImageAsset(name: "block_icon")
static let callButton = ImageAsset(name: "call_button")
static let clearConversation = ImageAsset(name: "clear_conversation")
static let contactRequestIcon = ImageAsset(name: "contact_request_icon")
static let conversationIcon = ImageAsset(name: "conversation_icon")
static let device = ImageAsset(name: "device")
......@@ -85,6 +86,7 @@ enum Asset {
backgroundRing,
blockIcon,
callButton,
clearConversation,
contactRequestIcon,
conversationIcon,
device,
......
......@@ -65,6 +65,11 @@ enum StoryboardScene {
static let initialScene = InitialSceneType<Ring.ContactRequestsViewController>(storyboard: ContactRequestsViewController.self)
}
enum ContactViewController: StoryboardType {
static let storyboardName = "ContactViewController"
static let initialScene = InitialSceneType<Ring.ContactViewController>(storyboard: ContactViewController.self)
}
enum ConversationViewController: StoryboardType {
static let storyboardName = "ConversationViewController"
......
......@@ -53,6 +53,8 @@ enum L10n {
static let confirmBlockContactTitle = L10n.tr("Localizable", "alerts.confirmBlockContactTitle")
/// Are you sure you want to delete this conversation permanently?
static let confirmDeleteConversation = L10n.tr("Localizable", "alerts.confirmDeleteConversation")
/// Are you sure you want to delete the conversation with this contact?
static let confirmDeleteConversationFromContact = L10n.tr("Localizable", "alerts.confirmDeleteConversationFromContact")
/// Delete Conversation
static let confirmDeleteConversationTitle = L10n.tr("Localizable", "alerts.confirmDeleteConversationTitle")
/// Please close application and try to open it again
......@@ -86,6 +88,19 @@ enum L10n {
static let unknown = L10n.tr("Localizable", "calls.unknown")
}
enum Contactpage {
/// Block Contact
static let blockContact = L10n.tr("Localizable", "contactPage.blockContact")
/// Clear Chat
static let clearConversation = L10n.tr("Localizable", "contactPage.clearConversation")
/// Send Message
static let sendMessage = L10n.tr("Localizable", "contactPage.sendMessage")
/// Start Audio Call
static let startAudioCall = L10n.tr("Localizable", "contactPage.startAudioCall")
/// Start Video Call
static let startVideoCall = L10n.tr("Localizable", "contactPage.startVideoCall")
}
enum Createaccount {
/// Choose strong password you will remember to protect your Ring account.
static let chooseStrongPassword = L10n.tr("Localizable", "createAccount.chooseStrongPassword")
......
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="4oO-a5-k5Y">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Contact View Controller-->
<scene sceneID="ufa-hq-gXE">
<objects>
<viewController id="4oO-a5-k5Y" customClass="ContactViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="l1v-RV-Jo2">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="150" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Cjc-Dl-AsW">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="default" indentationWidth="0.0" reuseIdentifier="ProfileInfoCell" rowHeight="60" id="fHK-Bg-4wI">
<rect key="frame" x="0.0" y="28" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="fHK-Bg-4wI" id="Eoe-sh-EbE">
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="CcM-C0-9Qc">
<rect key="frame" x="0.0" y="59" width="375" height="1"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="Wce-19-zkv"/>
</constraints>
</view>
</subviews>
</tableViewCellContentView>
<constraints>
<constraint firstItem="CcM-C0-9Qc" firstAttribute="leading" secondItem="fHK-Bg-4wI" secondAttribute="leading" id="5vW-Qy-CMV"/>
<constraint firstAttribute="bottom" secondItem="CcM-C0-9Qc" secondAttribute="bottom" id="ED4-rl-PxK"/>
<constraint firstAttribute="trailing" secondItem="CcM-C0-9Qc" secondAttribute="trailing" id="XB6-gd-kTb"/>
</constraints>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="Cjc-Dl-AsW" firstAttribute="top" secondItem="l1v-RV-Jo2" secondAttribute="top" id="FCK-2Z-zav"/>
<constraint firstItem="Cjc-Dl-AsW" firstAttribute="leading" secondItem="cTQ-BN-ANe" secondAttribute="leading" id="JuN-WT-uvv"/>
<constraint firstItem="cTQ-BN-ANe" firstAttribute="trailing" secondItem="Cjc-Dl-AsW" secondAttribute="trailing" id="PIn-ZO-UAi"/>
<constraint firstItem="Cjc-Dl-AsW" firstAttribute="bottom" secondItem="cTQ-BN-ANe" secondAttribute="bottom" id="YSy-1B-Cde"/>
</constraints>
<viewLayoutGuide key="safeArea" id="cTQ-BN-ANe"/>
</view>
<connections>
<outlet property="tableView" destination="Cjc-Dl-AsW" id="NB3-A4-6sy"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="T10-Oq-bRM" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="132" y="-87.706146926536732"/>
</scene>
</scenes>
</document>
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import Reusable
import RxSwift
import RxCocoa
import RxDataSources
import GSKStretchyHeaderView
class ContactViewController: UIViewController, StoryboardBased, ViewModelBased {
var viewModel: ContactViewModel!
@IBOutlet private weak var tableView: UITableView!
private let disposeBag = DisposeBag()
private let cellIdentifier = "ProfileInfoCell"
private var stretchyHeader: ProfileHeaderView!
private let titleView = TitleView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
self.addHeaderView()
self.setUpTableView()
navigationItem.titleView = titleView
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.statusBarStyle = .default
self.navigationController?.navigationBar.layer.shadowColor = UIColor.clear.cgColor
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
}
private func addHeaderView() {
guard let nibViews = Bundle.main.loadNibNamed("ProfileHeaderView",
owner: self,
options: nil) else {
return
}
guard let headerView = nibViews.first as? ProfileHeaderView else {
return
}
self.stretchyHeader = headerView
self.tableView.addSubview(self.stretchyHeader)
self.tableView.delegate = self
self.configureHeaderViewBinding()
}
private func configureHeaderViewBinding() {
// avatar
Observable<(Data?, String)>.combineLatest(self.viewModel.profileImageData.asObservable(),
self.viewModel.userName.asObservable()) { profileImage, username in
return (profileImage, username)
}
.observeOn(MainScheduler.instance)
.startWith((self.viewModel.profileImageData.value, self.viewModel.userName.value))
.subscribe({ [weak self] profileData -> Void in
self?.stretchyHeader.avatarView?.subviews.forEach({ $0.removeFromSuperview() })
self?.stretchyHeader.avatarView?.addSubview(AvatarView(profileImageData: profileData.element?.0,
username: (profileData.element?.1)!,
size: 100))
self?.titleView.avatarImage = AvatarView(profileImageData: profileData.element?.0,
username: (profileData.element?.1)!,
size: 36)
return
})
.disposed(by: self.disposeBag)
self.viewModel.userName.asDriver()
.drive(self.stretchyHeader.userName.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.displayName.asDriver()
.drive(self.stretchyHeader.displayName.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.titleName
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] name in
self?.titleView.text = name
}).disposed(by: self.disposeBag)
}
private func setUpTableView() {
self.tableView.rowHeight = 60.0
let configureCell: (TableViewSectionedDataSource, UITableView, IndexPath, SectionModel<String, ContactActions>.Item)
-> UITableViewCell = {
(dataSource: TableViewSectionedDataSource<SectionModel<String, ContactActions>>,
tableView: UITableView,
indexPath: IndexPath,
conversationItem: SectionModel<String, ContactActions>.Item) in
let model = dataSource.sectionModels
if model[indexPath.section].model == self.cellIdentifier {
let cell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier)
let image = UIImage(asset: conversationItem.image)
let tintedImage = image?.withRenderingMode(.alwaysTemplate)
cell?.imageView?.image = tintedImage
cell?.imageView?.tintColor = UIColor.ringSecondary
cell?.textLabel?.text = conversationItem.title
return cell!
}
return UITableViewCell()
}
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String,
ContactActions>>(configureCell: configureCell)
self.viewModel.tableSection
.observeOn(MainScheduler.instance)
.bind(to: self.tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
self.tableView.rx.itemSelected
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] indexPath in
if self?.tableView.cellForRow(at: indexPath) != nil {
switch indexPath.row {
case 0:
self?.viewModel.startAudioCall()
case 1:
self?.viewModel.startCall()
case 2:
_ = self?.navigationController?.popViewController(animated: false)
case 3:
self?.showDeleteConversationConfirmation()
case 4:
self?.showBlockContactConfirmation()
default:
break
}
self?.tableView.deselectRow(at: indexPath, animated: true)
}
}).disposed(by: self.disposeBag)
}
private func showDeleteConversationConfirmation() {
let alert = UIAlertController(title: L10n.Alerts.confirmDeleteConversationTitle, message: L10n.Alerts.confirmDeleteConversationFromContact, preferredStyle: .alert)
let deleteAction = UIAlertAction(title: L10n.Actions.deleteAction, style: .destructive) { [weak self](_: UIAlertAction!) -> Void in
self?.viewModel.deleteConversation()
}
let cancelAction = UIAlertAction(title: L10n.Actions.cancelAction, style: .default) { (_: UIAlertAction!) -> Void in }
alert.addAction(deleteAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
private func showBlockContactConfirmation() {
let alert = UIAlertController(title: L10n.Alerts.confirmBlockContactTitle, message: L10n.Alerts.confirmBlockContact, preferredStyle: .alert)
let blockAction = UIAlertAction(title: L10n.Actions.blockAction, style: .destructive) { [weak self] (_: UIAlertAction!) -> Void in
self?.viewModel.blockContact()
_ = self?.navigationController?.popToRootViewController(animated: false)
}
let cancelAction = UIAlertAction(title: L10n.Actions.cancelAction, style: .default) { (_: UIAlertAction!) -> Void in }
alert.addAction(blockAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}
}
extension ContactViewController: UITableViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let navigationHeight = self.navigationController?.navigationBar.bounds.height
var size = self.view.bounds.size
var titlViewThreshold: CGFloat = 0
if let height = navigationHeight {
size.height -= (height - 10)
titlViewThreshold = height
}
if scrollView.contentSize.height < size.height {
scrollView.contentSize = size
}
guard let titleView = navigationItem.titleView as? TitleView else { return }
titleView.scrollViewDidScroll(scrollView, threshold: titlViewThreshold)
}
}
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
*
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
import Reusable
import RxSwift
import RxCocoa
import RxDataSources
struct ContactActions {
let title: String
let image: ImageAsset
}
class ContactViewModel: ViewModel, Stateable {
private let disposeBag = DisposeBag()
// MARK: - Rx Stateable
private let stateSubject = PublishSubject<State>()
lazy var state: Observable<State> = {
return self.stateSubject.asObservable()
}()
private let contactService: ContactsService
private let profileService: ProfilesService
private let accountService: AccountsService
private let conversationService: ConversationsService
let tableSection = Observable<[SectionModel<String, ContactActions>]>
.just([SectionModel(model: "ProfileInfoCell",
items:
[ ContactActions(title: L10n.Contactpage.startAudioCall, image: Asset.callButton),
ContactActions(title: L10n.Contactpage.startVideoCall, image: Asset.videoRunning),
ContactActions(title: L10n.Contactpage.sendMessage, image: Asset.conversationIcon),
ContactActions(title: L10n.Contactpage.clearConversation, image: Asset.clearConversation),
ContactActions(title: L10n.Contactpage.blockContact, image: Asset.blockIcon)])])
var conversation: ConversationModel! {
didSet {
self.userName.value = conversation.recipientRingId
if let profile = conversation.participantProfile, let alias = profile.alias, !alias.isEmpty {
self.displayName.value = alias
}
if let contact = self.contactService.contact(withRingId: conversation.recipientRingId),
let name = contact.userName {
self.userName.value = name
}
self.contactService
.getContactRequestVCard(forContactWithRingId: conversation.recipientRingId)
.subscribe(onSuccess: { [unowned self] vCard in
if !VCardUtils.getName(from: vCard).isEmpty {