diff --git a/Podfile.lock b/Podfile.lock index f3b3bc8b..6ca7bcaa 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -13,7 +13,7 @@ PODS: - FXBlurView (1.6.4) - JPush-iOS-SDK (1.8.8) - KeyboardMan (0.5.2) - - Kingfisher (1.9.0) + - Kingfisher (1.9.1) - MonkeyKing (0.0.2) - MZFayeClient (1.0.1): - Base64 (~> 1.0.1) @@ -67,7 +67,7 @@ SPEC CHECKSUMS: FXBlurView: db786c2561cb49a09ae98407f52460096ab8a44f JPush-iOS-SDK: d4c097d8abbdd0837e24d44b41aacf64a6feddfd KeyboardMan: b081c254c5c0adc794a49b4e97a86cb4ac917a66 - Kingfisher: 163bafc1af6ae14d707e76ecba256d8db99dc955 + Kingfisher: 98fedd5d44d982b331fc7303f9f6144b7d8ba289 MonkeyKing: 9be24307843a80f4cd3eca66adc12c67aefb83bb MZFayeClient: 65e7ef67e61fbe7a7a8b196130541c9f06bcb5d8 Navi: 24f5c81aeb46b89d27168ec1d459581230ae2675 diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index e6eccd71..32899474 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -327,6 +327,7 @@ 50F5AF961BBBCDA30033C9BC /* FeedMediaAddCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */; }; 50F5AF991BBBD02A0033C9BC /* FeedMediaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */; }; 50F5AF9A1BBBD02A0033C9BC /* FeedMediaCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 50F5AF981BBBD02A0033C9BC /* FeedMediaCell.xib */; }; + 50F6D32E1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50F6D32D1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift */; }; 50FA9A441B661698009BF2A0 /* UIViewController+Yep.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FA9A431B661698009BF2A0 /* UIViewController+Yep.swift */; }; 50FD753D1C1FB4AC0050A382 /* FPSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FD753C1C1FB4AC0050A382 /* FPSLabel.swift */; }; 50FD753F1C20FF8D0050A382 /* FeedMediaContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50FD753E1C20FF8D0050A382 /* FeedMediaContainerView.swift */; }; @@ -696,6 +697,7 @@ 50F5AF941BBBCDA30033C9BC /* FeedMediaAddCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedMediaAddCell.xib; path = Views/Cells/FeedMediaAdd/FeedMediaAddCell.xib; sourceTree = ""; }; 50F5AF971BBBD02A0033C9BC /* FeedMediaCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaCell.swift; path = Views/Cells/FeedMedia/FeedMediaCell.swift; sourceTree = ""; }; 50F5AF981BBBD02A0033C9BC /* FeedMediaCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FeedMediaCell.xib; path = Views/Cells/FeedMedia/FeedMediaCell.xib; sourceTree = ""; }; + 50F6D32D1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedUploadingErrorContainerView.swift; path = Views/ContainerViews/FeedUploadingErrorContainerView.swift; sourceTree = ""; }; 50FA9A431B661698009BF2A0 /* UIViewController+Yep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "UIViewController+Yep.swift"; path = "Extensions/UIViewController+Yep.swift"; sourceTree = ""; }; 50FD753C1C1FB4AC0050A382 /* FPSLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FPSLabel.swift; path = Views/Labels/FPSLabel.swift; sourceTree = ""; }; 50FD753E1C20FF8D0050A382 /* FeedMediaContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeedMediaContainerView.swift; path = Views/ContainerViews/FeedMediaContainerView.swift; sourceTree = ""; }; @@ -2206,6 +2208,7 @@ 505082321C21130100643CEB /* FeedGithubRepoContainerView.swift */, 505082341C212E9200643CEB /* FeedLocationContainerView.swift */, 505082361C21377E00643CEB /* FeedVoiceContainerView.swift */, + 50F6D32D1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift */, ); name = ContainerViews; sourceTree = ""; @@ -2737,6 +2740,7 @@ 6A56328E1BC90A4500397B53 /* DiscoverCardUserCell.swift in Sources */, 5016901C1BFB22180096C4F9 /* SocialWorkService.swift in Sources */, 0A0A11821AE389380008772C /* RegisterSkillsLayout.swift in Sources */, + 50F6D32E1C3CBE0000CB673C /* FeedUploadingErrorContainerView.swift in Sources */, 50C0909B1ADE234F00CC6389 /* RegisterPickSkillsViewController.swift in Sources */, 5076FC5A1AF9DF6B00D7381A /* ConversationMessagePreviewTransitionManager.swift in Sources */, 50F5AF951BBBCDA30033C9BC /* FeedMediaAddCell.swift in Sources */, diff --git a/Yep/Configs/YepConfig.swift b/Yep/Configs/YepConfig.swift index 3c564e5b..6bee23fd 100644 --- a/Yep/Configs/YepConfig.swift +++ b/Yep/Configs/YepConfig.swift @@ -266,6 +266,9 @@ class YepConfig { struct Media { static let imageWidth: CGFloat = 2048 static let imageHeight: CGFloat = 2048 + + static let miniImageWidth: CGFloat = 200 + static let miniImageHeight: CGFloat = 200 } struct Feedback { diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index 76e1ab7d..f00aa1a3 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -12,7 +12,7 @@ import CoreLocation import Alamofire #if STAGING -let yepBaseURL = NSURL(string: "https://park-staging.catchchatchina.com/api")! +let yepBaseURL = NSURL(string: "https://park-staging.catchchatchina.com")! let fayeBaseURL = NSURL(string: "wss://faye-staging.catchchatchina.com/faye")! #else let yepBaseURL = NSURL(string: "https://api.soyep.com")! @@ -1276,6 +1276,11 @@ struct DiscoveredUser: Hashable { return false } + + static func fromUser(user: User) -> DiscoveredUser { + + return DiscoveredUser(id: user.userID, username: user.username, nickname: user.nickname, introduction: user.introduction, avatarURLString: user.avatarURLString, badge: user.badge, createdUnixTime: user.createdUnixTime, lastSignInUnixTime: user.lastSignInUnixTime, longitude: user.longitude, latitude: user.latitude, distance: 0, masterSkills: [], learningSkills: [], socialAccountProviders: [], recently_updated_provider: nil) + } } func ==(lhs: DiscoveredUser, rhs: DiscoveredUser) -> Bool { @@ -2771,6 +2776,12 @@ struct DiscoveredAttachment { let metadata: String let URLString: String + var image: UIImage? + + var isTemporary: Bool { + return image != nil + } + var thumbnailImage: UIImage? { guard (metadata as NSString).length > 0 else { @@ -2802,7 +2813,7 @@ struct DiscoveredAttachment { } //return DiscoveredAttachment(kind: kind, metadata: metadata, URLString: URLString) - return DiscoveredAttachment(metadata: metadata, URLString: URLString) + return DiscoveredAttachment(metadata: metadata, URLString: URLString, image: nil) } } @@ -2976,6 +2987,8 @@ struct DiscoveredFeed: Hashable { let groupID: String var messagesCount: Int + var uploadingErrorMessage: String? = nil + var timeAndDistanceString: String { let timeString = "\(NSDate(timeIntervalSince1970: createdUnixTime).timeAgo)" @@ -3081,7 +3094,7 @@ struct DiscoveredFeed: Hashable { skill = Skill.fromJSONDictionary(skillInfo) } - return DiscoveredFeed(id: id, allowComment: allowComment, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: body, attachment: attachment, distance: distance, skill: skill, groupID: groupID, messagesCount: messagesCount) + return DiscoveredFeed(id: id, allowComment: allowComment, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: body, attachment: attachment, distance: distance, skill: skill, groupID: groupID, messagesCount: messagesCount, uploadingErrorMessage: nil) } } @@ -3228,6 +3241,17 @@ enum FeedKind: String { default: return nil } } + + var needBackgroundUpload: Bool { + switch self { + case .Image: + return true + case .Audio: + 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) { diff --git a/Yep/ViewControllers/Feeds/FeedsViewController.swift b/Yep/ViewControllers/Feeds/FeedsViewController.swift index f68e1e84..ccc8fb57 100644 --- a/Yep/ViewControllers/Feeds/FeedsViewController.swift +++ b/Yep/ViewControllers/Feeds/FeedsViewController.swift @@ -23,6 +23,15 @@ class FeedsViewController: BaseViewController { var hideRightBarItem: Bool = false + var uploadingFeeds = [DiscoveredFeed]() + func handleUploadingErrorMessage(message: String) { + if !uploadingFeeds.isEmpty { + uploadingFeeds[0].uploadingErrorMessage = message + feedsTableView.reloadSections(NSIndexSet(index: Section.UploadingFeed.rawValue), withRowAnimation: .None) + + println("handleUploadingErrorMessage: \(message)") + } + } var feeds = [DiscoveredFeed]() @IBOutlet weak var feedsTableView: UITableView! @@ -164,7 +173,7 @@ class FeedsViewController: BaseViewController { } } - private func updateFeedsTableViewOrInsertWithIndexPaths(indexPaths: [NSIndexPath]?) { + private func updateFeedsTableViewOrInsertWithIndexPaths(indexPaths: [NSIndexPath]?, animation: UITableViewRowAnimation? = nil) { // refresh skillUsers @@ -175,7 +184,7 @@ class FeedsViewController: BaseViewController { if let indexPaths = indexPaths where feeds.count > 1 { // insert - feedsTableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) + feedsTableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: animation ?? .Automatic) } else { // or reload @@ -624,22 +633,55 @@ class FeedsViewController: BaseViewController { // MARK: - Navigation + private var newFeedViewController: NewFeedViewController? + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = segue.identifier else { return } - let afterCreatedFeedAction: DiscoveredFeed -> Void = { [weak self] feed in + let beforeUploadingFeedAction: (DiscoveredFeed, NewFeedViewController) -> Void = { [weak self] feed, newFeedViewController in + + self?.newFeedViewController = newFeedViewController dispatch_async(dispatch_get_main_queue()) { if let strongSelf = self { - strongSelf.feeds.insert(feed, atIndex: 0) + strongSelf.uploadingFeeds.insert(feed, atIndex: 0) + let indexPath = NSIndexPath(forRow: 0, inSection: Section.UploadingFeed.rawValue) + strongSelf.feedsTableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) + } + } + } + + let afterCreatedFeedAction: DiscoveredFeed -> Void = { [weak self] feed in + + self?.newFeedViewController = nil + + dispatch_async(dispatch_get_main_queue()) { + + if let strongSelf = self { + + strongSelf.feedsTableView.beginUpdates() + + var animation: UITableViewRowAnimation = .Automatic + + if !strongSelf.uploadingFeeds.isEmpty { + + strongSelf.uploadingFeeds = [] + strongSelf.feedsTableView.reloadSections(NSIndexSet(index: Section.UploadingFeed.rawValue), withRowAnimation: .None) + + animation = .None + } + + strongSelf.feeds.insert(feed, atIndex: 0) let indexPath = NSIndexPath(forRow: 0, inSection: Section.Feed.rawValue) - strongSelf.updateFeedsTableViewOrInsertWithIndexPaths([indexPath]) + strongSelf.updateFeedsTableViewOrInsertWithIndexPaths([indexPath], animation: animation) + + strongSelf.feedsTableView.endUpdates() } } @@ -647,15 +689,30 @@ class FeedsViewController: BaseViewController { }) } + let getFeedsViewController: () -> FeedsViewController? = { [weak self] in + return self + } + switch identifier { case "showProfile": let vc = segue.destinationViewController as! ProfileViewController - if let indexPath = sender as? NSIndexPath { - let discoveredUser = feeds[indexPath.row].creator - vc.profileUser = ProfileUser.DiscoveredUserType(discoveredUser) + if let indexPath = sender as? NSIndexPath, section = Section(rawValue: indexPath.section) { + + switch section { + case .SkillUsers: + break + case .UploadingFeed: + let discoveredUser = uploadingFeeds[indexPath.row].creator + vc.profileUser = ProfileUser.DiscoveredUserType(discoveredUser) + case .Feed: + let discoveredUser = feeds[indexPath.row].creator + vc.profileUser = ProfileUser.DiscoveredUserType(discoveredUser) + case .LoadMore: + break + } } vc.fromType = .None @@ -677,8 +734,18 @@ class FeedsViewController: BaseViewController { let vc = segue.destinationViewController as! FeedsViewController - if let indexPath = sender as? NSIndexPath { - vc.skill = feeds[indexPath.row].skill + if let indexPath = sender as? NSIndexPath, section = Section(rawValue: indexPath.section) { + + switch section { + case .SkillUsers: + break + case .UploadingFeed: + vc.skill = uploadingFeeds[indexPath.row].skill + case .Feed: + vc.skill = feeds[indexPath.row].skill + case .LoadMore: + break + } } vc.hidesBottomBarWhenPushed = true @@ -744,7 +811,9 @@ class FeedsViewController: BaseViewController { vc.preparedSkill = skill + vc.beforeUploadingFeedAction = beforeUploadingFeedAction vc.afterCreatedFeedAction = afterCreatedFeedAction + vc.getFeedsViewController = getFeedsViewController case "presentNewFeedVoiceRecord": @@ -757,7 +826,9 @@ class FeedsViewController: BaseViewController { vc.preparedSkill = skill + vc.beforeUploadingFeedAction = beforeUploadingFeedAction vc.afterCreatedFeedAction = afterCreatedFeedAction + vc.getFeedsViewController = getFeedsViewController case "presentPickLocation": @@ -835,39 +906,41 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { private enum Section: Int { case SkillUsers + case UploadingFeed case Feed case LoadMore } func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 3 + return 4 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch section { - case Section.SkillUsers.rawValue: - return (skill == nil) ? 0 : 1 - case Section.Feed.rawValue: - return feeds.count - case Section.LoadMore.rawValue: - return feeds.isEmpty ? 0 : 1 - default: + guard let section = Section(rawValue: section) else { return 0 } + + switch section { + case .SkillUsers: + return (skill == nil) ? 0 : 1 + case .UploadingFeed: + return uploadingFeeds.count + case .Feed: + return feeds.count + case .LoadMore: + return feeds.isEmpty ? 0 : 1 + } } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - switch indexPath.section { + guard let section = Section(rawValue: indexPath.section) else { + return UITableViewCell() + } - case Section.SkillUsers.rawValue: - let cell = tableView.dequeueReusableCellWithIdentifier(feedSkillUsersCellID) as! FeedSkillUsersCell - return cell - - case Section.Feed.rawValue: - let feed = feeds[indexPath.row] + func cellForFeed(feed: DiscoveredFeed) -> UITableViewCell { switch feed.kind { @@ -909,38 +982,47 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { let cell = tableView.dequeueReusableCellWithIdentifier(feedBasicCellID) as! FeedBasicCell return cell } + } - case Section.LoadMore.rawValue: - let cell = tableView.dequeueReusableCellWithIdentifier(loadMoreTableViewCellID) as! LoadMoreTableViewCell + switch section { + + case .SkillUsers: + + let cell = tableView.dequeueReusableCellWithIdentifier(feedSkillUsersCellID) as! FeedSkillUsersCell return cell - default: - return UITableViewCell() + case .UploadingFeed: + + let feed = uploadingFeeds[indexPath.row] + return cellForFeed(feed) + + case .Feed: + + let feed = feeds[indexPath.row] + return cellForFeed(feed) + + case .LoadMore: + + let cell = tableView.dequeueReusableCellWithIdentifier(loadMoreTableViewCellID) as! LoadMoreTableViewCell + return cell } } func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { - switch indexPath.section { + guard let section = Section(rawValue: indexPath.section) else { + return + } - case Section.SkillUsers.rawValue: - - guard let cell = cell as? FeedSkillUsersCell else { - break - } - - cell.configureWithFeeds(feeds) - - case Section.Feed.rawValue: - - let feed = feeds[indexPath.row] + func configureFeedCell(cell: UITableViewCell, withFeed feed: DiscoveredFeed) { guard let cell = cell as? FeedBasicCell else { - break + return } cell.tapAvatarAction = { [weak self] cell in if let indexPath = tableView.indexPathForCell(cell) { // 不直接捕捉 indexPath + println("tapAvatarAction indexPath: \(indexPath.section), \(indexPath.row)") self?.performSegueWithIdentifier("showProfile", sender: indexPath) } } @@ -1190,8 +1272,52 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { default: break } + } - case Section.LoadMore.rawValue: + switch section { + + case .SkillUsers: + + guard let cell = cell as? FeedSkillUsersCell else { + break + } + + cell.configureWithFeeds(feeds) + + case .UploadingFeed: + + let feed = uploadingFeeds[indexPath.row] + configureFeedCell(cell, withFeed: feed) + + if let cell = cell as? FeedBasicCell { + + cell.retryUploadingFeedAction = { [weak self] cell in + + self?.newFeedViewController?.post(again: true) + + if let indexPath = self?.feedsTableView.indexPathForCell(cell) { + self?.uploadingFeeds[indexPath.row].uploadingErrorMessage = nil + cell.hasUploadingErrorMessage = false + } + } + + cell.deleteUploadingFeedAction = { [weak self] cell in + + if let indexPath = self?.feedsTableView.indexPathForCell(cell) { + self?.uploadingFeeds.removeAtIndex(indexPath.row) + self?.feedsTableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic) + + self?.newFeedViewController = nil + } + } + } + + case .Feed: + + let feed = feeds[indexPath.row] + configureFeedCell(cell, withFeed: feed) + + case .LoadMore: guard let cell = cell as? LoadMoreTableViewCell else { break @@ -1206,28 +1332,30 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { updateFeeds(mode: .LoadMore, finish: { [weak cell] in cell?.loadingActivityIndicator.stopAnimating() }) - - default: - break } } func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - switch indexPath.section { + guard let section = Section(rawValue: indexPath.section) else { + return 0 + } - case Section.SkillUsers.rawValue: + switch section { + + case .SkillUsers: return 70 - case Section.Feed.rawValue: + case .UploadingFeed: + let feed = uploadingFeeds[indexPath.row] + return FeedsViewController.layoutPool.heightOfFeed(feed) + + case .Feed: let feed = feeds[indexPath.row] return FeedsViewController.layoutPool.heightOfFeed(feed) - case Section.LoadMore.rawValue: + case .LoadMore: return 60 - - default: - return 0 } } @@ -1241,15 +1369,22 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { tableView.deselectRowAtIndexPath(indexPath, animated: true) } - switch indexPath.section { + guard let section = Section(rawValue: indexPath.section) else { + return + } - case Section.SkillUsers.rawValue: + switch section { + + case .SkillUsers: performSegueWithIdentifier("showSkillHome", sender: nil) - case Section.Feed.rawValue: + case .UploadingFeed: + break + + case .Feed: performSegueWithIdentifier("showConversation", sender: indexPath) - default: + case .LoadMore: break } } @@ -1258,12 +1393,19 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { - switch indexPath.section { + guard let section = Section(rawValue: indexPath.section) else { + return false + } - case Section.SkillUsers.rawValue: + switch section { + + case .SkillUsers: return false - case Section.Feed.rawValue: + case .UploadingFeed: + return false + + case .Feed: let feed = feeds[indexPath.item] if feed.creator.id == YepUserDefaults.userID.value { return false @@ -1271,23 +1413,32 @@ extension FeedsViewController: UITableViewDataSource, UITableViewDelegate { return true } - default: + case .LoadMore: return false } } func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { - let reportAction = UITableViewRowAction(style: .Default, title: NSLocalizedString("Report", comment: "")) { [weak self] action, indexPath in - - if let feed = self?.feeds[indexPath.row] { - self?.report(.Feed(feed)) - } - - tableView.setEditing(false, animated: true) + guard let section = Section(rawValue: indexPath.section) else { + return nil } - return [reportAction] + if case .Feed = section { + + let reportAction = UITableViewRowAction(style: .Default, title: NSLocalizedString("Report", comment: "")) { [weak self] action, indexPath in + + if let feed = self?.feeds[indexPath.row] { + self?.report(.Feed(feed)) + } + + tableView.setEditing(false, animated: true) + } + + return [reportAction] + } + + return nil } func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { diff --git a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift index 5cb0e38b..70495a18 100644 --- a/Yep/ViewControllers/NewFeed/NewFeedViewController.swift +++ b/Yep/ViewControllers/NewFeed/NewFeedViewController.swift @@ -49,10 +49,14 @@ class NewFeedViewController: SegueViewController { var attachment: Attachment = .Default + var beforeUploadingFeedAction: ((feed: DiscoveredFeed, newFeedViewController: NewFeedViewController) -> Void)? var afterCreatedFeedAction: ((feed: DiscoveredFeed) -> Void)? var preparedSkill: Skill? + weak var feedsViewController: FeedsViewController? + var getFeedsViewController: (() -> FeedsViewController?)? + @IBOutlet private weak var feedWhiteBGView: UIView! @@ -159,15 +163,20 @@ 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 - YepAlert.alertSorry(message: message, inViewController: self) + + if presentingViewController != nil { + YepAlert.alertSorry(message: message, inViewController: self) + } else { + feedsViewController?.handleUploadingErrorMessage(message) + } case .Success: - YepHUD.hideActivityIndicator() + //YepHUD.hideActivityIndicator() messageTextView.text = nil } } @@ -200,6 +209,10 @@ class NewFeedViewController: SegueViewController { choosePromptLabel.hidden = (newValue != nil) } } + + deinit { + println("NewFeed deinit") + } override func viewDidLoad() { super.viewDidLoad() @@ -216,6 +229,9 @@ class NewFeedViewController: SegueViewController { } view.sendSubviewToBack(feedWhiteBGView) + + feedsViewController = getFeedsViewController?() + println("feedsViewController: \(feedsViewController)") isDirty = false @@ -530,13 +546,96 @@ class NewFeedViewController: SegueViewController { self.dismissViewControllerAnimated(true, completion: nil) } - private struct UploadImageInfo { - - let s3UploadParams: S3UploadParams - let metaDataString: String? +// private struct UploadImageInfo { +// +// let s3UploadParams: S3UploadParams +// let metaDataString: String? +// } + + func tryMakeUploadingFeed() -> DiscoveredFeed? { + + guard let + myUserID = YepUserDefaults.userID.value, + realm = try? Realm(), + me = userWithUserID(myUserID, inRealm: realm) else { + return nil + } + + let creator = DiscoveredUser.fromUser(me) + + var kind: FeedKind = .Text + + let createdUnixTime = NSDate().timeIntervalSince1970 + let updatedUnixTime = createdUnixTime + + let message = messageTextView.text.trimming(.WhitespaceAndNewline) + + var feedAttachment: DiscoveredFeed.Attachment? + + switch attachment { + + case .Default: + + if !mediaImages.isEmpty { + kind = .Image + + let imageAttachments: [DiscoveredAttachment] = mediaImages.map({ image in + + let imageWidth = image.size.width + let imageHeight = image.size.height + + let fixedImageWidth: CGFloat + let fixedImageHeight: CGFloat + + if imageWidth > imageHeight { + fixedImageWidth = min(imageWidth, YepConfig.Media.miniImageWidth) + fixedImageHeight = imageHeight * (fixedImageWidth / imageWidth) + } else { + fixedImageHeight = min(imageHeight, YepConfig.Media.miniImageHeight) + fixedImageWidth = imageWidth * (fixedImageHeight / imageHeight) + } + + let fixedSize = CGSize(width: fixedImageWidth, height: fixedImageHeight) + + // resize to smaller, not need fixRotation + + if let image = image.resizeToSize(fixedSize, withInterpolationQuality: .Medium) { + return DiscoveredAttachment(metadata: "", URLString: "", image: image) + } else { + return nil + } + }).flatMap({ $0 }) + + feedAttachment = .Images(imageAttachments) + } + + case .Voice(let feedVoice): + + kind = .Audio + + let audioAsset = AVURLAsset(URL: feedVoice.fileURL, options: nil) + let audioDuration = CMTimeGetSeconds(audioAsset.duration) as Double + + let audioMetaDataInfo = [YepConfig.MetaData.audioSamples: feedVoice.limitedSampleValues, YepConfig.MetaData.audioDuration: audioDuration] + + let audioMetaData = try! NSJSONSerialization.dataWithJSONObject(audioMetaDataInfo, options: []) + + let audioInfo = DiscoveredFeed.AudioInfo(feedID: "", URLString: "", metaData: audioMetaData, duration: audioDuration, sampleValues: feedVoice.limitedSampleValues) + + feedAttachment = .Audio(audioInfo) + + default: + break + } + + return DiscoveredFeed(id: "", allowComment: true, kind: kind, createdUnixTime: createdUnixTime, updatedUnixTime: updatedUnixTime, creator: creator, body: message, attachment: feedAttachment, distance: 0, skill: nil, groupID: "", messagesCount: 0, uploadingErrorMessage: nil) } - + @objc private func post(sender: UIBarButtonItem) { + post(again: false) + } + + func post(again again: Bool) { let messageLength = (messageTextView.text as NSString).length @@ -546,7 +645,15 @@ class NewFeedViewController: SegueViewController { return } - + + if !again { + if let feed = tryMakeUploadingFeed() where feed.kind.needBackgroundUpload { + beforeUploadingFeedAction?(feed: feed, newFeedViewController: self) + + dismissViewControllerAnimated(true, completion: nil) + } + } + uploadState = .Uploading let message = messageTextView.text.trimming(.WhitespaceAndNewline) @@ -574,7 +681,7 @@ class NewFeedViewController: SegueViewController { } }, completion: { data in - println(data) + println("createFeedWithKind: \(data)") dispatch_async(dispatch_get_main_queue()) { [weak self] in @@ -584,7 +691,9 @@ class NewFeedViewController: SegueViewController { self?.afterCreatedFeedAction?(feed: feed) } - self?.dismissViewControllerAnimated(true, completion: nil) + if !kind.needBackgroundUpload { + self?.dismissViewControllerAnimated(true, completion: nil) + } } syncGroupsAndDoFurtherAction {} diff --git a/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift b/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift index 8391ff1b..6754dd36 100644 --- a/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift +++ b/Yep/ViewControllers/NewFeedVoiceRecord/NewFeedVoiceRecordViewController.swift @@ -13,7 +13,9 @@ class NewFeedVoiceRecordViewController: SegueViewController { var preparedSkill: Skill? + var beforeUploadingFeedAction: ((feed: DiscoveredFeed, newFeedViewController: NewFeedViewController) -> Void)? var afterCreatedFeedAction: ((feed: DiscoveredFeed) -> Void)? + var getFeedsViewController: (() -> FeedsViewController?)? @IBOutlet private weak var nextButton: UIBarButtonItem! @@ -429,7 +431,9 @@ class NewFeedVoiceRecordViewController: SegueViewController { vc.preparedSkill = preparedSkill + vc.beforeUploadingFeedAction = beforeUploadingFeedAction vc.afterCreatedFeedAction = afterCreatedFeedAction + vc.getFeedsViewController = getFeedsViewController } default: diff --git a/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift b/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift index 5f569858..888f7edd 100644 --- a/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift +++ b/Yep/Views/Cells/Feed/FeedAnyImagesCell.swift @@ -156,6 +156,10 @@ extension FeedAnyImagesCell: UICollectionViewDataSource, UICollectionViewDelegat func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { + guard let firstAttachment = attachments.first where !firstAttachment.isTemporary else { + return + } + let cell = collectionView.cellForItemAtIndexPath(indexPath) as! FeedMediaCell let transitionView = cell.imageView diff --git a/Yep/Views/Cells/Feed/FeedBasicCell.swift b/Yep/Views/Cells/Feed/FeedBasicCell.swift index bba2c55b..532e4a95 100644 --- a/Yep/Views/Cells/Feed/FeedBasicCell.swift +++ b/Yep/Views/Cells/Feed/FeedBasicCell.swift @@ -124,6 +124,20 @@ class FeedBasicCell: UITableViewCell { return imageView }() + lazy var uploadingErrorContainerView: FeedUploadingErrorContainerView = { + let view = FeedUploadingErrorContainerView(frame: CGRect(x: 0, y: 0, width: 200, height: 30)) + return view + }() + var hasUploadingErrorMessage = false { + didSet { + uploadingErrorContainerView.hidden = !hasUploadingErrorMessage + + leftBottomLabel.hidden = hasUploadingErrorMessage + messageCountLabel.hidden = hasUploadingErrorMessage + discussionImageView.hidden = hasUploadingErrorMessage + } + } + var feed: DiscoveredFeed? var tapAvatarAction: (UITableViewCell -> Void)? @@ -133,6 +147,9 @@ class FeedBasicCell: UITableViewCell { var touchesEndedAction: (UITableViewCell -> Void)? var touchesCancelledAction: (UITableViewCell -> Void)? + var retryUploadingFeedAction: ((cell: FeedBasicCell) -> Void)? + var deleteUploadingFeedAction: ((cell: FeedBasicCell) -> Void)? + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) @@ -162,7 +179,7 @@ class FeedBasicCell: UITableViewCell { let rect = feed.body.boundingRectWithSize(CGSize(width: FeedBasicCell.messageTextViewMaxWidth, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.textAttributes, context: nil) - let height: CGFloat = ceil(rect.height) + 10 + 40 + 4 + 15 + 17 + 15 + let height: CGFloat = 10 + 40 + ceil(rect.height) + 4 + 15 + 17 + 15 return ceil(height) } @@ -250,7 +267,7 @@ class FeedBasicCell: UITableViewCell { discussionImageView.frame = basicLayout.discussionImageViewFrame } else { - leftBottomLabel.frame.origin.y = contentView.bounds.height - leftBottomLabel.frame.height - 10 + leftBottomLabel.frame.origin.y = contentView.bounds.height - leftBottomLabel.frame.height - 15 let rect = messagesCountString.boundingRectWithSize(CGSize(width: 320, height: CGFloat(FLT_MAX)), options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: YepConfig.FeedBasicCell.bottomLabelsTextAttributes, context: nil) @@ -271,6 +288,33 @@ class FeedBasicCell: UITableViewCell { layoutCache.update(layout: newLayout) } + + do { + if let message = feed.uploadingErrorMessage { + hasUploadingErrorMessage = true + + let y = leftBottomLabel.frame.origin.y - (30 - leftBottomLabel.frame.height) * 0.5 + uploadingErrorContainerView.frame = CGRect(x: 65, y: y, width: screenWidth - 65, height: 30) + uploadingErrorContainerView.errorMessageLabel.text = message + + uploadingErrorContainerView.retryAction = { [weak self] in + if let strongSelf = self { + strongSelf.retryUploadingFeedAction?(cell: strongSelf) + } + } + + uploadingErrorContainerView.deleteAction = { [weak self] in + if let strongSelf = self { + strongSelf.deleteUploadingFeedAction?(cell: strongSelf) + } + } + + contentView.addSubview(uploadingErrorContainerView) + + } else { + hasUploadingErrorMessage = false + } + } } // MARK: Actions diff --git a/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift b/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift index f0627b24..992eb6a2 100644 --- a/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift +++ b/Yep/Views/Cells/Feed/FeedBiggerImageCell.swift @@ -71,8 +71,13 @@ class FeedBiggerImageCell: FeedBasicCell { } if let onlyAttachment = feed.imageAttachments?.first { - biggerImageView.yep_showActivityIndicatorWhenLoading = true - biggerImageView.yep_setImageOfAttachment(onlyAttachment, withSize: YepConfig.FeedBiggerImageCell.imageSize) + if onlyAttachment.isTemporary { + biggerImageView.image = onlyAttachment.image + + } else { + biggerImageView.yep_showActivityIndicatorWhenLoading = true + biggerImageView.yep_setImageOfAttachment(onlyAttachment, withSize: YepConfig.FeedBiggerImageCell.imageSize) + } } if layoutCache.layout == nil { @@ -88,6 +93,10 @@ class FeedBiggerImageCell: FeedBasicCell { @objc private func tap(sender: UITapGestureRecognizer) { + guard let firstAttachment = feed?.imageAttachments?.first where !firstAttachment.isTemporary else { + return + } + if let attachments = feed?.imageAttachments { tapMediaAction?(transitionView: biggerImageView, image: biggerImageView.image, attachments: attachments, index: 0) } diff --git a/Yep/Views/Cells/Feed/FeedCellLayout.swift b/Yep/Views/Cells/Feed/FeedCellLayout.swift index 4adce52a..ea061310 100644 --- a/Yep/Views/Cells/Feed/FeedCellLayout.swift +++ b/Yep/Views/Cells/Feed/FeedCellLayout.swift @@ -183,7 +183,7 @@ struct FeedCellLayout { let messageTextViewHeight = ceil(_rect1.height) let messageTextViewFrame = CGRect(x: 65, y: 54, width: screenWidth - 65 - 15, height: messageTextViewHeight) - let leftBottomLabelOriginY = height - 17 - 10 + let leftBottomLabelOriginY = height - 17 - 15 let leftBottomLabelFrame = CGRect(x: 65, y: leftBottomLabelOriginY, width: 200, height: 17) let messagesCountString = "\(feed.messagesCount)" diff --git a/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift b/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift index 29eec605..16dead3d 100644 --- a/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift +++ b/Yep/Views/Cells/Feed/FeedNormalImagesCell.swift @@ -112,8 +112,14 @@ class FeedNormalImagesCell: FeedBasicCell { for i in 0.. Void)? + var deleteAction: (() -> Void)? + + lazy var leftContainerView: UIView = { + let view = UIView() + view.backgroundColor = UIColor(red: 1, green: 56/255.0, blue: 36/255.0, alpha: 0.1) + view.layer.cornerRadius = 5 + return view + }() + + lazy var iconImageView: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "icon_topic_reddot")) + return imageView + }() + + lazy var errorMessageLabel: UILabel = { + let label = UILabel() + label.text = NSLocalizedString("Upload failed!", comment: "") + label.textColor = UIColor.redColor() + return label + }() + + lazy var retryButton: UIButton = { + let button = UIButton() + button.setTitle(NSLocalizedString("Retry", comment: ""), forState: .Normal) + button.setTitleColor(UIColor.yepTintColor(), forState: .Normal) + button.addTarget(self, action: "retryUploadingFeed:", forControlEvents: .TouchUpInside) + return button + }() + + lazy var deleteButton: UIButton = { + let button = UIButton() + button.setTitle(NSLocalizedString("Delete", comment: ""), forState: .Normal) + button.setTitleColor(UIColor.redColor(), forState: .Normal) + button.addTarget(self, action: "deleteUploadingFeed:", forControlEvents: .TouchUpInside) + return button + }() + + override func didMoveToSuperview() { + super.didMoveToSuperview() + + makeUI() + } + + func makeUI() { + + //backgroundColor = UIColor.whiteColor() + + do { + addSubview(leftContainerView) + addSubview(deleteButton) + + leftContainerView.translatesAutoresizingMaskIntoConstraints = false + deleteButton.translatesAutoresizingMaskIntoConstraints = false + + let views = [ + "leftContainerView": leftContainerView, + "deleteButton": deleteButton, + ] + + let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|[leftContainerView]-15-[deleteButton]-15-|", options: [.AlignAllCenterY], metrics: nil, views: views) + let constraintsV = NSLayoutConstraint.constraintsWithVisualFormat("V:|[leftContainerView]|", options: [], metrics: nil, views: views) + + NSLayoutConstraint.activateConstraints(constraintsH) + NSLayoutConstraint.activateConstraints(constraintsV) + } + + do { + leftContainerView.addSubview(iconImageView) + leftContainerView.addSubview(errorMessageLabel) + leftContainerView.addSubview(retryButton) + + iconImageView.translatesAutoresizingMaskIntoConstraints = false + errorMessageLabel.translatesAutoresizingMaskIntoConstraints = false + retryButton.translatesAutoresizingMaskIntoConstraints = false + + let views = [ + "iconImageView": iconImageView, + "errorMessageLabel": errorMessageLabel, + "retryButton": retryButton, + ] + + let constraintsH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-[iconImageView]-[errorMessageLabel]-[retryButton]-|", options: [.AlignAllCenterY], metrics: nil, views: views) + + iconImageView.setContentHuggingPriority(UILayoutPriorityRequired, forAxis: .Horizontal) + iconImageView.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Horizontal) + + let iconImageViewCenterY = NSLayoutConstraint(item: iconImageView, attribute: .CenterY, relatedBy: .Equal, toItem: leftContainerView, attribute: .CenterY, multiplier: 1.0, constant: 0) + + NSLayoutConstraint.activateConstraints(constraintsH) + NSLayoutConstraint.activateConstraints([iconImageViewCenterY]) + } + } + + @objc private func retryUploadingFeed(sender: UIButton) { + retryAction?() + } + + @objc private func deleteUploadingFeed(sender: UIButton) { + deleteAction?() + } +} + diff --git a/Yep/Views/Feed/FeedView.swift b/Yep/Views/Feed/FeedView.swift index d5243a5b..84fedcd3 100644 --- a/Yep/Views/Feed/FeedView.swift +++ b/Yep/Views/Feed/FeedView.swift @@ -298,7 +298,7 @@ class FeedView: UIView { attachments = feed.attachments.map({ //DiscoveredAttachment(kind: AttachmentKind(rawValue: $0.kind)!, metadata: $0.metadata, URLString: $0.URLString) - DiscoveredAttachment(metadata: $0.metadata, URLString: $0.URLString) + DiscoveredAttachment(metadata: $0.metadata, URLString: $0.URLString, image: nil) }) messageLabelTrailingConstraint.constant = attachments.isEmpty ? 15 : 60 diff --git a/Yep/zh-Hans.lproj/Localizable.strings b/Yep/zh-Hans.lproj/Localizable.strings index e1fc8f76..97e56efe 100644 --- a/Yep/zh-Hans.lproj/Localizable.strings +++ b/Yep/zh-Hans.lproj/Localizable.strings @@ -728,3 +728,5 @@ "Recalled by creator." = "消息已被撤回。"; +"Retry" = "重试"; +