fix(examples): revamp/fix Send-Availability, change URL to live Heroku link

Summary:
Several fixes and updates the the Send Availability example package. Switches from
using `electron-safe-ipc` to using `protocol.RegisterStringProtocol` to communicate
with the child window. Changes the URLs in the package from localhost to our live
demo backend.

Test Plan: manual

Reviewers: bengotow

Reviewed By: bengotow

Differential Revision: https://phab.nylas.com/D2271
This commit is contained in:
Drew Regitsky 2015-11-19 18:16:38 -08:00
parent aaf6132134
commit abb6378bd5
20 changed files with 275 additions and 205 deletions

View file

@ -0,0 +1 @@
web: gunicorn backend:app --log-file=-

View file

@ -0,0 +1,5 @@
{
"name": "N1 Send Availability Backend",
"description": "Server supporting the N1 Send Availability plugin",
"addons": [ "cleardb:ignite" ]
}

View file

@ -10,6 +10,7 @@ from cStringIO import StringIO
from datetime import datetime
import pytz
import time
import os
import models as m
from session import session_scope
@ -21,7 +22,7 @@ m.init_db()
@app.route('/', methods=["GET", "POST"])
def hello_world():
return 'Hello World!'
return render_template("success.html")
@app.route('/register-events', methods=["POST"])
@ -33,6 +34,14 @@ def register_events():
# Save new event details and times to database
with session_scope() as dbsession:
conflicts = [
dbsession.query(m.EventTime)
.filter(m.EventTime.key == tm['serverKey']).first()
for tm in data['times']
]
if any(n is not None for n in conflicts):
raise 'An event key conflicts with an existing key: %s' % conflicts
times = [_make_event_time(d) for d in data['times']]
attendees = [_make_attendee(d) for d in data['attendees']]
@ -60,8 +69,8 @@ def load_event(key):
times = []
for t in event.times:
times.append({
"start": time.mktime(t.start.timetuple()),
"end": time.mktime(t.end.timetuple()),
"start": timestamp(t.start),
"end": timestamp(t.end),
"key": t.key
})
return render_template("show_event.html",
@ -94,11 +103,11 @@ def schedule(key):
dbsession.commit()
dbsession.delete(event)
return 'Success!'
return render_template('success.html')
def _create_email(event, etime):
sender = event.organizer.email #"send-availability@nylas.com"
sender = event.organizer.email
html_body = render_template("event_email.html", event=event, time=etime)
text_body = render_template("event_email.txt", event=event, time=etime)
msg = mime.create.multipart('mixed')
@ -108,11 +117,14 @@ def _create_email(event, etime):
body.append(
mime.create.text('plain', text_body),
mime.create.text('html', html_body),
mime.create.text('calendar; method=REQUEST',
ical_txt, charset='utf8'))
msg.append(body)
mime.create.text('calendar; method=REQUEST', ical_txt, charset='utf8')
)
msg.append(
body,
mime.create.attachment('application/ics', ical_txt, filename='event.ics', disposition='attachment', charset='utf8')
)
msg.headers['From'] = sender
msg.headers['From'] = "scheduler@nylas.com"
msg.headers['Reply-To'] = sender
msg.headers['Subject'] = "Invitation: {}".format(event.title)
msg.headers['To'] = ", ".join([a.email for a in event.attendees])
@ -121,22 +133,22 @@ def _create_email(event, etime):
def _send_email(msg):
with open("mailgun-key.txt") as f:
print msg.to_string()
key = f.read()
requests.post(
"https://api.mailgun.net/v3/mg.nylas.com/messages.mime",
auth=("api", key),
data={"to": msg.headers["To"]},
files={"message": StringIO(msg.to_string())}
)
key = os.environ['MAILGUN_KEY']
requests.post(
"https://api.mailgun.net/v3/mg.nylas.com/messages.mime",
auth=("api", key),
data={"to": msg.headers["To"]},
files={"message": StringIO(msg.to_string())}
)
def _make_ics(event, etime):
cal = Calendar()
cal.add('prodid', 'N1-send-availability-package')
cal.add('version', '1.0')
cal.add('version', '2.0')
cal.add('method', 'REQUEST') # also have PUBLISH or CANCEL
evt = Event()
evt.add('summary', event.title)
@ -186,5 +198,9 @@ def _make_attendee(attendee_data):
is_sender=attendee_data["isSender"])
def timestamp(dt):
'Return POSIX timestamp as float'
return (dt - datetime.utcfromtimestamp(0)).total_seconds()
if __name__ == '__main__':
app.run(port=8888, debug=True)

