mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-10-06 19:26:55 +08:00
Build ContactBook concept to track which accounts have sync running
This commit is contained in:
parent
1f6aab1083
commit
96c6a64e46
19 changed files with 267 additions and 159 deletions
|
@ -110,7 +110,7 @@ export function registerMenuItems(accounts: Account[], sidebarAccountIds: string
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const idx = submenu.findIndex(({ type }) => type === 'separator');
|
const idx = submenu.findIndex(({ id }) => id === 'account-shortcuts-separator');
|
||||||
if (!(idx > 0)) {
|
if (!(idx > 0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,11 @@ class AddContactToolbarWithData extends React.Component<AddContactToolbarProps>
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
className={`btn btn-toolbar btn-new-contact ${!enabled && 'btn-disabled'}`}
|
className={`btn btn-toolbar btn-new-contact ${!enabled && 'btn-disabled'}`}
|
||||||
title={localized('New contact in %@', acct ? acct.label : 'account')}
|
title={
|
||||||
|
acct
|
||||||
|
? localized('New contact in %@', acct.label)
|
||||||
|
: localized('Select an account to add a contact.')
|
||||||
|
}
|
||||||
onClick={enabled ? this.onAdd : undefined}
|
onClick={enabled ? this.onAdd : undefined}
|
||||||
>
|
>
|
||||||
<Icons.NewPerson />
|
<Icons.NewPerson />
|
||||||
|
|
|
@ -86,6 +86,7 @@ class ContactDetailToolbarWithData extends React.Component<ContactDetailToolbarP
|
||||||
{perspective && perspective.type === 'group' && (
|
{perspective && perspective.type === 'group' && (
|
||||||
<button
|
<button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
title={localized('Remove from Group')}
|
||||||
className={`btn btn-toolbar ${actionSet.length === 0 && 'btn-disabled'}`}
|
className={`btn btn-toolbar ${actionSet.length === 0 && 'btn-disabled'}`}
|
||||||
onClick={actionSet.length > 0 ? this._onRemoveFromSource : undefined}
|
onClick={actionSet.length > 0 ? this._onRemoveFromSource : undefined}
|
||||||
>
|
>
|
||||||
|
@ -94,14 +95,15 @@ class ContactDetailToolbarWithData extends React.Component<ContactDetailToolbarP
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className={`btn btn-toolbar ${actionSet.length === 0 && 'btn-disabled'}`}
|
|
||||||
title={localized('Delete')}
|
title={localized('Delete')}
|
||||||
|
className={`btn btn-toolbar ${actionSet.length === 0 && 'btn-disabled'}`}
|
||||||
onClick={actionSet.length > 0 ? this._onDelete : undefined}
|
onClick={actionSet.length > 0 ? this._onDelete : undefined}
|
||||||
>
|
>
|
||||||
<RetinaImg name="toolbar-trash.png" mode={RetinaImg.Mode.ContentIsMask} />
|
<RetinaImg name="toolbar-trash.png" mode={RetinaImg.Mode.ContentIsMask} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
|
title={localized('Edit')}
|
||||||
className={`btn btn-toolbar ${!editable && 'btn-disabled'}`}
|
className={`btn btn-toolbar ${!editable && 'btn-disabled'}`}
|
||||||
onClick={editable ? () => Store.setEditing(actionSet[0].id) : undefined}
|
onClick={editable ? () => Store.setEditing(actionSet[0].id) : undefined}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,11 +3,13 @@ import {
|
||||||
Account,
|
Account,
|
||||||
AccountStore,
|
AccountStore,
|
||||||
ContactGroup,
|
ContactGroup,
|
||||||
|
ContactBook,
|
||||||
Rx,
|
Rx,
|
||||||
Actions,
|
Actions,
|
||||||
DestroyContactGroupTask,
|
DestroyContactGroupTask,
|
||||||
SyncbackContactGroupTask,
|
SyncbackContactGroupTask,
|
||||||
ChangeContactGroupMembershipTask,
|
ChangeContactGroupMembershipTask,
|
||||||
|
localized,
|
||||||
} from 'mailspring-exports';
|
} from 'mailspring-exports';
|
||||||
import { ContactsPerspective, Store } from './Store';
|
import { ContactsPerspective, Store } from './Store';
|
||||||
import {
|
import {
|
||||||
|
@ -16,12 +18,14 @@ import {
|
||||||
OutlineViewItem,
|
OutlineViewItem,
|
||||||
ListensToFluxStore,
|
ListensToFluxStore,
|
||||||
ListensToObservable,
|
ListensToObservable,
|
||||||
|
IOutlineViewItem,
|
||||||
} from 'mailspring-component-kit';
|
} from 'mailspring-component-kit';
|
||||||
import { isEqual } from 'underscore';
|
import { isEqual } from 'underscore';
|
||||||
|
|
||||||
interface ContactsPerspectivesProps {
|
interface ContactsPerspectivesProps {
|
||||||
accounts: Account[];
|
accounts: Account[];
|
||||||
groups: ContactGroup[];
|
groups: ContactGroup[];
|
||||||
|
books: ContactBook[];
|
||||||
findInMailDisabled: string[];
|
findInMailDisabled: string[];
|
||||||
selected: ContactsPerspective | null;
|
selected: ContactsPerspective | null;
|
||||||
onSelect: (item: ContactsPerspective | null) => void;
|
onSelect: (item: ContactsPerspective | null) => void;
|
||||||
|
@ -36,9 +40,115 @@ function perspectiveForGroup(g: ContactGroup): ContactsPerspective {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OutlineViewForAccountProps {
|
||||||
|
account: Account;
|
||||||
|
groups: ContactGroup[];
|
||||||
|
books: ContactBook[];
|
||||||
|
findInMailDisabled: boolean;
|
||||||
|
selected: ContactsPerspective | null;
|
||||||
|
onSelect: (item: ContactsPerspective | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutlineViewForAccount = ({
|
||||||
|
account,
|
||||||
|
groups,
|
||||||
|
books,
|
||||||
|
selected,
|
||||||
|
onSelect,
|
||||||
|
findInMailDisabled,
|
||||||
|
}: OutlineViewForAccountProps) => {
|
||||||
|
const items: IOutlineViewItem[] = [];
|
||||||
|
|
||||||
|
if (books.length) {
|
||||||
|
items.push({
|
||||||
|
id: 'all-contacts',
|
||||||
|
name: localized('All Contacts'),
|
||||||
|
iconName: 'person.png',
|
||||||
|
children: [],
|
||||||
|
selected: selected && selected.type === 'all',
|
||||||
|
onSelect: () =>
|
||||||
|
onSelect({ accountId: account.id, type: 'all', label: localized('All Contacts') }),
|
||||||
|
shouldAcceptDrop: () => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const group of groups) {
|
||||||
|
const perspective = perspectiveForGroup(group);
|
||||||
|
items.push({
|
||||||
|
id: `${perspective.accountId}-${perspective.label}`,
|
||||||
|
name: perspective.label,
|
||||||
|
iconName: 'label.png',
|
||||||
|
children: [],
|
||||||
|
selected: isEqual(selected, perspective),
|
||||||
|
onSelect: () => onSelect(perspective),
|
||||||
|
onEdited: (item, value: string) => {
|
||||||
|
Actions.queueTask(SyncbackContactGroupTask.forRenaming(group, value));
|
||||||
|
},
|
||||||
|
onDelete: () => {
|
||||||
|
Actions.queueTask(DestroyContactGroupTask.forRemoving(group));
|
||||||
|
},
|
||||||
|
onDrop: (item, { dataTransfer }) => {
|
||||||
|
const data = JSON.parse(dataTransfer.getData('mailspring-contacts-data'));
|
||||||
|
const contacts = data.ids.map(i => Store.filteredContacts().find(c => c.id === i));
|
||||||
|
Actions.queueTask(
|
||||||
|
ChangeContactGroupMembershipTask.forMoving({
|
||||||
|
direction: 'add',
|
||||||
|
contacts,
|
||||||
|
group,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
shouldAcceptDrop: (item, { dataTransfer }) => {
|
||||||
|
if (!dataTransfer.types.includes('mailspring-contacts-data')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isEqual(selected, perspective)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't inspect the drag payload until drop, so we use a dataTransfer
|
||||||
|
// type to encode the account IDs of threads currently being dragged.
|
||||||
|
const accountsType = dataTransfer.types.find(t => t.startsWith('mailspring-accounts='));
|
||||||
|
const accountIds = (accountsType || '').replace('mailspring-accounts=', '').split(',');
|
||||||
|
|
||||||
|
return isEqual(accountIds, [perspective.accountId]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
id: 'found-in-mail',
|
||||||
|
name: localized('Found in Mail'),
|
||||||
|
iconName: 'inbox.png',
|
||||||
|
children: [],
|
||||||
|
className: findInMailDisabled ? 'found-in-mail-disabled' : '',
|
||||||
|
selected: selected && selected.type === 'found-in-mail',
|
||||||
|
shouldAcceptDrop: () => false,
|
||||||
|
onSelect: () =>
|
||||||
|
onSelect({
|
||||||
|
accountId: account.id,
|
||||||
|
type: 'found-in-mail',
|
||||||
|
label: `${localized('Found in Mail')} (${account.label})`,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OutlineView
|
||||||
|
title={account.label}
|
||||||
|
items={items}
|
||||||
|
onItemCreated={
|
||||||
|
books.length > 0
|
||||||
|
? name => Actions.queueTask(SyncbackContactGroupTask.forCreating(account.id, name))
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const ContactsPerspectivesWithData: React.FunctionComponent<ContactsPerspectivesProps> = ({
|
const ContactsPerspectivesWithData: React.FunctionComponent<ContactsPerspectivesProps> = ({
|
||||||
findInMailDisabled,
|
findInMailDisabled,
|
||||||
groups,
|
groups,
|
||||||
|
books,
|
||||||
accounts,
|
accounts,
|
||||||
selected,
|
selected,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
@ -57,87 +167,14 @@ const ContactsPerspectivesWithData: React.FunctionComponent<ContactsPerspectives
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
{accounts.map(a => (
|
{accounts.map(a => (
|
||||||
<OutlineView
|
<OutlineViewForAccount
|
||||||
key={a.id}
|
key={a.id}
|
||||||
title={a.label}
|
account={a}
|
||||||
onItemCreated={name => Actions.queueTask(SyncbackContactGroupTask.forCreating(a.id, name))}
|
findInMailDisabled={findInMailDisabled.includes(a.id)}
|
||||||
items={[
|
books={books.filter(b => b.accountId === a.id)}
|
||||||
{
|
groups={groups.filter(b => b.accountId === a.id)}
|
||||||
id: 'all-contacts',
|
selected={selected && selected.accountId === a.id ? selected : null}
|
||||||
name: 'All Contacts',
|
onSelect={onSelect}
|
||||||
iconName: 'person.png',
|
|
||||||
children: [],
|
|
||||||
selected: selected && selected.accountId == a.id && selected.type === 'all',
|
|
||||||
onSelect: () => onSelect({ accountId: a.id, type: 'all', label: 'All Contacts' }),
|
|
||||||
shouldAcceptDrop: () => false,
|
|
||||||
},
|
|
||||||
...groups
|
|
||||||
.filter(g => g.accountId === a.id)
|
|
||||||
.map(group => {
|
|
||||||
const perspective = perspectiveForGroup(group);
|
|
||||||
return {
|
|
||||||
id: `${perspective.accountId}-${perspective.label}`,
|
|
||||||
name: perspective.label,
|
|
||||||
iconName: 'label.png',
|
|
||||||
children: [],
|
|
||||||
selected: isEqual(selected, perspective),
|
|
||||||
onSelect: () => onSelect(perspective),
|
|
||||||
onEdited: (item, value: string) => {
|
|
||||||
Actions.queueTask(SyncbackContactGroupTask.forRenaming(group, value));
|
|
||||||
},
|
|
||||||
onDelete: () => {
|
|
||||||
Actions.queueTask(DestroyContactGroupTask.forRemoving(group));
|
|
||||||
},
|
|
||||||
onDrop: (item, { dataTransfer }) => {
|
|
||||||
const data = JSON.parse(dataTransfer.getData('mailspring-contacts-data'));
|
|
||||||
const contacts = data.ids.map(i =>
|
|
||||||
Store.filteredContacts().find(c => c.id === i)
|
|
||||||
);
|
|
||||||
Actions.queueTask(
|
|
||||||
ChangeContactGroupMembershipTask.forMoving({
|
|
||||||
direction: 'add',
|
|
||||||
contacts,
|
|
||||||
group,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
shouldAcceptDrop: (item, { dataTransfer }) => {
|
|
||||||
if (!dataTransfer.types.includes('mailspring-contacts-data')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isEqual(selected, perspective)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't inspect the drag payload until drop, so we use a dataTransfer
|
|
||||||
// type to encode the account IDs of threads currently being dragged.
|
|
||||||
const accountsType = dataTransfer.types.find(t =>
|
|
||||||
t.startsWith('mailspring-accounts=')
|
|
||||||
);
|
|
||||||
const accountIds = (accountsType || '')
|
|
||||||
.replace('mailspring-accounts=', '')
|
|
||||||
.split(',');
|
|
||||||
|
|
||||||
return isEqual(accountIds, [perspective.accountId]);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
id: 'found-in-mail',
|
|
||||||
name: 'Found in Mail',
|
|
||||||
iconName: 'inbox.png',
|
|
||||||
children: [],
|
|
||||||
className: findInMailDisabled.includes(a.id) ? 'found-in-mail-disabled' : '',
|
|
||||||
selected: selected && selected.accountId == a.id && selected.type === 'found-in-mail',
|
|
||||||
shouldAcceptDrop: () => false,
|
|
||||||
onSelect: () =>
|
|
||||||
onSelect({
|
|
||||||
accountId: a.id,
|
|
||||||
type: 'found-in-mail',
|
|
||||||
label: `Found in Mail (${a.label})`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ScrollRegion>
|
</ScrollRegion>
|
||||||
|
@ -148,6 +185,7 @@ export const ContactPerspectivesList = ListensToObservable(
|
||||||
stores: [AccountStore, Store],
|
stores: [AccountStore, Store],
|
||||||
getStateFromStores: () => ({
|
getStateFromStores: () => ({
|
||||||
accounts: AccountStore.accounts(),
|
accounts: AccountStore.accounts(),
|
||||||
|
books: Store.books(),
|
||||||
groups: Store.groups(),
|
groups: Store.groups(),
|
||||||
selected: Store.perspective(),
|
selected: Store.perspective(),
|
||||||
onSelect: s => Store.setPerspective(s),
|
onSelect: s => Store.setPerspective(s),
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import Rx from 'rx-lite';
|
import Rx from 'rx-lite';
|
||||||
import { DatabaseStore, Contact, ContactGroup, MutableQuerySubscription } from 'mailspring-exports';
|
import {
|
||||||
|
DatabaseStore,
|
||||||
|
Contact,
|
||||||
|
ContactGroup,
|
||||||
|
ContactBook,
|
||||||
|
MutableQuerySubscription,
|
||||||
|
} from 'mailspring-exports';
|
||||||
import MailspringStore from 'mailspring-store';
|
import MailspringStore from 'mailspring-store';
|
||||||
import { ListTabular } from 'mailspring-component-kit';
|
import { ListTabular } from 'mailspring-component-kit';
|
||||||
|
|
||||||
|
@ -10,6 +16,7 @@ class ContactsWindowStore extends MailspringStore {
|
||||||
_contacts: Contact[] = [];
|
_contacts: Contact[] = [];
|
||||||
_contactsSubscription: MutableQuerySubscription<Contact>;
|
_contactsSubscription: MutableQuerySubscription<Contact>;
|
||||||
_groups: ContactGroup[] = [];
|
_groups: ContactGroup[] = [];
|
||||||
|
_books: ContactBook[] = [];
|
||||||
_search: string = '';
|
_search: string = '';
|
||||||
_filtered: Contact[] | null = null;
|
_filtered: Contact[] | null = null;
|
||||||
_editing: string | 'new' | false = false;
|
_editing: string | 'new' | false = false;
|
||||||
|
@ -24,6 +31,7 @@ class ContactsWindowStore extends MailspringStore {
|
||||||
.where(Contact.attributes.refs.greaterThan(0))
|
.where(Contact.attributes.refs.greaterThan(0))
|
||||||
.where(Contact.attributes.hidden.equal(false));
|
.where(Contact.attributes.hidden.equal(false));
|
||||||
this._contactsSubscription = new MutableQuerySubscription<Contact>(contacts);
|
this._contactsSubscription = new MutableQuerySubscription<Contact>(contacts);
|
||||||
|
|
||||||
Rx.Observable.fromNamedQuerySubscription('contacts', this._contactsSubscription).subscribe(
|
Rx.Observable.fromNamedQuerySubscription('contacts', this._contactsSubscription).subscribe(
|
||||||
contacts => {
|
contacts => {
|
||||||
this._contacts = contacts as Contact[];
|
this._contacts = contacts as Contact[];
|
||||||
|
@ -32,14 +40,24 @@ class ContactsWindowStore extends MailspringStore {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const groups = Rx.Observable.fromQuery(DatabaseStore.findAll<ContactGroup>(ContactGroup));
|
Rx.Observable.fromQuery(DatabaseStore.findAll<ContactGroup>(ContactGroup)).subscribe(
|
||||||
groups.subscribe(groups => {
|
groups => {
|
||||||
this._groups = groups;
|
this._groups = groups;
|
||||||
|
this.trigger();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Rx.Observable.fromQuery(DatabaseStore.findAll<ContactBook>(ContactBook)).subscribe(books => {
|
||||||
|
this._books = books;
|
||||||
this.trigger();
|
this.trigger();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
books() {
|
||||||
|
return this._books;
|
||||||
|
}
|
||||||
|
|
||||||
groups() {
|
groups() {
|
||||||
return this._groups;
|
return this._groups;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { FoundInMailEnabledBar } from './FoundInMailEnabledBar';
|
||||||
|
|
||||||
function adjustMenus() {
|
function adjustMenus() {
|
||||||
const contactMenu: typeof AppEnv.menu.template[0] = {
|
const contactMenu: typeof AppEnv.menu.template[0] = {
|
||||||
key: 'Contact',
|
id: 'Contact',
|
||||||
label: localized('Contact'),
|
label: localized('Contact'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -32,10 +32,8 @@ function adjustMenus() {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const template = AppEnv.menu.template.filter(
|
const template = AppEnv.menu.template.filter(item => item.id !== 'Thread' && item.id !== 'View');
|
||||||
item => item.key !== 'Thread' && item.key !== 'View'
|
const editIndex = template.findIndex(item => item.id === 'Edit');
|
||||||
);
|
|
||||||
const editIndex = template.findIndex(item => item.key === 'Edit');
|
|
||||||
template.splice(editIndex + 1, 0, contactMenu);
|
template.splice(editIndex + 1, 0, contactMenu);
|
||||||
|
|
||||||
AppEnv.menu.template = template;
|
AppEnv.menu.template = template;
|
||||||
|
|
|
@ -44,7 +44,8 @@
|
||||||
"window:select-account-8": "mod+9",
|
"window:select-account-8": "mod+9",
|
||||||
|
|
||||||
"window:sync-mail-now": "f5",
|
"window:sync-mail-now": "f5",
|
||||||
|
"application:show-main-window": "mod+0",
|
||||||
|
|
||||||
"contenteditable:underline": "mod+u",
|
"contenteditable:underline": "mod+u",
|
||||||
"contenteditable:bold": "mod+b",
|
"contenteditable:bold": "mod+b",
|
||||||
"contenteditable:italic": "mod+i",
|
"contenteditable:italic": "mod+i",
|
||||||
|
|
|
@ -3,7 +3,7 @@ const { localized } = require('../src/intl');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
key: 'Mailspring',
|
id: 'Mailspring',
|
||||||
label: 'Mailspring',
|
label: 'Mailspring',
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('About Mailspring'), command: 'application:about' },
|
{ label: localized('About Mailspring'), command: 'application:about' },
|
||||||
|
@ -42,7 +42,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'File',
|
id: 'File',
|
||||||
label: localized('File'),
|
label: localized('File'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Sync New Mail Now'), command: 'window:sync-mail-now' },
|
{ label: localized('Sync New Mail Now'), command: 'window:sync-mail-now' },
|
||||||
|
@ -56,7 +56,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Edit',
|
id: 'Edit',
|
||||||
label: localized('Edit'),
|
label: localized('Edit'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Undo'), command: 'core:undo' },
|
{ label: localized('Undo'), command: 'core:undo' },
|
||||||
|
@ -86,7 +86,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'View',
|
id: 'View',
|
||||||
label: localized('View'),
|
label: localized('View'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -140,7 +140,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Thread',
|
id: 'Thread',
|
||||||
label: localized('Thread'),
|
label: localized('Thread'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Reply'), command: 'core:reply' },
|
{ label: localized('Reply'), command: 'core:reply' },
|
||||||
|
@ -192,7 +192,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Developer',
|
id: 'Developer',
|
||||||
label: localized('Developer'),
|
label: localized('Developer'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -202,7 +202,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
||||||
{ label: localized('Contacts Preview'), command: 'application:show-contacts' },
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
||||||
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
||||||
|
@ -221,13 +220,22 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Window',
|
id: 'Window',
|
||||||
label: localized('Window'),
|
label: localized('Window'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Minimize'), command: 'application:minimize' },
|
{ label: localized('Minimize'), command: 'application:minimize' },
|
||||||
{ label: localized('Zoom'), command: 'application:zoom' },
|
{ label: localized('Zoom'), command: 'application:zoom' },
|
||||||
{ type: 'separator', id: 'window-list-separator' },
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: localized('Message Viewer'),
|
||||||
|
command: 'application:show-main-window',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: localized('Contacts'),
|
||||||
|
command: 'application:show-contacts',
|
||||||
|
},
|
||||||
|
{ type: 'separator', id: 'window-list-separator' },
|
||||||
|
{ type: 'separator', id: 'account-shortcuts-separator' },
|
||||||
{
|
{
|
||||||
label: localized('Bring All to Front'),
|
label: localized('Bring All to Front'),
|
||||||
command: 'application:bring-all-windows-to-front',
|
command: 'application:bring-all-windows-to-front',
|
||||||
|
@ -236,7 +244,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Help',
|
id: 'Help',
|
||||||
label: localized('Help'),
|
label: localized('Help'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Mailspring Help'), command: 'application:view-help' },
|
{ label: localized('Mailspring Help'), command: 'application:view-help' },
|
||||||
|
|
|
@ -3,7 +3,7 @@ const { localized } = require('../src/intl');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
key: 'File',
|
id: 'File',
|
||||||
label: localized('File'),
|
label: localized('File'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Sync New Mail Now'), command: 'window:sync-mail-now' },
|
{ label: localized('Sync New Mail Now'), command: 'window:sync-mail-now' },
|
||||||
|
@ -24,7 +24,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Edit',
|
id: 'Edit',
|
||||||
label: localized('Edit'),
|
label: localized('Edit'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Undo'), command: 'core:undo' },
|
{ label: localized('Undo'), command: 'core:undo' },
|
||||||
|
@ -58,7 +58,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'View',
|
id: 'View',
|
||||||
label: localized('View'),
|
label: localized('View'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -110,7 +110,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Thread',
|
id: 'Thread',
|
||||||
label: localized('Thread'),
|
label: localized('Thread'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Reply'), command: 'core:reply' },
|
{ label: localized('Reply'), command: 'core:reply' },
|
||||||
|
@ -161,7 +161,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Developer',
|
id: 'Developer',
|
||||||
label: localized('Developer'),
|
label: localized('Developer'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -171,7 +171,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
||||||
{ label: localized('Contacts Preview'), command: 'application:show-contacts' },
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
||||||
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
||||||
|
@ -189,16 +188,27 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Window',
|
id: 'Window',
|
||||||
label: localized('Window'),
|
label: localized('Window'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Minimize'), command: 'application:minimize' },
|
{ label: localized('Minimize'), command: 'application:minimize' },
|
||||||
{ label: localized('Zoom'), command: 'application:zoom' },
|
{ label: localized('Zoom'), command: 'application:zoom' },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: localized('Message Viewer'),
|
||||||
|
command: 'application:show-main-window',
|
||||||
|
accelerator: 'CmdOrCtrl+0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: localized('Contacts'),
|
||||||
|
command: 'application:show-contacts',
|
||||||
|
},
|
||||||
{ type: 'separator', id: 'window-list-separator' },
|
{ type: 'separator', id: 'window-list-separator' },
|
||||||
|
{ type: 'separator', id: 'account-shortcuts-separator' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Help',
|
id: 'Help',
|
||||||
label: localized('Help'),
|
label: localized('Help'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: 'VERSION', enabled: false },
|
{ label: 'VERSION', enabled: false },
|
||||||
|
|
|
@ -3,7 +3,7 @@ const { localized } = require('../src/intl');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
menu: [
|
menu: [
|
||||||
{
|
{
|
||||||
key: 'Edit',
|
id: 'Edit',
|
||||||
label: localized('Edit'),
|
label: localized('Edit'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Undo'), command: 'core:undo' },
|
{ label: localized('Undo'), command: 'core:undo' },
|
||||||
|
@ -33,7 +33,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'View',
|
id: 'View',
|
||||||
label: localized('View'),
|
label: localized('View'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -87,7 +87,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Thread',
|
id: 'Thread',
|
||||||
label: localized('Thread'),
|
label: localized('Thread'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Reply'), command: 'core:reply' },
|
{ label: localized('Reply'), command: 'core:reply' },
|
||||||
|
@ -138,7 +138,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Developer',
|
id: 'Developer',
|
||||||
label: localized('Developer'),
|
label: localized('Developer'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
|
@ -148,7 +148,6 @@ module.exports = {
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
{ label: localized('Calendar Preview'), command: 'application:show-calendar' },
|
||||||
{ label: localized('Contacts Preview'), command: 'application:show-contacts' },
|
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
{ label: localized('Create a Plugin') + '...', command: 'window:create-package' },
|
||||||
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
{ label: localized('Install a Plugin') + '...', command: 'window:install-package' },
|
||||||
|
@ -167,12 +166,23 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'Window',
|
id: 'Window',
|
||||||
label: localized('Window'),
|
label: localized('Window'),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: localized('Minimize'), command: 'application:minimize' },
|
{ label: localized('Minimize'), command: 'application:minimize' },
|
||||||
{ label: localized('Zoom'), command: 'application:zoom' },
|
{ label: localized('Zoom'), command: 'application:zoom' },
|
||||||
|
{ type: 'separator' },
|
||||||
|
{
|
||||||
|
label: localized('Message Viewer'),
|
||||||
|
command: 'application:show-main-window',
|
||||||
|
accelerator: 'CmdOrCtrl+0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: localized('Contacts'),
|
||||||
|
command: 'application:show-contacts',
|
||||||
|
},
|
||||||
{ type: 'separator', id: 'window-list-separator' },
|
{ type: 'separator', id: 'window-list-separator' },
|
||||||
|
{ type: 'separator', id: 'account-shortcuts-separator' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
|
|
|
@ -154,24 +154,17 @@ export default class ApplicationMenu {
|
||||||
}
|
}
|
||||||
const idx = windowMenu.submenu.findIndex(({ id }) => id === 'window-list-separator');
|
const idx = windowMenu.submenu.findIndex(({ id }) => id === 'window-list-separator');
|
||||||
|
|
||||||
let workShortcut = 'CmdOrCtrl+alt+w';
|
|
||||||
if (process.platform === 'win32') {
|
|
||||||
workShortcut = 'ctrl+shift+w';
|
|
||||||
}
|
|
||||||
|
|
||||||
const accelerators = {
|
|
||||||
default: 'CmdOrCtrl+0',
|
|
||||||
work: workShortcut,
|
|
||||||
};
|
|
||||||
const windows = global.application.windowManager.getOpenWindows();
|
const windows = global.application.windowManager.getOpenWindows();
|
||||||
const windowsItems = windows.map(w => ({
|
const windowsItems = windows
|
||||||
label: w.loadSettings().title || 'Window',
|
.filter(w => w.windowType !== 'default' && w.windowType !== 'contacts')
|
||||||
accelerator: accelerators[w.windowType],
|
.map(w => ({
|
||||||
click() {
|
label: w.loadSettings().title || 'Window',
|
||||||
w.show();
|
click() {
|
||||||
w.focus();
|
w.show();
|
||||||
},
|
w.focus();
|
||||||
}));
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
return windowMenu.submenu.splice(idx, 0, { type: 'separator' }, ...windowsItems);
|
return windowMenu.submenu.splice(idx, 0, { type: 'separator' }, ...windowsItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,7 @@ interface OutlineViewState {
|
||||||
* @param {props.onCollapseToggled} props.onCollapseToggled
|
* @param {props.onCollapseToggled} props.onCollapseToggled
|
||||||
* @class OutlineView
|
* @class OutlineView
|
||||||
*/
|
*/
|
||||||
class OutlineView extends Component<OutlineViewProps, OutlineViewState> {
|
export class OutlineView extends Component<OutlineViewProps, OutlineViewState> {
|
||||||
static displayName = 'OutlineView';
|
static displayName = 'OutlineView';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -232,5 +232,3 @@ class OutlineView extends Component<OutlineViewProps, OutlineViewState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default OutlineView;
|
|
||||||
|
|
32
app/src/flux/models/contact-book.ts
Normal file
32
app/src/flux/models/contact-book.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/* eslint global-require: 0 */
|
||||||
|
import { Model, AttributeValues } from './model';
|
||||||
|
import Attributes from '../attributes';
|
||||||
|
|
||||||
|
export class ContactBook extends Model {
|
||||||
|
static attributes = {
|
||||||
|
...Model.attributes,
|
||||||
|
|
||||||
|
readonly: Attributes.String({
|
||||||
|
modelKey: 'readonly',
|
||||||
|
}),
|
||||||
|
|
||||||
|
source: Attributes.String({
|
||||||
|
modelKey: 'source',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
readonly: boolean;
|
||||||
|
source: 'carddav' | 'gpeople';
|
||||||
|
|
||||||
|
static sortOrderAttribute = () => {
|
||||||
|
return ContactBook.attributes.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static naturalSortOrder = () => {
|
||||||
|
return ContactBook.sortOrderAttribute().ascending();
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(data: AttributeValues<typeof ContactBook.attributes>) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint global-require: 0 */
|
/* eslint global-require: 0 */
|
||||||
import _str from 'underscore.string';
|
|
||||||
import { Model, AttributeValues } from './model';
|
import { Model, AttributeValues } from './model';
|
||||||
import Attributes from '../attributes';
|
import Attributes from '../attributes';
|
||||||
|
|
||||||
|
@ -12,6 +11,8 @@ export class ContactGroup extends Model {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public name: string;
|
||||||
|
|
||||||
static sortOrderAttribute = () => {
|
static sortOrderAttribute = () => {
|
||||||
return ContactGroup.attributes.name;
|
return ContactGroup.attributes.name;
|
||||||
};
|
};
|
||||||
|
@ -20,8 +21,6 @@ export class ContactGroup extends Model {
|
||||||
return ContactGroup.sortOrderAttribute().ascending();
|
return ContactGroup.sortOrderAttribute().ascending();
|
||||||
};
|
};
|
||||||
|
|
||||||
public name: string;
|
|
||||||
|
|
||||||
constructor(data: AttributeValues<typeof ContactGroup.attributes>) {
|
constructor(data: AttributeValues<typeof ContactGroup.attributes>) {
|
||||||
super(data);
|
super(data);
|
||||||
}
|
}
|
||||||
|
|
2
app/src/global/mailspring-component-kit.d.ts
vendored
2
app/src/global/mailspring-component-kit.d.ts
vendored
|
@ -40,7 +40,7 @@ export * from '../components/disclosure-triangle';
|
||||||
export const EditableList: typeof import('../components/editable-list').default;
|
export const EditableList: typeof import('../components/editable-list').default;
|
||||||
export const DropdownMenu: typeof import('../components/dropdown-menu').default;
|
export const DropdownMenu: typeof import('../components/dropdown-menu').default;
|
||||||
export const OutlineViewItem: typeof import('../components/outline-view-item').default;
|
export const OutlineViewItem: typeof import('../components/outline-view-item').default;
|
||||||
export const OutlineView: typeof import('../components/outline-view').default;
|
export * from '../components/outline-view';
|
||||||
export const DateInput: typeof import('../components/date-input').default;
|
export const DateInput: typeof import('../components/date-input').default;
|
||||||
export const DatePicker: typeof import('../components/date-picker').default;
|
export const DatePicker: typeof import('../components/date-picker').default;
|
||||||
export const TimePicker: typeof import('../components/time-picker').default;
|
export const TimePicker: typeof import('../components/time-picker').default;
|
||||||
|
|
3
app/src/global/mailspring-exports.d.ts
vendored
3
app/src/global/mailspring-exports.d.ts
vendored
|
@ -44,6 +44,7 @@ export * from '../flux/models/thread';
|
||||||
export * from '../flux/models/account';
|
export * from '../flux/models/account';
|
||||||
export * from '../flux/models/message';
|
export * from '../flux/models/message';
|
||||||
export * from '../flux/models/contact';
|
export * from '../flux/models/contact';
|
||||||
|
export * from '../flux/models/contact-book';
|
||||||
export * from '../flux/models/contact-group';
|
export * from '../flux/models/contact-group';
|
||||||
export * from '../flux/models/category';
|
export * from '../flux/models/category';
|
||||||
export * from '../flux/models/calendar';
|
export * from '../flux/models/calendar';
|
||||||
|
@ -80,6 +81,8 @@ export * from '../flux/tasks/change-role-mapping-task';
|
||||||
export * from '../flux/tasks/send-feature-usage-event-task';
|
export * from '../flux/tasks/send-feature-usage-event-task';
|
||||||
export * from '../flux/tasks/syncback-contact-task';
|
export * from '../flux/tasks/syncback-contact-task';
|
||||||
export * from '../flux/tasks/destroy-contact-task';
|
export * from '../flux/tasks/destroy-contact-task';
|
||||||
|
export * from '../flux/tasks/destroy-contactgroup-task';
|
||||||
|
export * from '../flux/tasks/syncback-contactgroup-task';
|
||||||
export * from '../flux/tasks/change-contactgroup-membership-task';
|
export * from '../flux/tasks/change-contactgroup-membership-task';
|
||||||
|
|
||||||
// Stores
|
// Stores
|
||||||
|
|
|
@ -81,6 +81,7 @@ lazyLoadAndRegisterModel(`Thread`, 'thread');
|
||||||
lazyLoadAndRegisterModel(`Account`, 'account');
|
lazyLoadAndRegisterModel(`Account`, 'account');
|
||||||
lazyLoadAndRegisterModel(`Message`, 'message');
|
lazyLoadAndRegisterModel(`Message`, 'message');
|
||||||
lazyLoadAndRegisterModel(`Contact`, 'contact');
|
lazyLoadAndRegisterModel(`Contact`, 'contact');
|
||||||
|
lazyLoadAndRegisterModel(`ContactBook`, 'contact-book');
|
||||||
lazyLoadAndRegisterModel(`ContactGroup`, 'contact-group');
|
lazyLoadAndRegisterModel(`ContactGroup`, 'contact-group');
|
||||||
lazyLoadAndRegisterModel(`Category`, 'category');
|
lazyLoadAndRegisterModel(`Category`, 'category');
|
||||||
lazyLoadAndRegisterModel(`Calendar`, 'calendar');
|
lazyLoadAndRegisterModel(`Calendar`, 'calendar');
|
||||||
|
|
|
@ -8,24 +8,17 @@ import _ from 'underscore';
|
||||||
|
|
||||||
const ItemSpecificities = new WeakMap();
|
const ItemSpecificities = new WeakMap();
|
||||||
|
|
||||||
export type IMenuItem =
|
export type IMenuItem = {
|
||||||
| {
|
label?: string;
|
||||||
label: string;
|
submenu?: IMenuItem[];
|
||||||
submenu?: IMenuItem[];
|
type?: 'separator';
|
||||||
type?: string;
|
|
||||||
|
|
||||||
key?: string; //unlocalized label
|
id?: string; //unlocalized label
|
||||||
command?: string;
|
command?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
hideWhenDisabled?: boolean;
|
hideWhenDisabled?: boolean;
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
key?: string; //unlocalized label
|
|
||||||
label?: string;
|
|
||||||
submenu?: IMenuItem[];
|
|
||||||
type: 'separator';
|
|
||||||
};
|
|
||||||
|
|
||||||
export function merge(menu: IMenuItem[], item: IMenuItem, itemSpecificity?: number) {
|
export function merge(menu: IMenuItem[], item: IMenuItem, itemSpecificity?: number) {
|
||||||
let matchingItem;
|
let matchingItem;
|
||||||
|
|
2
mailsync
2
mailsync
|
@ -1 +1 @@
|
||||||
Subproject commit e78951655f7bc194029c709549c0f5c1a29e9c85
|
Subproject commit 8c3e2ad5ba07b19fd96fe18cbe589b8c05c72e8c
|
Loading…
Add table
Reference in a new issue