Commit a3044552 authored by Aral Balkan's avatar Aral Balkan

Started implementing a notifications-based interface for remote procedure...

Started implementing a notifications-based interface for remote procedure calls. Trailing and leading hyphens are now trimmed before the call to check if handle exists is fired (closes #20).
parent 1559ea77
......@@ -1150,7 +1150,7 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pwd-bE-SCo">
<rect key="frame" x="189" y="373" width="204" height="50"/>
<rect key="frame" x="-2" y="437" width="204" height="50"/>
<constraints>
<constraint firstAttribute="width" priority="250" constant="200" id="24j-SG-Len"/>
<constraint firstAttribute="height" constant="50" id="Hlw-s8-mhv"/>
......@@ -1204,8 +1204,8 @@
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</box>
<containerView ambiguous="YES" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wFH-mb-XEX">
<rect key="frame" x="195" y="23" width="180" height="17"/>
<containerView ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wFH-mb-XEX">
<rect key="frame" x="205" y="23" width="180" height="17"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="180" id="IVX-B5-LWA"/>
<constraint firstAttribute="height" constant="17" id="PMj-PE-Smk"/>
......@@ -1236,9 +1236,11 @@
</constraints>
</view>
<connections>
<outlet property="accountCheckProgressIndicator" destination="wFH-mb-XEX" id="iJe-XO-ukR"/>
<outlet property="nameTextField" destination="RGu-J6-FTj" id="gAD-So-D7Q"/>
<outlet property="nameTextFieldCopyForMeasurements" destination="pwd-bE-SCo" id="s9H-Cm-zeB"/>
<outlet property="nameTextFieldWidthConstraint" destination="27u-28-Wva" id="fiN-Uq-hpS"/>
<outlet property="yesButton" destination="HbO-hW-0fS" id="VtK-Hd-MLe"/>
</connections>
</viewController>
<customObject id="G4y-UY-3zk" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
......
......@@ -49,3 +49,40 @@ extension String {
}
}
//
// A usable String interface for common actions, courtesy of
// http://stackoverflow.com/a/25152652/253485
//
extension String {
// MARK: - substring
func substringToIndex(index:Int) -> String {
return self.substringToIndex(advance(self.startIndex, index))
}
func substringFromIndex(index:Int) -> String {
return self.substringFromIndex(advance(self.startIndex, index))
}
func substringWithRange(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self.substringWithRange(start..<end)
}
subscript(index:Int) -> Character{
return self[advance(self.startIndex, index)]
}
subscript(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self[start..<end]
}
// MARK: - replace
func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
var result:NSMutableString = NSMutableString(string: self)
result.replaceCharactersInRange(NSRange(range), withString: withString)
return result
}
}
\ No newline at end of file
......@@ -15,6 +15,7 @@ import Cocoa
public enum function:String
{
case hasWaystoneAccount = "hasWaystoneAccount"
case isHandleAvailable = "isHandleAvailable"
public static func named(name:function) -> String {
return name.rawValue
......@@ -46,6 +47,24 @@ public enum NodeNotification:String {
}
}
//
// MakeRemoteCall enum
//
public enum MakeRemoteCallNotification:String {
case isHandleAvailable = "isHandleAvailable"
case hasWaystoneAccount = "hasWaystoneAccount"
//
// Static convenience function so consuming code can have clearer intent
// DebugNotification.named(.NameOfNotification) instead of
// DebugNotification.NameofNotification.rawValue
//
public static func named(notification:MakeRemoteCallNotification) -> String {
return notification.rawValue
}
}
//
// Node class
//
......@@ -57,6 +76,8 @@ public class Node: NSObject, WebSocketDelegate
var fileHandle:NSFileHandle?
var delegate:NodeDelegate?
let callFromNotification:Selector = "callFromNotification:"
//
// A dictionary of currently active remote procedure calls.
// (Used for both invoking the callbacks when they return and for cancelling, etc. later)
......@@ -117,12 +138,23 @@ public class Node: NSObject, WebSocketDelegate
self.nodeTask!.arguments = [pathToNodeApp, NSHomeDirectory()]
self.nodeTask!.standardOutput = NSPipe()
// Listen for notifications on the pipe.
//
// Notification handlers
//
var 📡 = NSNotificationCenter.defaultCenter()
//
// Node pipe notifications
//
📡.addObserver(self, selector: "pipeHasOutput:", name: NSFileHandleReadCompletionNotification, object: self.nodeTask!.standardOutput.fileHandleForReading)
📡.addObserver(self, selector: "pipeIsClosed:", name: NSFileHandleReadToEndOfFileCompletionNotification, object: self.nodeTask!.standardOutput.fileHandleForReading) // Never gets called
//
// Relays for remote procedure call notifications
//
📡.addObserver(self, selector: callFromNotification, name: MakeRemoteCallNotification.named(.isHandleAvailable), object: nil)
📡.addObserver(self, selector: callFromNotification, name: MakeRemoteCallNotification.named(.hasWaystoneAccount), object: nil)
self.nodeTask!.standardOutput.fileHandleForReading.readInBackgroundAndNotify()
self.nodeTask!.launch()
......@@ -294,6 +326,17 @@ public class Node: NSObject, WebSocketDelegate
// RPC methods
//
//
// Convert a notification to a remote procedure call
//
func callFromNotification(notification:NSNotification) {
println("Call from notification: \(notification)")
}
//
// Make the call
//
func call(nameOfRemoteFunctionToCall: String, withArguments arguments: [String:AnyObject]?, andCallback callback: Callback) {
......
//
// FirstPageViewController.swift
// StepOneViewController
// Heartbeat
//
// Created by Aral Balkan on 19/09/2014.
......@@ -14,12 +14,17 @@ protocol SetupDelegate
func previousStep()
}
class StepOneViewController: SetupStepViewController
{
@IBOutlet weak var nameTextField: NSTextField!
@IBOutlet weak var nameTextFieldCopyForMeasurements: NSTextField!
@IBOutlet weak var nameTextFieldWidthConstraint: NSLayoutConstraint!
@IBOutlet weak var accountCheckProgressIndicator: NSView!
@IBOutlet weak var yesButton: NSButton!
var timer:NSTimer!
override func viewDidLoad()
{
......@@ -29,6 +34,13 @@ class StepOneViewController: SetupStepViewController
// Hide the copy of the text field that we use for measurements.
nameTextFieldCopyForMeasurements.hidden = true
// Setup an initial timer
self.timer = NSTimer(timeInterval: 0.1, target: self, selector: "checkIfNameExists", userInfo: nil, repeats: false)
// Hide both the Yes button and the account check progress indicator
yesButton.hidden = true
accountCheckProgressIndicator.hidden = true
}
......@@ -48,6 +60,43 @@ class StepOneViewController: SetupStepViewController
}
nameTextFieldWidthConstraint.constant = textFieldCopyWidth
//
// Hide the progress indicator while the user is typing.
//
yesButton.hidden = true
accountCheckProgressIndicator.hidden = true
timer.invalidate()
self.timer = NSTimer(timeInterval: 0.5, target: self, selector: "checkIfNameExists", userInfo: nil, repeats: false)
NSRunLoop.currentRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)
}
func checkIfNameExists() {
//
// Do some more validation on the field
// (Check for and strip leading and trailing hyphens — https://source.ind.ie/project/heartbeat-cocoa/issues/20 )
//
var nameText:String = nameTextField.stringValue as String
if nameText.hasPrefix("-") {
// Strip the first character
nameText = nameText.substringFromIndex(1)
nameTextField.stringValue = nameText
}
if nameText.hasSuffix("-") {
nameText = nameText.substringToIndex(countElements(nameText)-1)
nameTextField.stringValue = nameText
}
if countElements(nameText) > 0 {
accountCheckProgressIndicator.hidden = false
//
// Fire off the check.
//
// TODO: Post notification
NSNotificationCenter.defaultCenter().postNotificationName(MakeRemoteCallNotification.named(.isHandleAvailable), object: self, userInfo: ["handleToCheck":nameText])
}
}
}
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