2018-03-07 14:28:48 +01:00
|
|
|
'use strict'
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
import {
|
|
|
|
app,
|
|
|
|
ipcMain,
|
|
|
|
shell,
|
2019-10-27 05:29:49 +01:00
|
|
|
session,
|
2019-04-20 08:44:22 +02:00
|
|
|
Menu,
|
|
|
|
Tray,
|
|
|
|
BrowserWindow,
|
|
|
|
BrowserWindowConstructorOptions,
|
|
|
|
MenuItemConstructorOptions,
|
2019-10-23 16:07:20 +02:00
|
|
|
IpcMainEvent,
|
2019-07-21 17:19:23 +02:00
|
|
|
Notification,
|
2020-02-05 17:16:29 +01:00
|
|
|
NotificationConstructorOptions,
|
2020-03-31 14:14:24 +02:00
|
|
|
nativeTheme,
|
2022-09-04 15:48:47 +02:00
|
|
|
IpcMainInvokeEvent,
|
|
|
|
globalShortcut
|
2019-04-20 08:44:22 +02:00
|
|
|
} from 'electron'
|
2018-03-08 15:08:33 +01:00
|
|
|
import Datastore from 'nedb'
|
2019-04-16 13:38:02 +02:00
|
|
|
import { isEmpty } from 'lodash'
|
2018-03-24 02:57:41 +01:00
|
|
|
import log from 'electron-log'
|
2018-03-24 15:23:25 +01:00
|
|
|
import windowStateKeeper from 'electron-window-state'
|
2018-04-05 02:00:42 +02:00
|
|
|
import simplayer from 'simplayer'
|
|
|
|
import path from 'path'
|
2018-06-01 07:19:56 +02:00
|
|
|
import ContextMenu from 'electron-context-menu'
|
2019-04-15 18:35:20 +02:00
|
|
|
import { initSplashScreen, Config } from '@trodi/electron-splashscreen'
|
2018-08-10 17:40:06 +02:00
|
|
|
import openAboutWindow from 'about-window'
|
2022-02-24 13:16:30 +01:00
|
|
|
import generator, { Entity, detector, NotificationType, MegalodonInterface } from 'megalodon'
|
2019-07-21 17:19:23 +02:00
|
|
|
import sanitizeHtml from 'sanitize-html'
|
2019-09-23 12:31:25 +02:00
|
|
|
import AutoLaunch from 'auto-launch'
|
2021-01-24 11:01:34 +01:00
|
|
|
import minimist from 'minimist'
|
2018-03-08 15:08:33 +01:00
|
|
|
|
2018-03-08 09:41:39 +01:00
|
|
|
import Authentication from './auth'
|
2018-03-09 09:08:27 +01:00
|
|
|
import Account from './account'
|
2020-03-15 09:47:40 +01:00
|
|
|
import { StreamingURL, UserStreaming, DirectStreaming, LocalStreaming, PublicStreaming, ListStreaming, TagStreaming } from './websocket'
|
2018-04-07 15:40:57 +02:00
|
|
|
import Preferences from './preferences'
|
2018-09-25 18:02:36 +02:00
|
|
|
import Fonts from './fonts'
|
2018-06-01 07:19:56 +02:00
|
|
|
import Hashtags from './hashtags'
|
2020-01-11 13:48:22 +01:00
|
|
|
import i18next from '~/src/config/i18n'
|
|
|
|
import { i18n as I18n } from 'i18next'
|
2021-03-25 17:30:17 +01:00
|
|
|
import Language, { LanguageType } from '../constants/language'
|
2019-06-06 16:44:50 +02:00
|
|
|
import { LocalAccount } from '~/src/types/localAccount'
|
|
|
|
import { LocalTag } from '~/src/types/localTag'
|
2019-07-21 17:19:23 +02:00
|
|
|
import { Notify } from '~/src/types/notify'
|
2019-06-27 14:58:43 +02:00
|
|
|
import { StreamingError } from '~/src/errors/streamingError'
|
2019-07-30 17:17:30 +02:00
|
|
|
import HashtagCache from './cache/hashtag'
|
2019-08-08 16:39:27 +02:00
|
|
|
import AccountCache from './cache/account'
|
|
|
|
import { InsertAccountCache } from '~/src/types/insertAccountCache'
|
2019-10-22 17:08:56 +02:00
|
|
|
import { Proxy } from '~/src/types/proxy'
|
2019-10-27 05:29:49 +01:00
|
|
|
import ProxyConfiguration from './proxy'
|
2020-03-31 14:14:24 +02:00
|
|
|
import confirm from './timelines'
|
|
|
|
import { EnabledTimelines } from '~/src/types/enabledTimelines'
|
2020-06-08 15:48:52 +02:00
|
|
|
import { Menu as MenuPreferences } from '~/src/types/preference'
|
2021-06-10 05:03:51 +02:00
|
|
|
import { LocalMarker } from '~/src/types/localMarker'
|
|
|
|
import Marker from './marker'
|
2021-12-29 08:26:12 +01:00
|
|
|
import newDB from './database'
|
2022-01-01 10:39:33 +01:00
|
|
|
import Settings from './settings'
|
|
|
|
import { BaseSettings, Setting } from '~/src/types/setting'
|
2018-05-30 13:54:21 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Context menu
|
|
|
|
*/
|
2019-04-23 15:36:05 +02:00
|
|
|
ContextMenu({
|
|
|
|
showCopyImageAddress: true,
|
|
|
|
showSaveImageAs: true
|
|
|
|
})
|
2018-03-07 14:28:48 +01:00
|
|
|
|
2018-03-24 02:57:41 +01:00
|
|
|
/**
|
|
|
|
* Set log level
|
|
|
|
*/
|
|
|
|
log.transports.console.level = 'debug'
|
|
|
|
log.transports.file.level = 'info'
|
|
|
|
|
2019-04-15 18:35:20 +02:00
|
|
|
declare namespace global {
|
|
|
|
let __static: string
|
|
|
|
}
|
|
|
|
|
2018-03-07 14:28:48 +01:00
|
|
|
/**
|
|
|
|
* Set `__static` path to static files in production
|
|
|
|
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
|
|
|
|
*/
|
|
|
|
if (process.env.NODE_ENV !== 'development') {
|
2018-04-05 02:00:42 +02:00
|
|
|
global.__static = path.join(__dirname, '/static').replace(/\\/g, '\\\\')
|
2018-03-07 14:28:48 +01:00
|
|
|
}
|
|
|
|
|
2019-04-15 18:35:20 +02:00
|
|
|
let mainWindow: BrowserWindow | null
|
2019-04-18 09:15:50 +02:00
|
|
|
let tray: Tray | null
|
2020-11-30 14:50:31 +01:00
|
|
|
const winURL = process.env.NODE_ENV === 'development' ? `http://localhost:9080` : path.join('file://', __dirname, '/index.html')
|
2018-03-07 14:28:48 +01:00
|
|
|
|
2019-09-17 18:13:16 +02:00
|
|
|
// MAS build is not allowed requestSingleInstanceLock.
|
|
|
|
// ref: https://github.com/h3poteto/whalebird-desktop/issues/1030
|
|
|
|
// ref: https://github.com/electron/electron-osx-sign/issues/137#issuecomment-307626305
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
// Enforces single instance for linux and windows.
|
|
|
|
const gotTheLock = app.requestSingleInstanceLock()
|
2019-08-25 02:22:50 +02:00
|
|
|
|
2019-09-17 18:13:16 +02:00
|
|
|
if (!gotTheLock) {
|
|
|
|
app.quit()
|
|
|
|
} else {
|
|
|
|
app.on('second-instance', () => {
|
|
|
|
// Someone tried to run a second instance, we should focus our window.
|
|
|
|
if (mainWindow) {
|
|
|
|
if (mainWindow.isMinimized()) mainWindow.restore()
|
|
|
|
if (!mainWindow!.isVisible()) {
|
|
|
|
mainWindow!.show()
|
|
|
|
mainWindow!.setSkipTaskbar(false)
|
|
|
|
}
|
|
|
|
mainWindow.focus()
|
2019-08-25 02:22:50 +02:00
|
|
|
}
|
2019-09-17 18:13:16 +02:00
|
|
|
})
|
|
|
|
}
|
2019-08-25 02:22:50 +02:00
|
|
|
}
|
|
|
|
|
2021-09-05 16:57:05 +02:00
|
|
|
const appId = 'org.whalebird.desktop'
|
2019-07-23 18:14:43 +02:00
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
const splashURL =
|
|
|
|
process.env.NODE_ENV === 'development'
|
|
|
|
? path.resolve(__dirname, '../../static/splash-screen.html')
|
2020-11-30 14:50:31 +01:00
|
|
|
: path.join(__dirname, '/static/splash-screen.html')
|
2018-07-27 17:49:05 +02:00
|
|
|
|
2018-03-22 08:55:58 +01:00
|
|
|
// https://github.com/louischatriot/nedb/issues/459
|
2018-03-22 08:49:39 +01:00
|
|
|
const userData = app.getPath('userData')
|
2019-09-23 12:31:25 +02:00
|
|
|
const appPath = app.getPath('exe')
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
const accountDBPath = process.env.NODE_ENV === 'production' ? userData + '/db/account.db' : 'account.db'
|
2020-05-17 09:31:37 +02:00
|
|
|
const accountDB = new Datastore({
|
2018-04-07 15:40:57 +02:00
|
|
|
filename: accountDBPath,
|
2018-03-08 15:08:33 +01:00
|
|
|
autoload: true
|
|
|
|
})
|
2021-06-10 15:12:21 +02:00
|
|
|
const accountRepo = new Account(accountDB)
|
|
|
|
accountRepo.initialize().catch((err: Error) => log.error(err))
|
2018-10-11 16:12:22 +02:00
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
const hashtagsDBPath = process.env.NODE_ENV === 'production' ? userData + '/db/hashtags.db' : 'hashtags.db'
|
2020-05-17 09:31:37 +02:00
|
|
|
const hashtagsDB = new Datastore({
|
2018-06-01 07:19:56 +02:00
|
|
|
filename: hashtagsDBPath,
|
|
|
|
autoload: true
|
|
|
|
})
|
|
|
|
|
2022-01-01 10:39:33 +01:00
|
|
|
const settingsDBPath = process.env.NODE_ENV === 'production' ? userData + './db/settings.json' : 'settings.json'
|
2018-11-07 14:48:50 +01:00
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
const preferencesDBPath = process.env.NODE_ENV === 'production' ? userData + './db/preferences.json' : 'preferences.json'
|
2018-03-08 15:08:33 +01:00
|
|
|
|
2021-12-29 08:26:12 +01:00
|
|
|
const lokiDatabasePath = process.env.NODE_ENV === 'production' ? userData + '/db/lokiDatabase.db' : 'lokiDatabase.db'
|
|
|
|
|
|
|
|
let markerRepo: Marker | null = null
|
2021-06-10 05:03:51 +02:00
|
|
|
|
2019-07-30 17:17:30 +02:00
|
|
|
/**
|
|
|
|
* Cache path
|
|
|
|
*/
|
|
|
|
const hashtagCachePath = process.env.NODE_ENV === 'production' ? userData + '/cache/hashtag.db' : 'cache/hashtag.db'
|
2019-07-31 17:40:28 +02:00
|
|
|
const hashtagCache = new HashtagCache(hashtagCachePath)
|
2019-07-30 17:17:30 +02:00
|
|
|
|
2019-08-08 16:39:27 +02:00
|
|
|
const accountCachePath = process.env.NODE_ENV === 'production' ? userData + '/cache/account.db' : 'cache/account.db'
|
|
|
|
const accountCache = new AccountCache(accountCachePath)
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
const soundBasePath =
|
|
|
|
process.env.NODE_ENV === 'development' ? path.join(__dirname, '../../build/sounds/') : path.join(process.resourcesPath!, 'build/sounds/')
|
2021-05-22 14:50:45 +02:00
|
|
|
const iconBasePath =
|
|
|
|
process.env.NODE_ENV === 'development'
|
|
|
|
? path.resolve(__dirname, '../../build/icons/')
|
|
|
|
: path.resolve(process.resourcesPath!, 'build/icons/')
|
2018-04-07 16:18:20 +02:00
|
|
|
|
2019-09-25 18:32:52 +02:00
|
|
|
let launcher: AutoLaunch | null = null
|
2019-10-27 05:29:49 +01:00
|
|
|
const proxyConfiguration = new ProxyConfiguration(preferencesDBPath)
|
2019-09-25 18:32:52 +02:00
|
|
|
|
|
|
|
// On MAS build, auto launch is not working.
|
|
|
|
// We have to use Launch Agent: https://github.com/Teamwork/node-auto-launch/issues/43
|
|
|
|
// But it is too difficult to build, and Slack does not provide this function in MAS build.
|
|
|
|
// Therefore I don't provide this function for MacOS.
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
launcher = new AutoLaunch({
|
|
|
|
name: 'Whalebird',
|
|
|
|
path: appPath
|
|
|
|
})
|
|
|
|
}
|
2019-09-24 14:16:55 +02:00
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
async function listAccounts(): Promise<Array<LocalAccount>> {
|
2018-03-21 04:22:45 +01:00
|
|
|
try {
|
2021-06-10 15:12:21 +02:00
|
|
|
const accounts = await accountRepo.listAccounts()
|
2018-03-21 04:22:45 +01:00
|
|
|
return accounts
|
|
|
|
} catch (err) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
async function changeAccount(account: LocalAccount, index: number) {
|
2019-08-18 06:18:40 +02:00
|
|
|
// Sometimes application is closed to tray.
|
2018-07-21 07:13:52 +02:00
|
|
|
// In this time, mainWindow in not exist, so we have to create window.
|
|
|
|
if (mainWindow === null) {
|
|
|
|
await createWindow()
|
|
|
|
// We have to wait the web contents is loaded.
|
2019-04-15 18:35:20 +02:00
|
|
|
mainWindow!.webContents.on('did-finish-load', () => {
|
|
|
|
mainWindow!.webContents.send('change-account', Object.assign(account, { index: index }))
|
2018-07-21 07:13:52 +02:00
|
|
|
})
|
|
|
|
} else {
|
2019-08-18 06:18:40 +02:00
|
|
|
mainWindow.show()
|
2018-07-21 07:13:52 +02:00
|
|
|
mainWindow.webContents.send('change-account', Object.assign(account, { index: index }))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
async function getLanguage() {
|
2018-08-13 16:48:13 +02:00
|
|
|
try {
|
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.load()
|
|
|
|
return conf.language.language
|
|
|
|
} catch (err) {
|
2019-11-19 14:38:57 +01:00
|
|
|
log.warn(err)
|
2018-08-13 16:48:13 +02:00
|
|
|
return Language.en.key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-24 07:06:16 +01:00
|
|
|
const getSpellChecker = async (): Promise<boolean> => {
|
|
|
|
try {
|
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.load()
|
2021-03-25 11:04:31 +01:00
|
|
|
return conf.language.spellchecker.enabled
|
2021-01-24 07:06:16 +01:00
|
|
|
} catch (err) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 15:48:52 +02:00
|
|
|
const getMenuPreferences = async (): Promise<MenuPreferences> => {
|
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.load()
|
|
|
|
return conf.menu
|
|
|
|
}
|
|
|
|
|
2021-03-26 14:27:44 +01:00
|
|
|
/**
|
|
|
|
* Set application menu
|
|
|
|
* @return Whether the menu bar is auto hide.
|
|
|
|
*/
|
|
|
|
const updateApplicationMenu = async (accountsChange: Array<MenuItemConstructorOptions>): Promise<boolean> => {
|
|
|
|
const menuPreferences = await getMenuPreferences()
|
|
|
|
const menu = ApplicationMenu(accountsChange, menuPreferences, i18next)
|
|
|
|
Menu.setApplicationMenu(menu)
|
|
|
|
let autoHideMenuBar = false
|
|
|
|
if (menuPreferences.autoHideMenu) {
|
|
|
|
autoHideMenuBar = true
|
|
|
|
}
|
|
|
|
return autoHideMenuBar
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set dock menu for mac
|
|
|
|
*/
|
|
|
|
const updateDockMenu = async (accountsChange: Array<MenuItemConstructorOptions>) => {
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const dockMenu = Menu.buildFromTemplate(accountsChange)
|
|
|
|
app.dock.setMenu(dockMenu)
|
|
|
|
}
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
async function createWindow() {
|
2021-12-29 08:26:12 +01:00
|
|
|
/**
|
|
|
|
DB
|
|
|
|
*/
|
|
|
|
const lokiDB = await newDB(lokiDatabasePath)
|
|
|
|
markerRepo = new Marker(lokiDB)
|
2018-03-12 17:00:19 +01:00
|
|
|
/**
|
2018-03-21 04:22:45 +01:00
|
|
|
* List accounts
|
2018-03-12 17:00:19 +01:00
|
|
|
*/
|
2018-07-21 07:13:52 +02:00
|
|
|
const accounts = await listAccounts()
|
2019-04-17 12:52:01 +02:00
|
|
|
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
|
2018-07-21 07:13:52 +02:00
|
|
|
return {
|
|
|
|
label: a.domain,
|
|
|
|
accelerator: `CmdOrCtrl+${index + 1}`,
|
|
|
|
click: () => changeAccount(a, index)
|
|
|
|
}
|
|
|
|
})
|
2018-08-10 01:47:29 +02:00
|
|
|
|
2018-08-13 16:48:13 +02:00
|
|
|
/**
|
|
|
|
* Get language
|
|
|
|
*/
|
|
|
|
const language = await getLanguage()
|
2020-01-11 13:48:22 +01:00
|
|
|
i18next.changeLanguage(language)
|
2018-08-13 16:48:13 +02:00
|
|
|
|
2021-01-24 07:06:16 +01:00
|
|
|
/**
|
|
|
|
* Get spellcheck
|
|
|
|
*/
|
|
|
|
const spellcheck = await getSpellChecker()
|
|
|
|
|
2020-02-05 17:16:29 +01:00
|
|
|
/**
|
|
|
|
* Load system theme color for dark mode
|
|
|
|
*/
|
|
|
|
nativeTheme.themeSource = 'system'
|
|
|
|
|
2018-07-21 07:13:52 +02:00
|
|
|
/**
|
2021-03-26 14:27:44 +01:00
|
|
|
* Set Application Menu
|
2018-07-21 07:13:52 +02:00
|
|
|
*/
|
2021-03-26 14:27:44 +01:00
|
|
|
const autoHideMenuBar = await updateApplicationMenu(accountsChange)
|
2018-03-12 17:00:19 +01:00
|
|
|
|
2018-07-21 07:13:52 +02:00
|
|
|
/**
|
|
|
|
* Set dock menu for mac
|
|
|
|
*/
|
2021-03-26 14:27:44 +01:00
|
|
|
await updateDockMenu(accountsChange)
|
2018-07-21 07:13:52 +02:00
|
|
|
|
2019-07-23 18:14:43 +02:00
|
|
|
/**
|
2019-09-24 14:16:55 +02:00
|
|
|
* Windows10 don't notify, so we have to set appId
|
2019-07-23 18:14:43 +02:00
|
|
|
* https://github.com/electron/electron/issues/10864
|
|
|
|
*/
|
|
|
|
app.setAppUserModelId(appId)
|
|
|
|
|
2019-03-10 13:07:19 +01:00
|
|
|
/**
|
|
|
|
* Enable accessibility
|
|
|
|
*/
|
2019-10-23 16:07:20 +02:00
|
|
|
app.accessibilitySupportEnabled = true
|
2019-03-10 13:07:19 +01:00
|
|
|
|
2018-07-21 07:13:52 +02:00
|
|
|
/**
|
|
|
|
* Initial window options
|
|
|
|
*/
|
2020-05-17 09:31:37 +02:00
|
|
|
const mainWindowState = windowStateKeeper({
|
2018-07-21 07:13:52 +02:00
|
|
|
defaultWidth: 1000,
|
2019-04-15 18:35:20 +02:00
|
|
|
defaultHeight: 563
|
2018-07-21 07:13:52 +02:00
|
|
|
})
|
2021-10-02 08:37:54 +02:00
|
|
|
|
|
|
|
const titleBarStyle = process.platform === 'win32' ? 'default' : 'hidden'
|
|
|
|
|
2019-04-15 18:35:20 +02:00
|
|
|
const mainOpts: BrowserWindowConstructorOptions = {
|
2021-10-02 08:37:54 +02:00
|
|
|
titleBarStyle: titleBarStyle,
|
2018-07-21 07:13:52 +02:00
|
|
|
x: mainWindowState.x,
|
|
|
|
y: mainWindowState.y,
|
|
|
|
width: mainWindowState.width,
|
|
|
|
height: mainWindowState.height,
|
2020-09-16 14:39:27 +02:00
|
|
|
backgroundColor: '#fff',
|
2018-07-21 07:13:52 +02:00
|
|
|
useContentSize: true,
|
2021-05-22 14:50:45 +02:00
|
|
|
icon: path.join(iconBasePath, '256x256.png'),
|
2020-06-08 15:48:52 +02:00
|
|
|
autoHideMenuBar: autoHideMenuBar,
|
2019-05-06 15:35:09 +02:00
|
|
|
webPreferences: {
|
2022-04-09 18:10:14 +02:00
|
|
|
nodeIntegration: false,
|
2019-05-09 16:54:53 +02:00
|
|
|
contextIsolation: false,
|
2020-06-09 15:47:21 +02:00
|
|
|
preload: path.resolve(__dirname, './preload.js'),
|
2021-01-24 07:06:16 +01:00
|
|
|
spellcheck: spellcheck
|
2019-05-06 15:35:09 +02:00
|
|
|
}
|
2018-07-27 17:49:05 +02:00
|
|
|
}
|
2019-04-15 18:35:20 +02:00
|
|
|
const config: Config = {
|
2018-07-27 17:49:05 +02:00
|
|
|
windowOpts: mainOpts,
|
|
|
|
templateUrl: splashURL,
|
|
|
|
splashScreenOpts: {
|
|
|
|
width: 425,
|
|
|
|
height: 325
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 18:35:20 +02:00
|
|
|
mainWindow = initSplashScreen(config)
|
2018-03-07 14:28:48 +01:00
|
|
|
|
2018-07-27 17:49:05 +02:00
|
|
|
mainWindowState.manage(mainWindow)
|
2018-07-28 13:44:16 +02:00
|
|
|
|
2019-10-27 05:29:49 +01:00
|
|
|
/**
|
|
|
|
* Get system proxy configuration.
|
|
|
|
*/
|
|
|
|
if (session && session.defaultSession) {
|
2020-02-05 15:46:17 +01:00
|
|
|
const proxyInfo = await session.defaultSession.resolveProxy('https://mastodon.social')
|
|
|
|
proxyConfiguration.setSystemProxy(proxyInfo)
|
|
|
|
log.info(`System proxy configuration: ${proxyInfo}`)
|
2019-10-27 05:29:49 +01:00
|
|
|
}
|
|
|
|
|
2020-06-29 18:08:18 +02:00
|
|
|
/**
|
|
|
|
* Set proxy for BrowserWindow
|
|
|
|
*/
|
|
|
|
const proxyConfig = await proxyConfiguration.forMastodon()
|
|
|
|
if (proxyConfig) {
|
|
|
|
await mainWindow.webContents.session.setProxy({ proxyRules: `${proxyConfig.protocol}://${proxyConfig.host}:${proxyConfig.port}` })
|
|
|
|
}
|
|
|
|
mainWindow.loadURL(winURL)
|
|
|
|
|
|
|
|
mainWindow.webContents.on('will-navigate', event => event.preventDefault())
|
|
|
|
|
2019-08-18 06:18:40 +02:00
|
|
|
// Show tray icon only linux and windows.
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
// Show tray icon
|
2021-05-22 14:50:45 +02:00
|
|
|
tray = new Tray(path.join(iconBasePath, 'tray_icon.png'))
|
2020-01-11 13:48:22 +01:00
|
|
|
const trayMenu = TrayMenu(accountsChange, i18next)
|
2019-08-18 06:18:40 +02:00
|
|
|
tray.setContextMenu(trayMenu)
|
|
|
|
|
|
|
|
// For Windows
|
2020-01-11 13:48:22 +01:00
|
|
|
tray.setToolTip(i18next.t('main_menu.application.name'))
|
2019-08-18 06:18:40 +02:00
|
|
|
tray.on('click', () => {
|
|
|
|
if (mainWindow!.isVisible()) {
|
|
|
|
mainWindow!.hide()
|
|
|
|
mainWindow!.setSkipTaskbar(true)
|
|
|
|
} else {
|
|
|
|
mainWindow!.show()
|
|
|
|
mainWindow!.setSkipTaskbar(false)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-08-22 12:56:13 +02:00
|
|
|
// Minimize to tray
|
2021-02-15 13:03:18 +01:00
|
|
|
mainWindow.on('close', event => {
|
2019-08-22 12:56:13 +02:00
|
|
|
mainWindow!.hide()
|
|
|
|
mainWindow!.setSkipTaskbar(true)
|
|
|
|
event.preventDefault()
|
|
|
|
})
|
2021-02-15 13:03:18 +01:00
|
|
|
} else {
|
|
|
|
mainWindow.on('closed', () => {
|
|
|
|
mainWindow = null
|
|
|
|
})
|
2019-08-22 12:56:13 +02:00
|
|
|
}
|
2018-03-07 14:28:48 +01:00
|
|
|
}
|
|
|
|
|
2021-01-24 11:01:34 +01:00
|
|
|
// Parse command line arguments and show help command.
|
|
|
|
const args = minimist(process.argv.slice(process.env.NODE_ENV === 'development' ? 2 : 1))
|
|
|
|
if (args.help) {
|
|
|
|
console.log(`
|
|
|
|
Whalebird is Mastodon, Pleroma and Misskey client for desktop.
|
|
|
|
|
|
|
|
Usage
|
|
|
|
$ whalebird
|
|
|
|
|
|
|
|
Options
|
|
|
|
--help show help
|
|
|
|
`)
|
|
|
|
process.exit(0)
|
|
|
|
}
|
|
|
|
|
2018-04-19 17:34:24 +02:00
|
|
|
// Do not lower the rendering priority of Chromium when background
|
|
|
|
app.commandLine.appendSwitch('disable-renderer-backgrounding')
|
|
|
|
|
2018-03-07 14:28:48 +01:00
|
|
|
app.on('ready', createWindow)
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => {
|
2018-07-20 15:51:44 +02:00
|
|
|
// this action is called when user click the close button.
|
|
|
|
// In macOS, close button does not shutdown application. It is hide application window.
|
|
|
|
if (process.platform !== 'darwin') {
|
|
|
|
app.quit()
|
2018-07-22 15:25:23 +02:00
|
|
|
} else {
|
|
|
|
// In MacOS, we should change disable some menu items.
|
|
|
|
const menu = Menu.getApplicationMenu()
|
2020-02-05 15:46:17 +01:00
|
|
|
if (menu) {
|
|
|
|
if (menu.items[0].submenu) {
|
|
|
|
// Preferences
|
|
|
|
menu.items[0].submenu.items[2].enabled = false
|
|
|
|
}
|
|
|
|
if (menu.items[1].submenu) {
|
|
|
|
// New Toot
|
|
|
|
menu.items[1].submenu.items[0].enabled = false
|
|
|
|
}
|
|
|
|
if (menu.items[4].submenu) {
|
|
|
|
// Open Window
|
|
|
|
menu.items[4].submenu.items[1].enabled = true
|
|
|
|
// Jump to
|
|
|
|
menu.items[4].submenu.items[4].enabled = false
|
|
|
|
}
|
2019-04-15 18:35:20 +02:00
|
|
|
}
|
2018-07-20 15:51:44 +02:00
|
|
|
}
|
2018-03-07 14:28:48 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
app.on('activate', () => {
|
|
|
|
if (mainWindow === null) {
|
|
|
|
createWindow()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-09-04 15:48:47 +02:00
|
|
|
app.on('browser-window-focus', () => {
|
|
|
|
// Disable reload
|
|
|
|
globalShortcut.register('CommandOrControl+R', () => {
|
|
|
|
console.log('CommandOrControl+R is pressed: Shortcut Disabled')
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2021-06-10 15:12:21 +02:00
|
|
|
const auth = new Authentication(accountRepo)
|
2018-03-08 09:41:39 +01:00
|
|
|
|
2020-03-17 16:21:57 +01:00
|
|
|
type AuthRequest = {
|
|
|
|
instance: string
|
|
|
|
sns: 'mastodon' | 'pleroma' | 'misskey'
|
|
|
|
}
|
|
|
|
|
2020-11-28 14:42:19 +01:00
|
|
|
ipcMain.handle('get-auth-url', async (_: IpcMainInvokeEvent, request: AuthRequest) => {
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-11-28 14:42:19 +01:00
|
|
|
const url = await auth.getAuthorizationUrl(request.sns, request.instance, proxy)
|
|
|
|
log.debug(url)
|
|
|
|
// Open authorize url in default browser.
|
|
|
|
shell.openExternal(url)
|
|
|
|
return url
|
2018-03-08 09:41:39 +01:00
|
|
|
})
|
|
|
|
|
2020-03-17 16:21:57 +01:00
|
|
|
type TokenRequest = {
|
|
|
|
code: string | null
|
|
|
|
sns: 'mastodon' | 'pleroma' | 'misskey'
|
|
|
|
}
|
|
|
|
|
2021-04-06 15:17:18 +02:00
|
|
|
ipcMain.handle('get-and-update-access-token', async (_: IpcMainInvokeEvent, request: TokenRequest) => {
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2021-04-06 15:17:18 +02:00
|
|
|
const token = await auth.getAndUpdateAccessToken(request.sns, request.code, proxy)
|
|
|
|
// Update instance menu
|
|
|
|
const accounts = await listAccounts()
|
|
|
|
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
|
|
|
|
return {
|
|
|
|
label: a.domain,
|
|
|
|
accelerator: `CmdOrCtrl+${index + 1}`,
|
|
|
|
click: () => changeAccount(a, index)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
await updateApplicationMenu(accountsChange)
|
|
|
|
await updateDockMenu(accountsChange)
|
|
|
|
if (process.platform !== 'darwin' && tray !== null) {
|
|
|
|
tray.setContextMenu(TrayMenu(accountsChange, i18next))
|
|
|
|
}
|
|
|
|
|
2020-11-28 14:54:43 +01:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
accountDB.findOne(
|
|
|
|
{
|
|
|
|
accessToken: token
|
|
|
|
},
|
|
|
|
(err, doc: any) => {
|
|
|
|
if (err) return reject(err)
|
|
|
|
if (isEmpty(doc)) return reject(err)
|
|
|
|
resolve(doc._id)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
})
|
2018-03-08 09:41:39 +01:00
|
|
|
})
|
|
|
|
|
2018-03-09 09:08:27 +01:00
|
|
|
// nedb
|
2020-11-28 15:01:27 +01:00
|
|
|
ipcMain.handle('list-accounts', async (_: IpcMainInvokeEvent) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
const accounts = await accountRepo.listAccounts()
|
2020-11-28 15:01:27 +01:00
|
|
|
return accounts
|
2018-03-08 15:08:33 +01:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:07:18 +01:00
|
|
|
ipcMain.handle('get-local-account', async (_: IpcMainInvokeEvent, id: string) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
const account = await accountRepo.getAccount(id)
|
2020-11-28 15:07:18 +01:00
|
|
|
return account
|
2018-03-09 09:36:57 +01:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:10:24 +01:00
|
|
|
ipcMain.handle('update-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2021-06-10 15:12:21 +02:00
|
|
|
const ac: LocalAccount = await accountRepo.refresh(acct, proxy)
|
2020-11-28 15:10:24 +01:00
|
|
|
return ac
|
2018-04-01 14:43:23 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:14:27 +01:00
|
|
|
ipcMain.handle('remove-account', async (_: IpcMainInvokeEvent, id: string) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
const accountId = await accountRepo.removeAccount(id)
|
2021-04-06 16:09:04 +02:00
|
|
|
|
|
|
|
const accounts = await listAccounts()
|
|
|
|
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
|
|
|
|
return {
|
|
|
|
label: a.domain,
|
|
|
|
accelerator: `CmdOrCtrl+${index + 1}`,
|
|
|
|
click: () => changeAccount(a, index)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
await updateApplicationMenu(accountsChange)
|
|
|
|
await updateDockMenu(accountsChange)
|
|
|
|
if (process.platform !== 'darwin' && tray !== null) {
|
|
|
|
tray.setContextMenu(TrayMenu(accountsChange, i18next))
|
|
|
|
}
|
|
|
|
|
2020-11-28 15:14:27 +01:00
|
|
|
stopUserStreaming(accountId)
|
2018-04-01 12:23:07 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:16:23 +01:00
|
|
|
ipcMain.handle('forward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
await accountRepo.forwardAccount(acct)
|
2018-04-02 02:07:09 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:19:18 +01:00
|
|
|
ipcMain.handle('backward-account', async (_: IpcMainInvokeEvent, acct: LocalAccount) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
await accountRepo.backwardAccount(acct)
|
2018-04-02 15:17:08 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 15:22:13 +01:00
|
|
|
ipcMain.handle('refresh-accounts', async (_: IpcMainInvokeEvent) => {
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2021-06-10 15:12:21 +02:00
|
|
|
const accounts = await accountRepo.refreshAccounts(proxy)
|
2020-11-28 15:22:13 +01:00
|
|
|
|
|
|
|
return accounts
|
2018-04-22 08:48:20 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 17:21:39 +01:00
|
|
|
ipcMain.handle('remove-all-accounts', async (_: IpcMainInvokeEvent) => {
|
2021-06-10 15:12:21 +02:00
|
|
|
await accountRepo.removeAll()
|
2021-04-06 16:09:04 +02:00
|
|
|
|
|
|
|
const accounts = await listAccounts()
|
|
|
|
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
|
|
|
|
return {
|
|
|
|
label: a.domain,
|
|
|
|
accelerator: `CmdOrCtrl+${index + 1}`,
|
|
|
|
click: () => changeAccount(a, index)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
await updateApplicationMenu(accountsChange)
|
|
|
|
await updateDockMenu(accountsChange)
|
|
|
|
if (process.platform !== 'darwin' && tray !== null) {
|
|
|
|
tray.setContextMenu(TrayMenu(accountsChange, i18next))
|
|
|
|
}
|
2018-06-13 15:51:41 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 17:47:38 +01:00
|
|
|
ipcMain.handle('change-auto-launch', async (_: IpcMainInvokeEvent, enable: boolean) => {
|
2019-09-25 18:32:52 +02:00
|
|
|
if (launcher) {
|
2020-11-28 17:47:38 +01:00
|
|
|
const enabled = await launcher.isEnabled()
|
|
|
|
if (!enabled && enable && launcher) {
|
|
|
|
launcher.enable()
|
|
|
|
} else if (enabled && !enable && launcher) {
|
|
|
|
launcher.disable()
|
|
|
|
}
|
|
|
|
return enable
|
2019-09-25 18:32:52 +02:00
|
|
|
} else {
|
2020-11-28 17:47:38 +01:00
|
|
|
return false
|
2019-09-25 18:32:52 +02:00
|
|
|
}
|
2019-09-23 12:31:25 +02:00
|
|
|
})
|
|
|
|
|
2018-06-14 15:33:44 +02:00
|
|
|
// badge
|
|
|
|
ipcMain.on('reset-badge', () => {
|
2018-06-14 15:40:29 +02:00
|
|
|
if (process.platform === 'darwin') {
|
|
|
|
app.dock.setBadge('')
|
|
|
|
}
|
2018-06-14 15:33:44 +02:00
|
|
|
})
|
|
|
|
|
2022-04-09 18:10:14 +02:00
|
|
|
ipcMain.handle('confirm-timelines', async (_event: IpcMainInvokeEvent, account: LocalAccount): Promise<EnabledTimelines> => {
|
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
|
|
|
const timelines = await confirm(account, proxy)
|
2020-03-31 14:14:24 +02:00
|
|
|
|
2022-04-09 18:10:14 +02:00
|
|
|
return timelines
|
|
|
|
})
|
2020-03-31 14:14:24 +02:00
|
|
|
|
2019-06-27 16:03:30 +02:00
|
|
|
// user streaming
|
2020-05-17 09:31:37 +02:00
|
|
|
const userStreamings: { [key: string]: UserStreaming | null } = {}
|
2019-06-27 16:03:30 +02:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-all-user-streamings', (event: IpcMainEvent, accounts: Array<string>) => {
|
|
|
|
accounts.map(async id => {
|
|
|
|
const acct = await accountRepo.getAccount(id)
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
|
|
|
// Stop old user streaming
|
|
|
|
if (userStreamings[id]) {
|
|
|
|
userStreamings[id]!.stop()
|
|
|
|
userStreamings[id] = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
userStreamings[id] = new UserStreaming(sns, acct, url, proxy)
|
|
|
|
userStreamings[id]!.start(
|
|
|
|
async (update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send(`update-start-all-user-streamings-${id}`, update)
|
|
|
|
}
|
2019-07-31 17:40:28 +02:00
|
|
|
// Cache hashtag
|
|
|
|
update.tags.map(async tag => {
|
2019-08-29 15:42:32 +02:00
|
|
|
await hashtagCache.insertHashtag(tag.name).catch(err => console.error(err))
|
2019-07-31 17:40:28 +02:00
|
|
|
})
|
2019-08-10 11:31:50 +02:00
|
|
|
// Cache account
|
2019-08-10 15:30:30 +02:00
|
|
|
await accountCache.insertAccount(id, update.account.acct).catch(err => console.error(err))
|
2019-07-29 16:32:02 +02:00
|
|
|
},
|
2022-02-12 07:36:36 +01:00
|
|
|
async (notification: Entity.Notification) => {
|
|
|
|
await publishNotification(notification, event, id)
|
2019-07-22 16:32:14 +02:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// In macOS and Windows, sometimes window is closed (not quit).
|
|
|
|
// But streamings are always running.
|
|
|
|
// When window is closed, we can not send event to webContents; because it is already destroyed.
|
|
|
|
// So we have to guard it.
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
// To update notification timeline
|
|
|
|
event.sender.send(`notification-start-all-user-streamings-${id}`, notification)
|
|
|
|
|
|
|
|
// Does not exist a endpoint for only mention. And mention is a part of notification.
|
|
|
|
// So we have to get mention from notification.
|
|
|
|
if (notification.type === 'mention') {
|
|
|
|
event.sender.send(`mention-start-all-user-streamings-${id}`, notification)
|
2019-06-27 16:03:30 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
},
|
|
|
|
(statusId: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send(`delete-start-all-user-streamings-${id}`, statusId)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
// In macOS, sometimes window is closed (not quit).
|
|
|
|
// When window is closed, we can not send event to webContents; because it is destroyed.
|
|
|
|
// So we have to guard it.
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-all-user-streamings', err)
|
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
2022-02-24 13:16:30 +01:00
|
|
|
// Generate notifications received while the app was not running
|
|
|
|
const client = generator(sns, acct.baseURL, acct.accessToken, 'Whalebird', proxy)
|
|
|
|
const marker = await getMarker(client, id)
|
|
|
|
if (marker !== null) {
|
|
|
|
const unreadResponse = await client.getNotifications({ min_id: marker.last_read_id })
|
|
|
|
unreadResponse.data.map(async notification => {
|
|
|
|
await publishNotification(notification, event, id)
|
|
|
|
})
|
|
|
|
}
|
2021-12-30 02:47:48 +01:00
|
|
|
} catch (err: any) {
|
2019-07-29 16:32:02 +02:00
|
|
|
log.error(err)
|
2022-04-20 10:48:34 +02:00
|
|
|
const streamingError = new StreamingError(err.message, acct.domain)
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-all-user-streamings', streamingError)
|
|
|
|
}
|
|
|
|
}
|
2019-06-27 16:03:30 +02:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-06-27 15:42:46 +02:00
|
|
|
ipcMain.on('stop-all-user-streamings', () => {
|
2020-11-30 14:57:41 +01:00
|
|
|
Object.keys(userStreamings).forEach((key: string) => {
|
2019-06-27 15:42:46 +02:00
|
|
|
if (userStreamings[key]) {
|
|
|
|
userStreamings[key]!.stop()
|
|
|
|
userStreamings[key] = null
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2019-07-10 17:55:01 +02:00
|
|
|
/**
|
|
|
|
* Stop an user streaming in all user streamings.
|
|
|
|
* @param id specified user id in nedb.
|
|
|
|
*/
|
|
|
|
const stopUserStreaming = (id: string) => {
|
2020-11-30 14:57:41 +01:00
|
|
|
Object.keys(userStreamings).forEach((key: string) => {
|
2019-07-10 17:55:01 +02:00
|
|
|
if (key === id && userStreamings[id]) {
|
|
|
|
userStreamings[id]!.stop()
|
|
|
|
userStreamings[id] = null
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
let directMessagesStreaming: DirectStreaming | null = null
|
2018-11-03 17:12:36 +01:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-directmessages-streaming', async (event: IpcMainEvent, id: string) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
2022-04-20 10:48:34 +02:00
|
|
|
const acct = await accountRepo.getAccount(id)
|
2018-11-03 17:12:36 +01:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// Stop old directmessages streaming
|
|
|
|
if (directMessagesStreaming !== null) {
|
|
|
|
directMessagesStreaming.stop()
|
|
|
|
directMessagesStreaming = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
directMessagesStreaming = new DirectStreaming(sns, acct, url, proxy)
|
2019-07-29 16:32:02 +02:00
|
|
|
directMessagesStreaming.start(
|
2020-03-15 09:47:40 +01:00
|
|
|
(update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('update-start-directmessages-streaming', update)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(id: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('delete-start-directmessages-streaming', id)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-directmessages-streaming', err)
|
2018-11-03 17:12:36 +01:00
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-directmessages-streaming', err)
|
|
|
|
}
|
|
|
|
}
|
2018-11-03 17:12:36 +01:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('stop-directmessages-streaming', () => {
|
2018-11-03 17:37:30 +01:00
|
|
|
if (directMessagesStreaming !== null) {
|
|
|
|
directMessagesStreaming.stop()
|
|
|
|
directMessagesStreaming = null
|
2018-11-03 17:12:36 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
let localStreaming: LocalStreaming | null = null
|
2018-03-14 04:24:54 +01:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-local-streaming', async (event: IpcMainEvent, id: string) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
2022-04-20 10:48:34 +02:00
|
|
|
const acct = await accountRepo.getAccount(id)
|
2018-03-14 04:24:54 +01:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// Stop old local streaming
|
|
|
|
if (localStreaming !== null) {
|
|
|
|
localStreaming.stop()
|
|
|
|
localStreaming = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
localStreaming = new LocalStreaming(sns, acct, url, proxy)
|
2019-07-29 16:32:02 +02:00
|
|
|
localStreaming.start(
|
2020-03-15 09:47:40 +01:00
|
|
|
(update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('update-start-local-streaming', update)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(id: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('delete-start-local-streaming', id)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-local-streaming', err)
|
2018-03-14 04:24:54 +01:00
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-local-streaming', err)
|
|
|
|
}
|
|
|
|
}
|
2018-03-14 04:24:54 +01:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('stop-local-streaming', () => {
|
2018-07-01 14:15:54 +02:00
|
|
|
if (localStreaming !== null) {
|
|
|
|
localStreaming.stop()
|
|
|
|
localStreaming = null
|
|
|
|
}
|
2018-03-14 04:24:54 +01:00
|
|
|
})
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
let publicStreaming: PublicStreaming | null = null
|
2018-03-14 06:54:20 +01:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-public-streaming', async (event: IpcMainEvent, id: string) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
2022-04-20 10:48:34 +02:00
|
|
|
const acct = await accountRepo.getAccount(id)
|
2018-03-14 06:54:20 +01:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// Stop old public streaming
|
|
|
|
if (publicStreaming !== null) {
|
|
|
|
publicStreaming.stop()
|
|
|
|
publicStreaming = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
publicStreaming = new PublicStreaming(sns, acct, url, proxy)
|
2019-07-29 16:32:02 +02:00
|
|
|
publicStreaming.start(
|
2020-03-15 09:47:40 +01:00
|
|
|
(update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('update-start-public-streaming', update)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(id: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('delete-start-public-streaming', id)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-public-streaming', err)
|
2018-03-14 06:54:20 +01:00
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-public-streaming', err)
|
|
|
|
}
|
|
|
|
}
|
2018-03-14 06:54:20 +01:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('stop-public-streaming', () => {
|
2018-07-01 14:15:54 +02:00
|
|
|
if (publicStreaming !== null) {
|
|
|
|
publicStreaming.stop()
|
|
|
|
publicStreaming = null
|
|
|
|
}
|
2018-03-14 06:54:20 +01:00
|
|
|
})
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
let listStreaming: ListStreaming | null = null
|
2018-04-09 16:43:36 +02:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
type ListStreamingOpts = {
|
2019-05-27 15:56:54 +02:00
|
|
|
listID: string
|
2022-04-20 10:48:34 +02:00
|
|
|
accountID: string
|
2019-04-16 18:09:29 +02:00
|
|
|
}
|
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-list-streaming', async (event: IpcMainEvent, obj: ListStreamingOpts) => {
|
|
|
|
const { listID, accountID } = obj
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
2022-04-20 10:48:34 +02:00
|
|
|
const acct = await accountRepo.getAccount(accountID)
|
2018-04-09 16:43:36 +02:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// Stop old list streaming
|
|
|
|
if (listStreaming !== null) {
|
|
|
|
listStreaming.stop()
|
|
|
|
listStreaming = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
listStreaming = new ListStreaming(sns, acct, url, proxy)
|
2019-07-29 16:32:02 +02:00
|
|
|
listStreaming.start(
|
2020-03-15 09:47:40 +01:00
|
|
|
listID,
|
|
|
|
(update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('update-start-list-streaming', update)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(id: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('delete-start-list-streaming', id)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-list-streaming', err)
|
2018-04-09 16:43:36 +02:00
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-list-streaming', err)
|
|
|
|
}
|
|
|
|
}
|
2018-04-09 16:43:36 +02:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('stop-list-streaming', () => {
|
2018-06-01 06:09:42 +02:00
|
|
|
if (listStreaming !== null) {
|
|
|
|
listStreaming.stop()
|
|
|
|
listStreaming = null
|
|
|
|
}
|
2018-05-31 11:13:38 +02:00
|
|
|
})
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
let tagStreaming: TagStreaming | null = null
|
2018-05-31 11:13:38 +02:00
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
type TagStreamingOpts = {
|
2019-04-16 18:09:29 +02:00
|
|
|
tag: string
|
2022-04-20 10:48:34 +02:00
|
|
|
accountID: string
|
2019-04-16 18:09:29 +02:00
|
|
|
}
|
|
|
|
|
2022-04-20 10:48:34 +02:00
|
|
|
ipcMain.on('start-tag-streaming', async (event: IpcMainEvent, obj: TagStreamingOpts) => {
|
|
|
|
const { tag, accountID } = obj
|
2019-07-29 16:32:02 +02:00
|
|
|
try {
|
2022-04-20 10:48:34 +02:00
|
|
|
const acct = await accountRepo.getAccount(accountID)
|
2018-05-31 11:13:38 +02:00
|
|
|
|
2019-07-29 16:32:02 +02:00
|
|
|
// Stop old tag streaming
|
|
|
|
if (tagStreaming !== null) {
|
|
|
|
tagStreaming.stop()
|
|
|
|
tagStreaming = null
|
|
|
|
}
|
2019-10-27 09:22:44 +01:00
|
|
|
const proxy = await proxyConfiguration.forMastodon()
|
2020-03-15 09:47:40 +01:00
|
|
|
const sns = await detector(acct.baseURL, proxy)
|
|
|
|
const url = await StreamingURL(sns, acct, proxy)
|
|
|
|
tagStreaming = new TagStreaming(sns, acct, url, proxy)
|
2019-07-29 16:32:02 +02:00
|
|
|
tagStreaming.start(
|
2020-03-15 09:47:40 +01:00
|
|
|
tag,
|
|
|
|
(update: Entity.Status) => {
|
2019-07-29 16:32:02 +02:00
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('update-start-tag-streaming', update)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(id: string) => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('delete-start-tag-streaming', id)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err: Error) => {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-tag-streaming', err)
|
2018-05-31 11:13:38 +02:00
|
|
|
}
|
2019-07-22 16:32:14 +02:00
|
|
|
}
|
2019-07-29 16:32:02 +02:00
|
|
|
)
|
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('error-start-tag-streaming', err)
|
|
|
|
}
|
|
|
|
}
|
2018-05-31 11:13:38 +02:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('stop-tag-streaming', () => {
|
2018-07-01 14:15:54 +02:00
|
|
|
if (tagStreaming !== null) {
|
|
|
|
tagStreaming.stop()
|
|
|
|
tagStreaming = null
|
|
|
|
}
|
2018-05-31 11:13:38 +02:00
|
|
|
})
|
|
|
|
|
2018-04-05 02:00:42 +02:00
|
|
|
// sounds
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('fav-rt-action-sound', () => {
|
2018-04-07 16:18:20 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2019-04-20 08:44:22 +02:00
|
|
|
preferences
|
|
|
|
.load()
|
|
|
|
.then(conf => {
|
2018-04-07 16:18:20 +02:00
|
|
|
if (conf.general.sound.fav_rb) {
|
|
|
|
const sound = path.join(soundBasePath, 'operation_sound01.wav')
|
2019-04-16 16:50:53 +02:00
|
|
|
simplayer(sound, (err: Error) => {
|
2018-04-07 16:18:20 +02:00
|
|
|
if (err) log.error(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => log.error(err))
|
2018-04-05 14:04:40 +02:00
|
|
|
})
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('toot-action-sound', () => {
|
2018-04-07 16:18:20 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2019-04-20 08:44:22 +02:00
|
|
|
preferences
|
|
|
|
.load()
|
|
|
|
.then(conf => {
|
2018-04-07 16:18:20 +02:00
|
|
|
if (conf.general.sound.toot) {
|
|
|
|
const sound = path.join(soundBasePath, 'operation_sound02.wav')
|
2019-04-16 16:50:53 +02:00
|
|
|
simplayer(sound, (err: Error) => {
|
2018-04-07 16:18:20 +02:00
|
|
|
if (err) log.error(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => log.error(err))
|
2018-04-05 02:00:42 +02:00
|
|
|
})
|
|
|
|
|
2018-04-07 15:40:57 +02:00
|
|
|
// preferences
|
2020-11-29 14:31:01 +01:00
|
|
|
ipcMain.handle('get-preferences', async (_: IpcMainInvokeEvent) => {
|
2018-04-07 15:40:57 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2019-09-25 18:32:52 +02:00
|
|
|
let enabled = false
|
|
|
|
if (launcher) {
|
|
|
|
enabled = await launcher.isEnabled()
|
|
|
|
}
|
2019-09-24 14:16:55 +02:00
|
|
|
await preferences
|
|
|
|
.update({
|
|
|
|
general: {
|
2021-01-24 07:06:16 +01:00
|
|
|
other: {
|
|
|
|
launch: enabled
|
|
|
|
}
|
2019-09-24 14:16:55 +02:00
|
|
|
}
|
2018-04-07 15:40:57 +02:00
|
|
|
})
|
2019-09-24 14:16:55 +02:00
|
|
|
.catch(err => console.error(err))
|
2020-11-29 14:31:01 +01:00
|
|
|
const conf = await preferences.load()
|
|
|
|
return conf
|
2018-04-07 15:40:57 +02:00
|
|
|
})
|
|
|
|
|
2020-11-28 17:47:38 +01:00
|
|
|
ipcMain.handle('update-preferences', async (_: IpcMainInvokeEvent, data: any) => {
|
2018-08-29 17:56:56 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-11-28 17:47:38 +01:00
|
|
|
const conf = await preferences.update(data)
|
|
|
|
return conf
|
2018-08-29 17:56:56 +02:00
|
|
|
})
|
|
|
|
|
2021-10-24 15:07:11 +02:00
|
|
|
ipcMain.handle('reset-preferences', async (_: IpcMainInvokeEvent) => {
|
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.reset()
|
|
|
|
return conf
|
|
|
|
})
|
|
|
|
|
2021-10-23 16:13:44 +02:00
|
|
|
ipcMain.handle('system-use-dark-theme', async (_: IpcMainInvokeEvent) => {
|
|
|
|
return nativeTheme.shouldUseDarkColors
|
|
|
|
})
|
|
|
|
|
2019-10-23 16:07:20 +02:00
|
|
|
ipcMain.on('change-collapse', (_event: IpcMainEvent, value: boolean) => {
|
2018-07-03 16:02:16 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2019-04-20 08:44:22 +02:00
|
|
|
preferences
|
|
|
|
.update({
|
2018-07-03 16:02:16 +02:00
|
|
|
state: {
|
|
|
|
collapse: value
|
|
|
|
}
|
|
|
|
})
|
2019-04-20 08:44:22 +02:00
|
|
|
.catch(err => {
|
2018-07-03 16:02:16 +02:00
|
|
|
log.error(err)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2020-11-29 14:38:07 +01:00
|
|
|
ipcMain.handle('get-collapse', async (_: IpcMainInvokeEvent) => {
|
2018-07-03 16:02:16 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-11-29 14:38:07 +01:00
|
|
|
const conf = await preferences.load()
|
|
|
|
return conf.state.collapse
|
2018-10-23 01:49:53 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 14:42:16 +01:00
|
|
|
ipcMain.handle('change-global-header', async (_: IpcMainInvokeEvent, value: boolean) => {
|
2018-10-23 01:49:53 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-11-29 14:42:16 +01:00
|
|
|
const conf = await preferences.update({
|
|
|
|
state: {
|
|
|
|
hideGlobalHeader: value
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return conf
|
2018-10-23 01:49:53 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 14:43:59 +01:00
|
|
|
ipcMain.handle('get-global-header', async (_: IpcMainInvokeEvent) => {
|
2018-10-23 01:49:53 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-11-29 14:43:59 +01:00
|
|
|
const conf = await preferences.load()
|
|
|
|
return conf.state.hideGlobalHeader
|
2018-07-03 16:02:16 +02:00
|
|
|
})
|
|
|
|
|
2019-10-22 17:08:56 +02:00
|
|
|
// proxy
|
2020-06-30 16:48:55 +02:00
|
|
|
ipcMain.handle('update-proxy-config', async (_event: IpcMainInvokeEvent, proxy: Proxy) => {
|
2019-10-22 17:08:56 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-06-29 18:08:18 +02:00
|
|
|
try {
|
|
|
|
const conf = await preferences.update({
|
2019-10-22 17:08:56 +02:00
|
|
|
proxy: proxy
|
|
|
|
})
|
2020-06-29 18:08:18 +02:00
|
|
|
const proxyConfig = await proxyConfiguration.forMastodon()
|
|
|
|
if (proxyConfig) {
|
|
|
|
await mainWindow?.webContents.session.setProxy({ proxyRules: `${proxyConfig.protocol}://${proxyConfig.host}:${proxyConfig.port}` })
|
|
|
|
} else {
|
|
|
|
await mainWindow?.webContents.session.setProxy({})
|
|
|
|
}
|
2020-06-30 16:48:55 +02:00
|
|
|
return conf
|
2020-06-29 18:08:18 +02:00
|
|
|
} catch (err) {
|
|
|
|
log.error(err)
|
|
|
|
}
|
2020-06-30 16:48:55 +02:00
|
|
|
return null
|
2019-10-27 05:29:49 +01:00
|
|
|
})
|
|
|
|
|
2019-10-22 17:08:56 +02:00
|
|
|
// language
|
2020-11-29 14:47:23 +01:00
|
|
|
ipcMain.handle('change-language', async (_: IpcMainInvokeEvent, value: string) => {
|
2018-08-13 16:48:13 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
2020-11-29 14:47:23 +01:00
|
|
|
const conf = await preferences.update({
|
|
|
|
language: {
|
|
|
|
language: value
|
|
|
|
}
|
|
|
|
})
|
|
|
|
i18next.changeLanguage(conf.language.language)
|
2021-03-26 14:27:44 +01:00
|
|
|
|
|
|
|
const accounts = await listAccounts()
|
|
|
|
const accountsChange: Array<MenuItemConstructorOptions> = accounts.map((a, index) => {
|
|
|
|
return {
|
|
|
|
label: a.domain,
|
|
|
|
accelerator: `CmdOrCtrl+${index + 1}`,
|
|
|
|
click: () => changeAccount(a, index)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
await updateApplicationMenu(accountsChange)
|
|
|
|
await updateDockMenu(accountsChange)
|
|
|
|
if (process.platform !== 'darwin' && tray !== null) {
|
|
|
|
tray.setContextMenu(TrayMenu(accountsChange, i18next))
|
|
|
|
}
|
2020-11-29 14:47:23 +01:00
|
|
|
return conf.language.language
|
2018-08-13 16:48:13 +02:00
|
|
|
})
|
|
|
|
|
2021-03-25 11:04:31 +01:00
|
|
|
ipcMain.handle('toggle-spellchecker', async (_: IpcMainInvokeEvent, value: boolean) => {
|
2021-03-25 17:35:33 +01:00
|
|
|
mainWindow?.webContents.session.setSpellCheckerEnabled(value)
|
|
|
|
|
2021-03-25 11:04:31 +01:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.update({
|
|
|
|
language: {
|
|
|
|
spellchecker: {
|
|
|
|
enabled: value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return conf.language.spellchecker.enabled
|
|
|
|
})
|
|
|
|
|
2021-03-25 16:48:20 +01:00
|
|
|
ipcMain.handle('update-spellchecker-languages', async (_: IpcMainInvokeEvent, languages: Array<string>) => {
|
2021-03-25 17:30:17 +01:00
|
|
|
const decoded: Array<string> = languages.map(l => {
|
|
|
|
const d = decodeLanguage(l)
|
|
|
|
return d.rfc4646
|
|
|
|
})
|
|
|
|
mainWindow?.webContents.session.setSpellCheckerLanguages(decoded)
|
|
|
|
|
2021-03-25 16:48:20 +01:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.update({
|
|
|
|
language: {
|
|
|
|
spellchecker: {
|
|
|
|
languages: languages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return conf.language.spellchecker.languages
|
|
|
|
})
|
|
|
|
|
2021-06-10 05:03:51 +02:00
|
|
|
// marker
|
2021-06-10 18:07:41 +02:00
|
|
|
ipcMain.handle('get-home-marker', async (_: IpcMainInvokeEvent, ownerID: string) => {
|
2021-12-29 08:26:12 +01:00
|
|
|
if (markerRepo === null) {
|
|
|
|
return null
|
|
|
|
}
|
2021-06-10 18:07:41 +02:00
|
|
|
const marker = await markerRepo.get(ownerID, 'home')
|
|
|
|
return marker
|
|
|
|
})
|
|
|
|
|
|
|
|
ipcMain.handle('get-notifications-marker', async (_: IpcMainInvokeEvent, ownerID: string) => {
|
2021-12-29 08:26:12 +01:00
|
|
|
if (markerRepo === null) {
|
|
|
|
return null
|
|
|
|
}
|
2021-06-10 18:07:41 +02:00
|
|
|
const marker = await markerRepo.get(ownerID, 'notifications')
|
|
|
|
return marker
|
|
|
|
})
|
|
|
|
|
2022-03-18 16:46:22 +01:00
|
|
|
ipcMain.handle('get-mentions-marker', async (_: IpcMainInvokeEvent, ownerID: string) => {
|
|
|
|
if (markerRepo === null) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
const marker = await markerRepo.get(ownerID, 'mentions')
|
|
|
|
return marker
|
|
|
|
})
|
|
|
|
|
2022-04-09 18:10:14 +02:00
|
|
|
ipcMain.on('save-marker', async (_: IpcMainEvent, marker: LocalMarker): Promise<LocalMarker | null> => {
|
|
|
|
if (marker.owner_id === null || marker.owner_id === undefined || marker.owner_id === '') {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
if (markerRepo === null) {
|
|
|
|
return null
|
2021-06-10 15:12:21 +02:00
|
|
|
}
|
2022-04-09 18:10:14 +02:00
|
|
|
const res = await markerRepo.save(marker)
|
|
|
|
return res
|
|
|
|
})
|
2021-06-10 05:03:51 +02:00
|
|
|
|
2018-06-01 07:19:56 +02:00
|
|
|
// hashtag
|
2020-11-29 14:49:10 +01:00
|
|
|
ipcMain.handle('save-hashtag', async (_: IpcMainInvokeEvent, tag: string) => {
|
2018-06-01 07:19:56 +02:00
|
|
|
const hashtags = new Hashtags(hashtagsDB)
|
2020-11-29 14:49:10 +01:00
|
|
|
await hashtags.insertTag(tag)
|
2018-06-01 07:19:56 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 15:17:02 +01:00
|
|
|
ipcMain.handle('list-hashtags', async (_: IpcMainInvokeEvent) => {
|
2018-06-01 11:26:52 +02:00
|
|
|
const hashtags = new Hashtags(hashtagsDB)
|
2020-11-29 15:17:02 +01:00
|
|
|
const tags = await hashtags.listTags()
|
|
|
|
return tags
|
2018-06-01 11:26:52 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 15:19:16 +01:00
|
|
|
ipcMain.handle('remove-hashtag', async (_: IpcMainInvokeEvent, tag: LocalTag) => {
|
2018-06-02 08:30:20 +02:00
|
|
|
const hashtags = new Hashtags(hashtagsDB)
|
2020-11-29 15:19:16 +01:00
|
|
|
await hashtags.removeTag(tag)
|
2018-06-02 08:30:20 +02:00
|
|
|
})
|
|
|
|
|
2018-09-25 18:02:36 +02:00
|
|
|
// Fonts
|
2020-11-29 15:22:33 +01:00
|
|
|
ipcMain.handle('list-fonts', async (_: IpcMainInvokeEvent) => {
|
|
|
|
const list = await Fonts()
|
|
|
|
return list
|
2018-09-25 18:02:36 +02:00
|
|
|
})
|
|
|
|
|
2022-01-01 10:39:33 +01:00
|
|
|
// Settings
|
2022-04-09 18:10:14 +02:00
|
|
|
ipcMain.handle('get-account-setting', async (_: IpcMainInvokeEvent, accountID: string): Promise<Setting> => {
|
|
|
|
const settings = new Settings(settingsDBPath)
|
|
|
|
const setting = await settings.get(accountID)
|
|
|
|
return setting
|
|
|
|
})
|
|
|
|
|
|
|
|
ipcMain.handle('update-account-setting', async (_: IpcMainInvokeEvent, setting: Setting): Promise<BaseSettings> => {
|
|
|
|
const settings = new Settings(settingsDBPath)
|
|
|
|
const res = await settings.update(setting)
|
|
|
|
return res
|
|
|
|
})
|
2018-11-07 14:48:50 +01:00
|
|
|
|
2019-07-30 17:17:30 +02:00
|
|
|
// Cache
|
2020-11-29 15:40:52 +01:00
|
|
|
ipcMain.handle('get-cache-hashtags', async (_: IpcMainInvokeEvent) => {
|
2019-07-31 17:40:28 +02:00
|
|
|
const tags = await hashtagCache.listTags()
|
2020-11-29 15:40:52 +01:00
|
|
|
return tags
|
2019-07-30 17:17:30 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 15:43:24 +01:00
|
|
|
ipcMain.handle('insert-cache-hashtags', async (_: IpcMainInvokeEvent, tags: Array<string>) => {
|
|
|
|
await Promise.all(
|
|
|
|
tags.map(async name => {
|
|
|
|
await hashtagCache.insertHashtag(name).catch(err => console.error(err))
|
|
|
|
})
|
|
|
|
)
|
2019-08-08 16:39:27 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 15:46:45 +01:00
|
|
|
ipcMain.handle('get-cache-accounts', async (_: IpcMainInvokeEvent, ownerID: string) => {
|
2019-08-08 16:39:27 +02:00
|
|
|
const accounts = await accountCache.listAccounts(ownerID)
|
2020-11-29 15:46:45 +01:00
|
|
|
return accounts
|
2019-08-08 16:39:27 +02:00
|
|
|
})
|
|
|
|
|
2020-11-29 15:48:29 +01:00
|
|
|
ipcMain.handle('insert-cache-accounts', async (_: IpcMainInvokeEvent, obj: InsertAccountCache) => {
|
2019-08-08 16:39:27 +02:00
|
|
|
const { ownerID, accts } = obj
|
2020-11-29 15:48:29 +01:00
|
|
|
Promise.all(
|
|
|
|
accts.map(async acct => {
|
|
|
|
await accountCache.insertAccount(ownerID, acct).catch(err => console.error(err))
|
|
|
|
})
|
|
|
|
)
|
2019-07-30 17:17:30 +02:00
|
|
|
})
|
|
|
|
|
2018-08-14 03:18:30 +02:00
|
|
|
// Application control
|
2019-04-17 12:52:01 +02:00
|
|
|
ipcMain.on('relaunch', () => {
|
2018-08-14 03:18:30 +02:00
|
|
|
app.relaunch()
|
|
|
|
app.exit()
|
|
|
|
})
|
|
|
|
|
2018-03-07 14:28:48 +01:00
|
|
|
/**
|
|
|
|
* Auto Updater
|
|
|
|
*
|
|
|
|
* Uncomment the following code below and install `electron-updater` to
|
|
|
|
* support auto updating. Code Signing with a valid certificate is required.
|
|
|
|
* https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
import { autoUpdater } from 'electron-updater'
|
|
|
|
|
|
|
|
autoUpdater.on('update-downloaded', () => {
|
|
|
|
autoUpdater.quitAndInstall()
|
|
|
|
})
|
|
|
|
|
|
|
|
app.on('ready', () => {
|
|
|
|
if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
|
|
|
|
})
|
|
|
|
*/
|
2018-03-13 07:09:54 +01:00
|
|
|
|
2018-08-10 17:40:06 +02:00
|
|
|
/**
|
2020-06-08 15:48:52 +02:00
|
|
|
* Genrate application menu
|
2018-08-10 17:40:06 +02:00
|
|
|
*/
|
2020-06-08 15:48:52 +02:00
|
|
|
const ApplicationMenu = (accountsChange: Array<MenuItemConstructorOptions>, menu: MenuPreferences, i18n: I18n): Menu => {
|
2018-08-10 17:40:06 +02:00
|
|
|
/**
|
|
|
|
* For mac menu
|
|
|
|
*/
|
2019-04-20 08:44:22 +02:00
|
|
|
const macGeneralMenu: Array<MenuItemConstructorOptions> =
|
|
|
|
process.platform !== 'darwin'
|
|
|
|
? []
|
|
|
|
: [
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.services'),
|
2019-10-23 16:07:20 +02:00
|
|
|
role: 'services'
|
2019-04-20 08:44:22 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.hide'),
|
|
|
|
role: 'hide'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.hide_others'),
|
2019-10-23 16:07:20 +02:00
|
|
|
role: 'hideOthers'
|
2019-04-20 08:44:22 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.show_all'),
|
|
|
|
role: 'unhide'
|
|
|
|
}
|
|
|
|
]
|
2018-08-10 17:40:06 +02:00
|
|
|
|
2020-06-15 16:16:12 +02:00
|
|
|
const macWindowMenu: Array<MenuItemConstructorOptions> =
|
|
|
|
process.platform === 'darwin'
|
|
|
|
? []
|
|
|
|
: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.always_show_menu_bar'),
|
|
|
|
type: 'checkbox',
|
|
|
|
checked: !menu.autoHideMenu,
|
|
|
|
click: item => {
|
|
|
|
changeMenuAutoHide(!item.checked)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2021-02-15 13:03:18 +01:00
|
|
|
const applicationQuitMenu: Array<MenuItemConstructorOptions> =
|
|
|
|
process.platform === 'darwin'
|
|
|
|
? [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.quit'),
|
|
|
|
accelerator: 'CmdOrCtrl+Q',
|
|
|
|
role: 'quit'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.quit'),
|
|
|
|
accelerator: 'CmdOrCtrl+Q',
|
|
|
|
click: () => {
|
|
|
|
mainWindow!.destroy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2019-04-17 12:52:01 +02:00
|
|
|
const template: Array<MenuItemConstructorOptions> = [
|
2018-08-10 17:40:06 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.name'),
|
|
|
|
submenu: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.about'),
|
|
|
|
role: 'about',
|
|
|
|
click: () => {
|
|
|
|
openAboutWindow({
|
2021-05-22 14:50:45 +02:00
|
|
|
icon_path: path.join(iconBasePath, '256x256.png'),
|
2021-10-24 16:49:08 +02:00
|
|
|
copyright: 'Copyright (c) 2021 AkiraFukushima',
|
2018-08-10 17:40:06 +02:00
|
|
|
package_json_dir: path.resolve(__dirname, '../../'),
|
2020-10-04 13:59:42 +02:00
|
|
|
open_devtools: process.env.NODE_ENV !== 'production'
|
2018-08-10 17:40:06 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.preferences'),
|
|
|
|
accelerator: 'CmdOrCtrl+,',
|
|
|
|
click: () => {
|
2019-04-15 18:35:20 +02:00
|
|
|
mainWindow!.webContents.send('open-preferences')
|
2018-08-10 17:40:06 +02:00
|
|
|
}
|
|
|
|
},
|
2022-04-27 12:53:52 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.shortcuts'),
|
2022-05-03 04:25:06 +02:00
|
|
|
accelerator: 'Shift+?',
|
2022-04-27 12:53:52 +02:00
|
|
|
click: () => {
|
|
|
|
mainWindow!.webContents.send('open-shortcuts-list')
|
|
|
|
}
|
|
|
|
},
|
2018-08-10 17:40:06 +02:00
|
|
|
...macGeneralMenu,
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
2021-02-15 13:03:18 +01:00
|
|
|
...applicationQuitMenu
|
2018-08-10 17:40:06 +02:00
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.toot.name'),
|
|
|
|
submenu: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.toot.new'),
|
|
|
|
accelerator: 'CmdOrCtrl+N',
|
|
|
|
click: () => {
|
2019-04-15 18:35:20 +02:00
|
|
|
mainWindow!.webContents.send('CmdOrCtrl+N')
|
2018-08-10 17:40:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.name'),
|
|
|
|
submenu: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.undo'),
|
|
|
|
accelerator: 'CmdOrCtrl+Z',
|
|
|
|
role: 'undo'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.redo'),
|
|
|
|
accelerator: 'Shift+CmdOrCtrl+Z',
|
|
|
|
role: 'redo'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.cut'),
|
|
|
|
accelerator: 'CmdOrCtrl+X',
|
|
|
|
role: 'cut'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.copy'),
|
|
|
|
accelerator: 'CmdOrCtrl+C',
|
|
|
|
role: 'copy'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.paste'),
|
|
|
|
accelerator: 'CmdOrCtrl+V',
|
|
|
|
role: 'paste'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.edit.select_all'),
|
|
|
|
accelerator: 'CmdOrCtrl+A',
|
|
|
|
role: 'selectall'
|
|
|
|
}
|
2019-10-23 16:07:20 +02:00
|
|
|
] as Array<MenuItemConstructorOptions>
|
2018-08-10 17:40:06 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.view.name'),
|
|
|
|
submenu: [
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.view.toggle_full_screen'),
|
|
|
|
role: 'togglefullscreen'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.name'),
|
|
|
|
submenu: [
|
2020-06-15 16:16:12 +02:00
|
|
|
...macWindowMenu,
|
2018-08-10 17:40:06 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.close'),
|
|
|
|
role: 'close'
|
|
|
|
},
|
2018-10-10 02:05:21 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.open'),
|
|
|
|
enabled: false,
|
|
|
|
click: () => {
|
|
|
|
reopenWindow()
|
|
|
|
}
|
|
|
|
},
|
2018-08-10 17:40:06 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.minimize'),
|
|
|
|
role: 'minimize'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.window.jump_to'),
|
|
|
|
accelerator: 'CmdOrCtrl+K',
|
|
|
|
enabled: true,
|
|
|
|
click: () => {
|
2019-04-15 18:35:20 +02:00
|
|
|
mainWindow!.webContents.send('CmdOrCtrl+K')
|
2018-08-10 17:40:06 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator'
|
|
|
|
},
|
|
|
|
...accountsChange
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
2020-06-08 15:48:52 +02:00
|
|
|
return Menu.buildFromTemplate(template)
|
2018-08-10 17:40:06 +02:00
|
|
|
}
|
2018-10-10 02:05:21 +02:00
|
|
|
|
2020-01-11 13:48:22 +01:00
|
|
|
const TrayMenu = (accountsChange: Array<MenuItemConstructorOptions>, i18n: I18n): Menu => {
|
2019-08-18 06:18:40 +02:00
|
|
|
const template: Array<MenuItemConstructorOptions> = [
|
|
|
|
...accountsChange,
|
2019-10-13 09:07:35 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.open'),
|
|
|
|
click: async () => {
|
|
|
|
if (mainWindow) {
|
|
|
|
mainWindow.show()
|
|
|
|
} else {
|
|
|
|
await createWindow()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-08-18 06:18:40 +02:00
|
|
|
{
|
|
|
|
label: i18n.t('main_menu.application.quit'),
|
|
|
|
click: () => {
|
|
|
|
mainWindow!.destroy()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
const menu: Menu = Menu.buildFromTemplate(template)
|
|
|
|
return menu
|
|
|
|
}
|
|
|
|
|
2020-06-08 15:48:52 +02:00
|
|
|
const changeMenuAutoHide = async (autoHide: boolean) => {
|
2020-06-06 17:19:41 +02:00
|
|
|
if (mainWindow === null) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
mainWindow.autoHideMenuBar = autoHide
|
|
|
|
mainWindow.setMenuBarVisibility(!autoHide)
|
2020-06-08 15:48:52 +02:00
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
preferences.update({
|
|
|
|
menu: {
|
|
|
|
autoHideMenu: autoHide
|
|
|
|
}
|
|
|
|
})
|
2020-06-06 17:19:41 +02:00
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2019-04-20 08:44:22 +02:00
|
|
|
async function reopenWindow() {
|
2018-10-10 02:05:21 +02:00
|
|
|
if (mainWindow === null) {
|
|
|
|
await createWindow()
|
|
|
|
return null
|
|
|
|
} else {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
2019-07-21 17:19:23 +02:00
|
|
|
|
2022-02-12 07:36:36 +01:00
|
|
|
const publishNotification = async (notification: Entity.Notification, event: IpcMainEvent | IpcMainInvokeEvent, id: string) => {
|
|
|
|
const preferences = new Preferences(preferencesDBPath)
|
|
|
|
const conf = await preferences.load()
|
|
|
|
const options = createNotification(notification, conf.notification.notify)
|
|
|
|
if (options !== null) {
|
|
|
|
const notify = new Notification(options)
|
|
|
|
notify.on('click', _ => {
|
|
|
|
if (!event.sender.isDestroyed()) {
|
|
|
|
event.sender.send('open-notification-tab', id)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
notify.show()
|
|
|
|
}
|
|
|
|
if (process.platform === 'darwin') {
|
|
|
|
app.dock.setBadge('•')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
const createNotification = (notification: Entity.Notification, notifyConfig: Notify): NotificationConstructorOptions | null => {
|
2019-07-21 17:19:23 +02:00
|
|
|
switch (notification.type) {
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.Favourite:
|
2019-07-21 17:19:23 +02:00
|
|
|
if (notifyConfig.favourite) {
|
|
|
|
return {
|
2020-01-11 13:48:22 +01:00
|
|
|
title: i18next.t('notification.favourite.title'),
|
|
|
|
body: i18next.t('notification.favourite.body', { username: username(notification.account) }),
|
2019-07-21 17:19:23 +02:00
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.Follow:
|
2019-07-21 17:19:23 +02:00
|
|
|
if (notifyConfig.follow) {
|
|
|
|
return {
|
2020-01-11 13:48:22 +01:00
|
|
|
title: i18next.t('notification.follow.title'),
|
|
|
|
body: i18next.t('notification.follow.body', { username: username(notification.account) }),
|
2019-07-21 17:19:23 +02:00
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.FollowRequest:
|
2020-05-23 16:21:33 +02:00
|
|
|
if (notifyConfig.follow_request) {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.follow_request.title'),
|
|
|
|
body: i18next.t('notification.follow_request.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.Mention:
|
2019-07-21 17:19:23 +02:00
|
|
|
if (notifyConfig.reply) {
|
|
|
|
return {
|
|
|
|
title: `${username(notification.status!.account)}`,
|
|
|
|
body: sanitizeHtml(notification.status!.content, {
|
|
|
|
allowedTags: [],
|
|
|
|
allowedAttributes: []
|
|
|
|
}),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.Reblog:
|
2019-07-21 17:19:23 +02:00
|
|
|
if (notifyConfig.reblog) {
|
2020-05-23 15:41:17 +02:00
|
|
|
if (notification.status && notification.status.quote) {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.quote.title'),
|
|
|
|
body: i18next.t('notification.quote.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.reblog.title'),
|
|
|
|
body: i18next.t('notification.reblog.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
2019-07-21 17:19:23 +02:00
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.EmojiReaction:
|
2020-04-25 14:25:28 +02:00
|
|
|
if (notifyConfig.reaction) {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.reaction.title'),
|
|
|
|
body: i18next.t('notification.reaction.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-02 03:45:52 +01:00
|
|
|
case NotificationType.Status:
|
|
|
|
if (notifyConfig.status) {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.status.title'),
|
|
|
|
body: i18next.t('notification.status.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-23 16:46:31 +01:00
|
|
|
case NotificationType.PollVote:
|
|
|
|
if (notifyConfig.poll_vote) {
|
2021-03-21 13:27:47 +01:00
|
|
|
return {
|
|
|
|
title: i18next.t('notification.poll_vote.title'),
|
|
|
|
body: i18next.t('notification.poll_vote.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2021-03-23 17:28:10 +01:00
|
|
|
case NotificationType.PollExpired:
|
|
|
|
if (notifyConfig.poll_expired) {
|
|
|
|
return {
|
|
|
|
title: i18next.t('notification.poll_expired.title'),
|
|
|
|
body: i18next.t('notification.poll_expired.body', { username: username(notification.account) }),
|
|
|
|
silent: false
|
|
|
|
} as NotificationConstructorOptions
|
|
|
|
}
|
|
|
|
break
|
2019-07-21 17:19:23 +02:00
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2020-03-15 09:47:40 +01:00
|
|
|
const username = (account: Entity.Account): string => {
|
2019-07-21 17:19:23 +02:00
|
|
|
if (account.display_name !== '') {
|
|
|
|
return account.display_name
|
|
|
|
} else {
|
|
|
|
return account.username
|
|
|
|
}
|
|
|
|
}
|
2021-03-25 17:30:17 +01:00
|
|
|
|
|
|
|
const decodeLanguage = (lang: string): LanguageType => {
|
|
|
|
const l = Object.keys(Language).find(k => Language[k].key === lang)
|
|
|
|
if (l === undefined) {
|
|
|
|
return Language.en
|
|
|
|
} else {
|
|
|
|
return Language[l]
|
|
|
|
}
|
|
|
|
}
|
2022-02-24 13:16:30 +01:00
|
|
|
|
|
|
|
const getMarker = async (client: MegalodonInterface, accountID: string): Promise<LocalMarker | null> => {
|
|
|
|
let serverMarker: Entity.Marker | {} = {}
|
|
|
|
try {
|
|
|
|
const res = await client.getMarkers(['notifications'])
|
|
|
|
serverMarker = res.data
|
|
|
|
} catch (err) {
|
|
|
|
console.warn(err)
|
|
|
|
}
|
|
|
|
if ((serverMarker as Entity.Marker).notifications !== undefined) {
|
|
|
|
return {
|
|
|
|
timeline: 'notifications',
|
|
|
|
last_read_id: (serverMarker as Entity.Marker).notifications.last_read_id
|
|
|
|
} as LocalMarker
|
|
|
|
}
|
|
|
|
if (markerRepo === null) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
const marker = await markerRepo.get(accountID, 'notifications')
|
|
|
|
return marker
|
|
|
|
}
|