1
0
mirror of https://github.com/rhysd/Mstdn.git synced 2025-01-21 20:52:11 +01:00

implement account switcher

This commit is contained in:
rhysd 2017-04-17 18:40:55 +09:00
parent 98e0167038
commit 5437c28b25
6 changed files with 290 additions and 16 deletions

66
main/account_switcher.ts Normal file
View File

@ -0,0 +1,66 @@
import {EventEmitter} from 'events';
import {Menu, MenuItem, session} from 'electron';
import log from './log';
import {Account} from './config';
export function partitionForAccount(account: Account) {
return `persist:mstdn:${account.name}:${account.host}`;
}
export default class AccountSwitcher extends EventEmitter {
accounts: Account[];
current: Account;
constructor(private win: Electron.BrowserWindow, accounts: Account[]) {
super();
const submenu = [] as Electron.MenuItemOptions[];
for (const account of accounts.filter(a => a.name !== '')) {
let name = account.name;
if (!name.startsWith('@')) {
name = '@' + name;
}
name += '@' + account.host;
submenu.push({
label: name,
type: 'radio',
checked: false,
click: () => this.switchAccountTo(account),
});
}
this.accounts = accounts;
if (accounts.length === 0) {
return;
}
submenu[0].checked = true;
this.current = accounts[0];
const item = new MenuItem({
label: 'Accounts',
type: 'submenu',
submenu,
});
const menu = Menu.getApplicationMenu();
// Insert item before 'Help'
menu.insert(menu.items.length - 1, item);
Menu.setApplicationMenu(menu);
}
switchAccountTo(account: Account) {
if (this.current.name === account.name && this.current.host === account.host) {
log.debug('Current account is already @' + account.name);
return;
}
log.debug('Switch to account', account);
this.emit('will-switch', account);
log.error('TODO: Switch partition');
this.win.webContents.send('mstdn:change-account', account);
this.emit('did-switch', account);
this.current = account;
}
}

View File

