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:
parent
98e0167038
commit
5437c28b25
66
main/account_switcher.ts
Normal file
66
main/account_switcher.ts
Normal 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;
|
||||
}
|
||||
}
|
30
main/app.ts
30
main/app.ts
@ -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
189
main/default_menu.ts
Normal 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);
|
||||
}
|
@ -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);
|
||||
})
|
||||
|
@ -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
1
typings/ipc.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
type IpcChannel
|
||||
= 'mstdn:config'
|
||||
| 'mstdn:change-account'
|
||||
;
|
||||
|
Loading…
x
Reference in New Issue
Block a user