mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-09-21 20:15:52 +08:00
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:
parent
aaf6132134
commit
abb6378bd5
20 changed files with 275 additions and 205 deletions
1
examples/N1-Send-Availability/backend/Procfile
Normal file
1
examples/N1-Send-Availability/backend/Procfile
Normal file
|
@ -0,0 +1 @@
|
|||
web: gunicorn backend:app --log-file=-
|
5
examples/N1-Send-Availability/backend/app.json
Normal file
5
examples/N1-Send-Availability/backend/app.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "N1 Send Availability Backend",
|
||||
"description": "Server supporting the N1 Send Availability plugin",
|
||||
"addons": [ "cleardb:ignite" ]
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
31
examples/N1-Send-Availability/backend/requirements.txt
Normal file
31
examples/N1-Send-Availability/backend/requirements.txt
Normal 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
|
1
examples/N1-Send-Availability/backend/runtime.txt
Normal file
1
examples/N1-Send-Availability/backend/runtime.txt
Normal file
|
@ -0,0 +1 @@
|
|||
python-2.7.9
|
|
@ -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
|
@ -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>
|
|
@ -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>
|
|
@ -1 +1,5 @@
|
|||
ALT TEXT
|
||||
{{ event.title }}
|
||||
Location:
|
||||
{{ event.location }}
|
||||
Description:
|
||||
{{ event.description }}
|
|
@ -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
|
||||
};
|
||||
});
|
||||
|
|
30
examples/N1-Send-Availability/backend/templates/success.html
Normal file
30
examples/N1-Send-Availability/backend/templates/success.html
Normal 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>
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ method. Here, we’re registering the events before we send the draft.</p>
|
|||
data.attendees = participants.map (p) ->
|
||||
<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> =></span>
|
||||
<span class="hljs-built_in">console</span>.log(error,resp,data)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Add table
Reference in a new issue