View file

@ -10,10 +10,10 @@ Base = declarative_base()
def init_db():
engine = new_engine(None)
engine.execute("CREATE DATABASE IF NOT EXISTS sendavailability "
"DEFAULT CHARACTER SET utf8mb4 "
"DEFAULT COLLATE utf8mb4_general_ci;")
# engine = new_engine(None)
# engine.execute("CREATE DATABASE IF NOT EXISTS sendavailability "
# "DEFAULT CHARACTER SET utf8mb4 "
# "DEFAULT COLLATE utf8mb4_general_ci;")
Base.metadata.create_all(new_engine())

View file

@ -0,0 +1,31 @@
cchardet==1.0.0
cffi==1.3.0
chardet==2.3.0
cryptography==1.1
Cython==0.23.4
dnspython==1.12.0
dnsq==1.1.6
enum34==1.0.4
expiringdict==1.1.3
flanker==0.4.37
Flask==0.10.1
gunicorn==19.3.0
icalendar==3.9.1
idna==2.0
ipaddress==1.0.15
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
pyasn1==0.1.9
pycparser==2.14
PyMySQL==0.6.7
python-dateutil==2.4.2
pytz==2015.7
redis==2.10.5
regex==2015.11.14
requests==2.8.1
six==1.10.0
SQLAlchemy==1.0.9
WebOb==1.5.1
Werkzeug==0.11.2
wheel==0.24.0

View file

@ -0,0 +1 @@
python-2.7.9

View file

