MessageSplitViewController.swift 10.7 KB
Newer Older
1
////////////////////////////////////////////////////////////////////////////////
2
3
4
5
6
7
8
//
//  MessageSplitViewController.swift
//  MessageTextEntrySpike
//
//  Created by Aral Balkan on 03/01/2015.
//  Copyright (c) 2015 ind.ie. All rights reserved.
//
9
////////////////////////////////////////////////////////////////////////////////
10
11

import Cocoa
12

13
14
class MessageSplitViewController: NSSplitViewController
{
15

16
    var sendMessageNotificationHandler:NotificationHandler?
17
    
18
19
20
21
22
23
24
    //
    // MARK: - View lifecycle
    //
    
    override func viewWillAppear()
    {
        createConstraints()
25
26
        
        addNotificationHandlers()
27
28
29
    }
    
    
30
    override func viewWillDisappear()
31
    {
32
        removeNotificationHandlers()
33
34
35
    }
    
    //
36
    // MARK: - Notification handlers
37
38
    //
    
39
    func addNotificationHandlers()
40
    {
41
42
        println("\(self): adding notification handlers.")
        
Aral Balkan's avatar
Aral Balkan committed
43
44
45
        //
        // MARK: - .SendMessage
        //
46
        sendMessageNotificationHandler = sendMessageNotificationHandler ?? handle(MessageNotification.named(.SendMessage))
47
        {
48
            /* as */ notification in
49
            
50
            if let userInfo = (notification.userInfo as Dictionary!), messageAttributedString = userInfo["message"] as? NSAttributedString
51
            {
52
53
54
55
56
57
                println("Send message: received text: \(messageAttributedString)")
                
                //
                // Get the folder to save the message to based on the current timeline
                //
                
58
                if CurrentTimeline.path == nil
59
60
61
62
                {
                    fatalError("Conversation messages not implemented yet. Panicing!")
                }
                
63
                println("Current timeline path = \(CurrentTimeline.path)")
64
65
66
67
                
                //
                // Get the correct web prefix
                //
68
                if CurrentTimeline.relativeWebPrefix == nil
69
                {
70
                    fatalError("Could not get relative web prefix for current timeline — not implemented. Only Private, All Friends, and Public timelines implemented at the moment.")
71
72
                }
                
73
                println("Current timeline relative web prefix: \(CurrentTimeline.relativeWebPrefix)")
74
75
76
77
78
79
                
                //
                // 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)
                
80
81
82
                // The file name of the message folder (this is the unique ID — unique per message folder):
                // Local message clock (padding to 9 digits — which should be large enough for any timeline as it would take 
                // about a thousand years to fill it up at ten seconds a message, every ten seconds, eight hours a day, continuously) + current timestamp in ISO 8601 format
83

84
85
                CurrentTimeline.messageClock++
                let messageFileName = String(format:"%09d-\(ISO8601DateString())", CurrentTimeline.messageClock)
86
87
                
                // This is the URL that will be prefixed to all assets in the HTML (images, etc.)
88
                let assetURLPrefix = CurrentTimeline.relativeWebPrefix!.stringByAppendingPathComponent(messageFileName)
89
                
90
91
                println("Asset URL Prefix: \(assetURLPrefix)")
                
92
93
                println("Message file name: \(messageFileName)")
                
94
                let filePath = NSHomeDirectory().stringByAppendingPathComponent("Pulse/Sync").stringByAppendingPathComponent(CurrentTimeline.path!).stringByAppendingPathComponent(messageFileName)
95
96
97
98
                
                println("About to save message at path: \(filePath)")
                
                let fileURL:NSURL? = NSURL(fileURLWithPath: filePath, isDirectory: true)
99
                
100
                if let messageDirectoryWrapper = messageDirectoryWrapper, fileURL = fileURL
101
                {
102
103
104
                    let messageFileWrappers:[NSObject:AnyObject] = messageDirectoryWrapper.fileWrappers
                    
                    for (fileName, fileWrapper) in messageFileWrappers
105
                    {
106
107
                        println(" * \(fileName): \(fileWrapper)")
                        if fileName == "index.html"
108
                        {
109
110
                            let indexData:NSData? = fileWrapper.regularFileContents
                            if let indexData = indexData
111
                            {
112
113
                                let indexHTML = NSString(data: indexData, encoding: NSUTF8StringEncoding)
                                if var indexHTML = indexHTML
114
                                {
Aral Balkan's avatar
Aral Balkan committed
115
116
                                    // TODO: Parse markdown (?)
                                    
117
118
119
120
                                    //
                                    // Massage the HTML.
                                    //
                                    
121
122
                                    println(indexHTML)
                                    
123
124
125
126
127
128
                                    // Fix image URLs
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("file:///", withString: assetURLPrefix.stringByAppendingString("/"))
                                    
                                    // Remove redundant empty lines.
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("<p><br></p>", withString: "")
                                    
129
130
131
132
133
134
135
136
137
138
139
140
141
142
                                    //
                                    // If this is a new person notification, add the correct class wrapper for styling.
                                    //
                                    if let messageType = userInfo["type"] as? String
                                    {
                                        println("Got a valid userinfo type")
                                        if messageType == MessageType.named(.NewPersonNotification)
                                        {
                                            println("Got a new user notification")
                                            indexHTML = NSString(format: "<div class=\"new-person-notification\">%@</div>", indexHTML)
                                        }
                                    }

                                    
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
                                    //
                                    // 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)
                                    
                                    //
165
                                    // Create the message object and broadcast it.
166
167
                                    //
                                    let message = Message(id: messageFileName, assetURLPrefix: assetURLPrefix, folderURL: fileURL, html: indexHTML as String, synced: false)
168
                            
169
170
171
172
173
174
175
176
177
178
                                    //
                                    // Inform Node that it should post the message to the timeline
                                    // (TODO: Research: should we base this on the Pulse event flow instead? Which is cleaner?)
                                    //
                                    // Note: In the arguments, below:
                                    // * message file name is the message ID
                                    // * message body is the indexHTML
                                    // *
                                    //
                                    Node.sharedInstance.call(remoteFunction.named(.postMessage), withArguments: [messageFileName, indexHTML, CurrentTimeline.id!])
179
180
181
182
183
                                    {
                                        /* and handle callback with */ result in
                                        
                                        // println("<<<<< POST MESSAGE CALLBACK!!!! >>>>>")
                                        println(result)
184
                                    }
185
186
187
188
189
190
191

                                    
                                    //
                                    // Notify that we need to have the message displayed in the respective timeline
                                    //
                                    post(MessageNotification.named(.ShowMessage), from: self, with: ["message": message])
                                    
192
                                    
193
                                    break
194
195
196
197
198
199
200
201
                                }
                            }
                        }
                    }
                }
            }
        }
    }
202
203
204
205
    
    
    func removeNotificationHandlers()
    {
206
207
        println("\(self): removing notification handlers")
        
208
        sendMessageNotificationHandler?.remove()
209
        sendMessageNotificationHandler = nil
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
    }
    
    
    //
    // MARK: - Layout
    //
    
    func createConstraints()
    {
        view.removeConstraints(view.constraints)
        
        layout(view)
        {
            /* as */ view in
            
            view.width == view.superview!.width
            view.height == view.superview!.height
        }
    }
    
230
}