diff --git a/Yep.xcodeproj/project.pbxproj b/Yep.xcodeproj/project.pbxproj index a0742b12..66143f75 100644 --- a/Yep.xcodeproj/project.pbxproj +++ b/Yep.xcodeproj/project.pbxproj @@ -124,6 +124,7 @@ 5056AD6E1B05CB92003F9359 /* ProfileSocialAccountImagesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5056AD6C1B05CB92003F9359 /* ProfileSocialAccountImagesCell.swift */; }; 5056AD6F1B05CB92003F9359 /* ProfileSocialAccountImagesCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5056AD6D1B05CB92003F9359 /* ProfileSocialAccountImagesCell.xib */; }; 505A48401B4E70CB00C3A301 /* RegisterSelectSkillsLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505A483F1B4E70CB00C3A301 /* RegisterSelectSkillsLayout.swift */; }; + 505C338B1BAA92CA00E044E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 505C338D1BAA92CA00E044E2 /* InfoPlist.strings */; }; 505EC8131ADE607D001D27E0 /* SkillCategoryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505EC8121ADE607D001D27E0 /* SkillCategoryButton.swift */; }; 505EC8171ADF8151001D27E0 /* SkillCategoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505EC8151ADF8151001D27E0 /* SkillCategoryCell.swift */; }; 505EC8181ADF8151001D27E0 /* SkillCategoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 505EC8161ADF8151001D27E0 /* SkillCategoryCell.xib */; }; @@ -386,6 +387,8 @@ 5056AD6C1B05CB92003F9359 /* ProfileSocialAccountImagesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ProfileSocialAccountImagesCell.swift; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.swift; sourceTree = ""; }; 5056AD6D1B05CB92003F9359 /* ProfileSocialAccountImagesCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ProfileSocialAccountImagesCell.xib; path = Views/Cells/ProfileSocialAccount/ProfileSocialAccountImagesCell.xib; sourceTree = ""; }; 505A483F1B4E70CB00C3A301 /* RegisterSelectSkillsLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RegisterSelectSkillsLayout.swift; path = ViewControllers/Register/RegisterSelectSkillsLayout.swift; sourceTree = ""; }; + 505C338C1BAA92CA00E044E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 505C338E1BAA92CD00E044E2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; 505EC8121ADE607D001D27E0 /* SkillCategoryButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SkillCategoryButton.swift; path = Views/Buttons/SkillCategoryButton.swift; sourceTree = ""; }; 505EC8151ADF8151001D27E0 /* SkillCategoryCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SkillCategoryCell.swift; path = Views/Cells/SkillCategory/SkillCategoryCell.swift; sourceTree = ""; }; 505EC8161ADF8151001D27E0 /* SkillCategoryCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SkillCategoryCell.xib; path = Views/Cells/SkillCategory/SkillCategoryCell.xib; sourceTree = ""; }; @@ -632,6 +635,7 @@ 0A8F54B91AB67D11004AD60E /* LaunchScreen.xib */, 0A8F54AE1AB67D11004AD60E /* Supporting Files */, 5008A1951B8DB8B500E94274 /* Localizable.strings */, + 505C338D1BAA92CA00E044E2 /* InfoPlist.strings */, ); path = Yep; sourceTree = ""; @@ -1809,6 +1813,7 @@ buildActionMask = 2147483647; files = ( 5023DE381AB6979600B3EE96 /* ConversationCell.xib in Resources */, + 505C338B1BAA92CA00E044E2 /* InfoPlist.strings in Resources */, 50C090A11ADE391600CC6389 /* AddSkillsReusableView.xib in Resources */, 504F415B1AD5028200FBB19A /* SkillCell.xib in Resources */, 50E348931B9063A700A8E699 /* AboutCell.xib in Resources */, @@ -2164,6 +2169,15 @@ name = Intro.storyboard; sourceTree = ""; }; + 505C338D1BAA92CA00E044E2 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 505C338C1BAA92CA00E044E2 /* en */, + 505C338E1BAA92CD00E044E2 /* zh-Hans */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/Yep/AppDelegate.swift b/Yep/AppDelegate.swift index 6db7ac2c..20ceb12f 100644 --- a/Yep/AppDelegate.swift +++ b/Yep/AppDelegate.swift @@ -32,57 +32,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let directory: NSURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(YepConfig.appGroupID)! let realmPath = directory.path!.stringByAppendingPathComponent("db.realm") - return Realm.Configuration(path: realmPath, schemaVersion: 17, migrationBlock: { migration, oldSchemaVersion in - - if oldSchemaVersion < 9 { - migration.enumerate(Message.className()) { oldObject, newObject in - let createdAt = oldObject!["createdAt"] as! NSDate - let updatedAt = oldObject!["updatedAt"] as! NSDate - - newObject!["createdUnixTime"] = createdAt.timeIntervalSince1970 - newObject!["updatedUnixTime"] = updatedAt.timeIntervalSince1970 - } - - migration.enumerate(Conversation.className()) { oldObject, newObject in - let updatedAt = oldObject!["updatedAt"] as! NSDate - - newObject!["updatedUnixTime"] = updatedAt.timeIntervalSince1970 - } - } - - if oldSchemaVersion < 10 { - migration.enumerate(User.className()) { oldObject, newObject in - let createdAt = oldObject!["createdAt"] as! NSDate - let lastSignInAt = oldObject!["lastSignInAt"] as! NSDate - - newObject!["createdUnixTime"] = createdAt.timeIntervalSince1970 - newObject!["lastSignInUnixTime"] = lastSignInAt.timeIntervalSince1970 - } - - migration.enumerate(Group.className()) { oldObject, newObject in - let createdAt = oldObject!["createdAt"] as! NSDate - - newObject!["createdUnixTime"] = createdAt.timeIntervalSince1970 - } - } - - if oldSchemaVersion < 14 { - migration.enumerate(Message.className()) { oldObject, newObject in - let metaDataString = oldObject!["metaData"] as! String - - if !metaDataString.isEmpty { - if let data = metaDataString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) { - let mediaMetaData = MediaMetaData() - mediaMetaData.data = data - - newObject!["mediaMetaData"] = mediaMetaData - } - } - } - } - - if oldSchemaVersion < 17 { - } + return Realm.Configuration(path: realmPath, schemaVersion: 0, migrationBlock: { migration, oldSchemaVersion in }) } @@ -142,6 +92,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationDidEnterBackground(application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + + NSNotificationCenter.defaultCenter().postNotificationName(MessageToolbar.Notification.updateDraft, object: nil) } func applicationWillEnterForeground(application: UIApplication) { diff --git a/Yep/Info.plist b/Yep/Info.plist index 79d0c13d..27bae6fa 100644 --- a/Yep/Info.plist +++ b/Yep/Info.plist @@ -32,7 +32,7 @@ CFBundleVersion - 88 + 89 Fabric APIKey @@ -55,7 +55,7 @@ LSRequiresIPhoneOS NSLocationWhenInUseUsageDescription - Find pepole around you + Meeting with near genius UIBackgroundModes fetch diff --git a/Yep/Services/YepService.swift b/Yep/Services/YepService.swift index 2110f36d..7ce16637 100644 --- a/Yep/Services/YepService.swift +++ b/Yep/Services/YepService.swift @@ -1118,30 +1118,33 @@ let parseDiscoveredUser: JSONDictionary -> DiscoveredUser? = { userInfo in createdUnixTime = userInfo["created_at"] as? NSTimeInterval, lastSignInUnixTime = userInfo["last_sign_in_at"] as? NSTimeInterval, longitude = userInfo["longitude"] as? Double, - latitude = userInfo["latitude"] as? Double, - //distance = userInfo["distance"] as? Double, - masterSkillsData = userInfo["master_skills"] as? [JSONDictionary], - learningSkillsData = userInfo["learning_skills"] as? [JSONDictionary], - socialAccountProvidersInfo = userInfo["providers"] as? [String: Bool] { + latitude = userInfo["latitude"] as? Double { let username = userInfo["username"] as? String - - let masterSkills = skillsFromSkillsData(masterSkillsData) - let learningSkills = skillsFromSkillsData(learningSkillsData) - - var socialAccountProviders = Array() - - for (name, enabled) in socialAccountProvidersInfo { - let provider = DiscoveredUser.SocialAccountProvider(name: name, enabled: enabled) - - socialAccountProviders.append(provider) - } - let introduction = userInfo["introduction"] as? String let badge = userInfo["badge"] as? String - let distance = userInfo["distance"] as? Double + var masterSkills: [Skill] = [] + if let masterSkillsData = userInfo["master_skills"] as? [JSONDictionary] { + masterSkills = skillsFromSkillsData(masterSkillsData) + } + + var learningSkills: [Skill] = [] + if let learningSkillsData = userInfo["learning_skills"] as? [JSONDictionary] { + + learningSkills = skillsFromSkillsData(learningSkillsData) + } + + var socialAccountProviders = Array() + if let socialAccountProvidersInfo = userInfo["providers"] as? [String: Bool] { + for (name, enabled) in socialAccountProvidersInfo { + let provider = DiscoveredUser.SocialAccountProvider(name: name, enabled: enabled) + + socialAccountProviders.append(provider) + } + } + let discoverUser = DiscoveredUser(id: id, username: username, nickname: nickname, introduction: introduction, avatarURLString: avatarURLString, badge: badge, createdUnixTime: createdUnixTime, lastSignInUnixTime: lastSignInUnixTime, longitude: longitude, latitude: latitude, distance: distance, masterSkills: masterSkills, learningSkills: learningSkills, socialAccountProviders: socialAccountProviders) return discoverUser @@ -1152,7 +1155,7 @@ let parseDiscoveredUser: JSONDictionary -> DiscoveredUser? = { userInfo in let parseDiscoveredUsers: JSONDictionary -> [DiscoveredUser]? = { data in - //println("discoverUsers: \(data)") + println("discoverUsers: \(data)") if let usersData = data["users"] as? [JSONDictionary] { diff --git a/Yep/ViewControllers/Conversation/ConversationViewController.swift b/Yep/ViewControllers/Conversation/ConversationViewController.swift index 54238642..5136053d 100644 --- a/Yep/ViewControllers/Conversation/ConversationViewController.swift +++ b/Yep/ViewControllers/Conversation/ConversationViewController.swift @@ -190,15 +190,10 @@ class ConversationViewController: BaseViewController { NSNotificationCenter.defaultCenter().removeObserver(self) YepUserDefaults.avatarURLString.removeListenerWithName(Listener.Avatar) - } - - override func viewWillAppear(animated: Bool) { - super.viewWillAppear(animated) - // 尝试恢复原始的 NavigationControllerDelegate,如果自定义 push 了才需要 - if let delegate = originalNavigationControllerDelegate { - navigationController?.delegate = delegate - } + conversationCollectionView.delegate = nil + + println("deinit ConversationViewController") } override func viewDidLoad() { @@ -325,27 +320,103 @@ class ConversationViewController: BaseViewController { } } } - - func tryRecoverMessageToolBar() { - if let - draft = conversation.draft, - state = MessageToolbarState(rawValue: draft.messageToolbarState) { - - if state == .TextInputing || state == .Default { - messageToolbar.messageTextView.text = draft.text - } - // 恢复时特别注意:因为键盘改由用户触发,因此 - if state == .TextInputing || state == .BeginTextInput { - // 这两种状态时不恢复 messageToolbar.state - return + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + + // 尝试恢复原始的 NavigationControllerDelegate,如果自定义 push 了才需要 + if let delegate = originalNavigationControllerDelegate { + navigationController?.delegate = delegate + } + + if isFirstAppear { + + // 为记录草稿准备 + + messageToolbar.conversation = conversation + + // MARK: MessageToolbar State Transitions + + messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in + + if let strongSelf = self { + + switch (previousState, currentState) { + + case (.MoreMessages, .Default): fallthrough + case (.MoreMessages, .VoiceRecord): + + dispatch_async(dispatch_get_main_queue()) { [weak self] in + + UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in + + strongSelf.conversationCollectionView.contentOffset.y -= strongSelf.moreMessageTypesViewDefaultHeight + strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height + + strongSelf.messageToolbarBottomConstraint.constant = 0 + strongSelf.view.layoutIfNeeded() + + }, completion: { finished in + }) + } + + default: + + if currentState == .MoreMessages { + + if previousState != .BeginTextInput && previousState != .TextInputing { + + dispatch_async(dispatch_get_main_queue()) { [weak self] in + + UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in + + strongSelf.conversationCollectionView.contentOffset.y += strongSelf.moreMessageTypesViewDefaultHeight + strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height + strongSelf.moreMessageTypesViewDefaultHeight + + strongSelf.messageToolbarBottomConstraint.constant = strongSelf.moreMessageTypesViewDefaultHeight + strongSelf.view.layoutIfNeeded() + + }, completion: { finished in + }) + } + } + + // touch to create (if need) for faster appear + delay(0.2) { + self?.imagePicker.hidesBarsOnTap = false + } + } + } } - - // 这句要放在最后,因为它会触发 stateTransitionAction - // 只恢复不改变高度的状态 - if state == .VoiceRecord { - messageToolbar.state = state + } + + // 在这里才尝试恢复 messageToolbar 的状态,因为依赖 stateTransitionAction + + func tryRecoverMessageToolBar() { + if let + draft = conversation.draft, + state = MessageToolbarState(rawValue: draft.messageToolbarState) { + + if state == .TextInputing || state == .Default { + messageToolbar.messageTextView.text = draft.text + } + + // 恢复时特别注意:因为键盘改由用户触发,因此 + if state == .TextInputing || state == .BeginTextInput { + // 这两种状态时不恢复 messageToolbar.state + return + } + + // 这句要放在最后,因为它会触发 stateTransitionAction + // 只恢复不改变高度的状态 + if state == .VoiceRecord { + messageToolbar.state = state + } } + } + + tryRecoverMessageToolBar() } } @@ -369,19 +440,22 @@ class ConversationViewController: BaseViewController { } }).map({ self.markMessageAsReaded($0) }) + // MARK: Notify Typing + + // 为 nil 时才新建 + if checkTypingStatusTimer == nil { + checkTypingStatusTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("checkTypingStatus"), userInfo: nil, repeats: true) + } + // 尽量晚的设置一些属性和闭包 if isFirstAppear { isFirstAppear = false - // MARK: Notify Typing + messageToolbar.notifyTypingAction = { [weak self] in - checkTypingStatusTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("checkTypingStatus"), userInfo: nil, repeats: true) - - messageToolbar.notifyTypingAction = { - - if let withFriend = self.conversation.withFriend { + if let withFriend = self?.conversation.withFriend { let typingMessage: JSONDictionary = ["state": FayeService.InstantStateType.Text.rawValue] @@ -644,87 +718,6 @@ class ConversationViewController: BaseViewController { self?.swipeUpPromptLabel.text = text } - // MARK: MessageToolbar State Transitions - - messageToolbar.stateTransitionAction = { [weak self] (messageToolbar, previousState, currentState) in - - if let strongSelf = self { - - switch (previousState, currentState) { - - case (.MoreMessages, .Default): fallthrough - case (.MoreMessages, .VoiceRecord): - - dispatch_async(dispatch_get_main_queue()) { [weak self] in - - UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in - - strongSelf.conversationCollectionView.contentOffset.y -= strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height - - strongSelf.messageToolbarBottomConstraint.constant = 0 - strongSelf.view.layoutIfNeeded() - - }, completion: { finished in - }) - } - - default: - - if currentState == .MoreMessages { - - if previousState != .BeginTextInput && previousState != .TextInputing { - - dispatch_async(dispatch_get_main_queue()) { [weak self] in - - UIView.animateWithDuration(0.2, delay: 0.0, options: .CurveEaseInOut, animations: { _ in - - strongSelf.conversationCollectionView.contentOffset.y += strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.conversationCollectionView.contentInset.bottom = strongSelf.messageToolbar.frame.height + strongSelf.moreMessageTypesViewDefaultHeight - - strongSelf.messageToolbarBottomConstraint.constant = strongSelf.moreMessageTypesViewDefaultHeight - strongSelf.view.layoutIfNeeded() - - }, completion: { finished in - }) - } - } - - // touch to create (if need) for faster appear - delay(0.2) { - self?.imagePicker.hidesBarsOnTap = false - } - } - } - - // 尝试保留草稿 - - let realm = Realm() - - if let draft = strongSelf.conversation.draft { - realm.write { - draft.messageToolbarState = currentState.rawValue - - if currentState == .BeginTextInput || currentState == .TextInputing { - draft.text = messageToolbar.messageTextView.text - } - } - - } else { - let draft = Draft() - draft.messageToolbarState = currentState.rawValue - - realm.write { - strongSelf.conversation.draft = draft - } - } - } - } - - // 在这里才尝试恢复 messageToolbar 的状态,因为依赖 stateTransitionAction - - tryRecoverMessageToolBar() - // MARK: More Message Types choosePhotoButton.title = NSLocalizedString("Choose photo", comment: "") @@ -798,13 +791,20 @@ class ConversationViewController: BaseViewController { } } + override func viewWillDisappear(animated: Bool) { + super.viewWillDisappear(animated) + + NSNotificationCenter.defaultCenter().postNotificationName(MessageToolbar.Notification.updateDraft, object: nil) + } + override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) FayeService.sharedManager.delegate = nil checkTypingStatusTimer?.invalidate() + checkTypingStatusTimer = nil // 及时释放 - self.waverView.removeFromSuperview() + waverView.removeFromSuperview() } override func viewDidLayoutSubviews() { @@ -2087,7 +2087,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi self?.performSegueWithIdentifier("showMessageMedia", sender: message) } else { - YepAlert.alertSorry(message: NSLocalizedString("Please wait while the image is not ready!", comment: ""), inViewController: self) + //YepAlert.alertSorry(message: NSLocalizedString("Please wait while the image is not ready!", comment: ""), inViewController: self) } }, collectionView: collectionView, indexPath: indexPath) @@ -2105,7 +2105,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi self?.playMessageAudioWithMessage(message) } else { - YepAlert.alertSorry(message: NSLocalizedString("Please wait while the audio is not ready!", comment: ""), inViewController: self) + //YepAlert.alertSorry(message: NSLocalizedString("Please wait while the audio is not ready!", comment: ""), inViewController: self) } }, collectionView: collectionView, indexPath: indexPath) @@ -2129,7 +2129,7 @@ extension ConversationViewController: UICollectionViewDataSource, UICollectionVi self?.performSegueWithIdentifier("showMessageMedia", sender: message) } else { - YepAlert.alertSorry(message: NSLocalizedString("Please wait while the video is not ready!", comment: ""), inViewController: self) + //YepAlert.alertSorry(message: NSLocalizedString("Please wait while the video is not ready!", comment: ""), inViewController: self) } }, collectionView: collectionView, indexPath: indexPath) diff --git a/Yep/ViewControllers/Profile/ProfileViewController.swift b/Yep/ViewControllers/Profile/ProfileViewController.swift index 23213374..55c0b807 100644 --- a/Yep/ViewControllers/Profile/ProfileViewController.swift +++ b/Yep/ViewControllers/Profile/ProfileViewController.swift @@ -78,6 +78,52 @@ enum ProfileUser { case DiscoveredUserType(DiscoveredUser) case UserType(User) + var userID: String { + switch self { + case .DiscoveredUserType(let discoveredUser): + return discoveredUser.id + + case .UserType(let user): + return user.userID + } + } + + var username: String? { + + var username: String? = nil + + switch self { + + case .DiscoveredUserType(let discoveredUser): + username = discoveredUser.username + + case .UserType(let user): + if !user.username.isEmpty { + username = user.username + } + } + + return username + } + + var avatarURLString: String? { + + var avatarURLString: String? = nil + + switch self { + + case .DiscoveredUserType(let discoveredUser): + avatarURLString = discoveredUser.avatarURLString + + case .UserType(let user): + if !user.avatarURLString.isEmpty { + avatarURLString = user.avatarURLString + } + } + + return avatarURLString + } + var isMe: Bool { let userID: String @@ -400,20 +446,12 @@ class ProfileViewController: UIViewController { if let profileUser = profileUser { - let userID: String - switch profileUser { case .DiscoveredUserType(let discoveredUser): - - userID = discoveredUser.id - customNavigationItem.title = discoveredUser.nickname case .UserType(let user): - - userID = user.userID - customNavigationItem.title = user.nickname if user.friendState == UserFriendState.Me.rawValue { @@ -427,6 +465,8 @@ class ProfileViewController: UIViewController { if !profileUserIsMe { + let userID = profileUser.userID + userInfoOfUserWithUserID(userID, failureHandler: nil, completion: { [weak self] userInfo in //println("userInfoOfUserWithUserID \(userInfo)") @@ -434,6 +474,15 @@ class ProfileViewController: UIViewController { updateUserWithUserID(userID, useUserInfo: userInfo) + if let discoveredUser = parseDiscoveredUser(userInfo) { + switch profileUser { + case .DiscoveredUserType: + self?.profileUser = ProfileUser.DiscoveredUserType(discoveredUser) + default: + break + } + } + self?.updateProfileCollectionView() }) } @@ -454,11 +503,19 @@ class ProfileViewController: UIViewController { } } - // share button + // share my profile button if customNavigationItem.leftBarButtonItem == nil { - let shareButton = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "tryShareProfile") - customNavigationItem.leftBarButtonItem = shareButton + let shareMyProfileButton = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "tryShareMyProfile") + customNavigationItem.leftBarButtonItem = shareMyProfileButton + } + + } else { + // share others' profile button + + if let _ = profileUser.username { + let shareOthersProfileButton = UIBarButtonItem(barButtonSystemItem: .Action, target: self, action: "shareOthersProfile") + customNavigationItem.rightBarButtonItem = shareOthersProfileButton } } } @@ -491,105 +548,102 @@ class ProfileViewController: UIViewController { // MARK: Actions - func tryShareProfile() { + private func shareProfile() { - if let - myUserID = YepUserDefaults.userID.value, - me = userWithUserID(myUserID, inRealm: Realm()) { + if let username = profileUser?.username, profileURL = NSURL(string: "http://soyep.com/\(username)") { - let username = me.username + MonkeyKing.registerAccount(.WeChat(appID: YepConfig.ChinaSocialNetwork.WeChat.appID)) - let shareProfileWithUsername: String -> Void = { [weak self] username in + var thumbnail: UIImage? - if let profileURL = NSURL(string: "http://soyep.com/\(username)") { + if let + avatarURLString = profileUser?.avatarURLString, + avatar = avatarWithAvatarURLString(avatarURLString, inRealm: Realm()) { + if let + avatarFileURL = NSFileManager.yepAvatarURLWithName(avatar.avatarFileName), + avatarFilePath = avatarFileURL.path, + image = UIImage(contentsOfFile: avatarFilePath) { + thumbnail = image.roundImageOfRadius(50) + } + } - MonkeyKing.registerAccount(.WeChat(appID: YepConfig.ChinaSocialNetwork.WeChat.appID)) + let info = MonkeyKing.Message.WeChatSubtype.Info( + title: NSLocalizedString("Match me if you can", comment: ""), + description: NSLocalizedString("From Yep with Skills", comment: ""), + thumbnail: thumbnail, + media: .URL(profileURL) + ) - var thumbnail: UIImage? + let sessionMessage = MonkeyKing.Message.WeChat(.Session(info)) + let weChatSessionActivity = WeChatActivity( + type: .Session, + canPerform: sessionMessage.canBeDelivered, + perform: { + MonkeyKing.shareMessage(sessionMessage) { success in + println("share Profile to WeChat Session success: \(success)") + } + } + ) + + let timelineMessage = MonkeyKing.Message.WeChat(.Timeline(info)) + + let weChatTimelineActivity = WeChatActivity( + type: .Timeline, + canPerform: timelineMessage.canBeDelivered, + perform: { + MonkeyKing.shareMessage(timelineMessage) { success in + println("share Profile to WeChat Timeline success: \(success)") + } + } + ) + + let activityViewController = UIActivityViewController(activityItems: [profileURL], applicationActivities: [weChatSessionActivity, weChatTimelineActivity]) + + self.presentViewController(activityViewController, animated: true, completion: nil) + } + } + + func tryShareMyProfile() { + + if let _ = profileUser?.username { + shareProfile() + + } else { + + YepAlert.textInput(title: NSLocalizedString("Create a username", comment: ""), message: NSLocalizedString("In order to share your profile, create a unique username first.", comment: ""), placeholder: NSLocalizedString("use letters, numbers, and underscore", comment: ""), oldText: nil, confirmTitle: NSLocalizedString("Create", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: { text in + + let newUsername = text + + updateMyselfWithInfo(["username": newUsername], failureHandler: { [weak self] reason, errorMessage in + defaultFailureHandler(reason, errorMessage) + + YepAlert.alertSorry(message: errorMessage ?? NSLocalizedString("Create username failed!", comment: ""), inViewController: self) + + }, completion: { success in + dispatch_async(dispatch_get_main_queue()) { [weak self] in + let realm = Realm() if let - avatarURLString = YepUserDefaults.avatarURLString.value, - avatar = avatarWithAvatarURLString(avatarURLString, inRealm: Realm()) { - if let - avatarFileURL = NSFileManager.yepAvatarURLWithName(avatar.avatarFileName), - avatarFilePath = avatarFileURL.path, - image = UIImage(contentsOfFile: avatarFilePath) { - thumbnail = image.roundImageOfRadius(50) + myUserID = YepUserDefaults.userID.value, + me = userWithUserID(myUserID, inRealm: realm) { + realm.write { + me.username = newUsername } } - let info = MonkeyKing.Message.WeChatSubtype.Info( - title: NSLocalizedString("Match me if you can", comment: ""), - description: NSLocalizedString("From Yep with Skills", comment: ""), - thumbnail: thumbnail, - media: .URL(profileURL) - ) - - let sessionMessage = MonkeyKing.Message.WeChat(.Session(info)) - - let weChatSessionActivity = WeChatActivity( - type: .Session, - canPerform: sessionMessage.canBeDelivered, - perform: { - MonkeyKing.shareMessage(sessionMessage) { success in - println("share Profile to WeChat Session success: \(success)") - } - } - ) - - let timelineMessage = MonkeyKing.Message.WeChat(.Timeline(info)) - - let weChatTimelineActivity = WeChatActivity( - type: .Timeline, - canPerform: timelineMessage.canBeDelivered, - perform: { - MonkeyKing.shareMessage(timelineMessage) { success in - println("share Profile to WeChat Timeline success: \(success)") - } - } - ) - - let activityViewController = UIActivityViewController(activityItems: [profileURL], applicationActivities: [weChatSessionActivity, weChatTimelineActivity]) - - self?.presentViewController(activityViewController, animated: true, completion: nil) + self?.shareProfile() } - } + }) - if username.isEmpty { - - YepAlert.textInput(title: NSLocalizedString("Create a username", comment: ""), message: NSLocalizedString("In order to share your profile, create a unique username first.", comment: ""), placeholder: NSLocalizedString("use letters, numbers, and underscore", comment: ""), oldText: nil, confirmTitle: NSLocalizedString("Create", comment: ""), cancelTitle: NSLocalizedString("Cancel", comment: ""), inViewController: self, withConfirmAction: { text in - - let newUsername = text - - updateMyselfWithInfo(["username": newUsername], failureHandler: { [weak self] reason, errorMessage in - defaultFailureHandler(reason, errorMessage) - - YepAlert.alertSorry(message: errorMessage ?? NSLocalizedString("Create username failed!", comment: ""), inViewController: self) - - }, completion: { success in - dispatch_async(dispatch_get_main_queue()) { - let realm = Realm() - if let - myUserID = YepUserDefaults.userID.value, - me = userWithUserID(myUserID, inRealm: realm) { - realm.write { - me.username = newUsername - } - } - - shareProfileWithUsername(newUsername) - } - }) - - }, cancelAction: { - }) - - } else { - shareProfileWithUsername(username) - } + }, cancelAction: { + }) } } + func shareOthersProfile() { + shareProfile() + } + func pickSkills() { let storyboard = UIStoryboard(name: "Intro", bundle: nil) diff --git a/Yep/Views/MessageToolbar/MessageToolbar.swift b/Yep/Views/MessageToolbar/MessageToolbar.swift index ebda9dde..93b97608 100644 --- a/Yep/Views/MessageToolbar/MessageToolbar.swift +++ b/Yep/Views/MessageToolbar/MessageToolbar.swift @@ -38,6 +38,22 @@ class MessageToolbar: UIToolbar { let messageTextAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(15)] + deinit { + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + struct Notification { + static let updateDraft = "UpdateDraftOfConversation" + } + + var conversation: Conversation? { + willSet { + if let conversation = newValue { + NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateDraft:", name: Notification.updateDraft, object: nil) + } + } + } + var stateTransitionAction: ((messageToolbar: MessageToolbar, previousState: MessageToolbarState, currentState: MessageToolbarState) -> Void)? var previousState: MessageToolbarState = .Default @@ -84,6 +100,8 @@ class MessageToolbar: UIToolbar { messageTextView.hidden = true voiceRecordButton.hidden = false + messageTextView.text = nil + micButton.setImage(UIImage(named: "icon_keyboard"), forState: .Normal) micButton.tintColor = UIColor.messageToolBarNormalColor() @@ -272,8 +290,8 @@ class MessageToolbar: UIToolbar { func showVoiceButtonAnimation() { let animation = CABasicAnimation(keyPath: "cornerRadius") - animation.fromValue = voiceRecordButton.layer.cornerRadius - let newCornerRadius = CGRectGetHeight(voiceRecordButton.bounds) * 0.5 + animation.fromValue = normalCornerRadius + let newCornerRadius: CGFloat = 17 animation.toValue = newCornerRadius animation.timingFunction = CAMediaTimingFunction(name: "easeInEaseOut") animation.repeatCount = 0 @@ -281,7 +299,7 @@ class MessageToolbar: UIToolbar { voiceRecordButton.layer.addAnimation(animation, forKey: "cornerRadius") voiceRecordButton.layer.cornerRadius = newCornerRadius - messageTextView.layer.cornerRadius = CGRectGetHeight(messageTextView.bounds) * 0.5 + messageTextView.layer.cornerRadius = newCornerRadius } func hideVoiceButtonAnimation() { @@ -320,6 +338,33 @@ class MessageToolbar: UIToolbar { // MARK: Actions + func updateDraft(notification: NSNotification) { + + if let conversation = conversation, realm = conversation.realm { + + if let draft = conversation.draft { + + 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 + } + } + } + + } else { + let draft = Draft() + draft.messageToolbarState = state.rawValue + + realm.write { + conversation.draft = draft + } + } + } + } + func trySendTextMessage() { if let textSendAction = textSendAction { textSendAction(messageToolBar: self) diff --git a/Yep/en.lproj/InfoPlist.strings b/Yep/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..ba5be173 --- /dev/null +++ b/Yep/en.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* + InfoPlist.strings + Yep + + Created by nixzhu on 15/9/17. + Copyright (c) 2015年 Catch Inc. All rights reserved. +*/ + +"NSLocationWhenInUseUsageDescription" = "Meeting with near genius"; \ No newline at end of file diff --git a/Yep/zh-Hans.lproj/InfoPlist.strings b/Yep/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 00000000..d65d5d52 --- /dev/null +++ b/Yep/zh-Hans.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* + InfoPlist.strings + Yep + + Created by nixzhu on 15/9/17. + Copyright (c) 2015年 Catch Inc. All rights reserved. +*/ + +"NSLocationWhenInUseUsageDescription" = "与附近的天才相遇"; \ No newline at end of file