mirror of
https://github.com/rhysd/Mstdn.git
synced 2025-01-21 20:52:11 +01:00
use App as mediator and separate browser window as Window class
because window needs to be reopened for switching account
This commit is contained in:
parent
5437c28b25
commit
d9bdb4571d
@ -1,5 +1,5 @@
|
||||
import {EventEmitter} from 'events';
|
||||
import {Menu, MenuItem, session} from 'electron';
|
||||
import {Menu, MenuItem} from 'electron';
|
||||
import log from './log';
|
||||
import {Account} from './config';
|
||||
|
||||
|
195
main/app.ts
195
main/app.ts
@ -1,21 +1,18 @@
|
||||
import * as path from 'path';
|
||||
import {app, BrowserWindow, globalShortcut, Tray, shell, dialog, Menu} from 'electron';
|
||||
import windowState = require('electron-window-state');
|
||||
import * as menubar from 'menubar';
|
||||
import {app, Menu} from 'electron';
|
||||
import log from './log';
|
||||
import {Config} from './config';
|
||||
import AccountSwitcher, {partitionForAccount} from './account_switcher';
|
||||
import AccountSwitcher from './account_switcher';
|
||||
import defaultMenu from './default_menu';
|
||||
import Window from './window';
|
||||
|
||||
const IS_DEBUG = process.env.NODE_ENV === 'development';
|
||||
const IS_DARWIN = process.platform === 'darwin';
|
||||
const APP_ICON = path.join(__dirname, '..', 'resources', 'icon', 'icon.png');
|
||||
const PRELOAD_JS = path.join(__dirname, '..', 'renderer', 'preload.js');
|
||||
|
||||
export class App {
|
||||
private switcher: AccountSwitcher;
|
||||
|
||||
constructor(private win: Electron.BrowserWindow, private config: Config) {
|
||||
constructor(private win: Window, private config: Config) {
|
||||
if (config.accounts.length === 0) {
|
||||
throw new Error('No account found. Please check the config.');
|
||||
}
|
||||
@ -23,191 +20,17 @@ export class App {
|
||||
app.dock.setIcon(APP_ICON);
|
||||
}
|
||||
Menu.setApplicationMenu(defaultMenu());
|
||||
this.switcher = new AccountSwitcher(win, this.config.accounts);
|
||||
this.switcher = new AccountSwitcher(win.browser, this.config.accounts);
|
||||
this.switcher.on('did-switch', () => this.open());
|
||||
|
||||
if (!IS_DARWIN) {
|
||||
// Users can still access menu bar with pressing Alt key.
|
||||
this.win.setMenu(Menu.getApplicationMenu());
|
||||
}
|
||||
|
||||
this.win.webContents.on('will-navigate', (e, url) => {
|
||||
if (!url.startsWith(`https://${this.switcher.current.host}`)) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(url);
|
||||
}
|
||||
});
|
||||
this.win.webContents.on('new-window', (e, url) => {
|
||||
e.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
}
|
||||
|
||||
open() {
|
||||
this.win.loadURL(`https://${this.switcher.current.host}${this.switcher.current.default_page}`);
|
||||
|
||||
this.win.webContents.session.setPermissionRequestHandler((contents, permission, callback) => {
|
||||
if (permission !== 'geolocation' && permission !== 'media') {
|
||||
// Granted
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: ['Accept', 'Reject'],
|
||||
message: `Permission '${permission}' is requested by ${contents.getURL()}`,
|
||||
detail: "Please choose one of 'Accept' or 'Reject'",
|
||||
}, (buttonIndex: number) => {
|
||||
const granted = buttonIndex === 0;
|
||||
callback(granted);
|
||||
});
|
||||
});
|
||||
const url = `https://${this.switcher.current.host}${this.switcher.current.default_page}`;
|
||||
this.win.open(url);
|
||||
log.debug('Open URL: ', url);
|
||||
}
|
||||
}
|
||||
|
||||
function trayIcon(color: string) {
|
||||
return path.join(__dirname, '..', 'resources', 'icon', `tray-icon-${
|
||||
color === 'white' ? 'white' : 'black'
|
||||
}@2x.png`);
|
||||
}
|
||||
|
||||
export default function startApp(config: Config) {
|
||||
return (config.normal_window ? startNormalWindow : startMenuBar)(config)
|
||||
.then(win => new App(win, config));
|
||||
}
|
||||
|
||||
function startNormalWindow(config: Config): Promise<Electron.BrowserWindow> {
|
||||
log.debug('Setup a normal window');
|
||||
return new Promise<Electron.BrowserWindow>(resolve => {
|
||||
const state = windowState({
|
||||
defaultWidth: 600,
|
||||
defaultHeight: 800,
|
||||
});
|
||||
const win = new BrowserWindow({
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
x: state.x,
|
||||
y: state.y,
|
||||
icon: APP_ICON,
|
||||
show: false,
|
||||
useContentSize: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
preload: PRELOAD_JS,
|
||||
partition: partitionForAccount(config.accounts[0]),
|
||||
},
|
||||
});
|
||||
win.once('ready-to-show', () => {
|
||||
win.show();
|
||||
});
|
||||
win.once('closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
if (state.isFullScreen) {
|
||||
win.setFullScreen(true);
|
||||
} else if (state.isMaximized) {
|
||||
win.maximize();
|
||||
}
|
||||
state.manage(win);
|
||||
|
||||
const toggleWindow = () => {
|
||||
if (win.isFocused()) {
|
||||
log.debug('Toggle window: shown -> hidden');
|
||||
if (IS_DARWIN) {
|
||||
app.hide();
|
||||
} else {
|
||||
win.hide();
|
||||
}
|
||||
} else {
|
||||
log.debug('Toggle window: hidden -> shown');
|
||||
win.show();
|
||||
}
|
||||
};
|
||||
|
||||
win.webContents.on('dom-ready', () => {
|
||||
log.debug('Send config to renderer procress');
|
||||
win.webContents.send('mstdn:config', config);
|
||||
});
|
||||
win.webContents.once('dom-ready', () => {
|
||||
log.debug('Normal window application was launched');
|
||||
if (config.hot_key) {
|
||||
globalShortcut.register(config.hot_key, toggleWindow);
|
||||
log.debug('Hot key was set to:', config.hot_key);
|
||||
}
|
||||
if (IS_DEBUG) {
|
||||
win.webContents.openDevTools({mode: 'detach'});
|
||||
}
|
||||
});
|
||||
|
||||
const normalIcon = trayIcon(config.icon_color);
|
||||
const tray = new Tray(normalIcon);
|
||||
tray.on('click', toggleWindow);
|
||||
tray.on('double-click', toggleWindow);
|
||||
if (IS_DARWIN) {
|
||||
tray.setHighlightMode('never');
|
||||
}
|
||||
|
||||
resolve(win);
|
||||
});
|
||||
}
|
||||
|
||||
function startMenuBar(config: Config): Promise<Electron.BrowserWindow> {
|
||||
log.debug('Setup a menubar window');
|
||||
return new Promise<Electron.BrowserWindow>(resolve => {
|
||||
const state = windowState({
|
||||
defaultWidth: 350,
|
||||
defaultHeight: 420,
|
||||
});
|
||||
const icon = trayIcon(config.icon_color);
|
||||
const mb = menubar({
|
||||
icon,
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
alwaysOnTop: IS_DEBUG || !!config.always_on_top,
|
||||
tooltip: 'Mstdn',
|
||||
useContentSize: true,
|
||||
autoHideMenuBar: true,
|
||||
show: false,
|
||||
showDockIcon: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
preload: PRELOAD_JS,
|
||||
partition: partitionForAccount(config.accounts[0]),
|
||||
},
|
||||
});
|
||||
mb.once('ready', () => mb.showWindow());
|
||||
mb.once('after-create-window', () => {
|
||||
log.debug('Menubar application was launched');
|
||||
if (config.hot_key) {
|
||||
globalShortcut.register(config.hot_key, () => {
|
||||
if (mb.window.isFocused()) {
|
||||
log.debug('Toggle window: shown -> hidden');
|
||||
mb.hideWindow();
|
||||
} else {
|
||||
log.debug('Toggle window: hidden -> shown');
|
||||
mb.showWindow();
|
||||
}
|
||||
});
|
||||
log.debug('Hot key was set to:', config.hot_key);
|
||||
}
|
||||
if (IS_DEBUG) {
|
||||
mb.window.webContents.openDevTools({mode: 'detach'});
|
||||
}
|
||||
mb.window.webContents.on('dom-ready', () => {
|
||||
log.debug('Send config to renderer procress');
|
||||
mb.window.webContents.send('mstdn:config', config);
|
||||
});
|
||||
state.manage(mb.window);
|
||||
|
||||
resolve(mb.window);
|
||||
});
|
||||
mb.once('after-close', () => {
|
||||
app.quit();
|
||||
});
|
||||
});
|
||||
return Window.create(config).then(win => new App(win, config));
|
||||
}
|
||||
|
214
main/window.ts
Normal file
214
main/window.ts
Normal file
@ -0,0 +1,214 @@
|
||||
import * as path from 'path';
|
||||
import {app, BrowserWindow, globalShortcut, Tray, shell, dialog, Menu} from 'electron';
|
||||
import windowState = require('electron-window-state');
|
||||
import * as menubar from 'menubar';
|
||||
import {Config, Account} from './config';
|
||||
import {partitionForAccount} from './account_switcher';
|
||||
import log from './log';
|
||||
|
||||
const IS_DEBUG = process.env.NODE_ENV === 'development';
|
||||
const IS_DARWIN = process.platform === 'darwin';
|
||||
const APP_ICON = path.join(__dirname, '..', 'resources', 'icon', 'icon.png');
|
||||
const PRELOAD_JS = path.join(__dirname, '..', 'renderer', 'preload.js');
|
||||
|
||||
export default class Window {
|
||||
static create(config: Config) {
|
||||
return (config.normal_window ? startNormalWindow : startMenuBar)(config);
|
||||
}
|
||||
|
||||
constructor(
|
||||
public browser: Electron.BrowserWindow,
|
||||
public account: Account,
|
||||
public menubar: Menubar.MenubarApp | null,
|
||||
) {
|
||||
if (!IS_DARWIN) {
|
||||
// Users can still access menu bar with pressing Alt key.
|
||||
browser.setMenu(Menu.getApplicationMenu());
|
||||
}
|
||||
|
||||
browser.webContents.on('will-navigate', (e, url) => {
|
||||
if (!url.startsWith(`https://${this.account.host}`)) {
|
||||
e.preventDefault();
|
||||
shell.openExternal(url);
|
||||
}
|
||||
});
|
||||
browser.webContents.on('new-window', (e, url) => {
|
||||
e.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
|
||||
browser.webContents.session.setPermissionRequestHandler((contents, permission, callback) => {
|
||||
if (permission !== 'geolocation' && permission !== 'media') {
|
||||
// Granted
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.showMessageBox({
|
||||
type: 'question',
|
||||
buttons: ['Accept', 'Reject'],
|
||||
message: `Permission '${permission}' is requested by ${contents.getURL()}`,
|
||||
detail: "Please choose one of 'Accept' or 'Reject'",
|
||||
}, (buttonIndex: number) => {
|
||||
const granted = buttonIndex === 0;
|
||||
callback(granted);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
open(url: string) {
|
||||
this.browser.loadURL(url);
|
||||
}
|
||||
|
||||
isMenubar() {
|
||||
return this.browser !== null;
|
||||
}
|
||||
|
||||
close() {
|
||||
log.error('TODO: close window');
|
||||
}
|
||||
}
|
||||
|
||||
function trayIcon(color: string) {
|
||||
return path.join(__dirname, '..', 'resources', 'icon', `tray-icon-${
|
||||
color === 'white' ? 'white' : 'black'
|
||||
}@2x.png`);
|
||||
}
|
||||
|
||||
function startNormalWindow(config: Config): Promise<Window> {
|
||||
log.debug('Setup a normal window');
|
||||
return new Promise<Window>(resolve => {
|
||||
const state = windowState({
|
||||
defaultWidth: 600,
|
||||
defaultHeight: 800,
|
||||
});
|
||||
const account = config.accounts[0];
|
||||
const win = new BrowserWindow({
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
x: state.x,
|
||||
y: state.y,
|
||||
icon: APP_ICON,
|
||||
show: false,
|
||||
useContentSize: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
preload: PRELOAD_JS,
|
||||
partition: partitionForAccount(account),
|
||||
},
|
||||
});
|
||||
win.once('ready-to-show', () => {
|
||||
win.show();
|
||||
});
|
||||
win.once('closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
if (state.isFullScreen) {
|
||||
win.setFullScreen(true);
|
||||
} else if (state.isMaximized) {
|
||||
win.maximize();
|
||||
}
|
||||
state.manage(win);
|
||||
|
||||
const toggleWindow = () => {
|
||||
if (win.isFocused()) {
|
||||
log.debug('Toggle window: shown -> hidden');
|
||||
if (IS_DARWIN) {
|
||||
app.hide();
|
||||
} else {
|
||||
win.hide();
|
||||
}
|
||||
} else {
|
||||
log.debug('Toggle window: hidden -> shown');
|
||||
win.show();
|
||||
}
|
||||
};
|
||||
|
||||
win.webContents.on('dom-ready', () => {
|
||||
log.debug('Send config to renderer procress');
|
||||
win.webContents.send('mstdn:config', config);
|
||||
});
|
||||
win.webContents.once('dom-ready', () => {
|
||||
log.debug('Normal window application was launched');
|
||||
if (config.hot_key) {
|
||||
globalShortcut.register(config.hot_key, toggleWindow);
|
||||
log.debug('Hot key was set to:', config.hot_key);
|
||||
}
|
||||
if (IS_DEBUG) {
|
||||
win.webContents.openDevTools({mode: 'detach'});
|
||||
}
|
||||
});
|
||||
|
||||
const normalIcon = trayIcon(config.icon_color);
|
||||
const tray = new Tray(normalIcon);
|
||||
tray.on('click', toggleWindow);
|
||||
tray.on('double-click', toggleWindow);
|
||||
if (IS_DARWIN) {
|
||||
tray.setHighlightMode('never');
|
||||
}
|
||||
|
||||
resolve(new Window(win, account, null));
|
||||
});
|
||||
}
|
||||
|
||||
function startMenuBar(config: Config): Promise<Window> {
|
||||
log.debug('Setup a menubar window');
|
||||
return new Promise<Window>(resolve => {
|
||||
const state = windowState({
|
||||
defaultWidth: 350,
|
||||
defaultHeight: 420,
|
||||
});
|
||||
const account = config.accounts[0];
|
||||
const icon = trayIcon(config.icon_color);
|
||||
const mb = menubar({
|
||||
icon,
|
||||
width: state.width,
|
||||
height: state.height,
|
||||
alwaysOnTop: IS_DEBUG || !!config.always_on_top,
|
||||
tooltip: 'Mstdn',
|
||||
useContentSize: true,
|
||||
autoHideMenuBar: true,
|
||||
show: false,
|
||||
showDockIcon: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
preload: PRELOAD_JS,
|
||||
partition: partitionForAccount(account),
|
||||
},
|
||||
});
|
||||
mb.once('ready', () => mb.showWindow());
|
||||
mb.once('after-create-window', () => {
|
||||
log.debug('Menubar application was launched');
|
||||
if (config.hot_key) {
|
||||
globalShortcut.register(config.hot_key, () => {
|
||||
if (mb.window.isFocused()) {
|
||||
log.debug('Toggle window: shown -> hidden');
|
||||
mb.hideWindow();
|
||||
} else {
|
||||
log.debug('Toggle window: hidden -> shown');
|
||||
mb.showWindow();
|
||||
}
|
||||
});
|
||||
log.debug('Hot key was set to:', config.hot_key);
|
||||
}
|
||||
if (IS_DEBUG) {
|
||||
mb.window.webContents.openDevTools({mode: 'detach'});
|
||||
}
|
||||
mb.window.webContents.on('dom-ready', () => {
|
||||
log.debug('Send config to renderer procress');
|
||||
mb.window.webContents.send('mstdn:config', config);
|
||||
});
|
||||
state.manage(mb.window);
|
||||
|
||||
resolve(new Window(mb.window, account, mb));
|
||||
});
|
||||
mb.once('after-close', () => {
|
||||
app.quit();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user