Messages: Add messages screen to read messages and reply

Add MVVM, services and adpater to receive and reply to messages

This allows to read received messages from other users and reply to
them.

The messages screen uses a TableView that scroll automatically to the
bottom if the user a new message is received. Messages bubbles are
made with simple cells with a label that move to the right or the
left.

Change-Id: I1b6bd6fd36b88daab9d39cb5745d1ed953868ead
diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj
index 2777f22..198d149 100644
--- a/Ring/Ring.xcodeproj/project.pbxproj
+++ b/Ring/Ring.xcodeproj/project.pbxproj
@@ -40,9 +40,7 @@
 		0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0438663A1D2313B700E06CE2 /* AccountDetailsViewController.swift */; };
 		043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 043999F61D1C2D9D00E99CD9 /* AppDelegate.swift */; };
 		043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 043999F81D1C2D9D00E99CD9 /* Ring.xcdatamodeld */; };
-		04399A011D1C2D9D00E99CD9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 043999FF1D1C2D9D00E99CD9 /* Main.storyboard */; };
 		04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 04399A021D1C2D9D00E99CD9 /* Assets.xcassets */; };
-		04399A061D1C2D9D00E99CD9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 04399A041D1C2D9D00E99CD9 /* LaunchScreen.storyboard */; };
 		04399A111D1C2D9D00E99CD9 /* RingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A101D1C2D9D00E99CD9 /* RingTests.swift */; };
 		04399A1C1D1C2D9D00E99CD9 /* RingUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04399A1B1D1C2D9D00E99CD9 /* RingUITests.swift */; };
 		04399A2A1D1C2DE300E99CD9 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 04399A291D1C2DE300E99CD9 /* CoreMedia.framework */; };
@@ -108,6 +106,7 @@
 		5557FD4E1E81B1F20043E394 /* AccountModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5516C29E1E71CEFF009D3D2D /* AccountModelHelper.swift */; };
 		5557FD4F1E81B2990043E394 /* AccountCredentialsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02DD80C91E1EAF1A009A3510 /* AccountCredentialsModel.swift */; };
 		557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 557086511E8ADB9D001A7CE4 /* SystemAdapter.mm */; };
+		562FB6CD1EFAD18A00C61A78 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 562FB6CC1EFAD18A00C61A78 /* ConversationViewController.swift */; };
 		56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56308BA61EA00E5700660275 /* NameRegistrationResponse.m */; };
 		563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 563AEC761EA664C0003A5641 /* RegistrationResponse.m */; };
 		564C44591E8D7F8F000F92B1 /* LocalizedStringTableNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */; };
@@ -122,7 +121,6 @@
 		5669A8031EAA58E6003C7B93 /* LinkDeviceToAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5669A8021EAA58E6003C7B93 /* LinkDeviceToAccountViewController.swift */; };
 		568F56751EA7E5DE00132D7D /* PKHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 568F56721EA7E38F00132D7D /* PKHUD.framework */; };
 		56AC64D51E7C7F4000EA1AA9 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64D41E7C7F4000EA1AA9 /* WelcomeViewController.swift */; };
-		56AC64D91E8012CA00EA1AA9 /* Walkthrough.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */; };
 		56AC64DF1E804ECC00EA1AA9 /* SwitchCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64DE1E804ECC00EA1AA9 /* SwitchCell.swift */; };
 		56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */; };
 		56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E21E805F0200EA1AA9 /* TextCell.swift */; };
@@ -141,9 +139,16 @@
 		56BBC9BA1ED715FE00CDAF8B /* ContactHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9B81ED715FE00CDAF8B /* ContactHelper.swift */; };
 		56BBC9BC1ED7161200CDAF8B /* Date+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9BB1ED7161200CDAF8B /* Date+Helpers.swift */; };
 		56BBC9BF1ED7168400CDAF8B /* SmartlistViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9BE1ED7168400CDAF8B /* SmartlistViewModel.swift */; };