@ -3,7 +3,9 @@ import {app, BrowserWindow, globalShortcut, Tray, shell, dialog, Menu} from 'ele
import windowState = require('electron-window-state');
import * as menubar from 'menubar';
import log from './log';
import {Config, Account} from './config';
import {Config} from './config';
import AccountSwitcher, {partitionForAccount} from './account_switcher';
import defaultMenu from './default_menu';
const IS_DEBUG = process.env.NODE_ENV === 'development';
const IS_DARWIN = process.platform === 'darwin';
@ -11,27 +13,26 @@ const APP_ICON = path.join(__dirname, '..', 'resources', 'icon', 'icon.png');
const PRELOAD_JS = path.join(__dirname, '..', 'renderer', 'preload.js');
export class App {
private account: Account;
private switcher: AccountSwitcher;
constructor(private win: Electron.BrowserWindow, private config: Config) {
if (config.accounts.length === 0) {
throw new Error('No account found. Please check the config.');
}
this.account = this.config.accounts[0];
}
if (IS_DARWIN) {
app.dock.setIcon(APP_ICON);
}
Menu.setApplicationMenu(defaultMenu());
this.switcher = new AccountSwitcher(win, this.config.accounts);
this.switcher.on('did-switch', () => this.open());
open() {
if (!IS_DARWIN) {
// Users can still access menu bar with pressing Alt key.
this.win.setMenu(Menu.getApplicationMenu());
}
this.win.webContents.on('dom-ready', () => {
this.win.show();
});
this.win.loadURL(`https://${this.account.host}${this.account.default_page}`);
this.win.webContents.on('will-navigate', (e, url) => {
if (!url.startsWith(`https://${this.account.host}`)) {
if (!url.startsWith(`https://${this.switcher.current.host}`)) {
e.preventDefault();
shell.openExternal(url);
}
@ -40,6 +41,11 @@ export class App {
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
@ -91,6 +97,7 @@ function startNormalWindow(config: Config): Promise<Electron.BrowserWindow> {
nodeIntegration: false,
sandbox: true,
preload: PRELOAD_JS,
partition: partitionForAccount(config.accounts[0]),
},
});
win.once('ready-to-show', () => {
@ -142,7 +149,6 @@ function startNormalWindow(config: Config): Promise<Electron.BrowserWindow> {
tray.on('double-click', toggleWindow);
if (IS_DARWIN) {
tray.setHighlightMode('never');
app.dock.setIcon(APP_ICON);
}
resolve(win);
@ -163,13 +169,15 @@ function startMenuBar(config: Config): Promise<Electron.BrowserWindow> {
height: state.height,
alwaysOnTop: IS_DEBUG || !!config.always_on_top,
tooltip: 'Mstdn',
autoHideMenuBar: true,
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());

189
main/default_menu.ts Normal file
View File

@ -0,0 +1,189 @@
import * as path from 'path';
import {Menu, shell, app} from 'electron';
export default function defaultMenu() {
const template: Electron.MenuItemOptions[] = [
{
label: 'Edit',
submenu: [
{
label: 'Edit Config',
click() {
shell.openItem(path.join(app.getPath('userData'), 'config.json'));
}
},
{
type: 'separator'
},
{
role: 'undo'
},
{
role: 'redo'
},
{
type: 'separator'
},
{
role: 'cut'
},
{
role: 'copy'
},
{
role: 'paste'
},
{
role: 'pasteandmatchstyle'
},
{
role: 'delete'
},
{
role: 'selectall'
}
]
},
{
label: 'View',
submenu: [
{
role: 'reload'
},
{
role: 'toggledevtools'
},
{
type: 'separator'
},
{
role: 'resetzoom'
},
{
role: 'zoomin'
},
{
role: 'zoomout'
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
}
]
},
{
role: 'window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
}
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click () {
shell.openExternal('https://github.com/rhysd/Mstdn#readme');
}
},
{
label: 'Search Issues',
click () {
shell.openExternal('https://github.com/rhysd/Mstdn/issues');
}
}
]
}
];
if (process.platform === 'darwin') {
template.unshift({
label: 'Mstdn',
submenu: [
{
role: 'about'
},
{
type: 'separator'
},
{
role: 'services',
submenu: []
},
{
type: 'separator'
},
{
role: 'hide'
},
{
role: 'hideothers'
},
{
role: 'unhide'
},
{
type: 'separator'
},
{
role: 'quit'
}
]
});
(template[1].submenu as Electron.MenuItemOptions[]).push(
{
type: 'separator'
},
{
label: 'Speech',
submenu: [
{
role: 'startspeaking'
},
{
role: 'stopspeaking'
}
]
}
);
template[3].submenu = [
{
role: 'close'
},
{
role: 'minimize'
},
{
role: 'zoom'
},
{
type: 'separator'
},
{
role: 'front'
}
];
} else {
template.unshift(
{
label: 'File',
submenu: [
{
role: 'quit'
}
]
}
);
}
return Menu.buildFromTemplate(template);
}

View File

@ -1,5 +1,5 @@
import * as Mousetrap from 'mousetrap';
import {Config} from '../main/config';
import {Config, Account} from '../main/config';
import * as Ipc from './ipc';
import log from './log';
@ -52,8 +52,18 @@ function setupKeybinds(keybinds: {[key: string]: string}, host: string) {
}
}
Ipc.on('mstdn:config', (config: Config) => {
// TODO: Temporary. It should be fixed on supporting multi-account.
let config: Config | null = null;
Ipc.on('mstdn:config', (c: Config) => {
config = c;
const host = config.accounts[0].host;
setupKeybinds(config.keymaps, host);
});
Ipc.on('mstdn:change-account', (account: Account) => {
if (config === null) {
log.error('FATAL: config is null at receiving mstdn:change-account');
return;
}
setupKeybinds(config.keymaps, account.host);
})

View File

@ -5,7 +5,7 @@ const ipc = electron.ipcRenderer;
export function on(channel: IpcChannel, callback: (...args: any[]) => void) {
ipc.on(channel, (...args: any[]) => {
log.debug('IPC: Received from:', channel, args);
log.info('IPC: Received from:', channel, args);
callback(...args);
});
}

1
typings/ipc.d.ts vendored
View File

@ -1,3 +1,4 @@
type IpcChannel
= 'mstdn:config'
| 'mstdn:change-account'
;