mirror of
https://github.com/wahyd4/Yep.git
synced 2026-08-12 06:16:09 +10:00
Merge remote-tracking branch 'origin/develop'
This commit is contained in:
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 |
Binary file not shown.
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "icon_pin_mini_gray.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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"
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -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"
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -40,7 +40,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>217</string>
|
||||
<string>232</string>
|
||||
<key>Fabric</key>
|
||||
<dict>
|
||||
<key>APIKey</key>
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) // 6天,过期时间s3为7天,客户端防止误差减去1天
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"" 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"" 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -35,6 +35,12 @@ private class MentionUserCell: UITableViewCell {
|
||||
return label
|
||||
}()
|
||||
|
||||
private override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
|
||||
avatarImageView.image = nil
|
||||
}
|
||||
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 Reveal,0.12 tap link,0.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
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -726,7 +726,11 @@
|
||||
|
||||
"Hide" = "隐藏";
|
||||
|
||||
"Recalled by creator." = "消息已被撤回。";
|
||||
"%@ recalled a message." = "%@ 撤回了一条消息。";
|
||||
|
||||
"Retry" = "重试";
|
||||
|
||||
"User not found." = "找不到用户。";
|
||||
|
||||
"No username" = "没有用户名";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user