Remove Category concept, part 1

This commit is contained in:
Ben Gotow 2017-06-21 15:16:48 -07:00
parent 1219182b0a
commit 73f82ec079
5 changed files with 422 additions and 69 deletions

View file

@ -1,2 +1,188 @@
import Category from './category';
export default Category;
/* eslint global-require: 0 */
import {FolderSyncProgressStore} from 'nylas-exports';
import Model from './model';
import Attributes from '../attributes';
let AccountStore = null
// We look for a few standard categories and display them in the Mailboxes
// portion of the left sidebar. Note that these may not all be present on
// a particular account.
const ToObject = (arr) => {
return arr.reduce((o, v) => {
o[v] = v;
return o;
}, {});
}
const StandardCategories = ToObject([
"inbox",
"important",
"sent",
"drafts",
"all",
"spam",
"archive",
"trash",
]);
const LockedCategories = ToObject([
"sent",
"drafts",
"N1-Snoozed",
]);
const HiddenCategories = ToObject([
"sent",
"drafts",
"all",
"archive",
"starred",
"important",
"N1-Snoozed",
]);
/**
Private:
This abstract class has only two concrete implementations:
- `Folder`
- `Label`
See the equivalent models for details.
Folders and Labels have different semantics. The `Category` class only exists to help DRY code where they happen to behave the same
## Attributes
`name`: {AttributeString} The internal name of the label or folder. Queryable.
`displayName`: {AttributeString} The display-friendly name of the label or folder. Queryable.
Section: Models
*/
export default class Folder extends Model {
get displayName() {
if (this.path && this.path.startsWith('INBOX.')) {
return this.path.substr(6);
}
if (this.path && this.path === 'INBOX') {
return 'Inbox';
}
return this.path;
}
get name() {
return this.role;
}
static attributes = Object.assign({}, Model.attributes, {
role: Attributes.String({
queryable: true,
modelKey: 'role',
}),
path: Attributes.String({
queryable: true,
modelKey: 'path',
}),
imapName: Attributes.String({
modelKey: 'imapName',
jsonKey: 'imap_name',
}),
syncProgress: Attributes.Object({
modelKey: 'syncProgress',
jsonKey: 'sync_progress',
}),
});
static Types = {
Standard: 'standard',
Locked: 'locked',
User: 'user',
Hidden: 'hidden',
}
static StandardCategoryNames = Object.keys(StandardCategories)
static LockedCategoryNames = Object.keys(LockedCategories)
static HiddenCategoryNames = Object.keys(HiddenCategories)
static categoriesSharedName(cats) {
if (!cats || cats.length === 0) {
return null;
}
const name = cats[0].name
if (!cats.every((cat) => cat.name === name)) {
return null;
}
return name;
}
static additionalSQLiteConfig = {
setup: () => {
return [
// 'CREATE INDEX IF NOT EXISTS FolderNameIndex ON Folder(accountId,name)',
// 'CREATE UNIQUE INDEX IF NOT EXISTS FolderClientIndex ON Folder(id)',
];
},
};
displayType() {
return 'folder';
}
hue() {
if (!this.displayName) {
return 0;
}
let hue = 0;
for (let i = 0; i < this.displayName.length; i++) {
hue += this.displayName.charCodeAt(i);
}
hue *= (396.0 / 512.0);
return hue;
}
isStandardCategory(forceShowImportant) {
let showImportant = forceShowImportant;
if (showImportant === undefined) {
showImportant = NylasEnv.config.get('core.workspace.showImportant');
}
if (showImportant === true) {
return !!StandardCategories[this.name];
}
return !!StandardCategories[this.name] && (this.name !== 'important');
}
isLockedCategory() {
return !!LockedCategories[this.name] || !!LockedCategories[this.displayName];
}
isHiddenCategory() {
return !!HiddenCategories[this.name] || !!HiddenCategories[this.displayName];
}
isUserCategory() {
return !this.isStandardCategory() && !this.isHiddenCategory();
}
isInbox() {
return this.name === 'inbox'
}
isArchive() {
return ['all', 'archive'].includes(this.name);
}
isSyncComplete() {
// We sync by folders, not labels. If the category is a label, or hasn't been
// assigned an object type yet, just return based on the sync status for the
// entire account.
if (this.object !== 'folder') {
return FolderSyncProgressStore.isSyncCompleteForAccount(this.accountId);
}
return FolderSyncProgressStore.isSyncCompleteForAccount(
this.accountId,
this.name || this.displayName
);
}
}

View file

