diff --git a/Ring/Cartfile b/Ring/Cartfile index 79d55a06e97f7dcb9aca40ae06bc7d15b2a593da..e336d18989daa3646959a8bfaedd264f9de212e9 100644 --- a/Ring/Cartfile +++ b/Ring/Cartfile @@ -1,2 +1,3 @@ github "ReactiveX/RxSwift" == 3.0.1 +github "RxSwiftCommunity/RxDataSources" ~> 1.0 github "pkluz/PKHUD" ~> 4.0 diff --git a/Ring/Cartfile.resolved b/Ring/Cartfile.resolved index 8c2f05780c7acde945594e5685f359f4cc575036..306fc01f453935785c5a64ed932d38438a39a770 100644 --- a/Ring/Cartfile.resolved +++ b/Ring/Cartfile.resolved @@ -1,2 +1,3 @@ -github "pkluz/PKHUD" "4.2.1" github "ReactiveX/RxSwift" "3.0.1" +github "RxSwiftCommunity/RxDataSources" "1.0.3" +github "pkluz/PKHUD" "4.2.3" diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj index 57383a8ded912315ed0d7c186e18e0f5b3ac2162..2777f220a1ad238a1f4ac3dadcffc7d1acce5c25 100644 --- a/Ring/Ring.xcodeproj/project.pbxproj +++ b/Ring/Ring.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ 564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */; }; 564C44621E943DE6000F92B1 /* NameService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44611E943DE6000F92B1 /* NameService.swift */; }; 564C44641E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564C44631E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift */; }; + 56559B171EEED50D00BF20E1 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56559B161EEED50D00BF20E1 /* Colors.swift */; }; 5669A7FA1EA904AF003C7B93 /* SwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7F91EA904AF003C7B93 /* SwitchCell.xib */; }; 5669A7FC1EA904D2003C7B93 /* TextFieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7FB1EA904D2003C7B93 /* TextFieldCell.xib */; }; 5669A7FE1EA904E4003C7B93 /* TextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5669A7FD1EA904E4003C7B93 /* TextCell.xib */; }; @@ -126,8 +127,23 @@ 56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */; }; 56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC64E21E805F0200EA1AA9 /* TextCell.swift */; }; 56AC650E1E85694D00EA1AA9 /* RoundedTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56AC650D1E85694D00EA1AA9 /* RoundedTextField.swift */; }; + 56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC99E1ED714CB00CDAF8B /* MessagesAdapter.mm */; }; + 56BBC9A21ED714DF00CDAF8B /* MessagesAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A01ED714DF00CDAF8B /* MessagesAdapterDelegate.swift */; }; + 56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */; }; + 56BBC9A61ED7151500CDAF8B /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A51ED7151500CDAF8B /* MessageModel.swift */; }; + 56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9A71ED7152300CDAF8B /* SmartlistViewController.swift */; }; + 56BBC9AA1ED7153800CDAF8B /* Global.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9A91ED7153800CDAF8B /* Global.strings */; }; + 56BBC9AC1ED7154300CDAF8B /* Smartlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */; }; + 56BBC9B01ED7155700CDAF8B /* ConversationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */; }; + 56BBC9B41ED7156500CDAF8B /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */; }; + 56BBC9B51ED7156500CDAF8B /* ConversationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 56BBC9B31ED7156500CDAF8B /* ConversationCell.xib */; }; + 56BBC9B91ED715FE00CDAF8B /* ContactModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9B71ED715FE00CDAF8B /* ContactModel.swift */; }; + 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 */; }; - 56BBC9DA1EDDC0B400CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9D91EDDC0B400CDAF8B /* LookupNameResponse.m */; }; + 56BBC9DB1EDDC7F700CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9D91EDDC0B400CDAF8B /* LookupNameResponse.m */; }; + 56BBC9DC1EDDC82600CDAF8B /* ConversationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -269,6 +285,7 @@ 564C445F1E943C37000F92B1 /* NameRegistrationAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NameRegistrationAdapter.mm; sourceTree = "<group>"; }; 564C44611E943DE6000F92B1 /* NameService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NameService.swift; sourceTree = "<group>"; }; 564C44631E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NameRegistrationAdapterDelegate.swift; sourceTree = "<group>"; }; + 56559B161EEED50D00BF20E1 /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; }; 5669A7F91EA904AF003C7B93 /* SwitchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SwitchCell.xib; sourceTree = "<group>"; }; 5669A7FB1EA904D2003C7B93 /* TextFieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextFieldCell.xib; sourceTree = "<group>"; }; 5669A7FD1EA904E4003C7B93 /* TextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextCell.xib; sourceTree = "<group>"; }; @@ -280,6 +297,22 @@ 56AC64E01E80542300EA1AA9 /* TextFieldCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldCell.swift; sourceTree = "<group>"; }; 56AC64E21E805F0200EA1AA9 /* TextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = "<group>"; }; 56AC650D1E85694D00EA1AA9 /* RoundedTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedTextField.swift; sourceTree = "<group>"; }; + 56BBC99D1ED714CB00CDAF8B /* MessagesAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagesAdapter.h; sourceTree = "<group>"; }; + 56BBC99E1ED714CB00CDAF8B /* MessagesAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MessagesAdapter.mm; sourceTree = "<group>"; }; + 56BBC9A01ED714DF00CDAF8B /* MessagesAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesAdapterDelegate.swift; sourceTree = "<group>"; }; + 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationsService.swift; sourceTree = "<group>"; }; + 56BBC9A51ED7151500CDAF8B /* MessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageModel.swift; sourceTree = "<group>"; }; + 56BBC9A71ED7152300CDAF8B /* SmartlistViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmartlistViewController.swift; sourceTree = "<group>"; }; + 56BBC9A91ED7153800CDAF8B /* Global.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Global.strings; sourceTree = "<group>"; }; + 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Smartlist.strings; sourceTree = "<group>"; }; + 56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationModel.swift; sourceTree = "<group>"; }; + 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationViewModel.swift; sourceTree = "<group>"; }; + 56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConversationCell.swift; sourceTree = "<group>"; }; + 56BBC9B31ED7156500CDAF8B /* ConversationCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ConversationCell.xib; sourceTree = "<group>"; }; + 56BBC9B71ED715FE00CDAF8B /* ContactModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactModel.swift; sourceTree = "<group>"; }; + 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>"; }; @@ -427,6 +460,8 @@ 02E1A0261DDE4C2E00D75B59 /* Services */ = { isa = PBXGroup; children = ( + 56BBC9A01ED714DF00CDAF8B /* MessagesAdapterDelegate.swift */, + 56BBC9A11ED714DF00CDAF8B /* ConversationsService.swift */, 02B22E081DF7585F000358C9 /* DaemonService.swift */, 02B22DFE1DF755DB000358C9 /* AccountsService.swift */, 0273C2FE1E0C438F00CF00BA /* AccountAdapterDelegate.swift */, @@ -453,6 +488,8 @@ 02EFCACF1E0C3DD600FD8ED1 /* Bridging */ = { isa = PBXGroup; children = ( + 56BBC99D1ED714CB00CDAF8B /* MessagesAdapter.h */, + 56BBC99E1ED714CB00CDAF8B /* MessagesAdapter.mm */, 04399AA91D1C304300E99CD9 /* Ring-Bridging-Header.h */, 557086501E8ADB9D001A7CE4 /* SystemAdapter.h */, 557086511E8ADB9D001A7CE4 /* SystemAdapter.mm */, @@ -497,6 +534,7 @@ 043866391D2307C000E06CE2 /* Extensions */ = { isa = PBXGroup; children = ( + 56BBC9BB1ED7161200CDAF8B /* Date+Helpers.swift */, 043866371D2304A700E06CE2 /* BoolStringExtension.swift */, ); name = Extensions; @@ -529,6 +567,10 @@ 043999F51D1C2D9D00E99CD9 /* Ring */ = { isa = PBXGroup; children = ( + 56BBC9BD1ED7165800CDAF8B /* Smartlist */, + 56BBC9B61ED7158600CDAF8B /* Contacts */, + 56BBC9AD1ED7154800CDAF8B /* Conversations */, + 56BBC9A41ED7150200CDAF8B /* Messages */, 564C44571E8D7F68000F92B1 /* Constants */, 56AC64D61E80121200EA1AA9 /* Internationalization */, 0273C3021E0C689600CF00BA /* Walkthrough */, @@ -671,6 +713,7 @@ children = ( 564C44581E8D7F8F000F92B1 /* LocalizedStringTableNames.swift */, 564C445A1E8EA44E000F92B1 /* Durations.swift */, + 56559B161EEED50D00BF20E1 /* Colors.swift */, ); name = Constants; sourceTree = "<group>"; @@ -678,6 +721,8 @@ 56AC64D61E80121200EA1AA9 /* Internationalization */ = { isa = PBXGroup; children = ( + 56BBC9AB1ED7154300CDAF8B /* Smartlist.strings */, + 56BBC9A91ED7153800CDAF8B /* Global.strings */, 56AC64DB1E8012CA00EA1AA9 /* Walkthrough.strings */, ); name = Internationalization; @@ -696,6 +741,43 @@ name = Cells; sourceTree = "<group>"; }; + 56BBC9A41ED7150200CDAF8B /* Messages */ = { + isa = PBXGroup; + children = ( + 56BBC9A51ED7151500CDAF8B /* MessageModel.swift */, + ); + name = Messages; + sourceTree = "<group>"; + }; + 56BBC9AD1ED7154800CDAF8B /* Conversations */ = { + isa = PBXGroup; + children = ( + 56BBC9B21ED7156500CDAF8B /* ConversationCell.swift */, + 56BBC9B31ED7156500CDAF8B /* ConversationCell.xib */, + 56BBC9AE1ED7155700CDAF8B /* ConversationModel.swift */, + 56BBC9AF1ED7155700CDAF8B /* ConversationViewModel.swift */, + ); + name = Conversations; + sourceTree = "<group>"; + }; + 56BBC9B61ED7158600CDAF8B /* Contacts */ = { + isa = PBXGroup; + children = ( + 56BBC9B71ED715FE00CDAF8B /* ContactModel.swift */, + 56BBC9B81ED715FE00CDAF8B /* ContactHelper.swift */, + ); + name = Contacts; + sourceTree = "<group>"; + }; + 56BBC9BD1ED7165800CDAF8B /* Smartlist */ = { + isa = PBXGroup; + children = ( + 56BBC9BE1ED7168400CDAF8B /* SmartlistViewModel.swift */, + 56BBC9A71ED7152300CDAF8B /* SmartlistViewController.swift */, + ); + name = Smartlist; + sourceTree = "<group>"; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -809,9 +891,12 @@ 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 */, 04399A031D1C2D9D00E99CD9 /* Assets.xcassets in Resources */, 04399A011D1C2D9D00E99CD9 /* Main.storyboard in Resources */, 5669A7FA1EA904AF003C7B93 /* SwitchCell.xib in Resources */, + 56BBC9B51ED7156500CDAF8B /* ConversationCell.xib in Resources */, 5669A7FC1EA904D2003C7B93 /* TextFieldCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -856,41 +941,54 @@ 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 */, 02E1A0251DDE4ABA00D75B59 /* BoolStringExtension.swift in Sources */, 04399AAC1D1C304300E99CD9 /* AccountAdapter.mm in Sources */, 02DD80C81E1EAD70009A3510 /* AccountConfigModel.swift in Sources */, - 56BBC9DA1EDDC0B400CDAF8B /* LookupNameResponse.m in Sources */, 02B22E091DF7585F000358C9 /* DaemonService.swift in Sources */, 0273C3061E0C68B100CF00BA /* CreateRingAccountViewController.swift in Sources */, + 56BBC99F1ED714CB00CDAF8B /* MessagesAdapter.mm in Sources */, 02C9B63F1E1D4E8C00F82F0C /* ServiceEvent.swift in Sources */, 02DD80CD1E1EB2E4009A3510 /* ConfigKeyModel.swift in Sources */, + 56BBC9A81ED7152300CDAF8B /* SmartlistViewController.swift in Sources */, 5516C29F1E71CEFF009D3D2D /* AccountModelHelper.swift in Sources */, 56308BA71EA00E5700660275 /* NameRegistrationResponse.m in Sources */, 56AC64E11E80542300EA1AA9 /* TextFieldCell.swift in Sources */, 56AC64E31E805F0200EA1AA9 /* TextCell.swift in Sources */, 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 */, 043866331D22CE8C00E06CE2 /* MeViewController.swift in Sources */, 56AC64DF1E804ECC00EA1AA9 /* SwitchCell.swift in Sources */, + 56BBC9B91ED715FE00CDAF8B /* ContactModel.swift in Sources */, + 56BBC9BA1ED715FE00CDAF8B /* ContactHelper.swift in Sources */, 04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */, + 56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */, + 56BBC9B01ED7155700CDAF8B /* ConversationModel.swift in Sources */, 563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */, 02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */, 043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */, 564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */, 0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */, 02DD80CA1E1EAF1A009A3510 /* AccountCredentialsModel.swift in Sources */, + 56559B171EEED50D00BF20E1 /* Colors.swift in Sources */, 0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */, + 56BBC9BC1ED7161200CDAF8B /* Date+Helpers.swift in Sources */, 564C44621E943DE6000F92B1 /* NameService.swift in Sources */, 043866361D22D06500E06CE2 /* AccountTableViewCell.swift in Sources */, 04399AAD1D1C304300E99CD9 /* DRingAdapter.mm in Sources */, 0273C2FF1E0C438F00CF00BA /* AccountAdapterDelegate.swift in Sources */, 02B22DFF1DF755DB000358C9 /* AccountsService.swift in Sources */, + 56BBC9A61ED7151500CDAF8B /* MessageModel.swift in Sources */, + 56BBC9A21ED714DF00CDAF8B /* MessagesAdapterDelegate.swift in Sources */, + 56BBC9B41ED7156500CDAF8B /* ConversationCell.swift in Sources */, 564C44601E943C37000F92B1 /* NameRegistrationAdapter.mm in Sources */, 564C44591E8D7F8F000F92B1 /* LocalizedStringTableNames.swift in Sources */, 56AC64D51E7C7F4000EA1AA9 /* WelcomeViewController.swift in Sources */, diff --git a/Ring/Ring/AppDelegate.swift b/Ring/Ring/AppDelegate.swift index e670d035151dd2d946bcfedad3bdfae2431324de..1baaf7479a2f9ba287b2ad4d013c8c46bbe5db32 100644 --- a/Ring/Ring/AppDelegate.swift +++ b/Ring/Ring/AppDelegate.swift @@ -29,6 +29,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { static let daemonService = DaemonService(dRingAdaptor: DRingAdapter()) static let accountService = AccountsService(withAccountAdapter: AccountAdapter()) static let nameService = NameService(withNameRegistrationAdapter: NameRegistrationAdapter()) + static let conversationsService = ConversationsService(withMessageAdapter: MessagesAdapter()) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { SystemAdapter().registerConfigurationHandler() diff --git a/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/Contents.json b/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..f8e96decfdefe2b7c015a040744c65871e153d8f --- /dev/null +++ b/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "ic_contact_picture.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/ic_contact_picture.png b/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/ic_contact_picture.png new file mode 100644 index 0000000000000000000000000000000000000000..b84a9511860c02c2da725d0e2f94a24675eb81c7 Binary files /dev/null and b/Ring/Ring/Assets.xcassets/ic_contact_picture.imageset/ic_contact_picture.png differ diff --git a/Ring/Ring/Base.lproj/Main.storyboard b/Ring/Ring/Base.lproj/Main.storyboard index cee4ad0e1eefe35c443977884ae04aa81476e0fc..e764877f82a6a7e7410a3fe8fa64474347a0cada 100644 --- a/Ring/Ring/Base.lproj/Main.storyboard +++ b/Ring/Ring/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="qdG-Sd-QaE"> +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="qdG-Sd-QaE"> <device id="retina4_0" orientation="portrait"> <adaptation id="fullscreen"/> </device> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> <capability name="Constraints to layout margins" minToolsVersion="6.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -22,7 +22,7 @@ <rect key="frame" x="0.0" y="0.0" width="320" height="568"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <subviews> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PERSON PLACEHOLDER" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vcL-Nj-NcH"> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="PERSON PLACEHOLDER" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vcL-Nj-NcH"> <rect key="frame" x="16" y="102" width="180" height="64"/> <constraints> <constraint firstAttribute="width" constant="180" id="CUM-z9-ubY"/> @@ -50,7 +50,7 @@ <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="i1O-Yc-WGd" id="Bz1-A3-Z3f"> - <rect key="frame" x="0.0" y="0.0" width="320" height="43"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <subviews> <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bm0-lC-K2F"> @@ -96,7 +96,7 @@ <rect key="frame" x="0.0" y="72" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ok2-8L-eMm" id="m64-AI-t2h"> - <rect key="frame" x="0.0" y="0.0" width="320" height="43"/> + <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> <subviews> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kZv-uf-BsD" userLabel="Account Name Label"> @@ -192,9 +192,11 @@ <objects> <navigationController id="acv-jH-RCt" sceneMemberID="viewController"> <tabBarItem key="tabBarItem" title="Me" id="jo3-1i-bFH"/> - <navigationBar key="navigationBar" contentMode="scaleToFill" id="tAT-cg-hut"> + <navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="tAT-cg-hut"> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <autoresizingMask key="autoresizingMask"/> + <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="barTintColor" red="0.039215686270000001" green="0.4549019608" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/> </navigationBar> <connections> <segue destination="Adn-sB-m3h" kind="relationship" relationship="rootViewController" id="yoa-Sb-o2W"/> @@ -204,14 +206,16 @@ </objects> <point key="canvasLocation" x="-954" y="-438"/> </scene> - <!--Item--> + <!--Home--> <scene sceneID="RrV-5t-V1t"> <objects> <navigationController id="m3Y-hs-SzS" sceneMemberID="viewController"> - <tabBarItem key="tabBarItem" title="Item" id="vc8-sv-IF0"/> - <navigationBar key="navigationBar" contentMode="scaleToFill" id="G6y-tt-W3p"> + <tabBarItem key="tabBarItem" title="Home" id="vc8-sv-IF0"/> + <navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="G6y-tt-W3p"> <rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <autoresizingMask key="autoresizingMask"/> + <color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <color key="barTintColor" red="0.039215686270000001" green="0.4549019608" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/> </navigationBar> <connections> <segue destination="NIj-Cd-aWO" kind="relationship" relationship="rootViewController" id="GUx-m9-dbP"/> @@ -224,7 +228,7 @@ <!--Home--> <scene sceneID="UuZ-iE-GB0"> <objects> - <viewController title="Home" id="NIj-Cd-aWO" sceneMemberID="viewController"> + <viewController title="Home" id="NIj-Cd-aWO" customClass="SmartlistViewController" customModule="Ring" customModuleProvider="target" sceneMemberID="viewController"> <layoutGuides> <viewControllerLayoutGuide type="top" id="s1e-Lp-B2j"/> <viewControllerLayoutGuide type="bottom" id="aoH-Yk-Qrn"/> @@ -241,8 +245,10 @@ </subviews> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> </view> - <tabBarItem key="tabBarItem" title="Home" id="1QA-0Y-BFL"/> <navigationItem key="navigationItem" id="b8m-eG-Q9D"/> + <connections> + <outlet property="tableView" destination="B6Y-MZ-L7L" id="dXp-J4-x68"/> + </connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="rzQ-ll-5bo" userLabel="First Responder" sceneMemberID="firstResponder"/> </objects> @@ -256,6 +262,7 @@ <rect key="frame" x="0.0" y="0.0" width="320" height="49"/> <autoresizingMask key="autoresizingMask"/> <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/> + <color key="tintColor" red="0.039215686270000001" green="0.4549019608" blue="0.53725490200000003" alpha="1" colorSpace="calibratedRGB"/> </tabBar> <connections> <segue destination="m3Y-hs-SzS" kind="relationship" relationship="viewControllers" id="tnG-cB-lXh"/> diff --git a/Ring/Ring/Bridging/Ring-Bridging-Header.h b/Ring/Ring/Bridging/Ring-Bridging-Header.h index b0aecf0604caa9a124fcedab639c2c31c78e155b..600ce09858cf2bf89493815f8f64b9a7813bb636 100644 --- a/Ring/Ring/Bridging/Ring-Bridging-Header.h +++ b/Ring/Ring/Bridging/Ring-Bridging-Header.h @@ -29,3 +29,4 @@ #import "LookupNameResponse.h" #import "RegistrationResponse.h" #import "NameRegistrationResponse.h" +#import "MessagesAdapter.h" diff --git a/Ring/Ring/Colors.swift b/Ring/Ring/Colors.swift new file mode 100644 index 0000000000000000000000000000000000000000..94d112f16a17351c686d89b9c31e274168013e7d --- /dev/null +++ b/Ring/Ring/Colors.swift @@ -0,0 +1,28 @@ +/* + * 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 Colors { + static let ringMainColor = UIColor(colorLiteralRed: 10.0/255.0, + green: 116.0/255.0, + blue: 137.0/255.0, + alpha: 1.0) +} diff --git a/Ring/Ring/ContactHelper.swift b/Ring/Ring/ContactHelper.swift new file mode 100644 index 0000000000000000000000000000000000000000..67378a9553d39c45e5b935f163368a6e87663b9b --- /dev/null +++ b/Ring/Ring/ContactHelper.swift @@ -0,0 +1,59 @@ +/* + * 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 ContactHelper { + + fileprivate static var cache = [String : String]() + + static func lookupUserName(forRingId ringId: String, nameService: NameService, disposeBag: DisposeBag) -> Variable<String> { + + let userName = Variable("") + + if ContactHelper.cache[ringId] == nil { + + //Lookup the user name observer + nameService.usernameLookupStatus + .observeOn(MainScheduler.instance) + .filter({ lookupNameResponse in + return lookupNameResponse.address != nil && lookupNameResponse.address == ringId + }).subscribe(onNext: { lookupNameResponse in + if lookupNameResponse.state == .found { + self.cache[ringId] = lookupNameResponse.name + userName.value = lookupNameResponse.name + } else { + self.cache[ringId] = lookupNameResponse.address + userName.value = lookupNameResponse.address + } + }).addDisposableTo(disposeBag) + + nameService.lookupAddress(withAccount: "", nameserver: "", address: ringId) + + } else { + userName.value = self.cache[ringId]! + } + + return userName + } + +} diff --git a/Ring/Ring/ContactModel.swift b/Ring/Ring/ContactModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..f4a8f16e1dadc28632c1dd002e99fd3955fb300b --- /dev/null +++ b/Ring/Ring/ContactModel.swift @@ -0,0 +1,34 @@ +/* + * 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 ContactModel { + + var ringId: String + + init(withRingId ringId: String) { + self.ringId = ringId + } + + public static func ==(lhs: ContactModel, rhs: ContactModel) -> Bool { + return lhs.ringId == rhs.ringId + } +} diff --git a/Ring/Ring/ConversationCell.swift b/Ring/Ring/ConversationCell.swift new file mode 100644 index 0000000000000000000000000000000000000000..d3fd4fd62bfbafb54f8a96def7fddc72e97832dd --- /dev/null +++ b/Ring/Ring/ConversationCell.swift @@ -0,0 +1,47 @@ +/* + * 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 ConversationCell: UITableViewCell { + + @IBOutlet weak var profileImage: UIImageView! + @IBOutlet weak var nameLabel: UILabel! + @IBOutlet weak var newMessagesIndicator: UIView! + @IBOutlet weak var newMessagesLabel: UILabel! + @IBOutlet weak var lastMessageDateLabel: UILabel! + @IBOutlet weak var lastMessagePreviewLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + self.newMessagesIndicator.backgroundColor = UIColor.red + } + + override func setHighlighted(_ highlighted: Bool, animated: Bool) { + super.setHighlighted(highlighted, animated: animated) + self.newMessagesIndicator.backgroundColor = UIColor.red + } +} diff --git a/Ring/Ring/ConversationCell.xib b/Ring/Ring/ConversationCell.xib new file mode 100644 index 0000000000000000000000000000000000000000..c48708bf66698f6ca971f9c9e28c5943c5e1dc38 --- /dev/null +++ b/Ring/Ring/ConversationCell.xib @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16A323" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES"> + <device id="retina4_7" orientation="portrait"> + <adaptation id="fullscreen"/> + </device> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/> + <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="default" indentationWidth="10" reuseIdentifier="ConversationCellId" rowHeight="76" id="KGk-i7-Jjw" customClass="ConversationCell" customModule="Ring" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="358" height="76"/> + <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="358" height="75.5"/> + <autoresizingMask key="autoresizingMask"/> + <subviews> + <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ic_contact_picture" translatesAutoresizingMaskIntoConstraints="NO" id="pFB-Jn-TNP"> + <rect key="frame" x="16" y="18" width="40" height="40"/> + <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="Ptm-nU-6Xi"/> + <constraint firstAttribute="height" constant="40" id="eES-QW-paO"/> + </constraints> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius"> + <integer key="value" value="20"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </imageView> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Yesterday" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Yv-cC-LKx"> + <rect key="frame" x="281" y="30.5" width="61" height="14.5"/> + <fontDescription key="fontDescription" type="boldSystem" pointSize="12"/> + <color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2fJ-Wf-1e0"> + <rect key="frame" x="60" y="4" width="217" height="41"/> + <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eug-ak-r49"> + <rect key="frame" x="60" y="49" width="282" height="22.5"/> + <fontDescription key="fontDescription" type="system" pointSize="14"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <view clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="JTE-eF-Y5s"> + <rect key="frame" x="42" y="18" width="14" height="14"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="1" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="P5S-4k-0yx"> + <rect key="frame" x="0.0" y="0.0" width="14" height="14"/> + <constraints> + <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="14" id="VEi-Hm-z85"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="10"/> + <color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <color key="backgroundColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> + <constraints> + <constraint firstItem="P5S-4k-0yx" firstAttribute="top" secondItem="JTE-eF-Y5s" secondAttribute="top" id="BH8-rD-0Oo"/> + <constraint firstAttribute="height" constant="14" id="Qxc-Mv-ssH"/> + <constraint firstItem="P5S-4k-0yx" firstAttribute="leading" secondItem="JTE-eF-Y5s" secondAttribute="leading" id="ZE1-mR-DoI"/> + <constraint firstAttribute="trailing" secondItem="P5S-4k-0yx" secondAttribute="trailing" id="a9D-mE-0qK"/> + <constraint firstAttribute="bottom" secondItem="P5S-4k-0yx" secondAttribute="bottom" id="qMW-uw-IoS"/> + </constraints> + <userDefinedRuntimeAttributes> + <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius"> + <integer key="value" value="6"/> + </userDefinedRuntimeAttribute> + </userDefinedRuntimeAttributes> + </view> + </subviews> + <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="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"/> + <constraint firstAttribute="trailing" secondItem="7Yv-cC-LKx" secondAttribute="trailing" constant="16" id="UOx-Og-IuZ"/> + <constraint firstItem="JTE-eF-Y5s" firstAttribute="top" secondItem="pFB-Jn-TNP" secondAttribute="top" id="W3A-IX-eXJ"/> + <constraint firstItem="2fJ-Wf-1e0" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="4" id="omS-kb-QbN"/> + <constraint firstItem="eug-ak-r49" firstAttribute="top" secondItem="7Yv-cC-LKx" secondAttribute="bottom" constant="4" id="oox-mY-e8b"/> + <constraint firstAttribute="bottom" secondItem="eug-ak-r49" secondAttribute="bottom" constant="4" id="rzH-w5-tpt"/> + <constraint firstItem="eug-ak-r49" firstAttribute="top" secondItem="2fJ-Wf-1e0" secondAttribute="bottom" constant="4" id="se0-Ur-K7G"/> + <constraint firstItem="pFB-Jn-TNP" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="16" id="suq-ak-BYg"/> + <constraint firstItem="7Yv-cC-LKx" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="xVO-gP-WdP"/> + </constraints> + </tableViewCellContentView> + <connections> + <outlet property="lastMessageDateLabel" destination="7Yv-cC-LKx" id="pFf-hW-zD8"/> + <outlet property="lastMessagePreviewLabel" destination="eug-ak-r49" id="3jj-Lx-jbU"/> + <outlet property="nameLabel" destination="2fJ-Wf-1e0" id="0Mb-yC-vh6"/> + <outlet property="newMessagesIndicator" destination="JTE-eF-Y5s" id="9kR-8x-Zpk"/> + <outlet property="newMessagesLabel" destination="P5S-4k-0yx" id="WlA-Z8-sNC"/> + <outlet property="profileImage" destination="pFB-Jn-TNP" id="zuf-CZ-9wL"/> + </connections> + <point key="canvasLocation" x="70" y="-92"/> + </tableViewCell> + </objects> + <resources> + <image name="ic_contact_picture" width="128" height="128"/> + </resources> +</document> diff --git a/Ring/Ring/ConversationModel.swift b/Ring/Ring/ConversationModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..d1624cda7713eacc7ce0ba66bd1998e73369ab2e --- /dev/null +++ b/Ring/Ring/ConversationModel.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. + */ + +class ConversationModel { + + var messages = [MessageModel]() + var recipient: ContactModel + var accountId: String + + init(withRecipient recipient: ContactModel, accountId: String) { + self.recipient = recipient + self.accountId = accountId + } + +} diff --git a/Ring/Ring/ConversationViewModel.swift b/Ring/Ring/ConversationViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..19537035c2ece2f2a61117460ba0f5707da4f8ea --- /dev/null +++ b/Ring/Ring/ConversationViewModel.swift @@ -0,0 +1,99 @@ +/* + * 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 ConversationViewModel { + + let conversation: ConversationModel + let userName: Observable<String> + + //Displays the entire date ( for messages received before the current week ) + private let dateFormatter = DateFormatter() + + //Displays the hour of the message reception ( for messages received today ) + private let hourFormatter = DateFormatter() + + private let disposeBag = DisposeBag() + + 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() + } + + 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 + } + + var lastMessage: String { + if let lastMessage = conversation.messages.last?.content { + return lastMessage + } else { + return "" + } + } + + var lastMessageReceivedDate: String { + + guard let lastMessageDate = self.conversation.messages.last?.receivedDate else { + return "" + } + + let dateToday = Date() + + //Get components from today date + let todayWeekOfYear = Calendar.current.component(.weekOfYear, from: dateToday) + let todayDay = Calendar.current.component(.day, from: dateToday) + let todayMonth = Calendar.current.component(.month, from: dateToday) + let todayYear = Calendar.current.component(.year, from: dateToday) + + //Get components from last message date + let weekOfYear = Calendar.current.component(.weekOfYear, from: lastMessageDate) + let day = Calendar.current.component(.day, from: lastMessageDate) + let month = Calendar.current.component(.month, from: lastMessageDate) + let year = Calendar.current.component(.year, from: lastMessageDate) + + if todayDay == day && todayMonth == month && todayYear == year { + return hourFormatter.string(from: lastMessageDate) + } else if day == todayDay - 1 { + return NSLocalizedString("Yesterday", tableName: "Smartlist", comment: "") + } else if todayYear == year && todayWeekOfYear == weekOfYear { + return lastMessageDate.dayOfWeek() + } else { + return dateFormatter.string(from: lastMessageDate) + } + } +} diff --git a/Ring/Ring/ConversationsService.swift b/Ring/Ring/ConversationsService.swift new file mode 100644 index 0000000000000000000000000000000000000000..0d77c07f2d8ad3e59e61e775e247b420647846e6 --- /dev/null +++ b/Ring/Ring/ConversationsService.swift @@ -0,0 +1,76 @@ +/* + * 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 ConversationsService: MessagesAdapterDelegate { + + fileprivate let messageAdapter :MessagesAdapter + fileprivate let disposeBag = DisposeBag() + fileprivate let textPlainMIMEType = "text/plain" + + let 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) + } + + //MARK: Message Adapter delegate + + 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) + + //Get conversations for this sender + var currentConversation = conversations.value.filter({ conversation in + return conversation.recipient.ringId == senderAccount + }).first + + //Get the current array of conversations + var currentConversations = self.conversations.value + + //Create a new conversation for this sender if not exists + if currentConversation == nil { + currentConversation = ConversationModel(withRecipient: ContactModel(withRingId: senderAccount), accountId: receiverAccountId) + currentConversations.append(currentConversation!) + } + + //Add the received message into the conversation + currentConversation?.messages.append(message) + + //Upate the value of the Variable + self.conversations.value = currentConversations + } + } + + func messageStatusChanged(_ status: MessageStatus, for messageId: UInt64, + from senderAccountId: String, to receiverAccount: String) { + + print("messageStatusChanged: \(status.rawValue) for: \(messageId) from: \(senderAccountId) to: \(receiverAccount)") + } +} diff --git a/Ring/Ring/Extensions/Date+Helpers.swift b/Ring/Ring/Extensions/Date+Helpers.swift new file mode 100644 index 0000000000000000000000000000000000000000..19a58ed73b4b5f33bd25bc43c38c013affccd53c --- /dev/null +++ b/Ring/Ring/Extensions/Date+Helpers.swift @@ -0,0 +1,31 @@ +/* + * 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. + */ + +extension Date { + func dayNumberOfWeek() -> Int? { + return Calendar.current.dateComponents([.weekday], from: self).weekday + } + + func dayOfWeek() -> String { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "EEEE" + return dateFormatter.string(from: self) + } +} diff --git a/Ring/Ring/Global.strings b/Ring/Ring/Global.strings new file mode 100644 index 0000000000000000000000000000000000000000..0cc9f97f37d8df660c579e3caa2b4c9ce03f7338 --- /dev/null +++ b/Ring/Ring/Global.strings @@ -0,0 +1,21 @@ +/* + * 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. + */ + +"HomeTabBarTitle" = "Home"; diff --git a/Ring/Ring/MainTabBar/MainTabBarViewController.swift b/Ring/Ring/MainTabBar/MainTabBarViewController.swift index 678d30a0b04126345c21b71650cd2695549abaad..72cdb8bfafad4c70ee3786fda787a758c4482a98 100644 --- a/Ring/Ring/MainTabBar/MainTabBarViewController.swift +++ b/Ring/Ring/MainTabBar/MainTabBarViewController.swift @@ -23,6 +23,12 @@ import UIKit class MainTabBarViewController: UITabBarController { fileprivate let accountService = AppDelegate.accountService + override func viewDidLoad() { + super.viewDidLoad() + UITabBarItem.appearance() + .setTitleTextAttributes( [NSForegroundColorAttributeName : Colors.ringMainColor], for: .selected) + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if !accountService.hasAccounts() { diff --git a/Ring/Ring/MessageModel.swift b/Ring/Ring/MessageModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..000e4858c806bc0dc2569cc30c81355c49303f72 --- /dev/null +++ b/Ring/Ring/MessageModel.swift @@ -0,0 +1,37 @@ +/* + * 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. + */ + +class MessageModel { + + var id: UInt64? + var receivedDate: Date + var content: String + var author: String + var status: MessageStatus + + init(withId id: UInt64?, receivedDate: Date, content: String, author: String) { + self.id = id + self.receivedDate = receivedDate + self.content = content + self.author = author + self.status = .unknown + } + +} diff --git a/Ring/Ring/MessagesAdapter.h b/Ring/Ring/MessagesAdapter.h new file mode 100644 index 0000000000000000000000000000000000000000..4cc6c8553e0aaf3a7859629ddbdcfda0f04d2df4 --- /dev/null +++ b/Ring/Ring/MessagesAdapter.h @@ -0,0 +1,43 @@ +/* + * 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 <Foundation/Foundation.h> + +typedef NS_ENUM(int, MessageStatus) { + MessageStatusUnknown = 0, + MessageStatusIdle, + MessageStatusSending, + MessageStatusSent, + MessageStatusRead, + MessageStatusFailure +}; + +@protocol MessagesAdapterDelegate; + +@interface MessagesAdapter : NSObject + +@property (class, nonatomic, weak) id <MessagesAdapterDelegate> delegate; + +- (NSUInteger)sendMessageWithContent:(NSDictionary*)content withAccountId:(NSString*)accountId + to:(NSString*)toAccountId; + +- (MessageStatus)statusForMessageId:(uint64_t)messageId; + +@end diff --git a/Ring/Ring/MessagesAdapter.mm b/Ring/Ring/MessagesAdapter.mm new file mode 100644 index 0000000000000000000000000000000000000000..69f90d4a37c886b27a71da16f7dabb4b0900f5eb --- /dev/null +++ b/Ring/Ring/MessagesAdapter.mm @@ -0,0 +1,102 @@ +/* + * 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 "MessagesAdapter.h" + +#import "Ring-Swift.h" +#import "Utils.h" +#import "dring/configurationmanager_interface.h" + +@implementation MessagesAdapter + +using namespace DRing; + +/// Static delegate that will receive the propagated daemon events +static id <MessagesAdapterDelegate> _delegate; + +#pragma mark Init +- (id)init { + if (self = [super init]) { + [self registerConfigurationHandler]; + } + return self; +} +#pragma mark - + +#pragma mark Callbacks registration +- (void)registerConfigurationHandler { + std::map<std::string, std::shared_ptr<CallbackWrapperBase>> confHandlers; + + confHandlers.insert(exportable_callback<ConfigurationSignal::IncomingAccountMessage>([&](const std::string& account_id, + const std::string& from, + const std::map<std::string, + std::string>& payloads) { + if (MessagesAdapter.delegate) { + NSDictionary* message = [Utils mapToDictionnary:payloads]; + NSString* fromAccount = [NSString stringWithUTF8String:from.c_str()]; + NSString* toAccountId = [NSString stringWithUTF8String:account_id.c_str()]; + [MessagesAdapter.delegate didReceiveMessage:message from:fromAccount to:toAccountId]; + } + })); + + confHandlers.insert(exportable_callback<ConfigurationSignal::AccountMessageStatusChanged>([&](const std::string& account_id, uint64_t message_id, const std::string& to, int state) { + if (MessagesAdapter.delegate) { + NSString* fromAccountId = [NSString stringWithUTF8String:account_id.c_str()]; + NSString* toAccount = [NSString stringWithUTF8String:to.c_str()]; + [MessagesAdapter.delegate messageStatusChanged:(MessageStatus)state + for:message_id from:fromAccountId + to:toAccount]; + } + })); + + confHandlers.insert(exportable_callback<DebugSignal::MessageSend>([&](const std::string& message) { + if (MessagesAdapter.delegate) { + NSString* messageSend = [NSString stringWithUTF8String:message.c_str()]; + NSLog(@"MessageSend = %@",messageSend); + } + })); + + registerConfHandlers(confHandlers); +} +#pragma mark - + +- (NSUInteger)sendMessageWithContent:(NSDictionary*)content withAccountId:(NSString*)accountId + to:(NSString*)toAccountId { + + return (NSUInteger) sendAccountTextMessage(std::string([accountId UTF8String]), + std::string([toAccountId UTF8String]), + [Utils dictionnaryToMap:content]); +} + +- (MessageStatus)statusForMessageId:(uint64_t)messageId { + return (MessageStatus)getMessageStatus(messageId); +} + +#pragma mark AccountAdapterDelegate ++ (id <MessagesAdapterDelegate>)delegate { + return _delegate; +} + ++ (void) setDelegate:(id<MessagesAdapterDelegate>)delegate { + _delegate = delegate; +} +#pragma mark - + +@end diff --git a/Ring/Ring/MessagesAdapterDelegate.swift b/Ring/Ring/MessagesAdapterDelegate.swift new file mode 100644 index 0000000000000000000000000000000000000000..dc50514e383a9e6882f309c7297116306b5f8a2e --- /dev/null +++ b/Ring/Ring/MessagesAdapterDelegate.swift @@ -0,0 +1,28 @@ +/* + * 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. + */ + +@objc protocol MessagesAdapterDelegate { + + func didReceiveMessage(_ message: Dictionary<String, String>, from senderAccount: String, + to receiverAccountId: String) + + func messageStatusChanged(_ status: MessageStatus, for messageId: UInt64, from senderAccountId: String, + to receiverAccount: String) +} diff --git a/Ring/Ring/NameRegistrationAdapter.h b/Ring/Ring/NameRegistrationAdapter.h index 2147523a9edad7d6d48078e26926d5365abee47a..6806055bbaa86617f3eea9ba4493c67866bacb0d 100644 --- a/Ring/Ring/NameRegistrationAdapter.h +++ b/Ring/Ring/NameRegistrationAdapter.h @@ -32,4 +32,8 @@ - (void)registerNameWithAccount:(NSString*)account password:(NSString*)password name:(NSString*)name; +- (void)lookupAddressWithAccount:(NSString*)account nameserver:(NSString*)nameserver + address:(NSString*)address; + + @end diff --git a/Ring/Ring/NameRegistrationAdapter.mm b/Ring/Ring/NameRegistrationAdapter.mm index 84a5881f64955a2205f9884450fe2426a8895df7..99a9a03570ca655751ea540849c655179c48d1d7 100644 --- a/Ring/Ring/NameRegistrationAdapter.mm +++ b/Ring/Ring/NameRegistrationAdapter.mm @@ -79,6 +79,10 @@ static id <NameRegistrationAdapterDelegate> _delegate; lookupName(std::string([account UTF8String]),std::string([nameserver UTF8String]),std::string([name UTF8String])); } +- (void)lookupAddressWithAccount:(NSString*)account nameserver:(NSString*)nameserver address:(NSString*)address { + lookupAddress(std::string([account UTF8String]), std::string([nameserver UTF8String]), std::string([address UTF8String])); +} + - (void)registerNameWithAccount:(NSString*)account password:(NSString*)password name:(NSString*)name { registerName(std::string([account UTF8String]), std::string([password UTF8String]), std::string([name UTF8String])); } diff --git a/Ring/Ring/NameService.swift b/Ring/Ring/NameService.swift index 214fd00ed3b7d39e3d09914ccc596c84bda585eb..c15dceb81058e892ec1e6f32afc42bad706cc9b9 100644 --- a/Ring/Ring/NameService.swift +++ b/Ring/Ring/NameService.swift @@ -44,7 +44,7 @@ class NameService: NameRegistrationAdapterDelegate { fileprivate let lookupNameCallDelay = 0.5 /** - Status of the current username lookup request + Status of the current username validation request */ var usernameValidationStatus = PublishSubject<UsernameValidationStatus>() @@ -53,6 +53,11 @@ class NameService: NameRegistrationAdapterDelegate { NameRegistrationAdapter.delegate = self } + /** + Status of the current username lookup request + */ + var usernameLookupStatus = PublishSubject<LookupNameResponse>() + /** Make a username lookup request to the daemon */ @@ -75,6 +80,13 @@ class NameService: NameRegistrationAdapterDelegate { } } + /** + Make an address lookup request to the daemon + */ + func lookupAddress(withAccount account: String, nameserver: String, address: String) { + self.nameRegistrationAdapter.lookupAddress(withAccount: account, nameserver: nameserver, address: address) + } + /** Register the username into the the blockchain */ @@ -95,6 +107,8 @@ class NameService: NameRegistrationAdapterDelegate { } else { print("Lookup name error") } + + usernameLookupStatus.onNext(response) } internal func nameRegistrationEnded(with response: NameRegistrationResponse) { diff --git a/Ring/Ring/Smartlist.strings b/Ring/Ring/Smartlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..e7d37ca1d5a432b941112f2ea26eb1334815c758 --- /dev/null +++ b/Ring/Ring/Smartlist.strings @@ -0,0 +1,22 @@ +/* + * 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. + */ + +"Yesterday" = "Yesterday"; + diff --git a/Ring/Ring/SmartlistViewController.swift b/Ring/Ring/SmartlistViewController.swift new file mode 100644 index 0000000000000000000000000000000000000000..9a76c111711b46b00d99302656cfd61b1740caa1 --- /dev/null +++ b/Ring/Ring/SmartlistViewController.swift @@ -0,0 +1,76 @@ +/* + * 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 +import RxDataSources + +class SmartlistViewController: UIViewController, UITableViewDelegate { + + @IBOutlet weak var tableView: UITableView! + + fileprivate let viewModel = SmartlistViewModel(withConversationsService: AppDelegate.conversationsService) + fileprivate let disposeBag = DisposeBag() + fileprivate let SmartlistRowHeight :CGFloat = 64.0 + + override func viewDidLoad() { + super.viewDidLoad() + self.setupUI() + self.setupTableView() + } + + func setupUI() { + + let title = NSLocalizedString("HomeTabBarTitle", tableName: "Global", comment: "") + + self.title = title + self.navigationItem.title = title + } + + func setupTableView() { + + //Set row height + self.tableView.rowHeight = SmartlistRowHeight + + //Register Cell + self.tableView.register(UINib.init(nibName: "ConversationCell", bundle: nil), forCellReuseIdentifier: "ConversationCellId") + + //Bind the TableView to the ViewModel + self.viewModel.conversationsObservable.bindTo(tableView.rx.items(cellIdentifier: "ConversationCellId", cellType: ConversationCell.self) ) { index, viewModel, cell in + viewModel.userName.bindTo(cell.nameLabel.rx.text).addDisposableTo(self.disposeBag) + cell.newMessagesLabel.text = viewModel.unreadMessages + cell.lastMessageDateLabel.text = viewModel.lastMessageReceivedDate + cell.newMessagesIndicator.isHidden = !viewModel.hasUnreadMessages + cell.lastMessagePreviewLabel.text = viewModel.lastMessage + }.addDisposableTo(disposeBag) + + self.tableView.rx.itemSelected.asObservable().subscribe(onNext: { indexPath in + self.tableView.deselectRow(at: indexPath, animated: true) + }).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?) { + + } + +} diff --git a/Ring/Ring/SmartlistViewModel.swift b/Ring/Ring/SmartlistViewModel.swift new file mode 100644 index 0000000000000000000000000000000000000000..3c0ab418bbfebd88a71b7400bbccfe4fb2923d40 --- /dev/null +++ b/Ring/Ring/SmartlistViewModel.swift @@ -0,0 +1,66 @@ +/* + * 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 SmartlistViewModel: NSObject { + + fileprivate let conversationsService: ConversationsService + fileprivate var conversationViewModels :[ConversationViewModel] + + let conversationsObservable :Observable<[ConversationViewModel]> + + init(withConversationsService conversationsService: ConversationsService) { + self.conversationsService = conversationsService + + var conversationViewModels = [ConversationViewModel]() + self.conversationViewModels = conversationViewModels + + //Create observable from sorted conversations and flatMap them to view models + self.conversationsObservable = self.conversationsService.conversations.asObservable().map({ conversations in + return conversations.sorted(by: { conversation1, conversations2 in + + guard let lastMessage1 = conversation1.messages.last, + let lastMessage2 = conversations2.messages.last else { + return true + } + + return lastMessage1.receivedDate > lastMessage2.receivedDate + }).flatMap({ conversationModel in + + var conversationViewModel: ConversationViewModel? + + //Get the current ConversationViewModel if exists or create it + if let foundConversationViewModel = conversationViewModels.filter({ conversationViewModel in + return conversationViewModel.conversation === conversationModel + }).first { + conversationViewModel = foundConversationViewModel + } else { + conversationViewModel = ConversationViewModel(withConversation: conversationModel) + conversationViewModels.append(conversationViewModel!) + } + + return conversationViewModel + }) + }).observeOn(MainScheduler.instance) + } + +}