Extend participant search to return / expand groups

This commit is contained in:
Ben Gotow 2019-10-08 11:00:37 -05:00
parent 3bded91307
commit 66dc60a731
5 changed files with 135 additions and 96 deletions

View file

@ -1,99 +1,107 @@
# Mailspring SDK
* Mailspring SDK Guides
* [Introduction](/README.md)
* [Getting Started](/guides/GettingStarted.md)
* [Getting Started Part 2](/guides/GettingStarted-2.md)
* [Building a Package](/guides/PackageOverview.md)
* [Interface Concepts](/guides/InterfaceConcepts.md)
* [UI Components](/guides/React.md)
* [Application Architecture](/guides/Architecture.md)
* [Debugging Mailspring](/guides/Debugging.md)
* [Accessing the Database](/guides/Database.md)
* [Extending the Composer](/guides/ComposerExtensions.md)
* [Writing Tests](/guides/WritingSpecs.md)
* [Integration Testing](/guides/IntegrationTesting.md)
* [Developing on Windows](/guides/Windows.md)
* [FAQ](/guides/FAQ.md)
- Mailspring SDK Guides
- [Introduction](/README.md)
- [Getting Started](/guides/GettingStarted.md)
- [Getting Started Part 2](/guides/GettingStarted-2.md)
- [Building a Package](/guides/PackageOverview.md)
- [Interface Concepts](/guides/InterfaceConcepts.md)
- [UI Components](/guides/React.md)
- [Application Architecture](/guides/Architecture.md)
- [Debugging Mailspring](/guides/Debugging.md)
- [Accessing the Database](/guides/Database.md)
- [Extending the Composer](/guides/ComposerExtensions.md)
- [Writing Tests](/guides/WritingSpecs.md)
- [Integration Testing](/guides/IntegrationTesting.md)
- [Developing on Windows](/guides/Windows.md)
- [FAQ](/guides/FAQ.md)
----
---
### Full Class Reference
* General
* [Actions](/classes/Actions.md)
* [BufferedProcess](/classes/BufferedProcess.md)
* [ChangeFolderTask](/classes/ChangeFolderTask.md)
* [ChangeLabelsTask](/classes/ChangeLabelsTask.md)
* [ChangeMailTask](/classes/ChangeMailTask.md)
* [Color](/classes/Color.md)
* [Config](/classes/Config.md)
* [Contenteditable](/classes/Contenteditable.md)
* [KeyCommandsRegion](/classes/KeyCommandsRegion.md)
* [AppEnvConstructor](/classes/AppEnvConstructor.md)
* [QueryResultSet](/classes/QueryResultSet.md)
* [QuerySubscriptionPool](/classes/QuerySubscriptionPool.md)
* [StyleManager](/classes/StyleManager.md)
* [Task](/classes/Task.md)
* [TaskQueueStatusStore](/classes/TaskQueueStatusStore.md)
* [ThemeManager](/classes/ThemeManager.md)
- General
* Component Kit
* [EventedIFrame](/classes/EventedIFrame.md)
* [Flexbox](/classes/Flexbox.md)
* [InjectedComponent](/classes/InjectedComponent.md)
* [InjectedComponentSet](/classes/InjectedComponentSet.md)
* [Menu](/classes/Menu.md)
* [MenuItem](/classes/MenuItem.md)
* [MenuNameEmailItem](/classes/MenuNameEmailItem.md)
* [MultiselectActionBar](/classes/MultiselectActionBar.md)
* [MultiselectList](/classes/MultiselectList.md)
* [ResizableRegion](/classes/ResizableRegion.md)
* [RetinaImg](/classes/RetinaImg.md)
* [Spinner](/classes/Spinner.md)
- [Actions](/classes/Actions.md)
- [BufferedProcess](/classes/BufferedProcess.md)
- [ChangeFolderTask](/classes/ChangeFolderTask.md)
- [ChangeLabelsTask](/classes/ChangeLabelsTask.md)
- [ChangeMailTask](/classes/ChangeMailTask.md)
- [Color](/classes/Color.md)
- [Config](/classes/Config.md)
- [Contenteditable](/classes/Contenteditable.md)
- [KeyCommandsRegion](/classes/KeyCommandsRegion.md)
- [AppEnvConstructor](/classes/AppEnvConstructor.md)
- [QueryResultSet](/classes/QueryResultSet.md)
- [QuerySubscriptionPool](/classes/QuerySubscriptionPool.md)
- [StyleManager](/classes/StyleManager.md)
- [Task](/classes/Task.md)
- [TaskQueueStatusStore](/classes/TaskQueueStatusStore.md)
- [ThemeManager](/classes/ThemeManager.md)
* Extensions
* [ComposerExtension](/classes/ComposerExtension.md)
* [ContenteditableExtension](/classes/ContenteditableExtension.md)
* [DraftStoreExtension](/classes/DraftStoreExtension.md)
* [MessageStoreExtension](/classes/MessageStoreExtension.md)
* [MessageViewExtension](/classes/MessageViewExtension.md)
- Component Kit
* Models
* [Contact](/classes/Contact.md)
* [Message](/classes/Message.md)
* [Model](/classes/Model.md)
* [Thread](/classes/Thread.md)
- [EventedIFrame](/classes/EventedIFrame.md)
- [Flexbox](/classes/Flexbox.md)
- [InjectedComponent](/classes/InjectedComponent.md)
- [InjectedComponentSet](/classes/InjectedComponentSet.md)
- [Menu](/classes/Menu.md)
- [MenuItem](/classes/MenuItem.md)
- [MenuNameEmailContent](/classes/MenuNameEmailContent.md)
- [MultiselectActionBar](/classes/MultiselectActionBar.md)
- [MultiselectList](/classes/MultiselectList.md)
- [ResizableRegion](/classes/ResizableRegion.md)
- [RetinaImg](/classes/RetinaImg.md)
- [Spinner](/classes/Spinner.md)
* Stores
* [AccountStore](/classes/AccountStore.md)
* [ComponentRegistry](/classes/ComponentRegistry.md)
* [ContactStore](/classes/ContactStore.md)
* [FocusedContentStore](/classes/FocusedContentStore.md)
* [TaskQueue](/classes/TaskQueue.md)
* [WorkspaceStore](/classes/WorkspaceStore.md)
- Extensions
* Database
* [Attribute](/classes/Attribute.md)
* [AttributeBoolean](/classes/AttributeBoolean.md)
* [AttributeCollection](/classes/AttributeCollection.md)
* [AttributeDateTime](/classes/AttributeDateTime.md)
* [AttributeJoinedData](/classes/AttributeJoinedData.md)
* [AttributeNumber](/classes/AttributeNumber.md)
* [AttributeObject](/classes/AttributeObject.md)
* [AttributeServerId](/classes/AttributeServerId.md)
* [AttributeString](/classes/AttributeString.md)
* [DatabaseStore](/classes/DatabaseStore.md)
* [Matcher](/classes/Matcher.md)
* [ModelQuery](/classes/ModelQuery.md)
* [SortOrder](/classes/SortOrder.md)
- [ComposerExtension](/classes/ComposerExtension.md)
- [ContenteditableExtension](/classes/ContenteditableExtension.md)
- [DraftStoreExtension](/classes/DraftStoreExtension.md)
- [MessageStoreExtension](/classes/MessageStoreExtension.md)
- [MessageViewExtension](/classes/MessageViewExtension.md)
* Drafts
* [DraftChangeSet](/classes/DraftChangeSet.md)
* [DraftEditingSession](/classes/DraftEditingSession.md)
* [DraftStore](/classes/DraftStore.md)
- Models
* AppEnv
* [PackageManager](/classes/PackageManager.md)
- [Contact](/classes/Contact.md)
- [Message](/classes/Message.md)
- [Model](/classes/Model.md)
- [Thread](/classes/Thread.md)
* Atom
- Stores
- [AccountStore](/classes/AccountStore.md)
- [ComponentRegistry](/classes/ComponentRegistry.md)
- [ContactStore](/classes/ContactStore.md)
- [FocusedContentStore](/classes/FocusedContentStore.md)
- [TaskQueue](/classes/TaskQueue.md)
- [WorkspaceStore](/classes/WorkspaceStore.md)
- Database
- [Attribute](/classes/Attribute.md)
- [AttributeBoolean](/classes/AttributeBoolean.md)
- [AttributeCollection](/classes/AttributeCollection.md)
- [AttributeDateTime](/classes/AttributeDateTime.md)
- [AttributeJoinedData](/classes/AttributeJoinedData.md)
- [AttributeNumber](/classes/AttributeNumber.md)
- [AttributeObject](/classes/AttributeObject.md)
- [AttributeServerId](/classes/AttributeServerId.md)
- [AttributeString](/classes/AttributeString.md)
- [DatabaseStore](/classes/DatabaseStore.md)
- [Matcher](/classes/Matcher.md)
- [ModelQuery](/classes/ModelQuery.md)
- [SortOrder](/classes/SortOrder.md)
- Drafts
- [DraftChangeSet](/classes/DraftChangeSet.md)
- [DraftEditingSession](/classes/DraftEditingSession.md)
- [DraftStore](/classes/DraftStore.md)
- AppEnv
- [PackageManager](/classes/PackageManager.md)
- Atom