-		56BBC9C51ED8BF3300CDAF8B /* libargon2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56BBC9C41ED8BF3300CDAF8B /* libargon2.a */; };
-		56BBC9DB1EDDC7F700CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9D91EDDC0B400CDAF8B /* LookupNameResponse.m */; };
-		56BBC9DC1EDDC82600CDAF8B /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */; };
+		56BBC9CD1EDC5E7000CDAF8B /* MessageAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9C71EDC5E7000CDAF8B /* MessageAccessoryView.swift */; };
+		56BBC9CE1EDC5E7000CDAF8B /* MessageAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9C81EDC5E7000CDAF8B /* MessageAccessoryView.xib */; };
+		56BBC9CF1EDC5E7000CDAF8B /* MessageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9C91EDC5E7000CDAF8B /* MessageCell.swift */; };
+		56BBC9D01EDC5E7000CDAF8B /* MessageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9CA1EDC5E7000CDAF8B /* MessageCell.xib */; };
+		56BBC9D21EDC5E7000CDAF8B /* MessageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9CC1EDC5E7000CDAF8B /* MessageViewModel.swift */; };
+		56BBC9D41EDC7A6D00CDAF8B /* libargon2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 56BBC9D31EDC7A6D00CDAF8B /* libargon2.a */; };
+		56BBC9D51EDCA85900CDAF8B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 043999FF1D1C2D9D00E99CD9 /* Main.storyboard */; };
+		56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */; };
+		56BBC9E01EDDC9E600CDAF8B /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */; };
+		56BBC9E11EDDCA5900CDAF8B /* Walkthrough.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -275,6 +280,7 @@
 		5557FD491E81AE850043E394 /* AccountModelHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountModelHelperTests.swift; sourceTree = "<group>"; };
 		557086501E8ADB9D001A7CE4 /* SystemAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SystemAdapter.h; path = Bridging/SystemAdapter.h; sourceTree = "<group>"; };
 		557086511E8ADB9D001A7CE4 /* SystemAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SystemAdapter.mm; path = Bridging/SystemAdapter.mm; sourceTree = "<group>"; };
+		562FB6CC1EFAD18A00C61A78 /* ConversationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationViewController.swift; sourceTree = "<group>"; };
 		56308BA51EA00E5700660275 /* NameRegistrationResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NameRegistrationResponse.h; sourceTree = "<group>"; };
 		56308BA61EA00E5700660275 /* NameRegistrationResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NameRegistrationResponse.m; sourceTree = "<group>"; };
 		563AEC751EA664C0003A5641 /* RegistrationResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegistrationResponse.h; sourceTree = "<group>"; };
@@ -313,9 +319,14 @@
 		56BBC9B81ED715FE00CDAF8B /* ContactHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactHelper.swift; sourceTree = "<group>"; };
 		56BBC9BB1ED7161200CDAF8B /* Date+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Date+Helpers.swift"; path = "Extensions/Date+Helpers.swift"; sourceTree = "<group>"; };
 		56BBC9BE1ED7168400CDAF8B /* SmartlistViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmartlistViewModel.swift; sourceTree = "<group>"; };
-		56BBC9C41ED8BF3300CDAF8B /* libargon2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libargon2.a; path = ../fat/lib/libargon2.a; sourceTree = "<group>"; };
-		56BBC9D81EDDC0B400CDAF8B /* LookupNameResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LookupNameResponse.h; sourceTree = "<group>"; };
-		56BBC9D91EDDC0B400CDAF8B /* LookupNameResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LookupNameResponse.m; sourceTree = "<group>"; };
+		56BBC9C71EDC5E7000CDAF8B /* MessageAccessoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageAccessoryView.swift; sourceTree = "<group>"; };
+		56BBC9C81EDC5E7000CDAF8B /* MessageAccessoryView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageAccessoryView.xib; sourceTree = "<group>"; };
+		56BBC9C91EDC5E7000CDAF8B /* MessageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageCell.swift; sourceTree = "<group>"; };
+		56BBC9CA1EDC5E7000CDAF8B /* MessageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MessageCell.xib; sourceTree = "<group>"; };
+		56BBC9CC1EDC5E7000CDAF8B /* MessageViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageViewModel.swift; sourceTree = "<group>"; };
+		56BBC9D31EDC7A6D00CDAF8B /* libargon2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libargon2.a; path = ../fat/lib/libargon2.a; sourceTree = "<group>"; };
+		56BBC9DD1EDDC9D300CDAF8B /* LookupNameResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LookupNameResponse.h; sourceTree = "<group>"; };
+		56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LookupNameResponse.m; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -323,7 +334,7 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				56BBC9C51ED8BF3300CDAF8B /* libargon2.a in Frameworks */,
+				56BBC9D41EDC7A6D00CDAF8B /* libargon2.a in Frameworks */,
 				568F56751EA7E5DE00132D7D /* PKHUD.framework in Frameworks */,
 				02674C851E0C757B0065EDF9 /* RxCocoa.framework in Frameworks */,
 				02674C861E0C757B0065EDF9 /* RxSwift.framework in Frameworks */,
@@ -437,7 +448,7 @@
 		02AED8171DD4C4B000F740BA /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
-				56BBC9C41ED8BF3300CDAF8B /* libargon2.a */,
+				56BBC9D31EDC7A6D00CDAF8B /* libargon2.a */,
 				568F56721EA7E38F00132D7D /* PKHUD.framework */,
 				02674C801E0C757B0065EDF9 /* RxBlocking.framework */,
 				02674C811E0C757B0065EDF9 /* RxCocoa.framework */,
@@ -687,8 +698,8 @@
 		563AEC731EA6627F003A5641 /* NameRegistration */ = {
 			isa = PBXGroup;
 			children = (
-				56BBC9D81EDDC0B400CDAF8B /* LookupNameResponse.h */,
-				56BBC9D91EDDC0B400CDAF8B /* LookupNameResponse.m */,
+				56BBC9DD1EDDC9D300CDAF8B /* LookupNameResponse.h */,
+				56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */,
 				564C445E1E943C37000F92B1 /* NameRegistrationAdapter.h */,
 				564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */,
 				56308BA51EA00E5700660275 /* NameRegistrationResponse.h */,
@@ -744,6 +755,11 @@
 		56BBC9A41ED7150200CDAF8B /* Messages */ = {
 			isa = PBXGroup;
 			children = (
+				56BBC9C71EDC5E7000CDAF8B /* MessageAccessoryView.swift */,
+				56BBC9C81EDC5E7000CDAF8B /* MessageAccessoryView.xib */,
+				56BBC9C91EDC5E7000CDAF8B /* MessageCell.swift */,
+				56BBC9CA1EDC5E7000CDAF8B /* MessageCell.xib */,
+				56BBC9CC1EDC5E7000CDAF8B /* MessageViewModel.swift */,
 				56BBC9A51ED7151500CDAF8B /* MessageModel.swift */,
 			);
 			name = Messages;
@@ -752,6 +768,7 @@
 		56BBC9AD1ED7154800CDAF8B /* Conversations */ = {
 			isa = PBXGroup;
 			children = (
+				562FB6CC1EFAD18A00C61A78 /* ConversationViewController.swift */,
 				56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */,
 				56BBC9B31ED7156500CDAF8B /* ConversationCell.xib */,
 				56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */,
@@ -849,6 +866,11 @@
 						CreatedOnToolsVersion = 7.3.1;
 						DevelopmentTeam = KM95526DS8;
 						LastSwiftMigration = 0810;
+						SystemCapabilities = {
+							com.apple.BackgroundModes = {
+								enabled = 0;
+							};
+						};
 					};
 					04399A0B1D1C2D9D00E99CD9 = {
 						CreatedOnToolsVersion = 7.3.1;
@@ -887,17 +909,18 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				56AC64D91E8012CA00EA1AA9 /* Walkthrough.strings in Resources */,
 				02B22E031DF755F7000358C9 /* WalkthroughStoryboard.storyboard in Resources */,
 				5669A7FE1EA904E4003C7B93 /* TextCell.xib in Resources */,
-				04399A061D1C2D9D00E99CD9 /* LaunchScreen.storyboard in Resources */,
 				56BBC9AC1ED7154300CDAF8B /* Smartlist.strings in Resources */,
 				56BBC9AA1ED7153800CDAF8B /* Global.strings in Resources */,
+				56BBC9CE1EDC5E7000CDAF8B /* MessageAccessoryView.xib in Resources */,
+				56BBC9D51EDCA85900CDAF8B /* Main.storyboard in Resources */,
 				04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */,
-				04399A011D1C2D9D00E99CD9 /* Main.storyboard in Resources */,
+				56BBC9D01EDC5E7000CDAF8B /* MessageCell.xib in Resources */,
 				5669A7FA1EA904AF003C7B93 /* SwitchCell.xib in Resources */,
 				56BBC9B51ED7156500CDAF8B /* ConversationCell.xib in Resources */,
 				5669A7FC1EA904D2003C7B93 /* TextFieldCell.xib in Resources */,
+				56BBC9E11EDDCA5900CDAF8B /* Walkthrough.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -941,7 +964,6 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				56BBC9DC1EDDC82600CDAF8B /* ConversationViewModel.swift in Sources */,
 				557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */,
 				5669A8031EAA58E6003C7B93 /* LinkDeviceToAccountViewController.swift in Sources */,
 				0273C3051E0C68B100CF00BA /* CreateProfileViewController.swift in Sources */,
@@ -951,7 +973,9 @@
 				02B22E091DF7585F000358C9 /* DaemonService.swift in Sources */,
 				0273C3061E0C68B100CF00BA /* CreateRingAccountViewController.swift in Sources */,
 				56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */,
+				56BBC9CF1EDC5E7000CDAF8B /* MessageCell.swift in Sources */,
 				02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */,
+				56BBC9D21EDC5E7000CDAF8B /* MessageViewModel.swift in Sources */,
 				02DD80CD1E1EB2E4009A3510 /* ConfigKeyModel.swift in Sources */,
 				56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */,
 				5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */,
@@ -961,7 +985,6 @@
 				56AC650E1E85694D00EA1AA9 /* RoundedTextField.swift in Sources */,
 				56BBC9BF1ED7168400CDAF8B /* SmartlistViewModel.swift in Sources */,
 				02B22E011DF755E5000358C9 /* MainTabBarViewController.swift in Sources */,
-				56BBC9DB1EDDC7F700CDAF8B /* LookupNameResponse.m in Sources */,
 				564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */,
 				043999F71D1C2D9D00E99CD9 /* AppDelegate.swift in Sources */,
 				02B22DFC1DF755BB000358C9 /* AccountModel.swift in Sources */,
@@ -977,11 +1000,15 @@
 				043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */,
 				564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */,
 				0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */,
+				56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */,
+				56BBC9CD1EDC5E7000CDAF8B /* MessageAccessoryView.swift in Sources */,
 				02DD80CA1E1EAF1A009A3510 /* AccountCredentialsModel.swift in Sources */,
 				56559B171EEED50D00BF20E1 /* Colors.swift in Sources */,
 				0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */,
 				56BBC9BC1ED7161200CDAF8B /* Date+Helpers.swift in Sources */,
+				562FB6CD1EFAD18A00C61A78 /* ConversationViewController.swift in Sources */,
 				564C44621E943DE6000F92B1 /* NameService.swift in Sources */,
+				56BBC9E01EDDC9E600CDAF8B /* ConversationViewModel.swift in Sources */,
 				043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */,
 				04399AAD1D1C304300E99CD9 /* DRingAdapter.mm in Sources */,
 				0273C2FF1E0C438F00CF00BA /* AccountAdapterDelegate.swift in Sources */,
diff --git a/Ring/Ring/Account/AccountModelHelper.swift b/Ring/Ring/Account/AccountModelHelper.swift
index f64dcb6..202bdeb 100644
--- a/Ring/Ring/Account/AccountModelHelper.swift
+++ b/Ring/Ring/Account/AccountModelHelper.swift
@@ -120,4 +120,18 @@
         }
         return self.account
     }
+
+    var ringId :String? {
+
+        let accountUsernameKey = ConfigKeyModel(withKey: ConfigKey.AccountUsername)
+        let accountUsername = self.account.details.get(withConfigKeyModel: accountUsernameKey)
+
+        let ringIdPrefix = "ring:"
+        if accountUsername.contains(ringIdPrefix) {
+            let index = accountUsername.range(of: ringIdPrefix)?.upperBound
+            return accountUsername.substring(from: index!)
+        } else {
+            return nil
+        }
+    }
 }
diff --git a/Ring/Ring/Base.lproj/Main.storyboard b/Ring/Ring/Base.lproj/Main.storyboard
index e764877..a736386 100644
--- a/Ring/Ring/Base.lproj/Main.storyboard
+++ b/Ring/Ring/Base.lproj/Main.storyboard
@@ -248,12 +248,76 @@
                     <navigationItem key="navigationItem" id="b8m-eG-Q9D"/>
                     <connections>
                         <outlet property="tableView" destination="B6Y-MZ-L7L" id="dXp-J4-x68"/>
+                        <segue destination="Qlv-cA-wRT" kind="show" identifier="ShowMessages" id="X75-kM-dPZ"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="rzQ-ll-5bo" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
             <point key="canvasLocation" x="-97.5" y="-1177.8169014084508"/>
         </scene>
+        <!--Conversation View Controller-->
+        <scene sceneID="N9T-Vl-P5n">
+            <objects>
+                <viewController hidesBottomBarWhenPushed="YES" id="Qlv-cA-wRT" customClass="ConversationViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="wEb-Zj-bvJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="S9d-I1-nWj"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" misplaced="YES" id="jPi-CC-dFO">
+                        <rect key="frame" x="0.0" y="64" width="320" height="455"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="M1r-X5-oFv">
+                                <rect key="frame" x="0.0" y="0.0" width="320" height="455"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <prototypes>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MessageCellId" id="8rX-Qa-Ypu">
+                                        <rect key="frame" x="0.0" y="28" width="320" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8rX-Qa-Ypu" id="7vA-nx-B3h">
+                                            <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                        </tableViewCellContentView>
+                                    </tableViewCell>
+                                </prototypes>
+                            </tableView>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="PYu-2x-JXM">
+                                <rect key="frame" x="0.0" y="0.0" width="320" height="455"/>
+                                <subviews>
+                                    <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="oH9-4M-JpG">
+                                        <rect key="frame" x="142" y="209" width="37" height="37"/>
+                                        <color key="color" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
+                                    </activityIndicatorView>
+                                </subviews>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                <constraints>
+                                    <constraint firstItem="oH9-4M-JpG" firstAttribute="centerY" secondItem="PYu-2x-JXM" secondAttribute="centerY" id="105-id-Oqu"/>
+                                    <constraint firstItem="oH9-4M-JpG" firstAttribute="centerX" secondItem="PYu-2x-JXM" secondAttribute="centerX" id="Fk6-H6-Vdq"/>
+                                </constraints>
+                            </view>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstAttribute="trailing" secondItem="M1r-X5-oFv" secondAttribute="trailing" id="4Dh-nK-p84"/>
+                            <constraint firstItem="M1r-X5-oFv" firstAttribute="top" secondItem="jPi-CC-dFO" secondAttribute="top" id="BFo-2E-BaG"/>
+                            <constraint firstAttribute="bottom" secondItem="M1r-X5-oFv" secondAttribute="bottom" id="PoM-IX-CFt"/>
+                            <constraint firstAttribute="trailing" secondItem="PYu-2x-JXM" secondAttribute="trailing" id="X7H-Er-Udk"/>
+                            <constraint firstItem="PYu-2x-JXM" firstAttribute="leading" secondItem="jPi-CC-dFO" secondAttribute="leading" id="dQk-7P-cld"/>
+                            <constraint firstItem="PYu-2x-JXM" firstAttribute="top" secondItem="jPi-CC-dFO" secondAttribute="top" id="g9s-sd-GJC"/>
+                            <constraint firstItem="M1r-X5-oFv" firstAttribute="leading" secondItem="jPi-CC-dFO" secondAttribute="leading" id="i0E-9E-6co"/>
+                            <constraint firstAttribute="bottom" secondItem="PYu-2x-JXM" secondAttribute="bottom" id="j3b-gd-iBK"/>
+                        </constraints>
+                    </view>
+                    <extendedEdge key="edgesForExtendedLayout"/>
+                    <connections>
+                        <outlet property="spinnerView" destination="PYu-2x-JXM" id="hxZ-lU-3XC"/>
+                        <outlet property="tableView" destination="M1r-X5-oFv" id="5Lh-Iu-nQb"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="bv6-qf-2Pa" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="844" y="-1179"/>
+        </scene>
         <!--Ring-->
         <scene sceneID="oqo-zJ-m0o">
             <objects>
diff --git a/Ring/Ring/ConversationCell.xib b/Ring/Ring/ConversationCell.xib
index c48708b..2125486 100644
--- a/Ring/Ring/ConversationCell.xib
+++ b/Ring/Ring/ConversationCell.xib
@@ -80,7 +80,7 @@
                 <constraints>
                     <constraint firstItem="2fJ-Wf-1e0" firstAttribute="leading" secondItem="pFB-Jn-TNP" secondAttribute="trailing" constant="4" id="2NV-6m-dri"/>
                     <constraint firstItem="eug-ak-r49" firstAttribute="leading" secondItem="pFB-Jn-TNP" secondAttribute="trailing" constant="4" id="9ah-Ed-RlY"/>
-                    <constraint firstItem="pFB-Jn-TNP" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="9mO-5E-3lA"/>
+                   <constraint firstItem="pFB-Jn-TNP" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="9mO-5E-3lA"/>
                     <constraint firstItem="7Yv-cC-LKx" firstAttribute="leading" secondItem="2fJ-Wf-1e0" secondAttribute="trailing" constant="4" id="BzU-Ya-2ME"/>
                     <constraint firstAttribute="trailing" secondItem="eug-ak-r49" secondAttribute="trailing" constant="16" id="ITl-14-BeZ"/>
                     <constraint firstItem="JTE-eF-Y5s" firstAttribute="trailing" secondItem="pFB-Jn-TNP" secondAttribute="trailing" id="MgK-cd-QXM"/>
diff --git a/Ring/Ring/ConversationViewController.swift b/Ring/Ring/ConversationViewController.swift
new file mode 100644
index 0000000..22638ed
--- /dev/null
+++ b/Ring/Ring/ConversationViewController.swift
@@ -0,0 +1,168 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+import UIKit
+import RxSwift
+
+class ConversationViewController: UIViewController, UITextFieldDelegate {
+
+    let disposeBag = DisposeBag()
+
+    var viewModel: ConversationViewModel?
+    var textFieldShouldEndEditing = false
+    var bottomOffset :CGFloat = 0
+
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var spinnerView: UIView!
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        self.setupUI()
+        self.setupTableView()
+        self.setupBindings()
+
+        self.messageAccessoryView.messageTextField.delegate = self
+
+        /*
+         Register to keyboard notifications to adjust tableView insets when the keybaord appears
+         or disappears
+         */
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(withNotification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(withNotification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
+    }
+
+    func keyboardWillShow(withNotification notification: Notification) {
+
+        let userInfo: Dictionary = notification.userInfo!
+        let keyboardFrame: NSValue = userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue
+        let keyboardRectangle = keyboardFrame.cgRectValue
+        let keyboardHeight = keyboardRectangle.height
+
+        self.tableView.contentInset.bottom = keyboardHeight
+        self.tableView.scrollIndicatorInsets.bottom = keyboardHeight
+
+        self.scrollToBottom(animated: true)
+        self.updateBottomOffset()
+    }
+
+    func keyboardWillHide(withNotification notification: Notification) {
+        self.tableView.contentInset.bottom = 0
+        self.tableView.scrollIndicatorInsets.bottom = 0
+        self.updateBottomOffset()
+    }
+
+    func setupUI() {
+        self.viewModel?.userName.bind(to: self.navigationItem.rx.title).addDisposableTo(disposeBag)
+
+        self.tableView.contentInset.bottom = messageAccessoryView.frame.size.height
+        self.tableView.scrollIndicatorInsets.bottom = messageAccessoryView.frame.size.height
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        self.scrollToBottom(animated: false)
+        self.messagesLoadingFinished()
+    }
+
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+
+        self.textFieldShouldEndEditing = true
+        self.viewModel?.setMessagesAsRead()
+    }
+
+    func setupTableView() {
+        self.tableView.estimatedRowHeight = 50
+        self.tableView.rowHeight = UITableViewAutomaticDimension
+        self.tableView.separatorStyle = .none
+
+        //Register cell
+        self.tableView.register(UINib.init(nibName: "MessageCell", bundle: nil),
+                                forCellReuseIdentifier: "MessageCellId")
+
+        //Bind the TableView to the ViewModel
+        self.viewModel?.messages.bind(to: tableView.rx.items(cellIdentifier: "MessageCellId", cellType: MessageCell.self))
+            { index, messageViewModel, cell in
+                cell.messageLabel.text = messageViewModel.content
+                cell.bubblePosition = messageViewModel.bubblePosition()
+            }.addDisposableTo(disposeBag)
+
+        //Scroll to bottom when reloaded
+        self.tableView.rx.methodInvoked(#selector(UITableView.reloadData)).subscribe(onNext: { element in
+            self.scrollToBottomIfNeed()
+            self.updateBottomOffset()
+        }).addDisposableTo(disposeBag)
+    }
+
+    fileprivate func updateBottomOffset() {
+        self.bottomOffset = self.tableView.contentSize.height
+            - ( self.tableView.frame.size.height
+                - self.tableView.contentInset.top
+                - self.tableView.contentInset.bottom )
+    }
+
+    fileprivate func messagesLoadingFinished() {
+        self.spinnerView.isHidden = true
+    }
+
+    fileprivate func scrollToBottomIfNeed() {
+        if self.isBottomContentOffset {
+            self.scrollToBottom(animated: true)
+        }
+    }
+
+    fileprivate func scrollToBottom(animated: Bool) {
+        let last = IndexPath(row: self.tableView.numberOfRows(inSection: 0) - 1, section: 0)
+        self.tableView.scrollToRow(at: last, at: .bottom, animated: animated)
+    }
+
+    fileprivate var isBottomContentOffset: Bool {
+        return self.tableView.contentOffset.y + self.tableView.contentInset.top >= bottomOffset
+    }
+
+    override var inputAccessoryView: UIView {
+        return self.messageAccessoryView
+    }
+
+    override var canBecomeFirstResponder: Bool {
+        return true
+    }
+
+    lazy var messageAccessoryView: MessageAccessoryView = {
+        return MessageAccessoryView.instanceFromNib()
+    }()
+
+    func setupBindings() {
+
+        //Binds the keyboard Send button action to the ViewModel
+        self.messageAccessoryView.messageTextField.rx.controlEvent(.editingDidEndOnExit).subscribe(onNext: { event in
+            self.viewModel?.sendMessage(withContent: self.messageAccessoryView.messageTextField.text!)
+            self.messageAccessoryView.messageTextField.text = ""
+        }).addDisposableTo(disposeBag)
+    }
+
+    // Avoid the keyboard to be hidden when the Send button is touched
+    func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
+        return textFieldShouldEndEditing
+    }
+
+}
diff --git a/Ring/Ring/ConversationViewModel.swift b/Ring/Ring/ConversationViewModel.swift
index 1953703..833f8d3 100644
--- a/Ring/Ring/ConversationViewModel.swift
+++ b/Ring/Ring/ConversationViewModel.swift
@@ -34,28 +34,40 @@
 
     private let disposeBag = DisposeBag()
 
