Disappearing Messages
* Per thread settings menu accessed by tapping on thread title This removed the toggle-phone behavior. You'll be able to see the phone number in the settings table view. This removed the "add contact" functionality, although it was already broken for ios>=9 (which is basically everybody). The group actions menu was absorbed into this screen * Added a confirm alert to leave group (fixes #938) * New Translation Strings * Extend "Add People" label to fit translations. * resolved issues with translations not fitting in group menu * Fix the long standing type warning where TSCalls were assigned to a TSMessageAdapter. * Can delete info messages Follow the JSQMVC pattern and put UIResponder-able content in the messageBubbleContainer. This gives us more functionality *and* allows us to delete some code. yay! It's still not yet possible to delete phone messages. =( * Fixed some compiler warnings. * xcode8 touching storyboard. So long xcode7! * Fixup multiline info messages. We were seeing info messages like "You set disappearing message timer to 10" instead of "You set disappearing message timer to 10 seconds." Admittedly this isn't a very good fix, as now one liners feel like they have too much padding. If the message is well over one line, we were wrapping properly, but there's a problem when the message is *just barely* two lines, the cell height grows, but the label still thinks it's just one line (as evinced by the one line appearing in the center of the label frame. The result being that the last word of the label is cropped. * Disable group actions after leaving group. // FREEBIE
|
@ -1,7 +1,7 @@
|
|||
language: objective-c
|
||||
cache: cocoapods # pod install somtimes takes >20 minutes, so lets cache this
|
||||
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode8
|
||||
|
||||
before_install:
|
||||
- brew update # we may not be running the latest version so always update
|
||||
|
@ -16,5 +16,8 @@ script:
|
|||
- |
|
||||
set -o pipefail
|
||||
# default xctool was hanging. see https://github.com/travis-ci/travis-ci/issues/3986
|
||||
xcodebuild -workspace Signal.xcworkspace -scheme Signal -sdk iphonesimulator build test | xcpretty
|
||||
xcodebuild -workspace Signal.xcworkspace -scheme Signal \
|
||||
-sdk iphonesimulator \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 6,OS=10.0' \
|
||||
build test | xcpretty
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
#error This class requires automatic reference counting
|
||||
#endif
|
||||
|
||||
|
||||
#pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
// Unknown diagnostic
|
||||
// #pragma clang diagnostic ignored "-Wreceiver-is-weak"
|
||||
#pragma clang diagnostic ignored "-Warc-repeated-use-of-weak"
|
||||
#pragma clang diagnostic ignored "-Wobjc-missing-property-synthesis"
|
||||
#pragma clang diagnostic ignored "-Wdirect-ivar-access"
|
||||
|
|
2
Podfile
|
@ -3,7 +3,7 @@ source 'https://github.com/CocoaPods/Specs.git'
|
|||
|
||||
target 'Signal' do
|
||||
pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git'
|
||||
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'new-fingerprint-format'
|
||||
pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'release/2.6'
|
||||
#pod 'SignalServiceKit', path: '../SignalServiceKit'
|
||||
pod 'OpenSSL', '~> 1.0.208'
|
||||
pod 'PastelogKit', '~> 1.3'
|
||||
|
|
|
@ -122,20 +122,20 @@ DEPENDENCIES:
|
|||
- OpenSSL (~> 1.0.208)
|
||||
- PastelogKit (~> 1.3)
|
||||
- SCWaveformView (~> 1.0)
|
||||
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `new-fingerprint-format`)
|
||||
- SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `release/2.6`)
|
||||
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
SignalServiceKit:
|
||||
:branch: new-fingerprint-format
|
||||
:branch: release/2.6
|
||||
:git: https://github.com/WhisperSystems/SignalServiceKit.git
|
||||
SocketRocket:
|
||||
:git: https://github.com/facebook/SocketRocket.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
SignalServiceKit:
|
||||
:commit: 2b612e9ab72b427b310b125b1dc82eef1d3f85ec
|
||||
:commit: a277fbfacb10599ce609e625e599a9903f8d0ebf
|
||||
:git: https://github.com/WhisperSystems/SignalServiceKit.git
|
||||
SocketRocket:
|
||||
:commit: 41b57bb2fc292a814f758441a05243eb38457027
|
||||
|
@ -167,6 +167,6 @@ SPEC CHECKSUMS:
|
|||
YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f
|
||||
ZXingObjC: bf15b3814f7a105b6d99f47da2333c93a063650a
|
||||
|
||||
PODFILE CHECKSUM: d4204cf787649f9512dc74e5844f48d2570d5b2e
|
||||
PODFILE CHECKSUM: 1e76310fe364da5f11d50ef76ccf13714b52926b
|
||||
|
||||
COCOAPODS: 1.0.1
|
||||
|
|
|
@ -9,12 +9,20 @@
|
|||
/* 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 */; };
|
||||
450873C31D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */; };
|
||||
450873C41D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */; };
|
||||
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; };
|
||||
450873C81D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */; };
|
||||
4520D8D51D417D8E00123472 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4520D8D41D417D8E00123472 /* Photos.framework */; };
|
||||
452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
|
||||
452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */; };
|
||||
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 */; };
|
||||
45666EC61D99483D008FE134 /* OWSAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */; };
|
||||
45666EC91D994C0D008FE134 /* OWSGroupAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */; };
|
||||
45666F561D9B2827008FE134 /* OWSScrubbingLogFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */; };
|
||||
45666F581D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */; };
|
||||
45666F761D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m in Sources */ = {isa = PBXBuildFile; fileRef = 45666F751D9BFE00008FE134 /* OWS100RemoveTSRecipientsMigration.m */; };
|
||||
|
@ -23,6 +31,8 @@
|
|||
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 */; };
|
||||
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
|
||||
45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */; };
|
||||
458E38311D6682450094BD24 /* OWSQRCodeScanningViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */; };
|
||||
458E38341D66873D0094BD24 /* OWSLinkDeviceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38331D66873D0094BD24 /* OWSLinkDeviceViewController.m */; };
|
||||
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */; };
|
||||
|
@ -43,6 +53,9 @@
|
|||
45C681C91D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45C681C31D305C9E0050903A /* OWSDisplayedMessageCollectionViewCell.xib */; };
|
||||
45CB2FA81CB7146C00E1B343 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 45CB2FA71CB7146C00E1B343 /* Launch Screen.storyboard */; };
|
||||
45EB32CF1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 45EB32CE1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m */; };
|
||||
45F2B1941D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */; };
|
||||
45F2B1971D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */; };
|
||||
45F2B1981D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */; };
|
||||
4CE0E3771B954546007210CF /* TSAnimatedAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE0E3761B954546007210CF /* TSAnimatedAdapter.m */; };
|
||||
701231B518ECAA4500D456C4 /* EvpMessageDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 701231B418ECAA4500D456C4 /* EvpMessageDigest.m */; };
|
||||
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
|
||||
|
@ -392,7 +405,6 @@
|
|||
B6A3EB4B1A423B3800B2236B /* TSPhotoAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = B6A3EB4A1A423B3800B2236B /* TSPhotoAdapter.m */; };
|
||||
B6B1013C196D213F007E3930 /* SignalKeyingStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B1013B196D213F007E3930 /* SignalKeyingStorage.m */; };
|
||||
B6B226971BE4B7D200860F4D /* ContactsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B6B226961BE4B7D200860F4D /* ContactsUI.framework */; };
|
||||
B6B2269A1BE4C59200860F4D /* APNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B226991BE4C59200860F4D /* APNavigationController.m */; };
|
||||
B6B9ECFC198B31BA00C620D3 /* PushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B9ECFB198B31BA00C620D3 /* PushManager.m */; };
|
||||
B6BADBE71B88D1AC0086A80D /* LockInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6BADBE61B88D1AC0086A80D /* LockInteractionController.m */; };
|
||||
B6C6AE551A305ED1006BAF8F /* redphone.cer in Resources */ = {isa = PBXBuildFile; fileRef = B6C6AE531A305ED1006BAF8F /* redphone.cer */; };
|
||||
|
@ -462,7 +474,7 @@
|
|||
FC9120411A39EFB70074545C /* qr@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FC91203F1A39EFB70074545C /* qr@2x.png */; };
|
||||
FCAC963C19FEF9280046DFC5 /* SignalsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCAC963B19FEF9280046DFC5 /* SignalsViewController.m */; };
|
||||
FCAC964019FEF99A0046DFC5 /* InboxTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FCAC963E19FEF99A0046DFC5 /* InboxTableViewCell.m */; };
|
||||
FCAC965119FF0A6E0046DFC5 /* MessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCAC965019FF0A6E0046DFC5 /* MessagesViewController.m */; settings = {COMPILER_FLAGS = "-Wno-receiver-is-weak"; }; };
|
||||
FCAC965119FF0A6E0046DFC5 /* MessagesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCAC965019FF0A6E0046DFC5 /* MessagesViewController.m */; };
|
||||
FCB11D8C1A129A76002F93FB /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FCB11D8B1A129A76002F93FB /* CoreMedia.framework */; };
|
||||
FCB11D931A12A4AA002F93FB /* FullImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FCB11D921A12A4AA002F93FB /* FullImageViewController.m */; };
|
||||
FCC81A981A44558300DFEC7D /* UIDevice+TSHardwareVersion.m in Sources */ = {isa = PBXBuildFile; fileRef = FCC81A971A44558300DFEC7D /* UIDevice+TSHardwareVersion.m */; };
|
||||
|
@ -515,8 +527,15 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
450873C11D9D5149006B54F2 /* OWSExpirationTimerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirationTimerView.h; sourceTree = "<group>"; };
|
||||
450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSExpirationTimerView.m; sourceTree = "<group>"; };
|
||||
450873C51D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSIncomingMessageCollectionViewCell.h; sourceTree = "<group>"; };
|
||||
450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSIncomingMessageCollectionViewCell.m; sourceTree = "<group>"; };
|
||||
450873C91D9D86F4006B54F2 /* OWSExpirableMessageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSExpirableMessageView.h; sourceTree = "<group>"; };
|
||||
4520D8D41D417D8E00123472 /* Photos.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Photos.framework; path = System/Library/Frameworks/Photos.framework; sourceTree = SDKROOT; };
|
||||
4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageEditing.h; sourceTree = "<group>"; };
|
||||
452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSConversationSettingsTableViewController.h; sourceTree = "<group>"; };
|
||||
452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OWSConversationSettingsTableViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
453D28AF1D32B87100D523F0 /* OWSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSErrorMessage.h; sourceTree = "<group>"; };
|
||||
453D28B01D32B87100D523F0 /* OWSErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSErrorMessage.m; sourceTree = "<group>"; };
|
||||
|
@ -527,6 +546,11 @@
|
|||
453D28B81D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessagesBubblesSizeCalculator.h; sourceTree = "<group>"; };
|
||||
453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSMessagesBubblesSizeCalculator.m; sourceTree = "<group>"; };
|
||||
454B35071D08EED80026D658 /* mk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mk; path = translations/mk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
45666EC41D99483D008FE134 /* OWSAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAvatarBuilder.h; sourceTree = "<group>"; };
|
||||
45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSAvatarBuilder.m; sourceTree = "<group>"; };
|
||||
45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSGroupAvatarBuilder.h; sourceTree = "<group>"; };
|
||||
45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSGroupAvatarBuilder.m; sourceTree = "<group>"; };
|
||||
45666ECE1D995B94008FE134 /* OWSMessageData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSMessageData.h; sourceTree = "<group>"; };
|
||||
45666F541D9B2827008FE134 /* OWSScrubbingLogFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSScrubbingLogFormatter.h; sourceTree = "<group>"; };
|
||||
45666F551D9B2827008FE134 /* OWSScrubbingLogFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatter.m; sourceTree = "<group>"; };
|
||||
45666F571D9B2880008FE134 /* OWSScrubbingLogFormatterTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSScrubbingLogFormatterTest.m; sourceTree = "<group>"; };
|
||||
|
@ -539,6 +563,8 @@
|
|||
45843D1D1D2236B30013E85A /* OWSContactsSearcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactsSearcher.h; sourceTree = "<group>"; };
|
||||
45843D1E1D2236B30013E85A /* OWSContactsSearcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcher.m; sourceTree = "<group>"; };
|
||||
45843D211D223BA10013E85A /* OWSContactsSearcherTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactsSearcherTest.m; sourceTree = "<group>"; };
|
||||
45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSContactAvatarBuilder.h; sourceTree = "<group>"; };
|
||||
45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSContactAvatarBuilder.m; sourceTree = "<group>"; };
|
||||
458E382F1D6682450094BD24 /* OWSQRCodeScanningViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSQRCodeScanningViewController.h; sourceTree = "<group>"; };
|
||||
458E38301D6682450094BD24 /* OWSQRCodeScanningViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSQRCodeScanningViewController.m; sourceTree = "<group>"; };
|
||||
458E38321D66873D0094BD24 /* OWSLinkDeviceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkDeviceViewController.h; sourceTree = "<group>"; };
|
||||
|
@ -566,6 +592,10 @@
|
|||
45E282DF1D08E6CC00ADD4C8 /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = translations/id.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
45EB32CD1D7465C900735B2E /* OWSLinkedDevicesTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSLinkedDevicesTableViewController.h; sourceTree = "<group>"; };
|
||||
45EB32CE1D7465C900735B2E /* OWSLinkedDevicesTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSLinkedDevicesTableViewController.m; sourceTree = "<group>"; };
|
||||
45F2B1921D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingMessageCollectionViewCell.h; sourceTree = "<group>"; };
|
||||
45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingMessageCollectionViewCell.m; sourceTree = "<group>"; };
|
||||
45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSIncomingMessageCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||
45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSOutgoingMessageCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||
4CE0E3751B95453C007210CF /* TSAnimatedAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSAnimatedAdapter.h; sourceTree = "<group>"; };
|
||||
4CE0E3761B954546007210CF /* TSAnimatedAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSAnimatedAdapter.m; sourceTree = "<group>"; };
|
||||
701231B318ECAA4500D456C4 /* EvpMessageDigest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EvpMessageDigest.h; sourceTree = "<group>"; };
|
||||
|
@ -944,8 +974,6 @@
|
|||
B6B1013A196D213F007E3930 /* SignalKeyingStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SignalKeyingStorage.h; sourceTree = "<group>"; };
|
||||
B6B1013B196D213F007E3930 /* SignalKeyingStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SignalKeyingStorage.m; sourceTree = "<group>"; };
|
||||
B6B226961BE4B7D200860F4D /* ContactsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ContactsUI.framework; path = System/Library/Frameworks/ContactsUI.framework; sourceTree = SDKROOT; };
|
||||
B6B226981BE4C59200860F4D /* APNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APNavigationController.h; sourceTree = "<group>"; };
|
||||
B6B226991BE4C59200860F4D /* APNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APNavigationController.m; sourceTree = "<group>"; };
|
||||
B6B9ECFA198B31BA00C620D3 /* PushManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PushManager.h; sourceTree = "<group>"; };
|
||||
B6B9ECFB198B31BA00C620D3 /* PushManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PushManager.m; sourceTree = "<group>"; };
|
||||
B6BADBE51B88D1AC0086A80D /* LockInteractionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockInteractionController.h; sourceTree = "<group>"; };
|
||||
|
@ -1047,8 +1075,8 @@
|
|||
E85DB184824BA9DC302EC8B3 /* Pods-SignalTests.app store release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalTests.app store release.xcconfig"; path = "Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests.app store release.xcconfig"; sourceTree = "<group>"; };
|
||||
FC3196281A067D8F0094C78E /* MessageComposeTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageComposeTableViewController.h; sourceTree = "<group>"; };
|
||||
FC3196291A067D8F0094C78E /* MessageComposeTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageComposeTableViewController.m; sourceTree = "<group>"; };
|
||||
FC31962B1A06A2190094C78E /* FingerprintViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FingerprintViewController.h; sourceTree = "<group>"; };
|
||||
FC31962C1A06A2190094C78E /* FingerprintViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FingerprintViewController.m; sourceTree = "<group>"; };
|
||||
FC31962B1A06A2190094C78E /* FingerprintViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FingerprintViewController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
FC31962C1A06A2190094C78E /* FingerprintViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = FingerprintViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
FC31962E1A0814130094C78E /* SettingsTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsTableViewController.h; sourceTree = "<group>"; };
|
||||
FC31962F1A0814130094C78E /* SettingsTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsTableViewController.m; sourceTree = "<group>"; };
|
||||
FC3BD9871A30A790005B96BB /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
|
||||
|
@ -1062,7 +1090,7 @@
|
|||
FCAC963D19FEF99A0046DFC5 /* InboxTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InboxTableViewCell.h; path = "../view controllers/InboxTableViewCell.h"; sourceTree = "<group>"; };
|
||||
FCAC963E19FEF99A0046DFC5 /* InboxTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InboxTableViewCell.m; path = "../view controllers/InboxTableViewCell.m"; sourceTree = "<group>"; };
|
||||
FCAC964F19FF0A6E0046DFC5 /* MessagesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagesViewController.h; sourceTree = "<group>"; };
|
||||
FCAC965019FF0A6E0046DFC5 /* MessagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessagesViewController.m; sourceTree = "<group>"; };
|
||||
FCAC965019FF0A6E0046DFC5 /* MessagesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MessagesViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
|
||||
FCB11D8B1A129A76002F93FB /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
|
||||
FCB11D911A12A4AA002F93FB /* FullImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FullImageViewController.h; sourceTree = "<group>"; };
|
||||
FCB11D921A12A4AA002F93FB /* FullImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FullImageViewController.m; sourceTree = "<group>"; };
|
||||
|
@ -1172,6 +1200,12 @@
|
|||
453D28B91D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m */,
|
||||
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
|
||||
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
|
||||
45855F351D9498A40084F340 /* OWSContactAvatarBuilder.h */,
|
||||
45855F361D9498A40084F340 /* OWSContactAvatarBuilder.m */,
|
||||
45666EC41D99483D008FE134 /* OWSAvatarBuilder.h */,
|
||||
45666EC51D99483D008FE134 /* OWSAvatarBuilder.m */,
|
||||
45666EC71D994C0D008FE134 /* OWSGroupAvatarBuilder.h */,
|
||||
45666EC81D994C0D008FE134 /* OWSGroupAvatarBuilder.m */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1731,8 +1765,6 @@
|
|||
FC3196311A08141D0094C78E /* Settings */,
|
||||
FC3196321A08142D0094C78E /* Signals */,
|
||||
FCFD25791A1543D500F4C644 /* Signup */,
|
||||
B6B226981BE4C59200860F4D /* APNavigationController.h */,
|
||||
B6B226991BE4C59200860F4D /* APNavigationController.m */,
|
||||
B6BADBE51B88D1AC0086A80D /* LockInteractionController.h */,
|
||||
B6BADBE61B88D1AC0086A80D /* LockInteractionController.m */,
|
||||
76EB050B18170B33006006FC /* InCallViewController.h */,
|
||||
|
@ -1753,6 +1785,12 @@
|
|||
45C681B91D305C080050903A /* OWSCallCollectionViewCell.h */,
|
||||
45C681BA1D305C080050903A /* OWSCallCollectionViewCell.m */,
|
||||
45C681C01D305C9E0050903A /* OWSCallCollectionViewCell.xib */,
|
||||
450873C51D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.h */,
|
||||
450873C61D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m */,
|
||||
45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */,
|
||||
45F2B1921D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.h */,
|
||||
45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */,
|
||||
45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */,
|
||||
A5509ECB1A69B1D600ABA4BC /* CountryCodeTableViewCell.h */,
|
||||
A5509ECC1A69B1D600ABA4BC /* CountryCodeTableViewCell.m */,
|
||||
FCAC963D19FEF99A0046DFC5 /* InboxTableViewCell.h */,
|
||||
|
@ -1762,6 +1800,9 @@
|
|||
76EB053818170B33006006FC /* xibs */,
|
||||
459311FA1D75C948008DD4F0 /* OWSDeviceTableViewCell.h */,
|
||||
459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */,
|
||||
450873C11D9D5149006B54F2 /* OWSExpirationTimerView.h */,
|
||||
450873C21D9D5149006B54F2 /* OWSExpirationTimerView.m */,
|
||||
450873C91D9D86F4006B54F2 /* OWSExpirableMessageView.h */,
|
||||
);
|
||||
name = Views;
|
||||
path = views;
|
||||
|
@ -1809,6 +1850,7 @@
|
|||
B62D53F61A23CCAD009AAF82 /* TSMessageAdapter.m */,
|
||||
B6D3CBCE1C1376BE00C039DF /* TSContentAdapters.h */,
|
||||
4526BD481CA61C8D00166BC8 /* OWSMessageEditing.h */,
|
||||
45666ECE1D995B94008FE134 /* OWSMessageData.h */,
|
||||
);
|
||||
name = TSMessageAdapters;
|
||||
path = TSMessageAdapaters;
|
||||
|
@ -2311,6 +2353,8 @@
|
|||
FCFD256E1A151BCB00F4C644 /* NewGroupViewController.m */,
|
||||
FC4FA0241A1B9DC600DA100A /* SignalsNavigationController.h */,
|
||||
FC4FA0251A1B9DC600DA100A /* SignalsNavigationController.m */,
|
||||
452E3C8C1D935C77002A45B0 /* OWSConversationSettingsTableViewController.h */,
|
||||
452E3C8D1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m */,
|
||||
);
|
||||
name = Signals;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2521,7 +2565,9 @@
|
|||
AD83FF3F1A73426500B5C81A /* audio_pause_button_blue.png in Resources */,
|
||||
A5509ECA1A69AB8B00ABA4BC /* Storyboard.storyboard in Resources */,
|
||||
A507A3B11A6C60E300BEED0D /* InboxTableViewCell.xib in Resources */,
|
||||
45F2B1971D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib in Resources */,
|
||||
AD83FF421A73426500B5C81A /* audio_play_button.png in Resources */,
|
||||
45F2B1981D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib in Resources */,
|
||||
45C681C41D305C9E0050903A /* OWSCallCollectionViewCell.xib in Resources */,
|
||||
B633C5C41A1D190B0059AC12 /* mute_on@2x.png in Resources */,
|
||||
B633C5CE1A1D190B0059AC12 /* quit@2x.png in Resources */,
|
||||
|
@ -2720,6 +2766,7 @@
|
|||
76EB058A18170B33006006FC /* Release.m in Sources */,
|
||||
76EB061018170B33006006FC /* EventWindow.m in Sources */,
|
||||
E197B62718BBF63B00F073E5 /* SoundBoard.m in Sources */,
|
||||
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,
|
||||
76EB058418170B33006006FC /* LocalizableText.m in Sources */,
|
||||
76EB057A18170B33006006FC /* OWSContactsManager.m in Sources */,
|
||||
E197B61918BBEC1A00F073E5 /* RemoteIOBufferListWrapper.m in Sources */,
|
||||
|
@ -2732,7 +2779,9 @@
|
|||
D221A09A169C9E5E00537ABF /* main.m in Sources */,
|
||||
45843D1F1D2236B30013E85A /* OWSContactsSearcher.m in Sources */,
|
||||
76EB061618170B33006006FC /* AnonymousOccurrenceLogger.m in Sources */,
|
||||
450873C31D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */,
|
||||
B6258B331C29E2E60014138E /* NotificationsManager.m in Sources */,
|
||||
45666EC91D994C0D008FE134 /* OWSGroupAvatarBuilder.m in Sources */,
|
||||
76EB063018170B33006006FC /* Conversions.m in Sources */,
|
||||
76EB065618170B34006006FC /* InCallViewController.m in Sources */,
|
||||
76EB05FE18170B33006006FC /* InitiateSignal.pb.m in Sources */,
|
||||
|
@ -2754,10 +2803,12 @@
|
|||
E197B61E18BBEC6D00F073E5 /* AudioRouter.m in Sources */,
|
||||
E197B60D18BBEC1A00F073E5 /* AudioSocket.m in Sources */,
|
||||
A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */,
|
||||
45855F371D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */,
|
||||
FC31962D1A06A2190094C78E /* FingerprintViewController.m in Sources */,
|
||||
76EB061418170B33006006FC /* AnonymousConditionLogger.m in Sources */,
|
||||
76EB05C018170B33006006FC /* DhPacket.m in Sources */,
|
||||
FC3196301A0814130094C78E /* SettingsTableViewController.m in Sources */,
|
||||
45666EC61D99483D008FE134 /* OWSAvatarBuilder.m in Sources */,
|
||||
7038632818F70C0700D4A43F /* EvpSymetricUtil.m in Sources */,
|
||||
76EB068618170B34006006FC /* ContactTableViewCell.m in Sources */,
|
||||
B63761ED19E1FBE8005735D1 /* HttpRequestOrResponse.m in Sources */,
|
||||
|
@ -2783,6 +2834,7 @@
|
|||
76EB05A218170B33006006FC /* IpEndPoint.m in Sources */,
|
||||
E197B61A18BBEC1A00F073E5 /* SpeexCodec.m in Sources */,
|
||||
76EB05F018170B33006006FC /* PhoneManager.m in Sources */,
|
||||
452E3C8E1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */,
|
||||
E197B60F18BBEC1A00F073E5 /* EncodedAudioFrame.m in Sources */,
|
||||
76EB061818170B33006006FC /* AnonymousValueLogger.m in Sources */,
|
||||
76EB05E618170B33006006FC /* CallController.m in Sources */,
|
||||
|
@ -2824,6 +2876,7 @@
|
|||
FCC81A981A44558300DFEC7D /* UIDevice+TSHardwareVersion.m in Sources */,
|
||||
76EB054018170B33006006FC /* AppDelegate.m in Sources */,
|
||||
76EB05D018170B33006006FC /* ZrtpHandshakeSocket.m in Sources */,
|
||||
45F2B1941D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m in Sources */,
|
||||
B63761EF19E1FBE8005735D1 /* HttpResponse.m in Sources */,
|
||||
E197B61518BBEC1A00F073E5 /* JitterQueue.m in Sources */,
|
||||
BFB074C919A5611000F2947C /* ObservableValue.m in Sources */,
|
||||
|
@ -2839,7 +2892,6 @@
|
|||
76EB059218170B33006006FC /* UnrecognizedRequestFailure.m in Sources */,
|
||||
76EB05F818170B33006006FC /* CallConnectUtil_Initiator.m in Sources */,
|
||||
B62F5E101C2980B4000D370C /* NSData+ows_StripToken.m in Sources */,
|
||||
B6B2269A1BE4C59200860F4D /* APNavigationController.m in Sources */,
|
||||
45666F7E1D9C0814008FE134 /* OWSDatabaseMigrationRunner.m in Sources */,
|
||||
B63761E319E1F487005735D1 /* AFHTTPSessionManager+SignalMethods.m in Sources */,
|
||||
76EB05CC18170B33006006FC /* ShortAuthenticationStringGenerator.m in Sources */,
|
||||
|
@ -2959,6 +3011,7 @@
|
|||
B660F7501C29988E00687D6E /* LowLatencyConnector.m in Sources */,
|
||||
B660F7511C29988E00687D6E /* StreamPair.m in Sources */,
|
||||
B660F7521C29988E00687D6E /* Certificate.m in Sources */,
|
||||
450873C41D9D5149006B54F2 /* OWSExpirationTimerView.m in Sources */,
|
||||
B660F7531C29988E00687D6E /* NetworkStream.m in Sources */,
|
||||
453D28BB1D332DB100D523F0 /* OWSMessagesBubblesSizeCalculator.m in Sources */,
|
||||
B660F7541C29988E00687D6E /* SecureEndPoint.m in Sources */,
|
||||
|
@ -2983,6 +3036,7 @@
|
|||
B660F7661C29988E00687D6E /* ResponderSessionDescriptor.m in Sources */,
|
||||
45C681B81D305A580050903A /* OWSCall.m in Sources */,
|
||||
B660F7671C29988E00687D6E /* SignalUtil.m in Sources */,
|
||||
45855F381D9498A40084F340 /* OWSContactAvatarBuilder.m in Sources */,
|
||||
B660F7681C29988E00687D6E /* CategorizingLogger.m in Sources */,
|
||||
B660F7691C29988E00687D6E /* DecayingSampleEstimator.m in Sources */,
|
||||
B660F76A1C29988E00687D6E /* EventWindow.m in Sources */,
|
||||
|
@ -3037,6 +3091,7 @@
|
|||
B660F6DB1C29868000687D6E /* FunctionalUtilTest.m in Sources */,
|
||||
B660F6CF1C29868000687D6E /* SessionDescriptorTest.m in Sources */,
|
||||
B660F6C81C29868000687D6E /* PregeneratedKeyAgreementParticipantProtocol.m in Sources */,
|
||||
452E3C8F1D935C77002A45B0 /* OWSConversationSettingsTableViewController.m in Sources */,
|
||||
B660F6BC1C29868000687D6E /* DnsManagerTest.m in Sources */,
|
||||
B660F6B61C29868000687D6E /* AudioRemoteIOTest.m in Sources */,
|
||||
B660F6D71C29868000687D6E /* Crc32Test.m in Sources */,
|
||||
|
@ -3061,6 +3116,7 @@
|
|||
B660F6C61C29868000687D6E /* MasterSecretTest.m in Sources */,
|
||||
B660F6D91C29868000687D6E /* CyclicalBufferTest.m in Sources */,
|
||||
B660F6DC1C29868000687D6E /* FutureUtilTest.m in Sources */,
|
||||
450873C81D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,
|
||||
B660F6CA1C29868000687D6E /* LowLatencyConnectorTest.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_empty.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_empty@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_empty@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 271 B |
BIN
Signal/Images.xcassets/ic_hourglass_empty.imageset/ic_hourglass_empty@2x.png
vendored
Normal file
After Width: | Height: | Size: 315 B |
BIN
Signal/Images.xcassets/ic_hourglass_empty.imageset/ic_hourglass_empty@3x.png
vendored
Normal file
After Width: | Height: | Size: 421 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_full.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_full@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_hourglass_full@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 207 B |
After Width: | Height: | Size: 231 B |
After Width: | Height: | Size: 367 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_lock_outline_white.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_lock_outline_white@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_lock_outline_white@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 360 B |
BIN
Signal/Images.xcassets/ic_lock_outline.imageset/ic_lock_outline_white@2x.png
vendored
Normal file
After Width: | Height: | Size: 614 B |
BIN
Signal/Images.xcassets/ic_lock_outline.imageset/ic_lock_outline_white@3x.png
vendored
Normal file
After Width: | Height: | Size: 788 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_timer_white.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_timer_white@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "ic_timer_white@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 354 B |
After Width: | Height: | Size: 631 B |
After Width: | Height: | Size: 809 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_empty.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_empty@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_empty@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
BIN
Signal/Images.xcassets/table_ic_hourglass_empty.imageset/table_ic_hourglass_empty.png
vendored
Normal file
After Width: | Height: | Size: 254 B |
BIN
Signal/Images.xcassets/table_ic_hourglass_empty.imageset/table_ic_hourglass_empty@2x.png
vendored
Normal file
After Width: | Height: | Size: 344 B |
BIN
Signal/Images.xcassets/table_ic_hourglass_empty.imageset/table_ic_hourglass_empty@3x.png
vendored
Normal file
After Width: | Height: | Size: 432 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_full.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_full@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_hourglass_full@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
BIN
Signal/Images.xcassets/table_ic_hourglass_full.imageset/table_ic_hourglass_full.png
vendored
Normal file
After Width: | Height: | Size: 209 B |
BIN
Signal/Images.xcassets/table_ic_hourglass_full.imageset/table_ic_hourglass_full@2x.png
vendored
Normal file
After Width: | Height: | Size: 302 B |
BIN
Signal/Images.xcassets/table_ic_hourglass_full.imageset/table_ic_hourglass_full@3x.png
vendored
Normal file
After Width: | Height: | Size: 424 B |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_timer.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_timer@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "table_ic_timer@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 518 B |
After Width: | Height: | Size: 844 B |
After Width: | Height: | Size: 1.0 KiB |
|
@ -38,7 +38,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.6.0.1</string>
|
||||
<string>2.6.0.7</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LOGS_EMAIL</key>
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
#import "TextSecureKitEnv.h"
|
||||
#import "VersionMigrations.h"
|
||||
#import "OWSStaleNotificationObserver.h"
|
||||
#import <SignalServiceKit/OWSReadReceiptObserver.h>
|
||||
#import <SignalServiceKit/OWSDisappearingMessagesJob.h>
|
||||
#import <SignalServiceKit/OWSIncomingMessageReadObserver.h>
|
||||
|
||||
static NSString *const kStoryboardName = @"Storyboard";
|
||||
static NSString *const kInitialViewControllerIdentifier = @"UserInitialViewController";
|
||||
|
@ -26,7 +27,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
|
|||
@interface AppDelegate ()
|
||||
|
||||
@property (nonatomic, retain) UIWindow *screenProtectionWindow;
|
||||
@property (nonatomic) OWSReadReceiptObserver *readReceiptObserver;
|
||||
@property (nonatomic) OWSIncomingMessageReadObserver *incomingMessageReadObserver;
|
||||
@property (nonatomic) OWSStaleNotificationObserver *staleNotificationObserver;
|
||||
|
||||
@end
|
||||
|
@ -111,6 +112,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
|
|||
|
||||
[[PushManager sharedManager] validateUserNotificationSettings];
|
||||
[TSPreKeyManager refreshPreKeys];
|
||||
|
||||
// Clean up any messages that expired since last launch.
|
||||
[[[OWSDisappearingMessagesJob alloc] initWithStorageManager:[TSStorageManager sharedManager]] run];
|
||||
}];
|
||||
|
||||
[AppStoreRating setupRatingLibrary];
|
||||
|
@ -121,8 +125,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
|
|||
[TextSecureKitEnv sharedEnv].contactsManager = [Environment getCurrent].contactsManager;
|
||||
[[TSStorageManager sharedManager] setupDatabase];
|
||||
[TextSecureKitEnv sharedEnv].notificationsManager = [[NotificationsManager alloc] init];
|
||||
self.readReceiptObserver = [OWSReadReceiptObserver new];
|
||||
[self.readReceiptObserver startObserving];
|
||||
self.incomingMessageReadObserver = [[OWSIncomingMessageReadObserver alloc] initWithStorageManager:[TSStorageManager sharedManager]
|
||||
messagesManager:[TSMessagesManager sharedManager]];
|
||||
[self.incomingMessageReadObserver startObserving];
|
||||
|
||||
self.staleNotificationObserver = [OWSStaleNotificationObserver new];
|
||||
[self.staleNotificationObserver startObserving];
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Created by Michael Kirk on 9/26/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSThread;
|
||||
@class OWSContactsManager;
|
||||
|
||||
@interface OWSAvatarBuilder : NSObject
|
||||
|
||||
+ (UIImage *)buildImageForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
|
||||
|
||||
- (nullable UIImage *)buildSavedImage;
|
||||
- (UIImage *)buildDefaultImage;
|
||||
- (UIImage *)build;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,68 @@
|
|||
// Created by Michael Kirk on 9/26/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSAvatarBuilder.h"
|
||||
#import "OWSContactAvatarBuilder.h"
|
||||
#import "OWSGroupAvatarBuilder.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSGroupThread.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSAvatarBuilder
|
||||
|
||||
+ (UIImage *)buildImageForThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
OWSAvatarBuilder *avatarBuilder;
|
||||
if ([thread isKindOfClass:[TSContactThread class]]) {
|
||||
avatarBuilder =
|
||||
[[OWSContactAvatarBuilder alloc] initWithThread:(TSContactThread *)thread contactsManager:contactsManager];
|
||||
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
avatarBuilder = [[OWSGroupAvatarBuilder alloc] initWithThread:(TSGroupThread *)thread];
|
||||
} else {
|
||||
DDLogError(@"%@ called with unsupported thread: %@", self.tag, thread);
|
||||
}
|
||||
return [avatarBuilder build];
|
||||
}
|
||||
|
||||
- (UIImage *)build
|
||||
{
|
||||
UIImage *_Nullable savedImage = [self buildSavedImage];
|
||||
if (savedImage) {
|
||||
return savedImage;
|
||||
} else {
|
||||
return [self buildDefaultImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable UIImage *)buildSavedImage
|
||||
{
|
||||
@throw [NSException
|
||||
exceptionWithName:NSInternalInconsistencyException
|
||||
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (UIImage *)buildDefaultImage
|
||||
{
|
||||
@throw [NSException
|
||||
exceptionWithName:NSInternalInconsistencyException
|
||||
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,9 +1,8 @@
|
|||
// Created by Dylan Bourgeois on 20/11/14.
|
||||
// Portions Copyright (c) 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSMessageData.h"
|
||||
#import "TSMessageAdapter.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <JSQMessagesViewController/JSQMessageData.h>
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
kCallOutgoing = 1,
|
||||
|
@ -14,7 +13,7 @@ typedef enum : NSUInteger {
|
|||
kGroupUpdate = 6
|
||||
} CallStatus;
|
||||
|
||||
@interface OWSCall : NSObject <JSQMessageData, NSCoding, NSCopying>
|
||||
@interface OWSCall : NSObject <OWSMessageData, NSCoding, NSCopying>
|
||||
|
||||
/*
|
||||
* Returns the string Id of the user who initiated the call
|
||||
|
@ -37,11 +36,6 @@ typedef enum : NSUInteger {
|
|||
*/
|
||||
@property (nonatomic) CallStatus status;
|
||||
|
||||
/*
|
||||
* Returns message type for adapter
|
||||
*/
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
|
||||
/**
|
||||
* String to be displayed
|
||||
*/
|
||||
|
|
|
@ -5,6 +5,16 @@
|
|||
#import <JSQMessagesViewController/JSQMessagesTimestampFormatter.h>
|
||||
#import <JSQMessagesViewController/UIImage+JSQMessages.h>
|
||||
|
||||
@interface OWSCall ()
|
||||
|
||||
// -- Redeclaring properties from OWSMessageData protocol to synthesize variables
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
@property (nonatomic, getter=isExpiringMessage) BOOL expiringMessage;
|
||||
@property (nonatomic) uint64_t expiresAtSeconds;
|
||||
@property (nonatomic) uint32_t expiresInSeconds;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSCall
|
||||
|
||||
#pragma mark - Initialzation
|
||||
|
@ -37,6 +47,7 @@
|
|||
_senderDisplayName = [senderDisplayName copy];
|
||||
_date = [date copy];
|
||||
_status = status;
|
||||
_expiringMessage = NO; // TODO - call notifications should expire too.
|
||||
_messageType = TSCallAdapter;
|
||||
|
||||
// TODO interpret detailString from status. make sure it works for calls and
|
||||
|
@ -88,6 +99,20 @@
|
|||
self.date];
|
||||
}
|
||||
|
||||
#pragma mark - OWSMessageEditing
|
||||
|
||||
- (BOOL)canPerformEditingAction:(SEL)action
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)performEditingAction:(SEL)action
|
||||
{
|
||||
// Shouldn't get here, as only supported actions should be exposed via canPerformEditingAction
|
||||
NSString *actionString = NSStringFromSelector(action);
|
||||
DDLogError(@"%@ '%@' action unsupported", self.tag, actionString);
|
||||
}
|
||||
|
||||
#pragma mark - JSQMessageData
|
||||
|
||||
- (BOOL)isMediaMessage
|
||||
|
@ -141,4 +166,16 @@
|
|||
return _detailString;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Created by Michael Kirk on 9/22/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSAvatarBuilder.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSContactsManager;
|
||||
@class TSContactThread;
|
||||
|
||||
@interface OWSContactAvatarBuilder : OWSAvatarBuilder
|
||||
|
||||
- (instancetype)initWithThread:(TSContactThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,74 @@
|
|||
// Created by Michael Kirk on 9/22/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSContactAvatarBuilder.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import "TSThread.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import <JSQMessagesViewController/JSQMessagesAvatarImageFactory.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSContactAvatarBuilder ()
|
||||
|
||||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
@property (nonatomic, readonly) NSString *signalId;
|
||||
@property (nonatomic, readonly) NSString *contactName;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSContactAvatarBuilder
|
||||
|
||||
- (instancetype)initWithThread:(TSContactThread *)thread contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_signalId = thread.contactIdentifier;
|
||||
_contactName = thread.name;
|
||||
_contactsManager = contactsManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)buildSavedImage
|
||||
{
|
||||
return [self.contactsManager imageForPhoneIdentifier:self.signalId];
|
||||
}
|
||||
|
||||
- (UIImage *)buildDefaultImage
|
||||
{
|
||||
NSMutableString *initials = [NSMutableString string];
|
||||
|
||||
if (self.contactName.length > 0) {
|
||||
NSArray *words =
|
||||
[self.contactName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
for (NSString *word in words) {
|
||||
if (word.length > 0) {
|
||||
NSString *firstLetter = [word substringToIndex:1];
|
||||
[initials appendString:[firstLetter uppercaseString]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSRange stringRange = { 0, MIN([initials length], (NSUInteger)3) }; // Rendering max 3 letters.
|
||||
initials = [[initials substringWithRange:stringRange] mutableCopy];
|
||||
|
||||
UIColor *backgroundColor = [UIColor backgroundColorForContact:self.signalId];
|
||||
|
||||
return [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
|
||||
backgroundColor:backgroundColor
|
||||
textColor:[UIColor whiteColor]
|
||||
font:[UIFont ows_boldFontWithSize:36.0]
|
||||
diameter:100] avatarImage];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,16 @@
|
|||
// Created by Michael Kirk on 9/26/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSAvatarBuilder.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSGroupThread;
|
||||
|
||||
@interface OWSGroupAvatarBuilder : OWSAvatarBuilder
|
||||
|
||||
- (instancetype)initWithThread:(TSGroupThread *)thread;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,40 @@
|
|||
// Created by Michael Kirk on 9/26/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSGroupAvatarBuilder.h"
|
||||
#import "TSGroupThread.h"
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSGroupAvatarBuilder ()
|
||||
|
||||
@property (nonatomic, readonly) TSGroupThread *thread;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSGroupAvatarBuilder
|
||||
|
||||
- (instancetype)initWithThread:(TSGroupThread *)thread
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_thread = thread;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)buildSavedImage
|
||||
{
|
||||
return self.thread.groupModel.groupImage;
|
||||
}
|
||||
|
||||
- (UIImage *)buildDefaultImage
|
||||
{
|
||||
return [UIImage imageNamed:@"empty-group-avatar"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -3,6 +3,7 @@
|
|||
#import "OWSMessagesBubblesSizeCalculator.h"
|
||||
#import "OWSDisplayedMessageCollectionViewCell.h"
|
||||
#import "TSMessageAdapter.h"
|
||||
#import "tgmath.h" // generic math allows fmax to handle CGFLoat correctly on 32 & 64bit.
|
||||
|
||||
@implementation OWSMessagesBubblesSizeCalculator
|
||||
|
||||
|
@ -23,11 +24,19 @@
|
|||
{
|
||||
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
|
||||
// But also allow for multi-line error messages.
|
||||
superSize.height = fmax(superSize.height, OWSDisplayedMessageCellHeight);
|
||||
if ([messageData isKindOfClass:[TSMessageAdapter class]]) {
|
||||
TSMessageAdapter *message = (TSMessageAdapter *)messageData;
|
||||
|
||||
if (message.messageType == TSInfoMessageAdapter || message.messageType == TSErrorMessageAdapter) {
|
||||
// DDLogVerbose(@"[OWSMessagesBubblesSizeCalculator] superSize.height:%f, superSize.width:%f",
|
||||
// superSize.height,
|
||||
// superSize.width);
|
||||
|
||||
// header icon hangs ouside of the frame a bit.
|
||||
CGFloat headerIconProtrusion = 30.0f; // too much padding with normal font.
|
||||
// CGFloat headerIconProtrusion = 18.0f; // clips
|
||||
superSize.height = superSize.height + headerIconProtrusion;
|
||||
}
|
||||
}
|
||||
|
||||
return superSize;
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Created by Michael Kirk on 9/26/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSMessageEditing.h"
|
||||
#import <JSQMessagesViewController/JSQMessageData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, TSMessageAdapterType) {
|
||||
TSIncomingMessageAdapter,
|
||||
TSOutgoingMessageAdapter,
|
||||
TSCallAdapter,
|
||||
TSInfoMessageAdapter,
|
||||
TSErrorMessageAdapter,
|
||||
TSMediaAttachmentAdapter,
|
||||
TSGenericTextMessageAdapter, // Used when message direction is unknown (outgoing or incoming)
|
||||
};
|
||||
|
||||
@protocol OWSMessageData <JSQMessageData, OWSMessageEditing>
|
||||
|
||||
@property (nonatomic, readonly) TSMessageAdapterType messageType;
|
||||
@property (nonatomic, readonly, getter=isExpiringMessage) BOOL expiringMessage;
|
||||
@property (nonatomic, readonly) uint64_t expiresAtSeconds;
|
||||
@property (nonatomic, readonly) uint32_t expiresInSeconds;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,4 +1,7 @@
|
|||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
// Created by Michael Kirk on 3/11/16.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol OWSMessageEditing <NSObject>
|
||||
|
||||
|
@ -6,3 +9,5 @@
|
|||
- (void)performEditingAction:(SEL)action;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -6,29 +6,22 @@
|
|||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSMessageData.h"
|
||||
#import "OWSMessageEditing.h"
|
||||
#import <JSQMessagesViewController/JSQMessageData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSInteraction;
|
||||
@class TSThread;
|
||||
|
||||
#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 <OWSMessageData>
|
||||
|
||||
@interface TSMessageAdapter : NSObject <JSQMessageData, OWSMessageEditing>
|
||||
|
||||
+ (id<JSQMessageData>)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread;
|
||||
+ (id<OWSMessageData>)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread;
|
||||
|
||||
@property TSInteraction *interaction;
|
||||
@property TSMessageAdapterType messageType;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -47,7 +47,12 @@
|
|||
|
||||
@property JSQMediaItem<OWSMessageEditing> *mediaItem;
|
||||
|
||||
// ---
|
||||
|
||||
// -- Redeclaring properties from OWSMessageData protocol to synthesize variables
|
||||
@property (nonatomic) TSMessageAdapterType messageType;
|
||||
@property (nonatomic, getter=isExpiringMessage) BOOL expiringMessage;
|
||||
@property (nonatomic) uint64_t expiresAtSeconds;
|
||||
@property (nonatomic) uint32_t expiresInSeconds;
|
||||
|
||||
@property (nonatomic, copy) NSDate *messageDate;
|
||||
@property (nonatomic, retain) NSString *messageBody;
|
||||
|
@ -59,13 +64,34 @@
|
|||
|
||||
@implementation TSMessageAdapter
|
||||
|
||||
+ (id<JSQMessageData>)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread
|
||||
- (instancetype)initWithInteraction:(TSInteraction *)interaction
|
||||
{
|
||||
TSMessageAdapter *adapter = [[TSMessageAdapter alloc] init];
|
||||
adapter.interaction = interaction;
|
||||
adapter.messageDate = interaction.date;
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_interaction = interaction;
|
||||
_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;
|
||||
// Can we just remove this? Haven't found where we're using it...
|
||||
_identifier = (NSUInteger)interaction.uniqueId;
|
||||
|
||||
if ([interaction isKindOfClass:[TSMessage class]]) {
|
||||
TSMessage *message = (TSMessage *)interaction;
|
||||
_expiringMessage = message.isExpiringMessage;
|
||||
_expiresAtSeconds = message.expiresAt / 1000;
|
||||
_expiresInSeconds = message.expiresInSeconds;
|
||||
} else {
|
||||
_expiringMessage = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (id<OWSMessageData>)messageViewDataWithInteraction:(TSInteraction *)interaction inThread:(TSThread *)thread
|
||||
{
|
||||
TSMessageAdapter *adapter = [[TSMessageAdapter alloc] initWithInteraction:interaction];
|
||||
|
||||
if ([thread isKindOfClass:[TSContactThread class]]) {
|
||||
adapter.thread = (TSContactThread *)thread;
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
|
||||
case UIImageOrientationDown: // EXIF = 3
|
||||
transform = CGAffineTransformMakeTranslation(srcSize.width, srcSize.height);
|
||||
transform = CGAffineTransformRotate(transform, M_PI);
|
||||
transform = CGAffineTransformRotate(transform, (CGFloat)M_PI);
|
||||
break;
|
||||
|
||||
case UIImageOrientationDownMirrored: // EXIF = 4
|
||||
|
@ -78,25 +78,25 @@
|
|||
dstSize = CGSizeMake(dstSize.height, dstSize.width);
|
||||
transform = CGAffineTransformMakeTranslation(srcSize.height, srcSize.width);
|
||||
transform = CGAffineTransformScale(transform, -1.0, 1.0);
|
||||
transform = CGAffineTransformRotate(transform, 3.0 * M_PI_2);
|
||||
transform = CGAffineTransformRotate(transform, (CGFloat)(3.0f * M_PI_2));
|
||||
break;
|
||||
|
||||
case UIImageOrientationLeft: // EXIF = 6
|
||||
dstSize = CGSizeMake(dstSize.height, dstSize.width);
|
||||
transform = CGAffineTransformMakeTranslation(0.0, srcSize.width);
|
||||
transform = CGAffineTransformRotate(transform, 3.0 * M_PI_2);
|
||||
transform = CGAffineTransformRotate(transform, (CGFloat)(3.0 * M_PI_2));
|
||||
break;
|
||||
|
||||
case UIImageOrientationRightMirrored: // EXIF = 7
|
||||
dstSize = CGSizeMake(dstSize.height, dstSize.width);
|
||||
transform = CGAffineTransformMakeScale(-1.0, 1.0);
|
||||
transform = CGAffineTransformRotate(transform, M_PI_2);
|
||||
transform = CGAffineTransformRotate(transform, (CGFloat)M_PI_2);
|
||||
break;
|
||||
|
||||
case UIImageOrientationRight: // EXIF = 8
|
||||
dstSize = CGSizeMake(dstSize.height, dstSize.width);
|
||||
transform = CGAffineTransformMakeTranslation(srcSize.height, 0.0);
|
||||
transform = CGAffineTransformRotate(transform, M_PI_2);
|
||||
transform = CGAffineTransformRotate(transform, (CGFloat)M_PI_2);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -166,10 +166,10 @@
|
|||
|
||||
if (wRatio < hRatio) {
|
||||
// NSLog(@"Width imposed, Height scaled ; ratio = %f",wRatio);
|
||||
dstSize = CGSizeMake(boundingSize.width, floor(srcSize.height * wRatio));
|
||||
dstSize = CGSizeMake(boundingSize.width, (CGFloat)floor(srcSize.height * wRatio));
|
||||
} else {
|
||||
// NSLog(@"Height imposed, Width scaled ; ratio = %f",hRatio);
|
||||
dstSize = CGSizeMake(floor(srcSize.width * hRatio), boundingSize.height);
|
||||
dstSize = CGSizeMake((CGFloat)floor(srcSize.width * hRatio), boundingSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// APNavigationController.h
|
||||
// DropDownToolBar
|
||||
//
|
||||
// Created by Ankur Patel on 2/24/14.
|
||||
// Copyright (c) 2014 Encore Dev Labs LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface APNavigationController : UINavigationController
|
||||
|
||||
@property (nonatomic, strong)
|
||||
UIToolbar *dropDownToolbar; // Reference to dynamically change items based on which bar button item is clicked
|
||||
@property (nonatomic, strong) NSString *activeNavigationBarTitle; // Navigation bar title when the toolbar is shown
|
||||
@property (nonatomic, strong) NSString *activeBarButtonTitle; // UIBarButton title when toolbar is shown
|
||||
@property (nonatomic, assign) BOOL isDropDownVisible;
|
||||
|
||||
- (void)setActiveBarButtonTitle:(NSString *)title;
|
||||
- (void)setActiveNavigationBarTitle:(NSString *)title;
|
||||
- (void)toggleDropDown:(id)sender;
|
||||
- (void)hideDropDown:(id)sender;
|
||||
- (void)showDropDown:(id)sender;
|
||||
|
||||
@end
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// APNavigationController.m
|
||||
// DropDownToolBar
|
||||
//
|
||||
// Created by Ankur Patel on 2/24/14.
|
||||
// Copyright (c) 2014 Encore Dev Labs LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#import "APNavigationController.h"
|
||||
|
||||
@interface APNavigationController ()
|
||||
|
||||
@property (nonatomic, copy) NSString *originalNavigationBarTitle;
|
||||
@property (nonatomic, copy) NSString *originalBarButtonTitle;
|
||||
|
||||
@end
|
||||
|
||||
@implementation APNavigationController
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self) {
|
||||
// Custom initialization
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
self.dropDownToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 64)];
|
||||
self.dropDownToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
||||
self.dropDownToolbar.tintColor = self.navigationBar.tintColor;
|
||||
[self.navigationBar.superview insertSubview:self.dropDownToolbar belowSubview:self.navigationBar];
|
||||
self.originalNavigationBarTitle = self.navigationBar.topItem.title;
|
||||
}
|
||||
|
||||
- (void)toggleDropDown:(id)sender {
|
||||
if (self.isDropDownVisible) {
|
||||
[self hideDropDown:sender];
|
||||
} else {
|
||||
[self showDropDown:sender];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)hideDropDown:(id)sender {
|
||||
if (self.isDropDownVisible) {
|
||||
__weak APNavigationController *weakSelf = self;
|
||||
CGRect frame = self.dropDownToolbar.frame;
|
||||
frame.origin.y = CGRectGetMaxY(self.navigationBar.frame);
|
||||
self.dropDownToolbar.frame = frame;
|
||||
[UIView animateWithDuration:0.25
|
||||
animations:^{
|
||||
CGRect finalframe = self.dropDownToolbar.frame;
|
||||
finalframe.origin.y = 0.;
|
||||
weakSelf.dropDownToolbar.frame = finalframe;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
weakSelf.isDropDownVisible = !weakSelf.isDropDownVisible;
|
||||
weakSelf.dropDownToolbar.hidden = YES;
|
||||
}];
|
||||
if (self.activeNavigationBarTitle) {
|
||||
self.navigationBar.topItem.title = self.originalNavigationBarTitle;
|
||||
}
|
||||
if (sender && [sender isKindOfClass:[UIBarButtonItem class]]) {
|
||||
[(UIBarButtonItem *)sender setTitle:self.originalBarButtonTitle];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showDropDown:(id)sender {
|
||||
if (!self.isDropDownVisible) {
|
||||
__weak APNavigationController *weakSelf = self;
|
||||
CGRect frame = self.dropDownToolbar.frame;
|
||||
frame.origin.y = 0.f;
|
||||
self.dropDownToolbar.hidden = NO;
|
||||
self.dropDownToolbar.frame = frame;
|
||||
[UIView animateWithDuration:0.25
|
||||
animations:^{
|
||||
CGRect finalframe = self.dropDownToolbar.frame;
|
||||
finalframe.origin.y = CGRectGetMaxY(self.navigationBar.frame);
|
||||
weakSelf.dropDownToolbar.frame = finalframe;
|
||||
}
|
||||
completion:^(BOOL finished) {
|
||||
weakSelf.isDropDownVisible = !weakSelf.isDropDownVisible;
|
||||
}];
|
||||
if (self.activeNavigationBarTitle) {
|
||||
self.navigationBar.topItem.title = self.activeNavigationBarTitle;
|
||||
}
|
||||
if (sender && [sender isKindOfClass:[UIBarButtonItem class]]) {
|
||||
self.originalBarButtonTitle = [(UIBarButtonItem *)sender title];
|
||||
if (self.activeBarButtonTitle) {
|
||||
[(UIBarButtonItem *)sender setTitle:self.activeBarButtonTitle];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -12,12 +12,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@class TSThread;
|
||||
@class OWSFingerprint;
|
||||
@class OWSConversationSettingsTableViewController;
|
||||
|
||||
@interface FingerprintViewController : UIViewController <OWSQRScannerDelegate>
|
||||
|
||||
@property (nullable) OWSConversationSettingsTableViewController *dismissDelegate;
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
fingerprint:(OWSFingerprint *)fingerprint
|
||||
contactName:(NSString *)contactName;
|
||||
|
||||
- (void)controller:(OWSQRCodeScanningViewController *)controller didDetectQRCodeWithData:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#import "FingerprintViewController.h"
|
||||
#import "DJWActionSheet+OWS.h"
|
||||
#import "OWSConversationSettingsTableViewController.h"
|
||||
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||
#import <SignalServiceKit/OWSFingerprint.h>
|
||||
#import <SignalServiceKit/TSInfoMessage.h>
|
||||
|
@ -27,7 +28,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (strong, nonatomic) NSString *contactName;
|
||||
@property (strong, nonatomic) OWSQRCodeScanningViewController *qrScanningController;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UINavigationBar *modalNavigationBar;
|
||||
@property (strong, nonatomic) IBOutlet UIBarButtonItem *dismissModalButton;
|
||||
@property (strong, nonatomic) IBOutlet UIView *qrScanningView;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *scanningInstructions;
|
||||
@property (strong, nonatomic) IBOutlet UIView *scanningContainer;
|
||||
@property (strong, nonatomic) IBOutlet UIView *instructionsContainer;
|
||||
@property (strong, nonatomic) IBOutlet UIView *qrContainer;
|
||||
|
@ -35,9 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (strong, nonatomic) IBOutlet UIImageView *privacyVerificationQRCode;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *privacyVerificationFingerprint;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *instructionsLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *titleLabel;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *scanButton;
|
||||
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *qrCodeCenterConstraint;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -55,8 +57,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.leftBarButtonItem = self.dismissModalButton;
|
||||
[self.modalNavigationBar pushNavigationItem:self.navigationItem animated:NO];
|
||||
|
||||
// HACK for transparent navigation bar.
|
||||
[self.modalNavigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
|
||||
self.modalNavigationBar.shadowImage = [UIImage new];
|
||||
self.modalNavigationBar.translucent = YES;
|
||||
|
||||
self.storageManager = [TSStorageManager sharedManager];
|
||||
self.qrScanningView.hidden = YES;
|
||||
|
||||
// HACK to get full width preview layer
|
||||
CGRect oldFrame = self.qrScanningView.frame;
|
||||
|
@ -67,7 +77,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.qrScanningView.frame = newFrame;
|
||||
// END HACK to get full width preview layer
|
||||
|
||||
self.titleLabel.text = NSLocalizedString(@"PRIVACY_VERIFICATION_TITLE", @"Navbar title");
|
||||
self.title = NSLocalizedString(@"PRIVACY_VERIFICATION_TITLE", @"Navbar title");
|
||||
NSString *instructionsFormat = NSLocalizedString(@"PRIVACY_VERIFICATION_INSTRUCTIONS",
|
||||
@"Paragraph(s) shown alongside keying material when verifying privacy with {{contact name}}");
|
||||
self.instructionsLabel.text = [NSString stringWithFormat:instructionsFormat, self.contactName];
|
||||
|
@ -97,6 +107,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.privacyVerificationQRCodeFrame.layer.cornerRadius = self.privacyVerificationQRCodeFrame.frame.size.height / 2;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:YES];
|
||||
if (self.dismissDelegate) {
|
||||
[self.dismissDelegate presentedModalWasDismissed];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
|
||||
{
|
||||
if ([segue.identifier isEqualToString:@"embedIdentityQRScanner"]) {
|
||||
|
@ -150,23 +168,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
DDLogInfo(@"%@ Showing Scanner", self.tag);
|
||||
self.qrScanningView.hidden = NO;
|
||||
|
||||
// Recommended before animating a constraint.
|
||||
[self.view layoutIfNeeded];
|
||||
|
||||
// Shift QRCode up within it's own frame, while shifting it's whole
|
||||
// frame down.
|
||||
self.qrCodeCenterConstraint.constant = 0.0f;
|
||||
self.scanningInstructions.hidden = NO;
|
||||
[UIView animateWithDuration:0.4
|
||||
delay:0.0
|
||||
options:UIViewAnimationOptionCurveEaseInOut
|
||||
animations:^{
|
||||
|
||||
self.scanningContainer.frame = self.qrContainer.frame;
|
||||
self.qrContainer.frame = self.instructionsContainer.frame;
|
||||
self.instructionsContainer.alpha = 0.0f;
|
||||
// animate constraint smoothly
|
||||
[self.view layoutIfNeeded];
|
||||
}
|
||||
completion:nil];
|
||||
|
||||
|
@ -254,6 +263,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[super dismissViewControllerAnimated:flag completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
#import "TSThread.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSContactsManager;
|
||||
|
||||
typedef enum : NSUInteger { kArchiveState = 0, kInboxState = 1 } CellState;
|
||||
|
||||
@interface InboxTableViewCell : UITableViewCell <UIScrollViewDelegate>
|
||||
|
@ -17,7 +21,10 @@ typedef enum : NSUInteger { kArchiveState = 0, kInboxState = 1 } CellState;
|
|||
@property (nonatomic, retain) NSString *threadId;
|
||||
|
||||
+ (instancetype)inboxTableViewCell;
|
||||
- (void)configureWithThread:(TSThread *)thread;
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager;
|
||||
- (void)animateDisappear;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
// Created by Dylan Bourgeois on 27/10/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import <JSQMessagesViewController/JSQMessagesAvatarImageFactory.h>
|
||||
#import <JSQMessagesViewController/UIImage+JSQMessages.h>
|
||||
#import "Environment.h"
|
||||
#import "InboxTableViewCell.h"
|
||||
#import "Environment.h"
|
||||
#import "OWSAvatarBuilder.h"
|
||||
#import "PreferencesUtil.h"
|
||||
#import "TSContactThread.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import "TSMessagesManager.h"
|
||||
#import "Util.h"
|
||||
#import <JSQMessagesViewController/JSQMessagesAvatarImageFactory.h>
|
||||
#import <JSQMessagesViewController/UIImage+JSQMessages.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define ARCHIVE_IMAGE_VIEW_WIDTH 22.0f
|
||||
#define DELETE_IMAGE_VIEW_WIDTH 19.0f
|
||||
|
@ -39,86 +42,45 @@
|
|||
self.selectionStyle = UITableViewCellSelectionStyleDefault;
|
||||
}
|
||||
|
||||
- (NSString *)reuseIdentifier {
|
||||
- (nullable NSString *)reuseIdentifier
|
||||
{
|
||||
return NSStringFromClass(self.class);
|
||||
}
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread {
|
||||
- (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
if (!_threadId || ![_threadId isEqualToString:thread.uniqueId]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.hidden = YES;
|
||||
});
|
||||
}
|
||||
|
||||
NSString *name = thread.name;
|
||||
_threadId = thread.uniqueId;
|
||||
NSString *name = thread.name;
|
||||
if (name.length == 0 && [thread isKindOfClass:[TSGroupThread class]]) {
|
||||
name = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
|
||||
}
|
||||
UIImage *avatar = [OWSAvatarBuilder buildImageForThread:thread contactsManager:contactsManager];
|
||||
self.threadId = thread.uniqueId;
|
||||
NSString *snippetLabel = thread.lastMessageLabel;
|
||||
NSAttributedString *attributedDate = [self dateAttributedString:thread.lastMessageDate];
|
||||
NSUInteger unreadCount = [[TSMessagesManager sharedManager] unreadMessagesInThread:thread];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_nameLabel.text = name;
|
||||
_snippetLabel.text = snippetLabel;
|
||||
_timeLabel.attributedText = attributedDate;
|
||||
self.nameLabel.text = name;
|
||||
self.snippetLabel.text = snippetLabel;
|
||||
self.timeLabel.attributedText = attributedDate;
|
||||
self.contactPictureView.image = avatar;
|
||||
[UIUtil applyRoundedBorderToImageView:&_contactPictureView];
|
||||
|
||||
if ([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
_contactPictureView.contentMode = UIViewContentModeScaleAspectFill;
|
||||
_contactPictureView.image = ((TSGroupThread *)thread).groupModel.groupImage != nil
|
||||
? ((TSGroupThread *)thread).groupModel.groupImage
|
||||
: [UIImage imageNamed:@"empty-group-avatar"];
|
||||
if ([_nameLabel.text length] == 0) {
|
||||
_nameLabel.text = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
|
||||
}
|
||||
if (_contactPictureView.image != nil) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[UIUtil applyRoundedBorderToImageView:&_contactPictureView];
|
||||
});
|
||||
}
|
||||
} else {
|
||||
NSMutableString *initials = [NSMutableString string];
|
||||
self.separatorInset = UIEdgeInsetsMake(0, _contactPictureView.frame.size.width * 1.5f, 0, 0);
|
||||
|
||||
if ([name length] > 0) {
|
||||
NSArray *words = [name componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
for (NSString *word in words) {
|
||||
if ([word length] > 0) {
|
||||
NSString *firstLetter = [word substringToIndex:1];
|
||||
[initials appendString:[firstLetter uppercaseString]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSRange stringRange = {0, MIN([initials length], (NSUInteger)3)}; // Rendering max 3 letters.
|
||||
initials = [[initials substringWithRange:stringRange] mutableCopy];
|
||||
|
||||
UIColor *backgroundColor =
|
||||
thread.isGroupThread ? [UIColor whiteColor]
|
||||
: [UIColor backgroundColorForContact:((TSContactThread *)thread).contactIdentifier];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
UIImage *image =
|
||||
[[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
|
||||
backgroundColor:backgroundColor
|
||||
textColor:[UIColor whiteColor]
|
||||
font:[UIFont ows_boldFontWithSize:36.0]
|
||||
diameter:100] avatarImage];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_contactPictureView.image = thread.image != nil ? thread.image : image;
|
||||
|
||||
if (thread.image != nil) {
|
||||
[UIUtil applyRoundedBorderToImageView:&_contactPictureView];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
self.separatorInset = UIEdgeInsetsMake(0, _contactPictureView.frame.size.width * 1.5f, 0, 0);
|
||||
|
||||
if (thread.hasUnreadMessages) {
|
||||
[self updateCellForUnreadMessage];
|
||||
} else {
|
||||
[self updateCellForReadMessage];
|
||||
}
|
||||
[self setUnreadMsgCount:unreadCount];
|
||||
self.hidden = NO;
|
||||
if (thread.hasUnreadMessages) {
|
||||
[self updateCellForUnreadMessage];
|
||||
} else {
|
||||
[self updateCellForReadMessage];
|
||||
}
|
||||
[self setUnreadMsgCount:unreadCount];
|
||||
self.hidden = NO;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -202,11 +164,11 @@
|
|||
[_unreadLabel sizeToFit];
|
||||
|
||||
CGPoint offset = CGPointMake(0.0f, 5.0f);
|
||||
_unreadLabel.frame =
|
||||
CGRectMake(offset.x + floor((2.0f * (25 - _unreadLabel.frame.size.width) / 2.0f) / 2.0f),
|
||||
offset.y,
|
||||
_unreadLabel.frame.size.width,
|
||||
_unreadLabel.frame.size.height);
|
||||
_unreadLabel.frame
|
||||
= CGRectMake(offset.x + (CGFloat)floor((2.0f * (25.0f - _unreadLabel.frame.size.width) / 2.0f) / 2.0f),
|
||||
offset.y,
|
||||
_unreadLabel.frame.size.width,
|
||||
_unreadLabel.frame.size.height);
|
||||
_messageCounter.hidden = NO;
|
||||
} else {
|
||||
_messageCounter.hidden = YES;
|
||||
|
@ -225,3 +187,5 @@
|
|||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -222,9 +222,9 @@
|
|||
[self.sendTextButton setBackgroundColor:[UIColor ows_materialBlueColor]];
|
||||
[self.sendTextButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
self.sendTextButton.frame = CGRectMake(self.searchController.searchBar.frame.origin.x,
|
||||
self.searchController.searchBar.frame.origin.y + 44.0,
|
||||
self.searchController.searchBar.frame.size.width,
|
||||
44.0);
|
||||
self.searchController.searchBar.frame.origin.y + 44.0f,
|
||||
self.searchController.searchBar.frame.size.width,
|
||||
44.0);
|
||||
[self.view addSubview:self.sendTextButton];
|
||||
self.sendTextButton.hidden = YES;
|
||||
|
||||
|
|
|
@ -8,11 +8,12 @@
|
|||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <MediaPlayer/MediaPlayer.h>
|
||||
#import "APNavigationController.h"
|
||||
#import <JSQMessagesViewController/JSQMessagesViewController.h>
|
||||
#import "TSGroupModel.h"
|
||||
@class TSThread;
|
||||
|
||||
extern NSString *const OWSMessagesViewControllerDidAppearNotification;
|
||||
|
||||
@interface MessagesViewController : JSQMessagesViewController <UIImagePickerControllerDelegate,
|
||||
UINavigationControllerDelegate,
|
||||
UITextViewDelegate,
|
||||
|
@ -22,7 +23,6 @@
|
|||
|
||||
|
||||
@property (nonatomic, readonly) TSThread *thread;
|
||||
@property (nonatomic, retain) APNavigationController *navController;
|
||||
@property (nonatomic, strong) MPMoviePlayerController *videoPlayer;
|
||||
@property (nonatomic, strong) AVAudioPlayer *audioPlayer;
|
||||
@property (nonatomic, strong) AVAudioRecorder *audioRecorder;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Created by Michael Kirk on 9/21/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSThread;
|
||||
|
||||
@interface OWSConversationSettingsTableViewController : UITableViewController
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread;
|
||||
- (void)presentedModalWasDismissed;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,459 @@
|
|||
// Created by Michael Kirk on 9/21/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSConversationSettingsTableViewController.h"
|
||||
#import "Environment.h"
|
||||
#import "FingerprintViewController.h"
|
||||
#import "NewGroupViewController.h"
|
||||
#import "OWSAvatarBuilder.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "PhoneNumber.h"
|
||||
#import "ShowGroupMembersViewController.h"
|
||||
#import "UIUtil.h"
|
||||
#import <25519/Curve25519.h>
|
||||
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||
#import <SignalServiceKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
|
||||
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
|
||||
#import <SignalServiceKit/OWSFingerprint.h>
|
||||
#import <SignalServiceKit/OWSFingerprintBuilder.h>
|
||||
#import <SignalServiceKit/OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h>
|
||||
#import <SignalServiceKit/TSGroupThread.h>
|
||||
#import <SignalServiceKit/TSMessagesManager+sendMessages.h>
|
||||
#import <SignalServiceKit/TSStorageManager.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerSection) {
|
||||
OWSConversationSettingsTableViewControllerSectionContact,
|
||||
OWSConversationSettingsTableViewControllerSectionGroup
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerContactCellIndex) {
|
||||
OWSConversationSettingsTableViewControllerCellIndexShowFingerprint,
|
||||
OWSConversationSettingsTableViewControllerCellIndexToggleDisappearingMessages,
|
||||
OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration
|
||||
};
|
||||
|
||||
typedef NS_ENUM(NSUInteger, OWSConversationSettingsTableViewControllerGroupCellIndex) {
|
||||
OWSConversationSettingsTableViewControllerCellIndexUpdateGroup,
|
||||
OWSConversationSettingsTableViewControllerCellIndexLeaveGroup,
|
||||
OWSConversationSettingsTableViewControllerCellIndexSeeGroupMembers
|
||||
};
|
||||
|
||||
static NSString *const OWSConversationSettingsTableViewControllerSegueUpdateGroup =
|
||||
@"OWSConversationSettingsTableViewControllerSegueUpdateGroup";
|
||||
static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupMembers =
|
||||
@"OWSConversationSettingsTableViewControllerSegueShowGroupMembers";
|
||||
|
||||
@interface OWSConversationSettingsTableViewController ()
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *verifyPrivacyCell;
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *toggleDisappearingMessagesCell;
|
||||
@property (strong, nonatomic) IBOutlet UISwitch *disappearingMessagesSwitch;
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *disappearingMessagesDurationCell;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *disappearingMessagesDurationLabel;
|
||||
@property (strong, nonatomic) IBOutlet UISlider *disappearingMessagesDurationSlider;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *updateGroupCell;
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *leaveGroupCell;
|
||||
@property (strong, nonatomic) IBOutlet UITableViewCell *listGroupMembersCell;
|
||||
@property (strong, nonatomic) IBOutlet UIImageView *avatar;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *nameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *signalIdLabel;
|
||||
|
||||
@property (nonatomic) TSThread *thread;
|
||||
@property (nonatomic) NSString *contactName;
|
||||
@property (nonatomic) NSString *signalId;
|
||||
@property (nonatomic) UIImage *avatarImage;
|
||||
@property (nonatomic) BOOL isGroupThread;
|
||||
|
||||
@property (nonatomic) NSArray<NSNumber *> *disappearingMessagesDurations;
|
||||
@property (nonatomic) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
|
||||
|
||||
@property (nonatomic, readonly) TSStorageManager *storageManager;
|
||||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
@property (nonatomic, readonly) TSMessagesManager *messagesManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSConversationSettingsTableViewController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_storageManager = [TSStorageManager sharedManager];
|
||||
_contactsManager = [[Environment getCurrent] contactsManager];
|
||||
_messagesManager = [TSMessagesManager sharedManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_storageManager = [TSStorageManager sharedManager];
|
||||
_contactsManager = [[Environment getCurrent] contactsManager];
|
||||
_messagesManager = [TSMessagesManager sharedManager];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
{
|
||||
self.thread = thread;
|
||||
self.signalId = thread.contactIdentifier;
|
||||
self.contactName = thread.name;
|
||||
|
||||
if ([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
self.isGroupThread = YES;
|
||||
if (self.contactName.length == 0) {
|
||||
self.contactName = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"");
|
||||
}
|
||||
} else {
|
||||
self.isGroupThread = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - View Lifecycle
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.nameLabel.text = self.contactName;
|
||||
if (self.signalId) {
|
||||
self.signalIdLabel.text =
|
||||
[PhoneNumber bestEffortFormatPartialUserSpecifiedTextToLookLikeAPhoneNumber:self.signalId];
|
||||
} else {
|
||||
// Don't print anything for groups.
|
||||
self.signalIdLabel.text = nil;
|
||||
}
|
||||
self.avatar.image = [OWSAvatarBuilder buildImageForThread:self.thread contactsManager:self.contactsManager];
|
||||
|
||||
// Translations
|
||||
self.title = NSLocalizedString(@"CONVERSATION_SETTINGS", @"title for conversation settings screen");
|
||||
self.verifyPrivacyCell.textLabel.text
|
||||
= NSLocalizedString(@"VERIFY_PRIVACY", @"table cell label in conversation settings");
|
||||
self.toggleDisappearingMessagesCell.textLabel.text
|
||||
= NSLocalizedString(@"DISAPPEARING_MESSAGES", @"table cell label in conversation settings");
|
||||
self.updateGroupCell.textLabel.text
|
||||
= NSLocalizedString(@"EDIT_GROUP_ACTION", @"table cell label in conversation settings");
|
||||
self.leaveGroupCell.textLabel.text
|
||||
= NSLocalizedString(@"LEAVE_GROUP_ACTION", @"table cell label in conversation settings");
|
||||
self.listGroupMembersCell.textLabel.text
|
||||
= NSLocalizedString(@"LIST_GROUP_MEMBERS_ACTION", @"table cell label in conversation settings");
|
||||
|
||||
self.toggleDisappearingMessagesCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
self.disappearingMessagesDurationCell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
|
||||
self.disappearingMessagesDurations = [OWSDisappearingMessagesConfiguration validDurationsSeconds];
|
||||
self.disappearingMessagesDurationSlider.maximumValue = (float)(self.disappearingMessagesDurations.count - 1);
|
||||
self.disappearingMessagesDurationSlider.minimumValue = 0;
|
||||
self.disappearingMessagesDurationSlider.continuous = YES; // NO fires change event only once you let go
|
||||
|
||||
self.disappearingMessagesConfiguration =
|
||||
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:self.thread.uniqueId];
|
||||
|
||||
if (!self.disappearingMessagesConfiguration) {
|
||||
self.disappearingMessagesConfiguration =
|
||||
[[OWSDisappearingMessagesConfiguration alloc] initDefaultWithThreadId:self.thread.uniqueId];
|
||||
}
|
||||
|
||||
self.disappearingMessagesDurationSlider.value = self.disappearingMessagesConfiguration.durationIndex;
|
||||
[self toggleDisappearingMessages:self.disappearingMessagesConfiguration.isEnabled];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
// HACK to unselect rows when swiping back
|
||||
// http://stackoverflow.com/questions/19379510/uitableviewcell-doesnt-get-deselected-when-swiping-back-quickly
|
||||
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (self.disappearingMessagesConfiguration.isNewRecord && !self.disappearingMessagesConfiguration.isEnabled) {
|
||||
// don't save defaults, else we'll unintentionally save the configuration and notify the contact.
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.disappearingMessagesConfiguration.dictionaryValueDidChange) {
|
||||
[self.disappearingMessagesConfiguration save];
|
||||
OWSDisappearingConfigurationUpdateInfoMessage *infoMessage =
|
||||
[[OWSDisappearingConfigurationUpdateInfoMessage alloc]
|
||||
initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
thread:self.thread
|
||||
configuration:self.disappearingMessagesConfiguration];
|
||||
[infoMessage save];
|
||||
|
||||
[OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob
|
||||
runWithConfiguration:self.disappearingMessagesConfiguration
|
||||
thread:self.thread
|
||||
messagesManager:self.messagesManager];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
// Round avatar corners.
|
||||
self.avatar.layer.borderColor = UIColor.clearColor.CGColor;
|
||||
self.avatar.layer.masksToBounds = YES;
|
||||
self.avatar.layer.cornerRadius = self.avatar.frame.size.height / 2.0f;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
NSInteger baseCount = [super tableView:tableView numberOfRowsInSection:section];
|
||||
|
||||
if (section == OWSConversationSettingsTableViewControllerSectionGroup) {
|
||||
if (self.isGroupThread) {
|
||||
return baseCount;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (section == OWSConversationSettingsTableViewControllerSectionContact) {
|
||||
if (self.isGroupThread) {
|
||||
// No fingerprint for group thread.
|
||||
baseCount -= 1;
|
||||
}
|
||||
|
||||
if (!self.disappearingMessagesSwitch.isOn) {
|
||||
// hide duration slider when disappearing messages is off.
|
||||
baseCount -= 1;
|
||||
}
|
||||
}
|
||||
return baseCount;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (self.isGroupThread && indexPath.section == OWSConversationSettingsTableViewControllerSectionContact) {
|
||||
// Since fingerprint cell is hidden for group threads we offset our index path
|
||||
NSIndexPath *offsetIndexPath = [NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section];
|
||||
return [super tableView:tableView cellForRowAtIndexPath:offsetIndexPath];
|
||||
}
|
||||
|
||||
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
if (cell == self.disappearingMessagesDurationCell) {
|
||||
NSIndexPath *originalDurationSliderIndexPath = [NSIndexPath
|
||||
indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration
|
||||
inSection:OWSConversationSettingsTableViewControllerSectionContact];
|
||||
return [super tableView:tableView heightForRowAtIndexPath:originalDurationSliderIndexPath];
|
||||
} else {
|
||||
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
|
||||
}
|
||||
}
|
||||
|
||||
// Called before the user changes the selection. Return a new indexPath, or nil, to change the proposed selection.
|
||||
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
|
||||
|
||||
// Don't highlight rows that have no selection style.
|
||||
if (cell.selectionStyle == UITableViewCellSelectionStyleNone) {
|
||||
return nil;
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
DDLogDebug(@"%@ tapped indexPath:%@", self.tag, indexPath);
|
||||
|
||||
if (indexPath.section == OWSConversationSettingsTableViewControllerSectionGroup
|
||||
&& indexPath.row == OWSConversationSettingsTableViewControllerCellIndexLeaveGroup) {
|
||||
|
||||
[self didTapLeaveGroup];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
if (section == OWSConversationSettingsTableViewControllerSectionGroup) {
|
||||
if (self.isGroupThread) {
|
||||
return NSLocalizedString(@"GROUP_MANAGEMENT_SECTION", @"Conversation settings table section title");
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
return [super tableView:tableView titleForHeaderInSection:section];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)didTapLeaveGroup
|
||||
{
|
||||
UIAlertController *alertController =
|
||||
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"CONFIRM_LEAVE_GROUP_TITLE", @"Alert title")
|
||||
message:NSLocalizedString(@"CONFIRM_LEAVE_GROUP_DESCRIPTION", @"Alert body")
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
UIAlertAction *leaveAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"LEAVE_BUTTON_TITLE", @"Confirmation button within contextual alert")
|
||||
style:UIAlertActionStyleDestructive
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[self leaveGroup];
|
||||
}];
|
||||
[alertController addAction:leaveAction];
|
||||
|
||||
UIAlertAction *cancelAction = [UIAlertAction
|
||||
actionWithTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", nil)
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction *_Nonnull action) {
|
||||
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
|
||||
}];
|
||||
[alertController addAction:cancelAction];
|
||||
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)leaveGroup
|
||||
{
|
||||
TSGroupThread *gThread = (TSGroupThread *)self.thread;
|
||||
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
|
||||
inThread:gThread
|
||||
messageBody:@""];
|
||||
message.groupMetaMessage = TSGroupMessageQuit;
|
||||
[self.messagesManager sendMessage:message
|
||||
inThread:gThread
|
||||
success:^{
|
||||
DDLogInfo(@"%@ Successfully left group.", self.tag);
|
||||
}
|
||||
failure:^{
|
||||
DDLogWarn(@"%@ Failed to leave group", self.tag);
|
||||
}];
|
||||
|
||||
NSMutableArray *newGroupMemberIds = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
|
||||
[newGroupMemberIds removeObject:[self.storageManager localNumber]];
|
||||
gThread.groupModel.groupMemberIds = newGroupMemberIds;
|
||||
[gThread save];
|
||||
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void)presentedModalWasDismissed
|
||||
{
|
||||
// Else row stays selected after dismissing modal.
|
||||
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
|
||||
}
|
||||
|
||||
- (IBAction)disappearingMessagesSwitchValueDidChange:(id)sender
|
||||
{
|
||||
if (![sender isKindOfClass:[UISwitch class]]) {
|
||||
DDLogError(@"%@ Unexpected sender for disappearing messages switch: %@", self.tag, sender);
|
||||
}
|
||||
UISwitch *disappearingMessagesSwitch = (UISwitch *)sender;
|
||||
[self toggleDisappearingMessages:disappearingMessagesSwitch.isOn];
|
||||
}
|
||||
|
||||
- (void)toggleDisappearingMessages:(BOOL)flag
|
||||
{
|
||||
self.disappearingMessagesConfiguration.enabled = flag;
|
||||
|
||||
// When this message is called as a result of the switch being flipped, this will be a no-op
|
||||
// but it allows us to resuse the method to set the switch programmatically in view setup.
|
||||
self.disappearingMessagesSwitch.on = flag;
|
||||
[self durationSliderDidChange:self.disappearingMessagesDurationSlider];
|
||||
|
||||
// Animate show/hide of duration settings.
|
||||
if (flag) {
|
||||
[self.tableView insertRowsAtIndexPaths:@[ self.indexPathForDurationSlider ]
|
||||
withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
} else {
|
||||
[self.tableView deleteRowsAtIndexPaths:@[ self.indexPathForDurationSlider ]
|
||||
withRowAnimation:UITableViewRowAnimationTop];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForDurationSlider
|
||||
{
|
||||
if (self.isGroupThread) {
|
||||
return [NSIndexPath
|
||||
indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration - 1
|
||||
inSection:OWSConversationSettingsTableViewControllerSectionContact];
|
||||
} else {
|
||||
return [NSIndexPath
|
||||
indexPathForRow:OWSConversationSettingsTableViewControllerCellIndexSetDisappearingMessagesDuration
|
||||
inSection:OWSConversationSettingsTableViewControllerSectionContact];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)durationSliderDidChange:(UISlider *)slider
|
||||
{
|
||||
// snap the slider to a valid value
|
||||
NSUInteger index = (NSUInteger)(slider.value + 0.5);
|
||||
[slider setValue:index animated:YES];
|
||||
NSNumber *numberOfSeconds = self.disappearingMessagesDurations[index];
|
||||
self.disappearingMessagesConfiguration.durationSeconds = [numberOfSeconds unsignedIntValue];
|
||||
|
||||
if (self.disappearingMessagesConfiguration.isEnabled) {
|
||||
NSString *keepForFormat = NSLocalizedString(@"KEEP_MESSAGES_DURATION",
|
||||
@"Slider label embeds {{TIME_AMOUNT}}, e.g. '2 hours'. See *_TIME_AMOUNT strings for examples.");
|
||||
self.disappearingMessagesDurationLabel.text =
|
||||
[NSString stringWithFormat:keepForFormat, self.disappearingMessagesConfiguration.durationString];
|
||||
} else {
|
||||
self.disappearingMessagesDurationLabel.text
|
||||
= NSLocalizedString(@"KEEP_MESSAGES_FOREVER", @"Slider label when disappearing messages is off");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender
|
||||
{
|
||||
if ([segue.destinationViewController isKindOfClass:[FingerprintViewController class]]) {
|
||||
FingerprintViewController *controller = (FingerprintViewController *)segue.destinationViewController;
|
||||
|
||||
OWSFingerprintBuilder *fingerprintBuilder =
|
||||
[[OWSFingerprintBuilder alloc] initWithStorageManager:self.storageManager];
|
||||
OWSFingerprint *fingerprint = [fingerprintBuilder fingerprintWithTheirSignalId:self.thread.contactIdentifier];
|
||||
|
||||
[controller configureWithThread:self.thread fingerprint:fingerprint contactName:self.contactName];
|
||||
controller.dismissDelegate = self;
|
||||
} else if ([segue.identifier isEqualToString:OWSConversationSettingsTableViewControllerSegueUpdateGroup]) {
|
||||
NewGroupViewController *vc = [segue destinationViewController];
|
||||
[vc configWithThread:(TSGroupThread *)self.thread];
|
||||
} else if ([segue.identifier isEqualToString:OWSConversationSettingsTableViewControllerSegueShowGroupMembers]) {
|
||||
ShowGroupMembersViewController *vc = [segue destinationViewController];
|
||||
[vc configWithThread:(TSGroupThread *)self.thread];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -59,8 +59,6 @@ int const OWSLinkedDevicesTableViewControllerSectionAddDevice = 1;
|
|||
[self.refreshControl addTarget:self action:@selector(refreshDevices) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
[self setupEditButton];
|
||||
// So we can still tap on "add new device"
|
||||
self.tableView.allowsSelectionDuringEditing = YES;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "APNavigationController.h"
|
||||
#import "TSSocketManager.h"
|
||||
@interface SignalsNavigationController : APNavigationController
|
||||
|
||||
@interface SignalsNavigationController : UINavigationController
|
||||
@property (nonatomic, strong) UIProgressView *socketStatusView;
|
||||
@property (nonatomic, strong) NSTimer *updateStatusTimer;
|
||||
@end
|
||||
|
|
|
@ -39,12 +39,39 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow";
|
|||
@property (nonatomic) long inboxCount;
|
||||
@property (nonatomic, retain) UISegmentedControl *segmentedControl;
|
||||
@property (nonatomic, strong) id previewingContext;
|
||||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SignalsViewController
|
||||
|
||||
- (void)awakeFromNib {
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_contactsManager = [Environment getCurrent].contactsManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_contactsManager = [Environment getCurrent].contactsManager;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
[[Environment getCurrent] setSignalsViewController:self];
|
||||
}
|
||||
|
||||
|
@ -172,7 +199,7 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow";
|
|||
}
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[cell configureWithThread:thread];
|
||||
[cell configureWithThread:thread contactsManager:self.contactsManager];
|
||||
});
|
||||
|
||||
if ((unsigned long)indexPath.row == [self.threadMappings numberOfItemsInSection:0] - 1) {
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
#import <JSQMessagesViewController/JSQMessagesCollectionViewCell.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
static const CGFloat OWSDisplayedMessageCellHeight = 70.0f;
|
||||
extern const CGFloat OWSDisplayedMessageCellMinimumHeight;
|
||||
|
||||
@interface OWSDisplayedMessageCollectionViewCell : JSQMessagesCollectionViewCell
|
||||
|
||||
@property (weak, nonatomic, readonly) JSQMessagesLabel *cellLabel;
|
||||
@property (weak, nonatomic, readonly) UIImageView *headerImageView;
|
||||
@property (strong, nonatomic, readonly) UIView *textContainer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
#import <JSQMessagesViewController/UIView+JSQMessages.h>
|
||||
|
||||
const CGFloat OWSDisplayedMessageCellMinimumHeight = 70.0;
|
||||
|
||||
@interface OWSDisplayedMessageCollectionViewCell ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet JSQMessagesLabel *cellLabel;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *cellTopLabelHeightConstraint;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *headerImageView;
|
||||
@property (strong, nonatomic) IBOutlet UIView *textContainer;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -39,12 +40,10 @@
|
|||
|
||||
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];
|
||||
self.messageBubbleContainerView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
|
||||
self.messageBubbleContainerView.layer.borderWidth = 0.75f;
|
||||
self.messageBubbleContainerView.layer.cornerRadius = 5.0f;
|
||||
self.cellLabel.textColor = [UIColor darkGrayColor];
|
||||
}
|
||||
|
||||
#pragma mark - Collection view cell
|
||||
|
@ -56,13 +55,4 @@
|
|||
self.cellLabel.text = nil;
|
||||
}
|
||||
|
||||
// This subclass does not have a messageBubbleContainerView, so superclass
|
||||
// touch calculations will be incorrect. Since this view spans the entire
|
||||
// frame, we can override the touch handler to respond to user actions by
|
||||
// default.
|
||||
- (void)jsq_handleTapGesture:(UITapGestureRecognizer *)tap
|
||||
{
|
||||
[self.delegate messagesCollectionViewCellDidTapMessageBubble:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
|
@ -16,8 +17,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cell top label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gcR-Rk-KDC" userLabel="Cell top label" customClass="JSQMessagesLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="20"/>
|
||||
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="ckj-xD-FJI"/>
|
||||
</constraints>
|
||||
|
@ -25,20 +25,18 @@
|
|||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qCf-bs-dBd" userLabel="textContainer">
|
||||
<rect key="frame" x="0.0" y="42" width="320" height="48"/>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qCf-bs-dBd" userLabel="Bubble container">
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" numberOfLines="0" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
|
||||
<rect key="frame" x="8" y="12" width="304" height="28"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Info Message" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="OVa-Xw-5vl" customClass="JSQMessagesLabel">
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="14" id="fed-2c-dqd"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="OVa-Xw-5vl" firstAttribute="leading" secondItem="qCf-bs-dBd" secondAttribute="leading" constant="8" id="2IE-8k-czI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OVa-Xw-5vl" secondAttribute="bottom" constant="8" id="MtI-jW-t1x"/>
|
||||
|
@ -47,14 +45,12 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="warning_white.png" translatesAutoresizingMaskIntoConstraints="NO" id="ePO-Cy-jUE">
|
||||
<rect key="frame" x="143" y="20" width="35" height="35"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="35" id="Llx-81-oyV"/>
|
||||
<constraint firstAttribute="width" constant="35" id="Nth-3D-Wo9"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstItem="ePO-Cy-jUE" firstAttribute="top" secondItem="gcR-Rk-KDC" secondAttribute="bottom" id="1yH-xH-np2"/>
|
||||
|
@ -73,7 +69,7 @@
|
|||
<outlet property="cellTopLabel" destination="gcR-Rk-KDC" id="Ogk-hD-ge8"/>
|
||||
<outlet property="cellTopLabelHeightConstraint" destination="ckj-xD-FJI" id="wBH-pQ-Wc7"/>
|
||||
<outlet property="headerImageView" destination="ePO-Cy-jUE" id="4uq-2C-V7U"/>
|
||||
<outlet property="textContainer" destination="qCf-bs-dBd" id="fL7-LO-El1"/>
|
||||
<outlet property="messageBubbleContainerView" destination="qCf-bs-dBd" id="WMx-Di-LZG"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="219" y="433"/>
|
||||
</collectionViewCell>
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Created by Michael Kirk on 9/29/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSExpirationTimerView;
|
||||
|
||||
static const CGFloat OWSExpirableMessageViewTimerWidth = 10.0f;
|
||||
|
||||
@protocol OWSExpirableMessageView
|
||||
|
||||
@property (strong, nonatomic, readonly) IBOutlet OWSExpirationTimerView *expirationTimerView;
|
||||
@property (strong, nonatomic, readonly) IBOutlet NSLayoutConstraint *expirationTimerViewWidthConstraint;
|
||||
|
||||
- (void)startExpirationTimerWithExpiresAtSeconds:(uint64_t)expiresAtSeconds
|
||||
initialDurationSeconds:(uint32_t)initialDurationSeconds;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,17 @@
|
|||
// Created by Michael Kirk on 9/29/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSExpirationTimerView : UIView
|
||||
|
||||
- (void)startTimerWithExpiresAtSeconds:(uint64_t)expiresAtSeconds
|
||||
initialDurationSeconds:(uint32_t)initialDurationSeconds;
|
||||
|
||||
- (void)stopBlinking;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,184 @@
|
|||
// Created by Michael Kirk on 9/29/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSExpirationTimerView.h"
|
||||
#import "MessagesViewController.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import <QuartzCore/CAShapeLayer.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSExpirationTimerView ()
|
||||
|
||||
@property (nonatomic) uint32_t initialDurationSeconds;
|
||||
@property (atomic) uint64_t expiresAtSeconds;
|
||||
|
||||
@property (nonatomic, readonly) UIImageView *emptyHourglassImageView;
|
||||
@property (nonatomic, readonly) UIImageView *fullHourglassImageView;
|
||||
@property CGFloat ratioRemaining;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSExpirationTimerView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
_emptyHourglassImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_hourglass_empty"]];
|
||||
_emptyHourglassImageView.tintColor = [UIColor ows_blackColor];
|
||||
[self insertSubview:_emptyHourglassImageView atIndex:0];
|
||||
|
||||
_fullHourglassImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_hourglass_full"]];
|
||||
_fullHourglassImageView.tintColor = [UIColor ows_darkGrayColor];
|
||||
[self insertSubview:_fullHourglassImageView atIndex:1];
|
||||
|
||||
_ratioRemaining = 1.0f;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
_emptyHourglassImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_hourglass_empty"]];
|
||||
_emptyHourglassImageView.tintColor = [UIColor lightGrayColor];
|
||||
[self insertSubview:_emptyHourglassImageView atIndex:1];
|
||||
|
||||
_fullHourglassImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ic_hourglass_full"]];
|
||||
_fullHourglassImageView.tintColor = [UIColor lightGrayColor];
|
||||
[self insertSubview:_fullHourglassImageView atIndex:0];
|
||||
|
||||
_ratioRemaining = 1.0f;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
CGFloat leftMargin = 0.0f;
|
||||
CGFloat padding = 6.0f;
|
||||
CGRect hourglassFrame
|
||||
= CGRectMake(leftMargin, padding / 2, self.frame.size.height - padding, self.frame.size.height - padding);
|
||||
self.emptyHourglassImageView.frame = hourglassFrame;
|
||||
self.emptyHourglassImageView.bounds = hourglassFrame;
|
||||
self.fullHourglassImageView.frame = hourglassFrame;
|
||||
self.fullHourglassImageView.bounds = hourglassFrame;
|
||||
}
|
||||
|
||||
- (void)restartAnimation:(NSNotification *)notification
|
||||
{
|
||||
[self startTimerWithExpiresAtSeconds:self.expiresAtSeconds initialDurationSeconds:self.initialDurationSeconds];
|
||||
}
|
||||
|
||||
- (void)startTimerWithExpiresAtSeconds:(uint64_t)expiresAtSeconds
|
||||
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
||||
{
|
||||
if (expiresAtSeconds == 0) {
|
||||
DDLogWarn(
|
||||
@"%@ Asked to animate expiration for message which hasn't started expiring. intitialDurationSeconds:%u",
|
||||
self.logTag,
|
||||
initialDurationSeconds);
|
||||
}
|
||||
|
||||
DDLogVerbose(@"%@ Starting animation timer with expiresAtSeconds: %llu initialDurationSeconds: %d",
|
||||
self.logTag,
|
||||
expiresAtSeconds,
|
||||
initialDurationSeconds);
|
||||
|
||||
self.expiresAtSeconds = expiresAtSeconds;
|
||||
self.initialDurationSeconds = initialDurationSeconds;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(restartAnimation:)
|
||||
name:OWSMessagesViewControllerDidAppearNotification
|
||||
object:nil];
|
||||
|
||||
double secondsLeft = (double)self.expiresAtSeconds - [NSDate new].timeIntervalSince1970;
|
||||
|
||||
if (secondsLeft < 0) {
|
||||
secondsLeft = 0;
|
||||
}
|
||||
|
||||
// Get hourglass frames to the proper size.
|
||||
[self setNeedsLayout];
|
||||
[self layoutIfNeeded];
|
||||
|
||||
CAGradientLayer *maskLayer = [CAGradientLayer new];
|
||||
self.fullHourglassImageView.layer.mask = maskLayer;
|
||||
|
||||
maskLayer.frame = self.fullHourglassImageView.frame;
|
||||
|
||||
// Blur the top of the mask a bit with gradient
|
||||
maskLayer.colors = @[ (id)[UIColor clearColor].CGColor, (id)[UIColor blackColor].CGColor ];
|
||||
maskLayer.startPoint = CGPointMake(0.5f, 0);
|
||||
maskLayer.endPoint = CGPointMake(0.5f, 0.2f);
|
||||
|
||||
CGFloat ratioRemaining = ((CGFloat)secondsLeft / (CGFloat)self.initialDurationSeconds);
|
||||
if (ratioRemaining < 0) {
|
||||
ratioRemaining = 0.0;
|
||||
}
|
||||
CGPoint defaultPosition = maskLayer.position;
|
||||
CGPoint finalPosition = CGPointMake(defaultPosition.x, defaultPosition.y + maskLayer.bounds.size.height);
|
||||
CGPoint startingPosition
|
||||
= CGPointMake(defaultPosition.x, finalPosition.y - maskLayer.bounds.size.height * ratioRemaining);
|
||||
maskLayer.position = startingPosition;
|
||||
|
||||
CABasicAnimation *revealAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
|
||||
revealAnimation.duration = secondsLeft;
|
||||
revealAnimation.fromValue = [NSValue valueWithCGPoint:startingPosition];
|
||||
revealAnimation.toValue = [NSValue valueWithCGPoint:finalPosition];
|
||||
|
||||
[maskLayer addAnimation:revealAnimation forKey:@"revealAnimation"];
|
||||
maskLayer.position = finalPosition; // don't snap back
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, ((long long)secondsLeft - 2) * (long long)NSEC_PER_SEC),
|
||||
dispatch_get_main_queue(),
|
||||
^{
|
||||
[self startBlinking];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)startBlinking
|
||||
{
|
||||
CABasicAnimation *blinkAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
|
||||
blinkAnimation.duration = 0.5;
|
||||
blinkAnimation.fromValue = @(1.0);
|
||||
blinkAnimation.toValue = @(0.0);
|
||||
blinkAnimation.repeatCount = 4;
|
||||
blinkAnimation.autoreverses = YES;
|
||||
blinkAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
||||
[self.layer addAnimation:blinkAnimation forKey:@"alphaBlink"];
|
||||
}
|
||||
|
||||
- (void)stopBlinking
|
||||
{
|
||||
[self.layer removeAnimationForKey:@"alphaBlink"];
|
||||
self.layer.opacity = 1;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)logTag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)logTag
|
||||
{
|
||||
return self.class.logTag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,13 @@
|
|||
// Created by Michael Kirk on 9/29/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSExpirableMessageView.h"
|
||||
#import <JSQMessagesViewController/JSQMessagesCollectionViewCellIncoming.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSIncomingMessageCollectionViewCell : JSQMessagesCollectionViewCellIncoming <OWSExpirableMessageView>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,43 @@
|
|||
// Created by Michael Kirk on 9/29/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSIncomingMessageCollectionViewCell.h"
|
||||
#import "OWSExpirationTimerView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSIncomingMessageCollectionViewCell ()
|
||||
|
||||
@property (strong, nonatomic) IBOutlet OWSExpirationTimerView *expirationTimerView;
|
||||
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *expirationTimerViewWidthConstraint;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSIncomingMessageCollectionViewCell
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
self.expirationTimerViewWidthConstraint.constant = 0.0;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[self.expirationTimerView stopBlinking];
|
||||
self.expirationTimerViewWidthConstraint.constant = 0.0f;
|
||||
}
|
||||
|
||||
// pragma mark - OWSExpirableMessageView
|
||||
|
||||
- (void)startExpirationTimerWithExpiresAtSeconds:(uint64_t)expiresAtSeconds
|
||||
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
||||
{
|
||||
self.expirationTimerViewWidthConstraint.constant = OWSExpirableMessageViewTimerWidth;
|
||||
[self.expirationTimerView startTimerWithExpiresAtSeconds:expiresAtSeconds
|
||||
initialDurationSeconds:initialDurationSeconds];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,153 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
|
||||
<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"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="" id="4lh-CK-yVn" customClass="OWSIncomingMessageCollectionViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="154"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="154"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cell top label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="afj-rd-iNv" userLabel="Cell top label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="fKS-MR-YPI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="bubble top label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ufa-bF-l1Y" userLabel="Bubble top label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="fal-sy-hrK"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="btS-p8-B7Z" userLabel="Bubble container">
|
||||
<frame key="frameInset" minX="36" minY="40" width="244" height="94"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OCS-Fu-acq" userLabel="Bubble Image View">
|
||||
<frame key="frameInset" width="244" height="94"/>
|
||||
</imageView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KYU-B8-cUW" customClass="JSQMessagesCellTextView">
|
||||
<frame key="frameInset" minX="6" width="238" height="94"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="KYU-B8-cUW" secondAttribute="trailing" id="4qS-03-PFO"/>
|
||||
<constraint firstAttribute="bottom" secondItem="KYU-B8-cUW" secondAttribute="bottom" id="B2v-Gq-Y1L"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OCS-Fu-acq" secondAttribute="trailing" id="TdB-8V-aUc"/>
|
||||
<constraint firstItem="KYU-B8-cUW" firstAttribute="leading" secondItem="btS-p8-B7Z" secondAttribute="leading" constant="6" id="Tg9-9l-vr8"/>
|
||||
<constraint firstItem="KYU-B8-cUW" firstAttribute="top" secondItem="btS-p8-B7Z" secondAttribute="top" id="aEL-yH-N1p"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OCS-Fu-acq" secondAttribute="bottom" id="aJ4-ZD-tk9"/>
|
||||
<constraint firstItem="OCS-Fu-acq" firstAttribute="leading" secondItem="btS-p8-B7Z" secondAttribute="leading" id="qpQ-dc-2V5"/>
|
||||
<constraint firstAttribute="width" constant="244" id="stE-iz-VHo"/>
|
||||
<constraint firstItem="OCS-Fu-acq" firstAttribute="top" secondItem="btS-p8-B7Z" secondAttribute="top" id="zTa-8g-VY4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Da1-09-wR4" userLabel="Avatar container">
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="w23-sL-0Rh" userLabel="Avatar Image View"/>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="w23-sL-0Rh" firstAttribute="leading" secondItem="Da1-09-wR4" secondAttribute="leading" id="6zm-sH-cOT"/>
|
||||
<constraint firstAttribute="trailing" secondItem="w23-sL-0Rh" secondAttribute="trailing" id="JgK-7W-L10"/>
|
||||
<constraint firstAttribute="bottom" secondItem="w23-sL-0Rh" secondAttribute="bottom" id="U39-Ze-JZ6"/>
|
||||
<constraint firstAttribute="width" constant="34" id="YwX-fW-Me6"/>
|
||||
<constraint firstItem="w23-sL-0Rh" firstAttribute="top" secondItem="Da1-09-wR4" secondAttribute="top" id="hxb-KR-hNK"/>
|
||||
<constraint firstAttribute="height" constant="34" id="tZk-AK-JFa"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0eu-Z9-ndP" userLabel="Footer Container">
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cell bottom label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UPz-5x-c1T" userLabel="Cell bottom label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="xPR-Ph-Ze9"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UYa-ee-mie" userLabel="Expiration Timer" customClass="OWSExpirationTimerView">
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="10" id="Spx-mv-2DX"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="UPz-5x-c1T" secondAttribute="bottom" id="3hV-mq-Bcf"/>
|
||||
<constraint firstItem="UPz-5x-c1T" firstAttribute="top" secondItem="0eu-Z9-ndP" secondAttribute="top" id="7v5-pU-s1n"/>
|
||||
<constraint firstAttribute="trailing" secondItem="UPz-5x-c1T" secondAttribute="trailing" id="TlZ-tg-six"/>
|
||||
<constraint firstItem="UPz-5x-c1T" firstAttribute="leading" secondItem="UYa-ee-mie" secondAttribute="trailing" id="WVv-6q-I5l"/>
|
||||
<constraint firstAttribute="bottom" secondItem="UYa-ee-mie" secondAttribute="bottom" id="fVy-Bf-ePa"/>
|
||||
<constraint firstItem="UYa-ee-mie" firstAttribute="top" secondItem="0eu-Z9-ndP" secondAttribute="top" id="iHb-8v-vc4"/>
|
||||
<constraint firstItem="UYa-ee-mie" firstAttribute="leading" secondItem="0eu-Z9-ndP" secondAttribute="leading" id="p2g-wW-Ra6"/>
|
||||
<constraint firstAttribute="height" secondItem="UPz-5x-c1T" secondAttribute="height" id="vbI-L6-mkn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="0eu-Z9-ndP" secondAttribute="trailing" id="JeQ-cB-BZe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="afj-rd-iNv" secondAttribute="trailing" id="Ka4-Dy-jCS"/>
|
||||
<constraint firstItem="0eu-Z9-ndP" firstAttribute="top" secondItem="Da1-09-wR4" secondAttribute="bottom" id="Mxx-Zi-4pN"/>
|
||||
<constraint firstItem="afj-rd-iNv" firstAttribute="leading" secondItem="4lh-CK-yVn" secondAttribute="leading" id="OnD-mZ-QtR"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0eu-Z9-ndP" secondAttribute="bottom" id="VPM-Vk-cDj"/>
|
||||
<constraint firstItem="Ufa-bF-l1Y" firstAttribute="leading" secondItem="4lh-CK-yVn" secondAttribute="leading" id="Z5z-m3-8Ne"/>
|
||||
<constraint firstItem="afj-rd-iNv" firstAttribute="top" secondItem="4lh-CK-yVn" secondAttribute="top" id="ZG9-2M-N52"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ufa-bF-l1Y" secondAttribute="trailing" id="cWQ-1Q-xOA"/>
|
||||
<constraint firstItem="Ufa-bF-l1Y" firstAttribute="top" secondItem="afj-rd-iNv" secondAttribute="bottom" id="i9Y-sV-v6b"/>
|
||||
<constraint firstItem="btS-p8-B7Z" firstAttribute="leading" secondItem="Da1-09-wR4" secondAttribute="trailing" constant="2" id="j0I-pm-eex"/>
|
||||
<constraint firstItem="btS-p8-B7Z" firstAttribute="top" secondItem="Ufa-bF-l1Y" secondAttribute="bottom" id="jAu-Dn-7rN"/>
|
||||
<constraint firstItem="Da1-09-wR4" firstAttribute="leading" secondItem="4lh-CK-yVn" secondAttribute="leading" id="jiu-B4-TSD"/>
|
||||
<constraint firstItem="0eu-Z9-ndP" firstAttribute="leading" secondItem="4lh-CK-yVn" secondAttribute="leading" constant="10" id="kwA-RP-CSv"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="317" height="245"/>
|
||||
<connections>
|
||||
<outlet property="avatarContainerView" destination="Da1-09-wR4" id="tpM-P4-qUR"/>
|
||||
<outlet property="avatarContainerViewHeightConstraint" destination="tZk-AK-JFa" id="tPu-mC-ITh"/>
|
||||
<outlet property="avatarContainerViewWidthConstraint" destination="YwX-fW-Me6" id="5PN-NL-hEP"/>
|
||||
<outlet property="avatarImageView" destination="w23-sL-0Rh" id="Yf4-UR-VRC"/>
|
||||
<outlet property="cellBottomLabel" destination="UPz-5x-c1T" id="MKm-hp-T6v"/>
|
||||
<outlet property="cellBottomLabelHeightConstraint" destination="xPR-Ph-Ze9" id="nvV-Tk-AIs"/>
|
||||
<outlet property="cellTopLabel" destination="afj-rd-iNv" id="bTd-4q-U7e"/>
|
||||
<outlet property="cellTopLabelHeightConstraint" destination="fKS-MR-YPI" id="YWd-Rd-qSL"/>
|
||||
<outlet property="expirationTimerView" destination="UYa-ee-mie" id="y5V-L9-vWJ"/>
|
||||
<outlet property="expirationTimerViewWidthConstraint" destination="Spx-mv-2DX" id="ebW-8s-AfN"/>
|
||||
<outlet property="messageBubbleContainerView" destination="btS-p8-B7Z" id="2sk-5p-NEd"/>
|
||||
<outlet property="messageBubbleContainerWidthConstraint" destination="stE-iz-VHo" id="lle-iT-67d"/>
|
||||
<outlet property="messageBubbleImageView" destination="OCS-Fu-acq" id="OuN-5t-30g"/>
|
||||
<outlet property="messageBubbleTopLabel" destination="Ufa-bF-l1Y" id="VtH-te-blR"/>
|
||||
<outlet property="messageBubbleTopLabelHeightConstraint" destination="fal-sy-hrK" id="kgv-NO-Gud"/>
|
||||
<outlet property="textView" destination="KYU-B8-cUW" id="1Yv-ln-EUZ"/>
|
||||
<outlet property="textViewAvatarHorizontalSpaceConstraint" destination="Tg9-9l-vr8" id="HWn-aO-NbR"/>
|
||||
<outlet property="textViewBottomVerticalSpaceConstraint" destination="B2v-Gq-Y1L" id="oKV-Ti-Oci"/>
|
||||
<outlet property="textViewMarginHorizontalSpaceConstraint" destination="4qS-03-PFO" id="1Qe-Ee-fUO"/>
|
||||
<outlet property="textViewTopVerticalSpaceConstraint" destination="aEL-yH-N1p" id="WPl-0b-tf1"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="255" y="202"/>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
|
@ -0,0 +1,13 @@
|
|||
// Created by Michael Kirk on 9/28/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSExpirableMessageView.h"
|
||||
#import <JSQMessagesViewController/JSQMessagesCollectionViewCellOutgoing.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSOutgoingMessageCollectionViewCell : JSQMessagesCollectionViewCellOutgoing <OWSExpirableMessageView>
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,43 @@
|
|||
// Created by Michael Kirk on 9/28/16.
|
||||
// Copyright © 2016 Open Whisper Systems. All rights reserved.
|
||||
|
||||
#import "OWSOutgoingMessageCollectionViewCell.h"
|
||||
#import "OWSExpirationTimerView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSOutgoingMessageCollectionViewCell ()
|
||||
|
||||
@property (strong, nonatomic) IBOutlet OWSExpirationTimerView *expirationTimerView;
|
||||
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *expirationTimerViewWidthConstraint;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OWSOutgoingMessageCollectionViewCell
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
self.expirationTimerViewWidthConstraint.constant = 0.0;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[self.expirationTimerView stopBlinking];
|
||||
self.expirationTimerViewWidthConstraint.constant = 0.0f;
|
||||
}
|
||||
|
||||
// pragma mark - OWSExpirableMessageView
|
||||
|
||||
- (void)startExpirationTimerWithExpiresAtSeconds:(uint64_t)expiresAtSeconds
|
||||
initialDurationSeconds:(uint32_t)initialDurationSeconds
|
||||
{
|
||||
self.expirationTimerViewWidthConstraint.constant = OWSExpirableMessageViewTimerWidth;
|
||||
[self.expirationTimerView startTimerWithExpiresAtSeconds:expiresAtSeconds
|
||||
initialDurationSeconds:initialDurationSeconds];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,150 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
|
||||
<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"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="" id="23f-xH-rkY" customClass="OWSOutgoingMessageCollectionViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="154"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="154"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cell top label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jxM-YD-sVG" userLabel="Cell top label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="9oK-E7-iXA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="bubble top label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p52-YN-yLu" userLabel="Bubble top label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="8TB-va-f8L"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="2zh-vR-QJW" userLabel="Bubble container">
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="2qm-c6-OZf" userLabel="Bubble Image View"/>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vLY-aM-0Dr" customClass="JSQMessagesCellTextView">
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="vLY-aM-0Dr" firstAttribute="leading" secondItem="2zh-vR-QJW" secondAttribute="leading" id="7rI-Nc-AK3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2qm-c6-OZf" secondAttribute="trailing" id="AEu-1l-tqh"/>
|
||||
<constraint firstItem="2qm-c6-OZf" firstAttribute="top" secondItem="2zh-vR-QJW" secondAttribute="top" id="DbW-Cx-zOW"/>
|
||||
<constraint firstItem="2qm-c6-OZf" firstAttribute="leading" secondItem="2zh-vR-QJW" secondAttribute="leading" id="H1H-yn-Raq"/>
|
||||
<constraint firstItem="vLY-aM-0Dr" firstAttribute="top" secondItem="2zh-vR-QJW" secondAttribute="top" id="RiG-21-Bqc"/>
|
||||
<constraint firstAttribute="bottom" secondItem="vLY-aM-0Dr" secondAttribute="bottom" id="UbF-Bl-Q7v"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vLY-aM-0Dr" secondAttribute="trailing" constant="6" id="aVg-yy-8K7"/>
|
||||
<constraint firstAttribute="width" constant="244" id="imD-52-K45"/>
|
||||
<constraint firstAttribute="bottom" secondItem="2qm-c6-OZf" secondAttribute="bottom" id="lts-Ve-wSh"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="X89-B1-aAd" userLabel="Avatar container">
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="y9b-D9-Q7W" userLabel="Avatar Image View"/>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="y9b-D9-Q7W" secondAttribute="bottom" id="7SX-4t-GAr"/>
|
||||
<constraint firstAttribute="width" constant="34" id="Pkm-tW-k4z"/>
|
||||
<constraint firstItem="y9b-D9-Q7W" firstAttribute="leading" secondItem="X89-B1-aAd" secondAttribute="leading" id="Pya-tL-FjE"/>
|
||||
<constraint firstItem="y9b-D9-Q7W" firstAttribute="top" secondItem="X89-B1-aAd" secondAttribute="top" id="e5w-hn-mre"/>
|
||||
<constraint firstAttribute="height" constant="34" id="tgw-aN-JJu"/>
|
||||
<constraint firstAttribute="trailing" secondItem="y9b-D9-Q7W" secondAttribute="trailing" id="w9X-3u-BNY"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wFK-dt-TWZ" userLabel="Footer Container">
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="cell bottom label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7Uc-dI-YDD" userLabel="Cell bottom label" customClass="JSQMessagesLabel">
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="cPs-M4-tjX"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0tt-tR-3Iu" userLabel="Expiration Timer" customClass="OWSExpirationTimerView">
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="10" id="5XO-l6-PgT"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstItem="0tt-tR-3Iu" firstAttribute="leading" secondItem="7Uc-dI-YDD" secondAttribute="trailing" id="3rd-VE-2kc"/>
|
||||
<constraint firstItem="7Uc-dI-YDD" firstAttribute="leading" secondItem="wFK-dt-TWZ" secondAttribute="leading" id="8bH-5V-WeT"/>
|
||||
<constraint firstItem="7Uc-dI-YDD" firstAttribute="top" secondItem="wFK-dt-TWZ" secondAttribute="top" id="HU0-Ql-nzh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="7Uc-dI-YDD" secondAttribute="bottom" id="aG2-Au-de4"/>
|
||||
<constraint firstItem="0tt-tR-3Iu" firstAttribute="top" secondItem="wFK-dt-TWZ" secondAttribute="top" id="cWj-FU-zAs"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0tt-tR-3Iu" secondAttribute="bottom" id="d3u-eO-0Vh"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0tt-tR-3Iu" secondAttribute="trailing" id="ikV-vO-dvu"/>
|
||||
<constraint firstItem="7Uc-dI-YDD" firstAttribute="height" secondItem="wFK-dt-TWZ" secondAttribute="height" id="tBD-NV-24p"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstItem="wFK-dt-TWZ" firstAttribute="leading" secondItem="23f-xH-rkY" secondAttribute="leading" id="3WR-qA-BlN"/>
|
||||
<constraint firstItem="2zh-vR-QJW" firstAttribute="top" secondItem="p52-YN-yLu" secondAttribute="bottom" id="3Wx-g0-fTc"/>
|
||||
<constraint firstAttribute="trailing" secondItem="jxM-YD-sVG" secondAttribute="trailing" id="AwY-g7-f1T"/>
|
||||
<constraint firstItem="wFK-dt-TWZ" firstAttribute="top" secondItem="2zh-vR-QJW" secondAttribute="bottom" id="Ckg-Bj-HXn"/>
|
||||
<constraint firstItem="jxM-YD-sVG" firstAttribute="top" secondItem="23f-xH-rkY" secondAttribute="top" id="HYT-Tw-whz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="X89-B1-aAd" secondAttribute="trailing" id="KLt-Ix-wDa"/>
|
||||
<constraint firstAttribute="bottom" secondItem="wFK-dt-TWZ" secondAttribute="bottom" id="g9n-w3-IXp"/>
|
||||
<constraint firstAttribute="trailing" secondItem="p52-YN-yLu" secondAttribute="trailing" id="gen-N0-uZj"/>
|
||||
<constraint firstItem="wFK-dt-TWZ" firstAttribute="top" secondItem="X89-B1-aAd" secondAttribute="bottom" id="hNe-hv-ytt"/>
|
||||
<constraint firstItem="p52-YN-yLu" firstAttribute="top" secondItem="jxM-YD-sVG" secondAttribute="bottom" id="jBD-JV-AWk"/>
|
||||
<constraint firstItem="jxM-YD-sVG" firstAttribute="leading" secondItem="23f-xH-rkY" secondAttribute="leading" id="qeB-G5-1Wq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wFK-dt-TWZ" secondAttribute="trailing" constant="10" id="r4Q-Yj-BY6"/>
|
||||
<constraint firstItem="p52-YN-yLu" firstAttribute="leading" secondItem="23f-xH-rkY" secondAttribute="leading" id="tTj-Mp-0va"/>
|
||||
<constraint firstItem="X89-B1-aAd" firstAttribute="leading" secondItem="2zh-vR-QJW" secondAttribute="trailing" constant="2" id="vMz-Yi-B0w"/>
|
||||
</constraints>
|
||||
<size key="customSize" width="317" height="245"/>
|
||||
<connections>
|
||||
<outlet property="avatarContainerView" destination="X89-B1-aAd" id="WSI-Zc-qIE"/>
|
||||
<outlet property="avatarContainerViewHeightConstraint" destination="tgw-aN-JJu" id="pgV-tY-5Cm"/>
|
||||
<outlet property="avatarContainerViewWidthConstraint" destination="Pkm-tW-k4z" id="Cpe-d3-yiq"/>
|
||||
<outlet property="avatarImageView" destination="y9b-D9-Q7W" id="cZo-SR-S9h"/>
|
||||
<outlet property="cellBottomLabel" destination="7Uc-dI-YDD" id="gVD-C2-UcZ"/>
|
||||
<outlet property="cellBottomLabelHeightConstraint" destination="cPs-M4-tjX" id="b5k-6e-iA8"/>
|
||||
<outlet property="cellTopLabel" destination="jxM-YD-sVG" id="acH-pr-spx"/>
|
||||
<outlet property="cellTopLabelHeightConstraint" destination="9oK-E7-iXA" id="MZM-kV-2dI"/>
|
||||
<outlet property="expirationTimerView" destination="0tt-tR-3Iu" id="1Yp-Fs-DUh"/>
|
||||
<outlet property="expirationTimerViewWidthConstraint" destination="5XO-l6-PgT" id="6md-KV-QkY"/>
|
||||
<outlet property="messageBubbleContainerView" destination="2zh-vR-QJW" id="pu0-GU-eZl"/>
|
||||
<outlet property="messageBubbleContainerWidthConstraint" destination="imD-52-K45" id="Xld-Pa-yJw"/>
|
||||
<outlet property="messageBubbleImageView" destination="2qm-c6-OZf" id="bpy-Gv-jSh"/>
|
||||
<outlet property="messageBubbleTopLabel" destination="p52-YN-yLu" id="SLH-sA-Chu"/>
|
||||
<outlet property="messageBubbleTopLabelHeightConstraint" destination="8TB-va-f8L" id="FNt-BS-Wxi"/>
|
||||
<outlet property="textView" destination="vLY-aM-0Dr" id="YEp-mW-xIY"/>
|
||||
<outlet property="textViewAvatarHorizontalSpaceConstraint" destination="aVg-yy-8K7" id="CIe-Bi-eng"/>
|
||||
<outlet property="textViewBottomVerticalSpaceConstraint" destination="UbF-Bl-Q7v" id="KHP-49-3u4"/>
|
||||
<outlet property="textViewMarginHorizontalSpaceConstraint" destination="7rI-Nc-AK3" id="ciu-j6-IpH"/>
|
||||
<outlet property="textViewTopVerticalSpaceConstraint" destination="RiG-21-Bqc" id="i3j-z0-feE"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="371" y="145"/>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4_7.fullscreen"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
function usage() {
|
||||
cat <<EOS
|
||||
Extracts string from target file and appends to Localizable.strings
|
||||
|
||||
$0 <filename.m>
|
||||
|
||||
e.g.
|
||||
|
||||
$0 path/to/my/ClassName.m
|
||||
EOS
|
||||
}
|
||||
|
||||
BIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd $BIN_DIR/..
|
||||
|
||||
TARGET=$1
|
||||
if [[ -z $TARGET ]]
|
||||
then
|
||||
echo "Can't proceed without target"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -e $TARGET ]]
|
||||
then
|
||||
echo "No file exists at ${TARGET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# pwd.
|
||||
OUTPUT_DIR=en.lproj/
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
genstrings -a -o "${OUTPUT_DIR}" "${TARGET}"
|
|
@ -1,27 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
function usage() {
|
||||
cat <<EOS
|
||||
Extracts string from target file to new.strings
|
||||
|
||||
$0 <filename.m>
|
||||
|
||||
e.g.
|
||||
|
||||
$0 path/to/my/ClassName.m
|
||||
EOS
|
||||
}
|
||||
|
||||
TARGET=$1
|
||||
if [[ -z $TARGET ]]
|
||||
then
|
||||
echo "Can't proceed without target"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUTPUT_DIR=new_strings
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
genstrings -o "${OUTPUT_DIR}" "${TARGET}"
|