mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-12-29 20:04:59 +08:00
feat(rsvp): "Quick RSVP" to events recongized by the API
This commit is contained in:
parent
09728d8cbc
commit
d48fa50654
12 changed files with 112 additions and 279 deletions
|
@ -59,7 +59,7 @@ class PreferencesSignatures extends React.Component
|
|||
|
||||
_renderAccountPicker: ->
|
||||
options = @state.accounts.map (account) ->
|
||||
<option value={account.id}>{account.emailAddress}</option>
|
||||
<option value={account.id} key={account.id}>{account.emailAddress}</option>
|
||||
|
||||
<select value={@state.selectedAccountId} onChange={@_onSelectAccount}>
|
||||
{options}
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
_ = require 'underscore'
|
||||
path = require 'path'
|
||||
React = require 'react'
|
||||
{RetinaImg} = require 'nylas-component-kit'
|
||||
{Actions,
|
||||
Event,
|
||||
Utils,
|
||||
ComponentRegistry,
|
||||
AccountStore} = require 'nylas-exports'
|
||||
EventRSVPTask = require './tasks/event-rsvp'
|
||||
moment = require 'moment-timezone'
|
||||
|
||||
class EventComponent extends React.Component
|
||||
@displayName: 'EventComponent'
|
||||
|
||||
@propTypes:
|
||||
event: React.PropTypes.object.isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
# Since getting state is asynchronous, default to empty values
|
||||
@state = @_nullEvent()
|
||||
|
||||
_nullEvent: ->
|
||||
participants: []
|
||||
title: ""
|
||||
when: {start_time: 0}
|
||||
|
||||
_onChange: =>
|
||||
DatabaseStore.find(Event, @props.event.id).then (event) =>
|
||||
event ?= @_nullEvent()
|
||||
@setState(event)
|
||||
|
||||
componentDidMount: -> @_onChange()
|
||||
|
||||
componentWillMount: ->
|
||||
@usubs.push DatabaseStore.listen (change) =>
|
||||
@_onChange() if change.objectClass is Event.name
|
||||
@usubs.push AccountStore.listen(@_onChange)
|
||||
|
||||
componentWillUnmount: -> usub?() for usub in @usubs()
|
||||
|
||||
_myStatus: =>
|
||||
myEmail = AccountStore.current()?.me().email
|
||||
for p in @state.participants
|
||||
if p['email'] == myEmail
|
||||
return p['status']
|
||||
|
||||
return null
|
||||
|
||||
render: =>
|
||||
<div className="event-wrapper">
|
||||
<div className="event-header">
|
||||
<RetinaImg name="icon-RSVP-calendar-mini@2x.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve}/>
|
||||
<span className="event-title-text">Event: </span><span className="event-title">{@state.title}</span>
|
||||
</div>
|
||||
<div className="event-body">
|
||||
<div className="event-date">
|
||||
<div className="event-day">
|
||||
{moment(@state.when['start_time']*1000).tz(Utils.timeZone).format("dddd, MMMM Do")}
|
||||
</div>
|
||||
<div>
|
||||
<div className="event-time">
|
||||
{moment(@state.when['start_time']*1000).tz(Utils.timeZone).format("h:mm a z")}
|
||||
</div>
|
||||
{@_renderEventActions()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
_renderEventActions: =>
|
||||
<div className="event-actions">
|
||||
{@_renderAcceptButton()}
|
||||
{@_renderMaybeButton()}
|
||||
{@_renderDeclineButton()}
|
||||
</div>
|
||||
|
||||
_renderAcceptButton: ->
|
||||
classes = "btn-rsvp"
|
||||
if @_myStatus() == "yes"
|
||||
classes += " yes"
|
||||
<div className=classes onClick={=> @_rsvp("yes")}>
|
||||
Accept
|
||||
</div>
|
||||
|
||||
_renderDeclineButton: ->
|
||||
classes = "btn-rsvp"
|
||||
if @_myStatus() == "no"
|
||||
classes += " no"
|
||||
<div className=classes onClick={=> @_rsvp("no")}>
|
||||
Decline
|
||||
</div>
|
||||
|
||||
_renderMaybeButton: ->
|
||||
classes = "btn-rsvp"
|
||||
if @_myStatus() == "maybe"
|
||||
classes += " maybe"
|
||||
<div className=classes onClick={=> @_rsvp("maybe")}>
|
||||
Maybe
|
||||
</div>
|
||||
|
||||
_rsvp: (status) ->
|
||||
Acitions.queueTask(new EventRSVPTask(@state, status))
|
||||
|
||||
|
||||
module.exports = EventComponent
|
93
internal_packages/events/lib/event-header.cjsx
Normal file
93
internal_packages/events/lib/event-header.cjsx
Normal file
|
@ -0,0 +1,93 @@
|
|||
_ = require 'underscore'
|
||||
path = require 'path'
|
||||
React = require 'react'
|
||||
{RetinaImg} = require 'nylas-component-kit'
|
||||
{Actions,
|
||||
Message,
|
||||
Event,
|
||||
Utils,
|
||||
ComponentRegistry,
|
||||
EventRSVPTask,
|
||||
DatabaseStore,
|
||||
AccountStore} = require 'nylas-exports'
|
||||
moment = require 'moment-timezone'
|
||||
|
||||
class EventHeader extends React.Component
|
||||
@displayName: 'EventHeader'
|
||||
|
||||
@propTypes:
|
||||
message: React.PropTypes.instanceOf(Message).isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
@state =
|
||||
event: @props.message.events[0]
|
||||
|
||||
_onChange: =>
|
||||
return unless @state.event
|
||||
DatabaseStore.find(Event, @state.event.id).then (event) =>
|
||||
return unless event
|
||||
@setState({event})
|
||||
|
||||
componentDidMount: =>
|
||||
@_unlisten = DatabaseStore.listen (change) =>
|
||||
if change.objectClass is Event.name
|
||||
updated = _.find change.objects, (o) => o.id is @state.event.id
|
||||
@setState({event: updated}) if updated
|
||||
@_onChange()
|
||||
|
||||
componentWillReceiveProps: (nextProps) =>
|
||||
@setState({event:nextProps.message.events[0]})
|
||||
@_onChange()
|
||||
|
||||
componentWillUnmount: =>
|
||||
@_unlisten?()
|
||||
|
||||
_myStatus: =>
|
||||
myEmail = AccountStore.current()?.me().email
|
||||
for p in @state.event.participants
|
||||
return p['status'] if p['email'] is myEmail
|
||||
return null
|
||||
|
||||
render: =>
|
||||
if @state.event?
|
||||
<div className="event-wrapper">
|
||||
<div className="event-header">
|
||||
<RetinaImg name="icon-RSVP-calendar-mini@2x.png"
|
||||
mode={RetinaImg.Mode.ContentPreserve}/>
|
||||
<span className="event-title-text">Event: </span><span className="event-title">{@state.event.title}</span>
|
||||
</div>
|
||||
<div className="event-body">
|
||||
<div className="event-date">
|
||||
<div className="event-day">
|
||||
{moment(@state.event.when['start_time']*1000).tz(Utils.timeZone).format("dddd, MMMM Do")}
|
||||
</div>
|
||||
<div>
|
||||
<div className="event-time">
|
||||
{moment(@state.event.when['start_time']*1000).tz(Utils.timeZone).format("h:mm a z")}
|
||||
</div>
|
||||
{@_renderEventActions()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
else
|
||||
<div></div>
|
||||
|
||||
_renderEventActions: =>
|
||||
actions = [["yes", "Accept"], ["maybe", "Maybe"], ["no", "Decline"]]
|
||||
|
||||
<div className="event-actions">
|
||||
{actions.map ([status, label]) =>
|
||||
classes = "btn-rsvp "
|
||||
classes += status if @_myStatus() is status
|
||||
<div key={status} className={classes} onClick={=> @_rsvp(status)}>
|
||||
{label}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
_rsvp: (status) =>
|
||||
Actions.queueTask(new EventRSVPTask(@state.event, status))
|
||||
|
||||
|
||||
module.exports = EventHeader
|
|
@ -1,12 +1,12 @@
|
|||
# {ComponentRegistry, WorkspaceStore} = require 'nylas-exports'
|
||||
# EventComponent = require "./event-component"
|
||||
{ComponentRegistry, WorkspaceStore} = require 'nylas-exports'
|
||||
EventHeader = require "./event-header"
|
||||
|
||||
module.exports =
|
||||
activate: (@state={}) ->
|
||||
# ComponentRegistry.register EventComponent,
|
||||
# role: 'Event'
|
||||
ComponentRegistry.register EventHeader,
|
||||
role: 'message:BodyHeader'
|
||||
|
||||
deactivate: ->
|
||||
# ComponentRegistry.unregister(EventComponent)
|
||||
ComponentRegistry.unregister(EventHeader)
|
||||
|
||||
serialize: -> @state
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
position: relative;
|
||||
font-size: @font-size-small;
|
||||
margin-top: @spacing-standard;
|
||||
box-shadow: inset 0 0 1px 1px rgba(0,0,0,0.09);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
border-radius: @border-radius-base;
|
||||
border: 1px solid @border-color-divider;
|
||||
|
||||
.event-header{
|
||||
border-bottom: 1px solid lighten(@border-color-divider, 6%);
|
||||
border-bottom: 1px solid @border-color-divider;
|
||||
padding: 10px;
|
||||
|
||||
img{
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
React = require 'react'
|
||||
AutoloadImagesStore = require './autoload-images-store'
|
||||
Actions = require './autoload-images-actions'
|
||||
{Message} = require 'nylas-exports'
|
||||
|
||||
class AutoloadImagesHeader extends React.Component
|
||||
@displayName: 'AutoloadImagesHeader'
|
||||
|
||||
@propTypes:
|
||||
message: React.PropTypes.instanceOf(Message).isRequired
|
||||
|
||||
constructor: (@props) ->
|
||||
|
||||
render: =>
|
||||
|
|
|
@ -80,7 +80,6 @@ class MessageItem extends React.Component
|
|||
<div className="message-item-area">
|
||||
{@_renderHeader()}
|
||||
<MessageItemBody message={@props.message} downloads={@state.downloads} />
|
||||
{@_renderEvents()}
|
||||
{@_renderAttachments()}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -156,13 +155,6 @@ class MessageItem extends React.Component
|
|||
else
|
||||
<div></div>
|
||||
|
||||
_renderEvents: =>
|
||||
events = @_eventComponents()
|
||||
if events.length > 0 and not Utils.looksLikeGmailInvite(@props.message)
|
||||
<div className="events-area">{events}</div>
|
||||
else
|
||||
<div></div>
|
||||
|
||||
_renderHeaderSideItems: ->
|
||||
styles =
|
||||
position: "absolute"
|
||||
|
@ -227,16 +219,6 @@ class MessageItem extends React.Component
|
|||
|
||||
return otherAttachments.concat(imageAttachments)
|
||||
|
||||
_eventComponents: =>
|
||||
events = @props.message.events.map (e) =>
|
||||
<InjectedComponent
|
||||
className="event-wrap"
|
||||
matching={role:"Event"}
|
||||
exposedProps={event:e}
|
||||
key={e.id}/>
|
||||
|
||||
return events
|
||||
|
||||
_isRealFile: (file) ->
|
||||
hasCIDInBody = file.contentId? and @props.message.body?.indexOf(file.contentId) > 0
|
||||
return not hasCIDInBody
|
||||
|
|
|
@ -46,7 +46,9 @@ class PreferencesSidebarItem extends React.Component
|
|||
"subitem": true
|
||||
"active": account.id is @props.selection.get('accountId')
|
||||
|
||||
<li className={classes} onClick={ (event) => @_onClickAccount(event, account.id)}>
|
||||
<li key={account.id}
|
||||
className={classes}
|
||||
onClick={ (event) => @_onClickAccount(event, account.id)}>
|
||||
{account.emailAddress}
|
||||
</li>
|
||||
else
|
||||
|
|
|
@ -23,6 +23,7 @@ class ConfigSchemaItem extends React.Component
|
|||
<h2>{_str.humanize(@props.keyName)}</h2>
|
||||
{_.pairs(@props.configSchema.properties).map ([key, value]) =>
|
||||
<ConfigSchemaItem
|
||||
key={key}
|
||||
keyName={key}
|
||||
keyPath={"#{@props.keyPath}.#{key}"}
|
||||
configSchema={value}
|
||||
|
@ -36,7 +37,7 @@ class ConfigSchemaItem extends React.Component
|
|||
<label htmlFor={@props.keyPath}>{@props.configSchema.title}:</label>
|
||||
<select onChange={@_onChangeValue} value={@props.config.get(@props.keyPath)}>
|
||||
{_.zip(@props.configSchema.enum, @props.configSchema.enumLabels).map ([value, label]) =>
|
||||
<option value={value}>{label}</option>
|
||||
<option key={value} value={value}>{label}</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -22,7 +22,7 @@ describe "modelFreeze", ->
|
|||
b: 2
|
||||
Utils.modelFreeze(o)
|
||||
expect(Object.isFrozen(o)).toBe(true)
|
||||
|
||||
|
||||
it "should not throw an exception when nulls appear in strange places", ->
|
||||
t = new Thread(participants: [new Contact(email: 'ben@nylas.com'), null], subject: '123')
|
||||
Utils.modelFreeze(t)
|
||||
|
@ -348,141 +348,6 @@ describe "isEqual", ->
|
|||
other = {a: 1}
|
||||
ok(!Utils.isEqual(new Foo, other))
|
||||
|
||||
describe "looksLikeGmailInvite", ->
|
||||
it "should return false for an exchange invite", ->
|
||||
message = {
|
||||
body: """<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
</head>
|
||||
<body>
|
||||
<div style="">
|
||||
<table cellspacing="0" cellpadding="8" border="0" summary="" style="width:100%;font-family:Arial,Sans-serif;border:1px Solid #ccc;border-width:1px 2px 2px 1px;background-color:#fff;" itemscope="" itemtype="http://schema.org/Event">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<meta itemprop="eventStatus" content="http://schema.org/EventScheduled">
|
||||
<div style="padding:2px"><span itemprop="publisher" itemscope="" itemtype="http://schema.org/Organization">
|
||||
<meta itemprop="name" content="Google Calendar">
|
||||
</span>
|
||||
<meta itemprop="eventId/googleCalendar" content="p068hq4mrslsbnddg0a8kdfg2s">
|
||||
<div style="float:right;font-weight:bold;font-size:13px"><a href="https://www.google.com/calendar/event?action=VIEW&eid=cDA2OGhxNG1yc2xzY…hYzQwYjFhNzEwZWUyZjkxNjI3ZTZlMzk3Yjk4NzQ4YWVhNjA4Nzg&ctz=UTC&hl=en" style="color:#20c;white-space:nowrap" itemprop="url">more
|
||||
details »</a><br>
|
||||
</div>
|
||||
<h3 style="padding:0 0 6px 0;margin:0;font-family:Arial,Sans-serif;font-size:16px;font-weight:bold;color:#222">
|
||||
<span itemprop="name">(No Subject)</span></h3>
|
||||
<table cellpadding="0" cellspacing="0" border="0" summary="Event details">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap" valign="top">
|
||||
<div><i style="font-style:normal">When</i></div>
|
||||
</td>
|
||||
<td style="padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222" valign="top">
|
||||
<time itemprop="startDate" datetime="20150730T220000Z"></time><time itemprop="endDate" datetime="20150730T230000Z"></time>Thu Jul 30, 2015 10pm – 11pm
|
||||
<span style="color:#888">GMT (no daylight saving)</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap" valign="top">
|
||||
<div><i style="font-style:normal">Calendar</i></div>
|
||||
</td>
|
||||
<td style="padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222" valign="top">
|
||||
ethan@nylas.com</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:0 1em 10px 0;font-family:Arial,Sans-serif;font-size:13px;color:#888;white-space:nowrap" valign="top">
|
||||
<div><i style="font-style:normal">Who</i></div>
|
||||
</td>
|
||||
<td style="padding-bottom:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222" valign="top">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222">
|
||||
<span style="font-family:Courier New,monospace">•</span></td>
|
||||
<td style="padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222">
|
||||
<div>
|
||||
<div style="margin:0 0 0.3em 0"><span itemprop="attendee" itemscope="" itemtype="http://schema.org/Person"><span itemprop="name">Ethan Blackburn</span>
|
||||
<meta itemprop="email" content="eblackb1@slu.edu">
|
||||
</span><span style="font-size:11px;color:#888"> - organizer</span></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222">
|
||||
<span style="font-family:Courier New,monospace">•</span></td>
|
||||
<td style="padding-right:10px;font-family:Arial,Sans-serif;font-size:13px;color:#222">
|
||||
<div>
|
||||
<div style="margin:0 0 0.3em 0"><span itemprop="attendee" itemscope="" itemtype="http://schema.org/Person"><span itemprop="name">ethan@nylas.com</span>
|
||||
<meta itemprop="email" content="ethan@nylas.com">
|
||||
</span></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p style="color:#222;font-size:13px;margin:0"><span style="color:#888">Going? </span>
|
||||
<wbr><strong><span itemprop="action" itemscope="" itemtype="http://schema.org/RsvpAction">
|
||||
<meta itemprop="attendance" content="http://schema.org/RsvpAttendance/Yes">
|
||||
<span itemprop="handler" itemscope="" itemtype="http://schema.org/HttpActionHandler"><link itemprop="method" href="http://schema.org/HttpRequestMethod/GET"><a href="https://www.google.com/calendar/event?action=RESPOND&eid=cDA2OGhxNG1yc2…hYzQwYjFhNzEwZWUyZjkxNjI3ZTZlMzk3Yjk4NzQ4YWVhNjA4Nzg&ctz=UTC&hl=en" style="color:#20c;white-space:nowrap" itemprop="url">Yes</a></span></span><span style="margin:0 0.4em;font-weight:normal">
|
||||
- </span><span itemprop="action" itemscope="" itemtype="http://schema.org/RsvpAction">
|
||||
<meta itemprop="attendance" content="http://schema.org/RsvpAttendance/Maybe">
|
||||
<span itemprop="handler" itemscope="" itemtype="http://schema.org/HttpActionHandler"><link itemprop="method" href="http://schema.org/HttpRequestMethod/GET"><a href="https://www.google.com/calendar/event?action=RESPOND&eid=cDA2OGhxNG1yc2…hYzQwYjFhNzEwZWUyZjkxNjI3ZTZlMzk3Yjk4NzQ4YWVhNjA4Nzg&ctz=UTC&hl=en" style="color:#20c;white-space:nowrap" itemprop="url">Maybe</a></span></span><span style="margin:0 0.4em;font-weight:normal">
|
||||
- </span><span itemprop="action" itemscope="" itemtype="http://schema.org/RsvpAction">
|
||||
<meta itemprop="attendance" content="http://schema.org/RsvpAttendance/No">
|
||||
<span itemprop="handler" itemscope="" itemtype="http://schema.org/HttpActionHandler"><link itemprop="method" href="http://schema.org/HttpRequestMethod/GET"><a href="https://www.google.com/calendar/event?action=RESPOND&eid=cDA2OGhxNG1yc2…hYzQwYjFhNzEwZWUyZjkxNjI3ZTZlMzk3Yjk4NzQ4YWVhNjA4Nzg&ctz=UTC&hl=en" style="color:#20c;white-space:nowrap" itemprop="url">No</a></span></span></strong>
|
||||
<wbr><a href="https://www.google.com/calendar/event?action=VIEW&eid=cDA2OGhxNG1yc2xzY…hYzQwYjFhNzEwZWUyZjkxNjI3ZTZlMzk3Yjk4NzQ4YWVhNjA4Nzg&ctz=UTC&hl=en" style="color:#20c;white-space:nowrap" itemprop="url">more
|
||||
options »</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="background-color:#f6f6f6;color:#888;border-top:1px Solid #ccc;font-family:Arial,Sans-serif;font-size:11px">
|
||||
<p>Invitation from <a href="https://www.google.com/calendar/" target="_blank" style="">
|
||||
Google Calendar</a></p>
|
||||
<p>You are receiving this email at the account ethan@nylas.com because you are subscribed for invitations on calendar ethan@nylas.com.</p>
|
||||
<p>To stop receiving these emails, please log in to https://www.google.com/calendar/ and change your notification settings for this calendar.</p>
|
||||
<p>Forwarding this invitation could allow any recipient to modify your RSVP response.
|
||||
<a href="https://support.google.com/calendar/answer/37135#forwarding">Learn More</a>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
}
|
||||
|
||||
expect(Utils.looksLikeGmailInvite(message)).toEqual(true)
|
||||
|
||||
it "should return true for a gmail invite", ->
|
||||
message = {
|
||||
body: """<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
|
||||
</head>
|
||||
<body>
|
||||
<br>
|
||||
<br>
|
||||
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
|
||||
Begin forwarded message: <br>
|
||||
<br>
|
||||
From: Originator <originator@nylas.com><br>
|
||||
Subject: <> Nylas E-mail Call and Webhooks review<br>
|
||||
Date: Jul 28 2015, at 2:32 pm<br>
|
||||
To: random@guy.com <random@guy.com>, Someone Else <someone@else.com>
|
||||
<br>
|
||||
<br>
|
||||
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
|
||||
<div id="divtagdefaultwrapper" style="font-size:12pt;color:#000000;background-color:#FFFFFF;font-family:Calibri,Arial,Helvetica,sans-serif;">
|
||||
Call to review Pipeline Deals email tool and webhooks support.</div>
|
||||
</blockquote>
|
||||
</body>
|
||||
</html>"""
|
||||
}
|
||||
|
||||
describe "subjectWithPrefix", ->
|
||||
it "should replace an existing Re:", ->
|
||||
expect(Utils.subjectWithPrefix("Re: Test Case", "Fwd:")).toEqual("Fwd: Test Case")
|
||||
|
|
|
@ -99,13 +99,6 @@ Utils =
|
|||
return ext in extensions and size > 512 and size < 1024*1024*5
|
||||
|
||||
|
||||
looksLikeGmailInvite: (message={}) ->
|
||||
idx = message.body.search('itemtype="http://schema.org/Event"')
|
||||
if idx == -1
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
# Escapes potentially dangerous html characters
|
||||
# This code is lifted from Angular.js
|
||||
# See their specs here:
|
||||
|
|
|
@ -88,7 +88,7 @@ class NylasExports
|
|||
# Tasks
|
||||
# These need to be required immediately to populate the TaskRegistry so
|
||||
# we know how to deserialized saved or IPC-sent tasks.
|
||||
@require "EventRSVP", 'flux/tasks/event-rsvp'
|
||||
@require "EventRSVPTask", 'flux/tasks/event-rsvp'
|
||||
@require "SendDraftTask", 'flux/tasks/send-draft'
|
||||
@require "FileUploadTask", 'flux/tasks/file-upload-task'
|
||||
@require "DestroyDraftTask", 'flux/tasks/destroy-draft'
|
||||
|
|
Loading…
Reference in a new issue