Merge remote-tracking branch 'origin/develop'

This commit is contained in:
kevinzhow
2016-02-02 01:07:17 +08:00
93 changed files with 3683 additions and 1449 deletions
+2 -2
View File
@@ -5,7 +5,7 @@ use_frameworks!
def pods
pod 'Kanna'
pod 'Navi'
# pod 'Appsee'
pod 'Appsee'
pod 'Alamofire'
pod 'DeviceGuru'
pod '1PasswordExtension', '~> 1.5'
@@ -22,7 +22,7 @@ def pods
pod 'SocketRocket'
pod 'RealmSwift'
pod 'MonkeyKing', '0.0.2'
pod 'JPush-iOS-SDK'
pod 'JPush'
pod 'Fabric'
pod 'Crashlytics'
end
+244 -64
View File
@@ -38,7 +38,6 @@
0AD7EA681AC3ED3600617758 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA671AC3ED3600617758 /* Foundation.framework */; };
0AD7EA6A1AC3ED3D00617758 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA691AC3ED3D00617758 /* UIKit.framework */; };
0AD7EA6C1AC3ED4300617758 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AD7EA6B1AC3ED4300617758 /* Security.framework */; };
0AD7EA771AC3EEB000617758 /* PushConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = 0AD7EA761AC3EEB000617758 /* PushConfig.plist */; };
0AEB1CC61B0E742400178C9C /* Double+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AEB1CC51B0E742400178C9C /* Double+Yep.swift */; };
5008A1931B8DB8B500E94274 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5008A1951B8DB8B500E94274 /* Localizable.strings */; };
500A0B1C1B450C2100D49358 /* EditNicknameAndBadgeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500A0B1B1B450C2100D49358 /* EditNicknameAndBadgeViewController.swift */; };
@@ -50,7 +49,6 @@
500EED9A1BF1B81000C6BABC /* MediaPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500EED991BF1B81000C6BABC /* MediaPreviewViewController.swift */; };
500EED9D1BF1B84A00C6BABC /* MediaPreview.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 500EED9C1BF1B84A00C6BABC /* MediaPreview.storyboard */; };
501138261BCB59120055A014 /* FeedConversationsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501138251BCB59120055A014 /* FeedConversationsViewController.swift */; };
501138291BCB593F0055A014 /* FeedConversations.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 501138281BCB593F0055A014 /* FeedConversations.storyboard */; };
5011382C1BCB5E150055A014 /* FeedConversationDockCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5011382A1BCB5E150055A014 /* FeedConversationDockCell.swift */; };
5011382D1BCB5E150055A014 /* FeedConversationDockCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5011382B1BCB5E150055A014 /* FeedConversationDockCell.xib */; };
501138311BCB62A20055A014 /* FeedConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5011382F1BCB62A20055A014 /* FeedConversationCell.swift */; };
@@ -113,9 +111,7 @@
502B606E1BD6567600D9C17E /* AvatarStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502B606D1BD6567600D9C17E /* AvatarStyles.swift */; };
502C24831C1034000074A1BA /* UIImageView+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 502C24821C1034000074A1BA /* UIImageView+Yep.swift */; };
503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503315951AC1017E0008A209 /* ChatLeftTextCell.swift */; };
503315981AC1017E0008A209 /* ChatLeftTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 503315961AC1017E0008A209 /* ChatLeftTextCell.xib */; };
5033159C1AC101C80008A209 /* ChatRightTextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5033159A1AC101C80008A209 /* ChatRightTextCell.swift */; };
5033159D1AC101C80008A209 /* ChatRightTextCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5033159B1AC101C80008A209 /* ChatRightTextCell.xib */; };
503315A01AC126100008A209 /* MessageToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5033159F1AC126100008A209 /* MessageToolbar.swift */; };
503807201BD9EEFC0068345E /* YepMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5038071F1BD9EEFC0068345E /* YepMedia.swift */; };
503807231BDA18040068345E /* FeedSkillUsersCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503807211BDA18040068345E /* FeedSkillUsersCell.swift */; };
@@ -126,6 +122,7 @@
5039594F1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5039594D1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib */; };
503CAB431AC00CB100DFE830 /* ConversationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503CAB421AC00CB100DFE830 /* ConversationViewController.swift */; };
503ED23A1B8ADCE70072187D /* BlackListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503ED2391B8ADCE70072187D /* BlackListViewController.swift */; };
50485BCE1C56062400431AC7 /* ChatLeftRecallCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50485BCD1C56062400431AC7 /* ChatLeftRecallCell.swift */; };
504BDC151B4528A500DBD35D /* Badges.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504BDC141B4528A500DBD35D /* Badges.xcassets */; };
504CBA3D1B3D04B800663A90 /* ChatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504CBA3C1B3D04B800663A90 /* ChatTextView.swift */; };
504F41251ACE2D1D00FBB19A /* SampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504F41241ACE2D1D00FBB19A /* SampleView.swift */; };
@@ -137,6 +134,11 @@
505082331C21130100643CEB /* FeedGithubRepoContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */; };
505082351C212E9200643CEB /* FeedLocationContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082341C212E9200643CEB /* FeedLocationContainerView.swift */; };
505082371C21377E00643CEB /* FeedVoiceContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505082361C21377E00643CEB /* FeedVoiceContainerView.swift */; };
5052620B1C51CDE600292C6A /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AC40E321C47A8FE0024FC63 /* libz.tbd */; };
505262101C52050700292C6A /* ConcurrentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5052620F1C52050700292C6A /* ConcurrentOperation.swift */; };
505262121C5205AB00292C6A /* UploadAttachmentOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505262111C5205AB00292C6A /* UploadAttachmentOperation.swift */; };
505262151C522A1E00292C6A /* Show.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 505262131C522A1E00292C6A /* Show.storyboard */; };
505262171C522A3D00292C6A /* FeedConversations.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 505262161C522A3D00292C6A /* FeedConversations.storyboard */; };
50535D1A1BCD19AE002FE1A4 /* TouchClosuresView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50535D191BCD19AE002FE1A4 /* TouchClosuresView.swift */; };
50535D1D1BCE42BE002FE1A4 /* PickPhotosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50535D1C1BCE42BE002FE1A4 /* PickPhotosViewController.swift */; };
50535D201BCE43ED002FE1A4 /* ImageCacheController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50535D1F1BCE43ED002FE1A4 /* ImageCacheController.swift */; };
@@ -148,9 +150,7 @@
5053AD501AF82A1700B3CBFA /* PickLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053AD4E1AF82A1700B3CBFA /* PickLocationCell.swift */; };
5053AD511AF82A1700B3CBFA /* PickLocationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5053AD4F1AF82A1700B3CBFA /* PickLocationCell.xib */; };
5053AD551AF83B0F00B3CBFA /* ChatRightLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053AD531AF83B0F00B3CBFA /* ChatRightLocationCell.swift */; };
5053AD561AF83B0F00B3CBFA /* ChatRightLocationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5053AD541AF83B0F00B3CBFA /* ChatRightLocationCell.xib */; };
5053AD5A1AF83B4200B3CBFA /* ChatLeftLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053AD581AF83B4200B3CBFA /* ChatLeftLocationCell.swift */; };
5053AD5B1AF83B4200B3CBFA /* ChatLeftLocationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5053AD591AF83B4200B3CBFA /* ChatLeftLocationCell.xib */; };
505498621B12FB0E0037E3BD /* ConversationMessagePreviewNavigationControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505498611B12FB0E0037E3BD /* ConversationMessagePreviewNavigationControllerDelegate.swift */; };
50553F8F1ADB8BA200F80B59 /* ChatSectionDateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50553F8D1ADB8BA200F80B59 /* ChatSectionDateCell.swift */; };
50553F901ADB8BA200F80B59 /* ChatSectionDateCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50553F8E1ADB8BA200F80B59 /* ChatSectionDateCell.xib */; };
@@ -207,9 +207,7 @@
508D17401ACCD2740092D666 /* YepButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508D173F1ACCD2740092D666 /* YepButton.swift */; };
508D17441ACCDC9F0092D666 /* VoiceRecordButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508D17431ACCDC9F0092D666 /* VoiceRecordButton.swift */; };
508D17471ACCF4710092D666 /* ChatLeftAudioCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508D17451ACCF4710092D666 /* ChatLeftAudioCell.swift */; };
508D17481ACCF4710092D666 /* ChatLeftAudioCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 508D17461ACCF4710092D666 /* ChatLeftAudioCell.xib */; };
508D174C1ACCF49B0092D666 /* ChatRightAudioCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508D174A1ACCF49B0092D666 /* ChatRightAudioCell.swift */; };
508D174D1ACCF49B0092D666 /* ChatRightAudioCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 508D174B1ACCF49B0092D666 /* ChatRightAudioCell.xib */; };
508DF62B1B9985F300B194A4 /* YepLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508DF62A1B9985F300B194A4 /* YepLog.swift */; };
508F8FC81B0161EF00461B0B /* ProfileSeparationLineCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508F8FC61B0161EF00461B0B /* ProfileSeparationLineCell.swift */; };
508F8FC91B0161EF00461B0B /* ProfileSeparationLineCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 508F8FC71B0161EF00461B0B /* ProfileSeparationLineCell.xib */; };
@@ -248,6 +246,7 @@
50AFBF4B1BB2A44C007D19DA /* InfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF4A1BB2A44C007D19DA /* InfoView.swift */; };
50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */ = {isa = PBXBuildFile; fileRef = 50AFBF4D1BB3A594007D19DA /* bub3.caf */; };
50AFBF5F1BB4F332007D19DA /* FeedsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */; };
50B22E461C588B6200219625 /* CGImage+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B22E451C588B6200219625 /* CGImage+Yep.swift */; };
50B2CA921BD0BF3800668AC6 /* FeedTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */; };
50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */; };
50B38FB71B69CD960064C132 /* Array+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B38FB61B69CD960064C132 /* Array+Yep.swift */; };
@@ -262,12 +261,14 @@
50CC4FA21C1AC0EE00B6A263 /* UITableView+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CC4FA11C1AC0EE00B6A263 /* UITableView+Yep.swift */; };
50CC4FA41C1AC58700B6A263 /* UICollectionView+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CC4FA31C1AC58700B6A263 /* UICollectionView+Yep.swift */; };
50CDBE421ACA9B4200459CE0 /* ChatRightImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE401ACA9B4200459CE0 /* ChatRightImageCell.swift */; };
50CDBE431ACA9B4200459CE0 /* ChatRightImageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50CDBE411ACA9B4200459CE0 /* ChatRightImageCell.xib */; };
50CDBE461ACA9F2200459CE0 /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE451ACA9F2200459CE0 /* ImageCache.swift */; };
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */; };
50CDBE4A1ACBAD3200459CE0 /* ChatLeftImageCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50CDBE481ACBAD3200459CE0 /* ChatLeftImageCell.xib */; };
50D45FC31C44D2300044C95A /* OpenGraphService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D45FC21C44D2300044C95A /* OpenGraphService.swift */; };
50D4D0DB1AFA825B005AEB6F /* MediaControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */; };
50DBDEF11C48C2240044E21B /* OpenGraphInfoType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */; };
50DBDEFD1C4CCB030044E21B /* ChatLeftTextURLCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEFB1C4CCB030044E21B /* ChatLeftTextURLCell.swift */; };
50DBDF011C4CE75E0044E21B /* ChatRightTextURLCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDEFF1C4CE75E0044E21B /* ChatRightTextURLCell.swift */; };
50DBDF051C4DD9530044E21B /* CGSize+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DBDF041C4DD9530044E21B /* CGSize+Yep.swift */; };
50DEE02C1BE209FF00015741 /* DeletedFeedConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DEE02A1BE209FF00015741 /* DeletedFeedConversationCell.swift */; };
50DEE02D1BE209FF00015741 /* DeletedFeedConversationCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50DEE02B1BE209FF00015741 /* DeletedFeedConversationCell.xib */; };
50DEE0311BE339B000015741 /* LoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50DEE02F1BE339B000015741 /* LoadMoreTableViewCell.swift */; };
@@ -315,9 +316,7 @@
50E61CEB1AF1F69D00908A9D /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E61CEA1AF1F69D00908A9D /* ConversationTitleView.swift */; };
50EB8C5E1AE78AB9001AC1EE /* YepAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */; };
50EB8CBF1AE89ED8001AC1EE /* ChatLeftVideoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CBD1AE89ED8001AC1EE /* ChatLeftVideoCell.swift */; };
50EB8CC01AE89ED8001AC1EE /* ChatLeftVideoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50EB8CBE1AE89ED8001AC1EE /* ChatLeftVideoCell.xib */; };
50EB8CC41AE89F12001AC1EE /* ChatRightVideoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CC21AE89F12001AC1EE /* ChatRightVideoCell.swift */; };
50EB8CC51AE89F12001AC1EE /* ChatRightVideoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50EB8CC31AE89F12001AC1EE /* ChatRightVideoCell.xib */; };
50EB8CCB1AEA1AC0001AC1EE /* MediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CCA1AEA1AC0001AC1EE /* MediaView.swift */; };
50EB8CCE1AEA1B38001AC1EE /* MessageMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */; };
50F2B9781B2EDF3D00F840CC /* BorderTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */; };
@@ -335,6 +334,8 @@
50FD753D1C1FB4AC0050A382 /* FPSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FD753C1C1FB4AC0050A382 /* FPSLabel.swift */; };
50FD753F1C20FF8D0050A382 /* FeedMediaContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FD753E1C20FF8D0050A382 /* FeedMediaContainerView.swift */; };
50FD75441C21097A0050A382 /* LinkContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FD75431C21097A0050A382 /* LinkContainerView.swift */; };
50FE29CD1C4786AE0067CA03 /* FeedURLCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FE29CC1C4786AE0067CA03 /* FeedURLCell.swift */; };
50FE29CF1C478A4A0067CA03 /* FeedURLContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FE29CE1C478A4A0067CA03 /* FeedURLContainerView.swift */; };
6A56328E1BC90A4500397B53 /* DiscoverCardUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A56328C1BC90A4500397B53 /* DiscoverCardUserCell.swift */; };
6A56328F1BC90A4500397B53 /* DiscoverCardUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A56328D1BC90A4500397B53 /* DiscoverCardUserCell.xib */; };
6A5632931BC911CB00397B53 /* DiscoverNormalUserCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A5632911BC911CB00397B53 /* DiscoverNormalUserCell.swift */; };
@@ -345,6 +346,8 @@
6A8215081BCBE8F10011F36C /* DiscoverSkillCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A8215061BCBE8F10011F36C /* DiscoverSkillCell.swift */; };
6A8215091BCBE8F10011F36C /* DiscoverSkillCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6A8215071BCBE8F10011F36C /* DiscoverSkillCell.xib */; };
6AB0DF5A1BE169000052BED1 /* ShareProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AB0DF591BE169000052BED1 /* ShareProfileView.swift */; };
6AC40E311C47A8EE0024FC63 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AC40E301C47A8EE0024FC63 /* CoreData.framework */; };
6AC40E351C47A9060024FC63 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AC40E341C47A9060024FC63 /* libsqlite3.tbd */; };
6ADDB4501B848C600012877A /* 1Password.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6ADDB44F1B848C600012877A /* 1Password.xcassets */; };
8409137D1B7DB173001C89D6 /* FriendRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8409137C1B7DB173001C89D6 /* FriendRequestView.swift */; };
840CD23C1B7B59D5000D2680 /* ShowStepViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840CD23B1B7B59D5000D2680 /* ShowStepViewController.swift */; };
@@ -355,7 +358,6 @@
845E1A661B788EF3001E0789 /* EditSkillsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845E1A651B788EF3001E0789 /* EditSkillsViewController.swift */; };
848806B01B789A2D000C5424 /* EditSkillCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848806AE1B789A2D000C5424 /* EditSkillCell.swift */; };
848806B11B789A2D000C5424 /* EditSkillCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 848806AF1B789A2D000C5424 /* EditSkillCell.xib */; };
848806B41B7B4E2E000C5424 /* Show.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 848806B31B7B4E2E000C5424 /* Show.storyboard */; };
848806B61B7B4F53000C5424 /* ShowViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848806B51B7B4F53000C5424 /* ShowViewController.swift */; };
84C959891B731FD20017D3ED /* ChatBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C959881B731FD20017D3ED /* ChatBaseCell.swift */; };
92E0FFE71C5F38E400226B96 /* MZFayeClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 92E0FFE41C5F38E400226B96 /* MZFayeClient.m */; };
@@ -410,7 +412,6 @@
0AD7EA671AC3ED3600617758 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
0AD7EA691AC3ED3D00617758 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
0AD7EA6B1AC3ED4300617758 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
0AD7EA761AC3EEB000617758 /* PushConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PushConfig.plist; path = JPush/PushConfig.plist; sourceTree = "<group>"; };
0AEB1CC51B0E742400178C9C /* Double+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Double+Yep.swift"; path = "Extensions/Double+Yep.swift"; sourceTree = "<group>"; };
4EEAF88E6C6D61E595249DF5 /* Pods-Yep.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Yep.release.xcconfig"; path = "Pods/Target Support Files/Pods-Yep/Pods-Yep.release.xcconfig"; sourceTree = "<group>"; };
5008A1941B8DB8B500E94274 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -424,7 +425,6 @@
500EED991BF1B81000C6BABC /* MediaPreviewViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaPreviewViewController.swift; path = ViewControllers/MediaPreview/MediaPreviewViewController.swift; sourceTree = "<group>"; };
500EED9C1BF1B84A00C6BABC /* MediaPreview.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = MediaPreview.storyboard; path = ViewControllers/MediaPreview/MediaPreview.storyboard; sourceTree = "<group>"; };
501138251BCB59120055A014 /* FeedConversationsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedConversationsViewController.swift; path = ViewControllers/FeedConversations/FeedConversationsViewController.swift; sourceTree = "<group>"; };
501138281BCB593F0055A014 /* FeedConversations.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = FeedConversations.storyboard; sourceTree = "<group>"; };
5011382A1BCB5E150055A014 /* FeedConversationDockCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedConversationDockCell.swift; path = Views/Cells/FeedConversationDock/FeedConversationDockCell.swift; sourceTree = "<group>"; };
5011382B1BCB5E150055A014 /* FeedConversationDockCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedConversationDockCell.xib; path = Views/Cells/FeedConversationDock/FeedConversationDockCell.xib; sourceTree = "<group>"; };
5011382F1BCB62A20055A014 /* FeedConversationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedConversationCell.swift; path = Views/Cells/FeedConversation/FeedConversationCell.swift; sourceTree = "<group>"; };
@@ -487,9 +487,7 @@
502B606D1BD6567600D9C17E /* AvatarStyles.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AvatarStyles.swift; path = Caches/AvatarStyles.swift; sourceTree = "<group>"; };
502C24821C1034000074A1BA /* UIImageView+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIImageView+Yep.swift"; path = "Extensions/UIImageView+Yep.swift"; sourceTree = "<group>"; };
503315951AC1017E0008A209 /* ChatLeftTextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftTextCell.swift; path = Views/Cells/ChatLeftText/ChatLeftTextCell.swift; sourceTree = "<group>"; };
503315961AC1017E0008A209 /* ChatLeftTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftTextCell.xib; path = Views/Cells/ChatLeftText/ChatLeftTextCell.xib; sourceTree = "<group>"; };
5033159A1AC101C80008A209 /* ChatRightTextCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightTextCell.swift; path = Views/Cells/ChatRightText/ChatRightTextCell.swift; sourceTree = "<group>"; };
5033159B1AC101C80008A209 /* ChatRightTextCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatRightTextCell.xib; path = Views/Cells/ChatRightText/ChatRightTextCell.xib; sourceTree = "<group>"; };
5033159F1AC126100008A209 /* MessageToolbar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageToolbar.swift; path = Views/MessageToolbar/MessageToolbar.swift; sourceTree = "<group>"; };
5038071F1BD9EEFC0068345E /* YepMedia.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepMedia.swift; path = Helpers/YepMedia.swift; sourceTree = "<group>"; };
503807211BDA18040068345E /* FeedSkillUsersCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedSkillUsersCell.swift; path = Views/Cells/FeedSkillUsers/FeedSkillUsersCell.swift; sourceTree = "<group>"; };
@@ -500,6 +498,7 @@
5039594D1B05E6E000797A3E /* ProfileSocialAccountGithubCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSocialAccountGithubCell.xib; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountGithubCell.xib; sourceTree = "<group>"; };
503CAB421AC00CB100DFE830 /* ConversationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationViewController.swift; path = ViewControllers/Conversation/ConversationViewController.swift; sourceTree = "<group>"; };
503ED2391B8ADCE70072187D /* BlackListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BlackListViewController.swift; path = ViewControllers/BlackList/BlackListViewController.swift; sourceTree = "<group>"; };
50485BCD1C56062400431AC7 /* ChatLeftRecallCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftRecallCell.swift; path = Views/Cells/ChatLeftRecall/ChatLeftRecallCell.swift; sourceTree = "<group>"; };
504BDC141B4528A500DBD35D /* Badges.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Badges.xcassets; sourceTree = "<group>"; };
504CBA3C1B3D04B800663A90 /* ChatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatTextView.swift; path = Views/TextViews/ChatTextView.swift; sourceTree = "<group>"; };
504F41241ACE2D1D00FBB19A /* SampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SampleView.swift; path = Views/AudioWaves/SampleView.swift; sourceTree = "<group>"; };
@@ -511,6 +510,10 @@
505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedGithubRepoContainerView.swift; path = Views/ContainerViews/FeedGithubRepoContainerView.swift; sourceTree = "<group>"; };
505082341C212E9200643CEB /* FeedLocationContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedLocationContainerView.swift; path = Views/ContainerViews/FeedLocationContainerView.swift; sourceTree = "<group>"; };
505082361C21377E00643CEB /* FeedVoiceContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedVoiceContainerView.swift; path = Views/ContainerViews/FeedVoiceContainerView.swift; sourceTree = "<group>"; };
5052620F1C52050700292C6A /* ConcurrentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConcurrentOperation.swift; path = Operations/ConcurrentOperation.swift; sourceTree = "<group>"; };
505262111C5205AB00292C6A /* UploadAttachmentOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = UploadAttachmentOperation.swift; path = Operations/UploadAttachmentOperation.swift; sourceTree = "<group>"; };
505262141C522A1E00292C6A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Show.storyboard; sourceTree = "<group>"; };
505262161C522A3D00292C6A /* FeedConversations.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = FeedConversations.storyboard; path = ViewControllers/FeedConversations/FeedConversations.storyboard; sourceTree = "<group>"; };
50535D191BCD19AE002FE1A4 /* TouchClosuresView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TouchClosuresView.swift; path = Views/TouchClosures/TouchClosuresView.swift; sourceTree = "<group>"; };
50535D1C1BCE42BE002FE1A4 /* PickPhotosViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PickPhotosViewController.swift; path = ViewControllers/PickPhotos/PickPhotosViewController.swift; sourceTree = "<group>"; };
50535D1F1BCE43ED002FE1A4 /* ImageCacheController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageCacheController.swift; path = ViewControllers/PickPhotos/ImageCacheController.swift; sourceTree = "<group>"; };
@@ -522,9 +525,7 @@
5053AD4E1AF82A1700B3CBFA /* PickLocationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PickLocationCell.swift; path = Views/Cells/PickLocation/PickLocationCell.swift; sourceTree = "<group>"; };
5053AD4F1AF82A1700B3CBFA /* PickLocationCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PickLocationCell.xib; path = Views/Cells/PickLocation/PickLocationCell.xib; sourceTree = "<group>"; };
5053AD531AF83B0F00B3CBFA /* ChatRightLocationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightLocationCell.swift; path = Views/Cells/ChatRightLocation/ChatRightLocationCell.swift; sourceTree = "<group>"; };
5053AD541AF83B0F00B3CBFA /* ChatRightLocationCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatRightLocationCell.xib; path = Views/Cells/ChatRightLocation/ChatRightLocationCell.xib; sourceTree = "<group>"; };
5053AD581AF83B4200B3CBFA /* ChatLeftLocationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftLocationCell.swift; path = Views/Cells/ChatLeftLocation/ChatLeftLocationCell.swift; sourceTree = "<group>"; };
5053AD591AF83B4200B3CBFA /* ChatLeftLocationCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftLocationCell.xib; path = Views/Cells/ChatLeftLocation/ChatLeftLocationCell.xib; sourceTree = "<group>"; };
505498611B12FB0E0037E3BD /* ConversationMessagePreviewNavigationControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationMessagePreviewNavigationControllerDelegate.swift; path = ViewControllers/Conversation/ConversationMessagePreviewNavigationControllerDelegate.swift; sourceTree = "<group>"; };
50553F8D1ADB8BA200F80B59 /* ChatSectionDateCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatSectionDateCell.swift; path = Views/Cells/ChatSectionDate/ChatSectionDateCell.swift; sourceTree = "<group>"; };
50553F8E1ADB8BA200F80B59 /* ChatSectionDateCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatSectionDateCell.xib; path = Views/Cells/ChatSectionDate/ChatSectionDateCell.xib; sourceTree = "<group>"; };
@@ -582,9 +583,7 @@
508D173F1ACCD2740092D666 /* YepButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepButton.swift; path = Views/Buttons/YepButton.swift; sourceTree = "<group>"; };
508D17431ACCDC9F0092D666 /* VoiceRecordButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = VoiceRecordButton.swift; path = Views/MessageToolbar/VoiceRecordButton.swift; sourceTree = "<group>"; };
508D17451ACCF4710092D666 /* ChatLeftAudioCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftAudioCell.swift; path = Views/Cells/ChatLeftAudio/ChatLeftAudioCell.swift; sourceTree = "<group>"; };
508D17461ACCF4710092D666 /* ChatLeftAudioCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftAudioCell.xib; path = Views/Cells/ChatLeftAudio/ChatLeftAudioCell.xib; sourceTree = "<group>"; };
508D174A1ACCF49B0092D666 /* ChatRightAudioCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightAudioCell.swift; path = Views/Cells/ChatRightAudio/ChatRightAudioCell.swift; sourceTree = "<group>"; };
508D174B1ACCF49B0092D666 /* ChatRightAudioCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatRightAudioCell.xib; path = Views/Cells/ChatRightAudio/ChatRightAudioCell.xib; sourceTree = "<group>"; };
508DF62A1B9985F300B194A4 /* YepLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepLog.swift; path = Helpers/YepLog.swift; sourceTree = "<group>"; };
508F8FC61B0161EF00461B0B /* ProfileSeparationLineCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileSeparationLineCell.swift; path = Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.swift; sourceTree = "<group>"; };
508F8FC71B0161EF00461B0B /* ProfileSeparationLineCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSeparationLineCell.xib; path = Views/Cells/ProfileSeparationLine/ProfileSeparationLineCell.xib; sourceTree = "<group>"; };
@@ -623,6 +622,7 @@
50AFBF4A1BB2A44C007D19DA /* InfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InfoView.swift; path = Views/InfoView/InfoView.swift; sourceTree = "<group>"; };
50AFBF4D1BB3A594007D19DA /* bub3.caf */ = {isa = PBXFileReference; lastKnownFileType = file; name = bub3.caf; path = Sounds/bub3.caf; sourceTree = "<group>"; };
50AFBF5E1BB4F332007D19DA /* FeedsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedsViewController.swift; path = ViewControllers/Feeds/FeedsViewController.swift; sourceTree = "<group>"; };
50B22E451C588B6200219625 /* CGImage+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CGImage+Yep.swift"; path = "Extensions/CGImage+Yep.swift"; sourceTree = "<group>"; };
50B2CA911BD0BF3800668AC6 /* FeedTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedTextView.swift; path = Views/TextViews/FeedTextView.swift; sourceTree = "<group>"; };
50B2CA931BD0EF7900668AC6 /* MoreMessageTypesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MoreMessageTypesView.swift; path = Views/MoreMessageTypes/MoreMessageTypesView.swift; sourceTree = "<group>"; };
50B38FB61B69CD960064C132 /* Array+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "Array+Yep.swift"; path = "Extensions/Array+Yep.swift"; sourceTree = "<group>"; };
@@ -637,12 +637,14 @@
50CC4FA11C1AC0EE00B6A263 /* UITableView+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UITableView+Yep.swift"; path = "Extensions/UITableView+Yep.swift"; sourceTree = "<group>"; };
50CC4FA31C1AC58700B6A263 /* UICollectionView+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UICollectionView+Yep.swift"; path = "Extensions/UICollectionView+Yep.swift"; sourceTree = "<group>"; };
50CDBE401ACA9B4200459CE0 /* ChatRightImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightImageCell.swift; path = Views/Cells/ChatRightImage/ChatRightImageCell.swift; sourceTree = "<group>"; };
50CDBE411ACA9B4200459CE0 /* ChatRightImageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatRightImageCell.xib; path = Views/Cells/ChatRightImage/ChatRightImageCell.xib; sourceTree = "<group>"; };
50CDBE451ACA9F2200459CE0 /* ImageCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageCache.swift; path = Caches/ImageCache.swift; sourceTree = "<group>"; };
50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftImageCell.swift; path = Views/Cells/ChatLeftImage/ChatLeftImageCell.swift; sourceTree = "<group>"; };
50CDBE481ACBAD3200459CE0 /* ChatLeftImageCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftImageCell.xib; path = Views/Cells/ChatLeftImage/ChatLeftImageCell.xib; sourceTree = "<group>"; };
50D45FC21C44D2300044C95A /* OpenGraphService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OpenGraphService.swift; path = Services/OpenGraphService.swift; sourceTree = "<group>"; };
50D4D0DA1AFA825B005AEB6F /* MediaControlView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaControlView.swift; path = Views/Media/MediaControlView.swift; sourceTree = "<group>"; };
50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OpenGraphInfoType.swift; path = Protocols/OpenGraphInfoType.swift; sourceTree = "<group>"; };
50DBDEFB1C4CCB030044E21B /* ChatLeftTextURLCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftTextURLCell.swift; path = Views/Cells/ChatLeftTextURL/ChatLeftTextURLCell.swift; sourceTree = "<group>"; };
50DBDEFF1C4CE75E0044E21B /* ChatRightTextURLCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightTextURLCell.swift; path = Views/Cells/ChatRightTextURL/ChatRightTextURLCell.swift; sourceTree = "<group>"; };
50DBDF041C4DD9530044E21B /* CGSize+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "CGSize+Yep.swift"; path = "Extensions/CGSize+Yep.swift"; sourceTree = "<group>"; };
50DEE02A1BE209FF00015741 /* DeletedFeedConversationCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DeletedFeedConversationCell.swift; path = Views/Cells/DeletedFeedConversation/DeletedFeedConversationCell.swift; sourceTree = "<group>"; };
50DEE02B1BE209FF00015741 /* DeletedFeedConversationCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DeletedFeedConversationCell.xib; path = Views/Cells/DeletedFeedConversation/DeletedFeedConversationCell.xib; sourceTree = "<group>"; };
50DEE02F1BE339B000015741 /* LoadMoreTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadMoreTableViewCell.swift; path = Views/Cells/LoadMore/LoadMoreTableViewCell.swift; sourceTree = "<group>"; };
@@ -690,9 +692,7 @@
50E61CEA1AF1F69D00908A9D /* ConversationTitleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConversationTitleView.swift; path = Views/ConversationTitle/ConversationTitleView.swift; sourceTree = "<group>"; };
50EB8C5D1AE78AB9001AC1EE /* YepAsset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YepAsset.swift; path = Helpers/YepAsset.swift; sourceTree = "<group>"; };
50EB8CBD1AE89ED8001AC1EE /* ChatLeftVideoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatLeftVideoCell.swift; path = Views/Cells/ChatLeftVideo/ChatLeftVideoCell.swift; sourceTree = "<group>"; };
50EB8CBE1AE89ED8001AC1EE /* ChatLeftVideoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatLeftVideoCell.xib; path = Views/Cells/ChatLeftVideo/ChatLeftVideoCell.xib; sourceTree = "<group>"; };
50EB8CC21AE89F12001AC1EE /* ChatRightVideoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatRightVideoCell.swift; path = Views/Cells/ChatRightVideo/ChatRightVideoCell.swift; sourceTree = "<group>"; };
50EB8CC31AE89F12001AC1EE /* ChatRightVideoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ChatRightVideoCell.xib; path = Views/Cells/ChatRightVideo/ChatRightVideoCell.xib; sourceTree = "<group>"; };
50EB8CCA1AEA1AC0001AC1EE /* MediaView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MediaView.swift; path = Views/Media/MediaView.swift; sourceTree = "<group>"; };
50EB8CCD1AEA1B38001AC1EE /* MessageMediaViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MessageMediaViewController.swift; path = ViewControllers/MessageMedia/MessageMediaViewController.swift; sourceTree = "<group>"; };
50F2B9771B2EDF3D00F840CC /* BorderTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BorderTextField.swift; path = Views/TextFields/BorderTextField.swift; sourceTree = "<group>"; };
@@ -710,6 +710,8 @@
50FD753C1C1FB4AC0050A382 /* FPSLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FPSLabel.swift; path = Views/Labels/FPSLabel.swift; sourceTree = "<group>"; };
50FD753E1C20FF8D0050A382 /* FeedMediaContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaContainerView.swift; path = Views/ContainerViews/FeedMediaContainerView.swift; sourceTree = "<group>"; };
50FD75431C21097A0050A382 /* LinkContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LinkContainerView.swift; path = Views/ContainerViews/LinkContainerView.swift; sourceTree = "<group>"; };
50FE29CC1C4786AE0067CA03 /* FeedURLCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedURLCell.swift; path = Views/Cells/Feed/FeedURLCell.swift; sourceTree = "<group>"; };
50FE29CE1C478A4A0067CA03 /* FeedURLContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedURLContainerView.swift; path = Views/ContainerViews/FeedURLContainerView.swift; sourceTree = "<group>"; };
6A56328C1BC90A4500397B53 /* DiscoverCardUserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DiscoverCardUserCell.swift; path = Views/Cells/DiscoverCardUser/DiscoverCardUserCell.swift; sourceTree = "<group>"; };
6A56328D1BC90A4500397B53 /* DiscoverCardUserCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DiscoverCardUserCell.xib; path = Views/Cells/DiscoverCardUser/DiscoverCardUserCell.xib; sourceTree = "<group>"; };
6A5632911BC911CB00397B53 /* DiscoverNormalUserCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DiscoverNormalUserCell.swift; path = Views/Cells/DiscoverNormalUser/DiscoverNormalUserCell.swift; sourceTree = "<group>"; };
@@ -720,6 +722,9 @@
6A8215061BCBE8F10011F36C /* DiscoverSkillCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DiscoverSkillCell.swift; path = Views/Cells/DiscoverSkill/DiscoverSkillCell.swift; sourceTree = "<group>"; };
6A8215071BCBE8F10011F36C /* DiscoverSkillCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = DiscoverSkillCell.xib; path = Views/Cells/DiscoverSkill/DiscoverSkillCell.xib; sourceTree = "<group>"; };
6AB0DF591BE169000052BED1 /* ShareProfileView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShareProfileView.swift; path = Views/ShareProfileView/ShareProfileView.swift; sourceTree = "<group>"; };
6AC40E301C47A8EE0024FC63 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
6AC40E321C47A8FE0024FC63 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
6AC40E341C47A9060024FC63 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
6ADDB44F1B848C600012877A /* 1Password.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = 1Password.xcassets; sourceTree = "<group>"; };
8409137C1B7DB173001C89D6 /* FriendRequestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FriendRequestView.swift; path = Views/FriendRequest/FriendRequestView.swift; sourceTree = "<group>"; };
840CD23B1B7B59D5000D2680 /* ShowStepViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShowStepViewController.swift; path = ViewControllers/Show/ShowStepViewController.swift; sourceTree = "<group>"; };
@@ -731,7 +736,6 @@
848806AE1B789A2D000C5424 /* EditSkillCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditSkillCell.swift; path = Views/Cells/EditSkill/EditSkillCell.swift; sourceTree = "<group>"; };
848806AF1B789A2D000C5424 /* EditSkillCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = EditSkillCell.xib; path = Views/Cells/EditSkill/EditSkillCell.xib; sourceTree = "<group>"; };
848806B21B7B0230000C5424 /* Yep.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Yep.entitlements; sourceTree = "<group>"; };
848806B31B7B4E2E000C5424 /* Show.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Show.storyboard; sourceTree = "<group>"; };
848806B51B7B4F53000C5424 /* ShowViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShowViewController.swift; path = ViewControllers/Show/ShowViewController.swift; sourceTree = "<group>"; };
84C959881B731FD20017D3ED /* ChatBaseCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ChatBaseCell.swift; path = Views/Cells/ChatBaseCell/ChatBaseCell.swift; sourceTree = "<group>"; };
88C96122C478C4D134F634F6 /* Pods-Yep.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Yep.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Yep/Pods-Yep.debug.xcconfig"; sourceTree = "<group>"; };
@@ -747,12 +751,15 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5052620B1C51CDE600292C6A /* libz.tbd in Frameworks */,
6AC40E351C47A9060024FC63 /* libsqlite3.tbd in Frameworks */,
0AD7EA641AC3ED2A00617758 /* SystemConfiguration.framework in Frameworks */,
6AC40E311C47A8EE0024FC63 /* CoreData.framework in Frameworks */,
0AD7EA681AC3ED3600617758 /* Foundation.framework in Frameworks */,
0AAFCFF91AC8CA5B00EB0EF8 /* MobileCoreServices.framework in Frameworks */,
0AD7EA6C1AC3ED4300617758 /* Security.framework in Frameworks */,
0AD7EA6A1AC3ED3D00617758 /* UIKit.framework in Frameworks */,
0AD7EA661AC3ED3000617758 /* CoreGraphics.framework in Frameworks */,
0AD7EA641AC3ED2A00617758 /* SystemConfiguration.framework in Frameworks */,
0AD7EA621AC3ED2100617758 /* CoreTelephony.framework in Frameworks */,
0AD7EA601AC3ED1900617758 /* CoreFoundation.framework in Frameworks */,
0AD7EA5E1AC3ED1200617758 /* CFNetwork.framework in Frameworks */,
@@ -846,11 +853,13 @@
502AE5251AB72BE9005BD199 /* Services */,
5023DE571AB6CA1400B3EE96 /* Caches */,
5023DE341AB688F900B3EE96 /* Extensions */,
50DBDEF21C48C2290044E21B /* Protocols */,
5052620E1C5202C700292C6A /* Operations */,
504F5E721BA034BB006CE418 /* Activities */,
5023DE391AB6979C00B3EE96 /* Views */,
5023DE271AB6856600B3EE96 /* ViewControllers */,
848806B31B7B4E2E000C5424 /* Show.storyboard */,
502AE5171AB70D72005BD199 /* Intro.storyboard */,
505262131C522A1E00292C6A /* Show.storyboard */,
0A8F54B41AB67D11004AD60E /* Main.storyboard */,
6ADDB44F1B848C600012877A /* 1Password.xcassets */,
0A8F54B71AB67D11004AD60E /* Images.xcassets */,
@@ -868,7 +877,6 @@
0A8F54AE1AB67D11004AD60E /* Supporting Files */ = {
isa = PBXGroup;
children = (
0AD7EA751AC3EE9B00617758 /* JPush */,
0A8F54AF1AB67D11004AD60E /* Info.plist */,
);
name = "Supporting Files";
@@ -938,14 +946,6 @@
name = ScrollView;
sourceTree = "<group>";
};
0AD7EA751AC3EE9B00617758 /* JPush */ = {
isa = PBXGroup;
children = (
0AD7EA761AC3EEB000617758 /* PushConfig.plist */,
);
name = JPush;
sourceTree = "<group>";
};
500A0B1D1B450C2F00D49358 /* EditNicknameAndBadge */ = {
isa = PBXGroup;
children = (
@@ -976,7 +976,7 @@
isa = PBXGroup;
children = (
501138251BCB59120055A014 /* FeedConversationsViewController.swift */,
501138281BCB593F0055A014 /* FeedConversations.storyboard */,
505262161C522A3D00292C6A /* FeedConversations.storyboard */,
);
name = FeedConversations;
sourceTree = "<group>";
@@ -1143,8 +1143,10 @@
children = (
0A3E60631ACCAC02003DC853 /* NSDate+TimeAgo */,
5023DE321AB688F600B3EE96 /* UIColor+Yep.swift */,
50DBDF041C4DD9530044E21B /* CGSize+Yep.swift */,
5023DE511AB6C51300B3EE96 /* CGRect+Yep.swift */,
5023DE581AB6DBC100B3EE96 /* UIImage+Yep.swift */,
50B22E451C588B6200219625 /* CGImage+Yep.swift */,
506EC2091AC95BFC00CE42B3 /* UIFont+Yep.swift */,
0A3E605F1ACCA9C0003DC853 /* String+Yep.swift */,
50A9D44A1ACD37A8000B2599 /* NSFileManager+Yep.swift */,
@@ -1228,6 +1230,8 @@
508B717E1B1FE9BD006BDDB3 /* ChatRightBase */,
503315991AC101830008A209 /* ChatLeftText */,
5033159E1AC101CB0008A209 /* ChatRightText */,
50DBDEFA1C4CBB0F0044E21B /* ChatLeftTextURL */,
50DBDF031C4CE7640044E21B /* ChatRightTextURL */,
50CDBE4B1ACBAD3700459CE0 /* ChatLeftImage */,
50CDBE441ACA9B4600459CE0 /* ChatRightImage */,
50EB8CC11AE89EDE001AC1EE /* ChatLeftVideo */,
@@ -1236,6 +1240,7 @@
508D174E1ACCF49F0092D666 /* ChatRightAudio */,
5053AD5C1AF83B4600B3CBFA /* ChatLeftLocation */,
5053AD571AF83B1600B3CBFA /* ChatRightLocation */,
50485BCF1C56062900431AC7 /* ChatLeftRecall */,
5016901A1BFB18D70096C4F9 /* ChatLeftSocialWork */,
508C2AA61AD661B8002B8097 /* UserState */,
50553F911ADB8BA600F80B59 /* ChatSectionDate */,
@@ -1469,7 +1474,6 @@
isa = PBXGroup;
children = (
503315951AC1017E0008A209 /* ChatLeftTextCell.swift */,
503315961AC1017E0008A209 /* ChatLeftTextCell.xib */,
);
name = ChatLeftText;
sourceTree = "<group>";
@@ -1478,7 +1482,6 @@
isa = PBXGroup;
children = (
5033159A1AC101C80008A209 /* ChatRightTextCell.swift */,
5033159B1AC101C80008A209 /* ChatRightTextCell.xib */,
);
name = ChatRightText;
sourceTree = "<group>";
@@ -1533,6 +1536,14 @@
name = BlackList;
sourceTree = "<group>";
};
50485BCF1C56062900431AC7 /* ChatLeftRecall */ = {
isa = PBXGroup;
children = (
50485BCD1C56062400431AC7 /* ChatLeftRecallCell.swift */,
);
name = ChatLeftRecall;
sourceTree = "<group>";
};
504CBA3B1B3D048900663A90 /* TextViews */ = {
isa = PBXGroup;
children = (
@@ -1567,6 +1578,15 @@
name = DiscoverFilter;
sourceTree = "<group>";
};
5052620E1C5202C700292C6A /* Operations */ = {
isa = PBXGroup;
children = (
5052620F1C52050700292C6A /* ConcurrentOperation.swift */,
505262111C5205AB00292C6A /* UploadAttachmentOperation.swift */,
);
name = Operations;
sourceTree = "<group>";
};
50535D1B1BCD19B4002FE1A4 /* TouchClosures */ = {
isa = PBXGroup;
children = (
@@ -1617,7 +1637,6 @@
isa = PBXGroup;
children = (
5053AD531AF83B0F00B3CBFA /* ChatRightLocationCell.swift */,
5053AD541AF83B0F00B3CBFA /* ChatRightLocationCell.xib */,
);
name = ChatRightLocation;
sourceTree = "<group>";
@@ -1626,7 +1645,6 @@
isa = PBXGroup;
children = (
5053AD581AF83B4200B3CBFA /* ChatLeftLocationCell.swift */,
5053AD591AF83B4200B3CBFA /* ChatLeftLocationCell.xib */,
);
name = ChatLeftLocation;
sourceTree = "<group>";
@@ -1804,7 +1822,6 @@
isa = PBXGroup;
children = (
508D17451ACCF4710092D666 /* ChatLeftAudioCell.swift */,
508D17461ACCF4710092D666 /* ChatLeftAudioCell.xib */,
);
name = ChatLeftAudio;
sourceTree = "<group>";
@@ -1813,7 +1830,6 @@
isa = PBXGroup;
children = (
508D174A1ACCF49B0092D666 /* ChatRightAudioCell.swift */,
508D174B1ACCF49B0092D666 /* ChatRightAudioCell.xib */,
);
name = ChatRightAudio;
sourceTree = "<group>";
@@ -2007,6 +2023,7 @@
50E392C11C22B9E400C0CECB /* FeedDribbbleShotCell.swift */,
50E392C31C22C0F600C0CECB /* FeedVoiceCell.swift */,
50E392C51C22C82800C0CECB /* FeedLocationCell.swift */,
50FE29CC1C4786AE0067CA03 /* FeedURLCell.swift */,
);
name = Feed;
sourceTree = "<group>";
@@ -2049,7 +2066,6 @@
isa = PBXGroup;
children = (
50CDBE401ACA9B4200459CE0 /* ChatRightImageCell.swift */,
50CDBE411ACA9B4200459CE0 /* ChatRightImageCell.xib */,
);
name = ChatRightImage;
sourceTree = "<group>";
@@ -2058,11 +2074,34 @@
isa = PBXGroup;
children = (
50CDBE471ACBAD3200459CE0 /* ChatLeftImageCell.swift */,
50CDBE481ACBAD3200459CE0 /* ChatLeftImageCell.xib */,
);
name = ChatLeftImage;
sourceTree = "<group>";
};
50DBDEF21C48C2290044E21B /* Protocols */ = {
isa = PBXGroup;
children = (
50DBDEF01C48C2240044E21B /* OpenGraphInfoType.swift */,
);
name = Protocols;
sourceTree = "<group>";
};
50DBDEFA1C4CBB0F0044E21B /* ChatLeftTextURL */ = {
isa = PBXGroup;
children = (
50DBDEFB1C4CCB030044E21B /* ChatLeftTextURLCell.swift */,
);
name = ChatLeftTextURL;
sourceTree = "<group>";
};
50DBDF031C4CE7640044E21B /* ChatRightTextURL */ = {
isa = PBXGroup;
children = (
50DBDEFF1C4CE75E0044E21B /* ChatRightTextURLCell.swift */,
);
name = ChatRightTextURL;
sourceTree = "<group>";
};
50DEE02E1BE20A0400015741 /* DeletedFeedConversation */ = {
isa = PBXGroup;
children = (
@@ -2147,7 +2186,6 @@
isa = PBXGroup;
children = (
50EB8CBD1AE89ED8001AC1EE /* ChatLeftVideoCell.swift */,
50EB8CBE1AE89ED8001AC1EE /* ChatLeftVideoCell.xib */,
);
name = ChatLeftVideo;
sourceTree = "<group>";
@@ -2156,7 +2194,6 @@
isa = PBXGroup;
children = (
50EB8CC21AE89F12001AC1EE /* ChatRightVideoCell.swift */,
50EB8CC31AE89F12001AC1EE /* ChatRightVideoCell.xib */,
);
name = ChatRightVideo;
sourceTree = "<group>";
@@ -2232,6 +2269,7 @@
505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */,
505082341C212E9200643CEB /* FeedLocationContainerView.swift */,
505082361C21377E00643CEB /* FeedVoiceContainerView.swift */,
50FE29CE1C478A4A0067CA03 /* FeedURLContainerView.swift */,
50F6D32D1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift */,
);
name = ContainerViews;
@@ -2249,6 +2287,9 @@
67B708B60AAD1DEA82A6A682 /* Frameworks */ = {
isa = PBXGroup;
children = (
6AC40E341C47A9060024FC63 /* libsqlite3.tbd */,
6AC40E321C47A8FE0024FC63 /* libz.tbd */,
6AC40E301C47A8EE0024FC63 /* CoreData.framework */,
0AAFCFF81AC8CA5B00EB0EF8 /* MobileCoreServices.framework */,
0AD7EA6B1AC3ED4300617758 /* Security.framework */,
0AD7EA691AC3ED3D00617758 /* UIKit.framework */,
@@ -2484,12 +2525,12 @@
506BB7F61AE4E0A500C1A2A0 /* SkillAnnotationHeader.xib in Resources */,
50AFBF4E1BB3A594007D19DA /* bub3.caf in Resources */,
50DEE02D1BE209FF00015741 /* DeletedFeedConversationCell.xib in Resources */,
5053AD561AF83B0F00B3CBFA /* ChatRightLocationCell.xib in Resources */,
50553F901ADB8BA200F80B59 /* ChatSectionDateCell.xib in Resources */,
50DEE0491BEA04CC00015741 /* SocialWorks.storyboard in Resources */,
501813261BA07CED0025873C /* Activities.xcassets in Resources */,
50DEE04B1BEA071100015741 /* OAuth.storyboard in Resources */,
502AE5411AB88862005BD199 /* SkillRankCell.xib in Resources */,
505262171C522A3D00292C6A /* FeedConversations.storyboard in Resources */,
508F8FD11B01D04400461B0B /* GithubRepoCell.xib in Resources */,
6A5632941BC911CB00397B53 /* DiscoverNormalUserCell.xib in Resources */,
500EED9D1BF1B84A00C6BABC /* MediaPreview.storyboard in Resources */,
@@ -2498,9 +2539,7 @@
508F8FD81B01EDB400461B0B /* DribbbleShotCell.xib in Resources */,
5020B8A31C05813C006CE8EA /* NewFeedVoiceRecord.storyboard in Resources */,
502AE5541AB93215005BD199 /* ProfileFooterCell.xib in Resources */,
50CDBE4A1ACBAD3200459CE0 /* ChatLeftImageCell.xib in Resources */,
6A56328F1BC90A4500397B53 /* DiscoverCardUserCell.xib in Resources */,
501138291BCB593F0055A014 /* FeedConversations.storyboard in Resources */,
50DEE05F1BEA119D00015741 /* AddFriends.storyboard in Resources */,
0A90187F1B01152800AE4B7F /* ProfileSocialAccountCell.xib in Resources */,
5064F6551B0B0E160089FAD4 /* AddFriendSearchCell.xib in Resources */,
@@ -2509,12 +2548,11 @@
5008A1931B8DB8B500E94274 /* Localizable.strings in Resources */,
50DEE0511BEA09DD00015741 /* BlackList.storyboard in Resources */,
50894CF61AEA4A01000981AF /* SettingsUserCell.xib in Resources */,
505262151C522A1E00292C6A /* Show.storyboard in Resources */,
6ADDB4501B848C600012877A /* 1Password.xcassets in Resources */,
50DEE04F1BEA092100015741 /* EditProfile.storyboard in Resources */,
50DEE05D1BEA100100015741 /* SearchedUsers.storyboard in Resources */,
508D17481ACCF4710092D666 /* ChatLeftAudioCell.xib in Resources */,
0A8F54B61AB67D11004AD60E /* Main.storyboard in Resources */,
5033159D1AC101C80008A209 /* ChatRightTextCell.xib in Resources */,
50535D291BCE4A71002FE1A4 /* PhotoCell.xib in Resources */,
84385C461B8594E80071130D /* Show.xcassets in Resources */,
50894CFB1AEA54BA000981AF /* SettingsMoreCell.xib in Resources */,
@@ -2524,9 +2562,7 @@
50DEE0451BEA025100015741 /* About.storyboard in Resources */,
50DEE0371BE3720F00015741 /* LoadMoreCollectionViewCell.xib in Resources */,
505EC81D1ADFBE1B001D27E0 /* SkillSelectionCell.xib in Resources */,
50EB8CC01AE89ED8001AC1EE /* ChatLeftVideoCell.xib in Resources */,
50894D0D1AEE0177000981AF /* EditProfileColoredTitleCell.xib in Resources */,
50CDBE431ACA9B4200459CE0 /* ChatRightImageCell.xib in Resources */,
50E1CE8B1BE0BC940025B933 /* MessageMedia.storyboard in Resources */,
50DEE0471BEA030900015741 /* PodsHelpYep.storyboard in Resources */,
508C2AA51AD661B3002B8097 /* UserStateCell.xib in Resources */,
@@ -2546,9 +2582,7 @@
5011382D1BCB5E150055A014 /* FeedConversationDockCell.xib in Resources */,
50E1CE891BDA3CED0025B933 /* SkillHome.storyboard in Resources */,
50DEE0571BEA0C9A00015741 /* Settings.storyboard in Resources */,
5053AD5B1AF83B4200B3CBFA /* ChatLeftLocationCell.xib in Resources */,
0A8F54B81AB67D11004AD60E /* Images.xcassets in Resources */,
50EB8CC51AE89F12001AC1EE /* ChatRightVideoCell.xib in Resources */,
50DEE0321BE339B000015741 /* LoadMoreTableViewCell.xib in Resources */,
502AE5191AB70D72005BD199 /* Intro.storyboard in Resources */,
502B60601BD112E500D9C17E /* QuickPickPhotosCell.xib in Resources */,
@@ -2556,14 +2590,10 @@
50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */,
50894D081AEDFDA2000981AF /* EditProfileMoreInfoCell.xib in Resources */,
50A640E61B6F41E00050E4A3 /* DoNotDisturbSwitchCell.xib in Resources */,
508D174D1ACCF49B0092D666 /* ChatRightAudioCell.xib in Resources */,
50A9D44E1ACD382B000B2599 /* NSDateTimeAgo.bundle in Resources */,
50392CD21B393FF60009D647 /* SkillAddCell.xib in Resources */,
503315981AC1017E0008A209 /* ChatLeftTextCell.xib in Resources */,
50894D031AEA6433000981AF /* EditProfileLessInfoCell.xib in Resources */,
501138321BCB62A20055A014 /* FeedConversationCell.xib in Resources */,
848806B41B7B4E2E000C5424 /* Show.storyboard in Resources */,
0AD7EA771AC3EEB000617758 /* PushConfig.plist in Resources */,
502B605B1BD1102B00D9C17E /* TitleCell.xib in Resources */,
5064F65A1B0B14420089FAD4 /* AddFriendMoreCell.xib in Resources */,
);
@@ -2648,6 +2678,7 @@
50CDBE421ACA9B4200459CE0 /* ChatRightImageCell.swift in Sources */,
5099B9C41AB9510D002F940B /* RankView.swift in Sources */,
502AE5311AB85067005BD199 /* RegisterPickMobileViewController.swift in Sources */,
50B22E461C588B6200219625 /* CGImage+Yep.swift in Sources */,
50535D1A1BCD19AE002FE1A4 /* TouchClosuresView.swift in Sources */,
506E241E1AE6391A00A33C1A /* TouchZoomButton.swift in Sources */,
5020B8A51C059662006CE8EA /* VoiceRecordSampleView.swift in Sources */,
@@ -2678,6 +2709,7 @@
5023DE331AB688F600B3EE96 /* UIColor+Yep.swift in Sources */,
503315971AC1017E0008A209 /* ChatLeftTextCell.swift in Sources */,
509B120F1C436E7C0000BE71 /* MentionView.swift in Sources */,
505262101C52050700292C6A /* ConcurrentOperation.swift in Sources */,
50B2CA941BD0EF7900668AC6 /* MoreMessageTypesView.swift in Sources */,
0A8F54B11AB67D11004AD60E /* AppDelegate.swift in Sources */,
508F8FC81B0161EF00461B0B /* ProfileSeparationLineCell.swift in Sources */,
@@ -2717,6 +2749,7 @@
501690181BFB18D20096C4F9 /* ChatLeftSocialWorkCell.swift in Sources */,
50E114F41ABBF9CC00F13000 /* Models.swift in Sources */,
848806B01B789A2D000C5424 /* EditSkillCell.swift in Sources */,
50485BCE1C56062400431AC7 /* ChatLeftRecallCell.swift in Sources */,
5018132C1BA3CFEF0025873C /* WeChatActivity.swift in Sources */,
5053AD551AF83B0F00B3CBFA /* ChatRightLocationCell.swift in Sources */,
50A9D4571ACD393A000B2599 /* YepWaverView.swift in Sources */,
@@ -2753,6 +2786,7 @@
50DEE0411BE7626100015741 /* YepHelpers.swift in Sources */,
501138311BCB62A20055A014 /* FeedConversationCell.swift in Sources */,
502AE5401AB88862005BD199 /* SkillRankCell.swift in Sources */,
50DBDF011C4CE75E0044E21B /* ChatRightTextURLCell.swift in Sources */,
50E392C41C22C0F600C0CECB /* FeedVoiceCell.swift in Sources */,
50C090A01ADE391600CC6389 /* AddSkillsReusableView.swift in Sources */,
84385C4A1B85BE200071130D /* ShowStepMeetViewController.swift in Sources */,
@@ -2794,6 +2828,7 @@
50F5AF951BBBCDA30033C9BC /* FeedMediaAddCell.swift in Sources */,
50FD753F1C20FF8D0050A382 /* FeedMediaContainerView.swift in Sources */,
508D16BD1ADCAF0400270E3F /* PullToRefreshView.swift in Sources */,
505262121C5205AB00292C6A /* UploadAttachmentOperation.swift in Sources */,
508D17441ACCDC9F0092D666 /* VoiceRecordButton.swift in Sources */,
502AE5271AB72D06005BD199 /* YepNetworking.swift in Sources */,
5076C56A1AC46B9F00B22952 /* FayeService.swift in Sources */,
@@ -2823,6 +2858,7 @@
6A8215051BCBE5170011F36C /* MiniCardSkillLayout.swift in Sources */,
50F5AF8C1BBBB64C0033C9BC /* FeedAnyImagesCell.swift in Sources */,
50894CF11AEA4726000981AF /* SettingsViewController.swift in Sources */,
50DBDF051C4DD9530044E21B /* CGSize+Yep.swift in Sources */,
5023DE2A1AB685BA00B3EE96 /* ContactsViewController.swift in Sources */,
50CDBE491ACBAD3200459CE0 /* ChatLeftImageCell.swift in Sources */,
5033159C1AC101C80008A209 /* ChatRightTextCell.swift in Sources */,
@@ -2834,12 +2870,14 @@
502AE5351AB8649F005BD199 /* LoginByMobileViewController.swift in Sources */,
506BB7F91AE5069500C1A2A0 /* RegisterPickSkillsSelectSkillsTransitionManager.swift in Sources */,
50B8E6181B41340A00C17EA1 /* MessageLoadingProgressView.swift in Sources */,
50DBDEFD1C4CCB030044E21B /* ChatLeftTextURLCell.swift in Sources */,
505A48401B4E70CB00C3A301 /* RegisterSelectSkillsLayout.swift in Sources */,
502AE52B1AB819AF005BD199 /* YepConfig.swift in Sources */,
50B38FB71B69CD960064C132 /* Array+Yep.swift in Sources */,
5011382C1BCB5E150055A014 /* FeedConversationDockCell.swift in Sources */,
845E1A661B788EF3001E0789 /* EditSkillsViewController.swift in Sources */,
500D35DF1C3A00D2006D17E3 /* SegueViewController.swift in Sources */,
50FE29CF1C478A4A0067CA03 /* FeedURLContainerView.swift in Sources */,
50CC4FA41C1AC58700B6A263 /* UICollectionView+Yep.swift in Sources */,
504F85AC1B4D21DD00F909BE /* DiscoverFilterView.swift in Sources */,
0A271FD71ACA055400822DFC /* YepAudioService.swift in Sources */,
@@ -2865,6 +2903,8 @@
50E18BB71B180F7D0076C3C6 /* BottomButtonView.swift in Sources */,
0A90187E1B01152800AE4B7F /* ProfileSocialAccountCell.swift in Sources */,
50F5AF831BBA67060033C9BC /* NewFeedViewController.swift in Sources */,
50FE29CD1C4786AE0067CA03 /* FeedURLCell.swift in Sources */,
50DBDEF11C48C2240044E21B /* OpenGraphInfoType.swift in Sources */,
50894CF51AEA4A01000981AF /* SettingsUserCell.swift in Sources */,
84385C441B8592120071130D /* ShowStepGeniusViewController.swift in Sources */,
6A5632931BC911CB00397B53 /* DiscoverNormalUserCell.swift in Sources */,
@@ -2942,6 +2982,14 @@
name = Intro.storyboard;
sourceTree = "<group>";
};
505262131C522A1E00292C6A /* Show.storyboard */ = {
isa = PBXVariantGroup;
children = (
505262141C522A1E00292C6A /* Base */,
);
name = Show.storyboard;
sourceTree = "<group>";
};
505C338D1BAA92CA00E044E2 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
@@ -2995,7 +3043,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_SWIFT_FLAGS = "-D DEBUG -D STAGING_";
OTHER_SWIFT_FLAGS = "-D DEBUG -D STAGING";
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
@@ -3052,7 +3100,6 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Yep",
"$(PROJECT_DIR)/build/Debug-iphoneos/Pods",
"$(PROJECT_DIR)",
);
GCC_OPTIMIZATION_LEVEL = 0;
@@ -3064,11 +3111,78 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/Yep/JPush",
"$(PROJECT_DIR)/Yep",
"$(PROJECT_DIR)/Yep/GoogleAnalytics",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"c++\"",
"-l\"jpush-ios-2.1.0\"",
"-l\"z\"",
"-framework",
"\"APAddressBook\"",
"-framework",
"\"Alamofire\"",
"-framework",
"\"Base64\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreFoundation\"",
"-framework",
"\"CoreGraphics\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"Crashlytics\"",
"-framework",
"\"DeviceGuru\"",
"-framework",
"\"FXBlurView\"",
"-framework",
"\"Fabric\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"Kanna\"",
"-framework",
"\"KeyboardMan\"",
"-framework",
"\"Kingfisher\"",
"-framework",
"\"MZFayeClient\"",
"-framework",
"\"MonkeyKing\"",
"-framework",
"\"Navi\"",
"-framework",
"\"OnePasswordExtension\"",
"-framework",
"\"Proposer\"",
"-framework",
"\"Realm\"",
"-framework",
"\"RealmSwift\"",
"-framework",
"\"Ruler\"",
"-framework",
"\"Security\"",
"-framework",
"\"SocketRocket\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"TPKeyboardAvoiding\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"pop\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SWIFT_OBJC_BRIDGING_HEADER = "Yep/Yep-Bridging-Header.h";
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
@@ -3085,7 +3199,6 @@
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Yep",
"$(PROJECT_DIR)/build/Debug-iphoneos/Pods",
"$(PROJECT_DIR)",
);
GCC_OPTIMIZATION_LEVEL = s;
@@ -3097,11 +3210,78 @@
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/Yep/JPush",
"$(PROJECT_DIR)/Yep",
"$(PROJECT_DIR)/Yep/GoogleAnalytics",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"c++\"",
"-l\"jpush-ios-2.1.0\"",
"-l\"z\"",
"-framework",
"\"APAddressBook\"",
"-framework",
"\"Alamofire\"",
"-framework",
"\"Base64\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"CoreFoundation\"",
"-framework",
"\"CoreGraphics\"",
"-framework",
"\"CoreTelephony\"",
"-framework",
"\"Crashlytics\"",
"-framework",
"\"DeviceGuru\"",
"-framework",
"\"FXBlurView\"",
"-framework",
"\"Fabric\"",
"-framework",
"\"Foundation\"",
"-framework",
"\"Kanna\"",
"-framework",
"\"KeyboardMan\"",
"-framework",
"\"Kingfisher\"",
"-framework",
"\"MZFayeClient\"",
"-framework",
"\"MonkeyKing\"",
"-framework",
"\"Navi\"",
"-framework",
"\"OnePasswordExtension\"",
"-framework",
"\"Proposer\"",
"-framework",
"\"Realm\"",
"-framework",
"\"RealmSwift\"",
"-framework",
"\"Ruler\"",
"-framework",
"\"Security\"",
"-framework",
"\"SocketRocket\"",
"-framework",
"\"SystemConfiguration\"",
"-framework",
"\"TPKeyboardAvoiding\"",
"-framework",
"\"UIKit\"",
"-framework",
"\"pop\"",
);
PRODUCT_BUNDLE_IDENTIFIER = "Catch-Inc.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SWIFT_OBJC_BRIDGING_HEADER = "Yep/Yep-Bridging-Header.h";
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
+16 -10
View File
@@ -13,7 +13,7 @@ import AVFoundation
import RealmSwift
import MonkeyKing
import Navi
//import Appsee
import Appsee
@UIApplicationMain
@@ -43,7 +43,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
let directory: NSURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(YepConfig.appGroupID)!
let realmPath = directory.URLByAppendingPathComponent("db.realm").path!
return Realm.Configuration(path: realmPath, schemaVersion: 18, migrationBlock: { migration, oldSchemaVersion in
return Realm.Configuration(path: realmPath, schemaVersion: 24, migrationBlock: { migration, oldSchemaVersion in
})
}
@@ -77,10 +77,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
cacheInAdvance()
delay(0.5) { () -> Void in
Fabric.with([Crashlytics.self])
//Fabric.with([Crashlytics.self, Appsee.self])
APService.setupWithOption(launchOptions)
delay(0.5) {
// Fabric.with([Crashlytics.self])
Fabric.with([Crashlytics.self, Appsee.self])
#if STAGING
let apsForProduction = false
#else
let apsForProduction = true
#endif
JPUSHService.setupWithOption(launchOptions, appKey: "e521aa97cd4cd4eba5b73669", channel: "AppStore", apsForProduction: apsForProduction)
}
let _ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker)
@@ -105,7 +111,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
} else {
startShowStory()
}
return true
}
@@ -224,7 +230,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
println("didReceiveRemoteNotification: \(userInfo)")
APService.handleRemoteNotification(userInfo)
JPUSHService.handleRemoteNotification(userInfo)
if YepUserDefaults.isLogined {
@@ -447,8 +453,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func registerThirdPartyPushWithDeciveToken(deviceToken: NSData, pusherID: String) {
APService.registerDeviceToken(deviceToken)
APService.setTags(Set(["iOS"]), alias: pusherID, callbackSelector:nil, object: nil)
JPUSHService.registerDeviceToken(deviceToken)
JPUSHService.setTags(Set(["iOS"]), alias: pusherID, callbackSelector:nil, object: nil)
}
func tagsAliasCallback(iResCode: Int, tags: NSSet, alias: NSString) {
+24 -2
View File
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
@@ -9,7 +10,28 @@
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tabBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="DOv-e8-hb8">
<rect key="frame" x="0.0" y="431" width="480" height="49"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<items/>
</tabBar>
<navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ctT-Te-uUH">
<rect key="frame" x="0.0" y="0.0" width="480" height="64"/>
<constraints>
<constraint firstAttribute="height" constant="64" id="04F-IC-P91"/>
</constraints>
</navigationBar>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="DOv-e8-hb8" secondAttribute="trailing" id="6UV-lT-7ko"/>
<constraint firstItem="ctT-Te-uUH" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="JK6-qx-Kwk"/>
<constraint firstItem="DOv-e8-hb8" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="M1f-I0-h5H"/>
<constraint firstItem="ctT-Te-uUH" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="VVa-RK-5Ag"/>
<constraint firstAttribute="trailing" secondItem="ctT-Te-uUH" secondAttribute="trailing" id="bYq-fm-1Ke"/>
<constraint firstAttribute="bottom" secondItem="DOv-e8-hb8" secondAttribute="bottom" id="l30-6h-OO8"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
+2 -23
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Ha4-ol-D2e">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Ha4-ol-D2e">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--FeedConversationsViewController-->
@@ -35,7 +35,6 @@
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="ECY-qd-LcH" userLabel="Conversations Table View">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<inset key="separatorInset" minX="90" minY="0.0" maxX="0.0" maxY="0.0"/>
@@ -45,7 +44,6 @@
</connections>
</tableView>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="ECY-qd-LcH" secondAttribute="trailing" id="0jB-Xu-lQd"/>
@@ -79,7 +77,6 @@
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="3hF-wd-yhY" userLabel="Contacts Table View">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<inset key="separatorInset" minX="90" minY="0.0" maxX="0.0" maxY="0.0"/>
@@ -90,14 +87,12 @@
</tableView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="G4u-1I-leN">
<rect key="frame" x="0.0" y="0.0" width="600" height="20"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="nQx-4t-rE1"/>
</constraints>
</view>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="G4u-1I-leN" secondAttribute="trailing" id="855-j7-2GG"/>
@@ -141,7 +136,6 @@
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="qiv-cC-3J3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="sWq-uv-kTu">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
@@ -152,10 +146,8 @@
</collectionView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="zzD-ES-eHf">
<rect key="frame" x="290" y="184" width="20" height="20"/>
<animations/>
</activityIndicatorView>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="qiv-cC-3J3" firstAttribute="top" secondItem="y3q-lk-c8x" secondAttribute="bottom" constant="-64" id="7Dp-9c-rpA"/>
@@ -212,7 +204,6 @@
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="VZO-hp-fyU" userLabel="Profile Collection View">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="WvZ-fD-rR0" customClass="ProfileLayout" customModule="Yep" customModuleProvider="target">
<size key="itemSize" width="141" height="116"/>
@@ -228,7 +219,6 @@
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sfC-fv-gcr" userLabel="Say Hi View" customClass="BottomButtonView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="550" width="600" height="50"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="be0-Cf-Dol"/>
@@ -236,13 +226,11 @@
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="top_shadow" translatesAutoresizingMaskIntoConstraints="NO" id="fut-Ug-l4V" userLabel="Shadow Image View">
<rect key="frame" x="0.0" y="0.0" width="600" height="80"/>
<animations/>
<constraints>
<constraint firstAttribute="height" constant="80" id="l36-59-jyr"/>
</constraints>
</imageView>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="sfC-fv-gcr" secondAttribute="bottom" id="0KI-ei-eq8"/>
@@ -306,7 +294,6 @@
<nil key="simulatedBottomBarMetrics"/>
<tabBar key="tabBar" contentMode="scaleToFill" id="DdC-xv-l7V">
<autoresizingMask key="autoresizingMask"/>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tabBar>
<connections>
@@ -335,7 +322,6 @@
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="D86-dp-qKQ">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<outlet property="dataSource" destination="W3w-jm-aNI" id="Ld3-tu-vGC"/>
@@ -344,10 +330,8 @@
</tableView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="sHk-eY-6TD">
<rect key="frame" x="290" y="184" width="20" height="20"/>
<animations/>
</activityIndicatorView>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="D86-dp-qKQ" secondAttribute="trailing" id="0cJ-BC-Zme"/>
@@ -459,7 +443,6 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="rBt-UI-PaQ">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
@@ -483,7 +466,6 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="F2p-n3-YGV">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
@@ -507,7 +489,6 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="udE-Dh-c2s">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
@@ -531,7 +512,6 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="m4n-ca-AbO">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
@@ -623,7 +603,6 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="6uo-JX-xvr">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
+82 -33
View File
@@ -181,20 +181,24 @@ class ImageCache {
if imageDownloadState == MessageDownloadState.Downloaded.rawValue {
if !fileName.isEmpty {
if
let imageFileURL = NSFileManager.yepMessageImageURLWithName(fileName),
let image = UIImage(contentsOfFile: imageFileURL.path!) {
let messageImage = image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
self.cache.setObject(messageImage, forKey: imageKey)
dispatch_async(dispatch_get_main_queue()) {
completion(loadingProgress: 1.0, image: messageImage)
}
return
if !fileName.isEmpty, let imageFileURL = NSFileManager.yepMessageImageURLWithName(fileName), image = UIImage(contentsOfFile: imageFileURL.path!) {
let messageImage = image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
self.cache.setObject(messageImage, forKey: imageKey)
dispatch_async(dispatch_get_main_queue()) {
completion(loadingProgress: 1.0, image: messageImage)
}
return
} else {
//
if let message = messageWithMessageID(messageID, inRealm: realm) {
let _ = try? realm.write {
message.downloadState = MessageDownloadState.NoDownload.rawValue
}
}
}
}
@@ -211,28 +215,73 @@ class ImageCache {
if let message = messageWithMessageID(messageID, inRealm: realm) {
let mediaType = message.mediaType
func doDownloadAttachmentsOfMessage(message: Message) {
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { progress in
dispatch_async(dispatch_get_main_queue()) {
completion(loadingProgress: progress, image: nil)
}
let mediaType = message.mediaType
}, imageFinished: { image in
let messageImage = image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
self.cache.setObject(messageImage, forKey: imageKey)
dispatch_async(dispatch_get_main_queue()) {
if mediaType == MessageMediaType.Image.rawValue {
completion(loadingProgress: 1.0, image: messageImage)
} else { //
completion(loadingProgress: 1.5, image: messageImage)
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { progress, image in
dispatch_async(dispatch_get_main_queue()) {
completion(loadingProgress: progress, image: image)
}
}
})
}, imageTransform: { image in
return image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
}, imageFinished: { image in
let messageImage = image.bubbleImageWithTailDirection(tailDirection, size: size).decodedImage()
self.cache.setObject(messageImage, forKey: imageKey)
dispatch_async(dispatch_get_main_queue()) {
if mediaType == MessageMediaType.Image.rawValue {
completion(loadingProgress: 1.0, image: messageImage)
} else { //
completion(loadingProgress: 1.5, image: messageImage)
}
}
})
}
//
if message.attachmentExpiresUnixTime < (NSDate().timeIntervalSince1970 + (60 * 60 * 24)) {
refreshAttachmentWithID(message.attachmentID, failureHandler: nil, completion: { newAttachmentInfo in
//println("newAttachmentInfo: \(newAttachmentInfo)")
guard let realm = try? Realm() else {
return
}
if let message = messageWithMessageID(messageID, inRealm: realm) {
if let fileInfo = newAttachmentInfo["file"] as? JSONDictionary {
realm.beginWrite()
if let attachmentExpiresUnixTime = fileInfo["expires_at"] as? NSTimeInterval {
message.attachmentExpiresUnixTime = attachmentExpiresUnixTime
}
if let URLString = fileInfo["url"] as? String {
message.attachmentURLString = URLString
}
if let URLString = fileInfo["thumb_url"] as? String {
message.thumbnailURLString = URLString
}
let _ = try? realm.commitWrite()
doDownloadAttachmentsOfMessage(message)
}
}
})
} else {
doDownloadAttachmentsOfMessage(message)
}
} else {
dispatch_async(dispatch_get_main_queue()) {
+3 -1
View File
@@ -193,7 +193,9 @@ class YepConfig {
static let mediaPreferredHeight: CGFloat = Ruler.iPhoneHorizontal(208, 244, 270).value
static let mediaMinWidth: CGFloat = 60
static let mediaMinHeight: CGFloat = 30
static let mediaMinHeight: CGFloat = 45
static let imageMaxWidth: CGFloat = Ruler.iPhoneHorizontal(230, 260, 300).value
static let centerXOffset: CGFloat = 4
+58 -1
View File
@@ -51,7 +51,7 @@ func cancel(cancelableTask: CancelableTask?) {
func unregisterThirdPartyPush() {
dispatch_async(dispatch_get_main_queue()) {
APService.setAlias(nil, callbackSelector: nil, object: nil)
JPUSHService.setAlias(nil, callbackSelector: nil, object: nil)
UIApplication.sharedApplication().applicationIconBadgeNumber = 0
}
}
@@ -115,4 +115,61 @@ func cleanDiskCacheFolder() {
}
}
}
extension UIImage {
class func imageWithColor(color: UIColor) -> UIImage {
let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
UIGraphicsBeginImageContext(rect.size)
let context = UIGraphicsGetCurrentContext()
CGContextSetFillColorWithColor(context, color.CGColor)
CGContextFillRect(context, rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
extension UINavigationBar {
func hideBottomHairline() {
let navigationBarImageView = hairlineImageViewInNavigationBar(self)
navigationBarImageView?.hidden = true
}
func showBottomHairline() {
let navigationBarImageView = hairlineImageViewInNavigationBar(self)
navigationBarImageView?.hidden = false
}
func changeBottomHairImage() {
}
private func hairlineImageViewInNavigationBar(view: UIView) -> UIImageView? {
if let view = view as? UIImageView where view.bounds.height <= 1.0 {
return view
}
for subview in view.subviews {
if let imageView = hairlineImageViewInNavigationBar(subview) {
return imageView
}
}
return nil
}
}
func GoogleAnalyticsTrackView(name: String) {
}
func GoogleAnalyticsTrackEvent(action: String, label: String, value: NSNumber) {
}
func GoogleAnalyticsTrackSocial(network: String, action: String, target: String) {
}
+52
View File
@@ -0,0 +1,52 @@
//
// CGImage+Yep.swift
// Yep
//
// Created by nixzhu on 16/1/27.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
extension CGImage {
var yep_extendedCanvasCGImage: CGImage {
let width = CGImageGetWidth(self)
let height = CGImageGetHeight(self)
guard width > 0 && height > 0 else {
return self
}
func hasAlpha() -> Bool {
let alpha = CGImageGetAlphaInfo(self)
switch alpha {
case .First, .Last, .PremultipliedFirst, .PremultipliedLast:
return true
default:
return false
}
}
var bitmapInfo = CGBitmapInfo.ByteOrder32Little.rawValue
if hasAlpha() {
bitmapInfo |= CGImageAlphaInfo.PremultipliedFirst.rawValue
} else {
bitmapInfo |= CGImageAlphaInfo.NoneSkipFirst.rawValue
}
guard let context = CGBitmapContextCreate(nil, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo) else {
return self
}
CGContextDrawImage(context, CGRect(x: 0, y: 0, width: width, height: height), self)
guard let newCGImage = CGBitmapContextCreateImage(context) else {
return self
}
return newCGImage
}
}
+31
View File
@@ -0,0 +1,31 @@
//
// CGSize+Yep.swift
// Yep
//
// Created by nixzhu on 16/1/19.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
extension CGSize {
func yep_ensureMinWidthOrHeight(value: CGFloat) -> CGSize {
if width > height {
if height < value {
let ratio = height / value
return CGSize(width: floor(width / ratio), height: value)
}
} else {
if width < value {
let ratio = width / value
return CGSize(width: value, height: floor(height / ratio))
}
}
return self
}
}
+40
View File
@@ -96,5 +96,45 @@ extension NSURL {
return nil
}
enum AppleOnlineStoreHost: String {
case iTunesLong = "itunes.apple.com"
case iTunesShort = "itun.es"
case AppStoreShort = "appsto.re"
}
var yep_isAppleiTunesURL: Bool {
guard let host = host, _ = AppleOnlineStoreHost(rawValue: host) else {
return false
}
return true
}
var yep_appleAllianceURL: NSURL {
guard self.yep_isAppleiTunesURL else {
return self
}
guard let URLComponents = NSURLComponents(URL: self, resolvingAgainstBaseURL: false) else {
return self
}
let queryItem = NSURLQueryItem(name: "at", value: "1010l9k7")
if URLComponents.queryItems == nil {
URLComponents.queryItems = [queryItem]
} else {
URLComponents.queryItems?.append(queryItem)
}
guard let resultURL = URLComponents.URL else {
return self
}
return resultURL
}
}
@@ -17,6 +17,7 @@ extension PHImageRequestOptions {
options.version = .Current
options.deliveryMode = .HighQualityFormat
options.resizeMode = .Exact
options.networkAccessAllowed = true
return options
}
+82 -1
View File
@@ -37,17 +37,52 @@ extension String {
return stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
}
var yep_removeAllNewLines: String {
return self.componentsSeparatedByCharactersInSet(NSCharacterSet.newlineCharacterSet()).joinWithSeparator("")
}
}
extension String {
func yep_rangeFromNSRange(nsRange: NSRange) -> Range<Index>? {
let from16 = utf16.startIndex.advancedBy(nsRange.location, limit: utf16.endIndex)
let to16 = from16.advancedBy(nsRange.length, limit: utf16.endIndex)
guard let from = String.Index(from16, within: self),
let to = String.Index(to16, within: self) else {
return nil
}
return from ..< to
}
func yep_NSRangeFromRange(range: Range<Index>) -> NSRange {
let utf16view = self.utf16
let from = String.UTF16View.Index(range.startIndex, within: utf16view)
let to = String.UTF16View.Index(range.endIndex, within: utf16view)
return NSMakeRange(utf16view.startIndex.distanceTo(from), from.distanceTo(to))
}
}
extension String {
func yep_mentionWordInIndex(index: Int) -> (wordString: String, mentionWordRange: Range<Index>)? {
//println("startIndex: \(startIndex), endIndex: \(endIndex), index: \(index), length: \((self as NSString).length), count: \(self.characters.count)")
guard index > 0 else {
return nil
}
let index = startIndex.advancedBy(index)
let nsRange = NSMakeRange(index, 0)
guard let range = self.yep_rangeFromNSRange(nsRange) else {
return nil
}
let index = range.startIndex
var wordString: String?
var wordRange: Range<Index>?
@@ -85,3 +120,49 @@ extension String {
}
}
extension String {
var yep_embeddedURLs: [NSURL] {
guard let detector = try? NSDataDetector(types: NSTextCheckingType.Link.rawValue) else {
return []
}
var URLs = [NSURL]()
detector.enumerateMatchesInString(self, options: [], range: NSMakeRange(0, (self as NSString).length)) { result, flags, stop in
if let URL = result?.URL {
URLs.append(URL)
}
}
return URLs
}
var yep_firstImageURL: NSURL? {
let URLs = yep_embeddedURLs
guard !URLs.isEmpty else {
return nil
}
let imageExtentions = [
"png",
"jpg",
"jpeg",
]
for URL in URLs {
if let pathExtension = URL.pathExtension?.lowercaseString {
if imageExtentions.contains(pathExtension) {
return URL
}
}
}
return nil
}
}
+48
View File
@@ -8,6 +8,8 @@
import UIKit
import Ruler
import ImageIO
import MobileCoreServices
extension UIImage {
@@ -572,3 +574,49 @@ extension UIImage {
}
}
// MARK: Progressive
extension UIImage {
var yep_progressiveImage: UIImage? {
guard let cgImage = CGImage else {
return nil
}
let data = NSMutableData()
guard let distination = CGImageDestinationCreateWithData(data, kUTTypeJPEG, 1, nil) else {
return nil
}
let jfifProperties = [
kCGImagePropertyJFIFIsProgressive as String: kCFBooleanTrue as Bool,
kCGImagePropertyJFIFXDensity as String: 72,
kCGImagePropertyJFIFYDensity as String: 72,
kCGImagePropertyJFIFDensityUnit as String: 1,
]
let properties = [
kCGImageDestinationLossyCompressionQuality as String: 0.9,
kCGImagePropertyJFIFDictionary as String: jfifProperties,
]
CGImageDestinationAddImage(distination, cgImage, properties)
guard CGImageDestinationFinalize(distination) else {
return nil
}
guard data.length > 0 else {
return nil
}
guard let progressiveImage = UIImage(data: data) else {
return nil
}
return progressiveImage
}
}
+12
View File
@@ -26,6 +26,8 @@ let feedSortStyleKey = "feedSortStyle"
let latitudeShiftKey = "latitudeShift"
let longitudeShiftKey = "longitudeShift"
let userLocationNameKey = "userLocationName"
struct Listener<T>: Hashable {
let name: String
@@ -118,6 +120,7 @@ class YepUserDefaults {
feedSortStyle.removeAllListeners()
latitudeShift.removeAllListeners()
longitudeShift.removeAllListeners()
userLocationName.removeAllListeners()
defaults.removeObjectForKey(v1AccessTokenKey)
defaults.removeObjectForKey(userIDKey)
@@ -132,6 +135,7 @@ class YepUserDefaults {
defaults.removeObjectForKey(feedSortStyleKey)
defaults.removeObjectForKey(latitudeShiftKey)
defaults.removeObjectForKey(longitudeShiftKey)
defaults.removeObjectForKey(userLocationNameKey)
defaults.synchronize()
}
@@ -339,6 +343,14 @@ class YepUserDefaults {
defaults.setObject(longitudeShift, forKey: longitudeShiftKey)
}
}()
static var userLocationName: Listenable<String?> = {
let userLocationName = defaults.stringForKey(userLocationNameKey)
return Listenable<String?>(userLocationName) { userLocationName in
defaults.setObject(userLocationName, forKey: userLocationNameKey)
}
}()
}
+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "bottomLine.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

@@ -0,0 +1,12 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "icon_pin_mini_gray.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,26 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "url_container_left_background.pdf",
"resizing" : {
"mode" : "9-part",
"center" : {
"mode" : "tile",
"width" : 1,
"height" : 1
},
"cap-insets" : {
"bottom" : 5,
"top" : 5,
"right" : 5,
"left" : 2
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,26 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "url_container_right_background.pdf",
"resizing" : {
"mode" : "9-part",
"center" : {
"mode" : "tile",
"width" : 1,
"height" : 1
},
"cap-insets" : {
"bottom" : 5,
"top" : 5,
"right" : 10,
"left" : 9
}
}
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+1 -1
View File
@@ -40,7 +40,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>217</string>
<string>232</string>
<key>Fabric</key>
<dict>
<key>APIKey</key>
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>APS_FOR_PRODUCTION</key>
<string>0</string>
<key>CHANNEL</key>
<string>AppStore</string>
<key>APP_KEY</key>
<string>e521aa97cd4cd4eba5b73669</string>
</dict>
</plist>
+65
View File
@@ -0,0 +1,65 @@
//
// ConcurrentOperation.swift
// Yep
//
// Created by nixzhu on 16/1/22.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
class ConcurrentOperation: NSOperation {
enum State: String {
case Ready, Executing, Finished
private var keyPath: String {
return "is" + rawValue
}
}
var state = State.Ready {
willSet {
willChangeValueForKey(newValue.keyPath)
willChangeValueForKey(state.keyPath)
}
didSet {
didChangeValueForKey(oldValue.keyPath)
didChangeValueForKey(state.keyPath)
}
}
}
extension ConcurrentOperation {
override var ready: Bool {
return super.ready && state == .Ready
}
override var executing: Bool {
return state == .Executing
}
override var finished: Bool {
return state == .Finished
}
override var asynchronous: Bool {
return true
}
override func start() {
if cancelled {
state = .Finished
return
}
main()
state = .Executing
}
override func cancel() {
state = .Finished
}
}
@@ -0,0 +1,40 @@
//
// UploadAttachmentOperation.swift
// Yep
//
// Created by nixzhu on 16/1/22.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
class UploadAttachmentOperation: ConcurrentOperation {
private let uploadAttachment: UploadAttachment
var uploadErrorMessage: String?
var uploadAttachmentID: String?
init(uploadAttachment: UploadAttachment) {
self.uploadAttachment = uploadAttachment
super.init()
}
override func main() {
tryUploadAttachment(uploadAttachment, failureHandler: { [weak self] (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
self?.uploadErrorMessage = errorMessage
self?.state = .Finished
}, completion: { [weak self] uploadAttachmentID in
self?.uploadAttachmentID = uploadAttachmentID
self?.state = .Finished
})
}
}
+20
View File
@@ -0,0 +1,20 @@
//
// OpenGraphInfoType.swift
// Yep
//
// Created by nixzhu on 16/1/15.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import Foundation
protocol OpenGraphInfoType {
var URL: NSURL { get }
var siteName: String { get }
var title: String { get }
var infoDescription: String { get }
var thumbnailImageURLString: String { get }
}
+119 -7
View File
@@ -189,6 +189,14 @@ class User: Object {
return false
}
var chatCellCompositedName: String {
if username.isEmpty {
return nickname
} else {
return "\(nickname) @\(username)"
}
}
//
func cascadeDeleteInRealm(realm: Realm) {
@@ -511,20 +519,23 @@ class Message: Object {
dynamic var mediaType: Int = MessageMediaType.Text.rawValue
dynamic var textContent: String = ""
var textContentToShow: String {
if deletedByCreator {
return NSLocalizedString("Recalled by creator.", comment: "")
} else {
return textContent
}
var recalledTextContent: String {
let nickname = fromFriend?.nickname ?? ""
return String(format: NSLocalizedString("%@ recalled a message.", comment: ""), nickname)
}
dynamic var openGraphDetected: Bool = false
dynamic var openGraphInfo: OpenGraphInfo?
dynamic var coordinate: Coordinate?
dynamic var attachmentURLString: String = ""
dynamic var localAttachmentName: String = ""
dynamic var thumbnailURLString: String = ""
dynamic var localThumbnailName: String = ""
dynamic var attachmentID: String = ""
dynamic var attachmentExpiresUnixTime: NSTimeInterval = NSDate().timeIntervalSince1970 + (6 * 60 * 60 * 24) // 6s371
var nicknameWithTextContent: String {
if let nickname = fromFriend?.nickname {
@@ -583,6 +594,15 @@ class Message: Object {
realm.delete(mediaMetaData)
}
// openGraphInfo
if let openGraphInfo = openGraphInfo {
if openGraphInfo.feeds.isEmpty {
if openGraphInfo.messages.count == 1, let first = openGraphInfo.messages.first where first == self {
realm.delete(openGraphInfo)
}
}
}
switch mediaType {
case MessageMediaType.Image.rawValue:
@@ -615,7 +635,7 @@ class Message: Object {
realm.delete(socialWork)
}
default:
break // TODO: if have other message media need to delete
}
@@ -808,6 +828,51 @@ class FeedLocation: Object {
dynamic var coordinate: Coordinate?
}
class OpenGraphInfo: Object {
dynamic var URLString: String = ""
dynamic var siteName: String = ""
dynamic var title: String = ""
dynamic var infoDescription: String = ""
dynamic var thumbnailImageURLString: String = ""
var messages: [Message] {
return linkingObjects(Message.self, forProperty: "openGraphInfo")
}
var feeds: [Feed] {
return linkingObjects(Feed.self, forProperty: "openGraphInfo")
}
override class func primaryKey() -> String? {
return "URLString"
}
override class func indexedProperties() -> [String] {
return ["URLString"]
}
convenience init(URLString: String, siteName: String, title: String, infoDescription: String, thumbnailImageURLString: String) {
self.init()
self.URLString = URLString
self.siteName = siteName
self.title = title
self.infoDescription = infoDescription
self.thumbnailImageURLString = thumbnailImageURLString
}
class func withURLString(URLString: String, inRealm realm: Realm) -> OpenGraphInfo? {
return realm.objects(OpenGraphInfo).filter("URLString = %@", URLString).first
}
}
extension OpenGraphInfo: OpenGraphInfoType {
var URL: NSURL {
return NSURL(string: URLString)!
}
}
class Feed: Object {
dynamic var feedID: String = ""
@@ -826,6 +891,7 @@ class Feed: Object {
dynamic var socialWork: MessageSocialWork?
dynamic var audio: FeedAudio?
dynamic var location: FeedLocation?
dynamic var openGraphInfo: OpenGraphInfo?
dynamic var skill: UserSkill?
@@ -874,6 +940,15 @@ class Feed: Object {
realm.delete(location)
}
// openGraphInfo
if let openGraphInfo = openGraphInfo {
if openGraphInfo.messages.isEmpty {
if openGraphInfo.feeds.count == 1, let first = openGraphInfo.messages.first where first == self {
realm.delete(openGraphInfo)
}
}
}
realm.delete(self)
}
}
@@ -911,6 +986,31 @@ class OfflineJSON: Object {
}
}
class UserLocationName: Object {
dynamic var userID: String = ""
dynamic var locationName: String = ""
override class func primaryKey() -> String? {
return "userID"
}
override class func indexedProperties() -> [String] {
return ["userID"]
}
convenience init(userID: String, locationName: String) {
self.init()
self.userID = userID
self.locationName = locationName
}
class func withUserID(userID: String, inRealm realm: Realm) -> UserLocationName? {
return realm.objects(UserLocationName).filter("userID = %@", userID).first
}
}
// MARK: Helpers
func normalFriends() -> Results<User> {
@@ -1229,6 +1329,18 @@ func saveFeedWithDiscoveredFeed(feedData: DiscoveredFeed, group: Group, inRealm
feedLocation.coordinate = coordinate
feed.location = feedLocation
case .URL(let info):
guard feed.openGraphInfo == nil else {
break
}
let openGraphInfo = OpenGraphInfo(URLString: info.URL.absoluteString, siteName: info.siteName, title: info.title, infoDescription: info.infoDescription, thumbnailImageURLString: info.thumbnailImageURLString)
realm.add(openGraphInfo, update: true)
feed.openGraphInfo = openGraphInfo
}
}
}
+203 -79
View File
@@ -20,6 +20,8 @@ struct OpenGraph {
var kind: Kind = .Default
var URL: NSURL
var siteName: String?
var title: String?
@@ -29,6 +31,20 @@ struct OpenGraph {
var previewVideoURLString: String?
var previewAudioURLString: String?
var isValid: Bool {
guard
let siteName = siteName?.trimming(.WhitespaceAndNewline) where !siteName.isEmpty,
let title = title?.trimming(.WhitespaceAndNewline) where !title.isEmpty,
let description = description?.trimming(.WhitespaceAndNewline) where !description.isEmpty,
let _ = previewImageURLString
else {
return false
}
return true
}
struct AppleMusic {
var artistName: String?
@@ -76,14 +92,15 @@ struct OpenGraph {
}
var appleEBook: AppleEBook?
init() {
init(URL: NSURL) {
self.URL = URL.yep_appleAllianceURL
}
static func fromHTMLString(HTMLString: String) -> OpenGraph? {
static func fromHTMLString(HTMLString: String, forURL URL: NSURL) -> OpenGraph? {
if let doc = Kanna.HTML(html: HTMLString, encoding: NSUTF8StringEncoding) {
var openGraph = OpenGraph()
var openGraph = OpenGraph(URL: URL)
if let metaSet = doc.head?.css("meta") {
@@ -105,6 +122,48 @@ struct OpenGraph {
openGraph.description = openGraphInfo["og:description"]
openGraph.previewImageURLString = openGraphInfo["og:image"]
// `og:`
if openGraph.siteName == nil {
openGraph.siteName = URL.host
}
if openGraph.title == nil {
if let title = doc.head?.css("title").first?.text where !title.isEmpty {
openGraph.title = title
}
}
if openGraph.description == nil {
for meta in metaSet {
if let name = meta["name"]?.lowercaseString {
if name == "description" {
if let description = meta["content"] where !description.isEmpty {
openGraph.description = description
break
}
}
}
}
}
if openGraph.previewImageURLString == nil {
openGraph.previewImageURLString = HTMLString.yep_firstImageURL?.absoluteString
}
// description
if openGraph.description == nil {
let firstParagraph = doc.body?.css("p").first?.text
openGraph.description = firstParagraph
}
//
openGraph.siteName = openGraph.siteName?.yep_removeAllNewLines
openGraph.title = openGraph.title?.yep_removeAllNewLines
openGraph.description = openGraph.description?.yep_removeAllNewLines
}
return openGraph
@@ -114,9 +173,66 @@ struct OpenGraph {
}
}
func openGraphWithURLString(URLString: String, failureHandler: ((Reason, String?) -> Void)?, completion: OpenGraph -> Void) {
// ref http://a4esl.org/c/charset.html
private enum WeirdCharset: String {
// China
case GB2312 = "GB2312"
case GBK = "GBK"
case GB18030 = "GB18030"
Alamofire.request(.GET, URLString, parameters: nil, encoding: .URL).responseString { response in
// Taiwan, HongKong ...
case BIG5 = "BIG5"
case BIG5HKSCS = "BIG5-HKSCS"
// Korean
case EUCKR = "EUC-KR"
}
private func getUTF8HTMLStringFromHTMLString(HTMLString: String, withData data: NSData) -> String {
let pattern = "charset=([A-Za-z0-9\\-]+)"
guard let
charsetRegex = try? NSRegularExpression(pattern: pattern, options: [.CaseInsensitive]),
result = charsetRegex.firstMatchInString(HTMLString, options: [.ReportCompletion], range: NSMakeRange(0, (HTMLString as NSString).length))
else {
return HTMLString
}
let charsetStringRange = result.rangeAtIndex(1)
let charsetString = (HTMLString as NSString).substringWithRange(charsetStringRange).uppercaseString
guard let weirdCharset = WeirdCharset(rawValue: charsetString) else {
return HTMLString
}
let encoding: NSStringEncoding
switch weirdCharset {
case .GB2312, .GBK, .GB18030:
let china = CFStringEncodings.GB_18030_2000
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(china.rawValue))
case .BIG5, .BIG5HKSCS:
let taiwan = CFStringEncodings.Big5_HKSCS_1999
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(taiwan.rawValue))
case .EUCKR:
let korean = CFStringEncodings.EUC_KR
encoding = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(korean.rawValue))
}
guard let newHTMLString = String(data: data, encoding: encoding) else {
return HTMLString
}
return newHTMLString
}
func openGraphWithURL(URL: NSURL, failureHandler: ((Reason, String?) -> Void)?, completion: OpenGraph -> Void) {
Alamofire.request(.GET, URL.absoluteString, parameters: nil, encoding: .URL).responseString(encoding: NSUTF8StringEncoding, completionHandler: { response in
let error = response.result.error
@@ -131,123 +247,131 @@ func openGraphWithURLString(URLString: String, failureHandler: ((Reason, String?
return
}
if let HTMLString = response.result.value {
println("\n openGraphWithURLString: \(URLString)\n\(HTMLString)")
if let HTMLString = response.result.value, data = response.data {
//println("\n openGraphWithURLString: \(URL)\n\(HTMLString)")
if let openGraph = OpenGraph.fromHTMLString(HTMLString) {
// 使
var finalURL = URL
if let _finalURL = response.response?.URL {
finalURL = _finalURL
}
//
let newHTMLString = getUTF8HTMLStringFromHTMLString(HTMLString, withData: data)
//println("newHTMLString: \(newHTMLString)")
if let openGraph = OpenGraph.fromHTMLString(newHTMLString, forURL: finalURL) {
completion(openGraph)
/*
var openGraph = openGraph
if let URL = response.response?.URL, host = URL.host {
guard let URL = response.response?.URL, host = URL.host, _ = NSURL.AppleOnlineStoreHost(rawValue: host) else {
completion(openGraph)
switch host {
return
}
case "itunes.apple.com":
if let lookupID = URL.yep_iTunesArtworkID {
iTunesLookupWithID(lookupID, failureHandler: nil, completion: { artworkInfo in
//println("iTunesLookupWithID: \(lookupID), \(artworkInfo)")
if let lookupID = URL.yep_iTunesArtworkID {
iTunesLookupWithID(lookupID, failureHandler: nil, completion: { artworkInfo in
//println("iTunesLookupWithID: \(lookupID), \(artworkInfo)")
if let kind = artworkInfo["kind"] as? String {
if let kind = artworkInfo["kind"] as? String {
switch kind.lowercaseString {
switch kind.lowercaseString {
case "song":
openGraph.kind = .AppleMusic
case "song":
openGraph.kind = .AppleMusic
openGraph.previewAudioURLString = artworkInfo["previewUrl"] as? String
openGraph.previewAudioURLString = artworkInfo["previewUrl"] as? String
var appleMusic = OpenGraph.AppleMusic()
var appleMusic = OpenGraph.AppleMusic()
appleMusic.artistName = artworkInfo["artistName"] as? String
appleMusic.artistName = artworkInfo["artistName"] as? String
appleMusic.artworkURLString30 = artworkInfo["artworkUrl30"] as? String
appleMusic.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleMusic.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMusic.artworkURLString30 = artworkInfo["artworkUrl30"] as? String
appleMusic.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleMusic.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMusic.collectionName = artworkInfo["collectionName"] as? String
appleMusic.collectionViewURLString = artworkInfo["collectionViewUrl"] as? String
appleMusic.collectionName = artworkInfo["collectionName"] as? String
appleMusic.collectionViewURLString = artworkInfo["collectionViewUrl"] as? String
appleMusic.trackTimeMillis = artworkInfo["trackTimeMillis"] as? Int
appleMusic.trackViewURLString = artworkInfo["trackViewUrl"] as? String
appleMusic.trackTimeMillis = artworkInfo["trackTimeMillis"] as? Int
appleMusic.trackViewURLString = artworkInfo["trackViewUrl"] as? String
openGraph.appleMusic = appleMusic
openGraph.appleMusic = appleMusic
case "feature-movie":
openGraph.kind = .AppleMovie
case "feature-movie":
openGraph.kind = .AppleMovie
openGraph.previewVideoURLString = artworkInfo["previewUrl"] as? String
openGraph.previewVideoURLString = artworkInfo["previewUrl"] as? String
var appleMovie = OpenGraph.AppleMovie()
var appleMovie = OpenGraph.AppleMovie()
appleMovie.artistName = artworkInfo["artistName"] as? String
appleMovie.artistName = artworkInfo["artistName"] as? String
appleMovie.artworkURLString30 = artworkInfo["artworkUrl30"] as? String
appleMovie.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleMovie.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMovie.artworkURLString30 = artworkInfo["artworkUrl30"] as? String
appleMovie.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleMovie.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMovie.shortDescription = artworkInfo["shortDescription"] as? String
appleMovie.longDescription = artworkInfo["longDescription"] as? String
appleMovie.trackTimeMillis = artworkInfo["trackTimeMillis"] as? Int
appleMovie.trackViewURLString = artworkInfo["trackViewUrl"] as? String
appleMovie.shortDescription = artworkInfo["shortDescription"] as? String
appleMovie.longDescription = artworkInfo["longDescription"] as? String
appleMovie.trackTimeMillis = artworkInfo["trackTimeMillis"] as? Int
appleMovie.trackViewURLString = artworkInfo["trackViewUrl"] as? String
openGraph.appleMovie = appleMovie
openGraph.appleMovie = appleMovie
case "ebook":
openGraph.kind = .AppleEBook
case "ebook":
openGraph.kind = .AppleEBook
var appleEBook = OpenGraph.AppleEBook()
var appleEBook = OpenGraph.AppleEBook()
appleEBook.artistName = artworkInfo["artistName"] as? String
appleEBook.artistName = artworkInfo["artistName"] as? String
appleEBook.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleEBook.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleEBook.artworkURLString60 = artworkInfo["artworkUrl60"] as? String
appleEBook.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleEBook.description = artworkInfo["description"] as? String
appleEBook.description = artworkInfo["description"] as? String
appleEBook.trackName = artworkInfo["trackName"] as? String
appleEBook.trackViewURLString = artworkInfo["trackViewUrl"] as? String
appleEBook.trackName = artworkInfo["trackName"] as? String
appleEBook.trackViewURLString = artworkInfo["trackViewUrl"] as? String
openGraph.appleEBook = appleEBook
openGraph.appleEBook = appleEBook
default:
break
}
}
default:
break
}
}
if let collectionType = artworkInfo["collectionType"] as? String {
if let collectionType = artworkInfo["collectionType"] as? String {
switch collectionType.lowercaseString {
switch collectionType.lowercaseString {
case "album":
case "album":
var appleMusic = OpenGraph.AppleMusic()
var appleMusic = OpenGraph.AppleMusic()
appleMusic.artistName = artworkInfo["artistName"] as? String
appleMusic.artistName = artworkInfo["artistName"] as? String
appleMusic.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMusic.artworkURLString160 = artworkInfo["artworkUrl160"] as? String
appleMusic.artworkURLString100 = artworkInfo["artworkUrl100"] as? String
appleMusic.artworkURLString160 = artworkInfo["artworkUrl160"] as? String
appleMusic.collectionType = collectionType
appleMusic.collectionName = artworkInfo["collectionName"] as? String
appleMusic.collectionViewURLString = artworkInfo["collectionViewUrl"] as? String
appleMusic.collectionType = collectionType
appleMusic.collectionName = artworkInfo["collectionName"] as? String
appleMusic.collectionViewURLString = artworkInfo["collectionViewUrl"] as? String
openGraph.appleMusic = appleMusic
openGraph.appleMusic = appleMusic
default:
break
}
}
completion(openGraph)
})
default:
break
}
}
default:
completion(openGraph)
}
})
}
*/
return
}
@@ -258,7 +382,7 @@ func openGraphWithURLString(URLString: String, failureHandler: ((Reason, String?
} else {
defaultFailureHandler(.CouldNotParseJSON, errorMessage: nil)
}
}
})
}
private enum iTunesCountry: String {
+142 -14
View File
@@ -8,6 +8,7 @@
import Foundation
import RealmSwift
import ImageIO
class YepDownloader: NSObject {
@@ -17,7 +18,7 @@ class YepDownloader: NSObject {
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: self, delegateQueue: nil)
return session
}()
}()
private class func updateAttachmentOfMessage(message: Message, withAttachmentFileName attachmentFileName: String, inRealm realm: Realm) {
@@ -47,17 +48,20 @@ class YepDownloader: NSObject {
struct ProgressReporter {
struct Task {
let downloadTask: NSURLSessionDownloadTask
let downloadTask: NSURLSessionDataTask
typealias FinishedAction = NSData -> Void
let finishedAction: FinishedAction
let progress = NSProgress()
let tempData = NSMutableData()
let imageSource = CGImageSourceCreateIncremental(nil)
let imageTransform: (UIImage -> UIImage)?
}
let tasks: [Task]
var finishedTasksCount = 0
typealias ReportProgress = Double -> Void
typealias ReportProgress = (progress: Double, image: UIImage?) -> Void
let reportProgress: ReportProgress?
init(tasks: [Task], reportProgress: ReportProgress?) {
@@ -77,10 +81,10 @@ class YepDownloader: NSObject {
var progressReporters = [ProgressReporter]()
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?) {
downloadAttachmentsOfMessage(message, reportProgress: reportProgress, imageFinished: nil)
downloadAttachmentsOfMessage(message, reportProgress: reportProgress, imageTransform: nil, imageFinished: nil)
}
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?, imageFinished: (UIImage -> Void)?) {
class func downloadAttachmentsOfMessage(message: Message, reportProgress: ProgressReporter.ReportProgress?, imageTransform: (UIImage -> UIImage)?, imageFinished: (UIImage -> Void)?) {
let downloadState = message.downloadState
@@ -91,14 +95,14 @@ class YepDownloader: NSObject {
let messageID = message.messageID
let mediaType = message.mediaType
var attachmentDownloadTask: NSURLSessionDownloadTask?
var attachmentDownloadTask: NSURLSessionDataTask?
var attachmentFinishedAction: ProgressReporter.Task.FinishedAction?
let attachmentURLString = message.attachmentURLString
if !attachmentURLString.isEmpty && message.localAttachmentName.isEmpty, let URL = NSURL(string: attachmentURLString) {
attachmentDownloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
attachmentDownloadTask = sharedDownloader.session.dataTaskWithURL(URL)
attachmentFinishedAction = { data in
@@ -152,7 +156,7 @@ class YepDownloader: NSObject {
}
}
var thumbnailDownloadTask: NSURLSessionDownloadTask?
var thumbnailDownloadTask: NSURLSessionDataTask?
var thumbnailFinishedAction: ProgressReporter.Task.FinishedAction?
if mediaType == MessageMediaType.Video.rawValue {
@@ -161,7 +165,7 @@ class YepDownloader: NSObject {
if !thumbnailURLString.isEmpty && message.localThumbnailName.isEmpty, let URL = NSURL(string: thumbnailURLString) {
thumbnailDownloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
thumbnailDownloadTask = sharedDownloader.session.dataTaskWithURL(URL)
thumbnailFinishedAction = { data in
@@ -196,11 +200,11 @@ class YepDownloader: NSObject {
var tasks: [ProgressReporter.Task] = []
if let attachmentDownloadTask = attachmentDownloadTask, attachmentFinishedAction = attachmentFinishedAction {
tasks.append(ProgressReporter.Task(downloadTask: attachmentDownloadTask, finishedAction: attachmentFinishedAction))
tasks.append(ProgressReporter.Task(downloadTask: attachmentDownloadTask, finishedAction: attachmentFinishedAction, imageTransform: imageTransform))
}
if let thumbnailDownloadTask = thumbnailDownloadTask, thumbnailFinishedAction = thumbnailFinishedAction {
tasks.append(ProgressReporter.Task(downloadTask: thumbnailDownloadTask, finishedAction: thumbnailFinishedAction))
tasks.append(ProgressReporter.Task(downloadTask: thumbnailDownloadTask, finishedAction: thumbnailFinishedAction, imageTransform: imageTransform))
}
if tasks.count > 0 {
@@ -218,8 +222,8 @@ class YepDownloader: NSObject {
class func downloadDataFromURL(URL: NSURL, reportProgress: ProgressReporter.ReportProgress?, finishedAction: ProgressReporter.Task.FinishedAction) {
let downloadTask = sharedDownloader.session.downloadTaskWithURL(URL)
let task = ProgressReporter.Task(downloadTask: downloadTask, finishedAction: finishedAction)
let downloadTask = sharedDownloader.session.dataTaskWithURL(URL)
let task = ProgressReporter.Task(downloadTask: downloadTask, finishedAction: finishedAction, imageTransform: nil)
let progressReporter = ProgressReporter(tasks: [task], reportProgress: reportProgress)
sharedDownloader.progressReporters.append(progressReporter)
@@ -232,6 +236,130 @@ extension YepDownloader: NSURLSessionDelegate {
}
extension YepDownloader: NSURLSessionDataDelegate {
private func reportProgressAssociatedWithDownloadTask(downloadTask: NSURLSessionDataTask, totalBytes: Int64) {
for progressReporter in progressReporters {
for i in 0..<progressReporter.tasks.count {
if downloadTask == progressReporter.tasks[i].downloadTask {
progressReporter.tasks[i].progress.totalUnitCount = totalBytes
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: nil)
return
}
}
}
}
private func reportProgressAssociatedWithDownloadTask(downloadTask: NSURLSessionDataTask, didReceiveData data: NSData) -> Bool {
for progressReporter in progressReporters {
for i in 0..<progressReporter.tasks.count {
if downloadTask == progressReporter.tasks[i].downloadTask {
let didReceiveDataBytes = Int64(data.length)
progressReporter.tasks[i].progress.completedUnitCount += didReceiveDataBytes
progressReporter.tasks[i].tempData.appendData(data)
let progress = progressReporter.tasks[i].progress
let final = progress.completedUnitCount == progress.totalUnitCount
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: nil)
/*
let imageSource = progressReporter.tasks[i].imageSource
let data = progressReporter.tasks[i].tempData
CGImageSourceUpdateData(imageSource, data, final)
var tranformedImage: UIImage?
if let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
let image = UIImage(CGImage: cgImage)
if let imageTransform = progressReporter.tasks[i].imageTransform {
tranformedImage = imageTransform(image)
}
/*
let image = UIImage(CGImage: cgImage.yep_extendedCanvasCGImage)
if progressReporter.totalProgress < 1 {
let blurPercent = CGFloat(1 - progressReporter.totalProgress)
let radius = 5 * blurPercent
let iterations = UInt(10 * blurPercent)
println("radius: \(radius), iterations: \(iterations)")
if let blurredImage = image.blurredImageWithRadius(radius, iterations: iterations, tintColor: UIColor.clearColor()) {
if let imageTransform = progressReporter.tasks[i].imageTransform {
tranformedImage = imageTransform(blurredImage)
/*
if progressReporter.totalProgress > 0.3 {
print("imageTransform")
}*/
}
}
}
*/
}
progressReporter.reportProgress?(progress: progressReporter.totalProgress, image: tranformedImage)
*/
return final
}
}
}
return false
}
private func finishDownloadTask(downloadTask: NSURLSessionDataTask) {
for i in 0..<progressReporters.count {
for j in 0..<progressReporters[i].tasks.count {
if downloadTask == progressReporters[i].tasks[j].downloadTask {
let finishedAction = progressReporters[i].tasks[j].finishedAction
let data = progressReporters[i].tasks[j].tempData
finishedAction(data)
progressReporters[i].finishedTasksCount++
// progressReporter
if progressReporters[i].finishedTasksCount == progressReporters[i].tasks.count {
progressReporters.removeAtIndex(i)
}
return
}
}
}
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
println("YepDownloader begin, expectedContentLength:\(response.expectedContentLength)")
reportProgressAssociatedWithDownloadTask(dataTask, totalBytes: response.expectedContentLength)
completionHandler(.Allow)
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
println("YepDownloader data.length: \(data.length)")
let finish = reportProgressAssociatedWithDownloadTask(dataTask, didReceiveData: data)
if finish {
println("YepDownloader finish")
finishDownloadTask(dataTask)
}
}
}
/*
extension YepDownloader: NSURLSessionDownloadDelegate {
private func handleData(data: NSData, ofDownloadTask downloadTask: NSURLSessionDownloadTask) {
@@ -300,4 +428,4 @@ extension YepDownloader: NSURLSessionDownloadDelegate {
reportProgressAssociatedWithDownloadTask(downloadTask, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
}
}
*/
+1 -1
View File
@@ -162,7 +162,7 @@ public func apiRequest<A>(modifyRequest: NSMutableURLRequest -> (), baseURL: NSU
}
#if DEBUG
println(request.cURLCommandLineWithSession(session))
//println(request.cURLCommandLineWithSession(session))
#endif
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
+65 -1
View File
@@ -12,7 +12,7 @@ import CoreLocation
import Alamofire
#if STAGING
let yepBaseURL = NSURL(string: "https://park-staging.catchchatchina.com")!
let yepBaseURL = NSURL(string: "https://park-staging.catchchatchina.com/api")!
let fayeBaseURL = NSURL(string: "wss://faye-staging.catchchatchina.com/faye")!
#else
let yepBaseURL = NSURL(string: "https://api.soyep.com")!
@@ -2004,6 +2004,8 @@ func unreadMessages(failureHandler failureHandler: ((Reason, String?) -> Void)?,
let parse: JSONDictionary -> [JSONDictionary]? = { data in
//println("unreadMessages data: \(data)")
guard let conversationsData = data["conversations"] as? [JSONDictionary] else {
return nil
}
@@ -2746,6 +2748,25 @@ func deleteMessageFromServer(messageID messageID: String, failureHandler: ((Reas
}
}
func refreshAttachmentWithID(attachmentID: String, failureHandler: ((Reason, String?) -> Void)?, completion: JSONDictionary -> Void) {
let requestParameters = [
"ids": [attachmentID],
]
let parse: JSONDictionary -> JSONDictionary? = { data in
return (data["attachments"] as? [JSONDictionary])?.first
}
let resource = authJsonResource(path: "/v1/attachments/refresh_url", method: .PATCH, requestParameters: requestParameters, parse: parse)
if let failureHandler = failureHandler {
apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: failureHandler, completion: completion)
} else {
apiRequest({_ in}, baseURL: yepBaseURL, resource: resource, failure: defaultFailureHandler, completion: completion)
}
}
// MARK: - Feeds
enum FeedSortStyle: String {
@@ -2959,12 +2980,37 @@ struct DiscoveredFeed: Hashable {
}
}
struct OpenGraphInfo: OpenGraphInfoType {
let URL: NSURL
let siteName: String
let title: String
let infoDescription: String
let thumbnailImageURLString: String
static func fromJSONDictionary(json: JSONDictionary) -> OpenGraphInfo? {
guard let
URLString = json["url"] as? String,
URL = NSURL(string: URLString),
siteName = json["site_name"] as? String,
title = json["title"] as? String,
infoDescription = json["description"] as? String,
thumbnailImageURLString = json["image_url"] as? String else {
return nil
}
return OpenGraphInfo(URL: URL, siteName: siteName, title: title, infoDescription: infoDescription, thumbnailImageURLString: thumbnailImageURLString)
}
}
enum Attachment {
case Images([DiscoveredAttachment])
case Github(GithubRepo)
case Dribbble(DribbbleShot)
case Audio(AudioInfo)
case Location(LocationInfo)
case URL(OpenGraphInfo)
}
let attachment: Attachment?
@@ -3042,6 +3088,14 @@ struct DiscoveredFeed: Hashable {
switch kind {
case .URL:
if let
openGraphInfosData = feedInfo["attachments"] as? [JSONDictionary],
openGraphInfoDict = openGraphInfosData.first,
openGraphInfo = DiscoveredFeed.OpenGraphInfo.fromJSONDictionary(openGraphInfoDict) {
attachment = .URL(openGraphInfo)
}
case .Image:
let attachmentsData = feedInfo["attachments"] as? [JSONDictionary]
@@ -3220,6 +3274,7 @@ func feedsOfUser(userID: String, pageIndex: Int, perPage: Int, failureHandler: (
enum FeedKind: String {
case Text = "text"
case URL = "web_page"
case Image = "image"
case Video = "video"
case Audio = "audio"
@@ -3252,6 +3307,15 @@ enum FeedKind: String {
return false
}
}
var needParseOpenGraph: Bool {
switch self {
case .Text:
return true
default:
return false
}
}
}
func createFeedWithKind(kind: FeedKind, message: String, attachments: [JSONDictionary]?, coordinate: CLLocationCoordinate2D?, skill: Skill?, allowComment: Bool, failureHandler: ((Reason, String?) -> Void)?, completion: JSONDictionary -> Void) {
+7 -11
View File
@@ -864,20 +864,16 @@ func recordMessageWithMessageID(messageID: String, detailInfo messageInfo: JSOND
for attachmentInfo in attachments {
// S3: normal file
// if let
// normalFileInfo = attachmentInfo["file"] as? JSONDictionary,
// fileURLString = normalFileInfo["url"] as? String,
// kind = attachmentInfo["kind"] as? String {
// if kind == "thumbnail" {
// message.thumbnailURLString = fileURLString
// } else {
// message.attachmentURLString = fileURLString
// }
// }
if let attachmentID = attachmentInfo["id"] as? String {
message.attachmentID = attachmentID
}
if let fileInfo = attachmentInfo["file"] as? JSONDictionary {
if let attachmentExpiresUnixTime = fileInfo["expires_at"] as? NSTimeInterval {
message.attachmentExpiresUnixTime = attachmentExpiresUnixTime
}
if let URLString = fileInfo["url"] as? String {
message.attachmentURLString = URLString
}
@@ -163,6 +163,10 @@ class ContactsViewController: BaseViewController {
vc.setBackButtonWithTitle()
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
}
// MARK: - UITableViewDataSource, UITableViewDelegate
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--Conversation-->
@@ -14,11 +14,11 @@
<viewControllerLayoutGuide type="bottom" id="UbN-sl-QUT"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="846-Dd-AxI">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Eug-oZ-gjQ">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<collectionViewLayout key="collectionViewLayout" id="sS6-P1-xbg" customClass="ConversationLayout" customModule="Yep" customModuleProvider="target"/>
<cells/>
@@ -28,16 +28,16 @@
</connections>
</collectionView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="H35-sK-viT">
<rect key="frame" x="0.0" y="270" width="600" height="100"/>
<rect key="frame" x="0.0" y="337" width="375" height="100"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Swipe Up to Cancel" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="erl-b0-CMQ">
<rect key="frame" x="234" y="62" width="132.5" height="18"/>
<rect key="frame" x="121" y="62" width="132.5" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" red="1" green="0.0" blue="0.23529411759999999" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="swipe_up" translatesAutoresizingMaskIntoConstraints="NO" id="WVE-v0-1iA">
<rect key="frame" x="287" y="44" width="25" height="8"/>
<rect key="frame" x="174" y="44" width="25" height="8"/>
<constraints>
<constraint firstAttribute="height" constant="8" id="Q0N-bH-k2Y"/>
<constraint firstAttribute="width" constant="25" id="gpR-7R-3D5"/>
@@ -54,11 +54,11 @@
</constraints>
</view>
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Uh-E6-Vss" customClass="MessageToolbar" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="390" width="600" height="50"/>
<rect key="frame" x="0.0" y="457" width="375" height="50"/>
<items/>
</toolbar>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="fP4-ji-yWc">
<rect key="frame" x="290" y="140" width="20" height="20"/>
<rect key="frame" x="177" y="140" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
@@ -73,10 +73,12 @@
<constraint firstItem="H35-sK-viT" firstAttribute="width" secondItem="9Uh-E6-Vss" secondAttribute="width" id="Thq-w4-dFj"/>
<constraint firstItem="9Uh-E6-Vss" firstAttribute="top" secondItem="H35-sK-viT" secondAttribute="bottom" constant="20" id="XB3-oz-ltH"/>
<constraint firstAttribute="trailing" secondItem="9Uh-E6-Vss" secondAttribute="trailing" id="sm6-8A-Lpk"/>
<constraint firstItem="9Uh-E6-Vss" firstAttribute="top" relation="greaterThanOrEqual" secondItem="3yg-SJ-CR8" secondAttribute="bottom" id="twv-t0-rmT"/>
<constraint firstAttribute="bottom" secondItem="9Uh-E6-Vss" secondAttribute="bottom" constant="160" id="xp5-FH-KYE"/>
<constraint firstAttribute="trailing" secondItem="Eug-oZ-gjQ" secondAttribute="trailing" id="zUR-03-3Fr"/>
</constraints>
</view>
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina47"/>
<connections>
<outlet property="activityIndicator" destination="fP4-ji-yWc" id="0aI-A0-ptB"/>
<outlet property="conversationCollectionView" destination="Eug-oZ-gjQ" id="Cvy-ke-csl"/>
@@ -94,6 +96,7 @@
<segue destination="1y0-IN-47w" kind="show" identifier="showProfileFromFeedView" id="eCp-94-guS"/>
<segue destination="bhi-Zn-U0N" kind="show" identifier="showProfile" id="HJ6-Sc-tfw"/>
<segue destination="9xD-Rm-9US" kind="presentation" identifier="presentNewFeed" id="e4k-KG-exv"/>
<segue destination="8Oq-Nk-eYq" kind="show" identifier="showProfileWithUsername" id="l5Y-gH-p57"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="zLf-XS-3py" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -114,7 +117,7 @@
<viewControllerPlaceholder storyboardName="PickLocation" id="f8Q-Nw-run" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="axC-MU-2PW" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-549" y="2288"/>
<point key="canvasLocation" x="-629" y="2344"/>
</scene>
<!--MessageMediaViewController-->
<scene sceneID="mD6-JY-R4j">
@@ -130,7 +133,7 @@
<viewControllerPlaceholder storyboardName="NewFeed" id="9xD-Rm-9US" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="mtb-Uy-ezu" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-399.5" y="2288"/>
<point key="canvasLocation" x="-455.5" y="2344"/>
</scene>
<!--ProfileViewController-->
<scene sceneID="ZHm-QA-IAa">
@@ -140,6 +143,14 @@
</objects>
<point key="canvasLocation" x="8" y="1699"/>
</scene>
<!--ProfileViewController-->
<scene sceneID="JvR-ag-PtC">
<objects>
<viewControllerPlaceholder storyboardName="Main" referencedIdentifier="ProfileViewController" id="8Oq-Nk-eYq" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="kuM-f9-DL1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="54" y="1769"/>
</scene>
</scenes>
<resources>
<image name="swipe_up" width="21" height="6"/>
@@ -258,6 +258,24 @@ enum ConversationFeed {
return nil
}
var openGraphInfo: OpenGraphInfoType? {
switch self {
case .DiscoveredFeedType(let discoveredFeed):
if let attachment = discoveredFeed.attachment {
if case let .URL(openGraphInfo) = attachment {
return openGraphInfo
}
}
case .FeedType(let feed):
if let openGraphInfo = feed.openGraphInfo {
return openGraphInfo
}
}
return nil
}
var attachments: [Attachment] {
switch self {
@@ -584,9 +602,10 @@ class ConversationViewController: BaseViewController {
#endif
private let chatSectionDateCellIdentifier = "ChatSectionDateCell"
private let chatStateCellIdentifier = "ChatStateCell"
private let chatLeftTextCellIdentifier = "ChatLeftTextCell"
private let chatRightTextCellIdentifier = "ChatRightTextCell"
private let chatLeftTextURLCellIdentifier = "ChatLeftTextURLCell"
private let chatRightTextURLCellIdentifier = "ChatRightTextURLCell"
private let chatLeftImageCellIdentifier = "ChatLeftImageCell"
private let chatRightImageCellIdentifier = "ChatRightImageCell"
private let chatLeftAudioCellIdentifier = "ChatLeftAudioCell"
@@ -595,6 +614,7 @@ class ConversationViewController: BaseViewController {
private let chatRightVideoCellIdentifier = "ChatRightVideoCell"
private let chatLeftLocationCellIdentifier = "ChatLeftLocationCell"
private let chatRightLocationCellIdentifier = "ChatRightLocationCell"
private let chatLeftRecallCellIdentifier = "ChatLeftRecallCell"
private let chatLeftSocialWorkCellIdentifier = "ChatLeftSocialWorkCell"
private struct Listener {
@@ -671,10 +691,32 @@ class ConversationViewController: BaseViewController {
conversationCollectionView.alwaysBounceVertical = true
conversationCollectionView.registerNib(UINib(nibName: chatStateCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatStateCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatSectionDateCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatSectionDateCellIdentifier)
conversationCollectionView.registerClass(ChatLeftTextCell.self, forCellWithReuseIdentifier: chatLeftTextCellIdentifier)
conversationCollectionView.registerClass(ChatRightTextCell.self, forCellWithReuseIdentifier: chatRightTextCellIdentifier)
conversationCollectionView.registerClass(ChatLeftTextURLCell.self, forCellWithReuseIdentifier: chatLeftTextURLCellIdentifier)
conversationCollectionView.registerClass(ChatRightTextURLCell.self, forCellWithReuseIdentifier: chatRightTextURLCellIdentifier)
conversationCollectionView.registerClass(ChatLeftImageCell.self, forCellWithReuseIdentifier: chatLeftImageCellIdentifier)
conversationCollectionView.registerClass(ChatRightImageCell.self, forCellWithReuseIdentifier: chatRightImageCellIdentifier)
conversationCollectionView.registerClass(ChatLeftAudioCell.self, forCellWithReuseIdentifier: chatLeftAudioCellIdentifier)
conversationCollectionView.registerClass(ChatRightAudioCell.self, forCellWithReuseIdentifier: chatRightAudioCellIdentifier)
conversationCollectionView.registerClass(ChatLeftVideoCell.self, forCellWithReuseIdentifier: chatLeftVideoCellIdentifier)
conversationCollectionView.registerClass(ChatRightVideoCell.self, forCellWithReuseIdentifier: chatRightVideoCellIdentifier)
conversationCollectionView.registerClass(ChatLeftLocationCell.self, forCellWithReuseIdentifier: chatLeftLocationCellIdentifier)
conversationCollectionView.registerClass(ChatRightLocationCell.self, forCellWithReuseIdentifier: chatRightLocationCellIdentifier)
conversationCollectionView.registerClass(ChatLeftRecallCell.self, forCellWithReuseIdentifier: chatLeftRecallCellIdentifier)
/*
conversationCollectionView.registerNib(UINib(nibName: chatLeftTextCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftTextCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatRightTextCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatRightTextCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatLeftTextURLCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftTextURLCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatRightTextURLCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatRightTextURLCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatLeftImageCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftImageCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatRightImageCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatRightImageCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatLeftAudioCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftAudioCellIdentifier)
@@ -683,6 +725,7 @@ class ConversationViewController: BaseViewController {
conversationCollectionView.registerNib(UINib(nibName: chatRightVideoCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatRightVideoCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatLeftLocationCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftLocationCellIdentifier)
conversationCollectionView.registerNib(UINib(nibName: chatRightLocationCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatRightLocationCellIdentifier)
*/
conversationCollectionView.registerNib(UINib(nibName: chatLeftSocialWorkCellIdentifier, bundle: nil), forCellWithReuseIdentifier: chatLeftSocialWorkCellIdentifier)
conversationCollectionView.bounces = true
@@ -834,75 +877,17 @@ class ConversationViewController: BaseViewController {
break
}
if let group = conversation.withGroup where !group.includeMe {
let groupID = group.groupID
meIsMemberOfGroup(groupID: groupID, failureHandler: nil, completion: { meIsMember in
println("meIsMember: \(meIsMember)")
guard !meIsMember else {
return
}
delay(1) { [weak self] in
self?.subscribeView.subscribeAction = { [weak self] in
joinGroup(groupID: groupID, failureHandler: nil, completion: {
println("subscribe OK")
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if let strongSelf = self {
let _ = try? strongSelf.realm.write {
strongSelf.conversation.withGroup?.includeMe = true
}
}
}
})
}
self?.subscribeView.showWithChangeAction = { [weak self] in
if let strongSelf = self {
let bottom = strongSelf.view.bounds.height - strongSelf.messageToolbar.frame.origin.y + SubscribeView.height
let newContentOffsetY = strongSelf.conversationCollectionView.contentSize.height - strongSelf.messageToolbar.frame.origin.y + SubscribeView.height
self?.tryUpdateConversationCollectionViewWith(newContentInsetBottom: bottom, newContentOffsetY: newContentOffsetY)
self?.isSubscribeViewShowing = true
}
}
self?.subscribeView.hideWithChangeAction = { [weak self] in
if let strongSelf = self {
let bottom = strongSelf.view.bounds.height - strongSelf.messageToolbar.frame.origin.y
let newContentOffsetY = strongSelf.conversationCollectionView.contentSize.height - strongSelf.messageToolbar.frame.origin.y
self?.tryUpdateConversationCollectionViewWith(newContentInsetBottom: bottom, newContentOffsetY: newContentOffsetY)
self?.isSubscribeViewShowing = false
}
}
self?.subscribeView.show()
}
})
}
#if DEBUG
// view.addSubview(conversationFPSLabel)
#endif
tryShowSubscribeView()
needDetectMention = conversation.needDetectMention
#if DEBUG
//view.addSubview(conversationFPSLabel)
#endif
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
/*
// NavigationControllerDelegate push
if let delegate = originalNavigationControllerDelegate {
@@ -947,6 +932,7 @@ class ConversationViewController: BaseViewController {
messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in
//println("messageToolbar.messageTextView.text 1: \(messageToolbar.messageTextView.text)")
switch currentState {
case .BeginTextInput:
@@ -961,6 +947,19 @@ class ConversationViewController: BaseViewController {
if self?.needDetectMention ?? false {
self?.mentionView.hide()
}
if previousState != .TextInputing {
if let
draft = self?.conversation.draft,
state = MessageToolbarState(rawValue: draft.messageToolbarState) {
messageToolbar.messageTextView.text = draft.text
}
}
}
if previousState != currentState {
//println("messageToolbar.messageTextView.text 2: \(messageToolbar.messageTextView.text)")
NSNotificationCenter.defaultCenter().postNotificationName(MessageToolbar.Notification.updateDraft, object: nil)
}
}
@@ -1419,6 +1418,7 @@ class ConversationViewController: BaseViewController {
if let checkTypingStatusTimer = checkTypingStatusTimer {
checkTypingStatusTimer.invalidate()
}
NSNotificationCenter.defaultCenter().postNotificationName(MessageToolbar.Notification.updateDraft, object: nil)
}
@@ -1735,6 +1735,11 @@ class ConversationViewController: BaseViewController {
mapItem.openInMapsWithLaunchOptions(nil)
}
feedView.tapURLInfoAction = { [weak self] URL in
println("tapURLInfoAction URL: \(URL)")
self?.yep_openURL(URL)
}
feedView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(feedView)
@@ -1874,12 +1879,21 @@ class ConversationViewController: BaseViewController {
case MessageMediaType.Text.rawValue:
let rect = message.textContentToShow.boundingRectWithSize(CGSize(width: messageTextLabelMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.ChatCell.textAttributes, context: nil)
if message.deletedByCreator {
height = 26
height = max(ceil(rect.height) + (11 * 2), YepConfig.chatCellAvatarSize())
} else {
let rect = message.textContent.boundingRectWithSize(CGSize(width: messageTextLabelMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.ChatCell.textAttributes, context: nil)
if !key.isEmpty {
textContentLabelWidths[key] = ceil(rect.width)
height = max(ceil(rect.height) + (11 * 2), YepConfig.chatCellAvatarSize())
if message.openGraphInfo != nil {
height += 100 + 10
}
if !key.isEmpty {
textContentLabelWidths[key] = ceil(rect.width)
}
}
case MessageMediaType.Image.rawValue:
@@ -1930,11 +1944,13 @@ class ConversationViewController: BaseViewController {
height = 20
}
if message.mediaType != MessageMediaType.SectionDate.rawValue && conversation.withGroup != nil {
if let sender = message.fromFriend {
if sender.friendState != UserFriendState.Me.rawValue {
height += YepConfig.ChatCell.marginTopForGroup
// inGroup, plus height for show name
if conversation.withGroup != nil {
if message.mediaType != MessageMediaType.SectionDate.rawValue && !message.deletedByCreator {
if let sender = message.fromFriend {
if sender.friendState != UserFriendState.Me.rawValue {
height += YepConfig.ChatCell.marginTopForGroup
}
}
}
}
@@ -2116,6 +2132,80 @@ class ConversationViewController: BaseViewController {
}
}
private func tryShowSubscribeView() {
guard let group = conversation.withGroup where !group.includeMe else {
return
}
let groupID = group.groupID
meIsMemberOfGroup(groupID: groupID, failureHandler: nil, completion: { meIsMember in
println("meIsMember: \(meIsMember)")
guard !meIsMember else {
return
}
delay(3) { [weak self] in
self?.subscribeView.subscribeAction = { [weak self] in
joinGroup(groupID: groupID, failureHandler: nil, completion: {
println("subscribe OK")
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if let strongSelf = self {
let _ = try? strongSelf.realm.write {
strongSelf.conversation.withGroup?.includeMe = true
}
}
}
})
}
self?.subscribeView.showWithChangeAction = { [weak self] in
if let strongSelf = self {
let bottom = strongSelf.view.bounds.height - strongSelf.messageToolbar.frame.origin.y + SubscribeView.height
//let newContentOffsetY = strongSelf.conversationCollectionView.contentSize.height - strongSelf.messageToolbar.frame.origin.y + SubscribeView.height
let extraPart = strongSelf.conversationCollectionView.contentSize.height - (strongSelf.messageToolbar.frame.origin.y - SubscribeView.height)
let newContentOffsetY: CGFloat
if extraPart > 0 {
newContentOffsetY = strongSelf.conversationCollectionView.contentOffset.y + SubscribeView.height
} else {
newContentOffsetY = strongSelf.conversationCollectionView.contentOffset.y
}
//println("extraPart: \(extraPart), newContentOffsetY: \(newContentOffsetY)")
self?.tryUpdateConversationCollectionViewWith(newContentInsetBottom: bottom, newContentOffsetY: newContentOffsetY)
self?.isSubscribeViewShowing = true
}
}
self?.subscribeView.hideWithChangeAction = { [weak self] in
if let strongSelf = self {
let bottom = strongSelf.view.bounds.height - strongSelf.messageToolbar.frame.origin.y
let newContentOffsetY = strongSelf.conversationCollectionView.contentSize.height - strongSelf.messageToolbar.frame.origin.y
self?.tryUpdateConversationCollectionViewWith(newContentInsetBottom: bottom, newContentOffsetY: newContentOffsetY)
self?.isSubscribeViewShowing = false
}
}
self?.subscribeView.show()
}
})
}
// MARK: Actions
@objc private func messagesMarkAsReadByRecipient(notifictaion: NSNotification) {
@@ -2313,6 +2403,7 @@ class ConversationViewController: BaseViewController {
message: sessionMessage,
finish: { success in
println("share Feed to WeChat Session success: \(success)")
GoogleAnalyticsTrackSocial("WeChat Session", action: "Feed", target: groupShareURLString)
}
)
@@ -2323,6 +2414,7 @@ class ConversationViewController: BaseViewController {
message: timelineMessage,
finish: { success in
println("share Feed to WeChat Timeline success: \(success)")
GoogleAnalyticsTrackSocial("WeChat TimeLine", action: "Feed", target: groupShareURLString)
}
)
@@ -2330,6 +2422,8 @@ class ConversationViewController: BaseViewController {
let activityViewController = UIActivityViewController(activityItems: [shareText], applicationActivities: [weChatSessionActivity, weChatTimelineActivity])
GoogleAnalyticsTrackSocial("Share", action: "Feed", target: groupShareURLString)
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.presentViewController(activityViewController, animated: true, completion: nil)
}
@@ -2899,6 +2993,16 @@ class ConversationViewController: BaseViewController {
switch identifier {
case "showProfileWithUsername":
let vc = segue.destinationViewController as! ProfileViewController
let box = sender as! Box<ProfileUser>
vc.profileUser = box.value
vc.fromType = .GroupConversation
vc.setBackButtonWithTitle()
case "showProfileFromFeedView":
let vc = segue.destinationViewController as! ProfileViewController
@@ -3276,12 +3380,18 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
var bubbleFrame = CGRectZero
if let cell = cell as? ChatLeftTextCell {
bubbleFrame = cell.convertRect(cell.textContainerView.frame, toView: view)
bubbleFrame = cell.convertRect(cell.textContentTextView.frame, toView: view)
} else if let cell = cell as? ChatRightTextCell {
bubbleFrame = cell.convertRect(cell.textContainerView.frame, toView: view)
} else if let cell = cell as? ChatLeftImageCell {
} else if let cell = cell as? ChatLeftTextURLCell {
bubbleFrame = cell.convertRect(cell.textContentTextView.frame, toView: view)
} else if let cell = cell as? ChatRightTextURLCell {
bubbleFrame = cell.convertRect(cell.textContainerView.frame, toView: view)
} else if let cell = cell as? ChatLeftImageCell {
bubbleFrame = cell.convertRect(cell.messageImageView.frame, toView: view)
} else if let cell = cell as? ChatRightImageCell {
@@ -3647,8 +3757,20 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
default:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftTextCellIdentifier, forIndexPath: indexPath) as! ChatLeftTextCell
return cell
if message.deletedByCreator {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftRecallCellIdentifier, forIndexPath: indexPath) as! ChatLeftRecallCell
return cell
} else {
if message.openGraphInfo != nil {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftTextURLCellIdentifier, forIndexPath: indexPath) as! ChatLeftTextURLCell
return cell
} else {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatLeftTextCellIdentifier, forIndexPath: indexPath) as! ChatLeftTextCell
return cell
}
}
}
} else { // from Me
@@ -3677,8 +3799,14 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
default:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightTextCellIdentifier, forIndexPath: indexPath) as! ChatRightTextCell
return cell
if message.openGraphInfo != nil {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightTextURLCellIdentifier, forIndexPath: indexPath) as! ChatRightTextURLCell
return cell
} else {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(chatRightTextCellIdentifier, forIndexPath: indexPath) as! ChatRightTextCell
return cell
}
}
}
}
@@ -3692,15 +3820,37 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
return cell
}
private func tryShowProfileWithUsername(username: String) {
if let realm = try? Realm(), user = userWithUsername(username, inRealm: realm) {
let profileUser = ProfileUser.UserType(user)
delay(0.1) { [weak self] in
self?.performSegueWithIdentifier("showProfileWithUsername", sender: Box<ProfileUser>(profileUser))
}
} else {
discoverUserByUsername(username, failureHandler: { [weak self] reason, errorMessage in
YepAlert.alertSorry(message: errorMessage ?? NSLocalizedString("User not found.", comment: ""), inViewController: self)
}, completion: { discoveredUser in
dispatch_async(dispatch_get_main_queue()) { [weak self] in
let profileUser = ProfileUser.DiscoveredUserType(discoveredUser)
self?.performSegueWithIdentifier("showProfileWithUsername", sender: Box<ProfileUser>(profileUser))
}
})
}
}
func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
if let message = messages[safe: (displayedMessagesRange.location + indexPath.item)] {
if message.mediaType == MessageMediaType.SectionDate.rawValue {
if let cell = cell as? ChatSectionDateCell {
let createdAt = NSDate(timeIntervalSince1970: message.createdUnixTime)
if createdAt.isInCurrentWeek() {
cell.sectionDateLabel.text = sectionDateInCurrentWeekFormatter.stringFromDate(createdAt)
@@ -3832,10 +3982,43 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
}
default:
if let cell = cell as? ChatLeftTextCell {
if message.deletedByCreator {
if let cell = cell as? ChatLeftRecallCell {
cell.configureWithMessage(message)
}
} else {
if message.openGraphInfo != nil {
if let cell = cell as? ChatLeftTextURLCell {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), collectionView: collectionView, indexPath: indexPath)
cell.tapUsernameAction = { [weak self] username in
println("left textURL cell.tapUsernameAction: \(username)")
self?.tryShowProfileWithUsername(username)
}
cell.tapOpenGraphURLAction = { [weak self] URL in
self?.yep_openURL(URL)
}
}
} else {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), collectionView: collectionView, indexPath: indexPath)
if let cell = cell as? ChatLeftTextCell {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), collectionView: collectionView, indexPath: indexPath)
cell.tapUsernameAction = { [weak self] username in
println("left text cell.tapUsernameAction: \(username)")
self?.tryShowProfileWithUsername(username)
}
}
}
tryDetectOpenGraphForMessage(message)
}
}
@@ -3986,41 +4169,134 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi
}
default:
if let cell = cell as? ChatRightTextCell {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), mediaTapAction: { [weak self] in
if message.sendState == MessageSendState.Failed.rawValue {
YepAlert.confirmOrCancel(title: NSLocalizedString("Action", comment: ""), message: NSLocalizedString("Resend text?", comment: ""), confirmTitle: NSLocalizedString("Resend", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: {
resendMessage(message, failureHandler: { [weak self] reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
YepAlert.alertSorry(message: NSLocalizedString("Failed to resend text!\nPlease make sure your iPhone is connected to the Internet.", comment: ""), inViewController: self)
}, completion: { success in
println("resendText: \(success)")
})
}, cancelAction: {
})
}
}, collectionView: collectionView, indexPath: indexPath)
let mediaTapAction: () -> Void = { [weak self] in
guard message.sendState == MessageSendState.Failed.rawValue else {
return
}
YepAlert.confirmOrCancel(title: NSLocalizedString("Action", comment: ""), message: NSLocalizedString("Resend text?", comment: ""), confirmTitle: NSLocalizedString("Resend", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: {
resendMessage(message, failureHandler: { [weak self] reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
YepAlert.alertSorry(message: NSLocalizedString("Failed to resend text!\nPlease make sure your iPhone is connected to the Internet.", comment: ""), inViewController: self)
}, completion: { success in
println("resendText: \(success)")
})
}, cancelAction: {
})
}
if message.openGraphInfo != nil {
if let cell = cell as? ChatRightTextURLCell {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), mediaTapAction: mediaTapAction, collectionView: collectionView, indexPath: indexPath)
cell.tapUsernameAction = { [weak self] username in
println("right textURL cell.tapUsernameAction: \(username)")
self?.tryShowProfileWithUsername(username)
}
cell.tapOpenGraphURLAction = { [weak self] URL in
self?.yep_openURL(URL)
}
}
} else {
if let cell = cell as? ChatRightTextCell {
cell.configureWithMessage(message, textContentLabelWidth: textContentLabelWidthOfMessage(message), mediaTapAction: mediaTapAction, collectionView: collectionView, indexPath: indexPath)
cell.tapUsernameAction = { [weak self] username in
println("right text cell.tapUsernameAction: \(username)")
self?.tryShowProfileWithUsername(username)
}
}
}
tryDetectOpenGraphForMessage(message)
}
}
}
}
}
private func tryDetectOpenGraphForMessage(message: Message) {
guard !message.openGraphDetected else {
return
}
func markMessageOpenGraphDetected() {
let _ = try? realm.write {
message.openGraphDetected = true
}
}
let text = message.textContent
guard let fisrtURL = text.yep_embeddedURLs.first else {
markMessageOpenGraphDetected()
return
}
openGraphWithURL(fisrtURL, failureHandler: { reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) {
markMessageOpenGraphDetected()
}
}, completion: { _openGraph in
println("message_openGraph: \(_openGraph)")
guard _openGraph.isValid else {
return
}
dispatch_async(dispatch_get_main_queue()) { [weak self] in
guard let strongSelf = self else {
return
}
let openGraphInfo = OpenGraphInfo(URLString: _openGraph.URL.absoluteString, siteName: _openGraph.siteName ?? "", title: _openGraph.title ?? "", infoDescription: _openGraph.description ?? "", thumbnailImageURLString: _openGraph.previewImageURLString ?? "")
let _ = try? strongSelf.realm.write {
strongSelf.realm.add(openGraphInfo, update: true)
message.openGraphInfo = openGraphInfo
}
markMessageOpenGraphDetected()
// update UI
strongSelf.clearHeightOfMessageWithKey(message.messageID)
if let index = strongSelf.messages.indexOf(message) {
let realIndex = index - strongSelf.displayedMessagesRange.location
let indexPath = NSIndexPath(forItem: realIndex, inSection: 0)
strongSelf.conversationCollectionView.reloadItemsAtIndexPaths([indexPath])
// only for latest one need to scroll
if index == (strongSelf.displayedMessagesRange.location + strongSelf.displayedMessagesRange.length - 1) {
strongSelf.conversationCollectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true)
}
}
}
})
}
func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize {
if let message = messages[safe: (displayedMessagesRange.location + indexPath.item)] {
let height = heightOfMessage(message)
return CGSize(width: collectionViewWidth, height: height)
} else {
@@ -4357,6 +4633,14 @@ extension ConversationViewController: UIImagePickerControllerDelegate, UINavigat
// Do send
let imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())!
/*
var imageData = UIImageJPEGRepresentation(image, YepConfig.messageImageCompressionQuality())!
if let progressiveImage = UIImage(data: imageData)?.yep_progressiveImage {
imageData = UIImageJPEGRepresentation(progressiveImage, YepConfig.messageImageCompressionQuality())!
}
*/
let messageImageName = NSUUID().UUIDString
@@ -63,7 +63,7 @@ class ConversationsViewController: SegueViewController {
private lazy var conversations: Results<Conversation> = {
let predicate = NSPredicate(format: "type = %d", ConversationType.OneToOne.rawValue)
return self.realm.objects(Conversation).filter(predicate).sorted("updatedUnixTime", ascending: false)
}()
}()
private struct Listener {
static let Nickname = "ConversationsViewController.Nickname"
@@ -155,7 +155,7 @@ class ConversationsViewController: SegueViewController {
private func cacheInAdvance() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
//
@@ -238,40 +238,21 @@ class ConversationsViewController: SegueViewController {
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
var isFirstAppear = true
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
delay(0.5) { [weak self] in
self?.askForNotification()
if isFirstAppear {
delay(0.5) { [weak self] in
self?.askForNotification()
}
//
cacheInAdvance()
}
//
cacheInAdvance()
// test open graph
/*
[
//"https://itunes.apple.com/cn/album/hello-single/id1051365605?i=1051366040&l=en",
//"https://itun.es/cn/5_268?i=1022063849",
//"www.douban.com",
//"http://swiftcn.io/topics/64?f=w",
//"https://github.com/",
//"http://www.douban.com/note/431101390/",
//"https://itunes.apple.com/us/movie/headhunters/id550338059", //
//"https://itunes.apple.com/cn/album/19-standard-edition/id270409624?l=en", //
//"https://itunes.apple.com/cn/album/hello-single/id1051365605?i=1051366040&l=en", //
"https://itunes.apple.com/us/book/swift-programming-language/id881256329", //
//"https://itunes.apple.com/cn/app/evernote/id281796108?l=en&mt=8", // APP
].forEach({
openGraphWithURLString($0, failureHandler: nil, completion: { openGraph in
println("openGraph: \(openGraph)")
})
})
*/
isFirstAppear = false
}
private func askForNotification() {
@@ -296,14 +277,14 @@ class ConversationsViewController: SegueViewController {
category.identifier = "YepMessageNotification"
category.setActions([replyAction, replyOKAction], forContext: UIUserNotificationActionContext.Minimal)
APService.registerForRemoteNotificationTypes(
JPUSHService.registerForRemoteNotificationTypes(
UIUserNotificationType.Badge.rawValue |
UIUserNotificationType.Sound.rawValue |
UIUserNotificationType.Alert.rawValue, categories: [category])
} else {
//
APService.registerForRemoteNotificationTypes(
JPUSHService.registerForRemoteNotificationTypes(
UIUserNotificationType.Badge.rawValue |
UIUserNotificationType.Sound.rawValue |
UIUserNotificationType.Alert.rawValue, categories: nil)
@@ -75,6 +75,11 @@ class DiscoverViewController: BaseViewController {
return label
}()
#endif
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -37,8 +37,6 @@ class EditSkillsViewController: BaseViewController {
// animatedOnNavigationBar = false
title = skillSet?.name
// get all skill categories
allSkillCategories(failureHandler: { (reason, errorMessage) -> Void in
@@ -127,6 +127,7 @@ class FeedsViewController: BaseViewController {
private let feedDribbbleShotCellID = "FeedDribbbleShotCell"
private let feedVoiceCellID = "FeedVoiceCell"
private let feedLocationCellID = "FeedLocationCell"
private let feedURLCellID = "FeedURLCell"
private let loadMoreTableViewCellID = "LoadMoreTableViewCell"
private lazy var noFeedsFooterView: InfoView = InfoView(NSLocalizedString("No Feeds.", comment: ""))
@@ -315,6 +316,7 @@ class FeedsViewController: BaseViewController {
feedsTableView.registerClass(FeedDribbbleShotCell.self, forCellReuseIdentifier: feedDribbbleShotCellID)
feedsTableView.registerClass(FeedVoiceCell.self, forCellReuseIdentifier: feedVoiceCellID)
feedsTableView.registerClass(FeedLocationCell.self, forCellReuseIdentifier: feedLocationCellID)
feedsTableView.registerClass(FeedURLCell.self, forCellReuseIdentifier: feedURLCellID)
feedsTableView.registerNib(UINib(nibName: loadMoreTableViewCellID, bundle: nil), forCellReuseIdentifier: loadMoreTableViewCellID)
@@ -401,7 +403,6 @@ class FeedsViewController: BaseViewController {
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
/*
// NavigationControllerDelegate push
if let delegate = originalNavigationControllerDelegate {
@@ -949,6 +950,10 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate {
let cell = tableView.dequeueReusableCellWithIdentifier(feedBasicCellID) as! FeedBasicCell
return cell
case .URL:
let cell = tableView.dequeueReusableCellWithIdentifier(feedURLCellID) as! FeedURLCell
return cell
case .Image:
if feed.imageAttachmentsCount == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier(feedBiggerImageCellID) as! FeedBiggerImageCell
@@ -1070,6 +1075,19 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate {
cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill)
case .URL:
guard let cell = cell as? FeedURLCell else {
break
}
cell.configureWithFeed(feed, layoutCache: layoutCache, needShowSkill: needShowSkill)
cell.tapURLInfoAction = { [weak self] URL in
println("tapURLInfoAction URL: \(URL)")
self?.yep_openURL(URL)
}
case .Image:
let tapMediaAction: FeedTapMediaAction = { [weak self] transitionView, image, attachments, index in
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--Media Preview View Controller-->
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--Message Media View Controller-->
@@ -18,6 +18,8 @@ class YepNavigationController: UINavigationController, UIGestureRecognizerDelega
delegate = self
}
// self.navigationBar.changeBottomHairImage()
}
override init(rootViewController: UIViewController) {
@@ -12,7 +12,6 @@ import MobileCoreServices
import Photos
import Proposer
import RealmSwift
import Crashlytics
import Kingfisher
import MapKit
@@ -163,10 +162,10 @@ class NewFeedViewController: SegueViewController {
case .Uploading:
postButton.enabled = false
messageTextView.resignFirstResponder()
//YepHUD.showActivityIndicator()
YepHUD.showActivityIndicator()
case .Failed(let message):
//YepHUD.hideActivityIndicator()
YepHUD.hideActivityIndicator()
postButton.enabled = true
if presentingViewController != nil {
@@ -176,7 +175,7 @@ class NewFeedViewController: SegueViewController {
}
case .Success:
//YepHUD.hideActivityIndicator()
YepHUD.hideActivityIndicator()
messageTextView.text = nil
}
}
@@ -647,69 +646,116 @@ class NewFeedViewController: SegueViewController {
}
if !again {
uploadState = .Uploading
if let feed = tryMakeUploadingFeed() where feed.kind.needBackgroundUpload {
beforeUploadingFeedAction?(feed: feed, newFeedViewController: self)
YepHUD.hideActivityIndicator()
dismissViewControllerAnimated(true, completion: nil)
}
}
uploadState = .Uploading
let message = messageTextView.text.trimming(.WhitespaceAndNewline)
let coordinate = YepLocationService.sharedManager.currentLocation?.coordinate
var kind: FeedKind = .Text
var attachments: [JSONDictionary]?
let doCreateFeed: () -> Void = { [weak self] in
let tryCreateFeed: () -> Void = { [weak self] in
if let userID = YepUserDefaults.userID.value, nickname = YepUserDefaults.nickname.value {
Answers.logCustomEventWithName("New Feed",
customAttributes: [
"userID": userID,
"nickname": nickname,
"time": NSDate().description
])
var openGraph: OpenGraph?
let doCreateFeed: () -> Void = { [weak self] in
if let userID = YepUserDefaults.userID.value {
GoogleAnalyticsTrackEvent("New Feed", label: userID, value: 0)
}
if let openGraph = openGraph where openGraph.isValid {
kind = .URL
let URLInfo = [
"url": openGraph.URL.absoluteString,
"site_name": openGraph.siteName ?? "",
"title": openGraph.title ?? "",
"description": openGraph.description ?? "",
"image_url": openGraph.previewImageURLString ?? "",
]
attachments = [URLInfo]
}
createFeedWithKind(kind, message: message, attachments: attachments, coordinate: coordinate, skill: self?.pickedSkill, allowComment: true, failureHandler: { [weak self] reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) { [weak self] in
let message = errorMessage ?? NSLocalizedString("Create feed failed!", comment: "")
self?.uploadState = .Failed(message: message)
}
}, completion: { data in
println("createFeedWithKind: \(data)")
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.uploadState = .Success
if let feed = DiscoveredFeed.fromFeedInfo(data, groupInfo: nil) {
self?.afterCreatedFeedAction?(feed: feed)
}
if !kind.needBackgroundUpload {
self?.dismissViewControllerAnimated(true, completion: nil)
}
}
syncGroupsAndDoFurtherAction {}
})
}
createFeedWithKind(kind, message: message, attachments: attachments, coordinate: coordinate, skill: self?.pickedSkill, allowComment: true, failureHandler: { [weak self] reason, errorMessage in
guard kind.needParseOpenGraph, let fisrtURL = message.yep_embeddedURLs.first else {
doCreateFeed()
return
}
let parseOpenGraphGroup = dispatch_group_create()
dispatch_group_enter(parseOpenGraphGroup)
openGraphWithURL(fisrtURL, failureHandler: { reason, errorMessage in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) { [weak self] in
let message = errorMessage ?? NSLocalizedString("Create feed failed!", comment: "")
self?.uploadState = .Failed(message: message)
dispatch_async(dispatch_get_main_queue()) {
dispatch_group_leave(parseOpenGraphGroup)
}
}, completion: { data in
println("createFeedWithKind: \(data)")
}, completion: { _openGraph in
println("_openGraph: \(_openGraph)")
dispatch_async(dispatch_get_main_queue()) { [weak self] in
dispatch_async(dispatch_get_main_queue()) {
openGraph = _openGraph
self?.uploadState = .Success
if let feed = DiscoveredFeed.fromFeedInfo(data, groupInfo: nil) {
self?.afterCreatedFeedAction?(feed: feed)
}
if !kind.needBackgroundUpload {
self?.dismissViewControllerAnimated(true, completion: nil)
}
dispatch_group_leave(parseOpenGraphGroup)
}
syncGroupsAndDoFurtherAction {}
})
dispatch_group_notify(parseOpenGraphGroup, dispatch_get_main_queue()) {
doCreateFeed()
}
}
switch attachment {
case .Default:
let uploadImagesGroup = dispatch_group_create()
//var uploadImageInfos = [UploadImageInfo]()
var uploadAttachmentIDs = [String]()
let mediaImagesCount = mediaImages.count
let uploadImagesQueue = NSOperationQueue()
var uploadAttachmentOperations = [UploadAttachmentOperation]()
var uploadAttachmentIDs = [String]()
var uploadErrorMessage: String?
mediaImages.forEach({ image in
@@ -734,94 +780,65 @@ class NewFeedViewController: SegueViewController {
if let image = image.resizeToSize(fixedSize, withInterpolationQuality: CGInterpolationQuality.High), imageData = UIImageJPEGRepresentation(image, 0.95) {
dispatch_group_enter(uploadImagesGroup)
let source: UploadAttachment.Source = .Data(imageData)
let metaDataString = metaDataStringOfImage(image, needBlurThumbnail: false)
let uploadAttachment = UploadAttachment(type: .Feed, source: source, fileExtension: .JPEG, metaDataString: metaDataString)
tryUploadAttachment(uploadAttachment, failureHandler: { (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) {
uploadErrorMessage = errorMessage
dispatch_group_leave(uploadImagesGroup)
}
}, completion: { uploadAttachmentID in
dispatch_async(dispatch_get_main_queue()) {
let operation = UploadAttachmentOperation(uploadAttachment: uploadAttachment)
operation.completionBlock = {
if let uploadAttachmentID = operation.uploadAttachmentID {
uploadAttachmentIDs.append(uploadAttachmentID)
dispatch_group_leave(uploadImagesGroup)
}
})
/*
let fileExtension: FileExtension = .JPEG
s3UploadFileOfKind(.Feed, withFileExtension: fileExtension, inFilePath: nil, orFileData: imageData, mimeType: fileExtension.mimeType, failureHandler: { (reason, errorMessage) in
defaultFailureHandler(reason, errorMessage: errorMessage)
dispatch_async(dispatch_get_main_queue()) {
uploadErrorMessage = errorMessage
dispatch_group_leave(uploadImagesGroup)
if let _uploadErrorMessage = operation.uploadErrorMessage {
uploadErrorMessage = _uploadErrorMessage
}
}, completion: { s3UploadParams in
// Prepare meta data
let metaDataString = metaDataStringOfImage(image, needBlurThumbnail: false)
let uploadImageInfo = UploadImageInfo(s3UploadParams: s3UploadParams, metaDataString: metaDataString)
dispatch_async(dispatch_get_main_queue()) {
uploadImageInfos.append(uploadImageInfo)
dispatch_group_leave(uploadImagesGroup)
}
})
*/
}
uploadAttachmentOperations.append(operation)
}
})
dispatch_group_notify(uploadImagesGroup, dispatch_get_main_queue()) { [weak self] in
if uploadAttachmentOperations.count > 1 {
for i in 1..<uploadAttachmentOperations.count {
let previousOperation = uploadAttachmentOperations[i-1]
let currentOperation = uploadAttachmentOperations[i]
currentOperation.addDependency(previousOperation)
}
}
let uploadFinishOperation = NSBlockOperation { [weak self] in
guard uploadAttachmentIDs.count == mediaImagesCount else {
let message = uploadErrorMessage ?? NSLocalizedString("Upload failed!", comment: "")
self?.uploadState = .Failed(message: message)
NSOperationQueue.mainQueue().addOperationWithBlock {
self?.uploadState = .Failed(message: message)
}
return
}
if !uploadAttachmentIDs.isEmpty {
/*
let imageInfosData = uploadImageInfos.map({
[
"file": $0.s3UploadParams.key,
"metadata": $0.metaDataString ?? "",
]
})
*/
let imageInfos: [JSONDictionary] = uploadAttachmentIDs.map({
["id": $0]
})
attachments = imageInfos
kind = .Image
}
doCreateFeed()
tryCreateFeed()
}
if let lastUploadAttachmentOperation = uploadAttachmentOperations.last {
uploadFinishOperation.addDependency(lastUploadAttachmentOperation)
}
uploadImagesQueue.addOperations(uploadAttachmentOperations, waitUntilFinished: false)
uploadImagesQueue.addOperation(uploadFinishOperation)
case .SocialWork(let socialWork):
guard let type = MessageSocialWorkType(rawValue: socialWork.type) else {
@@ -872,7 +889,7 @@ class NewFeedViewController: SegueViewController {
break
}
doCreateFeed()
tryCreateFeed()
case .Voice(let feedVoice):
@@ -958,7 +975,8 @@ class NewFeedViewController: SegueViewController {
}
kind = .Audio
doCreateFeed()
tryCreateFeed()
self?.tryDeleteFeedVoice()
}
@@ -975,7 +993,7 @@ class NewFeedViewController: SegueViewController {
kind = .Location
doCreateFeed()
tryCreateFeed()
}
}
@@ -17,6 +17,7 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange
var imageCacheController: ImageCacheController!
var pickedImageSet = Set<PHAsset>()
var pickedImages = [PHAsset]()
var completion: ((images: [UIImage], imageAssets: [PHAsset]) -> Void)?
var imageLimit = 0
@@ -64,7 +65,7 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange
let options = PHImageRequestOptions.yep_sharedOptions
let pickedImageAssets = Array(pickedImageSet)
let pickedImageAssets = pickedImages
for imageAsset in pickedImageAssets {
@@ -137,11 +138,15 @@ class PickPhotosViewController: UICollectionViewController, PHPhotoLibraryChange
if let imageAsset = images[indexPath.item] as? PHAsset {
if pickedImageSet.contains(imageAsset) {
pickedImageSet.remove(imageAsset)
if let index = pickedImages.indexOf(imageAsset) {
pickedImages.removeAtIndex(index)
}
} else {
if pickedImageSet.count + imageLimit == 4 {
return
}
pickedImageSet.insert(imageAsset)
pickedImages.append(imageAsset)
}
title = "\(NSLocalizedString("Pick Photos", comment: "")) (\(pickedImageSet.count + imageLimit)/4)"
let cell = collectionView.cellForItemAtIndexPath(indexPath) as! PhotoCell
@@ -13,6 +13,8 @@ import Navi
import Crashlytics
import SafariServices
import Kingfisher
import Proposer
import CoreLocation
let profileAvatarAspectRatio: CGFloat = 12.0 / 16.0
@@ -488,7 +490,7 @@ class ProfileViewController: SegueViewController {
let attributes = [NSFontAttributeName: YepConfig.Profile.introductionLabelFont]
let labelWidth = self.collectionViewWidth - (YepConfig.Profile.leftEdgeInset + YepConfig.Profile.rightEdgeInset)
let rect = self.introductionText.boundingRectWithSize(CGSize(width: labelWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes:attributes, context:nil)
return ceil(rect.height) + 4
return 10 + 24 + 4 + 18 + 10 + ceil(rect.height) + 4
}
private var customNavigationItem: UINavigationItem = UINavigationItem(title: "Details")
@@ -570,10 +572,7 @@ class ProfileViewController: SegueViewController {
updateProfileCollectionView()
Answers.logContentViewWithName("\(user.nickname) Profile",
contentType: "Profile",
contentId: "profile-\(user.userID)",
customAttributes: [:])
GoogleAnalyticsTrackEvent("Visit Profile", label: user.nickname, value: 0)
}
default:
@@ -595,8 +594,22 @@ class ProfileViewController: SegueViewController {
//
syncMyInfoAndDoFurtherAction {
// Skills
guard let
myUserID = YepUserDefaults.userID.value,
realm = try? Realm(),
me = userWithUserID(myUserID, inRealm: realm) else {
return
}
if me.masterSkills.count == 0 && me.learningSkills.count == 0 {
YepAlert.confirmOrCancel(title: NSLocalizedString("Notice", comment: ""), message: NSLocalizedString("You don't have any skills!\nWould you like to pick some?", comment: ""), confirmTitle: NSLocalizedString("OK", comment: ""), cancelTitle: NSLocalizedString("Not now", comment: ""), inViewController: self, withConfirmAction: { [weak self] in
self?.pickSkills()
}, cancelAction: {})
}
}
if let
myUserID = YepUserDefaults.userID.value,
@@ -760,21 +773,6 @@ class ProfileViewController: SegueViewController {
if profileUserIsMe {
// Skills
if let
myUserID = YepUserDefaults.userID.value,
realm = try? Realm(),
me = userWithUserID(myUserID, inRealm: realm) {
if me.masterSkills.count == 0 && me.learningSkills.count == 0 {
YepAlert.confirmOrCancel(title: NSLocalizedString("Notice", comment: ""), message: NSLocalizedString("You don't have any skills!\nWould you like to pick some?", comment: ""), confirmTitle: NSLocalizedString("OK", comment: ""), cancelTitle: NSLocalizedString("Not now", comment: ""), inViewController: self, withConfirmAction: { [weak self] in
self?.pickSkills()
}, cancelAction: {})
}
}
// share my profile button
if customNavigationItem.leftBarButtonItem == nil {
@@ -792,6 +790,23 @@ class ProfileViewController: SegueViewController {
}
}
if profileUserIsMe {
proposeToAccess(.Location(.WhenInUse), agreed: {
YepLocationService.turnOn()
YepLocationService.sharedManager.afterUpdatedLocationAction = { [weak self] newLocation in
let indexPath = NSIndexPath(forItem: 0, inSection: ProfileSection.Footer.rawValue)
if let cell = self?.profileCollectionView.cellForItemAtIndexPath(indexPath) as? ProfileFooterCell {
cell.location = newLocation
}
}
}, rejected: {
println("Yep can NOT get Location. :[\n")
})
}
#if DEBUG
// view.addSubview(profileFPSLabel)
#endif
@@ -857,6 +872,7 @@ class ProfileViewController: SegueViewController {
message: sessionMessage,
finish: { success in
println("share Profile to WeChat Session success: \(success)")
GoogleAnalyticsTrackSocial("WeChat Session", action: "Profile", target: profileURL.absoluteString)
}
)
@@ -867,8 +883,11 @@ class ProfileViewController: SegueViewController {
message: timelineMessage,
finish: { success in
println("share Profile to WeChat Timeline success: \(success)")
GoogleAnalyticsTrackSocial("WeChat Timeline", action: "Profile", target: profileURL.absoluteString)
}
)
GoogleAnalyticsTrackSocial("Share", action: "Profile", target: profileURL.absoluteString)
let activityViewController = UIActivityViewController(activityItems: ["\(nickname), \(NSLocalizedString("From Yep, with Skills.", comment: "")) \(profileURL)"], applicationActivities: [weChatSessionActivity, weChatTimelineActivity])
@@ -881,11 +900,7 @@ class ProfileViewController: SegueViewController {
if let _ = profileUser?.username {
if let profileUser = profileUser {
Answers.logCustomEventWithName("Share Profile",
customAttributes: [
"userID": profileUser.userID,
"nickname": profileUser.nickname
])
GoogleAnalyticsTrackEvent("Share Profile", label: "\(profileUser.nickname) \(profileUser.userID)", value: 0)
}
shareProfile()
@@ -980,18 +995,8 @@ class ProfileViewController: SegueViewController {
private func sayHi() {
if let profileUser = profileUser {
if let userID = YepUserDefaults.userID.value,
nickname = YepUserDefaults.nickname.value{
Answers.logCustomEventWithName("Say Hi",
customAttributes: [
"userID": profileUser.userID,
"nickname": profileUser.nickname,
"byUserID": userID,
"byNickname": nickname
])
}
GoogleAnalyticsTrackEvent("Say Hi", label: "\(profileUser.nickname) \(profileUser.userID)", value: 0)
guard let realm = try? Realm() else {
return
@@ -1437,7 +1442,9 @@ extension ProfileViewController: UICollectionViewDataSource, UICollectionViewDel
case ProfileSection.Footer.rawValue:
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(footerCellIdentifier, forIndexPath: indexPath) as! ProfileFooterCell
cell.introductionLabel.text = introductionText
if let profileUser = profileUser {
cell.configureWithProfileUser(profileUser, introduction: introductionText)
}
return cell
@@ -49,6 +49,11 @@ class SettingsViewController: BaseViewController {
println("deinit Settings")
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -38,7 +38,11 @@ class SocialWorkDribbbleViewController: BaseViewController {
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -65,10 +65,15 @@ class SocialWorkGithubViewController: BaseViewController {
updateGithubTableView()
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// animatedOnNavigationBar = false
if let socialAccount = socialAccount {
@@ -40,6 +40,10 @@ class SocialWorkInstagramViewController: BaseViewController {
}
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
@@ -122,10 +122,11 @@ extension YepTabBarController: UITabBarControllerDelegate {
}
if case .Feeds = tab {
if let vc = nvc.topViewController as? FeedsViewController {
if vc.feedsTableView.yep_isAtTop {
guard let feedsTableView = vc.feedsTableView else {
return true
}
if feedsTableView.yep_isAtTop {
if !hasFirstTapOnFeedsWhenItIsAtTop {
hasFirstTapOnFeedsWhenItIsAtTop = true
return false
+29 -15
View File
@@ -9,38 +9,52 @@
import UIKit
class ChatBaseCell: UICollectionViewCell {
@IBOutlet weak var avatarImageView: UIImageView!
var user: User?
var tapAvatarAction: ((user: User) -> Void)?
var deleteMessageAction: (() -> Void)?
lazy var nameLabel: UILabel = {
let label = UILabel(frame: CGRectZero)
label.font = UIFont.systemFontOfSize(10)
label.textColor = UIColor.yepGrayColor()
label.numberOfLines = 1
self.addSubview(label)
self.contentView.addSubview(label)
return label
}()
lazy var avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.frame = CGRect(x: 15, y: 0, width: 40, height: 40)
imageView.contentMode = .ScaleAspectFit
let tapAvatar = UITapGestureRecognizer(target: self, action: "tapAvatar:")
imageView.userInteractionEnabled = true
imageView.addGestureRecognizer(tapAvatar)
return imageView
}()
var user: User?
var tapAvatarAction: ((user: User) -> Void)?
var deleteMessageAction: (() -> Void)?
deinit {
NSNotificationCenter.defaultCenter()
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
avatarImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapAvatar")
avatarImageView.addGestureRecognizer(tap)
contentView.addSubview(avatarImageView)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "menuWillShow:", name: UIMenuControllerWillShowMenuNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "menuWillHide:", name: UIMenuControllerWillHideMenuNotification, object: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var prepareForMenuAction: ((otherGesturesEnabled: Bool) -> Void)?
@objc func menuWillShow(notification: NSNotification) {
@@ -53,7 +67,7 @@ class ChatBaseCell: UICollectionViewCell {
var inGroup = false
func tapAvatar() {
@objc private func tapAvatar(sender: UITapGestureRecognizer) {
println("tapAvatar")
if let user = user {
@@ -30,18 +30,48 @@ class ChatLeftAudioCell: ChatBaseCell {
}
}
@IBOutlet weak var audioContainerView: UIView!
lazy var audioContainerView: UIView = {
let view = UIView()
return view
}()
@IBOutlet weak var bubbleImageView: UIImageView!
@IBOutlet weak var sampleView: SampleView!
lazy var bubbleImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "left_tail_bubble"))
imageView.tintColor = UIColor.leftBubbleTintColor()
return imageView
}()
@IBOutlet weak var audioDurationLabel: UILabel!
lazy var sampleView: SampleView = {
let view = SampleView()
view.sampleColor = UIColor.leftWaveColor()
return view
}()
lazy var audioDurationLabel: UILabel = {
let label = UILabel()
label.textAlignment = .Center
label.textColor = UIColor.blackColor()
return label
}()
lazy var playButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "icon_play"), forState: .Normal)
button.userInteractionEnabled = false
button.tintColor = UIColor.darkGrayColor()
button.tintAdjustmentMode = .Normal
return button
}()
lazy var loadingProgressView: MessageLoadingProgressView = {
let view = MessageLoadingProgressView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
view.hidden = true
view.backgroundColor = UIColor.clearColor()
return view
}()
@IBOutlet weak var playButton: UIButton!
@IBOutlet weak var loadingProgressView: MessageLoadingProgressView!
typealias AudioBubbleTapAction = () -> Void
var audioBubbleTapAction: AudioBubbleTapAction?
@@ -60,8 +90,6 @@ class ChatLeftAudioCell: ChatBaseCell {
func makeUI() {
//let fullWidth = UIScreen.mainScreen().bounds.width
let halfAvatarSize = YepConfig.chatCellAvatarSize() / 2
var topOffset: CGFloat = 0
@@ -71,37 +99,37 @@ class ChatLeftAudioCell: ChatBaseCell {
} else {
topOffset = 0
}
avatarImageView.center = CGPoint(x: YepConfig.chatCellGapBetweenWallAndAvatar() + halfAvatarSize, y: halfAvatarSize + topOffset)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(audioContainerView)
audioContainerView.addSubview(bubbleImageView)
audioContainerView.addSubview(playButton)
audioContainerView.addSubview(loadingProgressView)
audioContainerView.addSubview(sampleView)
audioContainerView.addSubview(audioDurationLabel)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
bubbleImageView.tintColor = UIColor.leftBubbleTintColor()
sampleView.sampleColor = UIColor.leftWaveColor()
audioDurationLabel.textColor = UIColor.blackColor()
playButton.userInteractionEnabled = false
playButton.tintColor = UIColor.darkGrayColor()
playButton.tintAdjustmentMode = .Normal
bubbleImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
bubbleImageView.addGestureRecognizer(tap)
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
audioBubbleTapAction?()
}
@@ -115,7 +143,7 @@ class ChatLeftAudioCell: ChatBaseCell {
self.audioPlayedDuration = audioPlayedDuration
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { [weak self] progress in
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { [weak self] progress, _ in
dispatch_async(dispatch_get_main_queue()) {
self?.loadingWithProgress(progress)
}
@@ -174,6 +202,12 @@ class ChatLeftAudioCell: ChatBaseCell {
audioDurationLabel.text = ""
}
bubbleImageView.frame = audioContainerView.bounds
playButton.frame = CGRect(x: 13, y: 5, width: 30, height: 30)
loadingProgressView.frame = playButton.frame
sampleView.frame = CGRect(x: 48, y: 0, width: audioContainerView.bounds.width - 60, height: audioContainerView.bounds.height)
audioDurationLabel.frame = sampleView.frame
if let audioPlayer = YepAudioService.sharedManager.audioPlayer {
if audioPlayer.playing {
@@ -198,7 +232,7 @@ class ChatLeftAudioCell: ChatBaseCell {
private func configureNameLabel() {
if inGroup {
nameLabel.text = user?.nickname
nameLabel.text = user?.chatCellCompositedName
let height = YepConfig.ChatCell.nameLabelHeightForGroup
let x = CGRectGetMaxX(avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
</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="ChatLeftAudioCell" id="gTV-IL-0wX" customClass="ChatLeftAudioCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="276" height="126"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="276" height="126"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="1K0-wd-BOr" userLabel="Avatar Image View">
<rect key="frame" x="15" y="43" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
</imageView>
<view contentMode="scaleToFill" id="Whb-i5-kn8" userLabel="Audio Container View">
<rect key="frame" x="60" y="43" width="150" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="left_tail_bubble" id="j0p-8V-Tjb" userLabel="Bubble Image View">
<rect key="frame" x="0.0" y="0.0" width="150" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="JJr-x8-Ljw" userLabel="Play Button">
<rect key="frame" x="13" y="5" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
<animations/>
<state key="normal" image="icon_play">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
<view hidden="YES" contentMode="scaleToFill" id="nrA-Yd-6hV" customClass="MessageLoadingProgressView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="13" y="5" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="mlz-An-Tuw" userLabel="Wave View" customClass="SampleView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="48" y="0.0" width="90" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2&quot;" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Nar-PJ-uc5" userLabel="Time Length Label">
<rect key="frame" x="48" y="0.0" width="90" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<animations/>
</view>
</subviews>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<animations/>
<size key="customSize" width="276" height="126"/>
<connections>
<outlet property="audioContainerView" destination="Whb-i5-kn8" id="VQ7-cc-TDb"/>
<outlet property="audioDurationLabel" destination="Nar-PJ-uc5" id="cAT-ry-mhm"/>
<outlet property="avatarImageView" destination="1K0-wd-BOr" id="ldH-Pm-vDG"/>
<outlet property="bubbleImageView" destination="j0p-8V-Tjb" id="Uca-XB-lkc"/>
<outlet property="loadingProgressView" destination="nrA-Yd-6hV" id="juv-eK-E9s"/>
<outlet property="playButton" destination="JJr-x8-Ljw" id="iMv-JQ-MHS"/>
<outlet property="sampleView" destination="mlz-An-Tuw" id="Vne-hR-WQH"/>
</connections>
<point key="canvasLocation" x="360" y="358"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_play" width="30" height="30"/>
<image name="left_tail_bubble" width="48" height="40"/>
</resources>
</document>
@@ -10,17 +10,35 @@ import UIKit
class ChatLeftImageCell: ChatBaseCell {
@IBOutlet weak var messageImageView: UIImageView!
@IBOutlet weak var borderImageView: UIImageView!
lazy var messageImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.tintColor = UIColor.leftBubbleTintColor()
imageView.maskView = self.messageImageMaskImageView
return imageView
}()
@IBOutlet weak var loadingProgressView: MessageLoadingProgressView!
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "left_tail_image_bubble_border"))
return imageView
}()
lazy var loadingProgressView: MessageLoadingProgressView = {
let view = MessageLoadingProgressView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
view.hidden = true
view.backgroundColor = UIColor.clearColor()
return view
}()
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
func makeUI() {
lazy var messageImageMaskImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "left_tail_image_bubble"))
return imageView
}()
//let fullWidth = UIScreen.mainScreen().bounds.width
func makeUI() {
let halfAvatarSize = YepConfig.chatCellAvatarSize() / 2
@@ -35,24 +53,30 @@ class ChatLeftImageCell: ChatBaseCell {
avatarImageView.center = CGPoint(x: YepConfig.chatCellGapBetweenWallAndAvatar() + halfAvatarSize, y: halfAvatarSize + topOffset)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(messageImageView)
contentView.addSubview(borderImageView)
contentView.addSubview(loadingProgressView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
messageImageView.tintColor = UIColor.leftBubbleTintColor()
messageImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
messageImageView.addGestureRecognizer(tap)
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -88,8 +112,6 @@ class ChatLeftImageCell: ChatBaseCell {
if let image = image {
self?.messageImageView.image = image
self?.messageImageView.alpha = 1.0
}
}
@@ -101,12 +123,13 @@ class ChatLeftImageCell: ChatBaseCell {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.messageImageView.image = image
guard let strongSelf = self else {
return
}
UIView.animateWithDuration(YepConfig.ChatCell.imageAppearDuration, delay: 0.0, options: .CurveEaseInOut, animations: { [weak self] in
self?.messageImageView.alpha = 1.0
}, completion: { (finished) -> Void in
})
UIView.transitionWithView(strongSelf, duration: imageFadeTransitionDuration, options: .TransitionCrossDissolve, animations: { () -> Void in
strongSelf.messageImageView.image = image
}, completion: nil)
}
}
}
@@ -136,10 +159,6 @@ class ChatLeftImageCell: ChatBaseCell {
}
loadingProgress = 0
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.messageImageView.alpha = 0.0
}
if let (imageWidth, imageHeight) = imageMetaOfMessage(message) {
@@ -150,19 +169,25 @@ class ChatLeftImageCell: ChatBaseCell {
if aspectRatio >= 1 {
let width = messageImagePreferredWidth
let width = min(messageImagePreferredWidth, YepConfig.ChatCell.imageMaxWidth)
UIView.performWithoutAnimation { [weak self] in
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMaxX(strongSelf.avatarImageView.frame) + YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble, y: topOffset, width: width, height: strongSelf.bounds.height - topOffset)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.loadingProgressView.center = CGPoint(x: CGRectGetMidX(strongSelf.messageImageView.frame) + YepConfig.ChatCell.playImageViewXOffset, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / aspectRatio)), tailDirection: .Left, completion: { [weak self] progress, image in
var size = CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / aspectRatio))
size = size.yep_ensureMinWidthOrHeight(YepConfig.ChatCell.mediaMinHeight)
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Left, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -178,13 +203,18 @@ class ChatLeftImageCell: ChatBaseCell {
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMaxX(strongSelf.avatarImageView.frame) + YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble, y: topOffset, width: width, height: strongSelf.bounds.height - topOffset)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.loadingProgressView.center = CGPoint(x: CGRectGetMidX(strongSelf.messageImageView.frame) + YepConfig.ChatCell.playImageViewXOffset, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredHeight * aspectRatio, height: messageImagePreferredHeight), tailDirection: .Left, completion: { [weak self] progress, image in
var size = CGSize(width: messageImagePreferredHeight * aspectRatio, height: messageImagePreferredHeight)
size = size.yep_ensureMinWidthOrHeight(YepConfig.ChatCell.mediaMinHeight)
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Left, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -201,13 +231,17 @@ class ChatLeftImageCell: ChatBaseCell {
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMaxX(strongSelf.avatarImageView.frame) + YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble, y: topOffset, width: width, height: strongSelf.bounds.height - topOffset)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.loadingProgressView.center = CGPoint(x: CGRectGetMidX(strongSelf.messageImageView.frame) + YepConfig.ChatCell.playImageViewXOffset, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / messageImagePreferredAspectRatio)), tailDirection: .Left, completion: { [weak self] progress, image in
let size = CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / messageImagePreferredAspectRatio))
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Left, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -223,7 +257,7 @@ class ChatLeftImageCell: ChatBaseCell {
private func configureNameLabel() {
if inGroup {
nameLabel.text = user?.nickname
nameLabel.text = user?.chatCellCompositedName
let height = YepConfig.ChatCell.nameLabelHeightForGroup
let x = CGRectGetMaxX(avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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" id="gTV-IL-0wX" customClass="ChatLeftImageCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="298" height="144"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="298" height="144"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="m2x-3M-xx2" userLabel="Avatar Image View">
<rect key="frame" x="15" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="2Dg-WB-LbN" userLabel="Message Image View">
<rect key="frame" x="60" y="0.0" width="180" height="144"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="left_tail_image_bubble_border" id="ORB-JN-4bj" userLabel="Border Image View">
<rect key="frame" x="60" y="0.0" width="180" height="144"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<view hidden="YES" contentMode="scaleToFill" id="sFT-ZV-Yxs" customClass="MessageLoadingProgressView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="140" y="57" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="298" height="144"/>
<connections>
<outlet property="avatarImageView" destination="m2x-3M-xx2" id="YKv-gh-cr8"/>
<outlet property="borderImageView" destination="ORB-JN-4bj" id="m3u-nJ-UsG"/>
<outlet property="loadingProgressView" destination="sFT-ZV-Yxs" id="DEM-ij-EQj"/>
<outlet property="messageImageView" destination="2Dg-WB-LbN" id="JaH-m0-woi"/>
</connections>
<point key="canvasLocation" x="288" y="408"/>
</collectionViewCell>
</objects>
<resources>
<image name="left_tail_image_bubble_border" width="48" height="46"/>
</resources>
</document>
@@ -10,17 +10,30 @@ import UIKit
class ChatLeftLocationCell: ChatBaseCell {
@IBOutlet weak var mapImageView: UIImageView!
@IBOutlet weak var locationNameLabel: UILabel!
@IBOutlet weak var borderImageView: UIImageView!
lazy var mapImageView: UIImageView = {
let imageView = UIImageView()
imageView.tintColor = UIColor.leftBubbleTintColor()
return imageView
}()
lazy var locationNameLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.whiteColor()
label.font = UIFont.systemFontOfSize(12)
label.textAlignment = .Center
return label
}()
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "left_tail_image_bubble_border"))
return imageView
}()
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
func makeUI() {
//let fullWidth = UIScreen.mainScreen().bounds.width
let halfAvatarSize = YepConfig.chatCellAvatarSize() / 2
var topOffset: CGFloat = 0
@@ -40,30 +53,34 @@ class ChatLeftLocationCell: ChatBaseCell {
let locationNameLabelHeight = YepConfig.ChatCell.locationNameLabelHeight
locationNameLabel.frame = CGRect(x: CGRectGetMinX(mapImageView.frame) + 20 + 7, y: CGRectGetMaxY(mapImageView.frame) - locationNameLabelHeight, width: 192 - 20 * 2 - 7, height: locationNameLabelHeight)
//locationNameLabel.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.1)
configureNameLabel()
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(mapImageView)
contentView.addSubview(locationNameLabel)
contentView.addSubview(borderImageView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
mapImageView.tintColor = UIColor.leftBubbleTintColor()
locationNameLabel.textColor = UIColor.whiteColor()
mapImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
mapImageView.addGestureRecognizer(tap)
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -86,18 +103,6 @@ class ChatLeftLocationCell: ChatBaseCell {
let locationName = message.textContent
locationNameLabel.text = locationName
var shouldAdd = false
for view in subviews {
if view.isKindOfClass(UILabel) {
shouldAdd = true
break
}
}
if shouldAdd {
addSubview(locationNameLabel)
}
ImageCache.sharedInstance.mapImageOfMessage(message, withSize: CGSize(width: 192, height: 108), tailDirection: .Left, bottomShadowEnabled: !locationName.isEmpty) { mapImage in
dispatch_async(dispatch_get_main_queue()) {
@@ -111,7 +116,7 @@ class ChatLeftLocationCell: ChatBaseCell {
private func configureNameLabel() {
if inGroup {
nameLabel.text = user?.nickname
nameLabel.text = user?.chatCellCompositedName
let height = YepConfig.ChatCell.nameLabelHeightForGroup
let x = CGRectGetMaxX(avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
@@ -1,52 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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="ChatLeftLocationCell" id="gTV-IL-0wX" customClass="ChatLeftLocationCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="354" height="108"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="354" height="108"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="gG1-9X-ttU" userLabel="Avatar Image View">
<rect key="frame" x="15" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="J0W-x9-pAW" userLabel="Map Image View">
<rect key="frame" x="60" y="0.0" width="192" height="108"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ONx-FJ-Bc0" userLabel="Location Name Label">
<rect key="frame" x="80" y="88" width="152" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="left_tail_image_bubble_border" id="vNZ-Fc-a5q" userLabel="Border Image View">
<rect key="frame" x="60" y="0.0" width="192" height="108"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="354" height="108"/>
<connections>
<outlet property="avatarImageView" destination="gG1-9X-ttU" id="wiQ-7u-LHG"/>
<outlet property="borderImageView" destination="vNZ-Fc-a5q" id="Eka-hO-f5C"/>
<outlet property="locationNameLabel" destination="ONx-FJ-Bc0" id="v5b-es-skG"/>
<outlet property="mapImageView" destination="J0W-x9-pAW" id="O7a-kw-oQx"/>
</connections>
<point key="canvasLocation" x="470" y="439"/>
</collectionViewCell>
</objects>
<resources>
<image name="left_tail_image_bubble_border" width="48" height="46"/>
</resources>
</document>
@@ -0,0 +1,69 @@
//
// ChatLeftRecallCell.swift
// Yep
//
// Created by nixzhu on 16/1/25.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import UIKit
class ChatLeftRecallCell: UICollectionViewCell {
lazy var bubbleImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "skill_bubble"))
imageView.tintColor = UIColor(white: 0.95, alpha: 1.0)
imageView.tintAdjustmentMode = .Normal
return imageView
}()
lazy var recallLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(12)
label.textColor = UIColor(white: 0.75, alpha: 1.0)
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(bubbleImageView)
contentView.addSubview(recallLabel)
bubbleImageView.translatesAutoresizingMaskIntoConstraints = false
recallLabel.translatesAutoresizingMaskIntoConstraints = false
do {
let views = [
"recallLabel": recallLabel,
]
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-(>=20)-[recallLabel]-(>=20)-|", options: [], metrics: nil, views: views)
let centerX = NSLayoutConstraint(item: recallLabel, attribute: .CenterX, relatedBy: .Equal, toItem: contentView, attribute: .CenterX, multiplier: 1.0, constant: 0)
let centerY = NSLayoutConstraint(item: recallLabel, attribute: .CenterY, relatedBy: .Equal, toItem: contentView, attribute: .CenterY, multiplier: 1.0, constant: 0)
NSLayoutConstraint.activateConstraints(constraintsH)
NSLayoutConstraint.activateConstraints([centerX, centerY])
}
do {
let leading = NSLayoutConstraint(item: bubbleImageView, attribute: .Leading, relatedBy: .Equal, toItem: recallLabel, attribute: .Leading, multiplier: 1.0, constant: -10)
let trailing = NSLayoutConstraint(item: bubbleImageView, attribute: .Trailing, relatedBy: .Equal, toItem: recallLabel, attribute: .Trailing, multiplier: 1.0, constant: 10)
let centerX = NSLayoutConstraint(item: bubbleImageView, attribute: .CenterX, relatedBy: .Equal, toItem: recallLabel, attribute: .CenterX, multiplier: 1.0, constant: 0)
let centerY = NSLayoutConstraint(item: bubbleImageView, attribute: .CenterY, relatedBy: .Equal, toItem: recallLabel, attribute: .CenterY, multiplier: 1.0, constant: 0)
let height = NSLayoutConstraint(item: bubbleImageView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 24)
NSLayoutConstraint.activateConstraints([leading, trailing, centerX, centerY, height])
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureWithMessage(message: Message) {
recallLabel.text = message.recalledTextContent
}
}
@@ -10,12 +10,42 @@ import UIKit
class ChatLeftTextCell: ChatBaseCell {
@IBOutlet weak var bubbleTailImageView: UIImageView!
var bubbleBodyShapeLayer: CAShapeLayer!
var tapUsernameAction: ((username: String) -> Void)?
@IBOutlet weak var textContainerView: UIView!
@IBOutlet weak var textContentTextView: ChatTextView!
lazy var bubbleTailImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bubble_left_tail"))
imageView.tintColor = UIColor.leftBubbleTintColor()
return imageView
}()
lazy var bubbleBodyShapeLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.backgroundColor = UIColor.leftBubbleTintColor().CGColor
layer.fillColor = UIColor.leftBubbleTintColor().CGColor
return layer
}()
lazy var textContentTextView: ChatTextView = {
let view = ChatTextView()
view.textContainer.lineFragmentPadding = 0
view.font = UIFont.chatTextFont()
view.backgroundColor = UIColor.clearColor()
view.textColor = UIColor.blackColor()
view.tintColor = UIColor.blackColor()
view.linkTextAttributes = [
NSForegroundColorAttributeName: UIColor.yepTintColor(),
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
view.tapMentionAction = { [weak self] username in
self?.tapUsernameAction?(username: username)
}
return view
}()
var bottomGap: CGFloat = 0
func makeUI() {
@@ -32,45 +62,30 @@ class ChatLeftTextCell: ChatBaseCell {
avatarImageView.center = CGPoint(x: YepConfig.chatCellGapBetweenWallAndAvatar() + halfAvatarSize, y: halfAvatarSize + topOffset)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(bubbleTailImageView)
contentView.addSubview(textContentTextView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
bubbleBodyShapeLayer = CAShapeLayer()
bubbleBodyShapeLayer.backgroundColor = UIColor.leftBubbleTintColor().CGColor
bubbleBodyShapeLayer.fillColor = UIColor.leftBubbleTintColor().CGColor
textContentTextView.textContainer.lineFragmentPadding = 0
textContentTextView.font = UIFont.chatTextFont()
textContentTextView.backgroundColor = UIColor.clearColor()
textContentTextView.textColor = UIColor.blackColor()
textContentTextView.tintColor = UIColor.blackColor()
textContentTextView.linkTextAttributes = [
NSForegroundColorAttributeName: UIColor.yepTintColor(),
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
bubbleTailImageView.tintColor = UIColor.leftBubbleTintColor()
if let bubblePosition = layer.sublayers {
contentView.layer.insertSublayer(bubbleBodyShapeLayer, atIndex: UInt32(bubblePosition.count))
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configureWithMessage(message: Message, textContentLabelWidth: CGFloat, collectionView: UICollectionView, indexPath: NSIndexPath) {
self.user = message.fromFriend
textContentTextView.text = message.textContentToShow
if message.deletedByCreator {
textContentTextView.textColor = UIColor.lightGrayColor()
} else {
textContentTextView.textColor = UIColor.blackColor()
}
textContentTextView.text = message.textContent
//textContentTextView.attributedText = NSAttributedString(string: message.textContent, attributes: textAttributes)
@@ -108,18 +123,20 @@ class ChatLeftTextCell: ChatBaseCell {
topOffset = 0
}
strongSelf.textContainerView.frame = CGRect(x: CGRectGetMaxX(strongSelf.avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar(), y: 3 + topOffset, width: textContentLabelWidth, height: strongSelf.bounds.height - topOffset - 3 * 2)
let bubbleBodyFrame = CGRectInset(strongSelf.textContainerView.frame, -12, -3)
let textContentTextViewFrame = CGRect(x: CGRectGetMaxX(strongSelf.avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar(), y: 3 + topOffset, width: textContentLabelWidth, height: strongSelf.bounds.height - topOffset - 3 * 2 - strongSelf.bottomGap)
strongSelf.textContentTextView.frame = textContentTextViewFrame
let bubbleBodyFrame = CGRectInset(textContentTextViewFrame, -12, -3)
strongSelf.bubbleBodyShapeLayer.path = UIBezierPath(roundedRect: bubbleBodyFrame, byRoundingCorners: UIRectCorner.AllCorners, cornerRadii: CGSize(width: YepConfig.ChatCell.bubbleCornerRadius, height: YepConfig.ChatCell.bubbleCornerRadius)).CGPath
if strongSelf.inGroup {
strongSelf.nameLabel.text = strongSelf.user?.nickname
strongSelf.nameLabel.text = strongSelf.user?.chatCellCompositedName
let height = YepConfig.ChatCell.nameLabelHeightForGroup
let x = strongSelf.textContainerView.frame.origin.x
let y = strongSelf.textContainerView.frame.origin.y - height - 3
let x = textContentTextViewFrame.origin.x
let y = textContentTextViewFrame.origin.y - height - 3
let width = strongSelf.contentView.bounds.width - x - 10
strongSelf.nameLabel.frame = CGRect(x: x, y: y, width: width, height: height)
}
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="ChatLeftTextCell" id="gTV-IL-0wX" customClass="ChatLeftTextCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="500" height="129"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="500" height="129"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="bubble_left_tail" id="ppr-iG-zxj" userLabel="Bubble Tail Image View">
<rect key="frame" x="57" y="13" width="13" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="hYu-LI-zcX" userLabel="Avatar Image View">
<rect key="frame" x="15" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<view contentMode="scaleToFill" id="Ieh-wh-12u">
<rect key="frame" x="75" y="3" width="92" height="123"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" id="o2v-vm-ItZ" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="92" height="123"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
</textView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="350" height="129"/>
<connections>
<outlet property="avatarImageView" destination="hYu-LI-zcX" id="X8N-rV-848"/>
<outlet property="bubbleTailImageView" destination="ppr-iG-zxj" id="kxh-jI-4L8"/>
<outlet property="textContainerView" destination="Ieh-wh-12u" id="VVJ-yk-Twz"/>
<outlet property="textContentTextView" destination="o2v-vm-ItZ" id="lj4-7V-6TH"/>
</connections>
<point key="canvasLocation" x="338" y="346.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="bubble_left_tail" width="13" height="14"/>
</resources>
</document>
@@ -0,0 +1,60 @@
//
// ChatLeftTextURLCell.swift
// Yep
//
// Created by nixzhu on 16/1/18.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import UIKit
import Ruler
class ChatLeftTextURLCell: ChatLeftTextCell {
var openGraphURL: NSURL?
var tapOpenGraphURLAction: ((URL: NSURL) -> Void)?
lazy var feedURLContainerView: FeedURLContainerView = {
let view = FeedURLContainerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.directionLeading = true
view.compressionMode = false
view.tapAction = { [weak self] in
guard let URL = self?.openGraphURL else {
return
}
self?.tapOpenGraphURLAction?(URL: URL)
}
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(feedURLContainerView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func configureWithMessage(message: Message, textContentLabelWidth: CGFloat, collectionView: UICollectionView, indexPath: NSIndexPath) {
bottomGap = 100 + 10
super.configureWithMessage(message, textContentLabelWidth: textContentLabelWidth, collectionView: collectionView, indexPath: indexPath)
let minWidth: CGFloat = Ruler.iPhoneHorizontal(190, 220, 220).value
let width = max(minWidth, textContentTextView.frame.width + 12 * 2 - 1)
let feedURLContainerViewFrame = CGRect(x: textContentTextView.frame.origin.x - 12 + 1, y: CGRectGetMaxY(textContentTextView.frame) + 8, width: width, height: 100)
feedURLContainerView.frame = feedURLContainerViewFrame
if let openGraphInfo = message.openGraphInfo {
feedURLContainerView.configureWithOpenGraphInfoType(openGraphInfo)
openGraphURL = openGraphInfo.URL
}
}
}
@@ -10,20 +10,35 @@ import UIKit
class ChatLeftVideoCell: ChatBaseCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var borderImageView: UIImageView!
lazy var thumbnailImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.tintColor = UIColor.leftBubbleTintColor()
return imageView
}()
@IBOutlet weak var playImageView: UIImageView!
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "left_tail_image_bubble_border"))
return imageView
}()
lazy var playImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "icon_playvideo"))
return imageView
}()
lazy var loadingProgressView: MessageLoadingProgressView = {
let view = MessageLoadingProgressView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
view.hidden = true
view.backgroundColor = UIColor.clearColor()
return view
}()
@IBOutlet weak var loadingProgressView: MessageLoadingProgressView!
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
func makeUI() {
//let fullWidth = UIScreen.mainScreen().bounds.width
let halfAvatarSize = YepConfig.chatCellAvatarSize() / 2
var topOffset: CGFloat = 0
@@ -36,16 +51,19 @@ class ChatLeftVideoCell: ChatBaseCell {
avatarImageView.center = CGPoint(x: YepConfig.chatCellGapBetweenWallAndAvatar() + halfAvatarSize, y: halfAvatarSize + topOffset)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(thumbnailImageView)
contentView.addSubview(borderImageView)
contentView.addSubview(playImageView)
contentView.addSubview(loadingProgressView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
thumbnailImageView.tintColor = UIColor.leftBubbleTintColor()
thumbnailImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
thumbnailImageView.addGestureRecognizer(tap)
@@ -55,6 +73,10 @@ class ChatLeftVideoCell: ChatBaseCell {
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -229,7 +251,7 @@ class ChatLeftVideoCell: ChatBaseCell {
private func configureNameLabel() {
if inGroup {
nameLabel.text = user?.nickname
nameLabel.text = user?.chatCellCompositedName
let height = YepConfig.ChatCell.nameLabelHeightForGroup
let x = CGRectGetMaxX(avatarImageView.frame) + YepConfig.chatCellGapBetweenTextContentLabelAndAvatar()
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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="ChatLeftVideoCell" id="gTV-IL-0wX" customClass="ChatLeftVideoCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="252" height="121"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="252" height="121"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="NaY-sz-OCL" userLabel="Avatar Image View">
<rect key="frame" x="15" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="dJV-3p-NM6" userLabel="Thumbnail Image View">
<rect key="frame" x="60" y="0.0" width="126" height="121"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="left_tail_image_bubble_border" id="Sb6-ev-Qcn" userLabel="Border Image View">
<rect key="frame" x="60" y="0.0" width="126" height="121"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_playvideo" id="kj2-PU-9Ld" userLabel="Play Image View">
<rect key="frame" x="113" y="46" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<view hidden="YES" contentMode="scaleToFill" id="S4T-37-s77" customClass="MessageLoadingProgressView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="113" y="46" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="252" height="121"/>
<connections>
<outlet property="avatarImageView" destination="NaY-sz-OCL" id="WlB-8B-uyk"/>
<outlet property="borderImageView" destination="Sb6-ev-Qcn" id="eRo-va-z9d"/>
<outlet property="loadingProgressView" destination="S4T-37-s77" id="Mp9-dx-PM7"/>
<outlet property="playImageView" destination="kj2-PU-9Ld" id="h0Y-vn-EUp"/>
<outlet property="thumbnailImageView" destination="dJV-3p-NM6" id="47m-Jp-5x1"/>
</connections>
<point key="canvasLocation" x="372" y="378.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_playvideo" width="30" height="30"/>
<image name="left_tail_image_bubble_border" width="48" height="46"/>
</resources>
</document>
@@ -28,16 +28,40 @@ class ChatRightAudioCell: ChatRightBaseCell {
}
}
lazy var audioContainerView: UIView = {
let view = UIView()
return view
}()
@IBOutlet weak var audioContainerView: UIView!
@IBOutlet weak var bubbleImageView: UIImageView!
lazy var bubbleImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "right_tail_bubble"))
imageView.tintColor = UIColor.rightBubbleTintColor()
return imageView
}()
@IBOutlet weak var sampleView: SampleView!
lazy var sampleView: SampleView = {
let view = SampleView()
view.sampleColor = UIColor.rightWaveColor()
return view
}()
@IBOutlet weak var audioDurationLabel: UILabel!
lazy var audioDurationLabel: UILabel = {
let label = UILabel()
label.textAlignment = .Center
label.textColor = UIColor.whiteColor()
return label
}()
@IBOutlet weak var playButton: UIButton!
lazy var playButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(named: "icon_play"), forState: .Normal)
button.userInteractionEnabled = false
button.tintColor = UIColor.whiteColor()
button.tintAdjustmentMode = .Normal
return button
}()
typealias AudioBubbleTapAction = () -> Void
var audioBubbleTapAction: AudioBubbleTapAction?
@@ -51,32 +75,32 @@ class ChatRightAudioCell: ChatRightBaseCell {
avatarImageView.center = CGPoint(x: fullWidth - halfAvatarSize - YepConfig.chatCellGapBetweenWallAndAvatar(), y: halfAvatarSize)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(audioContainerView)
audioContainerView.addSubview(bubbleImageView)
audioContainerView.addSubview(playButton)
audioContainerView.addSubview(sampleView)
audioContainerView.addSubview(audioDurationLabel)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
bubbleImageView.tintColor = UIColor.rightBubbleTintColor()
sampleView.sampleColor = UIColor.rightWaveColor()
audioDurationLabel.textColor = UIColor.whiteColor()
playButton.userInteractionEnabled = false
playButton.tintColor = UIColor.whiteColor()
playButton.tintAdjustmentMode = .Normal
bubbleImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
bubbleImageView.addGestureRecognizer(tap)
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
audioBubbleTapAction?()
}
@@ -90,7 +114,7 @@ class ChatRightAudioCell: ChatRightBaseCell {
self.audioPlayedDuration = audioPlayedDuration
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { _ in })
YepDownloader.downloadAttachmentsOfMessage(message, reportProgress: { _, _ in })
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
@@ -142,6 +166,11 @@ class ChatRightAudioCell: ChatRightBaseCell {
audioDurationLabel.text = ""
}
bubbleImageView.frame = audioContainerView.bounds
playButton.frame = CGRect(x: 6, y: 5, width: 30, height: 30)
sampleView.frame = CGRect(x: 41, y: 0, width: audioContainerView.bounds.width - 60, height: audioContainerView.bounds.height)
audioDurationLabel.frame = sampleView.frame
if let audioPlayer = YepAudioService.sharedManager.audioPlayer {
if audioPlayer.playing {
if let playingMessage = YepAudioService.sharedManager.playingMessage {
@@ -1,84 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9060" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
</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="ChatRightAudioCell" id="gTV-IL-0wX" customClass="ChatRightAudioCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="316" height="105"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="316" height="105"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="76j-Ht-EsY" userLabel="Avatar Image View">
<rect key="frame" x="261" y="33" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
</imageView>
<view contentMode="scaleToFill" id="knU-A0-FO4" userLabel="Audio Container View">
<rect key="frame" x="106" y="33" width="150" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="right_tail_bubble" id="IUw-Mk-XlZ" userLabel="Bubble Image View">
<rect key="frame" x="0.0" y="0.0" width="150" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="XTQ-XN-AqX" userLabel="Play Button">
<rect key="frame" x="6" y="5" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES"/>
<animations/>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<state key="normal" image="icon_play">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
<view userInteractionEnabled="NO" contentMode="scaleToFill" id="ynj-aF-z4D" userLabel="Wave View" customClass="SampleView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="41" y="0.0" width="90" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2&quot;" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="alU-Q7-YxY" userLabel="Time Length Label">
<rect key="frame" x="41" y="0.0" width="90" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<animations/>
</view>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" id="dkr-y8-xRJ" userLabel="Dot Image View">
<rect key="frame" x="81" y="40" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
</imageView>
</subviews>
<animations/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<animations/>
<size key="customSize" width="316" height="105"/>
<connections>
<outlet property="audioContainerView" destination="knU-A0-FO4" id="Gfe-yh-kkC"/>
<outlet property="audioDurationLabel" destination="alU-Q7-YxY" id="2dD-Pg-BJw"/>
<outlet property="avatarImageView" destination="76j-Ht-EsY" id="uPV-sw-dlg"/>
<outlet property="bubbleImageView" destination="IUw-Mk-XlZ" id="eH5-bH-lrf"/>
<outlet property="dotImageView" destination="dkr-y8-xRJ" id="u39-ha-daM"/>
<outlet property="playButton" destination="XTQ-XN-AqX" id="UPv-c2-8JU"/>
<outlet property="sampleView" destination="ynj-aF-z4D" id="FIe-OE-IN2"/>
</connections>
<point key="canvasLocation" x="434" y="419.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_dot_sending" width="26" height="26"/>
<image name="icon_play" width="30" height="30"/>
<image name="right_tail_bubble" width="48" height="40"/>
</resources>
</document>
@@ -12,7 +12,13 @@ let sendingAnimationName = "RotationOnStateAnimation"
class ChatRightBaseCell: ChatBaseCell {
@IBOutlet weak var dotImageView: UIImageView!
lazy var dotImageView: UIImageView = {
let imageView = UIImageView()
imageView.frame = CGRect(x: 15, y: 0, width: 26, height: 26)
imageView.image = UIImage(named: "icon_dot_sending")
imageView.contentMode = .Center
return imageView
}()
override var inGroup: Bool {
willSet {
@@ -60,21 +66,22 @@ class ChatRightBaseCell: ChatBaseCell {
}
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(dotImageView)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "tryUpdateMessageState", name: MessageNotification.MessageStateChanged, object: nil)
}
override func layoutSubviews() {
super.layoutSubviews()
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tryUpdateMessageState() {
guard !inGroup else {
@@ -99,7 +106,6 @@ class ChatRightBaseCell: ChatBaseCell {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.dotImageView.layer.addAnimation(animation, forKey: sendingAnimationName)
}
}
func removeSendingAnimation() {
@@ -10,12 +10,27 @@ import UIKit
class ChatRightImageCell: ChatRightBaseCell {
@IBOutlet weak var messageImageView: UIImageView!
@IBOutlet weak var borderImageView: UIImageView!
lazy var messageImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.tintColor = UIColor.rightBubbleTintColor()
imageView.maskView = self.messageImageMaskImageView
return imageView
}()
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "right_tail_image_bubble_border"))
return imageView
}()
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
lazy var messageImageMaskImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "right_tail_image_bubble"))
return imageView
}()
func makeUI() {
let fullWidth = UIScreen.mainScreen().bounds.width
@@ -25,15 +40,16 @@ class ChatRightImageCell: ChatRightBaseCell {
avatarImageView.center = CGPoint(x: fullWidth - halfAvatarSize - YepConfig.chatCellGapBetweenWallAndAvatar(), y: halfAvatarSize)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(messageImageView)
contentView.addSubview(borderImageView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
messageImageView.tintColor = UIColor.rightBubbleTintColor()
messageImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
messageImageView.addGestureRecognizer(tap)
@@ -43,6 +59,10 @@ class ChatRightImageCell: ChatRightBaseCell {
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -61,12 +81,13 @@ class ChatRightImageCell: ChatRightBaseCell {
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.messageImageView.image = image
guard let strongSelf = self else {
return
}
UIView.animateWithDuration(YepConfig.ChatCell.imageAppearDuration, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
self?.messageImageView.alpha = 1.0
}, completion: { (finished) -> Void in
})
UIView.transitionWithView(strongSelf, duration: imageFadeTransitionDuration, options: .TransitionCrossDissolve, animations: { () -> Void in
strongSelf.messageImageView.image = image
}, completion: nil)
}
}
}
@@ -89,11 +110,6 @@ class ChatRightImageCell: ChatRightBaseCell {
}
loadingProgress = 0
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.messageImageView.alpha = 0.0
}
if let (imageWidth, imageHeight) = imageMetaOfMessage(message) {
@@ -103,19 +119,24 @@ class ChatRightImageCell: ChatRightBaseCell {
let messageImagePreferredHeight = max(messageImagePreferredHeight, ceil(YepConfig.ChatCell.mediaMinWidth / aspectRatio))
if aspectRatio >= 1 {
let width = messageImagePreferredWidth
let width = min(messageImagePreferredWidth, YepConfig.ChatCell.imageMaxWidth)
UIView.performWithoutAnimation { [weak self] in
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMinX(strongSelf.avatarImageView.frame) - YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble - width, y: 0, width: width, height: strongSelf.bounds.height)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.dotImageView.center = CGPoint(x: CGRectGetMinX(strongSelf.messageImageView.frame) - YepConfig.ChatCell.gapBetweenDotImageViewAndBubble, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
var size = CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / aspectRatio))
size = size.yep_ensureMinWidthOrHeight(YepConfig.ChatCell.mediaMinHeight)
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / aspectRatio)), tailDirection: .Right, completion: { [weak self] progress, image in
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Right, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -131,13 +152,18 @@ class ChatRightImageCell: ChatRightBaseCell {
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMinX(strongSelf.avatarImageView.frame) - YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble - width, y: 0, width: width, height: strongSelf.bounds.height)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.dotImageView.center = CGPoint(x: CGRectGetMinX(strongSelf.messageImageView.frame) - YepConfig.ChatCell.gapBetweenDotImageViewAndBubble, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredHeight * aspectRatio, height: messageImagePreferredHeight), tailDirection: .Right, completion: { [weak self] progress, image in
var size = CGSize(width: messageImagePreferredHeight * aspectRatio, height: messageImagePreferredHeight)
size = size.yep_ensureMinWidthOrHeight(YepConfig.ChatCell.mediaMinHeight)
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Right, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -154,13 +180,17 @@ class ChatRightImageCell: ChatRightBaseCell {
if let strongSelf = self {
strongSelf.messageImageView.frame = CGRect(x: CGRectGetMinX(strongSelf.avatarImageView.frame) - YepConfig.ChatCell.gapBetweenAvatarImageViewAndBubble - width, y: 0, width: width, height: strongSelf.bounds.height)
strongSelf.messageImageMaskImageView.frame = strongSelf.messageImageView.bounds
strongSelf.dotImageView.center = CGPoint(x: CGRectGetMinX(strongSelf.messageImageView.frame) - YepConfig.ChatCell.gapBetweenDotImageViewAndBubble, y: CGRectGetMidY(strongSelf.messageImageView.frame))
strongSelf.borderImageView.frame = strongSelf.messageImageView.frame
}
}
ImageCache.sharedInstance.imageOfMessage(message, withSize: CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / messageImagePreferredAspectRatio)), tailDirection: .Right, completion: { [weak self] progress, image in
let size = CGSize(width: messageImagePreferredWidth, height: ceil(messageImagePreferredWidth / messageImagePreferredAspectRatio))
ImageCache.sharedInstance.imageOfMessage(message, withSize: size, tailDirection: .Right, completion: { [weak self] progress, image in
dispatch_async(dispatch_get_main_queue()) {
if let _ = collectionView.cellForItemAtIndexPath(indexPath) {
@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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="ChatRightImageCell" id="gTV-IL-0wX" customClass="ChatRightImageCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="261" height="121"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="261" height="121"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="UrC-7s-78h" userLabel="Avatar Image View">
<rect key="frame" x="206" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="eM3-aJ-C2A" userLabel="Message Image View">
<rect key="frame" x="50" y="0.0" width="151" height="121"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="right_tail_image_bubble_border" id="kcW-0A-pyg" userLabel="Border Image View">
<rect key="frame" x="50" y="0.0" width="151" height="121"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" id="C9m-E2-m8h" userLabel="Dot Image View">
<rect key="frame" x="25" y="48" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="261" height="121"/>
<connections>
<outlet property="avatarImageView" destination="UrC-7s-78h" id="j8N-UJ-Hmt"/>
<outlet property="borderImageView" destination="kcW-0A-pyg" id="nJe-X8-FV0"/>
<outlet property="dotImageView" destination="C9m-E2-m8h" id="Kdo-7q-JdE"/>
<outlet property="messageImageView" destination="eM3-aJ-C2A" id="2Rb-E5-4eV"/>
</connections>
<point key="canvasLocation" x="360.5" y="414.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_dot_sending" width="26" height="26"/>
<image name="right_tail_image_bubble_border" width="48" height="45"/>
</resources>
</document>
@@ -11,9 +11,24 @@ import MapKit
class ChatRightLocationCell: ChatRightBaseCell {
@IBOutlet weak var mapImageView: UIImageView!
@IBOutlet weak var locationNameLabel: UILabel!
@IBOutlet weak var borderImageView: UIImageView!
lazy var mapImageView: UIImageView = {
let imageView = UIImageView()
imageView.tintColor = UIColor.rightBubbleTintColor()
return imageView
}()
lazy var locationNameLabel: UILabel = {
let label = UILabel()
label.textColor = UIColor.whiteColor()
label.font = UIFont.systemFontOfSize(12)
label.textAlignment = .Center
return label
}()
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "right_tail_image_bubble_border"))
return imageView
}()
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
@@ -34,28 +49,32 @@ class ChatRightLocationCell: ChatRightBaseCell {
let locationNameLabelHeight = YepConfig.ChatCell.locationNameLabelHeight
locationNameLabel.frame = CGRect(x: CGRectGetMinX(mapImageView.frame) + 20, y: CGRectGetMaxY(mapImageView.frame) - locationNameLabelHeight, width: 192 - 20 * 2 - 7, height: locationNameLabelHeight)
//locationNameLabel.backgroundColor = UIColor.redColor().colorWithAlphaComponent(0.1)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(mapImageView)
contentView.addSubview(locationNameLabel)
contentView.addSubview(borderImageView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
mapImageView.tintColor = UIColor.rightBubbleTintColor()
locationNameLabel.textColor = UIColor.whiteColor()
mapImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
mapImageView.addGestureRecognizer(tap)
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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="ChatRightLocationCell" id="gTV-IL-0wX" customClass="ChatRightLocationCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="309" height="108"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="309" height="108"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="SGV-zQ-j8s" userLabel="Avatar Image View">
<rect key="frame" x="254" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="ivW-u4-fKZ" userLabel="Map Image View">
<rect key="frame" x="57" y="0.0" width="192" height="108"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" id="eTJ-7c-9qB" userLabel="Dot Image View">
<rect key="frame" x="32" y="41" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="LcY-WJ-YG7" userLabel="Location Name Label">
<rect key="frame" x="77" y="88" width="152" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="right_tail_image_bubble_border" id="ypp-7s-nLF" userLabel="Border Image View">
<rect key="frame" x="57" y="0.0" width="192" height="108"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="309" height="108"/>
<connections>
<outlet property="avatarImageView" destination="SGV-zQ-j8s" id="anR-ZQ-6Ne"/>
<outlet property="borderImageView" destination="ypp-7s-nLF" id="9Gr-iW-eYI"/>
<outlet property="dotImageView" destination="eTJ-7c-9qB" id="AIr-Ap-2Hc"/>
<outlet property="locationNameLabel" destination="LcY-WJ-YG7" id="dPJ-m8-om6"/>
<outlet property="mapImageView" destination="ivW-u4-fKZ" id="0pJ-tA-WYa"/>
</connections>
<point key="canvasLocation" x="240.5" y="398"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_dot_sending" width="26" height="26"/>
<image name="right_tail_image_bubble_border" width="48" height="45"/>
</resources>
</document>
@@ -10,12 +10,47 @@ import UIKit
class ChatRightTextCell: ChatRightBaseCell {
@IBOutlet weak var bubbleTailImageView: UIImageView!
var bubbleBodyShapeLayer: CAShapeLayer!
var tapUsernameAction: ((username: String) -> Void)?
@IBOutlet weak var textContainerView: UIView!
@IBOutlet weak var textContentTextView: ChatTextView!
lazy var bubbleTailImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "bubble_right_tail"))
imageView.tintColor = UIColor.rightBubbleTintColor()
return imageView
}()
lazy var bubbleBodyShapeLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.backgroundColor = UIColor.rightBubbleTintColor().CGColor
layer.fillColor = UIColor.rightBubbleTintColor().CGColor
return layer
}()
lazy var textContainerView: UIView = {
let view = UIView()
return view
}()
lazy var textContentTextView: ChatTextView = {
let view = ChatTextView()
view.textContainer.lineFragmentPadding = 0
view.font = UIFont.chatTextFont()
view.backgroundColor = UIColor.clearColor()
view.textColor = UIColor.whiteColor()
view.tintColor = UIColor.whiteColor()
view.linkTextAttributes = [
NSForegroundColorAttributeName: UIColor.whiteColor(),
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
view.tapMentionAction = { [weak self] username in
self?.tapUsernameAction?(username: username)
}
return view
}()
var bottomGap: CGFloat = 0
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
@@ -29,27 +64,20 @@ class ChatRightTextCell: ChatRightBaseCell {
avatarImageView.center = CGPoint(x: fullWidth - halfAvatarSize - YepConfig.chatCellGapBetweenWallAndAvatar(), y: halfAvatarSize)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(bubbleTailImageView)
contentView.addSubview(textContainerView)
textContainerView.addSubview(textContentTextView)
if let bubblePosition = layer.sublayers {
contentView.layer.insertSublayer(bubbleBodyShapeLayer, atIndex: UInt32(bubblePosition.count))
}
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
bubbleBodyShapeLayer = CAShapeLayer()
bubbleBodyShapeLayer.backgroundColor = UIColor.rightBubbleTintColor().CGColor
bubbleBodyShapeLayer.fillColor = UIColor.rightBubbleTintColor().CGColor
textContentTextView.textContainer.lineFragmentPadding = 0
textContentTextView.font = UIFont.chatTextFont()
textContentTextView.backgroundColor = UIColor.clearColor()
textContentTextView.textColor = UIColor.whiteColor()
textContentTextView.tintColor = UIColor.whiteColor()
textContentTextView.linkTextAttributes = [
NSForegroundColorAttributeName: UIColor.whiteColor(),
NSUnderlineStyleAttributeName: NSNumber(integer: NSUnderlineStyle.StyleSingle.rawValue),
]
textContainerView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
@@ -58,12 +86,10 @@ class ChatRightTextCell: ChatRightBaseCell {
prepareForMenuAction = { otherGesturesEnabled in
tap.enabled = otherGesturesEnabled
}
}
bubbleTailImageView.tintColor = UIColor.rightBubbleTintColor()
if let bubblePosition = layer.sublayers {
contentView.layer.insertSublayer(bubbleBodyShapeLayer, atIndex: UInt32(bubblePosition.count))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
@@ -109,7 +135,9 @@ class ChatRightTextCell: ChatRightBaseCell {
strongSelf.makeUI()
strongSelf.textContainerView.frame = CGRect(x: CGRectGetMinX(strongSelf.avatarImageView.frame) - YepConfig.chatCellGapBetweenTextContentLabelAndAvatar() - textContentLabelWidth, y: 3, width: textContentLabelWidth, height: strongSelf.bounds.height - 3 * 2)
strongSelf.textContainerView.frame = CGRect(x: CGRectGetMinX(strongSelf.avatarImageView.frame) - YepConfig.chatCellGapBetweenTextContentLabelAndAvatar() - textContentLabelWidth, y: 3, width: textContentLabelWidth, height: strongSelf.bounds.height - 3 * 2 - strongSelf.bottomGap)
strongSelf.textContentTextView.frame = strongSelf.textContainerView.bounds
let bubbleBodyFrame = CGRectInset(strongSelf.textContainerView.frame, -12, -3)
@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="ChatRightTextCell" id="gTV-IL-0wX" customClass="ChatRightTextCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="500" height="131"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="500" height="131"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="bubble_right_tail" id="1JC-jy-KNh" userLabel="Bubble Tail Image View">
<rect key="frame" x="430" y="13" width="13" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="RKK-8d-9vO" userLabel="Avatar Image View">
<rect key="frame" x="445" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.80000001192092896" green="0.40000000596046448" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" id="tB7-GG-3Zd" userLabel="Dot Image View">
<rect key="frame" x="308" y="52.5" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<view contentMode="scaleToFill" id="6da-H4-jC2">
<rect key="frame" x="333" y="3" width="92" height="125"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" id="em7-jr-W4a" customClass="ChatTextView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="92" height="125"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" phoneNumber="YES" link="YES" address="YES" calendarEvent="YES"/>
</textView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="417" height="131"/>
<connections>
<outlet property="avatarImageView" destination="RKK-8d-9vO" id="Tsh-cB-lZt"/>
<outlet property="bubbleTailImageView" destination="1JC-jy-KNh" id="MGo-KB-Kb3"/>
<outlet property="dotImageView" destination="tB7-GG-3Zd" id="odg-hQ-nba"/>
<outlet property="textContainerView" destination="6da-H4-jC2" id="jNs-2X-eia"/>
<outlet property="textContentTextView" destination="em7-jr-W4a" id="Pch-yz-67H"/>
</connections>
<point key="canvasLocation" x="339" y="326.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="bubble_right_tail" width="13" height="14"/>
<image name="icon_dot_sending" width="26" height="26"/>
</resources>
</document>
@@ -0,0 +1,61 @@
//
// ChatRightTextURLCell.swift
// Yep
//
// Created by nixzhu on 16/1/18.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import UIKit
import Ruler
class ChatRightTextURLCell: ChatRightTextCell {
var openGraphURL: NSURL?
var tapOpenGraphURLAction: ((URL: NSURL) -> Void)?
lazy var feedURLContainerView: FeedURLContainerView = {
let view = FeedURLContainerView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.directionLeading = false
view.compressionMode = false
view.tapAction = { [weak self] in
guard let URL = self?.openGraphURL else {
return
}
self?.tapOpenGraphURLAction?(URL: URL)
}
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(feedURLContainerView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func configureWithMessage(message: Message, textContentLabelWidth: CGFloat, mediaTapAction: MediaTapAction?, collectionView: UICollectionView, indexPath: NSIndexPath) {
bottomGap = 100 + 10
super.configureWithMessage(message, textContentLabelWidth: textContentLabelWidth, mediaTapAction: mediaTapAction, collectionView: collectionView, indexPath: indexPath)
let minWidth: CGFloat = Ruler.iPhoneHorizontal(190, 220, 220).value
let fullWidth = UIScreen.mainScreen().bounds.width
let width = max(minWidth, textContainerView.frame.width + 12 * 2 - 1)
let feedURLContainerViewFrame = CGRect(x: fullWidth - 65 - width - 1, y: CGRectGetMaxY(textContainerView.frame) + 8, width: width, height: 100)
feedURLContainerView.frame = feedURLContainerViewFrame
if let openGraphInfo = message.openGraphInfo {
feedURLContainerView.configureWithOpenGraphInfoType(openGraphInfo)
openGraphURL = openGraphInfo.URL
}
}
}
@@ -10,10 +10,22 @@ import UIKit
class ChatRightVideoCell: ChatRightBaseCell {
@IBOutlet weak var thumbnailImageView: UIImageView!
@IBOutlet weak var borderImageView: UIImageView!
lazy var thumbnailImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.tintColor = UIColor.rightBubbleTintColor()
return imageView
}()
@IBOutlet weak var playImageView: UIImageView!
lazy var borderImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "right_tail_image_bubble_border"))
return imageView
}()
lazy var playImageView: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "icon_playvideo"))
return imageView
}()
typealias MediaTapAction = () -> Void
var mediaTapAction: MediaTapAction?
@@ -27,15 +39,17 @@ class ChatRightVideoCell: ChatRightBaseCell {
avatarImageView.center = CGPoint(x: fullWidth - halfAvatarSize - YepConfig.chatCellGapBetweenWallAndAvatar(), y: halfAvatarSize)
}
override func awakeFromNib() {
super.awakeFromNib()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.addSubview(thumbnailImageView)
contentView.addSubview(borderImageView)
contentView.addSubview(playImageView)
UIView.performWithoutAnimation { [weak self] in
self?.makeUI()
}
thumbnailImageView.tintColor = UIColor.rightBubbleTintColor()
thumbnailImageView.userInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: "tapMediaView")
thumbnailImageView.addGestureRecognizer(tap)
@@ -45,6 +59,10 @@ class ChatRightVideoCell: ChatRightBaseCell {
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func tapMediaView() {
mediaTapAction?()
}
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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" id="gTV-IL-0wX" customClass="ChatRightVideoCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="249" height="115"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="249" height="115"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="cv4-jk-ysG" userLabel="Avatar Image View">
<rect key="frame" x="194" y="0.0" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="Usu-mV-m39" userLabel="Thumbnail Image View">
<rect key="frame" x="63" y="0.0" width="126" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="right_tail_image_bubble_border" id="Wb3-oN-r4k" userLabel="Border Image View">
<rect key="frame" x="63" y="0.0" width="126" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_playvideo" id="4Zc-y0-R7I" userLabel="Play Image View">
<rect key="frame" x="106" y="43" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_dot_sending" id="t6J-8M-Prm" userLabel="Dot Image View">
<rect key="frame" x="38" y="45" width="26" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<size key="customSize" width="249" height="115"/>
<connections>
<outlet property="avatarImageView" destination="cv4-jk-ysG" id="pQU-nA-A0W"/>
<outlet property="borderImageView" destination="Wb3-oN-r4k" id="eHZ-GL-ipX"/>
<outlet property="dotImageView" destination="t6J-8M-Prm" id="gDj-YZ-VfS"/>
<outlet property="playImageView" destination="4Zc-y0-R7I" id="yph-te-Ga1"/>
<outlet property="thumbnailImageView" destination="Usu-mV-m39" id="Vsi-Q8-Ywh"/>
</connections>
<point key="canvasLocation" x="329.5" y="414.5"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_dot_sending" width="26" height="26"/>
<image name="icon_playvideo" width="30" height="30"/>
<image name="right_tail_image_bubble_border" width="48" height="45"/>
</resources>
</document>
+8 -2
View File
@@ -129,12 +129,18 @@ class FeedBasicCell: UITableViewCell {
let view = FeedUploadingErrorContainerView(frame: CGRect(x: 0, y: 0, width: 200, height: 30))
return view
}()
var messagesCountEqualsZero = false {
didSet {
messageCountLabel.hidden = messagesCountEqualsZero
}
}
var hasUploadingErrorMessage = false {
didSet {
uploadingErrorContainerView.hidden = !hasUploadingErrorMessage
leftBottomLabel.hidden = hasUploadingErrorMessage
messageCountLabel.hidden = hasUploadingErrorMessage
messageCountLabel.hidden = hasUploadingErrorMessage || (self.messagesCountEqualsZero)
discussionImageView.hidden = hasUploadingErrorMessage
}
}
@@ -261,7 +267,7 @@ class FeedBasicCell: UITableViewCell {
let messagesCountString = feed.messagesCount > 99 ? "99+" : "\(feed.messagesCount)"
messageCountLabel.text = messagesCountString
messageCountLabel.hidden = (feed.messagesCount == 0)
messagesCountEqualsZero = (feed.messagesCount == 0)
if let basicLayout = layout?.basicLayout {
leftBottomLabel.frame = basicLayout.leftBottomLabelFrame
+17
View File
@@ -111,6 +111,11 @@ struct FeedCellLayout {
}
var locationLayout: LocationLayout?
struct URLLayout {
let URLContainerViewFrame: CGRect
}
var _URLLayout: URLLayout?
// MARK: - Init
init(height: CGFloat, basicLayout: BasicLayout) {
@@ -127,6 +132,9 @@ struct FeedCellLayout {
case .Text:
height = FeedBasicCell.heightOfFeed(feed)
case .URL:
height = FeedURLCell.heightOfFeed(feed)
case .Image:
if feed.imageAttachmentsCount == 1 {
height = FeedBiggerImageCell.heightOfFeed(feed)
@@ -214,6 +222,15 @@ struct FeedCellLayout {
case .Text:
break
case .URL:
let height: CGFloat = leftBottomLabelFrame.origin.y - beginY - 15
let URLContainerViewFrame = CGRect(x: 65, y: beginY, width: screenWidth - 65 - 60, height: height)
let _URLLayout = FeedCellLayout.URLLayout(URLContainerViewFrame: URLContainerViewFrame)
self._URLLayout = _URLLayout
case .Image:
+74
View File
@@ -0,0 +1,74 @@
//
// FeedURLCell.swift
// Yep
//
// Created by nixzhu on 16/1/14.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import UIKit
private let screenWidth: CGFloat = UIScreen.mainScreen().bounds.width
class FeedURLCell: FeedBasicCell {
var tapURLInfoAction: ((URL: NSURL) -> Void)?
lazy var feedURLContainerView: FeedURLContainerView = {
let view = FeedURLContainerView()
view.compressionMode = false
return view
}()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(feedURLContainerView)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override class func heightOfFeed(feed: DiscoveredFeed) -> CGFloat {
let height = super.heightOfFeed(feed) + (100 + 15)
return ceil(height)
}
override func configureWithFeed(feed: DiscoveredFeed, layoutCache: FeedCellLayout.Cache, needShowSkill: Bool) {
//var _newLayout: FeedCellLayout?
super.configureWithFeed(feed, layoutCache: (layout: layoutCache.layout, update: { newLayout in
//_newLayout = newLayout
}), needShowSkill: needShowSkill)
if let _URLLayout = layoutCache.layout?._URLLayout {
feedURLContainerView.frame = _URLLayout.URLContainerViewFrame
} else {
let y = messageTextView.frame.origin.y + messageTextView.frame.height + 15
let height: CGFloat = leftBottomLabel.frame.origin.y - y - 15
feedURLContainerView.frame = CGRect(x: 65, y: y, width: screenWidth - 65 - 60, height: height)
}
if let attachment = feed.attachment {
if case let .URL(openGraphInfo) = attachment {
feedURLContainerView.configureWithOpenGraphInfoType(openGraphInfo)
feedURLContainerView.tapAction = { [weak self] in
self?.tapURLInfoAction?(URL: openGraphInfo.URL)
}
}
}
}
}
@@ -7,14 +7,85 @@
//
import UIKit
import CoreLocation
import RealmSwift
class ProfileFooterCell: UICollectionViewCell {
@IBOutlet weak var nicknameLabel: UILabel!
@IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var locationContainerView: UIView!
@IBOutlet weak var locationLabel: UILabel!
@IBOutlet weak var introductionLabel: UILabel!
@IBOutlet weak var instroductionLabelLeftConstraint: NSLayoutConstraint!
@IBOutlet weak var instroductionLabelRightConstraint: NSLayoutConstraint!
@IBOutlet weak var introductionLabel: UILabel!
private struct Listener {
let userLocationName: String
}
private lazy var listener: Listener = {
let suffix = NSUUID().UUIDString
return Listener(userLocationName: "ProfileFooterCell.userLocationName" + suffix)
}()
deinit {
YepUserDefaults.userLocationName.removeListenerWithName(listener.userLocationName)
}
private func updateUIWithLocationName(userLocationName: String?) {
if let userLocationName = userLocationName {
locationContainerView.hidden = false
locationLabel.text = userLocationName
} else {
locationContainerView.hidden = true
}
}
var userID: String? {
didSet {
if let userID = userID, realm = try? Realm(), userLocationName = UserLocationName.withUserID(userID, inRealm: realm) {
newLocationName = userLocationName.locationName
}
}
}
var profileUserIsMe = false {
didSet {
if profileUserIsMe {
YepUserDefaults.userLocationName.bindAndFireListener(listener.userLocationName, action: { [weak self] userLocationName in
self?.updateUIWithLocationName(userLocationName)
})
}
}
}
var newLocationName: String? {
didSet {
if profileUserIsMe {
YepUserDefaults.userLocationName.value = newLocationName
} else {
updateUIWithLocationName(newLocationName)
}
// save it
if let realm = try? Realm(), userID = userID, locationName = newLocationName {
let userLocationName = UserLocationName(userID: userID, locationName: locationName)
let _ = try? realm.write {
realm.add(userLocationName, update: true)
}
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
@@ -23,6 +94,66 @@ class ProfileFooterCell: UICollectionViewCell {
introductionLabel.font = YepConfig.Profile.introductionLabelFont
introductionLabel.textColor = UIColor.yepGrayColor()
newLocationName = nil
}
func configureWithProfileUser(profileUser: ProfileUser, introduction: String) {
userID = profileUser.userID
profileUserIsMe = profileUser.isMe
configureWithNickname(profileUser.nickname ?? "", username: profileUser.username, introduction: introduction)
switch profileUser {
case .DiscoveredUserType(let discoveredUser):
location = CLLocation(latitude: discoveredUser.latitude, longitude: discoveredUser.longitude)
case .UserType(let user):
location = CLLocation(latitude: user.latitude, longitude: user.longitude)
}
}
private func configureWithNickname(nickname: String, username: String?, introduction: String) {
nicknameLabel.text = nickname
if let username = username {
usernameLabel.text = "@" + username
} else {
usernameLabel.text = NSLocalizedString("No username", comment: "")
}
introductionLabel.text = introduction
}
var location: CLLocation? {
didSet {
if let location = location {
//
if let oldLocation = oldValue {
let distance = location.distanceFromLocation(oldLocation)
if distance < YepConfig.Location.distanceThreshold {
return
}
}
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in
dispatch_async(dispatch_get_main_queue()) { [weak self] in
if (error != nil) {
println("\(location) reverse geodcode fail: \(error?.localizedDescription)")
self?.location = nil
}
if let placemarks = placemarks {
if let firstPlacemark = placemarks.first {
self?.newLocationName = firstPlacemark.locality ?? (firstPlacemark.name ?? firstPlacemark.country)
}
}
}
})
}
}
}
}
@@ -1,38 +1,88 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="14F27" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</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="ProfileFooterCell" id="gTV-IL-0wX" customClass="ProfileFooterCell" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="180"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="180"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="I would like to learn Design or Speech, I can teach you iOS Dev in return. 😃" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Yw-Z7-Wv4" userLabel="Introduction Label">
<rect key="frame" x="38" y="11" width="244" height="29"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="I would like to learn Design or Speech, I can teach you iOS Dev in return. 😃" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5Yw-Z7-Wv4" userLabel="Introduction Label">
<rect key="frame" x="38" y="66" width="244" height="29"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="12"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NIX" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NxA-Mj-WGn">
<rect key="frame" x="38" y="10" width="32.5" height="24"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@nixzhu" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jnP-ym-ZBH">
<rect key="frame" x="38" y="38" width="58.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="q7V-Cn-Anl">
<rect key="frame" x="184.5" y="8" width="97.5" height="27"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon_pin_mini_gray" translatesAutoresizingMaskIntoConstraints="NO" id="jsS-4E-7vx">
<rect key="frame" x="0.0" y="6" width="12" height="15"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GuangZhou" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="azE-9R-4mk">
<rect key="frame" x="17" y="5" width="80.5" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="jsS-4E-7vx" firstAttribute="centerY" secondItem="q7V-Cn-Anl" secondAttribute="centerY" id="0RP-ev-VLf"/>
<constraint firstItem="jsS-4E-7vx" firstAttribute="leading" secondItem="q7V-Cn-Anl" secondAttribute="leading" id="171-4j-mBj"/>
<constraint firstAttribute="trailing" secondItem="azE-9R-4mk" secondAttribute="trailing" id="7xb-Uh-szg"/>
<constraint firstItem="azE-9R-4mk" firstAttribute="centerY" secondItem="jsS-4E-7vx" secondAttribute="centerY" id="Ig7-tV-R7n"/>
<constraint firstItem="azE-9R-4mk" firstAttribute="leading" secondItem="jsS-4E-7vx" secondAttribute="trailing" constant="5" id="kUB-Yc-6Jj"/>
<constraint firstAttribute="height" constant="27" id="tYV-wG-GDr"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
<constraints>
<constraint firstAttribute="centerY" secondItem="5Yw-Z7-Wv4" secondAttribute="centerY" constant="-0.5" id="5sP-Cg-pnW"/>
<constraint firstItem="q7V-Cn-Anl" firstAttribute="trailing" secondItem="5Yw-Z7-Wv4" secondAttribute="trailing" id="BCY-lQ-RaB"/>
<constraint firstItem="q7V-Cn-Anl" firstAttribute="centerY" secondItem="NxA-Mj-WGn" secondAttribute="centerY" id="C9L-QR-7ua"/>
<constraint firstItem="jnP-ym-ZBH" firstAttribute="top" secondItem="NxA-Mj-WGn" secondAttribute="bottom" constant="4" id="G2q-tX-3IP"/>
<constraint firstAttribute="trailing" secondItem="5Yw-Z7-Wv4" secondAttribute="trailing" constant="38" id="Grg-4P-8QR"/>
<constraint firstItem="5Yw-Z7-Wv4" firstAttribute="leading" secondItem="gTV-IL-0wX" secondAttribute="leading" constant="38" id="HPJ-WR-pZU"/>
<constraint firstItem="jnP-ym-ZBH" firstAttribute="leading" secondItem="5Yw-Z7-Wv4" secondAttribute="leading" id="HjV-WR-RVa"/>
<constraint firstItem="q7V-Cn-Anl" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="NxA-Mj-WGn" secondAttribute="trailing" id="Lbh-4Z-tcy"/>
<constraint firstItem="jnP-ym-ZBH" firstAttribute="leading" secondItem="NxA-Mj-WGn" secondAttribute="leading" id="cPb-4n-LbM"/>
<constraint firstItem="5Yw-Z7-Wv4" firstAttribute="top" secondItem="jnP-ym-ZBH" secondAttribute="bottom" constant="10" id="hD9-Ye-HHX"/>
<constraint firstItem="NxA-Mj-WGn" firstAttribute="top" secondItem="gTV-IL-0wX" secondAttribute="top" constant="10" id="uPe-lC-dCc"/>
</constraints>
<size key="customSize" width="320" height="50"/>
<size key="customSize" width="320" height="180"/>
<connections>
<outlet property="instroductionLabelLeftConstraint" destination="HPJ-WR-pZU" id="jFC-zO-l0X"/>
<outlet property="instroductionLabelRightConstraint" destination="Grg-4P-8QR" id="UCI-Hb-Phd"/>
<outlet property="introductionLabel" destination="5Yw-Z7-Wv4" id="2To-TB-hWM"/>
<outlet property="locationContainerView" destination="q7V-Cn-Anl" id="gd6-24-ezX"/>
<outlet property="locationLabel" destination="azE-9R-4mk" id="ei5-vp-90U"/>
<outlet property="nicknameLabel" destination="NxA-Mj-WGn" id="KJe-zP-OW6"/>
<outlet property="usernameLabel" destination="jnP-ym-ZBH" id="e49-HD-LL1"/>
</connections>
<point key="canvasLocation" x="237" y="285"/>
<point key="canvasLocation" x="237" y="350"/>
</collectionViewCell>
</objects>
<resources>
<image name="icon_pin_mini_gray" width="12" height="15"/>
</resources>
</document>
@@ -28,6 +28,8 @@ class ProfileHeaderCell: UICollectionViewCell {
override func awakeFromNib() {
super.awakeFromNib()
locationLabel.hidden = true
}
var blurredAvatarImage: UIImage? {
@@ -36,6 +38,7 @@ class ProfileHeaderCell: UICollectionViewCell {
}
}
/*
var location: CLLocation? {
didSet {
if let location = location {
@@ -68,17 +71,19 @@ class ProfileHeaderCell: UICollectionViewCell {
}
}
}
*/
func configureWithDiscoveredUser(discoveredUser: DiscoveredUser) {
updateAvatarWithAvatarURLString(discoveredUser.avatarURLString)
location = CLLocation(latitude: discoveredUser.latitude, longitude: discoveredUser.longitude)
//location = CLLocation(latitude: discoveredUser.latitude, longitude: discoveredUser.longitude)
}
func configureWithUser(user: User) {
updateAvatarWithAvatarURLString(user.avatarURLString)
/*
if user.friendState == UserFriendState.Me.rawValue {
if !askedForPermission {
@@ -101,6 +106,7 @@ class ProfileHeaderCell: UICollectionViewCell {
}
location = CLLocation(latitude: user.latitude, longitude: user.longitude)
*/
}
func blurImage(image: UIImage, completion: UIImage -> Void) {
@@ -0,0 +1,163 @@
//
// FeedURLContainerView.swift
// Yep
//
// Created by nixzhu on 16/1/14.
// Copyright © 2016 Catch Inc. All rights reserved.
//
import UIKit
class FeedURLContainerView: UIView {
var tapAction: (() -> Void)?
private var needMakeUI: Bool = true
var directionLeading = true { // set before compressionMode
didSet {
if directionLeading {
backgroundImageView.image = UIImage(named: "url_container_left_background")
} else {
backgroundImageView.image = UIImage(named: "url_container_right_background")
}
}
}
var compressionMode: Bool = false {
didSet {
if needMakeUI {
makeUI()
needMakeUI = false
}
}
}
lazy var backgroundImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = UIImage(named: "url_container_left_background")
return imageView
}()
lazy var siteNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(12)
label.textColor = UIColor.lightGrayColor()
return label
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(12)
label.textColor = UIColor.blackColor()
return label
}()
lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFontOfSize(10)
label.textColor = UIColor.lightGrayColor()
label.numberOfLines = 0
return label
}()
lazy var thumbnailImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .ScaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
override func didMoveToSuperview() {
super.didMoveToSuperview()
let tap = UITapGestureRecognizer(target: self, action: "tap:")
addGestureRecognizer(tap)
}
private func makeUI() {
addSubview(backgroundImageView)
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
addSubview(siteNameLabel)
addSubview(titleLabel)
siteNameLabel.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
let bottomContainerView = UIView()
addSubview(bottomContainerView)
bottomContainerView.translatesAutoresizingMaskIntoConstraints = false
bottomContainerView.addSubview(descriptionLabel)
bottomContainerView.addSubview(thumbnailImageView)
descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false
let views = [
"backgroundImageView": backgroundImageView,
"siteNameLabel": siteNameLabel,
"titleLabel": titleLabel,
"bottomContainerView": bottomContainerView,
"descriptionLabel": descriptionLabel,
"thumbnailImageView": thumbnailImageView,
]
do {
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[backgroundImageView]|", options: [], metrics: nil, views: views)
let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|[backgroundImageView]|", options: [], metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(constraintsH)
NSLayoutConstraint.activateConstraints(constraintsV)
}
do {
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[siteNameLabel]-|", options: [], metrics: nil, views: views)
let metrics = [
"top": compressionMode ? 4 : 8,
"gap": compressionMode ? 4 : 8,
"bottom": compressionMode ? 4 : 8,
]
let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-(top)-[siteNameLabel(15)]-(gap)-[titleLabel(15)]-(gap)-[bottomContainerView]-(bottom)-|", options: [.AlignAllLeading, .AlignAllTrailing], metrics: metrics, views: views)
NSLayoutConstraint.activateConstraints(constraintsH)
NSLayoutConstraint.activateConstraints(constraintsV)
}
do {
let metrics = [
"imageSize": compressionMode ? 35 : 40,
]
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[descriptionLabel]-[thumbnailImageView(imageSize)]|", options: [.AlignAllTop], metrics: metrics, views: views)
let constraintsV1 = NSLayoutConstraint.constraintsWithVisualFormat("V:|[descriptionLabel]-(>=0)-|", options: [.AlignAllLeading, .AlignAllTrailing], metrics: nil, views: views)
let constraintsV2 = NSLayoutConstraint.constraintsWithVisualFormat("V:|[thumbnailImageView(imageSize)]", options: [.AlignAllLeading, .AlignAllTrailing], metrics: metrics, views: views)
NSLayoutConstraint.activateConstraints(constraintsH)
NSLayoutConstraint.activateConstraints(constraintsV1)
NSLayoutConstraint.activateConstraints(constraintsV2)
}
}
@objc private func tap(sender: UITapGestureRecognizer) {
tapAction?()
}
func configureWithOpenGraphInfoType(openGraphInfo: OpenGraphInfoType) {
siteNameLabel.text = openGraphInfo.siteName
titleLabel.text = openGraphInfo.title
descriptionLabel.text = openGraphInfo.infoDescription
if let thumbnailImageURL = NSURL(string: openGraphInfo.thumbnailImageURLString) {
thumbnailImageView.kf_setImageWithURL(thumbnailImageURL, placeholderImage: nil)
} else {
thumbnailImageView.image = nil
thumbnailImageView.backgroundColor = UIColor.lightGrayColor()
}
}
}
+39
View File
@@ -49,6 +49,7 @@ class FeedView: UIView {
var tapGithubRepoAction: (NSURL -> Void)?
var tapDribbbleShotAction: (NSURL -> Void)?
var tapLocationAction: ((locationName: String, locationCoordinate: CLLocationCoordinate2D) -> Void)?
var tapURLInfoAction: ((URL: NSURL) -> Void)?
static let foldHeight: CGFloat = 60
@@ -157,6 +158,12 @@ class FeedView: UIView {
@IBOutlet weak var socialWorkBorderImageView: UIImageView!
@IBOutlet weak var feedURLContainerView: FeedURLContainerView! {
didSet {
feedURLContainerView.compressionMode = true
}
}
@IBOutlet weak var timeLabel: UILabel!
@IBOutlet weak var timeLabelTopConstraint: NSLayoutConstraint!
@@ -238,6 +245,9 @@ class FeedView: UIView {
let tapLocation = UITapGestureRecognizer(target: self, action: "tapLocation:")
locationContainerView.addGestureRecognizer(tapLocation)
let tapURLInfo = UITapGestureRecognizer(target: self, action: "tapURLInfo:")
feedURLContainerView.addGestureRecognizer(tapURLInfo)
}
func switchFold(sender: UITapGestureRecognizer) {
@@ -336,6 +346,23 @@ class FeedView: UIView {
mediaCollectionView.hidden = true
socialWorkContainerView.hidden = true
voiceContainerView.hidden = true
feedURLContainerView.hidden = true
case .URL:
mediaCollectionView.hidden = true
socialWorkContainerView.hidden = false
voiceContainerView.hidden = true
feedURLContainerView.hidden = false
socialWorkBorderImageView.hidden = true
socialWorkContainerViewHeightConstraint.constant = 80
if let openGraphInfo = feed.openGraphInfo {
feedURLContainerView.configureWithOpenGraphInfoType(openGraphInfo)
}
case .Image:
@@ -355,6 +382,7 @@ class FeedView: UIView {
githubRepoContainerView.hidden = false
voiceContainerView.hidden = true
locationContainerView.hidden = true
feedURLContainerView.hidden = true
socialWorkBorderImageView.hidden = false
@@ -376,6 +404,7 @@ class FeedView: UIView {
githubRepoContainerView.hidden = true
voiceContainerView.hidden = true
locationContainerView.hidden = true
feedURLContainerView.hidden = true
socialWorkBorderImageView.hidden = false
@@ -395,6 +424,7 @@ class FeedView: UIView {
githubRepoContainerView.hidden = true
voiceContainerView.hidden = false
locationContainerView.hidden = true
feedURLContainerView.hidden = true
socialWorkBorderImageView.hidden = true
@@ -421,6 +451,7 @@ class FeedView: UIView {
githubRepoContainerView.hidden = true
voiceContainerView.hidden = true
locationContainerView.hidden = false
feedURLContainerView.hidden = true
socialWorkBorderImageView.hidden = false
@@ -480,6 +511,14 @@ class FeedView: UIView {
tapLocationAction?(locationName: locationName, locationCoordinate: locationCoordinate)
}
func tapURLInfo(sender: UITapGestureRecognizer) {
guard let URL = feed?.openGraphInfo?.URL else {
return
}
tapURLInfoAction?(URL: URL)
}
@IBAction func playOrPauseAudio(sender: UIButton) {
if AVAudioSession.sharedInstance().category == AVAudioSessionCategoryRecord {
+9
View File
@@ -221,15 +221,21 @@
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="social_work_border" translatesAutoresizingMaskIntoConstraints="NO" id="4I1-rL-pwe">
<rect key="frame" x="0.0" y="0.0" width="294" height="80"/>
</imageView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hda-NM-a6m" customClass="FeedURLContainerView" customModule="Yep" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="294" height="80"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="5Md-c8-aoz" firstAttribute="trailing" secondItem="hRL-ts-we1" secondAttribute="trailing" constant="15" id="0iM-so-NGq"/>
<constraint firstAttribute="trailing" secondItem="hda-NM-a6m" secondAttribute="trailing" id="1o4-ge-6bb"/>
<constraint firstAttribute="bottom" secondItem="5Md-c8-aoz" secondAttribute="bottom" constant="1" id="1rt-eE-fH0"/>
<constraint firstAttribute="trailing" secondItem="loB-Af-q0W" secondAttribute="trailing" id="4tB-zm-8AF"/>
<constraint firstItem="loB-Af-q0W" firstAttribute="leading" secondItem="PFV-2X-1gy" secondAttribute="leading" id="DyM-BQ-fUc"/>
<constraint firstAttribute="trailing" secondItem="4I1-rL-pwe" secondAttribute="trailing" id="Ffz-uu-zWE"/>
<constraint firstItem="E4l-Kq-xE7" firstAttribute="centerY" secondItem="PFV-2X-1gy" secondAttribute="centerY" id="Fhu-K7-CHG"/>
<constraint firstAttribute="bottom" secondItem="hda-NM-a6m" secondAttribute="bottom" id="Fn2-4S-rKZ"/>
<constraint firstItem="5Md-c8-aoz" firstAttribute="leading" secondItem="PFV-2X-1gy" secondAttribute="leading" constant="1" id="N3W-8D-VA0"/>
<constraint firstItem="4I1-rL-pwe" firstAttribute="top" secondItem="PFV-2X-1gy" secondAttribute="top" id="PPc-Cb-hPs"/>
<constraint firstItem="hRL-ts-we1" firstAttribute="leading" secondItem="5Md-c8-aoz" secondAttribute="leading" constant="15" id="UJh-UY-LRT"/>
@@ -241,6 +247,8 @@
<constraint firstItem="loB-Af-q0W" firstAttribute="top" secondItem="PFV-2X-1gy" secondAttribute="top" id="fqs-kR-PUo"/>
<constraint firstItem="4I1-rL-pwe" firstAttribute="leading" secondItem="PFV-2X-1gy" secondAttribute="leading" id="gFu-jo-r4a"/>
<constraint firstAttribute="bottom" secondItem="loB-Af-q0W" secondAttribute="bottom" id="kAU-lH-td2"/>
<constraint firstItem="hda-NM-a6m" firstAttribute="top" secondItem="PFV-2X-1gy" secondAttribute="top" id="n3R-zA-U9x"/>
<constraint firstItem="hda-NM-a6m" firstAttribute="leading" secondItem="PFV-2X-1gy" secondAttribute="leading" id="qMT-zg-xEI"/>
<constraint firstAttribute="height" constant="80" id="utb-hg-0Rs"/>
</constraints>
</view>
@@ -282,6 +290,7 @@
<outlet property="avatarImageView" destination="0Hb-fw-n9b" id="AAN-W3-uXW"/>
<outlet property="distanceLabel" destination="ffh-Mx-17Q" id="rwL-f4-mRt"/>
<outlet property="dotLabel" destination="8CD-nd-UWy" id="Ro3-fd-cTh"/>
<outlet property="feedURLContainerView" destination="hda-NM-a6m" id="Z23-fK-bTI"/>
<outlet property="githubRepoContainerView" destination="hRL-ts-we1" id="ru8-BI-3nb"/>
<outlet property="githubRepoDescriptionLabel" destination="put-dx-KAt" id="5TU-Of-nB9"/>
<outlet property="githubRepoImageView" destination="Bgr-wb-WD7" id="D1Z-jW-yui"/>
+6
View File
@@ -35,6 +35,12 @@ private class MentionUserCell: UITableViewCell {
return label
}()
private override func prepareForReuse() {
super.prepareForReuse()
avatarImageView.image = nil
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
+38 -23
View File
@@ -166,13 +166,14 @@ class MessageToolbar: UIToolbar {
button.tintAdjustmentMode = .Normal
button.addTarget(self, action: "toggleRecordVoice", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
}()
let normalCornerRadius: CGFloat = 6
lazy var messageTextView: UITextView = {
let textView = UITextView()
textView.textContainerInset = UIEdgeInsets(top: 8, left: 4, bottom: 8, right: 4)
//textView.textContainerInset = UIEdgeInsetsZero
textView.font = UIFont.systemFontOfSize(15)
textView.layer.borderWidth = 1
textView.layer.borderColor = UIColor.yepMessageToolbarSubviewBorderColor().CGColor
@@ -180,7 +181,7 @@ class MessageToolbar: UIToolbar {
textView.delegate = self
textView.scrollEnabled = false // top inset
return textView
}()
}()
lazy var voiceRecordButton: VoiceRecordButton = {
let button = VoiceRecordButton()
@@ -214,7 +215,7 @@ class MessageToolbar: UIToolbar {
}
return button
}()
}()
lazy var moreButton: UIButton = {
let button = UIButton()
@@ -223,7 +224,7 @@ class MessageToolbar: UIToolbar {
button.tintAdjustmentMode = .Normal
button.addTarget(self, action: "moreMessageTypes", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
}()
lazy var sendButton: UIButton = {
let button = UIButton()
@@ -233,7 +234,7 @@ class MessageToolbar: UIToolbar {
button.setTitleColor(UIColor.messageToolBarHighlightColor(), forState: .Normal)
button.addTarget(self, action: "trySendTextMessage", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
}()
// MARK: UI
@@ -281,6 +282,7 @@ class MessageToolbar: UIToolbar {
let textContainerInset = messageTextView.textContainerInset
let constant = ceil(messageTextView.font!.lineHeight + textContainerInset.top + textContainerInset.bottom)
messageTextViewHeightConstraint = NSLayoutConstraint(item: messageTextView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: constant)
messageTextViewHeightConstraint.priority = UILayoutPriorityDefaultHigh
let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[micButton(48)][messageTextView][moreButton(==micButton)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewsDictionary)
@@ -363,10 +365,19 @@ class MessageToolbar: UIToolbar {
//println("oldHeight: \(messageTextViewHeightConstraint.constant), newHeight: \(newHeight)")
if newHeight != messageTextViewHeightConstraint.constant {
UIView.animateWithDuration(0.1, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
UIView.animateWithDuration(0.1, delay: 0.0, options: .CurveEaseInOut, animations: {
self.messageTextViewHeightConstraint.constant = newHeight
self.layoutIfNeeded()
}, completion: { (finished) -> Void in
}, completion: { [weak self] finished in
// hack for scrollEnabled when input lots of text
if finished, let strongSelf = self {
//println("messageToolbar.frame: \(strongSelf.frame)")
let enabled = strongSelf.frame.origin.y < 100
strongSelf.messageTextView.scrollEnabled = enabled
}
})
}
}
@@ -375,27 +386,29 @@ class MessageToolbar: UIToolbar {
func updateDraft(notification: NSNotification) {
if let conversation = conversation, realm = conversation.realm {
guard let conversation = conversation where !conversation.invalidated, let realm = conversation.realm else {
return
}
if let draft = conversation.draft {
if let draft = conversation.draft {
let _ = try? realm.write { [weak self] in
if let strongSelf = self {
draft.messageToolbarState = strongSelf.state.rawValue
let _ = try? realm.write { [weak self] in
if let strongSelf = self {
draft.messageToolbarState = strongSelf.state.rawValue
if strongSelf.state == .BeginTextInput || strongSelf.state == .TextInputing {
draft.text = strongSelf.messageTextView.text
}
}
//if strongSelf.state == .BeginTextInput || strongSelf.state == .TextInputing {
println("strongSelf.messageTextView.text: \(strongSelf.messageTextView.text)")
draft.text = strongSelf.messageTextView.text
//}
}
}
} else {
let draft = Draft()
draft.messageToolbarState = state.rawValue
let _ = try? realm.write {
conversation.draft = draft
}
} else {
let draft = Draft()
draft.messageToolbarState = state.rawValue
let _ = try? realm.write {
conversation.draft = draft
}
}
}
@@ -499,6 +512,8 @@ extension MessageToolbar: UITextViewDelegate {
//println("mentionWord: \(wordString), \(mentionWordRange)")
mentionUsernameRange = mentionWordRange
let wordString = wordString.trimming(.Whitespace)
tryMentionUserAction?(usernamePrefix: wordString)
return
+336
View File
@@ -10,6 +10,58 @@ import UIKit
class ChatTextView: UITextView {
var tapMentionAction: ((username: String) -> Void)?
static let detectionTypeName = "ChatTextStorage.detectionTypeName"
enum DetectionType: String {
case Mention
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
self.delegate = self
editable = false
dataDetectorTypes = [.Link, .PhoneNumber, .CalendarEvent]
}
override var text: String! {
didSet {
let attributedString = NSMutableAttributedString(string: text)
let textRange = NSMakeRange(0, (text as NSString).length)
attributedString.addAttribute(NSForegroundColorAttributeName, value: textColor!, range: textRange)
attributedString.addAttribute(NSFontAttributeName, value: font!, range: textRange)
// mention link
let mentionPattern = "[@]([A-Za-z0-9_]{4,16})"
let mentionExpression = try! NSRegularExpression(pattern: mentionPattern, options: NSRegularExpressionOptions())
mentionExpression.enumerateMatchesInString(text, options: NSMatchingOptions(), range: textRange, usingBlock: { result, flags, stop in
if let result = result {
let textValue = (self.text as NSString).substringWithRange(result.range)
let textAttributes: [String: AnyObject] = [
NSLinkAttributeName: textValue,
ChatTextView.detectionTypeName: DetectionType.Mention.rawValue,
]
attributedString.addAttributes(textAttributes, range: result.range )
}
})
self.attributedText = attributedString
}
}
// MARK: hack
override func canBecomeFirstResponder() -> Bool {
return false
}
@@ -30,3 +82,287 @@ class ChatTextView: UITextView {
}
}
extension ChatTextView: UITextViewDelegate {
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
guard let detectionTypeName = self.attributedText.attribute(ChatTextView.detectionTypeName, atIndex: characterRange.location, effectiveRange: nil) as? String, detectionType = DetectionType(rawValue: detectionTypeName) else {
return true
}
let text = (self.text as NSString).substringWithRange(characterRange)
self.hangleTapText(text, withDetectionType: detectionType)
return true
}
private func hangleTapText(text: String, withDetectionType detectionType: DetectionType) {
println("hangleTapText: \(text), \(detectionType)")
let username = text.substringFromIndex(text.startIndex.advancedBy(1))
if !username.isEmpty {
tapMentionAction?(username: username)
}
}
}
/*
// MARK: ChatTextStorage
class ChatTextStorage: NSTextStorage {
static let detectionTypeName = "ChatTextStorage.detectionTypeName"
enum DetectionType: String {
case Mention
}
private var backingStore: NSMutableAttributedString = NSMutableAttributedString()
var mentionRanges = [NSRange]()
var mentionForegroundColor: UIColor = UIColor.redColor()
override var string: String {
return backingStore.string
}
override func attributesAtIndex(index: Int, effectiveRange range: NSRangePointer) -> [String : AnyObject] {
return backingStore.attributesAtIndex(index, effectiveRange: range)
}
override func replaceCharactersInRange(range: NSRange, withString str: String) {
beginEditing()
backingStore.replaceCharactersInRange(range, withString: str)
edited([.EditedCharacters, .EditedAttributes], range: range, changeInLength: (str as NSString).length - range.length)
endEditing()
}
override func setAttributes(attrs: [String : AnyObject]!, range: NSRange) {
beginEditing()
backingStore.setAttributes(attrs, range: range)
edited(.EditedAttributes, range: range, changeInLength: 0)
endEditing()
}
override func addAttributes(attrs: [String : AnyObject], range: NSRange) {
beginEditing()
backingStore.addAttributes(attrs, range: range)
edited(.EditedAttributes, range: range, changeInLength: 0)
endEditing()
}
override func processEditing() {
let paragraphRange = (self.string as NSString).paragraphRangeForRange(self.editedRange)
//For Mention
mentionRanges.removeAll()
let mentionPattern = "@[^\\s:,@]+$?"
let mentionExpression = try? NSRegularExpression(pattern: mentionPattern, options: NSRegularExpressionOptions())
if let mentionExpression = mentionExpression {
mentionExpression.enumerateMatchesInString(self.string, options: NSMatchingOptions(), range: paragraphRange, usingBlock: { result, flags, stop in
if let result = result {
let textAttributes: [String: AnyObject] = [
NSForegroundColorAttributeName: self.mentionForegroundColor,
ChatTextStorage.detectionTypeName: DetectionType.Mention.rawValue,
]
self.addAttributes(textAttributes, range: result.range )
self.mentionRanges.append(result.range)
}
})
}
super.processEditing()
}
}
// MARK: ChatTextView
class ChatTextView: UITextView {
var tapMentionAction: ((username: String) -> Void)?
var chatTextStorage: ChatTextStorage {
return self.textStorage as! ChatTextStorage
}
required init?(coder aDecoder: NSCoder) {
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer()
textContainer.widthTracksTextView = true
textContainer.heightTracksTextView = true
layoutManager.addTextContainer(textContainer)
let textStorage = ChatTextStorage()
textStorage.addLayoutManager(layoutManager)
super.init(frame: CGRectZero, textContainer: textContainer)
}
var linkTapGestureRecognizer: UITapGestureRecognizer?
var linkTapEnabled: Bool = false {
willSet {
func tryRemoveLinkTapGestureRecognizer() {
if let linkTapGestureRecognizer = self.linkTapGestureRecognizer {
self.removeGestureRecognizer(linkTapGestureRecognizer)
}
}
func addLinkTapGestureRecognizer() {
let linkTapGestureRecognizer = UITapGestureRecognizer(target: self, action: "linkTapped:")
linkTapGestureRecognizer.delegate = self
self.addGestureRecognizer(linkTapGestureRecognizer)
self.linkTapGestureRecognizer = linkTapGestureRecognizer
}
if newValue {
tryRemoveLinkTapGestureRecognizer()
addLinkTapGestureRecognizer()
} else {
tryRemoveLinkTapGestureRecognizer()
}
}
}
@objc private func linkTapped(sender: UITapGestureRecognizer) {
println("linkTapped")
let location = sender.locationInView(self)
enumerateLinkRangesContainingLocation(location) { range in
guard let detectionTypeName = self.attributedText.attribute(ChatTextStorage.detectionTypeName, atIndex: range.location, effectiveRange: nil) as? String, detectionType = ChatTextStorage.DetectionType(rawValue: detectionTypeName) else {
return
}
let text = (self.text as NSString).substringWithRange(range)
self.hangleTapText(text, withDetectionType: detectionType)
}
}
private func hangleTapText(text: String, withDetectionType detectionType: ChatTextStorage.DetectionType) {
println("hangleTapText: \(text), \(detectionType)")
let username = text.substringFromIndex(text.startIndex.advancedBy(1))
if !username.isEmpty {
tapMentionAction?(username: username)
}
}
private func enumerateLinkRangesContainingLocation(location: CGPoint, complete: (NSRange) -> Void) {
var found = false
self.attributedText.enumerateAttribute(ChatTextStorage.detectionTypeName, inRange: NSMakeRange(0, attributedText.length), options: [], usingBlock: { value, range, stop in
if let _ = value {
self.enumerateViewRectsForRanges([NSValue(range: range)]) { rect, range, stop in
if !found {
if CGRectContainsPoint(rect, location) {
self.drawRoundedCornerForRange(range, rect: rect)
found = true
complete(range)
}
} else {
println("Found")
}
}
}
})
}
private func enumerateViewRectsForRanges(ranges: [NSValue], complete: (rect: CGRect, range: NSRange, stop: Bool) -> Void) {
for rangeValue in ranges {
let range = rangeValue.rangeValue
let glyphRange = layoutManager.glyphRangeForCharacterRange(range, actualCharacterRange: nil)
layoutManager.enumerateEnclosingRectsForGlyphRange(glyphRange, withinSelectedGlyphRange: NSMakeRange(NSNotFound, 0), inTextContainer: textContainer, usingBlock: { rect, stop in
var rect = rect
rect.origin.x += self.textContainerInset.left
rect.origin.y += self.textContainerInset.top
//rect = UIEdgeInsetsInsetRect(rect, self.tapAreaInsets)
complete(rect: rect, range: range, stop: true)
})
}
return
}
private func drawRoundedCornerForRange(range: NSRange, rect: CGRect) {
let layer = CALayer()
layer.frame = rect
layer.backgroundColor = chatTextStorage.mentionForegroundColor.colorWithAlphaComponent(0.5).CGColor
layer.cornerRadius = 3.0
layer.masksToBounds = true
self.layer.addSublayer(layer)
delay(0.2) {
layer.removeFromSuperlayer()
}
}
// MARK: hack
override func canBecomeFirstResponder() -> Bool {
return false
}
override func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer) {
// iOS 9
// iOS 9 0.1 Reveal0.12 tap link0.5 selection 0.75 press link
if isOperatingSystemAtLeastMajorVersion(9) {
if let longPressGestureRecognizer = gestureRecognizer as? UILongPressGestureRecognizer {
if longPressGestureRecognizer.minimumPressDuration == 0.5 {
return
}
}
}
super.addGestureRecognizer(gestureRecognizer)
}
}
extension ChatTextView: UIGestureRecognizerDelegate {
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
*/
+5 -1
View File
@@ -726,7 +726,11 @@
"Hide" = "隐藏";
"Recalled by creator." = "消息已被撤回。";
"%@ recalled a message." = "%@ 撤回了一条消息。";
"Retry" = "重试";
"User not found." = "找不到用户。";
"No username" = "没有用户名";