+    let messages :Observable<[MessageViewModel]>
+
+    private let conversationsService = AppDelegate.conversationsService
+    private let accountService = AppDelegate.accountService
+
     init(withConversation conversation: ConversationModel) {
         self.conversation = conversation
+
         dateFormatter.dateStyle = .medium
         hourFormatter.dateFormat = "HH:mm"
 
         self.userName = ContactHelper.lookupUserName(forRingId: self.conversation.recipient.ringId,
                                                 nameService: AppDelegate.nameService,
                                                 disposeBag: self.disposeBag).asObservable()
+
+        //Create observable from sorted conversations and flatMap them to view models
+        self.messages = self.conversationsService.conversations.asObservable().map({ conversations in
+            return conversations.filter({ currentConversation in
+                return currentConversation.recipient == conversation.recipient
+            }).flatMap({ conversation in
+                conversation.messages.map({ message in
+                    return MessageViewModel(withMessage: message)
+                })
+            })
+        }).observeOn(MainScheduler.instance)
+
     }
 
     var unreadMessages: String {
        return self.unreadMessagesCount.description
     }
 
-    fileprivate var unreadMessagesCount: Int {
-        return self.conversation.messages.filter({ message in
-            return message.status != .read
-        }).count
-    }
-
     var hasUnreadMessages: Bool {
-        return conversation.messages.count > 0
+        return unreadMessagesCount > 0
     }
 
     var lastMessage: String {
@@ -96,4 +108,43 @@
             return dateFormatter.string(from: lastMessageDate)
         }
     }
