diff --git a/.clang-format b/.clang-format index 0ef36b1e5..6d3ed1c24 100644 --- a/.clang-format +++ b/.clang-format @@ -1,14 +1,10 @@ --- -BasedOnStyle: Chromium -AlignTrailingComments: true -AlignConsecutiveAssignments: true -AllowShortIfStatementsOnASingleLine: false -BreakBeforeBraces: Attach +BasedOnStyle: WebKit +AllowShortFunctionsOnASingleLine: false BinPackArguments: false BinPackParameters: false ColumnLimit: 120 -IndentWidth: 4 -KeepEmptyLinesAtTheStartOfBlocks: false +IndentCaseLabels: true MaxEmptyLinesToKeep: 2 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true diff --git a/Podfile b/Podfile index 0f195570a..aefcc93b4 100644 --- a/Podfile +++ b/Podfile @@ -9,7 +9,7 @@ target 'Signal' do pod 'FFCircularProgressView', '~> 0.5' pod 'SCWaveformView', '~> 1.0' pod 'DJWActionSheet' - pod 'JSQMessagesViewController', :git => 'https://github.com/WhisperSystems/JSQMessagesViewController', :branch => 'JSignalQ' + pod 'JSQMessagesViewController' target 'SignalTests' do inherit! :search_paths end diff --git a/Podfile.lock b/Podfile.lock index 22f0ddbbc..17f976764 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -30,7 +30,7 @@ PODS: - DJWActionSheet (1.0.4) - FFCircularProgressView (0.5) - HKDFKit (0.0.3) - - JSQMessagesViewController (7.1.0): + - JSQMessagesViewController (7.3.3): - JSQSystemSoundPlayer (~> 2.0.1) - JSQSystemSoundPlayer (2.0.1) - libPhoneNumber-iOS (0.8.14) @@ -115,7 +115,7 @@ PODS: DEPENDENCIES: - DJWActionSheet - FFCircularProgressView (~> 0.5) - - JSQMessagesViewController (from `https://github.com/WhisperSystems/JSQMessagesViewController`, branch `JSignalQ`) + - JSQMessagesViewController - OpenSSL (~> 1.0.208) - PastelogKit (~> 1.3) - SCWaveformView (~> 1.0) @@ -123,18 +123,12 @@ DEPENDENCIES: - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) EXTERNAL SOURCES: - JSQMessagesViewController: - :branch: JSignalQ - :git: https://github.com/WhisperSystems/JSQMessagesViewController SignalServiceKit: :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :git: https://github.com/facebook/SocketRocket.git CHECKOUT OPTIONS: - JSQMessagesViewController: - :commit: 225b1baa11125ea84d4b960d700834b5b0a40ee1 - :git: https://github.com/WhisperSystems/JSQMessagesViewController SignalServiceKit: :commit: f537b6f19265b0f0845f15b3155cdac4f1913dc6 :git: https://github.com/WhisperSystems/SignalServiceKit.git @@ -150,7 +144,7 @@ SPEC CHECKSUMS: DJWActionSheet: 2fe54b1298a7f0fe44462233752c76a530e0cd80 FFCircularProgressView: 683a4ab1e1bd613246a3dffa61503ffdebcde8d8 HKDFKit: c058305d6f64b84f28c50bd7aa89574625bcb62a - JSQMessagesViewController: ca11f86fa68ca70835f05e169df9244147c1dc40 + JSQMessagesViewController: 0ee3f80237268192a3e8337fd0d787f1a1bf5a7a JSQSystemSoundPlayer: c5850e77a4363ffd374cd851154b9af93264ed8d libPhoneNumber-iOS: fb165271ebe7fb32e55da97b83219382f2f9d409 Mantle: bc40bb061d8c2c6fb48d5083e04d928c3b7f73d9 @@ -167,6 +161,6 @@ SPEC CHECKSUMS: UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d YapDatabase: 713d4018cfacbd6e77dd430710ca84730e450980 -PODFILE CHECKSUM: 860bce87f11d7ce3a8a80c10f8d35ef83699531e +PODFILE CHECKSUM: 060ff4edf8b7a110984cb2c1ffef3f6e19a6b8b6 COCOAPODS: 1.0.1 diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 49efdecbb..0e42bef81 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -9,9 +9,24 @@ /* Begin PBXBuildFile section */ 0DD55B166906AF3368995978 /* libPods-Signal.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 80CD5E19DD23200E7926EEA7 /* libPods-Signal.a */; }; 30209C98DABCE82064B4EAF5 /* libPods-SignalTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A33D3C7EB4B17BDBD47F0FCC /* libPods-SignalTests.a */; }; + 453D28B31D32B87100D523F0 /* OWSErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B01D32B87100D523F0 /* OWSErrorMessage.m */; }; + 453D28B41D32B87100D523F0 /* OWSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */; }; + 453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */; }; + 453D28BA1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; }; + 453D28BB1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */ = {isa = PBXBuildFile; fileRef = 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */; }; 45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; 45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */; }; 45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */; }; + 45C681B71D305A580050903A /* OWSCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681B61D305A580050903A /* OWSCall.m */; }; + 45C681B81D305A580050903A /* OWSCall.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681B61D305A580050903A /* OWSCall.m */; }; + 45C681BC1D305C080050903A /* OWSCallCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */; }; + 45C681BD1D305C080050903A /* OWSCallCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */; }; + 45C681C41D305C9E0050903A /* OWSCallCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */; }; + 45C681C51D305C9E0050903A /* OWSCallCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */; }; + 45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681C21D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m */; }; + 45C681C71D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45C681C21D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m */; }; + 45C681C81D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */; }; + 45C681C91D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */; }; 45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; }; 4CE0E3771B954546007210CF /* TSAnimatedAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E3761B954546007210CF /* TSAnimatedAdapter.m */; }; 701231B518ECAA4500D456C4 /* EvpMessageDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 701231B418ECAA4500D456C4 /* EvpMessageDigest.m */; }; @@ -131,7 +146,6 @@ A5509ECD1A69B1D600ABA4BC /* CountryCodeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */; }; A56977911A351BC400173BF2 /* ScanIdentityBarcodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */; }; A56977921A351BC400173BF2 /* PresentIdentityQRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */; }; - A5988A811A8A70D0002AD6BE /* UIButton+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = A5988A801A8A70D0002AD6BE /* UIButton+OWS.m */; }; A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */; }; A5E9D4BB1A65FAD800E4481C /* TSVideoAttachmentAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = A5E9D4B91A65FAD800E4481C /* TSVideoAttachmentAdapter.m */; }; AD41D7B51A6F6F0600241130 /* play_button.png in Resources */ = {isa = PBXBuildFile; fileRef = AD41D7B31A6F6F0600241130 /* play_button.png */; }; @@ -327,7 +341,6 @@ B660F7701C29988E00687D6E /* FLAnimatedImage.m in Sources */ = {isa = PBXBuildFile; fileRef = B68EF9B71C0B1EBD009C3DCD /* FLAnimatedImage.m */; }; B660F7711C29988E00687D6E /* FLAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = B68EF9B91C0B1EBD009C3DCD /* FLAnimatedImageView.m */; }; B660F7721C29988E00687D6E /* AppStoreRating.m in Sources */ = {isa = PBXBuildFile; fileRef = B6DA6B061B8A2F9A00CA6F98 /* AppStoreRating.m */; }; - B660F7731C29988E00687D6E /* UIButton+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = A5988A801A8A70D0002AD6BE /* UIButton+OWS.m */; }; B660F7741C29988E00687D6E /* DJWActionSheet+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = A547DD721A70A87800103EC7 /* DJWActionSheet+OWS.m */; }; B660F7751C29988E00687D6E /* UIColor+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = FCFA64B31A24F3880007FB87 /* UIColor+OWS.m */; }; B660F7761C29988E00687D6E /* UIFont+OWS.m in Sources */ = {isa = PBXBuildFile; fileRef = FCFA64B61A24F6730007FB87 /* UIFont+OWS.m */; }; @@ -491,10 +504,26 @@ /* Begin PBXFileReference section */ 453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = ""; }; + 453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSErrorMessage.h; sourceTree = ""; }; + 453D28B01D32B87100D523F0 /* OWSErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSErrorMessage.m; sourceTree = ""; }; + 453D28B11D32B87100D523F0 /* OWSInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSInfoMessage.h; sourceTree = ""; }; + 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSInfoMessage.m; sourceTree = ""; }; + 453D28B51D32BA5F00D523F0 /* OWSDisplayedMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisplayedMessage.h; sourceTree = ""; }; + 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisplayedMessage.m; sourceTree = ""; }; + 453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessagesBubblesSizeCalculator.h; sourceTree = ""; }; + 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessagesBubblesSizeCalculator.m; sourceTree = ""; }; 454B35071D08EED80026D658 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = translations/mk.lproj/Localizable.strings; sourceTree = ""; }; 45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = ""; }; 45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = ""; }; 45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = ""; }; + 45C681B51D305A580050903A /* OWSCall.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCall.h; sourceTree = ""; }; + 45C681B61D305A580050903A /* OWSCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCall.m; sourceTree = ""; }; + 45C681B91D305C080050903A /* OWSCallCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSCallCollectionViewCell.h; sourceTree = ""; }; + 45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCallCollectionViewCell.m; sourceTree = ""; }; + 45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSCallCollectionViewCell.xib; sourceTree = ""; }; + 45C681C11D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSDisplayedMessageCollectionViewCell.h; sourceTree = ""; }; + 45C681C21D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSDisplayedMessageCollectionViewCell.m; sourceTree = ""; }; + 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSDisplayedMessageCollectionViewCell.xib; sourceTree = ""; }; 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = "Launch Screen.storyboard"; path = "Signal/src/util/Launch Screen.storyboard"; sourceTree = SOURCE_ROOT; }; 45E282DE1D08E67800ADD4C8 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = translations/gl.lproj/Localizable.strings; sourceTree = ""; }; 45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = ""; }; @@ -723,8 +752,6 @@ A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScanIdentityBarcodeViewController.m; sourceTree = ""; }; A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentIdentityQRCodeViewController.m; sourceTree = ""; }; A56977901A351BC400173BF2 /* PresentIdentityQRCodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentIdentityQRCodeViewController.h; sourceTree = ""; }; - A5988A7F1A8A70D0002AD6BE /* UIButton+OWS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIButton+OWS.h"; path = "util/UIButton+OWS.h"; sourceTree = ""; }; - A5988A801A8A70D0002AD6BE /* UIButton+OWS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIButton+OWS.m"; path = "util/UIButton+OWS.m"; sourceTree = ""; }; A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ShowGroupMembersViewController.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; A5D0699A1A50E9CB004CB540 /* ShowGroupMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowGroupMembersViewController.h; sourceTree = ""; }; A5E9D4B91A65FAD800E4481C /* TSVideoAttachmentAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSVideoAttachmentAdapter.m; sourceTree = ""; }; @@ -1094,6 +1121,16 @@ isa = PBXGroup; children = ( B62D53F41A23CC8B009AAF82 /* TSMessageAdapters */, + 453D28B51D32BA5F00D523F0 /* OWSDisplayedMessage.h */, + 453D28B61D32BA5F00D523F0 /* OWSDisplayedMessage.m */, + 453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */, + 453D28B01D32B87100D523F0 /* OWSErrorMessage.m */, + 453D28B11D32B87100D523F0 /* OWSInfoMessage.h */, + 453D28B21D32B87100D523F0 /* OWSInfoMessage.m */, + 45C681B51D305A580050903A /* OWSCall.h */, + 45C681B61D305A580050903A /* OWSCall.m */, + 453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */, + 453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */, ); path = Models; sourceTree = ""; @@ -1638,6 +1675,12 @@ 76EB052B18170B33006006FC /* Views */ = { isa = PBXGroup; children = ( + 45C681C11D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.h */, + 45C681C21D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m */, + 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */, + 45C681B91D305C080050903A /* OWSCallCollectionViewCell.h */, + 45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */, + 45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */, A5509ECB1A69B1D600ABA4BC /* CountryCodeTableViewCell.h */, A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */, FCAC963D19FEF99A0046DFC5 /* InboxTableViewCell.h */, @@ -2199,8 +2242,6 @@ FCFA64B11A24F29E0007FB87 /* UI Categories */ = { isa = PBXGroup; children = ( - A5988A7F1A8A70D0002AD6BE /* UIButton+OWS.h */, - A5988A801A8A70D0002AD6BE /* UIButton+OWS.m */, A547DD731A70A87800103EC7 /* DJWActionSheet+OWS.h */, A547DD721A70A87800103EC7 /* DJWActionSheet+OWS.m */, FCFA64B21A24F3880007FB87 /* UIColor+OWS.h */, @@ -2399,6 +2440,7 @@ A5509ECA1A69AB8B00ABA4BC /* Storyboard.storyboard in Resources */, A507A3B11A6C60E300BEED0D /* InboxTableViewCell.xib in Resources */, AD83FF421A73426500B5C81A /* audio_play_button.png in Resources */, + 45C681C41D305C9E0050903A /* OWSCallCollectionViewCell.xib in Resources */, B633C5C41A1D190B0059AC12 /* mute_on@2x.png in Resources */, B633C5CE1A1D190B0059AC12 /* quit@2x.png in Resources */, AD83FF441A73426500B5C81A /* audio_pause_button.png in Resources */, @@ -2407,6 +2449,7 @@ B633C59D1A1D190B0059AC12 /* endcall@2x.png in Resources */, FC5CDF391A3393DD00B47253 /* error_white@2x.png in Resources */, B633C5D21A1D190B0059AC12 /* savephoto@2x.png in Resources */, + 45C681C81D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */, B10C9B611A7049EC00ECA2BF /* play_icon.png in Resources */, AD83FF401A73426500B5C81A /* audio_pause_button_blue@2x.png in Resources */, B66DBF4A19D5BBC8006EA940 /* Images.xcassets in Resources */, @@ -2443,6 +2486,8 @@ files = ( B660F6D41C29868000687D6E /* whisperFake.cer in Resources */, 76EB060118170B33006006FC /* InitiateSignal.proto in Resources */, + 45C681C91D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */, + 45C681C51D305C9E0050903A /* OWSCallCollectionViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2578,7 +2623,6 @@ B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */, 76EB05D618170B33006006FC /* ZrtpResponder.m in Sources */, B62D53F71A23CCAD009AAF82 /* TSMessageAdapter.m in Sources */, - A5988A811A8A70D0002AD6BE /* UIButton+OWS.m in Sources */, FCD274EB1A5AFDDB00202277 /* AboutTableViewController.m in Sources */, E197B61618BBEC1A00F073E5 /* StretchFactorController.m in Sources */, FCFD257F1A154B2C00F4C644 /* RegistrationViewController.m in Sources */, @@ -2601,6 +2645,7 @@ 76EB05A618170B33006006FC /* RtpPacket.m in Sources */, 76EB064218170B33006006FC /* StringUtil.m in Sources */, A547DD741A70A87800103EC7 /* DJWActionSheet+OWS.m in Sources */, + 45C681B71D305A580050903A /* OWSCall.m in Sources */, 76EB062618170B33006006FC /* Queue.m in Sources */, D221A09A169C9E5E00537ABF /* main.m in Sources */, 45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */, @@ -2611,15 +2656,18 @@ 76EB05FE18170B33006006FC /* InitiateSignal.pb.m in Sources */, 76EB05CA18170B33006006FC /* RecipientUnavailable.m in Sources */, E197B61418BBEC1A00F073E5 /* DropoutTracker.m in Sources */, + 453D28B41D32B87100D523F0 /* OWSInfoMessage.m in Sources */, FCAC963C19FEF9280046DFC5 /* SignalsViewController.m in Sources */, 76EB05DA18170B33006006FC /* LowLatencyConnector.m in Sources */, 76EB05EE18170B33006006FC /* CallTermination.m in Sources */, B66B9F7D1AEAF40500E2E609 /* NotificationSettingsOptionsViewController.m in Sources */, + 453D28B31D32B87100D523F0 /* OWSErrorMessage.m in Sources */, E1CD329618BCFF9900B1A496 /* SoundInstance.m in Sources */, 76EB05B418170B33006006FC /* HashChain.m in Sources */, 76EB05E418170B33006006FC /* UdpSocket.m in Sources */, 76EB058218170B33006006FC /* Environment.m in Sources */, 76EB064418170B33006006FC /* ThreadManager.m in Sources */, + 45C681C61D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */, E197B61E18BBEC6D00F073E5 /* AudioRouter.m in Sources */, E197B60D18BBEC1A00F073E5 /* AudioSocket.m in Sources */, A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */, @@ -2632,6 +2680,7 @@ B63761ED19E1FBE8005735D1 /* HttpRequestOrResponse.m in Sources */, 76EB05A018170B33006006FC /* IpAddress.m in Sources */, FCAC965119FF0A6E0046DFC5 /* MessagesViewController.m in Sources */, + 453D28BA1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */, B68EF9BB1C0B1EBD009C3DCD /* FLAnimatedImageView.m in Sources */, A5E9D4BB1A65FAD800E4481C /* TSVideoAttachmentAdapter.m in Sources */, E197B61118BBEC1A00F073E5 /* AudioProcessor.m in Sources */, @@ -2670,6 +2719,7 @@ E16E5BF018AAC40200B7C403 /* EvpKeyAgreement.m in Sources */, FCFD25821A154B3800F4C644 /* CodeVerificationViewController.m in Sources */, B65EDA1219E1BE6400AAA7CB /* RPAPICall.m in Sources */, + 453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */, 76EB05DC18170B33006006FC /* StreamPair.m in Sources */, 76EB064618170B33006006FC /* TimeUtil.m in Sources */, 70BAFD5D190584BE00FA5E0B /* NotificationTracker.m in Sources */, @@ -2707,6 +2757,7 @@ B63761E319E1F487005735D1 /* AFHTTPSessionManager+SignalMethods.m in Sources */, 76EB05CC18170B33006006FC /* ShortAuthenticationStringGenerator.m in Sources */, E16E5BEF18AAC40200B7C403 /* EC25KeyAgreementProtocol.m in Sources */, + 45C681BC1D305C080050903A /* OWSCallCollectionViewCell.m in Sources */, 76EB064018170B33006006FC /* AnonymousTerminator.m in Sources */, 76EB058818170B33006006FC /* PropertyListPreferences.m in Sources */, 76EB05B218170B33006006FC /* DH3KKeyAgreementProtocol.m in Sources */, @@ -2785,6 +2836,7 @@ B660F7341C29988E00687D6E /* RtpSocket.m in Sources */, B660F7351C29988E00687D6E /* SequenceCounter.m in Sources */, B660F7361C29988E00687D6E /* SrtpSocket.m in Sources */, + 45C681C71D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.m in Sources */, B660F7371C29988E00687D6E /* SrtpStream.m in Sources */, B660F7381C29988E00687D6E /* DH3KKeyAgreementParticipant.m in Sources */, B660F7391C29988E00687D6E /* DH3KKeyAgreementProtocol.m in Sources */, @@ -2805,6 +2857,7 @@ B660F7481C29988E00687D6E /* RecipientUnavailable.m in Sources */, 45843D201D2236B30013E85A /* OWSContactsSearcher.m in Sources */, B660F7491C29988E00687D6E /* ShortAuthenticationStringGenerator.m in Sources */, + 45C681BD1D305C080050903A /* OWSCallCollectionViewCell.m in Sources */, B660F74A1C29988E00687D6E /* ZrtpHandshakeResult.m in Sources */, B660F74B1C29988E00687D6E /* ZrtpHandshakeSocket.m in Sources */, B660F74C1C29988E00687D6E /* ZrtpInitiator.m in Sources */, @@ -2815,6 +2868,7 @@ B660F7511C29988E00687D6E /* StreamPair.m in Sources */, B660F7521C29988E00687D6E /* Certificate.m in Sources */, B660F7531C29988E00687D6E /* NetworkStream.m in Sources */, + 453D28BB1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */, B660F7541C29988E00687D6E /* SecureEndPoint.m in Sources */, B660F7551C29988E00687D6E /* UdpSocket.m in Sources */, 45843D221D223BA10013E85A /* OWSContactsSearcherTest.m in Sources */, @@ -2835,6 +2889,7 @@ B660F7641C29988E00687D6E /* InitiateSignal.pb.m in Sources */, B660F7651C29988E00687D6E /* InitiatorSessionDescriptor.m in Sources */, B660F7661C29988E00687D6E /* ResponderSessionDescriptor.m in Sources */, + 45C681B81D305A580050903A /* OWSCall.m in Sources */, B660F7671C29988E00687D6E /* SignalUtil.m in Sources */, B660F7681C29988E00687D6E /* CategorizingLogger.m in Sources */, B660F7691C29988E00687D6E /* DecayingSampleEstimator.m in Sources */, @@ -2847,7 +2902,6 @@ B660F7701C29988E00687D6E /* FLAnimatedImage.m in Sources */, B660F7711C29988E00687D6E /* FLAnimatedImageView.m in Sources */, B660F7721C29988E00687D6E /* AppStoreRating.m in Sources */, - B660F7731C29988E00687D6E /* UIButton+OWS.m in Sources */, B660F7741C29988E00687D6E /* DJWActionSheet+OWS.m in Sources */, B660F7751C29988E00687D6E /* UIColor+OWS.m in Sources */, B660F7761C29988E00687D6E /* UIFont+OWS.m in Sources */, diff --git a/Signal/src/Models/OWSCall.h b/Signal/src/Models/OWSCall.h new file mode 100644 index 000000000..cd296bafa --- /dev/null +++ b/Signal/src/Models/OWSCall.h @@ -0,0 +1,60 @@ +// Created by Dylan Bourgeois on 20/11/14. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "TSMessageAdapter.h" +#import +#import + +typedef enum : NSUInteger { + kCallOutgoing = 1, + kCallIncoming = 2, + kCallMissed = 3, + kGroupUpdateJoin = 4, + kGroupUpdateLeft = 5, + kGroupUpdate = 6 +} CallStatus; + +@interface OWSCall : NSObject + +/* + * Returns the string Id of the user who initiated the call + */ +@property (copy, nonatomic, readonly) NSString *senderId; + +/* + * Returns the display name for user who initiated the call + */ +@property (copy, nonatomic, readonly) NSString *senderDisplayName; + +/* + * Returns date of the call + */ +@property (copy, nonatomic, readonly) NSDate *date; + +/* + * Returns the call status + * @see CallStatus + */ +@property (nonatomic) CallStatus status; + +/* + * Returns message type for adapter + */ +@property (nonatomic) TSMessageAdapterType messageType; + +/** + * String to be displayed + */ +@property (nonatomic, copy) NSString *detailString; + +#pragma mark - Initialization + +- (instancetype)initWithCallerId:(NSString *)callerId + callerDisplayName:(NSString *)callerDisplayName + date:(NSDate *)date + status:(CallStatus)status + displayString:(NSString *)detailString NS_DESIGNATED_INITIALIZER; + +- (NSString *)dateText; + +@end diff --git a/Signal/src/Models/OWSCall.m b/Signal/src/Models/OWSCall.m new file mode 100644 index 000000000..b68d5f5e9 --- /dev/null +++ b/Signal/src/Models/OWSCall.m @@ -0,0 +1,144 @@ +// Created by Dylan Bourgeois on 20/11/14. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSCall.h" +#import +#import + +@implementation OWSCall + +#pragma mark - Initialzation + +- (id)init +{ + NSAssert(NO, + @"%s is not a valid initializer for %@. Use %@ instead", + __PRETTY_FUNCTION__, + [self class], + NSStringFromSelector(@selector(initWithCallerId:callerDisplayName:date:status:displayString:))); + return [self initWithCallerId:nil callerDisplayName:nil date:nil status:0 displayString:nil]; +} + +- (instancetype)initWithCallerId:(NSString *)senderId + callerDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date + status:(CallStatus)status + displayString:(NSString *)detailString +{ + NSParameterAssert(senderId != nil); + NSParameterAssert(senderDisplayName != nil); + + self = [super init]; + if (!self) { + return self; + } + + _senderId = [senderId copy]; + _senderDisplayName = [senderDisplayName copy]; + _date = [date copy]; + _status = status; + _messageType = TSCallAdapter; + + // TODO interpret detailString from status. make sure it works for calls and + // our re-use of calls as group update display + _detailString = [detailString stringByAppendingFormat:@" "]; + + return self; +} + +- (NSString *)dateText +{ + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.timeStyle = NSDateFormatterShortStyle; + dateFormatter.dateStyle = NSDateFormatterMediumStyle; + dateFormatter.doesRelativeDateFormatting = YES; + return [dateFormatter stringFromDate:_date]; +} + +#pragma mark - NSObject + +- (BOOL)isEqual:(id)object +{ + if (self == object) { + return YES; + } + + if (![object isKindOfClass:[self class]]) { + return NO; + } + + OWSCall *aCall = (OWSCall *)object; + + return [self.senderId isEqualToString:aCall.senderId] && + [self.senderDisplayName isEqualToString:aCall.senderDisplayName] + && ([self.date compare:aCall.date] == NSOrderedSame) && self.status == aCall.status; +} + +- (NSUInteger)hash +{ + return self.senderId.hash ^ self.date.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@>", + [self class], + self.senderId, + self.senderDisplayName, + self.date]; +} + +#pragma mark - JSQMessageData + +- (BOOL)isMediaMessage +{ + return NO; +} + +#pragma mark - NSCoding + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + NSString *senderId = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderId))]; + NSString *senderDisplayName = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderDisplayName))]; + NSDate *date = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(date))]; + CallStatus status = (CallStatus)[aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(status))]; + NSString *displayString = @""; // FIXME what should this be? + + return [self initWithCallerId:senderId + callerDisplayName:senderDisplayName + date:date + status:status + displayString:displayString]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:self.senderId forKey:NSStringFromSelector(@selector(senderId))]; + [aCoder encodeObject:self.senderDisplayName forKey:NSStringFromSelector(@selector(senderDisplayName))]; + [aCoder encodeObject:self.date forKey:NSStringFromSelector(@selector(date))]; + [aCoder encodeDouble:self.status forKey:NSStringFromSelector(@selector(status))]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone +{ + return [[[self class] allocWithZone:zone] initWithCallerId:self.senderId + callerDisplayName:self.senderDisplayName + date:self.date + status:self.status + displayString:self.detailString]; +} + +- (NSUInteger)messageHash +{ + return self.hash; +} + +- (NSString *)text +{ + return _detailString; +} + +@end diff --git a/Signal/src/Models/OWSDisplayedMessage.h b/Signal/src/Models/OWSDisplayedMessage.h new file mode 100644 index 000000000..a34a9ccda --- /dev/null +++ b/Signal/src/Models/OWSDisplayedMessage.h @@ -0,0 +1,38 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "JSQMessageData.h" +#import + +/* OWSDisplayedMessage message is the parent class for displaying information to the user + * from within the conversation view. Do not use directly : + * + * @see OWSInfoMessage + * @see OWSErrorMessage + * + */ +@interface OWSDisplayedMessage : NSObject + +/* + * Returns the unique identifier of the person affected by the displayed message + */ +@property (copy, nonatomic, readonly) NSString *senderId; + +/* + * Returns the name of the person affected by the displayed message + */ +@property (copy, nonatomic, readonly) NSString *senderDisplayName; + +/* + * Returns date of the displayed message + */ +@property (copy, nonatomic, readonly) NSDate *date; + +#pragma mark - Initializer + +- (instancetype)initWithSenderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date; + +@end diff --git a/Signal/src/Models/OWSDisplayedMessage.m b/Signal/src/Models/OWSDisplayedMessage.m new file mode 100644 index 000000000..983921e53 --- /dev/null +++ b/Signal/src/Models/OWSDisplayedMessage.m @@ -0,0 +1,45 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSDisplayedMessage.h" + +@implementation OWSDisplayedMessage + +- (id)init +{ + NSAssert(NO, + @"%s is not a valid initializer for %@. Use %@ instead", + __PRETTY_FUNCTION__, + [self class], + NSStringFromSelector(@selector(initWithSenderId:senderDisplayName:date:))); + return nil; +} + +- (instancetype)initWithSenderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date +{ + self = [super init]; + if (!self) { + return self; + } + + _senderId = [senderId copy]; + _senderDisplayName = [senderDisplayName copy]; + _date = [date copy]; + + return self; +} + +- (NSUInteger)messageHash +{ + return self.date.hash ^ self.senderId.hash; +} + +- (BOOL)isMediaMessage +{ + return NO; +} + +@end diff --git a/Signal/src/Models/OWSErrorMessage.h b/Signal/src/Models/OWSErrorMessage.h new file mode 100644 index 000000000..b35f76ee0 --- /dev/null +++ b/Signal/src/Models/OWSErrorMessage.h @@ -0,0 +1,32 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSDisplayedMessage.h" +#import "TSMessageAdapter.h" + +typedef NS_ENUM(NSInteger, OWSErrorMessageType) { + OWSErrorMessageNoSession, + OWSErrorMessageWrongTrustedIdentityKey, + OWSErrorMessageInvalidKeyException, + OWSErrorMessageMissingKeyId, + OWSErrorMessageInvalidMessage, + OWSErrorMessageDuplicateMessage, + OWSErrorMessageInvalidVersion +}; + +@interface OWSErrorMessage : OWSDisplayedMessage + +@property (nonatomic) OWSErrorMessageType errorMessageType; +@property (nonatomic) TSMessageAdapterType messageType; + +#pragma mark - Initialization + +- (instancetype)initWithErrorType:(OWSErrorMessageType)messageType + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date; + +- (NSString *)text; + +@end diff --git a/Signal/src/Models/OWSErrorMessage.m b/Signal/src/Models/OWSErrorMessage.m new file mode 100644 index 000000000..522c0adfe --- /dev/null +++ b/Signal/src/Models/OWSErrorMessage.m @@ -0,0 +1,75 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. + +#import "OWSErrorMessage.h" + +@implementation OWSErrorMessage + +- (instancetype)initWithErrorType:(OWSErrorMessageType)messageType + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date +{ + self = [super initWithSenderId:senderId senderDisplayName:senderDisplayName date:date]; + if (!self) { + return self; + } + + _errorMessageType = messageType; + _messageType = TSErrorMessageAdapter; + + return self; +} + +- (NSString *)text +{ + switch (self.errorMessageType) { + case OWSErrorMessageNoSession: + return [NSString stringWithFormat:@"No session error"]; + break; + case OWSErrorMessageWrongTrustedIdentityKey: + return [NSString stringWithFormat:@"Error : Wrong trusted identity key for %@.", self.senderDisplayName]; + break; + case OWSErrorMessageInvalidKeyException: + return [NSString stringWithFormat:@"Error : Invalid key exception for %@.", self.senderDisplayName]; + break; + case OWSErrorMessageMissingKeyId: + return [NSString stringWithFormat:@"Error: Missing key identifier for %@", self.senderDisplayName]; + break; + case OWSErrorMessageInvalidMessage: + return [NSString stringWithFormat:@"Error: Invalid message"]; + break; + case OWSErrorMessageDuplicateMessage: + return [NSString stringWithFormat:@"Error: Duplicate message"]; + break; + case OWSErrorMessageInvalidVersion: + return [NSString stringWithFormat:@"Error: Invalid version for contact %@.", self.senderDisplayName]; + break; + + default: + return nil; + break; + } +} + +- (NSUInteger)hash +{ + return self.senderId.hash ^ self.date.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@, type=%ld>", + [self class], + self.senderId, + self.senderDisplayName, + self.date, + self.errorMessageType]; +} + +- (TSMessageAdapterType)messageType +{ + return TSErrorMessageAdapter; +} + +@end diff --git a/Signal/src/Models/OWSInfoMessage.h b/Signal/src/Models/OWSInfoMessage.h new file mode 100644 index 000000000..f6b704cdf --- /dev/null +++ b/Signal/src/Models/OWSInfoMessage.h @@ -0,0 +1,26 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSDisplayedMessage.h" +#import "TSMessageAdapter.h" + +typedef NS_ENUM(NSInteger, OWSInfoMessageType) { + OWSInfoMessageTypeSessionDidEnd, +}; + +@interface OWSInfoMessage : OWSDisplayedMessage + +@property (nonatomic) OWSInfoMessageType infoMessageType; +@property (nonatomic) TSMessageAdapterType messageType; + +#pragma mark - Initialization + +- (instancetype)initWithInfoType:(OWSInfoMessageType)messageType + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date; + +- (NSString *)text; + +@end diff --git a/Signal/src/Models/OWSInfoMessage.m b/Signal/src/Models/OWSInfoMessage.m new file mode 100644 index 000000000..bc6319b08 --- /dev/null +++ b/Signal/src/Models/OWSInfoMessage.m @@ -0,0 +1,55 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSInfoMessage.h" + +@implementation OWSInfoMessage + +- (instancetype)initWithInfoType:(OWSInfoMessageType)messageType + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date +{ + //@discussion: NSParameterAssert() ? + + self = [super initWithSenderId:senderId senderDisplayName:senderDisplayName date:date]; + if (!self) { + return self; + } + + _infoMessageType = messageType; + _messageType = TSInfoMessageAdapter; + + return self; +} + +- (NSString *)text +{ + switch (self.infoMessageType) { + case OWSInfoMessageTypeSessionDidEnd: + return [NSString stringWithFormat:@"Session with %@ ended.", self.senderDisplayName]; + break; + + default: + return nil; + break; + } +} + +- (NSUInteger)hash +{ + return self.senderId.hash ^ self.date.hash; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@, type=%ld>", + [self class], + self.senderId, + self.senderDisplayName, + self.date, + self.infoMessageType]; +} + +@end diff --git a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.h b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.h new file mode 100644 index 000000000..af1419212 --- /dev/null +++ b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.h @@ -0,0 +1,7 @@ +// Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import + +@interface OWSMessagesBubblesSizeCalculator : JSQMessagesBubblesSizeCalculator + +@end diff --git a/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m new file mode 100644 index 000000000..2615cc552 --- /dev/null +++ b/Signal/src/Models/OWSMessagesBubblesSizeCalculator.m @@ -0,0 +1,36 @@ +// Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSMessagesBubblesSizeCalculator.h" +#import "OWSDisplayedMessageCollectionViewCell.h" +#import "TSMessageAdapter.h" + +@implementation OWSMessagesBubblesSizeCalculator + +/** + * Computes and returns the size of the `messageBubbleImageView` property + * of a `JSQMessagesCollectionViewCell` for the specified messageData at indexPath. + * + * @param messageData A message data object. + * @param indexPath The index path at which messageData is located. + * @param layout The layout object asking for this information. + * + * @return A sizes that specifies the required dimensions to display the entire message contents. + * Note, this is *not* the entire cell, but only its message bubble. + */ +- (CGSize)messageBubbleSizeForMessageData:(id)messageData + atIndexPath:(NSIndexPath *)indexPath + withLayout:(JSQMessagesCollectionViewFlowLayout *)layout +{ + CGSize superSize = [super messageBubbleSizeForMessageData:messageData atIndexPath:indexPath withLayout:layout]; + + TSMessageAdapter *message = (TSMessageAdapter *)messageData; + if (message.messageType == TSInfoMessageAdapter || message.messageType == TSErrorMessageAdapter) { + + // Prevent cropping message text by accounting for message container/icon + superSize.height = OWSDisplayedMessageCellHeight; + } + + return superSize; +} + +@end diff --git a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m index 68d885176..8faad7335 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSAnimatedAdapter.m @@ -7,10 +7,9 @@ // #import "TSAnimatedAdapter.h" - #import "FLAnimatedImage.h" -#import "JSQMessagesMediaViewBubbleImageMasker.h" #import "UIDevice+TSHardwareVersion.h" +#import @interface TSAnimatedAdapter () diff --git a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h index c1ccdefa0..bf660d4d3 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h @@ -8,13 +8,22 @@ #import #import - #import "TSInteraction.h" #import "TSMessageAdapter.h" #import "TSThread.h" #define ME_MESSAGE_IDENTIFIER @"Me"; +typedef NS_ENUM(NSInteger, TSMessageAdapterType) { + TSIncomingMessageAdapter, + TSOutgoingMessageAdapter, + TSCallAdapter, + TSInfoMessageAdapter, + TSErrorMessageAdapter, + TSMediaAttachmentAdapter, + TSGenericTextMessageAdapter, // Used when message direction is unknown (outgoing or incoming) +}; + @interface TSMessageAdapter : NSObject + (id)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread; diff --git a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m index 8a7c051c0..c5f0fe389 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m @@ -6,9 +6,9 @@ // Copyright (c) 2014 Open Whisper Systems. All rights reserved. // -#import "JSQCall.h" #import "TSAttachmentPointer.h" #import "TSCall.h" +#import "OWSCall.h" #import "TSContentAdapters.h" #import "TSErrorMessage.h" #import "TSIncomingMessage.h" @@ -58,6 +58,7 @@ + (id)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread { TSMessageAdapter *adapter = [[TSMessageAdapter alloc] init]; adapter.messageDate = interaction.date; + // TODO casting a string to an integer? At least need a comment here explaining why we are doing this. adapter.identifier = (NSUInteger)interaction.uniqueId; if ([thread isKindOfClass:[TSContactThread class]]) { @@ -136,8 +137,7 @@ } else if ([interaction isKindOfClass:[TSCall class]]) { adapter.messageBody = @"Placeholder for TSCalls"; adapter.messageType = TSCallAdapter; - JSQCall *call = [self jsqCallForTSCall:(TSCall *)interaction thread:(TSContactThread *)thread]; - call.useThumbnail = NO; // disables use of iconography to represent group update actions + OWSCall *call = [self owsCallForTSCall:(TSCall *)interaction thread:(TSContactThread *)thread]; return call; } else if ([interaction isKindOfClass:[TSInfoMessage class]]) { TSInfoMessage *infoMessage = (TSInfoMessage *)interaction; @@ -154,12 +154,11 @@ } else if (adapter.infoMessageType == TSInfoMessageTypeGroupUpdate) { status = kGroupUpdate; } - JSQCall *call = [[JSQCall alloc] initWithCallerId:@"" + OWSCall *call = [[OWSCall alloc] initWithCallerId:@"" callerDisplayName:adapter.messageBody date:nil status:status displayString:@""]; - call.useThumbnail = NO; // disables use of iconography to represent group update actions return call; } } else { @@ -176,7 +175,7 @@ return adapter; } -+ (JSQCall *)jsqCallForTSCall:(TSCall *)call thread:(TSContactThread *)thread { ++ (OWSCall *)owsCallForTSCall:(TSCall *)call thread:(TSContactThread *)thread { CallStatus status = 0; NSString *name = thread.name; NSString *detailString = @""; @@ -210,12 +209,12 @@ break; } - JSQCall *jsqCall = [[JSQCall alloc] initWithCallerId:thread.contactIdentifier + OWSCall *owsCall = [[OWSCall alloc] initWithCallerId:thread.contactIdentifier callerDisplayName:thread.name date:call.date status:status displayString:detailString]; - return jsqCall; + return owsCall; } - (NSString *)senderId { @@ -230,7 +229,7 @@ if (self.thread) { return _thread.name; } - return self.senderDisplayName; + return _senderDisplayName; } - (NSDate *)date { @@ -249,8 +248,13 @@ return self.messageBody; } -- (NSUInteger)messageHash { - return self.identifier; +- (NSUInteger)messageHash +{ + if (self.isMediaMessage) { + return [self.mediaItem mediaHash]; + } else { + return self.identifier; + } } - (NSInteger)messageState { diff --git a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h index 3f7da68f5..533a083cf 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.h @@ -1,10 +1,5 @@ -// -// TSAttachementAdapter.h -// Signal -// // Created by Frederic Jacobs on 17/12/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// #import #import diff --git a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m index 001613217..fb436b0f0 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSPhotoAdapter.m @@ -1,15 +1,9 @@ -// -// TSAttachementAdapter.m -// Signal -// // Created by Frederic Jacobs on 17/12/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// #import "TSPhotoAdapter.h" - -#import "JSQMessagesMediaViewBubbleImageMasker.h" #import "UIDevice+TSHardwareVersion.h" +#import @interface TSPhotoAdapter () diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h index e9a8e7736..40e2deed4 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.h @@ -1,10 +1,5 @@ -// -// TSAttachementAdapter.h -// Signal -// // Created by Frederic Jacobs on 17/12/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// #import #import diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m index a8ac8c47f..e3c54d7a5 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m @@ -1,19 +1,13 @@ -// -// TSAttachementAdapter.m -// Signal -// // Created by Frederic Jacobs on 17/12/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// -#import "TSMessagesManager.h" #import "TSVideoAttachmentAdapter.h" - -#import "FFCircularProgressView.h" -#import "JSQMessagesMediaViewBubbleImageMasker.h" #import "MIMETypeUtil.h" -#import "SCWaveformView.h" +#import "TSMessagesManager.h" #import "TSStorageManager+keyingMaterial.h" +#import +#import +#import #define AUDIO_BAR_HEIGHT 36 @interface TSVideoAttachmentAdapter () diff --git a/Signal/src/UIColor+OWS.h b/Signal/src/UIColor+OWS.h index 4df424fa6..6e68a2938 100644 --- a/Signal/src/UIColor+OWS.h +++ b/Signal/src/UIColor+OWS.h @@ -11,21 +11,15 @@ @interface UIColor (OWS) + (UIColor *)ows_materialBlueColor; - + (UIColor *)ows_fadedBlueColor; - + (UIColor *)ows_darkBackgroundColor; - + (UIColor *)ows_darkGrayColor; - + (UIColor *)ows_yellowColor; - + (UIColor *)ows_greenColor; - + (UIColor *)ows_redColor; - + (UIColor *)ows_blackColor; - ++ (UIColor *)ows_errorMessageBorderColor; ++ (UIColor *)ows_infoMessageBorderColor; + (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier; @end diff --git a/Signal/src/UIColor+OWS.m b/Signal/src/UIColor+OWS.m index c77496644..299d14a0d 100644 --- a/Signal/src/UIColor+OWS.m +++ b/Signal/src/UIColor+OWS.m @@ -1,61 +1,74 @@ -// -// UIColor+UIColor_OWS.m -// Signal -// // Created by Dylan Bourgeois on 25/11/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// #import "Cryptography.h" #import "UIColor+OWS.h" @implementation UIColor (OWS) - -+ (UIColor *)ows_materialBlueColor { ++ (UIColor *)ows_materialBlueColor +{ // blue: #2090EA return [UIColor colorWithRed:32.f / 255.f green:144.f / 255.f blue:234.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_blackColor { ++ (UIColor *)ows_blackColor +{ // black: #080A00 return [UIColor colorWithRed:8.f / 255.f green:10.f / 255.f blue:0. / 255.f alpha:1.f]; } - -+ (UIColor *)ows_darkGrayColor { ++ (UIColor *)ows_darkGrayColor +{ return [UIColor colorWithRed:81.f / 255.f green:81.f / 255.f blue:81.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_darkBackgroundColor { ++ (UIColor *)ows_darkBackgroundColor +{ return [UIColor colorWithRed:35.f / 255.f green:31.f / 255.f blue:32.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_fadedBlueColor { ++ (UIColor *)ows_fadedBlueColor +{ // blue: #B6DEF4 return [UIColor colorWithRed:182.f / 255.f green:222.f / 255.f blue:244.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_yellowColor { ++ (UIColor *)ows_yellowColor +{ // gold: #FFBB5C return [UIColor colorWithRed:245.f / 255.f green:186.f / 255.f blue:98.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_greenColor { ++ (UIColor *)ows_greenColor +{ // green: #BF4240 return [UIColor colorWithRed:66.f / 255.f green:191.f / 255.f blue:64.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_redColor { ++ (UIColor *)ows_redColor +{ // red: #FF3867 return [UIColor colorWithRed:255. / 255.f green:56.f / 255.f blue:103.f / 255.f alpha:1.f]; } -+ (UIColor *)ows_lightBackgroundColor { ++ (UIColor *)ows_errorMessageBorderColor +{ + return [UIColor colorWithRed:195.f / 255.f green:0 blue:22.f / 255.f alpha:1.0f]; +} + ++ (UIColor *)ows_infoMessageBorderColor +{ + return [UIColor colorWithRed:239.f / 255.f green:189.f / 255.f blue:88.f / 255.f alpha:1.0f]; +} + ++ (UIColor *)ows_lightBackgroundColor +{ return [UIColor colorWithRed:242.f / 255.f green:242.f / 255.f blue:242.f / 255.f alpha:1.f]; } -+ (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier { ++ (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier +{ NSArray *colors = @[ [UIColor colorWithRed:204.f / 255.f green:148.f / 255.f blue:102.f / 255.f alpha:1.f], [UIColor colorWithRed:187.f / 255.f green:104.f / 255.f blue:62.f / 255.f alpha:1.f], @@ -88,5 +101,4 @@ return [colors objectAtIndex:(choose % [colors count])]; } - @end diff --git a/Signal/src/util/UIButton+OWS.h b/Signal/src/util/UIButton+OWS.h deleted file mode 100644 index d5e2eff89..000000000 --- a/Signal/src/util/UIButton+OWS.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// UIButton+OWS.h -// Signal -// -// Created by Christine Corbett Moran on 2/10/15. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import - -@interface UIButton (OWS) - -+ (UIButton *)ows_blueButtonWithTitle:(NSString *)title; - -@end diff --git a/Signal/src/util/UIButton+OWS.m b/Signal/src/util/UIButton+OWS.m deleted file mode 100644 index 5e3467930..000000000 --- a/Signal/src/util/UIButton+OWS.m +++ /dev/null @@ -1,37 +0,0 @@ -// -// UIButton+OWS.m -// Signal -// -// Created by Christine Corbett Moran on 2/10/15. -// Copyright (c) 2013 Open Whisper Systems. All rights reserved. -// - -#import "UIButton+OWS.h" -#import "UIColor+OWS.h" -#import "UIFont+OWS.h" -@implementation UIButton (OWS) - -+ (UIButton *)ows_blueButtonWithTitle:(NSString *)title { - NSDictionary *buttonTextAttributes = @{ - NSFontAttributeName : [UIFont ows_regularFontWithSize:15.0f], - NSForegroundColorAttributeName : [UIColor ows_materialBlueColor] - }; - UIButton *button = [[UIButton alloc] init]; - NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:title]; - [attributedTitle setAttributes:buttonTextAttributes range:NSMakeRange(0, [attributedTitle length])]; - [button setAttributedTitle:attributedTitle forState:UIControlStateNormal]; - - NSDictionary *disabledAttributes = @{ - NSFontAttributeName : [UIFont ows_regularFontWithSize:15.0f], - NSForegroundColorAttributeName : [UIColor ows_darkGrayColor] - }; - NSMutableAttributedString *attributedTitleDisabled = [[NSMutableAttributedString alloc] initWithString:title]; - [attributedTitleDisabled setAttributes:disabledAttributes range:NSMakeRange(0, [attributedTitle length])]; - [button setAttributedTitle:attributedTitleDisabled forState:UIControlStateDisabled]; - - [button.titleLabel setTextAlignment:NSTextAlignmentCenter]; - - return button; -} - -@end diff --git a/Signal/src/view controllers/InboxTableViewCell.h b/Signal/src/view controllers/InboxTableViewCell.h index 392f5e3c6..dcf09f2ff 100644 --- a/Signal/src/view controllers/InboxTableViewCell.h +++ b/Signal/src/view controllers/InboxTableViewCell.h @@ -1,10 +1,5 @@ -// -// TableViewCell.h -// Signal -// // Created by Dylan Bourgeois on 27/10/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// #import #import "TSThread.h" diff --git a/Signal/src/view controllers/InboxTableViewCell.m b/Signal/src/view controllers/InboxTableViewCell.m index c0ec90b58..f978492b1 100644 --- a/Signal/src/view controllers/InboxTableViewCell.m +++ b/Signal/src/view controllers/InboxTableViewCell.m @@ -1,19 +1,14 @@ -// -// TableViewCell.m -// Signal -// // Created by Dylan Bourgeois on 27/10/14. // Copyright (c) 2014 Open Whisper Systems. All rights reserved. -// +#import +#import #import "Environment.h" #import "InboxTableViewCell.h" -#import "JSQMessagesAvatarImageFactory.h" #import "PreferencesUtil.h" #import "TSContactThread.h" #import "TSGroupThread.h" #import "TSMessagesManager.h" -#import "UIImage+JSQMessages.h" #import "Util.h" #define ARCHIVE_IMAGE_VIEW_WIDTH 22.0f diff --git a/Signal/src/view controllers/MessageComposeTableViewController.h b/Signal/src/view controllers/MessageComposeTableViewController.h index 00898a8d7..1c1a458e7 100644 --- a/Signal/src/view controllers/MessageComposeTableViewController.h +++ b/Signal/src/view controllers/MessageComposeTableViewController.h @@ -7,14 +7,11 @@ // #import - +#import +#import +#import +#import #import "Contact.h" - -#import "JSQMessagesComposerTextView.h" -#import "JSQMessagesInputToolbar.h" -#import "JSQMessagesToolbarContentView.h" - -#import "JSQMessagesKeyboardController.h" #import "LocalizableText.h" @interface MessageComposeTableViewController : UITableViewController diff --git a/Signal/src/view controllers/MessagesViewController.h b/Signal/src/view controllers/MessagesViewController.h index 438b2b89b..0bd2e1ebf 100644 --- a/Signal/src/view controllers/MessagesViewController.h +++ b/Signal/src/view controllers/MessagesViewController.h @@ -9,8 +9,7 @@ #import #import #import "APNavigationController.h" -#import "JSQMessages.h" -#import "JSQMessagesViewController.h" +#import #import "TSGroupModel.h" @class TSThread; diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index d2c1b145b..3a39928d2 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -14,12 +14,20 @@ #import #import #import +#import +#import +#import +#import +#import +#import +#import #import "OWSContactsManager.h" #import "DJWActionSheet+OWS.h" #import "Environment.h" #import "FingerprintViewController.h" #import "FullImageViewController.h" -#import "JSQCallCollectionViewCell.h" +#import "OWSCallCollectionViewCell.h" +#import "OWSDisplayedMessageCollectionViewCell.h" #import "MessagesViewController.h" #import "NSDate+millisecondTimeStamp.h" #import "NewGroupViewController.h" @@ -30,12 +38,17 @@ #import "TSAttachmentPointer.h" #import "TSContentAdapters.h" #import "TSDatabaseView.h" +#import "OWSMessagesBubblesSizeCalculator.h" +#import "OWSInfoMessage.h" +#import "TSInfoMessage.h" +#import "OWSErrorMessage.h" #import "TSErrorMessage.h" +#import "OWSCall.h" +#import "TSCall.h" #import "TSIncomingMessage.h" #import "TSInvalidIdentityKeyErrorMessage.h" #import "TSMessagesManager+attachments.h" #import "TSMessagesManager+sendMessages.h" -#import "UIButton+OWS.h" #import "UIFont+OWS.h" #import "UIUtil.h" @@ -79,7 +92,6 @@ typedef enum : NSUInteger { @property (nonatomic, strong) TSVideoAttachmentAdapter *currentMediaAdapter; @property (nonatomic, retain) NSTimer *readTimer; -@property (nonatomic, retain) UIButton *messageButton; @property (nonatomic, retain) UIButton *attachButton; @property (nonatomic, retain) NSIndexPath *lastDeliveredMessageIndexPath; @@ -102,7 +114,8 @@ typedef enum : NSUInteger { @implementation MessagesViewController -- (void)dealloc { +- (void)dealloc +{ [[NSNotificationCenter defaultCenter] removeObserver:self]; } @@ -152,8 +165,15 @@ typedef enum : NSUInteger { } } -- (void)viewDidLoad { +- (void)viewDidLoad +{ [super viewDidLoad]; + // JSQMVC width is 375px at this point (as specified by the xib), but this causes + // our initial bubble calculations to be off since they happen before the containing + // view is layed out. https://github.com/jessesquires/JSQMessagesViewController/issues/1257 + // Resetting here makes sure we've got a good initial width. + [self resetFrame]; + [self.navigationController.navigationBar setTranslucent:NO]; self.messageAdapterCache = [[NSCache alloc] init]; @@ -165,10 +185,6 @@ typedef enum : NSUInteger { [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleContactPhone)]; _toggleContactPhoneDisplay.numberOfTapsRequired = 1; - _messageButton = [UIButton ows_blueButtonWithTitle:NSLocalizedString(@"SEND_BUTTON_TITLE", @"")]; - _messageButton.enabled = NO; - _messageButton.titleLabel.adjustsFontSizeToFitWidth = YES; - _attachButton = [[UIButton alloc] init]; [_attachButton setFrame:CGRectMake(0, 0, @@ -182,16 +198,26 @@ typedef enum : NSUInteger { [self initializeTextView]; [JSQMessagesCollectionViewCell registerMenuAction:@selector(delete:)]; + self.collectionView.collectionViewLayout.bubbleSizeCalculator = [[OWSMessagesBubblesSizeCalculator alloc] init]; [self initializeCollectionViewLayout]; + [self registerCustomMessageNibs]; self.senderId = ME_MESSAGE_IDENTIFIER; self.senderDisplayName = ME_MESSAGE_IDENTIFIER; +} +- (void)registerCustomMessageNibs +{ + [self.collectionView registerNib:[OWSCallCollectionViewCell nib] + forCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier]]; + [self.collectionView registerNib:[OWSDisplayedMessageCollectionViewCell nib] + forCellWithReuseIdentifier:[OWSDisplayedMessageCollectionViewCell cellReuseIdentifier]]; } -- (void)toggleObservers:(BOOL)shouldObserve { +- (void)toggleObservers:(BOOL)shouldObserve +{ if (shouldObserve) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(yapDatabaseModified:) @@ -220,9 +246,15 @@ typedef enum : NSUInteger { - (void)initializeTextView { [self.inputToolbar.contentView.textView setFont:[UIFont ows_dynamicTypeBodyFont]]; - self.inputToolbar.contentView.leftBarButtonItem = _attachButton; - self.inputToolbar.contentView.rightBarButtonItem = _messageButton; - self.inputToolbar.contentView.textView.layer.cornerRadius = 4.f; + + self.inputToolbar.contentView.leftBarButtonItem = self.attachButton; + + UILabel *sendLabel = self.inputToolbar.contentView.rightBarButtonItem.titleLabel; + // override superclass translations since we support more translations than upstream. + sendLabel.text = NSLocalizedString(@"SEND_BUTTON_TITLE", nil); + sendLabel.font = [UIFont ows_regularFontWithSize:17.0f]; + sendLabel.textColor = [UIColor ows_materialBlueColor]; + sendLabel.textAlignment = NSTextAlignmentCenter; } - (void)viewWillAppear:(BOOL)animated { @@ -497,15 +529,13 @@ typedef enum : NSUInteger { } } -- (void)initializeBubbles { +- (void)initializeBubbles +{ JSQMessagesBubbleImageFactory *bubbleFactory = [[JSQMessagesBubbleImageFactory alloc] init]; + self.incomingBubbleImageData = [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; self.outgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor ows_materialBlueColor]]; - self.incomingBubbleImageData = - [bubbleFactory incomingMessagesBubbleImageWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; - self.currentlyOutgoingBubbleImageData = - [bubbleFactory outgoingMessageFailedBubbleImageWithColor:[UIColor ows_fadedBlueColor]]; - - self.outgoingMessageFailedImageData = [bubbleFactory outgoingMessageFailedBubbleImageWithColor:[UIColor grayColor]]; + self.currentlyOutgoingBubbleImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor ows_fadedBlueColor]]; + self.outgoingMessageFailedImageData = [bubbleFactory outgoingMessagesBubbleImageWithColor:[UIColor grayColor]]; } - (void)initializeCollectionViewLayout { @@ -651,13 +681,6 @@ typedef enum : NSUInteger { return !(isGroupConversation || [((TSContactThread *)self.thread).contactIdentifier isEqualToString:[TSAccountManager localNumber]]); } -- (void)textViewDidChange:(UITextView *)textView { - if ([textView.text length] > 0) { - self.inputToolbar.contentView.rightBarButtonItem.enabled = YES; - } else { - self.inputToolbar.contentView.rightBarButtonItem.enabled = NO; - } -} #pragma mark - JSQMessagesViewController method overrides - (void)didPressSendButton:(UIButton *)button @@ -681,16 +704,19 @@ typedef enum : NSUInteger { #pragma mark - JSQMessages CollectionView DataSource - (id)collectionView:(JSQMessagesCollectionView *)collectionView - messageDataForItemAtIndexPath:(NSIndexPath *)indexPath { + messageDataForItemAtIndexPath:(NSIndexPath *)indexPath +{ return [self messageAtIndexPath:indexPath]; } - (id)collectionView:(JSQMessagesCollectionView *)collectionView - messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath { - id message = [self messageAtIndexPath:indexPath]; + messageBubbleImageDataForItemAtIndexPath:(NSIndexPath *)indexPath +{ + TSInteraction *message = [self interactionAtIndexPath:indexPath]; - if ([message.senderId isEqualToString:self.senderId]) { - switch (message.messageState) { + if ([message isKindOfClass:[TSOutgoingMessage class]]) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; + switch (outgoingMessage.messageState) { case TSOutgoingMessageStateUnsent: return self.outgoingMessageFailedImageData; case TSOutgoingMessageStateAttemptingOut: @@ -711,31 +737,46 @@ typedef enum : NSUInteger { #pragma mark - UICollectionView DataSource - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView - cellForItemAtIndexPath:(NSIndexPath *)indexPath { - TSMessageAdapter *msg = [self messageAtIndexPath:indexPath]; - - switch (msg.messageType) { - case TSIncomingMessageAdapter: - return [self loadIncomingMessageCellForMessage:msg atIndexPath:indexPath]; - case TSOutgoingMessageAdapter: - return [self loadOutgoingCellForMessage:msg atIndexPath:indexPath]; - case TSCallAdapter: - return [self loadCallCellForCall:msg atIndexPath:indexPath]; - case TSInfoMessageAdapter: - return [self loadInfoMessageCellForMessage:msg atIndexPath:indexPath]; - case TSErrorMessageAdapter: - return [self loadErrorMessageCellForMessage:msg atIndexPath:indexPath]; - - default: - DDLogError(@"Something went wrong"); - return nil; + cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + TSMessageAdapter *message = [self messageAtIndexPath:indexPath]; + NSParameterAssert(message != nil); + + JSQMessagesCollectionViewCell *cell; + switch (message.messageType) { + case TSCallAdapter: { + OWSCall *call = (OWSCall *)message; + cell = [self loadCallCellForCall:call atIndexPath:indexPath]; + } break; + case TSInfoMessageAdapter: { + OWSInfoMessage *infoMessage = (OWSInfoMessage *)message; + cell = [self loadInfoMessageCellForMessage:infoMessage atIndexPath:indexPath]; + } break; + case TSErrorMessageAdapter: { + OWSErrorMessage *errorMessage = (OWSErrorMessage *)message; + cell = [self loadErrorMessageCellForMessage:errorMessage atIndexPath:indexPath]; + } break; + case TSIncomingMessageAdapter: { + cell = [self loadIncomingMessageCellForMessage:message atIndexPath:indexPath]; + } break; + case TSOutgoingMessageAdapter: { + cell = [self loadOutgoingCellForMessage:message atIndexPath:indexPath]; + } break; + default: { + DDLogWarn(@"using default cell constructor for message: %@", message); + cell = (JSQMessagesCollectionViewCell *)[super collectionView:collectionView cellForItemAtIndexPath:indexPath]; + } break; } + cell.delegate = collectionView; + + return cell; } #pragma mark - Loading message cells - (JSQMessagesCollectionViewCell *)loadIncomingMessageCellForMessage:(id)message - atIndexPath:(NSIndexPath *)indexPath { + atIndexPath:(NSIndexPath *)indexPath +{ JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath]; if (!message.isMediaMessage) { @@ -750,7 +791,8 @@ typedef enum : NSUInteger { } - (JSQMessagesCollectionViewCell *)loadOutgoingCellForMessage:(id)message - atIndexPath:(NSIndexPath *)indexPath { + atIndexPath:(NSIndexPath *)indexPath +{ JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath]; if (!message.isMediaMessage) { @@ -764,26 +806,61 @@ typedef enum : NSUInteger { return cell; } -- (JSQCallCollectionViewCell *)loadCallCellForCall:(id)call atIndexPath:(NSIndexPath *)indexPath { - JSQCallCollectionViewCell *cell = - (JSQCallCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath]; - return cell; +- (OWSCallCollectionViewCell *)loadCallCellForCall:(OWSCall *)call atIndexPath:(NSIndexPath *)indexPath +{ + OWSCallCollectionViewCell *callCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSCallCollectionViewCell cellReuseIdentifier] + forIndexPath:indexPath]; + + NSString *text = call.date != nil ? [call text] : call.senderDisplayName; + NSString *allText = call.date != nil ? [text stringByAppendingString:[call dateText]] : text; + + UIFont *boldFont = [UIFont fontWithName:@"HelveticaNeue-Medium" size:12.0f]; + NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:allText + attributes:@{ NSFontAttributeName: boldFont }]; + if([call date]!=nil) { + // Not a group meta message + UIFont *regularFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:12.0f]; + const NSRange range = NSMakeRange([text length], [[call dateText] length]); + [attributedText setAttributes:@{ NSFontAttributeName: regularFont } + range:range]; + } + callCell.cellLabel.attributedText = attributedText; + callCell.cellLabel.numberOfLines = 0; // uses as many lines as it needs + callCell.cellLabel.textColor = [UIColor ows_materialBlueColor]; + + callCell.layer.shouldRasterize = YES; + callCell.layer.rasterizationScale = [UIScreen mainScreen].scale; + return callCell; } -- (JSQDisplayedMessageCollectionViewCell *)loadInfoMessageCellForMessage:(id)message - atIndexPath:(NSIndexPath *)indexPath { - JSQDisplayedMessageCollectionViewCell *cell = - (JSQDisplayedMessageCollectionViewCell *)[super collectionView:self.collectionView - cellForItemAtIndexPath:indexPath]; - return cell; +- (OWSDisplayedMessageCollectionViewCell *)loadInfoMessageCellForMessage:(OWSInfoMessage *)infoMessage + atIndexPath:(NSIndexPath *)indexPath +{ + OWSDisplayedMessageCollectionViewCell *infoCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSDisplayedMessageCollectionViewCell cellReuseIdentifier] + forIndexPath:indexPath]; + infoCell.cellLabel.text = [infoMessage text]; + infoCell.cellLabel.textColor = [UIColor darkGrayColor]; + + infoCell.textContainer.layer.borderColor = infoCell.textContainer.layer.borderColor = [[UIColor ows_infoMessageBorderColor] CGColor]; + infoCell.headerImageView.image = [UIImage imageNamed:@"warning_white"]; + infoCell.layer.shouldRasterize = YES; + infoCell.layer.rasterizationScale = [UIScreen mainScreen].scale; + return infoCell; } -- (JSQDisplayedMessageCollectionViewCell *)loadErrorMessageCellForMessage:(id)message - atIndexPath:(NSIndexPath *)indexPath { - JSQDisplayedMessageCollectionViewCell *cell = - (JSQDisplayedMessageCollectionViewCell *)[super collectionView:self.collectionView - cellForItemAtIndexPath:indexPath]; - return cell; +- (OWSDisplayedMessageCollectionViewCell *)loadErrorMessageCellForMessage:(OWSErrorMessage *)errorMessage + atIndexPath:(NSIndexPath *)indexPath +{ + OWSDisplayedMessageCollectionViewCell *errorCell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[OWSDisplayedMessageCollectionViewCell cellReuseIdentifier] + forIndexPath:indexPath]; + errorCell.cellLabel.text = [errorMessage text]; + errorCell.cellLabel.textColor = [UIColor darkGrayColor]; + + errorCell.textContainer.layer.borderColor = [[UIColor ows_errorMessageBorderColor] CGColor]; + errorCell.headerImageView.image = [UIImage imageNamed:@"error_white"]; + errorCell.layer.shouldRasterize = YES; + errorCell.layer.rasterizationScale = [UIScreen mainScreen].scale; + return errorCell; } #pragma mark - Adjusting cell label heights @@ -831,9 +908,11 @@ typedef enum : NSUInteger { TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath]; // If message failed, say that message should be tapped to retry; - if (currentMessage.messageType == TSOutgoingMessageAdapter && - currentMessage.messageState == TSOutgoingMessageStateUnsent) { - return YES; + if (currentMessage.messageType == TSOutgoingMessageAdapter) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)currentMessage; + if(outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + return YES; + } } if ([self.thread isKindOfClass:[TSGroupThread class]]) { @@ -867,8 +946,15 @@ typedef enum : NSUInteger { return nextMessage; } -- (BOOL)isMessageOutgoingAndDelivered:(TSMessageAdapter *)message { - return message.messageType == TSOutgoingMessageAdapter && message.messageState == TSOutgoingMessageStateDelivered; +- (BOOL)isMessageOutgoingAndDelivered:(TSMessageAdapter *)message +{ + if (message.messageType == TSOutgoingMessageAdapter) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; + if(outgoingMessage.messageState == TSOutgoingMessageStateDelivered) { + return YES; + } + } + return NO; } @@ -879,11 +965,14 @@ typedef enum : NSUInteger { textAttachment.bounds = CGRectMake(0, 0, 11.0f, 10.0f); if ([self shouldShowMessageStatusAtIndexPath:indexPath]) { - if (msg.messageType == TSOutgoingMessageAdapter && msg.messageState == TSOutgoingMessageStateUnsent) { - NSMutableAttributedString *attrStr = + if (msg.messageType == TSOutgoingMessageAdapter) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)msg; + if(outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { + NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"FAILED_SENDING_TEXT", nil)]; - [attrStr appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]]; - return attrStr; + [attrStr appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]]; + return attrStr; + } } if ([self.thread isKindOfClass:[TSGroupThread class]]) { @@ -922,17 +1011,27 @@ typedef enum : NSUInteger { #pragma mark - Actions +- (void)collectionView:(JSQMessagesCollectionView *)collectionView didTapCellAtIndexPath:(NSIndexPath *)indexPath touchLocation:(CGPoint)touchLocation +{ + // Pass info/error message tapping to bubble tapping handler + [self collectionView:collectionView didTapMessageBubbleAtIndexPath:indexPath]; +} + - (void)collectionView:(JSQMessagesCollectionView *)collectionView - didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath { + didTapMessageBubbleAtIndexPath:(NSIndexPath *)indexPath +{ TSMessageAdapter *messageItem = [collectionView.dataSource collectionView:collectionView messageDataForItemAtIndexPath:indexPath]; TSInteraction *interaction = [self interactionAtIndexPath:indexPath]; switch (messageItem.messageType) { - case TSOutgoingMessageAdapter: - if (messageItem.messageState == TSOutgoingMessageStateUnsent) { + case TSOutgoingMessageAdapter: { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)messageItem; + if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { [self handleUnsentMessageTap:(TSOutgoingMessage *)interaction]; } + } + // No `break` as we want to fall through to capture tapping on media items case TSIncomingMessageAdapter: { BOOL isMediaMessage = [messageItem isMediaMessage]; @@ -1111,11 +1210,13 @@ typedef enum : NSUInteger { case TSCallAdapter: break; default: + DDLogDebug(@"Unhandled bubble touch for interaction: %@.", interaction); break; } } -- (void)handleWarningTap:(TSInteraction *)interaction { +- (void)handleWarningTap:(TSInteraction *)interaction +{ if ([interaction isKindOfClass:[TSIncomingMessage class]]) { TSIncomingMessage *message = (TSIncomingMessage *)interaction; @@ -1129,6 +1230,8 @@ typedef enum : NSUInteger { if ([attachment isKindOfClass:[TSAttachmentPointer class]]) { TSAttachmentPointer *pointer = (TSAttachmentPointer *)attachment; + // FIXME possible for pointer to get stuck in isDownloading state if app is closed while downloading. + // see: https://github.com/WhisperSystems/Signal-iOS/issues/1254 if (!pointer.isDownloading) { [[TSMessagesManager sharedManager] retrieveAttachment:pointer messageId:message.uniqueId]; } @@ -2000,5 +2103,4 @@ typedef enum : NSUInteger { return @[]; } - @end diff --git a/Signal/src/views/OWSCallCollectionViewCell.h b/Signal/src/views/OWSCallCollectionViewCell.h new file mode 100644 index 000000000..53d8bcc58 --- /dev/null +++ b/Signal/src/views/OWSCallCollectionViewCell.h @@ -0,0 +1,18 @@ +// Created by Dylan Bourgeois on 20/11/14. +// Portions Copyright (c) 2014 Open Whisper Systems. All rights reserved. + +#import +#import + +@interface OWSCallCollectionViewCell : JSQMessagesCollectionViewCell + +@property (weak, nonatomic, readonly) JSQMessagesLabel *cellLabel; +@property (weak, nonatomic, readonly) UIImageView *outgoingCallImageView; +@property (weak, nonatomic, readonly) UIImageView *incomingCallImageView; + +#pragma mark - Class methods + ++ (UINib *)nib; ++ (NSString *)cellReuseIdentifier; + +@end diff --git a/Signal/src/views/OWSCallCollectionViewCell.m b/Signal/src/views/OWSCallCollectionViewCell.m new file mode 100644 index 000000000..7ba7a13ef --- /dev/null +++ b/Signal/src/views/OWSCallCollectionViewCell.m @@ -0,0 +1,53 @@ +// Created by Dylan Bourgeois on 20/11/14. +// Portions Copyright (c) 2014 Open Whisper Systems. All rights reserved. + +#import "OWSCallCollectionViewCell.h" +#import + +@interface OWSCallCollectionViewCell () + +@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellLabel; +@property (weak, nonatomic) IBOutlet UIImageView *outgoingCallImageView; +@property (weak, nonatomic) IBOutlet UIImageView *incomingCallImageView; + +@end + +@implementation OWSCallCollectionViewCell + +#pragma mark - Class Methods + ++ (UINib *)nib +{ + return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]]; +} + ++ (NSString *)cellReuseIdentifier +{ + return NSStringFromClass([self class]); +} + +#pragma mark - Initializer + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + [self setTranslatesAutoresizingMaskIntoConstraints:NO]; + + self.backgroundColor = [UIColor whiteColor]; + + self.cellLabel.textAlignment = NSTextAlignmentCenter; + self.cellLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f]; + self.cellLabel.textColor = [UIColor lightGrayColor]; +} + + +#pragma mark - Collection view cell + +- (void)prepareForReuse +{ + [super prepareForReuse]; + self.cellLabel.text = nil; +} + +@end diff --git a/Signal/src/views/OWSCallCollectionViewCell.xib b/Signal/src/views/OWSCallCollectionViewCell.xib new file mode 100644 index 000000000..3ad5c26f2 --- /dev/null +++ b/Signal/src/views/OWSCallCollectionViewCell.xib @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Signal/src/views/OWSDisplayedMessageCollectionViewCell.h b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.h new file mode 100644 index 000000000..98a2e9b7a --- /dev/null +++ b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.h @@ -0,0 +1,16 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import +#import + +static const CGFloat OWSDisplayedMessageCellHeight = 70.0f; + +@interface OWSDisplayedMessageCollectionViewCell : JSQMessagesCollectionViewCell + +@property (weak, nonatomic, readonly) JSQMessagesLabel *cellLabel; +@property (weak, nonatomic, readonly) UIImageView *headerImageView; +@property (strong, nonatomic, readonly) UIView *textContainer; + +@end diff --git a/Signal/src/views/OWSDisplayedMessageCollectionViewCell.m b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.m new file mode 100644 index 000000000..e8e3d51a7 --- /dev/null +++ b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.m @@ -0,0 +1,58 @@ +// Created by Dylan Bourgeois on 29/11/14. +// Copyright (c) 2014 Hexed Bits. All rights reserved. +// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved. + +#import "OWSDisplayedMessageCollectionViewCell.h" + +#import + +@interface OWSDisplayedMessageCollectionViewCell () + +@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellLabel; +@property (weak, nonatomic) IBOutlet UIImageView *headerImageView; +@property (strong, nonatomic) IBOutlet UIView *textContainer; + +@end + +@implementation OWSDisplayedMessageCollectionViewCell + +#pragma mark - Class Methods + ++ (UINib *)nib +{ + return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]]; +} + ++ (NSString *)cellReuseIdentifier +{ + return NSStringFromClass([self class]); +} + +#pragma mark - Initializer + +- (void)awakeFromNib +{ + [super awakeFromNib]; + + [self setTranslatesAutoresizingMaskIntoConstraints:NO]; + + self.backgroundColor = [UIColor whiteColor]; + + self.textContainer.layer.borderColor = [[UIColor lightGrayColor] CGColor]; + self.textContainer.layer.borderWidth = 0.75f; + self.textContainer.layer.cornerRadius = 5.0f; + self.cellLabel.textAlignment = NSTextAlignmentCenter; + self.cellLabel.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:14.0f]; + self.cellLabel.textColor = [UIColor lightGrayColor]; +} + +#pragma mark - Collection view cell + +- (void)prepareForReuse +{ + [super prepareForReuse]; + + self.cellLabel.text = nil; +} + +@end diff --git a/Signal/src/views/OWSDisplayedMessageCollectionViewCell.xib b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.xib new file mode 100644 index 000000000..28933b8af --- /dev/null +++ b/Signal/src/views/OWSDisplayedMessageCollectionViewCell.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +