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
This commit is contained in:
Michael Kirk 2016-09-21 08:37:51 -04:00
parent 48336b6c53
commit ee0cce75e8
117 changed files with 2710 additions and 1079 deletions

View File

@ -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

View File

@ -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"

View File

@ -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'

View File

@ -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

View File

@ -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;

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 B

View File

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -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>

View File

@ -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];

View File

@ -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

View File

@ -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

View File

@ -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
*/

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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];

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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}"

View File

@ -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}"

Some files were not shown because too many files have changed in this diff Show More