mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-14 13:44:41 +08:00
92bff6ca5a
Summary: This diff implements a behavior change described in https://github.com/nylas/N1/issues/1722. Reply buttons should prefer to focus an existing draft in reply to the same message, if one is pristine, altering it as necessary to switch between reply / reply-all. If no pristine reply is already there, it creates one. Reply keyboard shortcuts should do the same, but more strictly - the shortcuts should switch between reply / reply-all for an existing draft regardless of whether it's pristine. This diff also cleans up the DraftStore and moves all the draft creation itself to a new DraftFactory object. This makes it much easier to see what's going on in the DraftStore, and I also refactored away the "newMessageWithContext" method, which was breaking the logic for Reply vs Forward between a bunch of different helper methods and was hard to follow. Test Plan: They're all wrecked. Will fix after concept is greenlighted Reviewers: evan, juan Reviewed By: juan Differential Revision: https://phab.nylas.com/D2776
175 lines
4 KiB
JavaScript
175 lines
4 KiB
JavaScript
import _ from 'underscore'
|
|
import {
|
|
Thread,
|
|
Actions,
|
|
Message,
|
|
TaskFactory,
|
|
DatabaseStore,
|
|
FocusedPerspectiveStore,
|
|
} from 'nylas-exports'
|
|
|
|
export default class ThreadListContextMenu {
|
|
constructor({threadIds = [], accountIds = []}) {
|
|
this.threadIds = threadIds
|
|
this.accountIds = accountIds
|
|
}
|
|
|
|
menuItemTemplate() {
|
|
return DatabaseStore.modelify(Thread, this.threadIds)
|
|
.then((threads) => {
|
|
this.threads = threads;
|
|
|
|
return Promise.all([
|
|
this.replyItem(),
|
|
this.replyAllItem(),
|
|
this.forwardItem(),
|
|
{type: 'separator'},
|
|
this.archiveItem(),
|
|
this.trashItem(),
|
|
this.markAsReadItem(),
|
|
this.starItem(),
|
|
// this.moveToOrLabelItem(),
|
|
// {type: 'separator'},
|
|
// this.extensionItems(),
|
|
])
|
|
}).then((menuItems) => {
|
|
return _.filter(_.compact(menuItems), (item, index) => {
|
|
if ((index === 0 || index === menuItems.length - 1) && item.type === "separator") {
|
|
return false
|
|
}
|
|
return true
|
|
});
|
|
});
|
|
}
|
|
|
|
replyItem() {
|
|
if (this.threadIds.length !== 1) { return null }
|
|
return {
|
|
label: "Reply",
|
|
click: () => {
|
|
Actions.composeReply({
|
|
threadId: this.threadIds[0],
|
|
popout: true,
|
|
type: 'reply',
|
|
behavior: 'prefer-existing-if-pristine',
|
|
});
|
|
},
|
|
}
|
|
}
|
|
|
|
replyAllItem() {
|
|
if (this.threadIds.length !== 1) { return null }
|
|
DatabaseStore.findBy(Message, {threadId: this.threadIds[0]})
|
|
.order(Message.attributes.date.descending())
|
|
.limit(1)
|
|
.then((message) => {
|
|
if (message && message.canReplyAll()) {
|
|
return {
|
|
label: "Reply All",
|
|
click: () => {
|
|
Actions.composeReply({
|
|
threadId: this.threadIds[0],
|
|
popout: true,
|
|
type: 'reply-all',
|
|
behavior: 'prefer-existing-if-pristine',
|
|
});
|
|
},
|
|
}
|
|
}
|
|
return null
|
|
})
|
|
}
|
|
|
|
forwardItem() {
|
|
if (this.threadIds.length !== 1) { return null }
|
|
return {
|
|
label: "Forward",
|
|
click: () => {
|
|
Actions.composeForward({threadId: this.threadIds[0], popout: true});
|
|
},
|
|
}
|
|
}
|
|
|
|
archiveItem() {
|
|
const perspective = FocusedPerspectiveStore.current()
|
|
const canArchiveThreads = perspective.canArchiveThreads(this.threads)
|
|
if (!canArchiveThreads) {
|
|
return null
|
|
}
|
|
return {
|
|
label: "Archive",
|
|
click: () => {
|
|
const tasks = TaskFactory.tasksForArchiving({
|
|
threads: this.threads,
|
|
})
|
|
Actions.queueTasks(tasks)
|
|
},
|
|
}
|
|
}
|
|
|
|
trashItem() {
|
|
const perspective = FocusedPerspectiveStore.current()
|
|
const canTrashThreads = perspective.canTrashThreads(this.threads)
|
|
if (!canTrashThreads) {
|
|
return null
|
|
}
|
|
return {
|
|
label: "Trash",
|
|
click: () => {
|
|
const tasks = TaskFactory.tasksForMovingToTrash({
|
|
threads: this.threads,
|
|
})
|
|
Actions.queueTasks(tasks)
|
|
},
|
|
}
|
|
}
|
|
|
|
markAsReadItem() {
|
|
const unread = _.every(this.threads, (t) => {
|
|
return _.isMatch(t, {unread: false})
|
|
});
|
|
const dir = unread ? "Unread" : "Read"
|
|
|
|
return {
|
|
label: `Mark as ${dir}`,
|
|
click: () => {
|
|
const task = TaskFactory.taskForInvertingUnread({
|
|
threads: this.threads,
|
|
})
|
|
Actions.queueTask(task)
|
|
},
|
|
}
|
|
}
|
|
|
|
starItem() {
|
|
const starred = _.every(this.threads, (t) => {
|
|
return _.isMatch(t, {starred: false})
|
|
});
|
|
|
|
let dir = ""
|
|
let star = "Star"
|
|
if (!starred) {
|
|
dir = "Remove "
|
|
star = (this.threadIds.length > 1) ? "Stars" : "Star"
|
|
}
|
|
|
|
|
|
return {
|
|
label: `${dir}${star}`,
|
|
click: () => {
|
|
const task = TaskFactory.taskForInvertingStarred({
|
|
threads: this.threads,
|
|
})
|
|
Actions.queueTask(task)
|
|
},
|
|
}
|
|
}
|
|
|
|
displayMenu() {
|
|
const {remote} = require('electron')
|
|
this.menuItemTemplate().then((template) => {
|
|
remote.Menu.buildFromTemplate(template)
|
|
.popup(remote.getCurrentWindow());
|
|
});
|
|
}
|
|
}
|