+
+    var hideNewMessagesLabel: Bool {
+        return self.unreadMessagesCount == 0
+    }
+
+    func sendMessage(withContent content: String) {
+        self.conversationsService
+            .sendMessage(withContent: content,
+                         from: accountService.currentAccount!,
+                         to: self.conversation.recipient)
+            .subscribe(onCompleted: {
+                let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!)
+                self.saveMessage(withContent: content, byAuthor: accountHelper.ringId!, toConversationWith: self.conversation.recipient.ringId)
+            }).addDisposableTo(disposeBag)
+    }
+
+    fileprivate func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) {
+        self.conversationsService
+            .saveMessage(withContent: content, byAuthor: author, toConversationWith: account)
+            .subscribe(onCompleted: {
+                print("Message saved")
+            })
+            .addDisposableTo(disposeBag)
+    }
+
+    func setMessagesAsRead() {
+        self.conversationsService
+            .setMessagesAsRead(forConversation: self.conversation)
+            .subscribe(onCompleted: {
+                print("Message set as read")
+            }).addDisposableTo(disposeBag)
+    }
+
+    fileprivate var unreadMessagesCount: Int {
+        let accountHelper = AccountModelHelper(withAccount: self.accountService.currentAccount!)
+        return self.conversation.messages.filter({ message in
+            return message.status != .read && message.author != accountHelper.ringId!
+        }).count
+    }
 }