@ -1,2 +1,187 @@
import Category from './category';
export default Category;
/* eslint global-require: 0 */
import {FolderSyncProgressStore} from 'nylas-exports';
import Model from './model';
import Attributes from '../attributes';
let AccountStore = null
// We look for a few standard categories and display them in the Mailboxes
// portion of the left sidebar. Note that these may not all be present on
// a particular account.
const ToObject = (arr) => {
return arr.reduce((o, v) => {
o[v] = v;
return o;
}, {});
}
const StandardCategories = ToObject([
"inbox",
"important",
"sent",
"drafts",
"all",
"spam",
"archive",
"trash",
]);
const LockedCategories = ToObject([
"sent",
"drafts",
"N1-Snoozed",
]);
const HiddenCategories = ToObject([
"sent",
"drafts",
"all",
"archive",
"starred",
"important",
"N1-Snoozed",
]);
/**
Private:
This abstract class has only two concrete implementations:
- `Folder`
- `Label`
See the equivalent models for details.
Folders and Labels have different semantics. The `Category` class only exists to help DRY code where they happen to behave the same
## Attributes
`name`: {AttributeString} The internal name of the label or folder. Queryable.
`displayName`: {AttributeString} The display-friendly name of the label or folder. Queryable.
Section: Models
*/
export default class Label extends Model {
static attributes = Object.assign({}, Model.attributes, {
name: Attributes.String({
queryable: true,
modelKey: 'name',
}),
displayName: Attributes.String({
queryable: true,
modelKey: 'displayName',
jsonKey: 'display_name',
}),
imapName: Attributes.String({
modelKey: 'imapName',
jsonKey: 'imap_name',
}),
syncProgress: Attributes.Object({
modelKey: 'syncProgress',
jsonKey: 'sync_progress',
}),
});
static Types = {
Standard: 'standard',
Locked: 'locked',
User: 'user',
Hidden: 'hidden',
}
static StandardCategoryNames = Object.keys(StandardCategories)
static LockedCategoryNames = Object.keys(LockedCategories)
static HiddenCategoryNames = Object.keys(HiddenCategories)
static categoriesSharedName(cats) {
if (!cats || cats.length === 0) {
return null;
}
const name = cats[0].name
if (!cats.every((cat) => cat.name === name)) {
return null;
}
return name;
}
static additionalSQLiteConfig = {
setup: () => {
return [
// 'CREATE INDEX IF NOT EXISTS LabelNameIndex ON Label(accountId,name)',
// 'CREATE UNIQUE INDEX IF NOT EXISTS LabelClientIndex ON Label(id)',
];
},
};
fromJSON(json) {
super.fromJSON(json);
if (this.displayName && this.displayName.startsWith('INBOX.')) {
this.displayName = this.displayName.substr(6);
}
if (this.displayName && this.displayName === 'INBOX') {
this.displayName = 'Inbox';
}
return this;
}
displayType() {
return 'label';
}
hue() {
if (!this.displayName) {
return 0;
}
let hue = 0;
for (let i = 0; i < this.displayName.length; i++) {
hue += this.displayName.charCodeAt(i);
}
hue *= (396.0 / 512.0);
return hue;
}
isStandardCategory(forceShowImportant) {
let showImportant = forceShowImportant;
if (showImportant === undefined) {
showImportant = NylasEnv.config.get('core.workspace.showImportant');
}
if (showImportant === true) {
return !!StandardCategories[this.name];
}
return !!StandardCategories[this.name] && (this.name !== 'important');
}
isLockedCategory() {
return !!LockedCategories[this.name] || !!LockedCategories[this.displayName];
}
isHiddenCategory() {
return !!HiddenCategories[this.name] || !!HiddenCategories[this.displayName];
}
isUserCategory() {
return !this.isStandardCategory() && !this.isHiddenCategory();
}
isInbox() {
return this.name === 'inbox'
}
isArchive() {
return ['all', 'archive'].includes(this.name);
}
isSyncComplete() {
// We sync by folders, not labels. If the category is a label, or hasn't been
// assigned an object type yet, just return based on the sync status for the
// entire account.
if (this.object !== 'folder') {
return FolderSyncProgressStore.isSyncCompleteForAccount(this.accountId);
}
return FolderSyncProgressStore.isSyncCompleteForAccount(
this.accountId,
this.name || this.displayName
);
}
}

View file

