Skip to content

Commit

Permalink
major refactor; remove all brand-specific stuff, implement a site con…
Browse files Browse the repository at this point in the history
…fig file format which customizes vizier to a specific brand or site without code changes
  • Loading branch information
ryanmark committed Oct 17, 2018
1 parent c078d12 commit 4802b77
Show file tree
Hide file tree
Showing 14 changed files with 252 additions and 219 deletions.
1 change: 0 additions & 1 deletion .electron-vue/webpack.main.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ else if (version.indexOf('alpha') >= 0 ) channel = 'alpha'

mainConfig.plugins.push(
new webpack.DefinePlugin({
'AI2HTML_HASH': `"${crypto.createHash('sha1').update(fs.readFileSync(path.join(__dirname, '../static/ai2html.js'))).digest('hex')}"`,
'AUTOUPDATE_CHANNEL': `"${channel}"`
})
)
Expand Down
4 changes: 4 additions & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ export function streamCopyFile(src, dest) {
)
})
}

export function settingsLabel() {
return process.platform === 'darwin' ? 'Preferences' : 'Settings'
}
52 changes: 50 additions & 2 deletions src/main/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import path from 'path'
import rmrf from 'rimraf'
import fs from 'fs'
import { slugify } from 'underscore.string'
import yaml from 'js-yaml'

import { dispatch, resetState } from './ipc'
import state from './index'
import { install } from './install_ai_plugin'
import { run } from './workers'
import { error } from './dialogs'
import { error, alert, confirm } from './dialogs'
import storage from './storage'
import defaultData from './default_data'

import { expandHomeDir, compactHomeDir } from '../lib'
import renderEmbedCode from '../lib/embed_code'
Expand Down Expand Up @@ -237,7 +239,7 @@ export function editSettings() {
: `file://${__dirname}/index.html#settings`

const winWidth = 520
const winHeight = 630
const winHeight = 512

state.settingsWindow = new BrowserWindow({
//parent: state.mainWindow,
Expand Down Expand Up @@ -291,3 +293,49 @@ export function clearState() {
})
})
}

export function resetSettings() {
confirm({
parentWin: state.settingsWindow,
message: 'Do you wish to reset and clear your settings?',
confirmLabel: 'Reset settings'
}).then(() => {
state.installedAi2htmlHash = null
state.newAi2htmlHash = null
dispatch('resetSettings', defaultData.Settings)
})
}

const ALLOWED_KEYS = [
'deployBaseUrl', 'deployType',
'awsBucket', 'awsPrefix', 'awsRegion', 'awsAccessKeyId', 'awsSecretAccessKey',
'siteConfigName', 'extraPreviewCss', 'extraEmbedCss', 'ai2htmlFonts'
]

export function importSettings() {
dialog.showOpenDialog( state.settingsWindow, {
message: 'Select a config file to load.',
filters: [{name: 'Viz Config', extensions: ['vizappconfig']}],
properties: [ 'openFile' ]
}, (filePaths) => {
if (!filePaths || filePaths.length === 0) return;

const configFile = filePaths[0]
const configContent = fs.readFileSync(configFile, 'utf8')
const data = yaml.safeLoad(configContent)
const configVersion = data.version || 1

if ( configVersion != 1 ) {
error({
parentWin: state.settingsWindow,
message: 'This config file is for a different version of the app.'
})
} else {
const newSettings = {}
for ( const k of ALLOWED_KEYS ) {
if ( k in data && data[k] ) newSettings[k] = data[k]
}
dispatch('updateSettings', newSettings)
}
})
}
5 changes: 4 additions & 1 deletion src/main/default_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const data = {
"awsPrefix": null,
"awsRegion": 'us-east-1',
"awsAccessKeyId": null,
"awsSecretAccessKey": null
"awsSecretAccessKey": null,
"extraPreviewCss": null,
"extraEmbedCss": null,
"ai2htmlFonts": null
}
}

Expand Down
139 changes: 81 additions & 58 deletions src/main/install_ai_plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import state from './index'
import crypto from 'crypto'
import { dispatch } from './ipc'
import { alert, confirm, error, chooseFolder } from './dialogs'
import { streamCopyFile } from '../lib'
import { render } from '../lib'

