// Copyright 2015-2017 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . import keycode from 'keycode'; import { action, computed, map, observable, transaction } from 'mobx'; import store from 'store'; import { evaluate } from '../utils'; const LS_SNIPPETS_KEY = '_console::snippets'; let instance; export default class SnippetsStore { @observable files = map(); @observable nextName = null; @observable renaming = null; @observable selected = null; codeMirror = null; constructor () { this.load(); } static get () { if (!instance) { instance = new SnippetsStore(); } return instance; } @computed get code () { if (!this.selected) { return ''; } return this.files.get(this.selected).content; } @action cancelRename () { if (!this.renaming || !this.nextName) { return; } this.renaming = null; this.nextName = null; } clearCodeHistory () { if (this.codeMirror) { this.codeMirror.doc.clearHistory(); } } @action create () { const id = this.getNewId(); const file = { content: '', isPristine: false, name: `Snippet #${id}`, id }; transaction(() => { this.files.set(id, file); this.select(id); }); } edit (value) { if (!this.selected) { this.create(); } const file = this.files.get(this.selected); file.content = value; this.updateFile(file); } evaluate () { const code = this.code; if (!code) { return; } const { result, error } = evaluate(code); if (error) { console.error(error); } else { console.log(result); } } getFromStorage () { return store.get(LS_SNIPPETS_KEY) || []; } getNewId () { if (this.files.size === 0) { return 1; } const ids = this.files.values().map((file) => file.id); return Math.max(...ids) + 1; } load () { const files = this.getFromStorage(); transaction(() => { files.forEach((file) => { this.files.set(file.id, file); }); }); } @action remove (id) { transaction(() => { if (id === this.selected) { this.selected = null; } this.files.delete(id); const files = this.getFromStorage() .filter((f) => f.id !== id); return store.set(LS_SNIPPETS_KEY, files); }); } save (_file) { let file; if (!_file) { if (!this.selected) { return false; } file = this.files.get(this.selected); } else { file = _file; } file.savedContent = file.content; this.updateFile(file); this.saveToStorage(file); } saveName () { if (!this.renaming || !this.nextName) { return; } const file = this.files.get(this.renaming); file.name = this.nextName; this.save(file); this.cancelRename(); } saveToStorage (file) { const files = this.getFromStorage(); const index = files.findIndex((f) => file.id === f.id); if (index === -1) { files.push(file); } else { files[index] = file; } return store.set(LS_SNIPPETS_KEY, files); } @action select (id) { this.selected = id; // Wait for the file content to be loaded setTimeout(() => { this.clearCodeHistory(); }, 50); } setCodeMirror (codeMirror) { this.codeMirror = codeMirror; if (!codeMirror) { return; } this.codeMirror .on('keydown', (_, event) => { const codeName = keycode(event); if (codeName === 'enter' && event.ctrlKey) { event.preventDefault(); event.stopPropagation(); return this.evaluate(); } }); } @action startRename (id) { const file = this.files.get(id); transaction(() => { this.renaming = id; this.nextName = file.name; }); } @action updateFile (file) { file.isPristine = (file.content === file.savedContent); this.files.set(file.id, file); } @action updateName (value) { this.nextName = value; } }