eletron 'download-progress' does not anything - electron

my build config
"build": {
"mac": {
"target": [
"dmg",
"zip"
],
"publish": {
"provider": "generic",
"url": "http://ip/update/darwin/0.0.1",
"channel": "stable"
}
},
"win": {
"target": [
"squirrel",
"nsis"
]
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": false
}
},
command
"build_mac": "electron-builder --mac"
and main.js
const { app, BrowserWindow, dialog } = require('electron');
const { autoUpdater } = require('electron-updater');
const log = require('electron-log');
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
log.info('App starting...');
app.whenReady().then(function() {
let win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
},
});
win.loadFile('buildprodp/index.html');
win.webContents.openDevTools();
setInterval(() => {
win.webContents.send('submitted-form', `currentVersion - ${app.getVersion()}`);
}, 5000);
init(win);
});
app.on('ready', function() {
setTimeout(() => {
autoUpdater.checkForUpdates();
}, 6000);
});
// autoUpdate codes
const init = win => {
autoUpdater.on('checking-for-update', (ev, err) => {
win.webContents.send('submitted-form', '🔎 Checking for updates');
win.webContents.send('submitted-form', ev);
win.webContents.send('submitted-form', err);
});
autoUpdater.on('update-available', (ev, err) => {
win.webContents.send('submitted-form', '🎉 Update available. Downloading ⌛️');
});
autoUpdater.on('update-not-available', (ev, err) => {
win.webContents.send('submitted-form', '👎 Update not available');
});
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
const dialogOpts = {
type: 'info',
buttons: ['Restart', 'Later'],
title: 'Application Update',
message: process.platform === 'win32' ? releaseNotes : releaseName,
detail: 'plz restart',
};
dialog.showMessageBox(dialogOpts).then(returnValue => {
if (returnValue.response === 0) autoUpdater.quitAndInstall();
});
});
autoUpdater.on('error', message => {
win.webContents.send('submitted-form', message);
});
autoUpdater.on('download-progress', progressObj => {
win.webContents.send('submitted-form', 'down start');
});
};
it did work
6s after
autoUpdater.checkForUpdates();
List item
autoUpdater.on('checking-for-update'
autoUpdater.on('update-available',
!!skiping autoUpdater.on('download-progress'!!
autoUpdater.on('update-downloaded'
https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/AppUpdater.ts#L555
Only 'download-progress' doesn't work

It was a cache problem
Deleting a folder works fine

Related

Setting up electron-forge with sveltekit - white screen on package

I am in the process of creating an electron-sveltekit starter. I have the development process working as expected but when I go to package the application it only shows a white screen when it is opened. Does anyone know how to resolve this issue?
Here is a link to my repo - https://github.com/N00nDay/sveltekit-electron-starter
package.json
...
"scripts": {
"start": "cross-env NODE_ENV=dev npm run start:all",
"start:all": "concurrently -n=svelte,electron -c='#ff3e00',blue \"npm run start:svelte\" \"npm run start:electron\"",
"start:svelte": "vite dev",
"start:electron": "electron-forge start",
"package": "cross-env NODE_ENV=production npm run package:svelte && npm run package:electron",
"package:svelte": "vite build",
"package:electron": "electron-forge package",
"make": "electron-forge make",
"build": "vite build",
"preview": "vite preview",
"test": "playwright test",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest",
"lint": "prettier --plugin-search-dir . --check .",
"format": "prettier --plugin-search-dir . --write ."
},
...
forge.config.cjs
module.exports = {
packagerConfig: {
dir: './build'
},
rebuildConfig: {},
makers: [
{
name: '#electron-forge/maker-squirrel',
config: {}
},
{
name: '#electron-forge/maker-zip',
platforms: ['darwin']
},
{
name: '#electron-forge/maker-deb',
config: {}
},
{
name: '#electron-forge/maker-rpm',
config: {}
}
]
};
svelte.config.cjs
import adapter from '#sveltejs/adapter-static';
import { vitePreprocess } from '#sveltejs/kit/vite';
/** #type {import('#sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
fallback: '/index.html'
}),
prerender: {
entries: []
}
}
};
export default config;
electron.cjs
const windowStateManager = require('electron-window-state');
const { app, BrowserWindow, ipcMain } = require('electron');
const serve = require('electron-serve');
const path = require('path');
const url = require('url');
const isDev = require('electron-is-dev');
try {
require('electron-reloader')(module);
} catch (e) {
console.error(e);
}
const serveURL = serve({ directory: '.' });
const port = process.env.PORT || 5173;
const dev = !app.isPackaged;
let mainWindow;
function createWindow() {
let windowState = windowStateManager({
defaultWidth: 800,
defaultHeight: 600
});
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: true,
contextIsolation: true,
nodeIntegration: true,
spellcheck: false,
devTools: dev,
preload: path.join(__dirname, 'preload.cjs')
},
x: windowState.x,
y: windowState.y,
width: windowState.width,
height: windowState.height
});
windowState.manage(mainWindow);
mainWindow.once('ready-to-show', () => {
mainWindow.show();
mainWindow.focus();
});
mainWindow.on('close', () => {
windowState.saveState(mainWindow);
});
return mainWindow;
}
function loadVite(port) {
const appURL = app.isPackaged
? url.format({
pathname: path.join(__dirname, '/../build/index.html'),
protocol: 'file:',
slashes: true
})
: `http://localhost:${port}`;
mainWindow.loadURL(appURL);
if (!app.isPackaged) {
mainWindow.webContents.openDevTools();
}
}
function createMainWindow() {
mainWindow = createWindow();
mainWindow.once('close', () => {
mainWindow = null;
});
if (dev) loadVite(port);
else serveURL(mainWindow);
}
app.once('ready', createMainWindow);
app.on('activate', () => {
if (!mainWindow) {
createMainWindow();
}
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
ipcMain.on('to-main', (event, count) => {
return mainWindow.webContents.send('from-main', `next count is ${count + 1}`);
});
preload.cjs
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
send: (channel, data) => {
ipcRenderer.send(channel, data);
},
sendSync: (channel, data) => {
ipcRenderer.sendSync(channel, data);
},
receive: (channel, func) => {
ipcRenderer.on(channel, (event, ...args) => func(...args));
}
});

Electron whitescreen without any elements

So I was working on an electron application for quite a while. Then suddenly when typing npm start into the console, it's just a white screen without any elements or scripts.
I think it doesn't load index.html properly, but there aren't any error messages. Before everything worked just fine.
I can't even use the JavaScript Console. When I type anything like
console.log("hello")
It just disappears and returns nothing after pressing Enter.
I don't remember changing anything.
This is my index.js file:
const { app, BrowserWindow, Menu } = require('electron');
const path = require('path');
const remoteMain = require('#electron/remote/main');
const fs = require('fs');
remoteMain.initialize();
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 1600,
height: 1000,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
devTools: true,
preload: path.join(__dirname, 'preload.js')
},
});
mainWindow.setMenuBarVisibility(false)
mainWindow.setMenu(null)
mainWindow.on('closed', () => {
win = null
})
mainWindow.loadFile(path.join(__dirname, 'index.html'));
remoteMain.enable(mainWindow.webContents)
mainWindow.once('ready-to-show', () => {
mainWindow.show();
})
mainWindow.webContents.openDevTools()
setMainMenu(mainWindow)
};
app.on('ready', createWindow);
function setMainMenu(win) {
const template = [
{
label: 'Filter',
submenu: [
{
label: 'Hello',
accelerator: 'Shift+CmdOrCtrl+H',
click() {
console.log("dont")
}
},
{
label: 'Quit',
accelerator: 'Cmd+Q',
click() {
app.quit()
}
},
{
label: "Reload",
accelerator: "Cmd+R",
click() {
win.reload()
}
},
{
label: "Open dev tools",
accelerator: "f12",
click() {
win.openDevTools()
}
}
]
},
{
label: "Edit",
submenu: [
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
{ type: "separator" },
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
]}
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

Electron: Why does showing localhost only work if never loaded another URL before?

In my Electron app I want to load an URL after the app has started:
mainWindow.loadURL(`file:///${process.resourcesPath}/Content/index.html`);
And later I want to show the file which is served on localhost:
mainWindow.loadURL(`http://127.0.0.1:2015/`);
I found out that showing localhost only works if I never load another URL before. And it does not matter if I load a local index.html or an online URL before showing localhost.
Does someone know how I can resolve this?
Here is my code. The problem is in the StartCaddy function. This function gets called when pressing on a menu item in the file menu.
// This only works if never used mainWindow.loadURL before???
mainWindow.loadURL("http://127.0.0.1:2015/");
If I comment out following line than it works without problem:
mainWindow.loadURL(windowURL);
// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')
let mainWindow;
var windowURL = "https://www.google.com/";
function createWindow() {
// Create the browser window.
//const mainWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
width: 800,
height: 600,
title: "", // Remove title in title bar
//frame: false,
//titleBarStyle: 'hidden',
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// MENU BEGIN /////////////////////////////////////////////
const { app, Menu } = require('electron')
const isMac = process.platform === 'darwin'
const template = [
// { role: 'appMenu' }
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// { role: 'fileMenu' }
{
label: 'File',
submenu: [
{
label: 'Start Caddy Server', click() {
StartCaddy();
}
},
{
label: 'Git Action', click() {
GitAction();
}
},
{
label: 'ZIP Action', click() {
ZipAction();
}
},
isMac ? { role: 'close' } : { role: 'quit' }
]
},
// { role: 'editMenu' }
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
// { role: 'viewMenu' }
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
// { role: 'windowMenu' }
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close' }
])
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
// MENU END ////////////////////////////////////////////////
// and load the index.html of the app.
mainWindow.loadURL(windowURL);
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
//windowURL = `file:///${process.resourcesPath}/Content/index.html`;
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// FUNCTIONS ///////////////////////////////////////////////
// Path to the resources directory (works only in builded app)
// ${process.resourcesPath}
// Path to the project's root folder
// ${__dirname}
// Start Caddy function
async function StartCaddy() {
console.log("Start of StartCaddy function");
const caddyAction = require("child_process").exec(`${process.resourcesPath}/Bin/caddy-start`, shellCallback);
caddyAction.stdout.pipe(process.stdout)
caddyAction.on("exit", () => console.log("Caddy action finished")) // This gets never executed
await sleep(3000); // Wait 3 seconds
console.log("Waited for 3 seconds now show localhost in new window");
//windowURL = `http://127.0.0.1:2015/`;
//mainWindow.close();
//createWindow();
// This only works if never used mainWindow.loadURL before???
mainWindow.loadURL("http://127.0.0.1:2015/");
//mainWindow.loadURL(`http://localhost:2015`);
//mainWindow.reload();
mainWindow.webContents.reloadIgnoringCache();
}
// Sleep function
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// Git action function
function GitAction() {
const gitAction = require("child_process").exec(`${process.resourcesPath}/Bin/git-action`, shellCallback);
gitAction.stdout.pipe(process.stdout)
gitAction.on("exit", () => console.log("Git action finished"))
}
// ZIP action function
function ZipAction() {
const zipAction = require("child_process").exec(`${process.resourcesPath}/Bin/zip-action`, shellCallback);
zipAction.stdout.pipe(process.stdout)
zipAction.on("exit", () => console.log("ZIP action finished"))
}
// Callback
function shellCallback(error, stdout, stderr) {
//console.log(error, stdout)
console.log("error: " + error)
//console.log("stdout: " + stdout)
//console.log("stderr: " + stderr)
}
// Execute bash script
//const exec = require("child_process").exec;
//exec("/Users/aronsommer/Documents/Xcode-Projects/CocoaWebView/Resources/caddy-start", shellCallback);
// Execute bash script and than do something if finished
// CAUTION!!! ${process.resourcesPath} does not work when yarn start, only works with app in dist folder
//const child = require("child_process").exec(`${process.resourcesPath}/Bin/test`, shellCallback);
//child.stdout.pipe(process.stdout)
//child.on("exit", () => console.log("guguuus fertig"))
// Load file from Resources folder
// CAUTION!!! ${process.resourcesPath} does not work when yarn start, only works with app in dist folder
//mainWindow.loadURL(`file:///${process.resourcesPath}/Content/index.html`);
//console.log(`file:///${process.resourcesPath}/Content/index.html`);
It looks like a scoping problem.
At the very end of your createWindow() function add the following line.
function createWindow() {
...
return mainWindow; // <-- Add this line
}
In your app.whenReady() function edit the following line.
app.whenReady().then(() => {
// windowURL = `file:///${process.resourcesPath}/Content/index.html`;
mainWindow = createWindow(); // <-- Edit this line
If functions in your above code are seperated into their own files then you can do something similar as shown below.
main.js
const { app, BrowserWindow } = require('electron')
const appMainWindow = require('mainWindow');
let mainWindow;
app.whenReady().then(() => {
mainWindow = appMainWindow.createWindow();
// Re-activate Application when in macOS dock.
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) { appMainWindow.createWindow(); }
})
})
mainWindow.js
let mainWindow;
function createWindow() {
...
return mainWindow;
}
function getWindow() {
return mainWindow;
}
module.exports = {createWindow, getWindow};
startCaddy.js
const appMainWindow = require('mainWindow');
function StartCaddy() {
...
appMainWindow.getWindow().loadURL("http://127.0.0.1:2015/");
...
}

don't display notification in ios device when use react-native-fcm

i'm using from react-native-fcm for recieve pushNotification and do all config in this document(https://github.com/evollu/react-native-fcm)
in ios device only recieve notification and call notificationListener that checked by console.log but dont display notification message and alert even test FCM.presentLocalNotification for show local notification still dont show notification
async componentDidMount() {
if (Platform.OS === 'ios') {
try {
const result = await FCM.requestPermissions({ badge: false, sound: true, alert: true });
} catch (e) {
console.error(e);
}
FCM.getFCMToken().then(token => {
if (token !== undefined) {
// this.props.onChangeToken(token);
} else {
console.log('TOKEN (getFCMToken)', 'null');
}
});
// FCM.getAPNSToken().then(token => {
// this.props.onAPNToken(token);
// });
FCM.getInitialNotification().then(notif => {
console.log('INITIAL NOTIFICATION', notif);
});
this.notificationListener = FCM.on(FCMEvent.Notification, async (notif) => {
console.log(" >> notificationListener: ", notif)
if (notif.local_notification) return;
FCM.presentLocalNotification({
body: 'tdtydt',
priority: "high",
title: 'notif.fcm.title',
sound: "default",
show_in_foreground: true,
local_notification: true,
icon: "ic_launcher",
status: "400"
});
});
this.refreshTokenListener = FCM.on(FCMEvent.RefreshToken, token => {
console.log('TOKEN (refreshUnsubscribe)', token);
this.props.onChangeToken(token);
});
FCM.enableDirectChannel();
this.channelConnectionListener = FCM.on(FCMEvent.DirectChannelConnectionChanged, (data) => {
console.log(`direct channel connected${data}`);
});
setTimeout(() => {
FCM.isDirectChannelEstablished().then(d => console.log('isDirectChannelEstablished', d));
}, 500);
}

Can I run `stencil push` command without prompt?

I'm trying to configure bitbucket pipelines with bigcommerce stencil.
The problem is the stencil push command asks some questions. I would like to auto-respond those questions.
Is that possible?
These are the questions prompted:
* Would you like to apply your theme to your store at http://xxxxxxx/? (y/N)
* Which variation would you like to apply?
- Light
- Bold
- Warm
You will need to make changes to the existing stencil-cli to make this work.
Stencil-cli uses the Commander package. My solution was to create an additional flag that would skip all the prompts if you supplied a variant name. This was created from stencil-cli version 1.13.1 so you may need to modify the example.
Inside /bin/stencil-push:
#!/usr/bin/env node
require('colors');
const apiHost = 'https://api.bigcommerce.com';
const dotStencilFilePath = './.stencil';
const options = { dotStencilFilePath };
const pkg = require('../package.json');
const Program = require('commander');
const stencilPush = require('../lib/stencil-push');
const versionCheck = require('../lib/version-check');
Program
.version(pkg.version)
.option('--host [hostname]', 'specify the api host', apiHost)
.option('-f, --file [filename]', 'specify the filename of the bundle to upload')
.option('-a, --activate [variationname]', 'specify the variation of the theme to activate')
.parse(process.argv);
if (!versionCheck()) {
return;
}
stencilPush(Object.assign({}, options, {
apiHost: Program.host || apiHost,
bundleZipPath: Program.file,
activate: Program.activate,
}), (err, result) => {
if (err) {
console.log('not ok'.red + ` -- ${err}`);
console.log('Please try again. If this error persists, please visit https://github.com/bigcommerce/stencil-cli/issues and submit an issue.');
} else {
console.log('ok'.green + ` -- ${result}`);
}
});
Inside /lib/stencil-push.js:
'use strict';
const _ = require('lodash');
const async = require('async');
const Bundle = require('./stencil-bundle');
const fs = require('fs');
const Inquirer = require('inquirer');
const os = require('os');
const ProgressBar = require('progress');
const themeApiClient = require('./theme-api-client');
const themePath = process.cwd();
const themeConfig = require('./theme-config').getInstance(themePath);
const uuid = require('uuid4');
const utils = {};
const Wreck = require('wreck');
const bar = new ProgressBar('Processing [:bar] :percent; ETA: :etas', {
complete: '=',
incomplete: ' ',
total: 100,
});
module.exports = utils;
function validateOptions(options, fields) {
options = options || {};
fields = fields || [];
fields.forEach(field => {
if (!_.has(options, field)) {
throw new Error(`${field} is required!`);
}
});
return options;
}
utils.readStencilConfigFile = (options, callback) => {
options = validateOptions(options, ['dotStencilFilePath']);
fs.readFile(options.dotStencilFilePath, { encoding: 'utf8' }, (err, data) => {
if (err) {
err.name = 'StencilConfigReadError';
return callback(err);
}
callback(null, Object.assign({}, options, {
config: JSON.parse(data),
}));
});
};
utils.getStoreHash = (options, callback) => {
options = validateOptions(options, ['config.normalStoreUrl']);
Wreck.get(`${options.config.normalStoreUrl}/admin/oauth/info`, { json: true, rejectUnauthorized: false }, (error, response, payload) => {
if (error) {
error.name = 'StoreHashReadError';
return callback(error);
}
if (response.statusCode !== 200 || !payload.store_hash) {
const err = new Error('Failed to retrieve store hash');
err.name = 'StoreHashReadError';
return callback(err);
}
callback(null, Object.assign({}, options, { storeHash: payload.store_hash }));
});
};
utils.getThemes = (options, callback) => {
const config = options.config;
themeApiClient.getThemes({
accessToken: config.accessToken,
apiHost: options.apiHost,
clientId: config.clientId,
storeHash: options.storeHash,
}, (error, result) => {
if (error) {
return callback(error);
}
callback(null, Object.assign({}, options, {
themes: result.themes,
}));
});
};
utils.generateBundle = (options, callback) => {
let bundle;
if (options.bundleZipPath) {
return async.nextTick(callback.bind(null, null, options));
}
bundle = new Bundle(themePath, themeConfig, themeConfig.getRawConfig(), {
dest: os.tmpdir(),
name: uuid(),
});
bundle.initBundle((err, bundleZipPath) => {
if (err) {
err.name = 'BundleInitError';
return callback(err);
}
callback(null, Object.assign(options, { bundleZipPath: options.bundleZipPath || bundleZipPath }));
});
};
utils.uploadBundle = (options, callback) => {
const config = options.config;
themeApiClient.postTheme({
accessToken: config.accessToken,
apiHost: options.apiHost,
bundleZipPath: options.bundleZipPath,
clientId: config.clientId,
storeHash: options.storeHash,
}, (error, result) => {
if (error) {
error.name = 'ThemeUploadError';
return callback(error);
}
callback(null, Object.assign({}, options, {
jobId: result.jobId,
themeLimitReached: !!result.themeLimitReached,
}));
});
};
utils.notifyUserOfThemeLimitReachedIfNecessary = (options, callback) => {
if (options.themeLimitReached) {
console.log('warning'.yellow + ` -- You have reached your upload limit. In order to proceed, you'll need to delete at least one theme.`);
}
return async.nextTick(callback.bind(null, null, options));
};
utils.promptUserToDeleteThemesIfNecessary = (options, callback) => {
if (!options.themeLimitReached) {
return async.nextTick(callback.bind(null, null, options));
}
const questions = [{
choices: options.themes.map(theme => ({
disabled: theme.is_active || !theme.is_private,
name: theme.name,
value: theme.uuid,
})),
message: 'Which theme(s) would you like to delete?',
name: 'themeIdsToDelete',
type: 'checkbox',
validate: (val) => {
if (val.length > 0) {
return true;
} else {
return 'You must delete at least one theme';
}
},
}];
Inquirer.prompt(questions, (answers) => {
callback(null, Object.assign({}, options, answers));
});
};
utils.deleteThemesIfNecessary = (options, callback) => {
const config = options.config;
if (!options.themeLimitReached) {
return async.nextTick(callback.bind(null, null, options));
}
async.parallel(options.themeIdsToDelete.map(themeId => {
return cb => {
themeApiClient.deleteThemeById(Object.assign({
accessToken: config.accessToken,
apiHost: options.apiHost,
clientId: config.clientId,
storeHash: options.storeHash,
themeId,
}, options), cb);
}
}), err => {
if (err) {
err.name = 'ThemeDeletionError';
return callback(err);
}
callback(null, options);
})
};
utils.uploadBundleAgainIfNecessary = (options, callback) => {
if (!options.themeLimitReached) {
return async.nextTick(callback.bind(null, null, options));
}
utils.uploadBundle(options, callback);
};
utils.notifyUserOfThemeUploadCompletion = (options, callback) => {
console.log('ok'.green + ' -- Theme Upload Finished');
return async.nextTick(callback.bind(null, null, options));
};
utils.markJobProgressPercentage = percentComplete => {
bar.update(percentComplete / 100);
};
utils.markJobComplete = () => {
utils.markJobProgressPercentage(100);
console.log('ok'.green + ' -- Theme Processing Finished');
};
utils.pollForJobCompletion = () => {
return async.retryable({
interval: 1000,
errorFilter: err => {
if (err.name === "JobCompletionStatusCheckPendingError") {
utils.markJobProgressPercentage(err.message);
return true;
}
return false;
},
times: Number.POSITIVE_INFINITY,
}, utils.checkIfJobIsComplete);
};
utils.checkIfJobIsComplete = (options, callback) => {
const config = options.config;
themeApiClient.getJob({
accessToken: config.accessToken,
apiHost: options.apiHost,
clientId: config.clientId,
storeHash: options.storeHash,
bundleZipPath: options.bundleZipPath,
jobId: options.jobId,
}, (error, result) => {
if (error) {
return callback(error);
}
utils.markJobComplete();
callback(null, Object.assign({}, options, result));
});
};
utils.promptUserWhetherToApplyTheme = (options, callback) => {
if (options.activate) {
callback(null, Object.assign({}, options, { applyTheme: true }));
} else {
const questions = [{
type: 'confirm',
name: 'applyTheme',
message: `Would you like to apply your theme to your store at ${options.config.normalStoreUrl}?`,
default: false,
}];
Inquirer.prompt(questions, answers => {
callback(null, Object.assign({}, options, { applyTheme: answers.applyTheme }));
});
};
};
utils.getVariations = (options, callback) => {
if (!options.applyTheme) {
return async.nextTick(callback.bind(null, null, options));
}
themeApiClient.getVariationsByThemeId({
accessToken: options.accessToken,
apiHost: options.apiHost,
clientId: options.clientId,
themeId: options.themeId,
storeHash: options.storeHash,
}, (error, result) => {
if (error) {
return callback(error);
};
if (options.activate !== true && options.activate !== undefined) {
const findVariation = result.variations.find(item => item.name === options.activate);
callback(null, Object.assign({}, options, { variationId: findVariation.uuid }));
} else if (options.activate === true) {
callback(null, Object.assign({}, options, { variationId: result.variations[0].uuid }));
} else {
callback(null, Object.assign({}, options, result));
};
});
};
utils.promptUserForVariation = (options, callback) => {
if (!options.applyTheme) {
return async.nextTick(callback.bind(null, null, options))
}
if (options.variationId) {
callback(null, options);
} else {
const questions = [{
type: 'list',
name: 'variationId',
message: 'Which variation would you like to apply?',
choices: options.variations.map(variation => ({ name: variation.name, value: variation.uuid })),
}];
Inquirer.prompt(questions, answers => {
console.log(answers);
callback(null, Object.assign({}, options, answers));
});
};
};
utils.requestToApplyVariationWithRetrys = () => {
return async.retryable({
interval: 1000,
errorFilter: err => {
if (err.name === "VariationActivationTimeoutError") {
console.log('warning'.yellow + ` -- Theme Activation Timed Out; Retrying...`);
return true;
}
return false;
},
times: 3,
}, utils.requestToApplyVariation);
};
utils.requestToApplyVariation = (options, callback) => {
if (!options.applyTheme) {
return async.nextTick(callback.bind(null, null, options));
}
themeApiClient.activateThemeByVariationId({
accessToken: options.accessToken,
apiHost: options.apiHost,
clientId: options.clientId,
storeHash: options.storeHash,
variationId: options.variationId,
}, (error, result) => {
if (error) {
return callback(error);
}
callback(null, Object.assign({}, options, result));
});
};
utils.notifyUserOfCompletion = (options, callback) => {
callback(null, 'Stencil Push Finished');
};
This allowed me to use something like stencil push --activate bold to specify a variation and skip all of the prompts.
As of version 1.15.1 this seems to be available with the -a, --activate [variationname] switch for stencil push
> stencil push -a "My Variant" worked for me
Thanks Nikita Puza!
It works like a charm. I applied the changes on stencil 1.14.1 version and the source code looks exactly the same.
The only difference is the second file is called stencil-push.utils.js instead of stencil-push.js

Resources