View file

@ -49,7 +49,7 @@ export default class EventAttendeesInput extends React.Component<EventAttendeesI
};
_completionNode = p => {
return <Menu.NameEmailItem name={p.name} email={p.email} />;
return <Menu.NameEmailContent name={p.name} email={p.email} />;
};
_tokensForString = (string, options = {}) => {

View file

@ -12,7 +12,7 @@ export interface MenuItemProps {
content?: any;
}
export interface MenuNameEmailItemProps {
export interface MenuNameEmailContentProps {
name?: string;
email?: string;
}
@ -82,11 +82,11 @@ Public: React component for a {Menu} item that displays a name and email address
Section: Component Kit
*/
class MenuNameEmailItem extends React.Component<MenuNameEmailItemProps> {
static displayName = 'MenuNameEmailItem';
class MenuNameEmailContent extends React.Component<MenuNameEmailContentProps> {
static displayName = 'MenuNameEmailContent';
/*
Public: React `props` supported by MenuNameEmailItem:
Public: React `props` supported by MenuNameEmailContent:
- `name` (optional) The {String} name to be displayed.
- `email` (optional) The {String} email address to be displayed.
@ -142,7 +142,7 @@ export class Menu extends React.Component<MenuProps, MenuState> {
static displayName = 'Menu';
static Item = MenuItem;
static NameEmailItem = MenuNameEmailItem;
static NameEmailContent = MenuNameEmailContent;
/*
Public: React `props` supported by Menu:

View file

@ -9,6 +9,8 @@ import {
RegExpUtils,
Message,
DraftEditingSession,
ContactGroup,
DatabaseStore,
} from 'mailspring-exports';
import { TokenizingTextField, Menu, InjectedComponentSet } from 'mailspring-component-kit';
@ -98,7 +100,11 @@ export default class ParticipantsTextField extends React.Component<ParticipantsT
_completionNode = p => {
const CustomComponent = p.customComponent;
if (CustomComponent) return <CustomComponent token={p} />;
return <Menu.NameEmailItem name={p.fullName()} email={p.email} key={p.id} />;
if (p instanceof Contact) {
return <Menu.NameEmailContent name={p.fullName()} email={p.email} key={p.id} />;
} else if (p instanceof ContactGroup) {
return p.name;
}
};
_tokensForString = async (string, options = {}) => {
@ -157,10 +163,18 @@ export default class ParticipantsTextField extends React.Component<ParticipantsT
tokensPromise = Promise.resolve(values);
}
tokensPromise.then(tokens => {
tokensPromise.then(async tokens => {
// Safety check: remove anything from the incoming tokens that isn't
// a Contact. We should never receive anything else in the tokens array.
const contactTokens = tokens.filter(value => value instanceof Contact);
const groupTokens = tokens.filter(value => value instanceof ContactGroup);
// convert the group tokens into contact tokens
const contactsFromGroups = await DatabaseStore.findAll<Contact>(Contact, [
Contact.attributes.contactGroups.containsAny(groupTokens.map(g => g.id)),
]);
contactTokens.push(...contactsFromGroups);
const updates = {};
for (const field of Object.keys(this.props.participants)) {
@ -234,10 +248,15 @@ export default class ParticipantsTextField extends React.Component<ParticipantsT
this._textfieldEl = el;
}}
tokens={this.props.participants[this.props.field]}
tokenKey={p => p.email}
tokenKey={p => p.email || p.id}
tokenIsValid={p => ContactStore.isValidContact(p)}
tokenRenderer={TokenRenderer}
onRequestCompletions={input => ContactStore.searchContacts(input)}
onRequestCompletions={async input =>
(await Promise.all([
ContactStore.searchContactGroups(input),
ContactStore.searchContacts(input),
])).flat()
}
shouldBreakOnKeydown={this._shouldBreakOnKeydown}
onInputTrySubmit={this._onInputTrySubmit}
completionNode={this._completionNode}

View file

@ -4,6 +4,7 @@ import RegExpUtils from '../../regexp-utils';
import DatabaseStore from './database-store';
import { AccountStore } from './account-store';
import ComponentRegistry from '../../registries/component-registry';
import { ContactGroup } from 'mailspring-exports';
/**
Public: ContactStore provides convenience methods for searching contacts and
@ -13,6 +14,17 @@ with additional actions.
Section: Stores
*/
class ContactStore extends MailspringStore {
async searchContactGroups(_search: string) {
const search = _search.toLowerCase();
if (!search || search.length === 0) {
return [];
}
const groups = await DatabaseStore.findAll<ContactGroup>(ContactGroup);
return groups.filter(g => g.name.toLowerCase().startsWith(search)).slice(0, 4);
}
// Public: Search the user's contact list for the given search term.
// This method compares the `search` string against each Contact's
// `name` and `email`.
@ -24,7 +36,7 @@ class ContactStore extends MailspringStore {
//
// Returns an {Array} of matching {Contact} models
//
searchContacts(_search, options: { limit?: number } = {}) {
searchContacts(_search: string, options: { limit?: number } = {}) {
const limit = Math.max(options.limit ? options.limit : 5, 0);
const search = _search.toLowerCase();