Commit 8ff99627 authored by Aral Balkan's avatar Aral Balkan
Browse files

Refactored the Messaging UI to pull out model and notifications.

parent cf5a0bda
......@@ -38,6 +38,8 @@
A74641261A6BD82E0083A0EC /* NodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74641251A6BD82E0083A0EC /* NodeTests.swift */; };
A7499B171AB4B47000B85BFB /* balloon_512x512@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A7499B161AB4B47000B85BFB /* balloon_512x512@2x.png */; };
A74BB4581AE2477A001CA7BB /* Delay.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74BB4571AE2477A001CA7BB /* Delay.swift */; };
A74BB4C11AE9117F001CA7BB /* MessageNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74BB4C01AE9117F001CA7BB /* MessageNotification.swift */; };
A74BB4C31AE911EF001CA7BB /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74BB4C21AE911EF001CA7BB /* MessageModel.swift */; };
A74C9EE91A65B55C0083B288 /* PreferencesKeyboardShortcutsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74C9EE81A65B55C0083B288 /* PreferencesKeyboardShortcutsViewController.swift */; };
A74C9EEC1A66889D0083B288 /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74C9EEB1A66889D0083B288 /* Node.swift */; };
A74C9EEF1A6688D30083B288 /* Websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A74C9EEE1A6688D30083B288 /* Websocket.swift */; };
......@@ -176,6 +178,8 @@
A74641251A6BD82E0083A0EC /* NodeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NodeTests.swift; sourceTree = "<group>"; };
A7499B161AB4B47000B85BFB /* balloon_512x512@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "balloon_512x512@2x.png"; sourceTree = "<group>"; };
A74BB4571AE2477A001CA7BB /* Delay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Delay.swift; sourceTree = "<group>"; };
A74BB4C01AE9117F001CA7BB /* MessageNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageNotification.swift; sourceTree = "<group>"; };
A74BB4C21AE911EF001CA7BB /* MessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageModel.swift; sourceTree = "<group>"; };
A74C9EE51A65AB070083B288 /* Credits.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Credits.md; sourceTree = "<group>"; };
A74C9EE81A65B55C0083B288 /* PreferencesKeyboardShortcutsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesKeyboardShortcutsViewController.swift; sourceTree = "<group>"; };
A74C9EEB1A66889D0083B288 /* Node.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = "<group>"; };
......@@ -369,6 +373,7 @@
A7296C001AA337D900D64CDA /* Main nagivation */ = {
isa = PBXGroup;
children = (
A73A48171AB3A615000ABBB7 /* MainNavigationViewController.swift */,
A7296C021AA337EF00D64CDA /* Vertical tab bar component.swift */,
A7296C011AA337E600D64CDA /* Icons */,
);
......@@ -599,11 +604,12 @@
isa = PBXGroup;
children = (
A79EDA881AA5031A00125690 /* HTML */,
A79EDA8C1AA5035C00125690 /* TimelineViewController.swift */,
A74BB4C01AE9117F001CA7BB /* MessageNotification.swift */,
A74BB4C21AE911EF001CA7BB /* MessageModel.swift */,
A79EDA8D1AA5035C00125690 /* MessageSplitViewController.swift */,
A79EDA8C1AA5035C00125690 /* TimelineViewController.swift */,
A79EDA8E1AA5035C00125690 /* MessageEditorViewController.swift */,
A79EDA8F1AA5035C00125690 /* IndieGrowingTextView.swift */,
A73A48171AB3A615000ABBB7 /* MainNavigationViewController.swift */,
);
name = "Messaging UI";
sourceTree = "<group>";
......@@ -944,6 +950,7 @@
A7FE4E6D1AD6A3FF00857C35 /* Constraint.swift in Sources */,
A7FE4E6F1AD6A3FF00857C35 /* Context.swift in Sources */,
A79EDA931AA5035C00125690 /* IndieGrowingTextView.swift in Sources */,
A74BB4C11AE9117F001CA7BB /* MessageNotification.swift in Sources */,
A78DAE7C1A7FCA60009FDB15 /* NoodleCustomImageRep.m in Sources */,
A7FE4E711AD6A3FF00857C35 /* Distribute.swift in Sources */,
A7FE4E6B1AD6A3FF00857C35 /* Coefficients.swift in Sources */,
......@@ -968,6 +975,7 @@
A74BB4581AE2477A001CA7BB /* Delay.swift in Sources */,
A70041801AC216A800AE43F1 /* ProfileViewController.swift in Sources */,
A70634B81A63112600A75BC0 /* SetupStepViewController.swift in Sources */,
A74BB4C31AE911EF001CA7BB /* MessageModel.swift in Sources */,
A74C9EEF1A6688D30083B288 /* Websocket.swift in Sources */,
A73A48181AB3A615000ABBB7 /* MainNavigationViewController.swift in Sources */,
A7F3A02C1AC3063400568DFF /* DebugPanelSplitViewController.swift in Sources */,
......
......@@ -8,18 +8,6 @@
import Cocoa
public enum MessageNotification:String
{
case SendMessage = "MessageSendNotification"
//
// Static convenience function so consuming code can have clearer intent
static func named(notification:MessageNotification) -> String
{
return notification.rawValue
}
}
class MessageEditorViewController: NSViewController, NSTextViewDelegate, NSTextStorageDelegate
{
......@@ -149,7 +137,7 @@ class MessageEditorViewController: NSViewController, NSTextViewDelegate, NSTextS
func postMessage()
{
post("MessageSendNotification", from: nil, with: ["message": messageTextView.attributedString()])
post(MessageNotification.named(.SendMessage), from: nil, with: ["message": messageTextView.attributedString()])
messageTextView.string = ""
stackViewHeightConstraint.constant = 76.0
......
//
// MessageModel.swift
// Heartbeat
//
// Created by Aral Balkan on 23/04/2015.
// Copyright (c) 2015 Ind.ie. All rights reserved.
//
import Cocoa
public struct Message
{
public var id:String
public var assetURLPrefix:String
public var folderURL:NSURL
public var html:String
public var synced:Bool = false
func JSONString() -> String
{
let dict = ["id": self.id, "assetURLPrefix": self.assetURLPrefix, "folderURL": self.folderURL.absoluteString!, "html": self.html, "synced":self.synced]
let jsonString = JSON(dict).rawString()
if let jsonString = jsonString
{
return jsonString
}
else
{
return ""
}
}
}
//
// MessagingNotifications.swift
// Heartbeat
//
// Created by Aral Balkan on 23/04/2015.
// Copyright (c) 2015 Ind.ie. All rights reserved.
//
import Cocoa
public enum MessageNotification:String
{
case SendMessage = "SendMessageNotification"
case ShowMessage = "ShowMessageNotification"
//
// Static convenience function so consuming code can have clearer intent
static func named(notification:MessageNotification) -> String
{
return notification.rawValue
}
}
......@@ -10,36 +10,12 @@
import Cocoa
public struct Message
{
public var id:String
public var assetURLPrefix:String
public var folderURL:NSURL
public var html:String
public var synced:Bool = false
func JSONString() -> String
{
let dict = ["id": self.id, "assetURLPrefix": self.assetURLPrefix, "folderURL": self.folderURL.absoluteString!, "html": self.html, "synced":self.synced]
let jsonString = JSON(dict).rawString()
if let jsonString = jsonString
{
return jsonString
}
else
{
return ""
}
}
}
class MessageSplitViewController: NSSplitViewController
{
var iso8601DateFormatter:ISO8601DateFormatter!
var sendMessageNotificationHandler:NotificationHandler?
//
// MARK: - View lifecycle
......@@ -57,148 +33,140 @@ class MessageSplitViewController: NSSplitViewController
iso8601DateFormatter.useMillisecondPrecision = true
iso8601DateFormatter.timeSeparator = ("_" as NSString).characterAtIndex(0)
handle(MessageNotification.named(.SendMessage), with: "sendMessage:", on: self)
}
override func viewWillAppear()
{
createConstraints()
addNotificationHandlers()
}
//
// MARK: - Layout
//
func createConstraints()
override func viewWillDisappear()
{
view.removeConstraints(view.constraints)
layout(view)
{
/* as */ view in
view.width == view.superview!.width
view.height == view.superview!.height
}
removeNotificationHandlers()
}
//
// MARK: Messaging
// MARK: - Notification handlers
//
func sendMessage(notification:NSNotification)
func addNotificationHandlers()
{
let timelineViewController = (self.splitViewItems[0] as! NSSplitViewItem).viewController as! TimelineViewController
if let userInfo = (notification.userInfo as Dictionary!)
sendMessageNotificationHandler = handle(MessageNotification.named(.SendMessage))
{
let messageAttributedString = userInfo["message"] as! NSAttributedString
println("Send message: received text: \(messageAttributedString)")
//
// Get the folder to save the message to based on the current timeline
//
let _currentTimelinePath = timelineViewController.currentTimelinePath()
if _currentTimelinePath == nil
{
fatalError("Conversation messages not implemented yet. Panicing!")
}
let currentTimelinePath = _currentTimelinePath!
println("Current timeline path = \(currentTimelinePath)")
/* as */ notification in
//
// Get the correct web prefix
//
let _currentTimelineRelativeWebPrefix = timelineViewController.currentTimelineRelativeWebPrefix()
if _currentTimelineRelativeWebPrefix == nil
let timelineViewController = (self.splitViewItems[0] as! NSSplitViewItem).viewController as! TimelineViewController
if let userInfo = (notification.userInfo as Dictionary!)
{
fatalError("Could not get relative web prefix for current timeline — not implemented. Only Private, All Friends, and Public timlines implemented at the moment.")
}
let currentTimelineRelativeWebPrefix = _currentTimelineRelativeWebPrefix!
println("Current timeline relative web prefix: \(currentTimelineRelativeWebPrefix)")
//
// Create a file wrapper to save the contents as a directory.
//
let messageDirectoryWrapper:NSFileWrapper? = messageAttributedString.fileWrapperFromRange(NSMakeRange(0, messageAttributedString.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding, NSExcludedElementsDocumentAttribute: ["xml", "html", "head", "body", "font", "span"], NSPrefixSpacesDocumentAttribute: 4], error: nil)
// The file name of the message folder (this is the unique ID — unique per message folder) is the current timestamp in ISO 8601 format
let messageFileName = iso8601DateFormatter.stringFromDate(NSDate(), timeZone: NSTimeZone(abbreviation: "UTC"))
// This is the URL that will be prefixed to all assets in the HTML (images, etc.)
let assetURLPrefix = currentTimelineRelativeWebPrefix.stringByAppendingPathComponent(messageFileName)
println("Message file name: \(messageFileName)")
let filePath = NSHomeDirectory().stringByAppendingPathComponent("Pulse/Sync").stringByAppendingPathComponent(currentTimelinePath).stringByAppendingPathComponent(messageFileName)
println("About to save message at path: \(filePath)")
let fileURL:NSURL? = NSURL(fileURLWithPath: filePath, isDirectory: true)
if let messageDirectoryWrapper = messageDirectoryWrapper, fileURL = fileURL
{
let messageFileWrappers:[NSObject:AnyObject] = messageDirectoryWrapper.fileWrappers
let messageAttributedString = userInfo["message"] as! NSAttributedString
println("Send message: received text: \(messageAttributedString)")
//
// Get the folder to save the message to based on the current timeline
//
let _currentTimelinePath = timelineViewController.currentTimelinePath()
if _currentTimelinePath == nil
{
fatalError("Conversation messages not implemented yet. Panicing!")
}
let currentTimelinePath = _currentTimelinePath!
println("Current timeline path = \(currentTimelinePath)")
//
// Get the correct web prefix
//
let _currentTimelineRelativeWebPrefix = timelineViewController.currentTimelineRelativeWebPrefix()
if _currentTimelineRelativeWebPrefix == nil
{
fatalError("Could not get relative web prefix for current timeline — not implemented. Only Private, All Friends, and Public timlines implemented at the moment.")
}
let currentTimelineRelativeWebPrefix = _currentTimelineRelativeWebPrefix!
println("Current timeline relative web prefix: \(currentTimelineRelativeWebPrefix)")
//
// Create a file wrapper to save the contents as a directory.
//
let messageDirectoryWrapper:NSFileWrapper? = messageAttributedString.fileWrapperFromRange(NSMakeRange(0, messageAttributedString.length), documentAttributes: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding, NSExcludedElementsDocumentAttribute: ["xml", "html", "head", "body", "font", "span"], NSPrefixSpacesDocumentAttribute: 4], error: nil)
// The file name of the message folder (this is the unique ID — unique per message folder) is the current timestamp in ISO 8601 format
let messageFileName = self.iso8601DateFormatter.stringFromDate(NSDate(), timeZone: NSTimeZone(abbreviation: "UTC"))
// This is the URL that will be prefixed to all assets in the HTML (images, etc.)
let assetURLPrefix = currentTimelineRelativeWebPrefix.stringByAppendingPathComponent(messageFileName)
println("Message file name: \(messageFileName)")
let filePath = NSHomeDirectory().stringByAppendingPathComponent("Pulse/Sync").stringByAppendingPathComponent(currentTimelinePath).stringByAppendingPathComponent(messageFileName)
println("About to save message at path: \(filePath)")
let fileURL:NSURL? = NSURL(fileURLWithPath: filePath, isDirectory: true)
for (fileName, fileWrapper) in messageFileWrappers
if let messageDirectoryWrapper = messageDirectoryWrapper, fileURL = fileURL
{
println(" * \(fileName): \(fileWrapper)")
if fileName == "index.html"
let messageFileWrappers:[NSObject:AnyObject] = messageDirectoryWrapper.fileWrappers
for (fileName, fileWrapper) in messageFileWrappers
{
let indexData:NSData? = fileWrapper.regularFileContents
if let indexData = indexData
println(" * \(fileName): \(fileWrapper)")
if fileName == "index.html"
{
let indexHTML = NSString(data: indexData, encoding: NSUTF8StringEncoding)
if var indexHTML = indexHTML
let indexData:NSData? = fileWrapper.regularFileContents
if let indexData = indexData
{
//
// Massage the HTML.
//
// Fix image URLs
indexHTML = indexHTML.stringByReplacingOccurrencesOfString("file:///", withString: assetURLPrefix.stringByAppendingString("/"))
// Remove redundant empty lines.
indexHTML = indexHTML.stringByReplacingOccurrencesOfString("<p><br></p>", withString: "")
//
// Update the index.html.
//
// Remove the index.html file wrapper.
messageDirectoryWrapper.removeFileWrapper(fileWrapper as! NSFileWrapper)
// And replace it with the new index.html we created.
let newIndexHTMLData:NSData? = indexHTML.dataUsingEncoding(NSUTF8StringEncoding)
if let newIndexHTMLData = newIndexHTMLData
let indexHTML = NSString(data: indexData, encoding: NSUTF8StringEncoding)
if var indexHTML = indexHTML
{
let newIndexHTMLFileWrapper = NSFileWrapper(regularFileWithContents: newIndexHTMLData)
newIndexHTMLFileWrapper.preferredFilename = "index.html"
messageDirectoryWrapper.addFileWrapper(newIndexHTMLFileWrapper)
//
// Massage the HTML.
//
// Fix image URLs
indexHTML = indexHTML.stringByReplacingOccurrencesOfString("file:///", withString: assetURLPrefix.stringByAppendingString("/"))
// Remove redundant empty lines.
indexHTML = indexHTML.stringByReplacingOccurrencesOfString("<p><br></p>", withString: "")
//
// Update the index.html.
//
// Remove the index.html file wrapper.
messageDirectoryWrapper.removeFileWrapper(fileWrapper as! NSFileWrapper)
// And replace it with the new index.html we created.
let newIndexHTMLData:NSData? = indexHTML.dataUsingEncoding(NSUTF8StringEncoding)
if let newIndexHTMLData = newIndexHTMLData
{
let newIndexHTMLFileWrapper = NSFileWrapper(regularFileWithContents: newIndexHTMLData)
newIndexHTMLFileWrapper.preferredFilename = "index.html"
messageDirectoryWrapper.addFileWrapper(newIndexHTMLFileWrapper)
}
// TODO: Error checking.
messageDirectoryWrapper.writeToURL(fileURL, options: NSFileWrapperWritingOptions.Atomic, originalContentsURL: nil, error: nil)
println(messageDirectoryWrapper)
//
// Create the message object and show it in the timeline.
//
let message = Message(id: messageFileName, assetURLPrefix: assetURLPrefix, folderURL: fileURL, html: indexHTML as String, synced: false)
timelineViewController.showMessage(message)
break
}
// TODO: Error checking.
messageDirectoryWrapper.writeToURL(fileURL, options: NSFileWrapperWritingOptions.Atomic, originalContentsURL: nil, error: nil)
println(messageDirectoryWrapper)
//
// Create the message object and show it in the timeline.
//
let message = Message(id: messageFileName, assetURLPrefix: assetURLPrefix, folderURL: fileURL, html: indexHTML as String, synced: false)
timelineViewController.showMessage(message)
break
}
}
}
......@@ -206,4 +174,29 @@ class MessageSplitViewController: NSSplitViewController
}
}
}
func removeNotificationHandlers()
{
sendMessageNotificationHandler?.remove()
}
//
// MARK: - Layout
//
func createConstraints()
{
view.removeConstraints(view.constraints)
layout(view)
{
/* as */ view in
view.width == view.superview!.width
view.height == view.superview!.height
}
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment