diff --git a/app/backend.js b/app/backend.js index 2c740022..cdaeb00e 100644 --- a/app/backend.js +++ b/app/backend.js @@ -1,5 +1,6 @@ /* globals __BUILD__ */ -import { app } from 'electron' +// import {chunksToLinesAsync, chomp} from '@rauschma/stringio' +import { app, dialog } from 'electron' import { spawn } from 'child_process' import { EventEmitter } from 'events' import path from 'path' @@ -151,9 +152,15 @@ export default class Backend extends EventEmitter { } this.backend.on('close', (code) => { + if (process.env.NODE_ENV !== 'development') { + dialog.showErrorBox("Qri Backend Closed", "For some unexpected reason the Qri backend process has closed. Things aren't going to work well. Mind restarting?") + } this.log('closed backend process') }) this.backend.on('error', (err) => { + if (process.env.NODE_ENV !== 'development') { + dialog.showErrorBox("Qri Backend Error", err) + } this.log(`backend error: ${err}`) }) } diff --git a/app/main.dev.js b/app/main.dev.js index 237f2177..16c6486f 100644 --- a/app/main.dev.js +++ b/app/main.dev.js @@ -17,7 +17,7 @@ import touchbar from './touchbar' const isDevelopment = (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD !== 'false') -let mainWindow = null +// let mainWindow = null let backend = null if (process.env.NODE_ENV === 'production') { @@ -97,7 +97,6 @@ app.on('quit', () => { }) function createMainWindow () { - console.log('booting app...') let mainWindow = new BrowserWindow({ show: false, width: 1500, @@ -140,31 +139,6 @@ function createMainWindow () { }) } - const menuBuilder = new MenuBuilder(mainWindow) + const menuBuilder = new MenuBuilder(mainWindow, createMainWindow) menuBuilder.buildMenu() - - // mainWindow.webContents.session.on('will-download', (event, item, webContents) => { - // console.log(event) - // // Set the save path, making Electron not to prompt a save dialog. - // item.setSavePath('/tmp/save.pdf') - - // item.on('updated', (event, state) => { - // if (state === 'interrupted') { - // console.log('Download is interrupted but can be resumed') - // } else if (state === 'progressing') { - // if (item.isPaused()) { - // console.log('Download is paused') - // } else { - // console.log(`Received bytes: ${item.getReceivedBytes()}`) - // } - // } - // }) - // item.once('done', (event, state) => { - // if (state === 'completed') { - // console.log('Download successfully') - // } else { - // console.log(`Download failed: ${state}`) - // } - // }) - // }) } diff --git a/app/menu.js b/app/menu.js index 06b1079a..81dcedbf 100644 --- a/app/menu.js +++ b/app/menu.js @@ -3,8 +3,9 @@ import { app, Menu, shell, BrowserWindow } from 'electron'; export default class MenuBuilder { mainWindow: BrowserWindow; - constructor(mainWindow: BrowserWindow) { + constructor(mainWindow: BrowserWindow, createWindow) { this.mainWindow = mainWindow; + this.createWindow = createWindow; } buildMenu() { @@ -44,19 +45,23 @@ export default class MenuBuilder { buildDarwinTemplate() { const subMenuAbout = { - label: 'qri', + label: 'Qri', submenu: [ - { label: 'About qri', selector: 'orderFrontStandardAboutPanel:' }, - // { type: 'separator' }, - // { label: 'Services', submenu: [] }, + { label: 'About Qri', selector: 'orderFrontStandardAboutPanel:' }, { type: 'separator' }, - { label: 'Hide qri', accelerator: 'Command+H', selector: 'hide:' }, + { label: 'Hide Qri', accelerator: 'Command+H', selector: 'hide:' }, { label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' }, { label: 'Show All', selector: 'unhideAllApplications:' }, { type: 'separator' }, { label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } } ] }; + const subMenuFile = { + label: 'File', + submenu: [ + { label: 'New Window', accelerator: 'Command+N', click: () => { this.createWindow() }} + ] + } const subMenuEdit = { label: 'Edit', submenu: [ @@ -69,7 +74,7 @@ export default class MenuBuilder { { label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' } ] }; - const subMenuViewDev = { + const subMenuView = { label: 'View', submenu: [ { label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } }, @@ -77,13 +82,6 @@ export default class MenuBuilder { { label: 'Toggle Developer Tools', accelerator: 'Command+Shift+C', click: () => { this.mainWindow.toggleDevTools(); } } ] }; - const subMenuViewProd = { - label: 'View', - submenu: [ - { label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } }, - { label: 'Toggle Developer Tools', accelerator: 'Command+Shift+C', click: () => { this.mainWindow.openDevTools(); } } - ] - }; const subMenuWindow = { label: 'Window', submenu: [ @@ -96,18 +94,17 @@ export default class MenuBuilder { const subMenuHelp = { label: 'Help', submenu: [ - { label: 'Learn More', click() { shell.openExternal('http://qri.io'); } }, - { label: 'Documentation', click() { shell.openExternal('https://github.com/qri-io/frontend/blob/master/readme.md'); } }, - // { label: 'Community Discussions', click() { shell.openExternal('https://discuss.atom.io/c/electron'); } }, - { label: 'Search Issues', click() { shell.openExternal('https://github.com/qri-io/frontend/issues'); } } + { label: 'Qri Site', click() { shell.openExternal('http://qri.io'); } }, + { label: 'Discord Chat', click() { shell.openExternal('https://discord.gg/etap8Gb'); } }, + { label: 'Docs', click() { shell.openExternal('https://qri.io/docs'); } }, + { label: 'App Github', click() { shell.openExternal('https://github.com/qri-io/frontend/blob/master/readme.md'); } }, + { label: 'App Github Issues', click() { shell.openExternal('https://github.com/qri-io/frontend/issues'); } } ] }; - const subMenuView = process.env.NODE_ENV === 'development' - ? subMenuViewDev : subMenuViewProd; - return [ subMenuAbout, + subMenuFile, subMenuEdit, subMenuView, subMenuWindow, diff --git a/app/package.json b/app/package.json index c2035e96..e6f3ce8f 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "name": "qri-webapp", "productName": "qri", - "version": "0.5.1", + "version": "0.5.2", "description": "qri (\"query\") frontend application", "main": "./main.prod.js", "author": { diff --git a/lib/actions/dataset.js b/lib/actions/dataset.js index be76ac30..4c94a8d2 100755 --- a/lib/actions/dataset.js +++ b/lib/actions/dataset.js @@ -245,7 +245,7 @@ export function downloadDataset (datasetRef) { } // prepFiles turns strings into File objects for submission via a form/multipart API request -function prepFiles (dataset = {}, transformScript = '', vizScript = '', bodyData) { +function prepFiles (dataset = {}, transformScript = '', vizScript = '', bodyData = '') { let viz, transform, body let file = new File([JSON.stringify(dataset)], 'dataset.json', { @@ -264,7 +264,8 @@ function prepFiles (dataset = {}, transformScript = '', vizScript = '', bodyData }) } - if (bodyData) { + if (bodyData && bodyData.length) { + console.log(bodyData, bodyData.length) body = new File([bodyData], 'body', { type: 'text/plain' }) diff --git a/lib/components/App.js b/lib/components/App.js index 6d5ff361..0865ceef 100644 --- a/lib/components/App.js +++ b/lib/components/App.js @@ -60,9 +60,11 @@ export default class App extends Base { this.props.layoutResize(window.innerWidth, window.innerHeight) }, 250) - window.onbeforeunload = () => { + window.onbeforeunload = (e) => { if (this.props.editorDirty) { - if (!confirm('the editor has unsaved changes. are you *sure* want to close this window?')) { + if (!confirm('the editor has unsaved changes. are you sure want to close this window?')) { + e.preventDefault() + e.returnValue = '' return false } } diff --git a/lib/components/ExternalLink.electron.js b/lib/components/ExternalLink.electron.js new file mode 100644 index 00000000..7a54acd4 --- /dev/null +++ b/lib/components/ExternalLink.electron.js @@ -0,0 +1,11 @@ +import React from 'react' +import { shell } from 'electron' + +export default class ExternalLink extends React.Component { + render () { + return ( { + e.preventDefault() + shell.openExternal(this.props.href) + }} {...this.props}>{this.props.children}) + } +} diff --git a/lib/components/ExternalLink.web.js b/lib/components/ExternalLink.web.js new file mode 100644 index 00000000..1013062e --- /dev/null +++ b/lib/components/ExternalLink.web.js @@ -0,0 +1,7 @@ +import React from 'react' + +export default class ExternalLink extends React.Component { + render () { + return {this.props.children} + } +} diff --git a/lib/components/TopBar.js b/lib/components/TopBar.js index c0514282..e407ca77 100644 --- a/lib/components/TopBar.js +++ b/lib/components/TopBar.js @@ -7,6 +7,7 @@ import { defaultPalette } from '../propTypes/palette' import SearchBar from './form/SearchBar' import Base from './Base' +import ExternalLink from './ExternalLink.APP_TARGET' export default class TopBar extends Base { constructor (props) { @@ -88,8 +89,8 @@ export default class TopBar extends Base {
- chat - help + chat + help
diff --git a/lib/components/dataset/Overview.js b/lib/components/dataset/Overview.js index 93946fa3..1d484325 100644 --- a/lib/components/dataset/Overview.js +++ b/lib/components/dataset/Overview.js @@ -10,6 +10,7 @@ import Button from '../chrome/Button' import { defaultPalette } from '../../propTypes/palette' import Schema from '../schema/schema.js' +import ExternalLink from '../ExternalLink.APP_TARGET' export default class Overview extends Base { constructor (props) { @@ -143,9 +144,9 @@ export default class Overview extends Base {

{ (meta && meta.title) || name}

{/*
open data
*/} - {meta && meta.keywords ? : undefined} - {meta && meta.description ?

{meta.description}

: undefined} - {meta && meta.downloadPath ? {meta.downloadPath} : undefined} + {meta && meta.keywords && } + {meta && meta.description &&

{meta.description}

} + {meta && meta.downloadPath && {meta.downloadPath}} ) } diff --git a/lib/components/editor/EditBody.js b/lib/components/editor/EditBody.js index a5879ecf..be5238ea 100644 --- a/lib/components/editor/EditBody.js +++ b/lib/components/editor/EditBody.js @@ -43,11 +43,12 @@ export default class EditBody extends Base { } onAddBody () { + // TODO - check structure & produce proper data format this.props.onChangeBody([['', ''], ['', '']]) } onRemoveBody () { - this.props.onChangeBody(undefined) + this.props.onChangeBody('') } onMonacoChange (body) { @@ -55,10 +56,7 @@ export default class EditBody extends Base { } onAfterCreateRow (start, rows, source) { - // const { body } = this.props - // const newBody = body.slice(0, start).concat([], body.slice(start, body.length - 1)) - // console.log(newBody) - // this.props.onChangeBody(newBody) + this.props.onChangeBody(cloneDeep(this.body)) } onAfterRemoveRow (changes, source) { this.props.onChangeBody(cloneDeep(this.body)) @@ -73,31 +71,7 @@ export default class EditBody extends Base { if (source === 'loadData' || !changes) { return } - this.props.onChangeBody(cloneDeep(this.body)) - - // const { body } = this.props - - // const newBody = body.concat([]) - // changes.forEach((a) => { - // if (!a) { - // return - // } - - // const [row, column,, newValue] = a - // if (!newBody[row]) { - // return - // } - - // newBody[row] = newBody[row].map((item, i) => { - // if (i === column) { - // return newValue - // } - // return item - // }) - // }) - - // this.props.onChangeBody(newBody) } renderEditor () { diff --git a/lib/components/editor/Editor.js b/lib/components/editor/Editor.js index 89a34831..d658a58b 100644 --- a/lib/components/editor/Editor.js +++ b/lib/components/editor/Editor.js @@ -88,6 +88,9 @@ export default class Editor extends Base { } else { body = JSON.stringify(body) } + if (body === '""') { + body = '' + } this.setState({ datasetError: '', datasetMessage: '', results: undefined, loading: true }) diff --git a/lib/scss/_site.scss b/lib/scss/_site.scss index 7f8809e7..a36c6553 100755 --- a/lib/scss/_site.scss +++ b/lib/scss/_site.scss @@ -124,31 +124,6 @@ b { font-size: 16px; } -// these overrides cause scrolling of the data body page to get messed up -// if we need to add them it won't be the worst, but until I hear that we -// need these overrides back, I'm going to keep them commented out. - -// ::-webkit-scrollbar { -// width: 8px; -// background-color: transparent; -// } -// ::-webkit-scrollbar-corner { -// background-color: transparent; -// } -// ::-webkit-scrollbar-thumb { -// width: 8px; -// border-radius: 4px; -// background-color: rgba(0,0,0,0.4); -// box-shadow: 0 0 0 12px rgba(49, 49, 49,0.2) inset; -// } -// ::-webkit-scrollbar-thumb:hover, -// ::-webkit-scrollbar-thumb:focus { -// box-shadow: 0 0 0 12px rgba(89, 89, 89, 0.5) inset; -// } -// ::-webkit-scrollbar-thumb:active { -// box-shadow: 0 0 0 12px rgba(169, 169, 169, 0.5) inset; -// } - // dataset nav links .navLink { font-size: $nav-font-size; diff --git a/lib/utils/reflect.js b/lib/utils/reflect.js index b1f996b5..41edfa51 100644 --- a/lib/utils/reflect.js +++ b/lib/utils/reflect.js @@ -2,7 +2,7 @@ // data structures export function isEmpty (v) { - if (!v) { return true } + if (v === undefined) { return true } switch (v.constructor) { case Object: @@ -10,7 +10,7 @@ export function isEmpty (v) { case Array: return v.length === 0 } - return !!v + return !v } export function isEmptyObj (obj = {}) { diff --git a/package.json b/package.json index b2b65463..5e4032c8 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "qri", "private": true, "productName": "qri", - "version": "0.5.1", + "version": "0.5.2", "description": "qri (\"query\") frontend application", "keywords": [], "homepage": "https://qri.io", diff --git a/resources/qri b/resources/qri index dcb0b6d0..6181c1d6 100755 Binary files a/resources/qri and b/resources/qri differ diff --git a/version.js b/version.js index 6d529e83..f94ea9de 100644 --- a/version.js +++ b/version.js @@ -1 +1 @@ -export default '0.5.1' +export default '0.5.2' diff --git a/webpack.config.renderer.dev.js b/webpack.config.renderer.dev.js index 34e504af..7d4ced76 100644 --- a/webpack.config.renderer.dev.js +++ b/webpack.config.renderer.dev.js @@ -24,6 +24,7 @@ const port = process.env.PORT || 1212 const publicPath = `http://localhost:${port}/dist` const dll = path.resolve(process.cwd(), 'dll') const manifest = path.resolve(dll, 'renderer.json') +const appTarget = process.env.APP_TARGET || 'electron' /** * Warn if the DLL is not built @@ -239,6 +240,10 @@ export default merge.smart(baseConfig, { }, plugins: [ + new webpack.NormalModuleReplacementPlugin(/(.*)\.APP_TARGET(\.*)/, function (resource) { + resource.request = resource.request.replace(/\.APP_TARGET/, `.${appTarget}`) + }), + new webpack.DllReferencePlugin({ context: process.cwd(), manifest: require(manifest), diff --git a/webpack.config.renderer.prod.js b/webpack.config.renderer.prod.js index 8504e5cf..1023d4bf 100644 --- a/webpack.config.renderer.prod.js +++ b/webpack.config.renderer.prod.js @@ -16,6 +16,8 @@ import version from './version' CheckNodeEnv('production') +const appTarget = process.env.APP_TARGET || 'electron' + export default merge.smart(baseConfig, { mode: 'production', devtool: 'source-map', @@ -156,6 +158,9 @@ export default merge.smart(baseConfig, { }, plugins: [ + new webpack.NormalModuleReplacementPlugin(/(.*)\.APP_TARGET(\.*)/, function (resource) { + resource.request = resource.request.replace(/\.APP_TARGET/, `.${appTarget}`) + }), /** * Create global constants which can be configured at compile time. * diff --git a/webpack.config.webapp.dev.js b/webpack.config.webapp.dev.js index 56aa1dad..0d09ed4e 100644 --- a/webpack.config.webapp.dev.js +++ b/webpack.config.webapp.dev.js @@ -14,6 +14,7 @@ CheckNodeEnv('development') const port = process.env.PORT || 2505 const publicPath = `/` +const appTarget = process.env.APP_TARGET || 'web' export default merge.smart(baseConfig, { devtool: 'inline-source-map', @@ -54,6 +55,9 @@ export default merge.smart(baseConfig, { }, plugins: [ + new webpack.NormalModuleReplacementPlugin(/(.*)\.APP_TARGET(\.*)/, function (resource) { + resource.request = resource.request.replace(/\.APP_TARGET/, `.${appTarget}`) + }), new HtmlWebpackPlugin({ template: 'resources/index.tpl.html', inject: 'body', diff --git a/webpack.config.webapp.prod.js b/webpack.config.webapp.prod.js index 830fd908..c084c6db 100644 --- a/webpack.config.webapp.prod.js +++ b/webpack.config.webapp.prod.js @@ -8,6 +8,7 @@ import CheckNodeEnv from './internals/scripts/CheckNodeEnv' import MinifyPlugin from 'babel-minify-webpack-plugin' import version from './version' +const appTarget = process.env.APP_TARGET || 'web' CheckNodeEnv('production') @@ -26,6 +27,9 @@ export default merge.smart(baseConfig, { }, plugins: [ + new webpack.NormalModuleReplacementPlugin(/(.*)\.APP_TARGET(\.*)/, function (resource) { + resource.request = resource.request.replace(/\.APP_TARGET/, `.${appTarget}`) + }), new MinifyPlugin({}, { sourceMap: null }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),