const PATHS = {
'darwin': [
Expand All @@ -20,6 +20,8 @@ const PATHS = {
],
}

const HASH_ALGO = 'sha1'

let DEFAULT_PROGRAMS_DIR = null
let SCRIPTS_DIR = null
if ( process.platform === 'darwin' ) {
Expand Down Expand Up @@ -60,18 +62,25 @@ function findScriptsPath(appPath) {
console.error("Can't find Adobe Illustrator scripts folder. Looked here: ", scriptsPath)
return Promise.reject(new Error('Adobe Illustrator Scripts folder is missing.'))
}

return Promise.resolve(scriptsPath)
}

function renderAi2htmlScript() {
return render('ai2html.js.ejs', {settings: state.data.Settings})
}

function copyScript(scriptsPath) {
const src = path.join(state.staticPath, 'ai2html.js')
const output = renderAi2htmlScript()
const dest = path.join(scriptsPath, 'ai2html.js')
return streamCopyFile(src, dest).then(() => scriptsPath)
fs.writeFileSync(dest, output)
return Promise.resolve(scriptsPath)
}

function calcHash(filename, type='sha1') {
function calcHash(filename) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash(type)
if ( !fs.existsSync(filename) ) return reject(`File not found ${filename}`)
const hash = crypto.createHash(HASH_ALGO)
hash.on('readable', () => {
const data = hash.read()
if ( data ) resolve(data.toString('hex'))
Expand All @@ -81,72 +90,86 @@ function calcHash(filename, type='sha1') {
})
}

function isInstalled() {
export function calcInstalledHash() {
const installPath = state.data.Settings.scriptInstallPath
return Promise.resolve(installPath && fs.existsSync(installPath))
if ( !installPath || !fs.existsSync(installPath) ) return null
const scriptPath = path.join(installPath, 'ai2html.js')
if ( !fs.existsSync(scriptPath) ) return null
const hash = crypto.createHash(HASH_ALGO)
hash.update(fs.readFileSync(scriptPath, 'utf8'))
return hash.digest('hex')
}

function isUpdated() {
const installPath = state.data.Settings.scriptInstallPath
if ( ! fs.existsSync(installPath) ) return Promise.resolve(null)
return calcHash(installPath).then((installedHash) => AI2HTML_HASH === installedHash)
export function calcNewHash() {
const hash = crypto.createHash(HASH_ALGO)
hash.update(renderAi2htmlScript())
return hash.digest('hex')
}

export function install({parentWin = null, forceInstall = false} = {}) {
const startupCheck = state.data.Settings.disableAi2htmlStartupCheck
const installPath = state.data.Settings.scriptInstallPath

Promise.all([isInstalled(), isUpdated()])
.then(([installed, updated]) => {
let verb
if(!installed) verb = 'Install'
else if (installed && !updated) verb = 'Update'
else if (forceInstall) verb = 'Reinstall'
else return;

dialog.showMessageBox(parentWin, {
type: 'question',
title: `${verb} ai2html`,
message: `Would you like to ${verb.toLowerCase()} ai2html?`,
defaultId: 1,
buttons: ['No', `${verb} ai2html`],
checkboxLabel: "Always check on startup",
checkboxChecked: !startupCheck,
}, (res, checkboxChecked) => {
dispatch('set', {key: 'disableAi2htmlStartupCheck', val: !checkboxChecked})

if ( res === 0 ) return;

let prom
if (!installed) {
prom = guessAppPath()
.then(findScriptsPath)
.catch(() => chooseAppPath(parentWin).then(findScriptsPath))
.then(copyScript)
// We don't recalculate hashes here because they should be accurate
const installedHash = state.installedAi2htmlHash
const newHash = state.newAi2htmlHash

let verb
if(!installedHash) verb = 'Install'
else if (installedHash != newHash) verb = 'Update'
else if (forceInstall) verb = 'Reinstall'
else return;

dialog.showMessageBox(parentWin, {
type: 'question',
title: `${verb} ai2html`,
message: `Would you like to ${verb.toLowerCase()} ai2html?`,
defaultId: 1,
buttons: ['No', `${verb} ai2html`],
checkboxLabel: "Always check on startup",
checkboxChecked: !startupCheck,
}, (res, checkboxChecked) => {
dispatch('updateSettings', {disableAi2htmlStartupCheck: !checkboxChecked})

if ( res === 0 ) return;

let prom
if (!installPath) {
prom = guessAppPath()
.then(findScriptsPath)
.catch(() => chooseAppPath(parentWin).then(findScriptsPath))
.then(copyScript)
} else {
prom = copyScript(installPath)
}

prom.then(
(path) => {
alert({parentWin, message: 'The ai2html script has been installed.'})
state.installedAi2htmlHash = newHash
dispatch('updateSettings', {scriptInstallPath: path})
},
(err) => {
if ( err.code && err.code == 'EACCES' ) {
error({
parentWin,
message: `The ai2html script install failed.\n\nYou do not have permission to install the plugin.\n\nPlease give yourself write access to ${path.dirname(err.path)}`,
details: err.toString()
})
} else {
prom = copyScript(path.dirname(installPath))
console.error('install script failed', err)
error({parentWin, message: 'The ai2html script install failed.', details: err.toString()})
}

prom.then(
(path) => {
alert({parentWin, message: 'The ai2html script has been installed.'})
if (!installed) dispatch('set', {key: 'scriptInstallPath', val: path})
},
(err) => {
if ( err.code && err.code == 'EACCES' ) {
error({parentWin, message: `The ai2html script install failed.\n\nYou do not have permission to install the plugin.\n\nPlease give yourself write access to ${path.dirname(err.path)}`, details: err.toString()})
} else {
console.error('install script failed', err)
error({parentWin, message: 'The ai2html script install failed.', details: err.toString()})
}
}
)
})

})
}
)
})
}

export function checkOnLaunch() {
// Calculate and stash these hashes at launch
state.installedAi2htmlHash = calcInstalledHash()
state.newAi2htmlHash = calcNewHash()

if ( state.data.Settings.disableAi2htmlStartupCheck === true ) return;
install()
install({parentWin: state.mainWindow})
}
28 changes: 27 additions & 1 deletion src/main/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { ipcMain, BrowserWindow } from 'electron'
import ProjectContextMenu from './menus/ProjectContextMenu'
import state from './index'
import storage from './storage'
import { newProject, addProjects, deployProject, editSettings, installAi2html, openInIllustrator } from './actions'
import { newProject, addProjects, deployProject, editSettings, installAi2html, openInIllustrator, importSettings, resetSettings } from './actions'
import { calcInstalledHash, calcNewHash } from './install_ai_plugin'

// Sync messages
ipcMain.on( 'get-state', (eve) => {
Expand All @@ -13,6 +14,13 @@ ipcMain.on( 'has-focus', (eve) => {
eve.returnValue = eve.sender.isFocused()
} )

ipcMain.on( 'get-hashes', (eve) => {
eve.returnValue = {
installedHash: state.installedAi2htmlHash,
newHash: state.newAi2htmlHash
}
} )


// Async messages
ipcMain.on( 'project-context-menu', (event, arg) => {
Expand All @@ -27,8 +35,13 @@ ipcMain.on( 'store-mutate', (eve, arg) => {
return console.error('State is missing in store-mutate ipc', arg.mutation, arg.state)

// Parse and cache current state
const oldData = state.data
state.data = JSON.parse( arg.state )

// Recalculate the ai2html script hash if necessary
if ( state.data.Settings.ai2htmlFonts != oldData.Settings.ai2htmlFonts )
state.newAi2htmlHash = calcNewHash()

// Make sure other windows have same state
const srcWin = BrowserWindow.fromWebContents(eve.sender)
BrowserWindow.getAllWindows().forEach((win) => {
Expand Down Expand Up @@ -75,6 +88,19 @@ ipcMain.on( 'install-ai2html', (eve, arg) => {
installAi2html()
} )

ipcMain.on( 'import-settings', (eve, arg) => {
if ( arg.from == 'settings-window' )
importSettings(state.settingsWindow)
else
importSettings()
} )

ipcMain.on( 'reset-settings', (eve, arg) => {
if ( arg.from == 'settings-window' )
resetSettings(state.settingsWindow)
else
resetSettings()
} )

// Senders
export function dispatch(action, payload) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/menus/Menubar.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {app, Menu, shell} from 'electron'
import { newProject, openProject, editSettings, installAi2html, clearState } from '../actions'
import { newProject, openProject, editSettings, installAi2html, clearState, importSettings } from '../actions'
import state from '../index'
import storage from '../storage'

Expand All @@ -10,6 +10,7 @@ const MACOSX_MENUBAR_TEMPLATE = [
{role: 'about'},
{type: 'separator'},
{label: 'Preferences', click(eve) { editSettings() }},
{label: 'Import preferences', click(eve) { importSettings() }},
{label: 'Install ai2html', click(eve) { installAi2html() }},
{type: 'separator'},
{role: 'services', submenu: []},
Expand Down
1 change: 1 addition & 0 deletions src/renderer/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
body {
font: caption;
overflow:hidden;
user-select:none;
}
/* CSS */
</style>
6 changes: 3 additions & 3 deletions src/renderer/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
<script>
import Toolbar from './components/Toolbar'
import SettingsForm from './components/SettingsForm'
import { settingsLabel } from '../lib'
export default {
name: 'settings',
components: { Toolbar, SettingsForm },
computed: {
settingsLabel() {
return process.platform === 'darwin' ? 'Preferences' : 'Settings'
}
settingsLabel
},
methods: {
handleDrop (eve) {
Expand All @@ -34,6 +33,7 @@
body {
font: caption;
overflow:hidden;
user-select:none;
}
/* CSS */
</style>
Loading

0 comments on commit 4802b77

Please sign in to comment.