From a06e25561e343cfc6f31644e878e04227f506f2d Mon Sep 17 00:00:00 2001 From: Kevin Lohman Date: Tue, 26 Jul 2016 15:46:45 -0700 Subject: [PATCH 1/5] Enhancement: Allow for sorting of messages, as well as providing an additional user info value to messages that can be used to provide metadata for the sorting --- SimpleChat/LGSimpleChat/LGSimpleChat.swift | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/SimpleChat/LGSimpleChat/LGSimpleChat.swift b/SimpleChat/LGSimpleChat/LGSimpleChat.swift index 800ebdc..c124f8c 100644 --- a/SimpleChat/LGSimpleChat/LGSimpleChat.swift +++ b/SimpleChat/LGSimpleChat/LGSimpleChat.swift @@ -13,6 +13,7 @@ https://tldrlegal.com/license/mozilla-public-license-2.0-(mpl-2) */ import UIKit +import Foundation // MARK: Message @@ -23,6 +24,9 @@ class LGChatMessage : NSObject { case Opponent = "LGChatMessageSentByOpponent" } + // Useful to provide meta data for filtering predicate + var userInfo = [ String : AnyObject ]() + // MARK: ObjC Compatibility /* @@ -227,6 +231,18 @@ class LGChatMessageCell : UITableViewCell { class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataSource, LGChatInputDelegate { + typealias isOrderedBefore = (LGChatMessage, LGChatMessage) -> Bool + + // Set this value to apply sorting + var sort : isOrderedBefore? { + didSet { + if let sort = sort { + messages.sortInPlace(sort) + tableView.reloadData() + } + } + } + // MARK: Constants private struct Constants { @@ -393,6 +409,14 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS func addNewMessage(message: LGChatMessage) { messages += [message] + + if let sort = sort { + let index = messages.insertionIndexOf(message, isOrderedBefore: sort) + messages.insert(message, atIndex: index) + } + else { + messages += [message] + } tableView.reloadData() self.scrollToBottom() self.delegate?.chatController?(self, didAddNewMessage: message) @@ -451,6 +475,24 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS } +extension Array { + func insertionIndexOf(elem: Element, isOrderedBefore: (Element, Element) -> Bool) -> Int { + var lo = 0 + var hi = self.count - 1 + while lo <= hi { + let mid = (lo + hi)/2 + if isOrderedBefore(self[mid], elem) { + lo = mid + 1 + } else if isOrderedBefore(elem, self[mid]) { + hi = mid - 1 + } else { + return mid // found at position mid + } + } + return lo // not found, would be inserted at position lo + } +} + // MARK: Chat Input protocol LGChatInputDelegate : class { From 1ba437181abe5e081f5f21b79087bce85bdd1508 Mon Sep 17 00:00:00 2001 From: Kevin Lohman Date: Tue, 26 Jul 2016 15:48:54 -0700 Subject: [PATCH 2/5] Enhancement: Allow for setting gravatar string (usually an email) to take advantage of automatically generated / user configured "Gravatars" (see http://gravatar.com) --- SimpleChat/LGSimpleChat/LGSimpleChat.swift | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/SimpleChat/LGSimpleChat/LGSimpleChat.swift b/SimpleChat/LGSimpleChat/LGSimpleChat.swift index c124f8c..3688592 100644 --- a/SimpleChat/LGSimpleChat/LGSimpleChat.swift +++ b/SimpleChat/LGSimpleChat/LGSimpleChat.swift @@ -35,6 +35,9 @@ class LGChatMessage : NSObject { var color : UIColor? = nil + // Set to any string to have a custom Gravatar used as the icon, same string will always have the same icon. + var gravatarString : String? + class func SentByUserString() -> String { return LGChatMessage.SentBy.User.rawValue } @@ -91,6 +94,8 @@ class LGChatMessage : NSObject { class LGChatMessageCell : UITableViewCell { + var gravatarString : String? + // MARK: Global MessageCell Appearance Modifier struct Appearance { @@ -230,6 +235,9 @@ class LGChatMessageCell : UITableViewCell { } class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataSource, LGChatInputDelegate { + var gravatarCache = [ String : UIImage ]() + var pendingGravatarLoad = [ String ]() + typealias isOrderedBefore = (LGChatMessage, LGChatMessage) -> Bool @@ -468,7 +476,42 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("identifier", forIndexPath: indexPath) as! LGChatMessageCell let message = self.messages[indexPath.row] + cell.gravatarString = message.gravatarString cell.opponentImageView.image = message.sentBy == .Opponent ? self.opponentImage : nil + if let gravatarString = cell.gravatarString { + if let gravatar = gravatarCache[gravatarString] { + cell.opponentImageView.image = gravatar + } + else { + if !pendingGravatarLoad.contains(gravatarString) { + pendingGravatarLoad.append(gravatarString) + let gravatarURL = NSURL(string: "https://www.gravatar.com/avatar/\(gravatarString.hash).png?d=retro&size=150")! + let task = NSURLSession.sharedSession().dataTaskWithURL(gravatarURL, completionHandler: { (data, response, error) in + dispatch_async(dispatch_get_main_queue(), { + self.pendingGravatarLoad.removeObject(gravatarString) + if let data = data, gravatar = UIImage(data: data) { + self.gravatarCache[gravatarString] = gravatar + if let paths = tableView.indexPathsForVisibleRows { + var reloadPaths = [NSIndexPath]() + for path in paths { + if let cell = tableView.cellForRowAtIndexPath(path) as? LGChatMessageCell { + if cell.gravatarString == gravatarString { + cell.opponentImageView.image = gravatar + reloadPaths.append(path) + } + } + } + if reloadPaths.count > 0 { + tableView.reloadRowsAtIndexPaths(reloadPaths, withRowAnimation: .Automatic) + } + } + } + }) + }) + task.resume() + } + } + } cell.setupWithMessage(message) return cell; } From cbecda0ecc303bbbfe040ebbb482825f54a56f4a Mon Sep 17 00:00:00 2001 From: Kevin Lohman Date: Tue, 26 Jul 2016 15:49:23 -0700 Subject: [PATCH 3/5] Minor change, add comment to describe color feature. --- SimpleChat/LGSimpleChat/LGSimpleChat.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SimpleChat/LGSimpleChat/LGSimpleChat.swift b/SimpleChat/LGSimpleChat/LGSimpleChat.swift index 3688592..323ac2e 100644 --- a/SimpleChat/LGSimpleChat/LGSimpleChat.swift +++ b/SimpleChat/LGSimpleChat/LGSimpleChat.swift @@ -33,7 +33,9 @@ class LGChatMessage : NSObject { ObjC can't interact w/ enums properly, so this is used for converting compatible values. */ - var color : UIColor? = nil + + // Custom color for the speech bubble, if nil, will use default + var color : UIColor? // Set to any string to have a custom Gravatar used as the icon, same string will always have the same icon. var gravatarString : String? From e625322c3826c3a6609c0431d9f29bc2199a011a Mon Sep 17 00:00:00 2001 From: Kevin Lohman Date: Tue, 26 Jul 2016 15:50:36 -0700 Subject: [PATCH 4/5] Enhancement: Duplicate message prevention - Enabled by a bool (Off by default) --- SimpleChat/LGSimpleChat/LGSimpleChat.swift | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/SimpleChat/LGSimpleChat/LGSimpleChat.swift b/SimpleChat/LGSimpleChat/LGSimpleChat.swift index 323ac2e..8de655c 100644 --- a/SimpleChat/LGSimpleChat/LGSimpleChat.swift +++ b/SimpleChat/LGSimpleChat/LGSimpleChat.swift @@ -90,6 +90,19 @@ class LGChatMessage : NSObject { fatalError("LGChatMessage.FatalError : Initialization : Incompatible string set to SentByString!") } } + + override var hashValue: Int { + get { + return Int.addWithOverflow(Int.addWithOverflow(sentBy.hashValue, (timeStamp ?? 0).hashValue).0, content.hashValue).0 + } + } + + override func isEqual(object: AnyObject?) -> Bool { + guard let object = object as? LGChatMessage else { + return false + } + return object.hashValue == self.hashValue + } } // MARK: Message Cell @@ -253,6 +266,9 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS } } + // Set to true to perform duplicate checking + var checkForDuplicates = false + // MARK: Constants private struct Constants { @@ -418,7 +434,11 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS // MARK: New messages func addNewMessage(message: LGChatMessage) { - messages += [message] + if checkForDuplicates { + if messages.contains(message) { + return // Dupe + } + } if let sort = sort { let index = messages.insertionIndexOf(message, isOrderedBefore: sort) @@ -538,6 +558,15 @@ extension Array { } } +extension Array where Element : Equatable { + // Remove first collection element that is equal to the given `object`: + mutating func removeObject(object : Generator.Element) { + if let index = self.indexOf(object) { + self.removeAtIndex(index) + } + } +} + // MARK: Chat Input protocol LGChatInputDelegate : class { From 12226d30b81eb5cab29cb76bcd86581ba73a6a4e Mon Sep 17 00:00:00 2001 From: Kevin Lohman Date: Tue, 26 Jul 2016 15:51:28 -0700 Subject: [PATCH 5/5] Enhancement: Allow for filtering of chats, change filter parameter for automatic filtering --- SimpleChat/LGSimpleChat/LGSimpleChat.swift | 23 +++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/SimpleChat/LGSimpleChat/LGSimpleChat.swift b/SimpleChat/LGSimpleChat/LGSimpleChat.swift index 8de655c..5367b9c 100644 --- a/SimpleChat/LGSimpleChat/LGSimpleChat.swift +++ b/SimpleChat/LGSimpleChat/LGSimpleChat.swift @@ -254,6 +254,13 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS var pendingGravatarLoad = [ String ]() + // Set this value to filter messages + var filter : NSPredicate? { + didSet { + tableView.reloadData() + } + } + typealias isOrderedBefore = (LGChatMessage, LGChatMessage) -> Bool // Set this value to apply sorting @@ -473,9 +480,17 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS // MARK: UITableViewDelegate func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + let message = messages[indexPath.row] + + if let filter = filter { + if filter.evaluateWithObject(message) == false { + return 0.0 // Filtered + } + } + let padding: CGFloat = 10.0 sizingCell.bounds.size.width = CGRectGetWidth(self.view.bounds) - let height = self.sizingCell.setupWithMessage(messages[indexPath.row]).height + padding; + let height = self.sizingCell.setupWithMessage(message).height + padding; return height } @@ -498,6 +513,12 @@ class LGChatController : UIViewController, UITableViewDelegate, UITableViewDataS func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("identifier", forIndexPath: indexPath) as! LGChatMessageCell let message = self.messages[indexPath.row] + cell.hidden = false + if let filter = filter { + if filter.evaluateWithObject(message) == false { + cell.hidden = true + } + } cell.gravatarString = message.gravatarString cell.opponentImageView.image = message.sentBy == .Opponent ? self.opponentImage : nil if let gravatarString = cell.gravatarString {