MessageSplitViewController.swift 12.5 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
17
18
19
20
    var sendMessageNotificationHandler: NotificationHandler?
    var timelineNoticeDisplayRequestHandler: NotificationHandler?
    var timelineNavigationRequestHandler: NotificationHandler?
    
    var messageEditorSplitViewItem: NSSplitViewItem?
21
    
22
23
24
25
    //
    // MARK: - View lifecycle
    //
    
26
    override func viewDidAppear()
27
    {
28
29
        super.viewDidAppear()
        
30
31
        messageEditorSplitViewItem = messageEditorSplitViewItem ?? splitViewItems[1] as? NSSplitViewItem

32
        createConstraints()
33
34
        
        addNotificationHandlers()
35
36
    }
    
37
    override func viewDidDisappear()
38
    {
39
40
        super.viewDidDisappear()
        
41
        removeNotificationHandlers()
42
43
44
    }
    
    //
45
    // MARK: - Notification handlers
46
47
    //
    
48
    func addNotificationHandlers()
49
    {
50
        // println("\(self): adding notification handlers.")
51
        
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
        //
        // Handle the showing and hiding of the message editor view based on whether a notice is showing.
        //
        timelineNoticeDisplayRequestHandler = timelineNoticeDisplayRequestHandler ?? handle(TimelineNoticeDisplayRequestNotification)
        {
            /* as */ notification in
            
            if let messageEditorSplitViewItem = self.messageEditorSplitViewItem
            {
                messageEditorSplitViewItem.animator().collapsed = true
            }
        }
        
        timelineNavigationRequestHandler = timelineNavigationRequestHandler ?? handle(TimelineNavigationRequestNotification)
        {
            /* as */ notification in
            
            if let messageEditorSplitViewItem = self.messageEditorSplitViewItem
            {
                messageEditorSplitViewItem.animator().collapsed = false
            }
        }
        
Aral Balkan's avatar
Aral Balkan committed
75
76
77
        //
        // MARK: - .SendMessage
        //
78
        sendMessageNotificationHandler = sendMessageNotificationHandler ?? handle(MessageNotification.named(.SendMessage))
79
        {
80
            /* as */ notification in
81
            
82
            if let userInfo = (notification.userInfo as Dictionary!), messageAttributedString = userInfo["message"] as? NSAttributedString
83
            {
84
85
86
87
88
89
                println("Send message: received text: \(messageAttributedString)")
                
                //
                // Get the folder to save the message to based on the current timeline
                //
                
90
                if CurrentTimeline.path == nil
91
92
93
94
                {
                    fatalError("Conversation messages not implemented yet. Panicing!")
                }
                
95
                println("Current timeline path = \(CurrentTimeline.path)")
96
97
98
99
                
                //
                // Get the correct web prefix
                //
100
                if CurrentTimeline.relativeWebPrefix == nil
101
                {
102
                    fatalError("Could not get relative web prefix for current timeline — not implemented. Only Private, All Friends, and Public timelines implemented at the moment.")
103
104
                }
                
105
                println("Current timeline relative web prefix: \(CurrentTimeline.relativeWebPrefix)")
106
107
108
109
110
111
                
                //
                // 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)
                
112
113
                // The file name of the message folder (this is the unique ID — unique per message folder): Current timestamp in ISO 8601 format
                let messageFileName = ISO8601DateString()
114
115
                
                // This is the URL that will be prefixed to all assets in the HTML (images, etc.)
116
                let assetURLPrefix = CurrentTimeline.relativeWebPrefix!.stringByAppendingPathComponent(messageFileName)
117
                
118
119
                // println("Asset URL Prefix: \(assetURLPrefix)")
                // println("Message file name: \(messageFileName)")
120
                
121
                let filePath = NSHomeDirectory().stringByAppendingPathComponent("Pulse/Sync").stringByAppendingPathComponent(CurrentTimeline.path!).stringByAppendingPathComponent(messageFileName)
122
                
123
                // println("About to save message at path: \(filePath)")
124
125
                
                let fileURL:NSURL? = NSURL(fileURLWithPath: filePath, isDirectory: true)
126
                
127
                if let messageDirectoryWrapper = messageDirectoryWrapper, fileURL = fileURL
128
                {
129
130
131
                    let messageFileWrappers:[NSObject:AnyObject] = messageDirectoryWrapper.fileWrappers
                    
                    for (fileName, fileWrapper) in messageFileWrappers
132
                    {
133
134
                        println(" * \(fileName): \(fileWrapper)")
                        if fileName == "index.html"
135
                        {
136
137
                            let indexData:NSData? = fileWrapper.regularFileContents
                            if let indexData = indexData
138
                            {
139
140
                                let indexHTML = NSString(data: indexData, encoding: NSUTF8StringEncoding)
                                if var indexHTML = indexHTML
141
                                {
Aral Balkan's avatar
Aral Balkan committed
142
143
                                    // TODO: Parse markdown (?)
                                    
144
145
146
147
                                    //
                                    // Massage the HTML.
                                    //
                                    
148
149
                                    println(indexHTML)
                                    
150
151
152
153
154
155
                                    // Fix image URLs
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("file:///", withString: assetURLPrefix.stringByAppendingString("/"))
                                    
                                    // Remove redundant empty lines.
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("<p><br></p>", withString: "")
                                    
Aral Balkan's avatar
Aral Balkan committed
156
157
158
159
160
161
162
163
164
165
166
167
168
169
                                    // Handle video links (m4v only at the moment)
                                    let matches = indexHTML.matchWithRegex("<object.*? data=\"(.*?\\.m4v)\">.*?</object>")
                                    if matches.count > 0
                                    {
                                        // Found a video link
                                        println("Found a video link.")
                                        if let objectTag = matches[0] as? String
                                        {
                                            var videoTag = objectTag.stringByReplacingOccurrencesOfString("<object data=\"", withString: "<video controls src=\"")
                                            videoTag = videoTag.stringByReplacingOccurrencesOfString("</object>", withString: "</video>")
                                            indexHTML = indexHTML.stringByReplacingOccurrencesOfString(objectTag, withString: videoTag)
                                        }
                                    }
                                    
170
                                    // Change objects to links
Aral Balkan's avatar
Aral Balkan committed
171
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("<object data=\"", withString: "<a class='attachment' href=\"download://\(CurrentPerson.sharedInstance.accountHandle!)")
172
173
                                    indexHTML = indexHTML.stringByReplacingOccurrencesOfString("</object>", withString: "</a>")
                                    
174
175
176
177
178
179
180
181
                                    //
                                    // 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)
                                        {
182
                                            println("Got a new person notification")
183
184
185
186
187
                                            indexHTML = NSString(format: "<div class=\"new-person-notification\">%@</div>", indexHTML)
                                        }
                                    }

                                    
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
                                    //
                                    // 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)
                                    
                                    //
210
                                    // Create the message object and broadcast it.
211
                                    // (An empty string person handle currently signal’s that’s us).
212
                                    //
213
                                    let message = Message(id: messageFileName, assetURLPrefix: assetURLPrefix, folderURL: fileURL, html: indexHTML as String, personHandle: "", synced: false)
214
215
216
217
218
219
                                    message.timelineID = CurrentTimeline.id
                                    
                                    // HACK: Pretend to be public timeline if we are on everyone
                                    let timelineIDToSend = CurrentTimeline.id! == "everyone" ? "public" : CurrentTimeline.id!
                                    
                                    Node.sharedInstance.call(remoteFunction.named(.postMessage), withArguments: [messageFileName, indexHTML, timelineIDToSend])
220
221
222
223
                                    {
                                        /* and handle callback with */ result in
                                        
                                        println(result)
224
                                    }
225

226
                                    break
227
228
229
230
231
232
233
234
                                }
                            }
                        }
                    }
                }
            }
        }
    }
235
236
237
238
    
    
    func removeNotificationHandlers()
    {
239
        // println("\(self): removing notification handlers")
240
        
241
        sendMessageNotificationHandler?.remove()
242
        sendMessageNotificationHandler = nil
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    }
    
    
    //
    // MARK: - Layout
    //
    
    func createConstraints()
    {
        view.removeConstraints(view.constraints)
        
        layout(view)
        {
            /* as */ view in
            
            view.width == view.superview!.width
            view.height == view.superview!.height
        }
    }
    
263
}