From 19a115c674df44777815e7e1a690970d7a66ebf6 Mon Sep 17 00:00:00 2001 From: Halla Moore Date: Thu, 17 Nov 2016 15:33:10 -0800 Subject: [PATCH 1/8] feat(calendar): Allow calendar events to be dragged and resized Summary: In order to allow events to be dragged across multiple days, this involved implementing a new data source for the WeekView that modifies the event feed based on mouse event data. Depends on D3451 Test Plan: manual Reviewers: evan, bengotow Differential Revision: https://phab.nylas.com/D3452 --- .../main-calendar/lib/calendar-wrapper.jsx | 4 +- .../calendar-dragging-data-source.es6 | 103 ++++++++++++++++++ .../calendar-event-container.jsx | 75 +++++++++++-- .../nylas-calendar/calendar-event.jsx | 12 +- .../nylas-calendar/calendar-helpers.jsx | 11 +- .../nylas-calendar/nylas-calendar.jsx | 1 + src/components/nylas-calendar/week-view.jsx | 53 ++++++++- src/flux/models/event.es6 | 22 ++++ src/global/nylas-exports.es6 | 1 + src/pro | 2 +- static/components/nylas-calendar.less | 15 +++ 11 files changed, 274 insertions(+), 25 deletions(-) create mode 100644 src/components/nylas-calendar/calendar-dragging-data-source.es6 diff --git a/internal_packages/main-calendar/lib/calendar-wrapper.jsx b/internal_packages/main-calendar/lib/calendar-wrapper.jsx index efc0d1853f..692e2377a6 100644 --- a/internal_packages/main-calendar/lib/calendar-wrapper.jsx +++ b/internal_packages/main-calendar/lib/calendar-wrapper.jsx @@ -1,7 +1,7 @@ import { Actions, DestroyModelTask, - CalendarDataSource, + CalendarDraggingDataSource, } from 'nylas-exports'; import { NylasCalendar, @@ -18,7 +18,7 @@ export default class CalendarWrapper extends React.Component { constructor(props) { super(props); - this._dataSource = new CalendarDataSource(); + this._dataSource = new CalendarDraggingDataSource(); this.state = {selectedEvents: []}; } diff --git a/src/components/nylas-calendar/calendar-dragging-data-source.es6 b/src/components/nylas-calendar/calendar-dragging-data-source.es6 new file mode 100644 index 0000000000..d82b1c3bcb --- /dev/null +++ b/src/components/nylas-calendar/calendar-dragging-data-source.es6 @@ -0,0 +1,103 @@ +import Rx from 'rx-lite'; +import {DatabaseStore, Event, Utils} from 'nylas-exports'; +import CalendarDataSource from './calendar-data-source'; + +/* This is a modified version of the CalendarDataSource that incorporates mouse +events in order to allow us to render the dragging/resizing of an event across +an entire calendar view. It removes the targeted event from the results of the +CalendarDataSource and adds a clone of that event with the updated times. */ + +export default class CalendarDraggingDataSource extends CalendarDataSource { + buildObservable({startTime, endTime, disabledCalendars, mouseHandlerObserver}) { + this.observable = Rx.Observable.combineLatest( + super.buildObservable({startTime, endTime, disabledCalendars}), + mouseHandlerObserver, + ) + .flatMapLatest(([superResult, mouseEventData]) => { + const results = Utils.deepClone(superResult); + const {calEventId, mouseDownCalEventId} = mouseEventData; + + // Dragging + if (mouseDownCalEventId != null) { + const {event} = this._findEvent(results.events, mouseDownCalEventId, true); + + // If we don't have the dragged event, find it in the database + if (!event) { + return Rx.Observable.fromPromise( + DatabaseStore.find(Event, mouseDownCalEventId).then((e) => { + this._dragAndAddEvent(e, mouseEventData, results); + return Promise.resolve(results); + }) + ) + } + + this._dragAndAddEvent(event, mouseEventData, results); + } + + // Hovering + if (calEventId != null && mouseDownCalEventId == null) { + const {event, index} = this._findEvent(results.events, calEventId); + if (event) { + event.hovered = true; + // Keep the events in order so that hovering over an overlapping event + // doesn't make it change positions. + results.events.splice(index, 1, event) + } + } + return Rx.Observable.from([results]); + }) + return this.observable; + } + + subscribe(callback) { + return this.observable.subscribe(callback) + } + + /* + * Create a dragged version of the event, based on the mouseData, and add it + * to the results. + * + * event - the calendar event that should be dragged + * mouseData - the data from the mouse event + * allResults - the whole results object, should have an 'events' entry that + * stores an array of calendar event models + */ + _dragAndAddEvent(event, mouseData, allResults) { + let newEvent; + if (mouseData.time) { + newEvent = event.shiftTimes(mouseData.dragHandles, mouseData.mouseDownTime, mouseData.time); + } else { + newEvent = event.clone(); + } + newEvent.hovered = true; + newEvent.dragged = true; + allResults.events.push(newEvent); + } + + /* + * Given an array of events and an eventId, find the event with that id. + * + * events - an array of calendar event models + * eventId - the id of the desired event + * remove - a boolean indicating whether event should be removed from the + * events array + * Returns an object - { + * event: the matching event, + * index: the index of the event within the events array, + * } + */ + _findEvent(events, eventId, removeEvent) { + let event; + let i; + for (i = 0; i < events.length; i++) { + if (events[i].id === eventId) { + event = events[i] + if (removeEvent) { + events.splice(i, 1); + } + break; + } + } + return {event, index: i}; + } +} diff --git a/src/components/nylas-calendar/calendar-event-container.jsx b/src/components/nylas-calendar/calendar-event-container.jsx index 530cac77f6..653a4f2135 100644 --- a/src/components/nylas-calendar/calendar-event-container.jsx +++ b/src/components/nylas-calendar/calendar-event-container.jsx @@ -1,7 +1,7 @@ import moment from 'moment' - import React from 'react' import ReactDOM from 'react-dom' +import {Actions, DatabaseStore, Event, SyncbackEventTask} from 'nylas-exports' export default class CalendarEventContainer extends React.Component { static displayName = "CalendarEventContainer"; @@ -30,26 +30,76 @@ export default class CalendarEventContainer extends React.Component { if (!this._mouseIsDown) { return } + const data = this._dataFromMouseEvent(event); + + // An event was dragged, persist and syncback the updated times + if (this._mouseDownCalEventId && data.time) { + const origTime = this._mouseDownTime; // Store current value for use in callback + + DatabaseStore.find(Event, this._mouseDownCalEventId).then((calEvent) => { + const newCalEvent = calEvent.shiftTimes(this._dragHandles, origTime, data.time); + + DatabaseStore.inTransaction((t) => { + t.persistModel(newCalEvent); + }).then(() => { + const task = new SyncbackEventTask(newCalEvent.clientId); + Actions.queueTask(task); + }) + }) + } + this._mouseIsDown = false; - this._runPropsHandler("onCalendarMouseUp", event) + this._mouseDownTime = null; + this._mouseDownCalEventId = null; + this._runPropsHandler("onCalendarMouseUp", event, data) } _onCalendarMouseDown = (event) => { this._DOMCache = {}; this._mouseIsDown = true; - this._runPropsHandler("onCalendarMouseDown", event) + + // Note that the values of _dragHandles are used to figure out which time fields + // in the Event model should be updated. Only 'start' and 'end' are valid values. + this._dragHandles = []; + + const data = this._dataFromMouseEvent(event); + this._mouseDownTime = data.time; + + if (data.calEventId) { + this._mouseDownCalEventId = data.calEventId; + + const classList = event.target.classList; + if (classList.contains("top")) { + this._dragHandles.push("start"); + } else if (classList.contains("bottom")) { + this._dragHandles.push("end"); + } else { + this._dragHandles.push("start", "end"); + } + } + + this._runPropsHandler("onCalendarMouseDown", event, data) } _onCalendarMouseMove = (event) => { this._runPropsHandler("onCalendarMouseMove", event) } - _runPropsHandler(name, event) { + + // data is an optional param for if the handler already ran + // this._dataFromMouseEvent() and can pass those results in. If not, + // this._dataFromMouseEvent() will be run here in this function. + _runPropsHandler(name, event, data) { const propsFn = this.props[name] if (!propsFn) { return } - const {time, x, y, width, height} = this._dataFromMouseEvent(event); + const {time, x, y, width, height, calEventId} = data || this._dataFromMouseEvent(event); try { - propsFn({event, time, x, y, width, height, mouseIsDown: this._mouseIsDown}) + const args = {event, time, x, y, width, height, calEventId}; + args.mouseIsDown = this._mouseIsDown; + args.mouseDownTime = this._mouseDownTime; + args.mouseDownCalEventId = this._mouseDownCalEventId; + args.dragHandles = this._dragHandles; + propsFn(args); } catch (error) { NylasEnv.reportError(error) } @@ -62,7 +112,7 @@ export default class CalendarEventContainer extends React.Component { let height = null; let time = null; if (!event.target || !event.target.closest) { return {x, y, width, height, time} } - const eventColumn = this._DOMCache.eventColumn || event.target.closest(".event-column"); + const eventColumn = event.target.closest(".event-column"); const gridWrap = this._DOMCache.gridWrap || event.target.closest(".event-grid-wrap .scroll-region-content-inner"); const calWrap = this._DOMCache.calWrap || event.target.closest(".calendar-area-wrap") if (!gridWrap || !eventColumn) { return {x, y, width, height, time} } @@ -70,7 +120,7 @@ export default class CalendarEventContainer extends React.Component { const rect = this._DOMCache.rect || gridWrap.getBoundingClientRect(); const calWrapRect = this._DOMCache.calWrapRect || calWrap.getBoundingClientRect(); - this._DOMCache = {rect, eventColumn, gridWrap, calWrap} + this._DOMCache = {rect, gridWrap, calWrap} y = (gridWrap.scrollTop + event.clientY - rect.top); x = (calWrap.scrollLeft + event.clientX - calWrapRect.left); @@ -79,7 +129,14 @@ export default class CalendarEventContainer extends React.Component { const percentDay = y / height; const diff = ((+eventColumn.dataset.end) - (+eventColumn.dataset.start)) time = moment(diff * percentDay + (+eventColumn.dataset.start)); - return {x, y, width, height, time} + + let calEventId; + const closestCalEvent = event.target.closest(".calendar-event"); + if (closestCalEvent) { + calEventId = closestCalEvent.dataset.id; + } + + return {x, y, width, height, time, calEventId} } _onWindowMouseUp = (event) => { diff --git a/src/components/nylas-calendar/calendar-event.jsx b/src/components/nylas-calendar/calendar-event.jsx index 12a6e037cc..fdc2267b31 100644 --- a/src/components/nylas-calendar/calendar-event.jsx +++ b/src/components/nylas-calendar/calendar-event.jsx @@ -88,7 +88,14 @@ export default class CalendarEvent extends React.Component { top: d.left, } } - styles.backgroundColor = calcColor(this.props.event.calendarId); + + if (this.props.event.dragged) { + styles.zIndex = 1; + } + + const bgOpacity = this.props.event.hovered ? 1 : null; + styles.backgroundColor = calcColor(this.props.event.calendarId, bgOpacity); + return styles } @@ -107,7 +114,9 @@ export default class CalendarEvent extends React.Component { className={`calendar-event ${direction} ${selected ? 'selected' : null}`} onClick={(e) => onClick(e, event)} onDoubleClick={() => onDoubleClick(event)} + data-id={event.id} > +
{event.displayTitle()} @@ -118,6 +127,7 @@ export default class CalendarEvent extends React.Component { exposedProps={{event: event}} direction="row" /> +
) } diff --git a/src/components/nylas-calendar/calendar-helpers.jsx b/src/components/nylas-calendar/calendar-helpers.jsx index 54eb71a561..37583de46c 100644 --- a/src/components/nylas-calendar/calendar-helpers.jsx +++ b/src/components/nylas-calendar/calendar-helpers.jsx @@ -1,10 +1,7 @@ import {Utils} from 'nylas-exports' -export function calcColor(calendarId) { - let bgColor = NylasEnv.config.get(`calendar.colors.${calendarId}`) - if (!bgColor) { - const hue = Utils.hueForString(calendarId); - bgColor = `hsla(${hue}, 50%, 45%, 0.35)` - } - return bgColor +export function calcColor(calendarId, opacity) { + const alpha = opacity || 0.35; + const hue = NylasEnv.config.get(`calendar.colors.${calendarId}`) || Utils.hueForString(calendarId); + return `hsla(${hue}, 50%, 45%, ${alpha})` } diff --git a/src/components/nylas-calendar/nylas-calendar.jsx b/src/components/nylas-calendar/nylas-calendar.jsx index ce3564fa12..f11a758b4a 100644 --- a/src/components/nylas-calendar/nylas-calendar.jsx +++ b/src/components/nylas-calendar/nylas-calendar.jsx @@ -1,6 +1,7 @@ import Rx from 'rx-lite' import React from 'react' import moment from 'moment' +import 'moment-round' import {DatabaseStore, AccountStore, Calendar} from 'nylas-exports' import {ScrollRegion, ResizableRegion, MiniMonthView} from 'nylas-component-kit' import WeekView from './week-view' diff --git a/src/components/nylas-calendar/week-view.jsx b/src/components/nylas-calendar/week-view.jsx index 747e7f62b1..027428bb5c 100644 --- a/src/components/nylas-calendar/week-view.jsx +++ b/src/components/nylas-calendar/week-view.jsx @@ -1,5 +1,6 @@ /* eslint react/jsx-no-bind: 0 */ import _ from 'underscore' +import Rx from 'rx-lite' import React from 'react' import ReactDOM from 'react-dom' import moment from 'moment-timezone' @@ -52,6 +53,9 @@ export default class WeekView extends React.Component { bannerComponents: false, headerComponents: false, footerComponents: false, + onCalendarMouseUp: () => {}, + onCalendarMouseDown: () => {}, + onCalendarMouseMove: () => {}, } constructor(props) { @@ -96,6 +100,11 @@ export default class WeekView extends React.Component { return moment() } + _mouseHandlerSubscribe = (observer) => { + this._observer = observer; + observer.onNext({}); + } + _initializeComponent(props) { this.todayYear = this._now().year() this.todayDayOfYear = this._now().dayOfYear() @@ -107,14 +116,15 @@ export default class WeekView extends React.Component { const startMoment = this._calculateStartMoment(props); const endMoment = this._calculateEndMoment(props); + const mouseHandlerObserver = Rx.Observable.create(this._mouseHandlerSubscribe); this._sub = this.props.dataSource.buildObservable({ disabledCalendars: props.disabledCalendars, startTime: startMoment.unix(), endTime: endMoment.unix(), + mouseHandlerObserver: mouseHandlerObserver, }).subscribe((state) => { this.setState(state); }) - this.setState({startMoment, endMoment}) const percent = (this._scrollTime - startMoment.unix()) / (endMoment.unix() - startMoment.unix()) @@ -205,14 +215,23 @@ export default class WeekView extends React.Component { */ _eventOverlap(events) { const times = {} + const overlapById = {} for (const event of events) { + // We want dragged events to stay full-width. Since width is calculated + // by number of concurrentEvents, mock that out here. + if (event.dragged) { + overlapById[event.id] = { + concurrentEvents: 1, + order: 1, + }; + continue; + } if (!times[event.start]) { times[event.start] = [] } if (!times[event.end]) { times[event.end] = [] } times[event.start].push(event) times[event.end].push(event) } const sortedTimes = Object.keys(times).map((k) => parseInt(k, 10)).sort(); - const overlapById = {} let startedEvents = [] for (const t of sortedTimes) { for (const e of times[t]) { @@ -411,6 +430,30 @@ export default class WeekView extends React.Component { return (BUFFER_DAYS * 2 + DAYS_IN_VIEW) / DAYS_IN_VIEW } + _sendMouseEventToObserver = (args) => { + if (this._observer) { + this._observer.onNext(args) + } + } + + _onCalendarMouseDown = (args) => { + this._sendMouseEventToObserver(args); + this.props.onCalendarMouseDown(args); + } + + _onCalendarMouseMove = (args) => { + this._sendMouseEventToObserver(args); + this.props.onCalendarMouseMove(args) + if (this.refs.eventGridBg) { + this.refs.eventGridBg.mouseMove(args) + } + } + + _onCalendarMouseUp = (args) => { + this._sendMouseEventToObserver(args); + this.props.onCalendarMouseUp(args); + } + // We calculate events by days so we only need to iterate through all // events in the span once. _eventsByDay(days) { @@ -450,9 +493,9 @@ export default class WeekView extends React.Component {
diff --git a/src/flux/models/event.es6 b/src/flux/models/event.es6 index a00362a5c7..8d3443e712 100644 --- a/src/flux/models/event.es6 +++ b/src/flux/models/event.es6 @@ -1,6 +1,7 @@ import chrono from 'chrono-node'; import moment from 'moment'; +import {Utils} from 'nylas-exports'; import Model from './model'; import Attributes from '../attributes'; import Contact from './contact'; @@ -135,6 +136,7 @@ export default class Event extends Model { fromJSON(json) { super.fromJSON(json) + this.when = Utils.deepClone(this.when); const when = this.when; @@ -200,4 +202,24 @@ export default class Event extends Model { } return null; } + + /* Shifts event times by newTime - origTime, rounded to the nearest multiple of + * 15 minutes. Also updates the corresponding entry in the when object. + * + * timeFields - an array, valid entries are 'start' and 'end' + * origTime - a Moment instance, the anchor point of the shift + * newTime - a Moment instance, the point where the shift ends. + */ + shiftTimes = (timeFields, origTime, newTime) => { + const timeDelta = newTime.clone() + .subtract(origTime.valueOf()) + .round(15, 'minutes') + .unix() + const newEvent = this.clone(); + for (const field of timeFields) { + newEvent[field] += timeDelta; + newEvent.when[`${field}_time`] += timeDelta; + } + return newEvent; + } } diff --git a/src/global/nylas-exports.es6 b/src/global/nylas-exports.es6 index 7e52d432b0..df9722f2f0 100644 --- a/src/global/nylas-exports.es6 +++ b/src/global/nylas-exports.es6 @@ -78,6 +78,7 @@ lazyLoad(`DatabaseStore`, 'flux/stores/database-store'); lazyLoad(`QueryResultSet`, 'flux/models/query-result-set'); lazyLoad(`QuerySubscription`, 'flux/models/query-subscription'); lazyLoad(`CalendarDataSource`, 'components/nylas-calendar/calendar-data-source'); +lazyLoad(`CalendarDraggingDataSource`, 'components/nylas-calendar/calendar-dragging-data-source'); lazyLoad(`DatabaseTransaction`, 'flux/stores/database-transaction'); lazyLoad(`MutableQueryResultSet`, 'flux/models/mutable-query-result-set'); lazyLoad(`QuerySubscriptionPool`, 'flux/models/query-subscription-pool'); diff --git a/src/pro b/src/pro index 73178c7699..925173864c 160000 --- a/src/pro +++ b/src/pro @@ -1 +1 @@ -Subproject commit 73178c7699588b40781badba85aceefda8c0bed7 +Subproject commit 925173864ca07f5e26150ec34eeb2421c89d0f57 diff --git a/static/components/nylas-calendar.less b/static/components/nylas-calendar.less index 554df3175d..02cc0c5e2a 100644 --- a/static/components/nylas-calendar.less +++ b/static/components/nylas-calendar.less @@ -96,6 +96,21 @@ body.platform-win32 { flex: 1; } + .resize-handle { + position: absolute; + width: 100%; + height: 5px; + cursor: ns-resize; + + &.top { + top: 0; + } + + &.bottom { + bottom: 0; + } + } + &:before { content: ""; position: absolute; From 5da2ff8284b001a1af35cd43e816d5c41675b403 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Tue, 22 Nov 2016 11:36:16 -0800 Subject: [PATCH 2/8] bump(changelog): 0.4.400 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 474502470e..d1097f62b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # N1 Changelog +### 0.4.400 (11/22/16) + +- Features: + + New Salesforce private beta plugin. + +- Fixes: + + + Improvements to quoted text folding + + Fixes to the creation of bullets & lists in the composer + + Send and Archive fixed in composer windows + + Typing in the composer with mispelled words is now much faster + +- Development: + + + We now use `electron-packager` to build N1 + + `script/bootstrap` has been replaced with a much simpler install flow built on `electron-compile` + + Removed devDependencies from main package.json reducing file size + + We no longer support Node 4 for building + + New background worker to parallelize expensive database queries + + ### 0.4.204 (11/7/16) - Features: From b462c4bd57d763199c0f3bebbe184cfc3edb2db6 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Tue, 22 Nov 2016 13:01:30 -0800 Subject: [PATCH 3/8] fix(spellcheck): fix right click menu on spellcheck --- .../composer-spellcheck/lib/spellcheck-composer-extension.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal_packages/composer-spellcheck/lib/spellcheck-composer-extension.es6 b/internal_packages/composer-spellcheck/lib/spellcheck-composer-extension.es6 index dd894533ea..eec17d7a04 100644 --- a/internal_packages/composer-spellcheck/lib/spellcheck-composer-extension.es6 +++ b/internal_packages/composer-spellcheck/lib/spellcheck-composer-extension.es6 @@ -163,10 +163,10 @@ export default class SpellcheckComposerExtension extends ComposerExtension { word, onCorrect: (correction) => { DOMUtils.Mutating.applyTextInRange(range, selection, correction); - SpellcheckComposerExtension.update(editor); + SpellcheckComposerExtension.onContentChanged({editor}); }, onDidLearn: () => { - SpellcheckComposerExtension.update(editor); + SpellcheckComposerExtension.onContentChanged({editor}); }, }); } From 76a75ff2b8b9c071818f25fb597ccc55236db78b Mon Sep 17 00:00:00 2001 From: Jonathan Lai Date: Tue, 22 Nov 2016 15:47:18 -0800 Subject: [PATCH 4/8] Typo: mispelled > misspelled (#3085) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1097f62b8..83e27795d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ + Improvements to quoted text folding + Fixes to the creation of bullets & lists in the composer + Send and Archive fixed in composer windows - + Typing in the composer with mispelled words is now much faster + + Typing in the composer with misspelled words is now much faster - Development: From 2f7b8b0bd22739be0fe2d28464cf17df08693bd9 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Mon, 5 Dec 2016 13:13:02 -0800 Subject: [PATCH 5/8] fix(SFDC): fix null defaultValue to tokenizing field --- src/components/tokenizing-text-field.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/tokenizing-text-field.jsx b/src/components/tokenizing-text-field.jsx index 2b34a7d288..014c4bb5d9 100644 --- a/src/components/tokenizing-text-field.jsx +++ b/src/components/tokenizing-text-field.jsx @@ -395,7 +395,7 @@ export default class TokenizingTextField extends React.Component { componentWillReceiveProps(newProps) { if (this.props.tokens.length === 0) { - this.setState({inputValue: newProps.defaultValue}); + this.setState({inputValue: newProps.defaultValue || ""}); if (newProps.defaultValue && newProps.defaultValue.length > 0) { this._refreshCompletions(newProps.defaultValue); } From 07b32006f21e29ca654c6365d33c9611821f113b Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Mon, 5 Dec 2016 13:56:29 -0800 Subject: [PATCH 6/8] feat(SFDC): pull up sidebar Contacts and Leads by name too --- src/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pro b/src/pro index 925173864c..d951de1611 160000 --- a/src/pro +++ b/src/pro @@ -1 +1 @@ -Subproject commit 925173864ca07f5e26150ec34eeb2421c89d0f57 +Subproject commit d951de1611a1c336dd85290f1b6ec3125df04d1e From b9702976af20c0e74bccf4455482f80f268ec8f3 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Mon, 5 Dec 2016 14:21:04 -0800 Subject: [PATCH 7/8] fix(iframe): return if the iframe doc hasn't loaded yet --- src/components/evented-iframe.cjsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/evented-iframe.cjsx b/src/components/evented-iframe.cjsx index e6ce9ff476..01ec294328 100644 --- a/src/components/evented-iframe.cjsx +++ b/src/components/evented-iframe.cjsx @@ -76,6 +76,7 @@ class EventedIFrame extends React.Component return unless @props.searchable node = ReactDOM.findDOMNode(@) doc = node.contentDocument?.body ? node.contentDocument + return unless doc searchIndex = SearchableComponentStore.getCurrentRegionIndex(@_regionId) {searchTerm} = SearchableComponentStore.getCurrentSearchData() if @lastSearchIndex isnt searchIndex or @lastSearchTerm isnt searchTerm From d8d1c5315058c2f28102aa33adb8612f5d72e658 Mon Sep 17 00:00:00 2001 From: Evan Morikawa Date: Mon, 5 Dec 2016 14:23:10 -0800 Subject: [PATCH 8/8] bump(version): 0.4.401 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e27795d4..a26b45359f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # N1 Changelog +### 0.4.401 (12/05/16) + +- Fixes: + + + Fixes to issues with the Salesforce plugin + + Will search for Salesforce Contacts by name in addition to email + + Fixes to errors being thrown on spellcheck right-click + ### 0.4.400 (11/22/16) - Features: diff --git a/package.json b/package.json index 1648f1cb54..0b991dec93 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nylas", "productName": "Nylas N1", - "version": "0.4.400", + "version": "0.4.401", "description": "The best email app for people and teams at work", "license": "GPL-3.0", "main": "./src/browser/main.js",