I am using node-auto-launch to launch my application after computer is restarted. This application is only for windows. I want this application by default to be launched minimized as it works in the background. HOw can I achieve this?
let bizAnalystAutoLauncher = new AutoLaunch({
name: 'BizAnalystDesktop'
});
bizAnalystAutoLauncher.enable();
bizAnalystAutoLauncher.isEnabled()
.then(function (isEnabled: boolean) {
if (isEnabled) {
return;
}
bizAnalystAutoLauncher.enable();
})
.catch(function (err: any) {
// handle error
console.log(err);
});
I don't want the application to be hidden. The application icon should be visible in the system tray in the taskbar.
So you want to have some kind of "minimize to tray" behaviour.
Initialize your app the usual way but instead of mainWindow.show() you call mainWindow.minimize() after initializing the mainWindow, then add EventListeners for the mainWiondw's minimize and restore events to hide or show the taskbar icon for your app via mainWindow.setSkipTaskbar():
...
mainWindow.on('restore', () => {
mainWindow.setSkipTaskbar(false)
})
mainWindow.on('minimize', () => {
mainWindow.setSkipTaskbar(true)
})
...
Add a Tray menu like in the documentation but make sure you add a menu item to restore the app window, otherwise you will end up with an app that is not accessible after it is minimized:
...
const trayMenu = Menu.buildFromTemplate([
{
label: 'Show',
click: () => {
mainWindow.restore()
}
},
{
label: 'Quit',
role: 'quit'
}
])
tray.setContextMenu(trayMenu)
...
The way I would do it is I would create a shortcut in the start menu Programs > startup with an argument instead of using node-auto-launch. Then when the app runs check for that argument in process.argv.
So to create a start menu shortcut with an argument of startMinimized you can use this module called windows-shortcuts
require('windows-shortcuts').create(
'%APPDATA%/Microsoft/Windows/Start Menu/Programs/Startup/myApp.lnk', {
target: process.execPath,
args: 'startMinimized',
icon: 'path/to/icon'
}, function (err) {
if (err) {
throw Error(err);
}
}
);
Then you could write some script like this to minimize the window at startup:
var startMinimized = false;
if (process.argv[2] && process.argv[2].indexOf('startMinimized') != -1) {
startMinimized = true;
}
var mainWindow = new BrowserWindow({show: !startMinimized});
if (startMinimized) {
mainWindow.minimize();
}
process.argv is an array of arguments the app starts with. The first one is the .exe path. The second is the squirrel argument.
Related
I've been trying to get deeplinks to work to do oauth for like 4 weeks:
on different boilerplates
on raw quick start of electron
on electron-forge with webpack.
None of them work while in dev mode, and only does while packaged.
I do not understand why it only works if I target command line the electron.exe and pass my app. OR when the app is packaged. If it's in development mode, it simply does not work.
I think? that the gotTheLock runs app.quit(), because in instances where I put the createWindow() outside of the lock if/else function, a window briefly opens and then closes immediately. For some reason it's opening a new instance completely? When createWindow() is inside, I basically see nothing happen.
I'm on Windows, I see nothing about forge that would cause issues or need extra setup in the forge config/package.json since I am on Windows
There's something fundamental flying over my head. Currently, my latest attempt is with the new 6.0 electron-forge from here with the following npm command to generate:
npm init electron-app#latest my-app --template=webpack cd my-app npm start
Here is my "main" which is called index.js here.
index.js
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
}
let mainWindow;
if (process.defaultApp) {
if (process.argv.length >= 2) {
app.setAsDefaultProtocolClient('electron-fiddle', process.execPath, [path.resolve(process.argv[1])])
}
} else {
app.setAsDefaultProtocolClient('electron-fiddle')
}
const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
app.quit()
} else {
app.on('second-instance', (event, commandLine, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (mainWindow) {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
}
})
app.on('ready', createWindow);
}
// 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.
// 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', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X 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();
}
});
// 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 import them here.
ipcMain.on('shell:open', () => {
const pageDirectory = __dirname.replace('app.asar', 'app.asar.unpacked')
const pagePath = path.join('file://', pageDirectory, 'index.html')
shell.openExternal(pagePath)
})
I've tried so many iterations i'm honestly just lost. What's wrong with this that the electron-forge dev mode doesn't like?
As far as any other code is concerned, it's "as-is" from that npm command with the latest 6.0 electron-forge with webpack template. Only the index.js was modified
I recently follow a project electron on geeksforgeeks: https://www.geeksforgeeks.org/create-a-geeksforgeeks-wrapper-application-using-electron/
Here is its code on Github https://github.com/sayantanm19/geeksforgeeks-desktop/blob/master/index.js
The function appenItemToMenu doesn't work, which means I could save pages offline but the savedpagelist on menu bar is always empty ...
Could you try it on your machine and point out the bug?
function appendItemToMenu(filename) {
curr_menu = Menu.getApplicationMenu()
.getMenuItemById("saved").submenu
curr_menu.append(
new MenuItem({
label: path.basename(filename, '.html'),
click() {
console.log('Saved page opened')
win.loadFile(savedFolder + path.basename(filename))
}
}))
}
function appendItemToMenu(filename) {
const newMenu = Menu.getApplicationMenu()
curr_menu = newMenu.getMenuItemById('saved').submenu
curr_menu.append(
new MenuItem({
label: path.basename(filename, '.html'),
click() {
console.log('Saved page opened')
win.loadFile(savedFolder + path.basename(filename))
}
}))
Menu.setApplicationMenu(newMenu)
}
Update the application menu after change. Any dynamic change or append Item not allowed.
I want to be able to run a function when a user goes into the app. I thought useEffect would already do something like this, say if I choose to change my phone's Clipboard (copy different texts from another app). But its not rerendering the component.
const [clipped, setClipboard] = useState();
const [appView, setAppView] = useState(AppState.currentState);
const getFromClipboard = useCallback(() => {
Clipboard.getString().then(content => {
console.log('content:', content)
setClipboard(content);
});
}, [clipped]);
useEffect(() => {
getFromClipboard();
}, [getFromClipboard, clipped, appView]);
I would assume that every time I copy a new text from a different app, into my clipboard, and then I go back to this app, because the state changed in clipped, it will rerender the useEffect? Unfortunately, its not calling the console log after first initial load of the component.
I stumbled across AppState, and thought I might be able to give this a shot, but not sure how to set this up with useEffect?
You can set an event listener for app state change it will trigger when the app is closed, moved to background or foreground, You can check the documentation for more details link:
useEffect(() => {
AppState.addEventListener('change', handleChange);
return () => {
AppState.removeEventListener('change', handleChange);
}
}, []);
const handleChange = (newState) => {
if (newState === "active") {
getFromClipboard();
}
}
I have a custom generator and am writing some tests for it. Before the app.run() call I already have a app.options['skip-install'] = true to prevent npm from running. But I need it to auto-overwrite files too.
Part way through the install I get a [?] Overwrite client/file.js? (Ynaxdh) and need it to auto-answer it for me.
I have tried app.options.force = true but that doesn't seem to do anything.
I'm running in app install with this:
function installApp(obj, opts, done) {
helpers.mockPrompt(obj.app, opts);
obj.app.options['skip-install'] = true;
obj.app.run({}, function () {
async.series([
function (cb) {
if (opts._extras.addPage) {
installAppPage(obj, cb);
} else {
cb();
}
}
], done);
});
}
Then I want to run a sub-generator with this:
function installAppPage(obj, done) {
helpers.mockPrompt(obj.page, {
pageName: 'webPage',
pageType: 'web'
});
obj.page.args = ['--force']; // This isn't working
obj.page.options.force; // This isn't working either
obj.page.run([], function () {
helpers.mockPrompt(obj.page, {
pageName: 'mobilePage',
pageType: 'mobile'
});
obj.page.run({}, function () {
done();
});
});
}
The sub-generator for the page modifies a file. I need to to just overwrite it so I can test it. How do I force it? I can't be prompted while running the tests, it needs to be automated.
I think you're looking for conflicter.force
obj.page.conflicter.force = true;
I am having a hard time with the new Alpha release of JQM not showing nested popups. For example, I am displaying a popup form that the user is supposed to fill out, if the server side validation fails, I want to display an error popup. I am finding that the error popup is not being shown. I suspect that it is being shown below the original popup.
function bindSongWriterInvite() {
// Bind the click event of the invite button
$("#invite-songwriter").click(function () {
$("#songwriter-invite-popup").popup("open");
});
// Bind the invitation click event of the invite modal
$("#songwriter-invite-invite").click(function () {
if ($('#songwriter-invite').valid()) {
$('#songwriter-invite-popup').popup('close');
$.ajax({
type: 'POST',
async: true,
url: '/songwriter/jsoncreate',
data: $('#songwriter-invite').serialize() + "&songName=" + $("#Song_Title").val(),
success: function (response) {
if (response.state != "success") {
alert("Should be showing error dialog");
mobileBindErrorDialog(response.message);
}
else {
mobileBindErrorDialog("Success!");
}
},
failure: function () {
mobileBindErrorDialog("Failure!");
},
dataType: 'json'
});
}
});
// Bind the cancel click event of the invite modal
$("#songwriter-invite-cancel").click(function () {
$("#songwriter-invite-popup").popup("close");
});
}
function mobileBindErrorDialog(errorMessage) {
// Close all open popups. This is a work around as the JQM Alpha does not
// open new popups in front of all other popups.
var error = $("<div></div>").append("<p>" + errorMessage + "</p>")
.popup();
$(error).popup("open");
}
So, you can see that I attempt to show the error dialog regardless of whether the ajax post succeeds or fails. It just never shows up.
Anyone have any thoughts?
Mike
I found a solution!
I set a delay on the bindErrorPopup function to wait until the popup close animation completes.
function mobileBindErrorDialog(errorMessage, delay) {
var error = $("<div></div>").attr({
'data-rel': "popup",
'data-theme': "a"
});
$(error).append("<p>" + errorMessage + "</p>")
.popup({
overlayTheme: "a",
positionTo: "window",
theme: "a"
});
setTimeout(function () {
$(error).popup("open");
}, delay);
}
Works awesome!