kumabook/stickynotes

View on GitHub
src/models/Sticky.js

Summary

Maintainability
D
1 day
Test Coverage
/* global IDBKeyRange: false */
import uuid  from 'uuid/v4';
import Model from './Model';
import Page  from './Page';
import Tag   from './Tag';
import idb   from '../utils/indexedDB';

const Sticky = new Model('stickies');

Sticky.State = {
  Normal:    0,
  Deleted:   1,
  Minimized: 2,
};

Sticky.normalizeLegacy = function normalizeLegacy(sticky) {
  /* eslint-disable  no-param-reassign, no-underscore-dangle */
  sticky.tags   = Array.from(new Set(sticky.tags.map(t => t.name)));
  sticky.url    = sticky.page_.url;
  sticky.title  = sticky.page_.title;
  delete sticky.page_;
  return Sticky.normalize(sticky);
};

Sticky.normalize = function normalize(sticky) {
  sticky.id       = sticky.uuid;
  sticky.tagNames = sticky.tags;
  sticky.tags     = [];
  if (typeof sticky.created_at === 'string') {
    sticky.created_at = new Date(sticky.created_at);
  }
  if (typeof sticky.updated_at === 'string') {
    sticky.updated_at = new Date(sticky.updated_at);
  }
  delete sticky.uuid;
  delete sticky.page;
  return sticky;
};

Sticky.createObjectStore = function createObjectStore(db) {
  const objectStore = db.createObjectStore(this.name, { keyPath: 'id' });
  objectStore.createIndex('url', 'url', { unique: false });
  objectStore.createIndex('created_at', 'created_at', { unique: false });
  objectStore.createIndex('updated_at', 'updated_at', { unique: false });
  return idb.transactionComplete(objectStore);
};


Sticky.new = (sticky, db) => {
  if (!sticky.id) {
    sticky.id = uuid();
  }
  return Page.findOrCreateBy({ url: sticky.url, title: sticky.title }, db)
    .then((page) => {
      sticky.page = page;
      return Sticky.updateTags(sticky, db);
    })
    .then(() => Sticky.create(sticky, db))
    .then(() => sticky);
};

Sticky.updateTags = function updateTags(sticky, db) {
  const removeFromTag = t => Tag.findById(t.id, db).then((tag) => {
    if (!tag) {
      return Promise.resolve();
    }
    tag.stickyIds = tag.stickyIds.filter(id => id !== sticky.id);
    return Tag.update(tag, db);
  });
  const addToTag = name => Tag.findOrCreateByName(name, db).then((t) => {
    if (!t.stickyIds.includes(sticky.id)) {
      t.stickyIds.push(sticky.id);
    }
    return Tag.update(t, db).then(() => ({ id: t.id, name: t.name }));
  });
  return Promise.all(sticky.tags.map(removeFromTag))
    .then(() => Promise.all(sticky.tagNames.map(addToTag)).then((tags) => {
      sticky.tags = tags;
    }));
};

Sticky.update = function update(sticky, db) {
  return Sticky.updateTags(sticky, db)
    .then(() => Model.prototype.update.call(this, sticky, db));
};

Sticky.findByUrl = function findByUrl(url, db) {
  const store = this.objectStore(db);
  const items = [];
  return new Promise((resolve, reject) => {
    const index   = store.index('url');
    const hasUrl  = IDBKeyRange.only(url);
    const request = index.openCursor(hasUrl);
    request.onerror   = reject;
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (cursor) {
        items.push(cursor.value);
        cursor.continue();
      } else {
        resolve(items);
      }
    };
  });
};

Sticky.findBySince = function findBySince(date, db) {
  const store = this.objectStore(db);
  const items = [];
  return new Promise((resolve, reject) => {
    const index       = store.index('updated_at');
    const since       = IDBKeyRange.lowerBound(date);
    const request     = index.openCursor(since);
    request.onerror   = reject;
    request.onsuccess = (event) => {
      const cursor = event.target.result;
      if (cursor) {
        items.push(cursor.value);
        cursor.continue();
      } else {
        resolve(items);
      }
    };
  });
};

Sticky.props = [
  'id',
  'uuid',
  'left',
  'top',
  'width',
  'height',
  'url',
  'title',
  'content',
  'color',
  'state',
];

Sticky.toCSV = function toCSV(s) {
  return Sticky.props.reduce((acc, key) => {
    acc.push(s[key]);
    return acc;
  }, []);
};

Sticky.isDeleted = sticky => sticky.state === Sticky.State.Deleted;

export default Sticky;