import { Component, DefaultDOMElement, platform, Router, domHelpers } from 'substance' import { throwMethodIsAbstract } from './kit' import Texture from './Texture' export default class TextureAppChrome extends Component { constructor (...args) { super(...args) if (this.props.enableRouting) { this._router = new Router() } // TODO: rethink how configuration is loaded this._config = Texture.getConfiguration() } getChildContext () { return this._childContext || {} } getInitialState () { return { archive: undefined, error: undefined } } didMount () { this._init(err => { // if debug is turned on do not 'forward' to an error display and instead // leave the app in its failed state if (err) { console.error(err) // TODO: we need to make sure that we disable 'debug' when bundling the release version if (!this.props.debug) { this.setState({ error: err }) } } }) // Note: adding global handlers causes problems in the test suite if (!platform.test) { DefaultDOMElement.getBrowserWindow().on('keydown', this._keyDown, this) DefaultDOMElement.getBrowserWindow().on('drop', domHelpers.stopAndPrevent, this) DefaultDOMElement.getBrowserWindow().on('dragover', domHelpers.stopAndPrevent, this) } if (this._router) { this._router.start() } this.handleActions({ 'save': this._handleSave }) } dispose () { DefaultDOMElement.getBrowserWindow().off(this) } _getBuffer () { throwMethodIsAbstract() } _getStorage () { throwMethodIsAbstract() } _loadArchive (archiveId, context, cb) { const ArchiveClass = this._getArchiveClass() let storage = this._getStorage() let buffer = this._getBuffer() let archive = new ArchiveClass(storage, buffer, context, this._config) // HACK: this should be done earlier in the lifecycle (after first didMount) // and later disposed properly. However we can accept this for now as // the app lives as a singleton atm. archive.on('archive:changed', this._archiveChanged, this) // Don't catch exception in debug mode const _afterLoad = (err, archive) => { if (err) return cb(err) if (this.props.isReadOnly) { archive.isReadOnly = true } cb(null, archive) } if (this.props.debug) { archive.load(archiveId, _afterLoad) } else { try { archive.load(archiveId, _afterLoad) } catch (err) { this.setState({ error: err }) console.error(err) } } } _init (cb) { if (!cb) cb = (err) => { if (err) throw err } this._setupChildContext((err, context) => { if (err) return cb(err) this._initContext(context, (err, context) => { if (err) return cb(err) this._loadArchive(this.props.archiveId, context, (err, archive) => { if (err) return cb(err) this._initArchive(archive, context, (err, archive) => { if (err) return cb(err) this._childContext = context this.setState({ archive }) this._afterInit() this.emit('archive:ready') }) }) }) }) } _setupChildContext (cb) { cb(null, { router: this._router }) } _initContext (context, cb) { cb(null, context) } _initArchive (archive, context, cb) { cb(null, archive) } _afterInit () { // Update window title after archive loading to display title this._updateTitle() } _archiveChanged () { this._updateTitle() } _handleSave () { this._save((err) => { if (err) console.error(err) }) } _save (cb) { this.state.archive.save((err, update) => { if (err) return cb(err) this._updateTitle() cb(null, update) }) } _updateTitle () {} _keyDown (event) { // TODO: should this really be suppressed here? if (event.key === 'Dead') return if (this._handleKeydown) { this._handleKeydown(event) } } _handleKeydown (event) { let handled = false handled = this.refs.texture._handleKeydown(event) if (handled) { event.preventDefault() event.stopPropagation() } } }