@ -1,7 +1,8 @@
import _ from 'underscore'
import Message from './message'
import Contact from './contact'
import Category from './category'
import Folder from './folder'
import Label from './label'
import Attributes from '../attributes'
import DatabaseStore from '../stores/database-store'
import ModelWithMetadata from './model-with-metadata'
@ -64,16 +65,20 @@ class Thread extends ModelWithMetadata {
modelKey: 'version',
}),
categories: Attributes.Collection({
folders: Attributes.Collection({
queryable: true,
modelKey: 'categories',
modelKey: 'folders',
joinOnField: 'id',
joinQueryableBy: ['inAllMail', 'lastMessageReceivedTimestamp', 'lastMessageSentTimestamp', 'unread'],
itemClass: Category,
itemClass: Folder,
}),
categoriesType: Attributes.String({
modelKey: 'categoriesType',
labels: Attributes.Collection({
queryable: true,
modelKey: 'labels',
joinOnField: 'id',
joinQueryableBy: ['inAllMail', 'lastMessageReceivedTimestamp', 'lastMessageSentTimestamp', 'unread'],
itemClass: Label,
}),
participants: Attributes.Collection({
@ -188,35 +193,8 @@ class Thread extends ModelWithMetadata {
})
}
get labels() {
return this.categories;
}
set labels(labels) {
this.categories = labels;
}
get folders() {
return this.categories;
}
set folders(folders) {
this.categories = folders;
}
get inAllMail() {
if (this.categoriesType === 'labels') {
const inAllMail = _.any(this.categories, cat => cat.name === 'all')
if (inAllMail) {
return true;
}
const inTrashOrSpam = _.any(this.categories, cat => cat.name === 'trash' || cat.name === 'spam')
if (!inTrashOrSpam) {
return true;
}
return false
}
return true
return this.folders.find(f => f.role === 'all');
}
/**
@ -229,17 +207,6 @@ class Thread extends ModelWithMetadata {
fromJSON(json) {
super.fromJSON(json)
if (json.folders) {
this.categoriesType = 'folders'
this.categories = Thread.attributes.categories.fromJSON(json.folders)
}
if (json.labels && json.labels.length > 0) {
this.categoriesType = 'labels'
if (!this.categories) this.categories = [];
this.categories = this.categories.concat(Thread.attributes.categories.fromJSON(json.labels))
}
['participants'].forEach((attr) => {
const value = this[attr]
if (!(value && value instanceof Array)) {
@ -296,14 +263,4 @@ class Thread extends ModelWithMetadata {
}
}
Object.defineProperty(Thread.attributes, "labels", {
enumerable: false,
get: () => Thread.attributes.categories,
})
Object.defineProperty(Thread.attributes, "folders", {
enumerable: false,
get: () => Thread.attributes.categories,
})
export default Thread;

View file

@ -1,6 +1,7 @@
Rx = require 'rx-lite'
_ = require 'underscore'
Category = require('../flux/models/category').default
Folder = require('../flux/models/folder').default
Label = require('../flux/models/label').default
QuerySubscriptionPool = require('../flux/models/query-subscription-pool').default
DatabaseStore = require('../flux/stores/database-store').default
@ -27,17 +28,32 @@ CategoryOperators =
CategoryObservables =
forAllAccounts: =>
observable = Rx.Observable.fromQuery(DatabaseStore.findAll(Category))
_.extend(observable, CategoryOperators)
observable
folders = Rx.Observable.fromQuery(DatabaseStore.findAll(Folder))
labels = Rx.Observable.fromQuery(DatabaseStore.findAll(Label))
joined = Rx.Observable.combineLatest(folders, labels, (f, l) =>
debugger
[].concat(f, l)
)
_.extend(joined, CategoryOperators)
joined
forAccount: (account) =>
if account
observable = Rx.Observable.fromQuery(DatabaseStore.findAll(Category).where(accountId: account.id))
folders = Rx.Observable.fromQuery(DatabaseStore.findAll(Folder).where(accountId: account.id))
labels = Rx.Observable.fromQuery(DatabaseStore.findAll(Label).where(accountId: account.id))
joined = Rx.Observable.combineLatest(folders, labels, (f, l) =>
debugger
[].concat(f, l)
)
else
observable = Rx.Observable.fromQuery(DatabaseStore.findAll(Category))
_.extend(observable, CategoryOperators)
observable
folders = Rx.Observable.fromQuery(DatabaseStore.findAll(Folder))
labels = Rx.Observable.fromQuery(DatabaseStore.findAll(Label))
joined = Rx.Observable.combineLatest(folders, labels, (f, l) =>
debugger
[].concat(f, l)
)
_.extend(joined, CategoryOperators)
joined
standard: (account) =>
observable = Rx.Observable.fromConfig('core.workspace.showImportant')

View file

@ -13,6 +13,8 @@ UnreadQuerySubscription = require('./flux/models/unread-query-subscription').def
Matcher = require('./flux/attributes/matcher').default
Thread = require('./flux/models/thread').default
Category = require('./flux/models/category').default
Folder = require('./flux/models/folder').default
Label = require('./flux/models/label').default
Actions = require('./flux/actions').default
ChangeUnreadTask = null
@ -268,9 +270,16 @@ class CategoryMailboxPerspective extends MailboxPerspective
super(other) and _.isEqual(_.pluck(@categories(), 'id'), _.pluck(other.categories(), 'id'))
threads: =>
folders = @categories().filter((c) => c instanceof Folder)
labels = @categories().filter((c) => c instanceof Label)
query = DatabaseStore.findAll(Thread)
.where([Thread.attributes.categories.containsAny(_.pluck(@categories(), 'id'))])
.limit(0)
if folders.length > 0
query = query.where([Thread.attributes.folders.containsAny(_.pluck(folders, 'id'))])
if labels.length > 0
query = query.where([Thread.attributes.labels.containsAny(_.pluck(labels, 'id'))])
query = query.limit(0)
if @isSent()
query.order(Thread.attributes.lastMessageSentTimestamp.descending())