Home Reference Source Repository

src/notebook/epics/saving.js

const Rx = require('rxjs/Rx');
const fs = require('fs');
const commutable = require('commutable');

const Observable = Rx.Observable;

const writeFileObservable = (filename, data, ...args) =>
  Observable.create(observer => {
    fs.writeFile(filename, data, ...args, error => {
      if (error) {
        observer.error(error);
      } else {
        observer.next({ filename, data });
        observer.complete();
      }
    });
  });

export const SAVE = 'SAVE';
export const SAVE_AS = 'SAVE_AS';
export const CHANGE_FILENAME = 'CHANGE_FILENAME';
export const DONE_SAVING = 'DONE_SAVING';

export const changeFilename = filename => ({ type: CHANGE_FILENAME, filename });
export const save = (filename, notebook) => ({ type: SAVE, filename, notebook });
export const saveAs = (filename, notebook) => ({ type: SAVE_AS, filename, notebook });
export const doneSaving = () => ({ type: DONE_SAVING });

export const saveEpic = actions =>
  actions.ofType(SAVE)
    .do(action => {
      // If there isn't a filename, save-as it instead
      if (!action.filename) {
        throw new Error('save needs a filename');
      }
    })
    .mergeMap(action =>
      writeFileObservable(action.filename,
        JSON.stringify(
          commutable.toJS(
            action.notebook.update('cellMap', (cells) =>
              cells.map((value) =>
                value.delete('inputHidden').delete('outputHidden').delete('status')))),
          null,
          1))
        .map(doneSaving)
        // .startWith({ type: START_SAVING })
        // since SAVE effectively acts as the same as START_SAVING
        // you could just look for that in your reducers instead of START_SAVING
    );

export const saveAsEpic = actions =>
  actions.ofType(SAVE_AS)
    .mergeMap(action => [
      changeFilename(action.filename),
      save(action.filename, action.notebook),
    ]);