diff --git a/Ring/Ring/ConversationsService.swift b/Ring/Ring/ConversationsService.swift
index 0d77c07..a6f7b9d 100644
--- a/Ring/Ring/ConversationsService.swift
+++ b/Ring/Ring/ConversationsService.swift
@@ -27,28 +27,34 @@
     fileprivate let disposeBag = DisposeBag()
     fileprivate let textPlainMIMEType = "text/plain"
 
-    let conversations = Variable([ConversationModel]())
+    var conversations = Variable([ConversationModel]())
 
     init(withMessageAdapter messageAdapter: MessagesAdapter) {
         self.messageAdapter = messageAdapter
         MessagesAdapter.delegate = self
+
     }
 
-    func status(forMessageId messageId: UInt64) -> MessageStatus {
-        return self.messageAdapter.status(forMessageId: messageId)
+    func sendMessage(withContent content: String, from senderAccount: AccountModel, to recipient: ContactModel) -> Completable {
+
+        return Completable.create(subscribe: { [unowned self] completable in
+            let contentDict = [self.textPlainMIMEType : content]
+            self.messageAdapter.sendMessage(withContent: contentDict, withAccountId: senderAccount.id, to: recipient.ringId)
+
+            completable(.completed)
+
+            return Disposables.create {}
+        })
     }
 
-    //MARK: Message Adapter delegate
+    func saveMessage(withContent content: String, byAuthor author: String, toConversationWith account: String) -> Completable {
 
-    func didReceiveMessage(_ message: Dictionary<String, String>, from senderAccount: String,
-                           to receiverAccountId: String) {
-
-        if let content = message[textPlainMIMEType] {
-            let message = MessageModel(withId: nil, receivedDate: Date(), content: content, author: senderAccount)
+        return Completable.create(subscribe: { [unowned self] completable in
+            let message = MessageModel(withId: nil, receivedDate: Date(), content: content, author: author)
 
             //Get conversations for this sender
-            var currentConversation = conversations.value.filter({ conversation in
-                return conversation.recipient.ringId == senderAccount
+            var currentConversation = self.conversations.value.filter({ conversation in
+                return conversation.recipient.ringId == account
             }).first
 
             //Get the current array of conversations
@@ -56,7 +62,7 @@
 
             //Create a new conversation for this sender if not exists
             if currentConversation == nil {
-                currentConversation = ConversationModel(withRecipient: ContactModel(withRingId: senderAccount), accountId: receiverAccountId)
+                currentConversation = ConversationModel(withRecipient: ContactModel(withRingId: account), accountId: author)
                 currentConversations.append(currentConversation!)
             }
 
@@ -65,6 +71,55 @@
 
             //Upate the value of the Variable
             self.conversations.value = currentConversations
+
+            completable(.completed)
+
+            return Disposables.create { }
+
+        })
+    }
+
+    func status(forMessageId messageId: UInt64) -> MessageStatus {
+        return self.messageAdapter.status(forMessageId: messageId)
+    }
+
+    func setMessagesAsRead(forConversation conversation: ConversationModel) -> Completable {
+
+        return Completable.create(subscribe: { completable in
+
+            //Get the current array of conversations
+            let currentConversations = self.conversations.value
+
+            //Filter unread messages
+            let unreadMessages = conversation.messages.filter({ messages in
+                return messages.status != .read
+            })
+
+            for message in unreadMessages {
+                message.status = .read
+            }
+
+            //Upate the value of the Variable
+            self.conversations.value = currentConversations
+
+            completable(.completed)
+
+            return Disposables.create { }
+
+        })
+    }
+
+    //MARK: Message Adapter delegate
+
+    func didReceiveMessage(_ message: Dictionary<String, String>, from senderAccount: String,
+                           to receiverAccountId: String) {
+
+        if let content = message[textPlainMIMEType] {
+            self.saveMessage(withContent: content, byAuthor: senderAccount, toConversationWith: senderAccount)
+                .subscribe(onCompleted: {
+                    print("Message saved")
+                })
+                .addDisposableTo(disposeBag)
         }
     }
 
diff --git a/Ring/Ring/Info.plist b/Ring/Ring/Info.plist
index 3af58c7..b4b4391 100644
--- a/Ring/Ring/Info.plist
+++ b/Ring/Ring/Info.plist
@@ -30,6 +30,8 @@
 	<array>
 		<string>armv7</string>
 	</array>
+	<key>UIStatusBarStyle</key>
+	<string>UIStatusBarStyleLightContent</string>
 	<key>UIStatusBarTintParameters</key>
 	<dict>
 		<key>UINavigationBar</key>
diff --git a/Ring/Ring/MessageAccessoryView.swift b/Ring/Ring/MessageAccessoryView.swift
new file mode 100644
index 0000000..3e285bc
--- /dev/null
+++ b/Ring/Ring/MessageAccessoryView.swift
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Silbino Gonçalves Matado <silbino.gmatado@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 MessageAccessoryView: UIView {
+
+    @IBOutlet weak var messageTextField: UITextField!
+
+    class func instanceFromNib() -> MessageAccessoryView {
+        return UINib(nibName: "MessageAccessoryView", bundle: nil)
+            .instantiate(withOwner: nil, options: nil).first as! MessageAccessoryView
+    }
+
+}
diff --git a/Ring/Ring/MessageAccessoryView.xib b/Ring/Ring/MessageAccessoryView.xib
new file mode 100644
index 0000000..65027b0
--- /dev/null
+++ b/Ring/Ring/MessageAccessoryView.xib
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="Fja-dy-lIy" customClass="MessageAccessoryView" customModule="Ring" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="315" height="38"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <subviews>
+                <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Write a message..." textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="AJA-0c-Rp7">
+                    <rect key="frame" x="4" y="4" width="307" height="30"/>
+                    <nil key="textColor"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
+                    <textInputTraits key="textInputTraits" returnKeyType="send" enablesReturnKeyAutomatically="YES"/>
+                </textField>
+            </subviews>
+            <color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstAttribute="bottom" secondItem="AJA-0c-Rp7" secondAttribute="bottom" constant="4" id="Gph-b4-ZJH"/>
+                <constraint firstAttribute="trailing" secondItem="AJA-0c-Rp7" secondAttribute="trailing" constant="4" id="UyK-3a-JcH"/>
+                <constraint firstItem="AJA-0c-Rp7" firstAttribute="top" secondItem="Fja-dy-lIy" secondAttribute="top" constant="4" id="mDu-cF-UdO"/>
+                <constraint firstItem="AJA-0c-Rp7" firstAttribute="leading" secondItem="Fja-dy-lIy" secondAttribute="leading" constant="4" id="tvd-4f-jdO"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <connections>
+                <outlet property="messageTextField" destination="AJA-0c-Rp7" id="ga9-yi-G2h"/>
+            </connections>
+            <point key="canvasLocation" x="-15" y="-178"/>
+        </view>
+    </objects>
+</document>
diff --git a/Ring/Ring/MessageCell.swift b/Ring/Ring/MessageCell.swift
new file mode 100644
index 0000000..7cc80f6
--- /dev/null
+++ b/Ring/Ring/MessageCell.swift
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Silbino Gonçalves Matado <silbino.gmatado@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
+
+enum BubblePosition {
+    case received
+    case sent
+}
+
+class MessageCell: UITableViewCell {
+
+    @IBOutlet weak var bubble: UIView!
+    @IBOutlet weak var messageLabel: UILabel!
+
+    @IBOutlet weak var minimumLeadingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var containerLeadingConstraint: NSLayoutConstraint!
+
+    @IBOutlet weak var minimumTrailingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var containerTrailingConstraint: NSLayoutConstraint!
+
+    var bubblePosition = BubblePosition.received {
+        didSet {
+            if bubblePosition == .sent {
+                self.minimumTrailingConstraint.priority = 1
+                self.containerTrailingConstraint.priority = 999
+                self.containerLeadingConstraint.priority = 1
+                self.minimumLeadingConstraint.priority = 999
+
+                self.bubble.backgroundColor = Colors.ringMainColor
+                self.messageLabel.textColor = UIColor.white
+            } else {
+                self.minimumLeadingConstraint.priority = 1
+                self.containerLeadingConstraint.priority = 999
+                self.containerTrailingConstraint.priority = 1
+                self.minimumTrailingConstraint.priority = 999
+
+                self.bubble.backgroundColor = UIColor.lightGray
+                self.messageLabel.textColor = UIColor.black
+            }
+        }
+    }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+        self.bubblePosition = .received
+    }
+
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        super.setSelected(selected, animated: animated)
+    }
+}
diff --git a/Ring/Ring/MessageCell.xib b/Ring/Ring/MessageCell.xib
new file mode 100644
index 0000000..0bf5445
--- /dev/null
+++ b/Ring/Ring/MessageCell.xib
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="MessageCellId" rowHeight="60" id="KGk-i7-Jjw" customClass="MessageCell" customModule="Ring" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="510" height="47"/>
+            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
+                <rect key="frame" x="0.0" y="0.0" width="510" height="46"/>
+                <autoresizingMask key="autoresizingMask"/>
+                <subviews>
+                    <view clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="kZJ-Ay-LTR">
+                        <rect key="frame" x="16" y="8" width="152.5" height="30.5"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label Label Label Label " lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lyR-7c-S2k">
+                                <rect key="frame" x="8" y="4" width="136.5" height="22.5"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                        </subviews>
+                        <color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="calibratedRGB"/>
+                        <constraints>
+                            <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="30" id="1Kj-UZ-gu7"/>
+                            <constraint firstItem="lyR-7c-S2k" firstAttribute="leading" secondItem="kZJ-Ay-LTR" secondAttribute="leading" constant="8" id="8m5-sR-xnh"/>
+                            <constraint firstAttribute="bottom" secondItem="lyR-7c-S2k" secondAttribute="bottom" constant="4" id="gwN-uX-PWd"/>
+                            <constraint firstAttribute="trailing" secondItem="lyR-7c-S2k" secondAttribute="trailing" constant="8" id="uzV-kG-oGN"/>
+                            <constraint firstItem="lyR-7c-S2k" firstAttribute="top" secondItem="kZJ-Ay-LTR" secondAttribute="top" constant="4" id="ycc-WI-Jk6"/>
+                        </constraints>
+                        <userDefinedRuntimeAttributes>
+                            <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+                                <integer key="value" value="5"/>
+                            </userDefinedRuntimeAttribute>
+                        </userDefinedRuntimeAttributes>
+                    </view>
+                </subviews>
+                <constraints>
+                    <constraint firstAttribute="bottom" secondItem="kZJ-Ay-LTR" secondAttribute="bottom" constant="8" id="1QQ-bu-6Bl"/>
+                    <constraint firstAttribute="trailing" secondItem="kZJ-Ay-LTR" secondAttribute="trailing" priority="1" constant="16" id="99Y-bR-Ioq"/>
+                    <constraint firstItem="kZJ-Ay-LTR" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="H2p-sc-9uM" secondAttribute="leading" priority="1" constant="64" id="Eso-cy-OYs"/>
+                    <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="kZJ-Ay-LTR" secondAttribute="trailing" constant="64" id="TCY-7X-mFs"/>
+                    <constraint firstItem="kZJ-Ay-LTR" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="8" id="jhd-A8-c1o"/>
+                    <constraint firstItem="kZJ-Ay-LTR" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="nWe-5k-Qpn"/>
+                </constraints>
+            </tableViewCellContentView>
+            <connections>
+                <outlet property="bubble" destination="kZJ-Ay-LTR" id="hdG-fG-L69"/>
+                <outlet property="containerLeadingConstraint" destination="nWe-5k-Qpn" id="Ahu-gW-4IY"/>
+                <outlet property="containerTrailingConstraint" destination="99Y-bR-Ioq" id="JJr-pE-UfA"/>
+                <outlet property="messageLabel" destination="lyR-7c-S2k" id="hd3-pz-Pwh"/>
+                <outlet property="minimumLeadingConstraint" destination="Eso-cy-OYs" id="jhV-mE-FMA"/>
+                <outlet property="minimumTrailingConstraint" destination="TCY-7X-mFs" id="xj3-4A-I9G"/>
+            </connections>
+            <point key="canvasLocation" x="-411" y="-132"/>
+        </tableViewCell>
+    </objects>
+</document>
diff --git a/Ring/Ring/MessageViewModel.swift b/Ring/Ring/MessageViewModel.swift
new file mode 100644
index 0000000..ea74b87
--- /dev/null
+++ b/Ring/Ring/MessageViewModel.swift
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Silbino Gonçalves Matado <silbino.gmatado@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 RxSwift
+
+class MessageViewModel {
+
+    fileprivate let accountService = AppDelegate.accountService
+    fileprivate var message :MessageModel
+
+    init(withMessage message: MessageModel) {
+        self.message = message
+    }
+
+    var content: String {
+        return self.message.content
+    }
+
+    func bubblePosition() -> BubblePosition {
+
+        let accountHelper = AccountModelHelper(withAccount: accountService.currentAccount!)
+
+        if self.message.author == accountHelper.ringId! {
+            return .sent
+        } else {
+            return .received
+        }
+    }
+}
diff --git a/Ring/Ring/SmartlistViewController.swift b/Ring/Ring/SmartlistViewController.swift
index 0768bdf..821409b 100644
--- a/Ring/Ring/SmartlistViewController.swift
+++ b/Ring/Ring/SmartlistViewController.swift
@@ -30,6 +30,8 @@
     fileprivate let disposeBag = DisposeBag()
     fileprivate let SmartlistRowHeight :CGFloat = 64.0
 
+    var selectedItem: ConversationViewModel?
+
     override func viewDidLoad() {
         super.viewDidLoad()
         self.setupUI()
@@ -61,16 +63,25 @@
             cell.lastMessagePreviewLabel.text = viewModel.lastMessage
         }.addDisposableTo(disposeBag)
 
+        //Deselect the row
         self.tableView.rx.itemSelected.asObservable().subscribe(onNext: { indexPath in
             self.tableView.deselectRow(at: indexPath, animated: true)
         }).addDisposableTo(disposeBag)
+
+        //Show the Messages screens and pass the viewModel
+        self.tableView.rx.modelSelected(ConversationViewModel.self).subscribe(onNext: { item in
+            self.selectedItem = item
+            self.performSegue(withIdentifier: "ShowMessages", sender: nil)
+        }).addDisposableTo(disposeBag)
     }
 
     // MARK: - Navigation
 
     // In a storyboard-based application, you will often want to do a little preparation before navigation
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
-
+        if let msgVC = segue.destination as? ConversationViewController {
+            msgVC.viewModel = self.selectedItem
+        }
     }
 
 }