@ -3,19 +3,24 @@ from contextlib import contextmanager
from sqlalchemy.orm.session import Session
from sqlalchemy import create_engine
import os
cached_engine = None
def new_engine(database="sendavailability"):
uri_template = ("mysql+pymysql://{username}:{password}@{host}:"
"{port}/{database}")
uri = uri_template.format(username="root",
password="root",
host="localhost",
port=3307,
database=database if database else '')
db_url = os.environ['CLEARDB_DATABASE_URL']
if db_url:
uri = 'mysql+pymysql'+db_url[5:].split('?')[0]
else:
uri_template = ("mysql+pymysql://{username}:{password}@{host}:"
"{port}/{database}")
uri = uri_template.format(username="root",
password="root",
host="localhost",
port=3307,
database=database if database else '')
return create_engine(uri,
isolation_level='READ COMMITTED',
echo=False,

File diff suppressed because one or more lines are too long

View file

@ -3,8 +3,25 @@
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
<style>
body{
text-align: center;
}
h1 {
margin-top: 50px;
}
.content {
font-size: 18px;
margin-top: 20px;
}
</style>
</head>
<body>
BAD EVENT LINK
<h1>No event found.</h1>
<div class="content">
Uh oh, it looks like that event doesn't exist or is no longer valid.<br/>
Maybe it was already scheduled?
</div>
</body>
</html>

View file

@ -3,8 +3,12 @@
<head>
<meta charset="UTF-8">
<title></title>
<style>
</style>
</head>
<body>
EVENT EMAIL
<h1 class="title"><span style="color:#AAA">Invitation:</span> {{ event.title }}</h1>
<h3 class="location"><span style="color:#AAA">Location:</span> {{ event.location }}</h3>
<div class="description">{{ event.description }}</div>
</body>
</html>

View file

@ -1 +1,5 @@
ALT TEXT
{{ event.title }}
Location:
{{ event.location }}
Description:
{{ event.description }}

View file

@ -3,11 +3,9 @@
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
<script src='/static/js/moment.min.js'></script>
<style>
body{
font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
}
.selected {
background: #8CF;
}
@ -23,7 +21,8 @@
</style>
</head>
<body>
<h1>Confirm event scheduling</h1>
<div class="container">
<h1>Confirm event scheduling</h1>
<div style="border:1px solid #eee;border-radius:3px;padding:10px">
<div style="padding:0 5px">
<div style="font-weight:bold;font-size:18px">{{ event.title }}</div>
@ -37,16 +36,16 @@
<form action="/event/{{selected_key}}" method="post">
<button type="submit" class="schedule">Schedule!</button>
</form>
</div>
<script>
events = {{ times_json|tojson|safe }}
console.log(events[0].start,events[0].end)
times = events.map(function(e) {
return {
date: moment(e.start*1000).local().format("dddd, MMM Do"),
time: moment(e.start*1000).local().format("h:mma") + "-" +
moment(e.end*1000).local().format("h:mma"),
date: moment(e.start*1000).format("dddd, MMM Do"),
time: moment(e.start*1000).format("h:mma") + "-" +
moment(e.end*1000).format("h:mma"),
serverKey: e.key
};
});

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">
<style>
.icon {
font-size: 120px;
color: #DDD;
}
.content {
margin-top:50px;
text-align: center;
font-size: 18px;
}
.content.top {
margin-top: 100px;
}
</style>
</head>
<body>
<div class="content top">
<span class="glyphicon glyphicon-calendar icon"></span>
</div>
<div class="content">
Event scheduled! Check your email for the invitation.
</div>
</body>
</html>

View file

@ -4,11 +4,10 @@
<meta charset="UTF-8">
<title></title>
<link rel='stylesheet' href='fullcalendar/fullcalendar.css' />
<script src='nylas://send-availability/fullcalendar/lib/jquery.min.js'></script>
<script src='nylas://send-availability/fullcalendar/lib/moment.min.js'></script>
<script src='nylas://send-availability/fullcalendar/fullcalendar.js'></script>
<script src='nylas://N1-Send-Availability/fullcalendar/lib/jquery.min.js'></script>
<script src='nylas://N1-Send-Availability/fullcalendar/lib/moment.min.js'></script>
<script src='nylas://N1-Send-Availability/fullcalendar/fullcalendar.js'></script>
<script src="nylas://send-availability/node_modules/electron-safe-ipc/guest-bundle.js"></script>
<script>
function randomId(len) {
@ -21,68 +20,21 @@
// class to communicate with the parent window
Parent = (function(){
function Parent() {
//listen for and and route messages from the parent
var _this = this;
electronSafeIpc.on("fromMain", function (event, data) {
_this._onReceive(event, data)
});
//initialize container for event listeners
this.listeners = {};
}
Parent.prototype._send = function (event, data) {
//send a message to the parent
electronSafeIpc.send("fromRenderer", event, data);
};
Parent.prototype._onReceive = function (event, data) {
//handle an incoming message from the parent. Route the message data
//to the appropriate event listeners based on the "event" param
var listeners = this.listeners[event];
if(listeners !== undefined) {
listeners.forEach(function(callback) {
callback(JSON.parse(data));
});
}
};
//basic jquery-style event listener methods
Parent.prototype.on = function(event, callback) {
//add an event listener
if(this.listeners[event] === undefined)
this.listeners[event] = [];
this.listeners[event].push(callback);
return callback
};
Parent.prototype.off = function (event, callback) {
//remove an event listener
if(this.listeners[event] !== undefined) {
var i = this.listeners[event].indexOf(callback);
if(i > -1)
this.listeners[event].splice(i, 1);
}
};
Parent.prototype.one = function(event,callback) {
//add a single-use event listener
var _this = this;
var outer = function(data) {
try {callback(data);}
finally {_this.off(event,outer)}
};
this.on(event,outer);
};
function Parent() {}
Parent.prototype.load_events = function (start, end, callback) {
//use timestamp as a request id
var id = Date.now();
//register a single-use event listener
this.one("get_events_"+id,callback);
//send the request, which will trigger an event when it completes
this._send("get_events",{start:start,end:end,id:id});
$.ajax({
url: "send-availability://get_events",
data: {start:start, end:end},
success: callback,
dataType: 'json'
});
};
Parent.prototype.send_results = function(data) {
this._send("available_times", data);
$.ajax({
url: "send-availability://available_times",
data: {data:JSON.stringify(data)}
});
};
return Parent})();
@ -108,6 +60,7 @@
defaultView: 'agendaWeek',
handleWindowResize: false,
aspectRatio: 1,
timezone: 'local',
selectable: true,
selectHelper: true,
select: function(start, end) {

View file

@ -13,13 +13,13 @@ class AvailabilityDraftExtension extends DraftStoreExtension
sender = session.draft().from
matches = (/data-send-availability="(.*)?" style/).exec body
if matches?
json = matches[1].replace(/'/g,'"')
json = atob(matches[1])
data = JSON.parse(json)
data.attendees = []
data.attendees = participants.map (p) ->
name: p.name, email: p.email, isSender: c.isMe()
name: p.name, email: p.email, isSender: p.isMe()
console.log "Sending request!\n",JSON.stringify data
serverUrl = "http://localhost:8888/register-events"
serverUrl = "https://sendavail.herokuapp.com/register-events"
request.post {url: serverUrl, body: JSON.stringify(data)}, (error, resp, data) =>
console.log(error,resp,data)

View file

@ -91,7 +91,7 @@ method. Here, were registering the events before we send the draft.</p>
data.attendees = participants.map (p) -&gt;
<span class="hljs-attribute">name</span>: p.name, <span class="hljs-attribute">email</span>: p.email, <span class="hljs-attribute">isSender</span>: c.isMe()
<span class="hljs-built_in">console</span>.log <span class="hljs-string">"Sending request!\n"</span>,JSON.stringify data
serverUrl = <span class="hljs-string">"http://localhost:8888/register-events"</span>
serverUrl = <span class="hljs-string">"https://sendavail.herokuapp.com/register-events"</span>
request.post {<span class="hljs-attribute">url</span>: serverUrl, <span class="hljs-attribute">body</span>: JSON.stringify(data)}, <span class="hljs-function"><span class="hljs-params">(error, resp, data)</span> =&gt;</span>
<span class="hljs-built_in">console</span>.log(error,resp,data)

View file

@ -5,18 +5,18 @@
# your availabilities to schedule an appointment with you.
{ComponentRegistry,
DatabaseStore,
DraftStore,
QuotedHTMLParser,
Event} = require 'nylas-exports'
DatabaseStore,
DraftStore,
QuotedHTMLParser,
Event} = require 'nylas-exports'
url = require('url')
qs = require("querystring")
CalendarButton = require './calendar-button'
AvailabilityDraftExtension = require './availability-draft-extension'
path = require.resolve("electron-safe-ipc/host")
ipc = require('remote').require(path)
protocol = require('remote').require('protocol')
# A simple class for building HTML in code
class HtmlNode
@ -27,7 +27,7 @@ class HtmlNode
@children = []
attr: (k,v,isJson=false) ->
@attrs[k] = if isJson then v.replace(/"/g,"'") else v
@attrs[k] = if isJson then btoa(v) else v
return @
style: (k,v) ->
@ -39,8 +39,8 @@ class HtmlNode
return node
appendNode: (name) ->
node = new HtmlNode(name);
return @append(node);
node = new HtmlNode(name)
return @append(node)
appendText: (text) ->
@append(text)
@ -54,7 +54,7 @@ class HtmlNode
children = (if n instanceof HtmlNode then n.toString() else n for n in @children).join("\n")
return "<#{@name} #{attrs} style=\"#{styles}\">\n#{children}\n</#{@name}>"
else
return "<#{@name} #{attrs} style=\"#{styles}\" />"
return "<#{@name} #{attrs} style=\"#{styles}\" />"
module.exports =
@ -79,7 +79,13 @@ module.exports =
# Subscribe to the ipc event `fromRenderer`, which will be published
# elsewhere in the package.
ipc.on "fromRenderer", (args...) => @_onCalendarEvent(args...)
protocol.registerStringProtocol 'send-availability', (request, callback) =>
{host:event,query:rawQuery} = url.parse(request.url)
stringArgs = qs.parse(rawQuery)
data = {}
for own k,v of stringArgs
data[k] = JSON.parse(v)
response = @_onCalendarEvent(event,data,callback)
# Serialize is called when your package is about to be unmounted.
# You can return a state object that will be passed back to your package
@ -95,10 +101,11 @@ module.exports =
deactivate: ->
ComponentRegistry.unregister CalendarButton
DraftStore.unregister AvailabilityDraftExtension
protocol.unregisterProtocol('send-availability')
### Internal Methods ###
_onCalendarEvent: (event,data) ->
_onCalendarEvent: (event,data,callback) ->
switch event
when "get_events"
{start,end,id:eventId} = data
@ -106,14 +113,10 @@ module.exports =
Event.attributes.start.lessThan(end),
Event.attributes.end.greaterThan(start),
]).then (events) =>
@_sendToCalendar("get_events_"+eventId,events)
callback(JSON.stringify(events))
when "available_times"
{draftClientId,eventData,events} = data
@_addBlockToDraft(events,draftClientId,eventData);
# Sends a message to the calendar window
_sendToCalendar: (event,data) ->
ipc.send("fromMain", event, JSON.stringify(data))
{draftClientId,eventData,events} = data.data
@_addBlockToDraft(events,draftClientId,eventData)
# Grabs the current draft text, appends the availability HTML block to it, and saves
_addBlockToDraft: (events,draftClientId,eventData) ->
@ -123,8 +126,7 @@ module.exports =
text = QuotedHTMLParser.removeQuotedHTML(draftHtml)
# add the block
console.log(@_createBlock(events,eventData));
text += "<br/>"+@_createBlock(events,eventData)+"<br/>";
text += "<br/>"+@_createBlock(events,eventData)+"<br/>"
newDraftHtml = QuotedHTMLParser.appendQuotedHTML(text, draftHtml)
@ -142,82 +144,82 @@ module.exports =
# Create an HtmlNode and write its attributes and child nodes
block = new HtmlNode("div")
.attr("class","send-availability")
.attr("data-send-availability",JSON.stringify({
# add the full event data here as JSON so that it can be read by this plugin
# elsewhere (e.g. right before sending the draft, etc)
event: eventData
times: ({start,end,serverKey} = e for e in events)
}), true)
.style("border","1px solid #EEE")
.style("border-radius","3px")
.style("padding","10px")
.attr("class","send-availability")
.attr("data-send-availability",JSON.stringify({
# add the full event data here as JSON so that it can be read by this plugin
# elsewhere (e.g. right before sending the draft, etc)
event: eventData
times: ({start,end,serverKey} = e for e in events)
}), true)
.style("border","1px solid #EEE")
.style("border-radius","3px")
.style("padding","10px")
eventInfo = block.appendNode("div")
.attr("class","event-container")
.style("padding","0 5px")
.attr("class","event-container")
.style("padding","0 5px")
eventInfo.appendNode("div")
.attr("class","event-title")
.appendText(eventData.title)
.style("font-weight","bold")
.style("font-size","18px")
.attr("class","event-title")
.appendText(eventData.title)
.style("font-weight","bold")
.style("font-size","18px")
eventInfo.appendNode("div")
.attr("class","event-location")
.appendText(eventData.location)
.attr("class","event-location")
.appendText(eventData.location)
eventInfo.appendNode("div")
.attr("class","event-description")
.style("font-size","13px")
.appendText(eventData.description)
.attr("class","event-description")
.style("font-size","13px")
.appendText(eventData.description)
eventInfo.appendNode("span")
.appendText("Click on a time to schedule instantly:")
.style("font-size","13px")
.style("color","#AAA")
.appendText("Click on a time to schedule instantly:")
.style("font-size","13px")
.style("color","#AAA")
daysContainer = block.appendNode("div")
.attr("class","days")
.style("display","flex")
.style("flex-wrap","wrap")
.style("padding","10px 0")
.attr("class","days")
.style("display","flex")
.style("flex-wrap","wrap")
.style("padding","10px 0")
# Create one div per day, and write each time slot in as a line
for dayText,dayEvents of byDay
dayBlock = daysContainer.appendNode("div")
.attr("class","day-container")
.style("flex-grow","1")
.style("margin","5px")
.style("border","1px solid #DDD")
.style("border-radius","3px")
.attr("class","day-container")
.style("flex-grow","1")
.style("margin","5px")
.style("border","1px solid #DDD")
.style("border-radius","3px")
dayBlock.appendNode("div")
.attr("class","day-title")
.style("text-align","center")
.style("font-size","13px")
.style("background","#EEE")
.style("color","#666")
.style("padding","2px 4px")
.appendText(dayText.toUpperCase())
.attr("class","day-title")
.style("text-align","center")
.style("font-size","13px")
.style("background","#EEE")
.style("color","#666")
.style("padding","2px 4px")
.appendText(dayText.toUpperCase())
times = dayBlock.appendNode("div")
.attr("class","day-times")
.style("padding","5px")
.attr("class","day-times")
.style("padding","5px")
# One line per time slot
for e in dayEvents
# The URL points to the event page with this time slot selected
eventUrl = url.format({
protocol: "http"
host: "localhost:8888"
protocol: "https"
host: "sendavail.herokuapp.com"
pathname: "/event/#{e.serverKey}"
})
times.appendNode("div")
.attr("class","day-time")
.style("padding","2px 0")
.appendNode("a")
.attr("href",eventUrl)
.attr("data-starttime",e.start)
.attr("data-endtime",e.end)
.style("text-decoration","none")
.appendText(e.time)
.attr("class","day-time")
.style("padding","2px 0")
.appendNode("a")
.attr("href",eventUrl)
.attr("data-starttime",e.start)
.attr("data-endtime",e.end)
.style("text-decoration","none")
.appendText(e.time)
return block.toString()

View file

@ -13,13 +13,13 @@ class AvailabilityDraftExtension extends DraftStoreExtension
sender = session.draft().from
matches = (/data-send-availability="(.*)?" style/).exec body
if matches?
json = matches[1].replace(/'/g,'"')
json = atob(matches[1])
data = JSON.parse(json)
data.attendees = []
data.attendees = participants.map (p) ->
name: p.name, email: p.email, isSender: c.isMe()
name: p.name, email: p.email, isSender: p.isMe()
console.log "Sending request!\n",JSON.stringify data
serverUrl = "http://localhost:8888/register-events"
serverUrl = "https://sendavail.herokuapp.com/register-events"
request.post {url: serverUrl, body: JSON.stringify(data)}, (error, resp, data) =>
console.log(error,resp,data)

View file

@ -5,9 +5,9 @@ class CalendarButton extends React.Component
@displayName: 'CalendarButton'
render: =>
<div className="btn btn-toolbar" onClick={@_onClick}>
<button className="btn btn-toolbar" onClick={@_onClick}>
Add Availability
</div>
</button>
_onClick: =>
BrowserWindow = require('remote').require('browser-window')

View file

@ -11,12 +11,12 @@
Event} = require 'nylas-exports'
url = require('url')
qs = require("querystring")
CalendarButton = require './calendar-button'
AvailabilityDraftExtension = require './availability-draft-extension'
path = require.resolve("electron-safe-ipc/host")
ipc = require('remote').require(path)
protocol = require('remote').require('protocol')
# A simple class for building HTML in code
class HtmlNode
@ -27,7 +27,7 @@ class HtmlNode
@children = []
attr: (k,v,isJson=false) ->
@attrs[k] = if isJson then v.replace(/"/g,"'") else v
@attrs[k] = if isJson then btoa(v) else v
return @
style: (k,v) ->
@ -39,8 +39,8 @@ class HtmlNode
return node
appendNode: (name) ->
node = new HtmlNode(name);
return @append(node);
node = new HtmlNode(name)
return @append(node)
appendText: (text) ->
@append(text)
@ -54,7 +54,7 @@ class HtmlNode
children = (if n instanceof HtmlNode then n.toString() else n for n in @children).join("\n")
return "<#{@name} #{attrs} style=\"#{styles}\">\n#{children}\n</#{@name}>"
else
return "<#{@name} #{attrs} style=\"#{styles}\" />"
return "<#{@name} #{attrs} style=\"#{styles}\" />"
module.exports =
@ -79,7 +79,13 @@ module.exports =
# Subscribe to the ipc event `fromRenderer`, which will be published
# elsewhere in the package.
ipc.on "fromRenderer", (args...) => @_onCalendarEvent(args...)
protocol.registerStringProtocol 'send-availability', (request, callback) =>
{host:event,query:rawQuery} = url.parse(request.url)
stringArgs = qs.parse(rawQuery)
data = {}
for own k,v of stringArgs
data[k] = JSON.parse(v)
response = @_onCalendarEvent(event,data,callback)
# Serialize is called when your package is about to be unmounted.
# You can return a state object that will be passed back to your package
@ -95,10 +101,11 @@ module.exports =
deactivate: ->
ComponentRegistry.unregister CalendarButton
DraftStore.unregister AvailabilityDraftExtension
protocol.unregisterProtocol('send-availability')
### Internal Methods ###
_onCalendarEvent: (event,data) ->
_onCalendarEvent: (event,data,callback) ->
switch event
when "get_events"
{start,end,id:eventId} = data
@ -106,14 +113,10 @@ module.exports =
Event.attributes.start.lessThan(end),
Event.attributes.end.greaterThan(start),
]).then (events) =>
@_sendToCalendar("get_events_"+eventId,events)
callback(JSON.stringify(events))
when "available_times"
{draftClientId,eventData,events} = data
@_addBlockToDraft(events,draftClientId,eventData);
# Sends a message to the calendar window
_sendToCalendar: (event,data) ->
ipc.send("fromMain", event, JSON.stringify(data))
{draftClientId,eventData,events} = data.data
@_addBlockToDraft(events,draftClientId,eventData)
# Grabs the current draft text, appends the availability HTML block to it, and saves
_addBlockToDraft: (events,draftClientId,eventData) ->
@ -123,8 +126,7 @@ module.exports =
text = QuotedHTMLParser.removeQuotedHTML(draftHtml)
# add the block
console.log(@_createBlock(events,eventData));
text += "<br/>"+@_createBlock(events,eventData)+"<br/>";
text += "<br/>"+@_createBlock(events,eventData)+"<br/>"
newDraftHtml = QuotedHTMLParser.appendQuotedHTML(text, draftHtml)
@ -206,8 +208,8 @@ module.exports =
for e in dayEvents
# The URL points to the event page with this time slot selected
eventUrl = url.format({
protocol: "http"
host: "localhost:8888"
protocol: "https"
host: "sendavail.herokuapp.com"
pathname: "/event/#{e.serverKey}"
})
times.appendNode("div")