fchat-rising/electron/chat.ts

270 lines
10 KiB
TypeScript
Raw Permalink Normal View History

2017-09-02 01:50:31 +00:00
/**
* @license
* MIT License
*
* Copyright (c) 2018 F-List
2017-09-02 01:50:31 +00:00
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* This license header applies to this file and all of the non-third-party assets it includes.
* @file The entry point for the Electron renderer of F-Chat 3.0.
* @copyright 2018 F-List
2017-09-02 01:50:31 +00:00
* @author Maya Wolf <maya@f-list.net>
* @version 3.0
* @see {@link https://github.com/f-list/exported|GitHub repo}
*/
// import { DebugLogger } from './debug-logger';
// // @ts-ignore
// const dl = new DebugLogger('chat');
2017-09-02 01:50:31 +00:00
import * as electron from 'electron';
2021-12-23 22:59:09 +00:00
2021-09-11 01:59:05 +00:00
import * as remote from '@electron/remote';
2021-12-23 22:59:09 +00:00
const webContents = remote.getCurrentWebContents();
// tslint:disable-next-line:no-require-imports no-submodule-imports
require('@electron/remote/main').enable(webContents);
import Axios from 'axios';
import {exec, execSync} from 'child_process';
2018-01-06 16:14:21 +00:00
import * as path from 'path';
import * as qs from 'querystring';
2017-10-16 23:58:57 +00:00
import {getKey} from '../chat/common';
2020-03-15 16:23:39 +00:00
import { EventBus } from '../chat/preview/event-bus';
2019-09-17 17:14:14 +00:00
import {init as initCore} from '../chat/core';
2017-10-16 23:58:57 +00:00
import l from '../chat/localize';
2020-06-18 21:07:27 +00:00
// import {setupRaven} from '../chat/vue-raven';
2019-09-17 17:14:14 +00:00
import Socket from '../chat/WebSocket';
import Connection from '../fchat/connection';
import {Keys} from '../keys';
2020-04-01 22:19:55 +00:00
import {GeneralSettings /*, nativeRequire*/ } from './common';
2019-09-17 17:14:14 +00:00
import {Logs, SettingsStore} from './filesystem';
2020-03-14 21:24:49 +00:00
import Notifications from './notifications';
2018-01-06 16:14:21 +00:00
import * as SlimcatImporter from './importer';
2017-09-02 01:50:31 +00:00
import Index from './Index.vue';
2021-01-21 00:02:15 +00:00
import log from 'electron-log'; // tslint:disable-line: match-default-export-name
import { WordPosSearch } from '../learn/dictionary/word-pos-search';
2017-09-02 01:50:31 +00:00
2020-07-05 17:43:27 +00:00
log.debug('init.chat');
2018-01-06 16:14:21 +00:00
document.addEventListener('keydown', (e: KeyboardEvent) => {
if(e.ctrlKey && e.shiftKey && getKey(e) === Keys.KeyI)
2021-09-11 01:59:05 +00:00
remote.getCurrentWebContents().toggleDevTools();
2018-01-06 16:14:21 +00:00
});
2020-04-01 22:19:55 +00:00
/* process.env.SPELLCHECKER_PREFER_HUNSPELL = '1';
2018-01-06 16:14:21 +00:00
const sc = nativeRequire<{
2019-01-03 17:38:17 +00:00
Spellchecker: new() => {
add(word: string): void
remove(word: string): void
isMisspelled(x: string): boolean
setDictionary(name: string | undefined, dir: string): void
getCorrectionsForMisspelling(word: string): ReadonlyArray<string>
2018-01-06 16:14:21 +00:00
}
}>('spellchecker/build/Release/spellchecker.node');
2020-04-01 22:19:55 +00:00
const spellchecker = new sc.Spellchecker();*/
2018-01-06 16:14:21 +00:00
2021-01-20 20:22:22 +00:00
2021-09-11 01:59:05 +00:00
Axios.defaults.params = {__fchat: `desktop/${remote.app.getVersion()}`};
2017-09-02 01:50:31 +00:00
if(process.env.NODE_ENV === 'production') {
2021-09-11 01:59:05 +00:00
// setupRaven('https://a9239b17b0a14f72ba85e8729b9d1612@sentry.f-list.net/2', remote.app.getVersion());
2017-10-16 23:58:57 +00:00
2021-09-11 01:59:05 +00:00
remote.getCurrentWebContents().on('devtools-opened', () => {
2017-10-16 23:58:57 +00:00
console.log(`%c${l('consoleWarning.head')}`, 'background: red; color: yellow; font-size: 30pt');
console.log(`%c${l('consoleWarning.body')}`, 'font-size: 16pt; color:red');
});
2017-09-02 01:50:31 +00:00
}
2018-04-16 23:14:13 +00:00
let browser: string | undefined;
2019-01-03 17:38:17 +00:00
2018-04-16 23:14:13 +00:00
function openIncognito(url: string): void {
if(browser === undefined)
try { //tslint:disable-next-line:max-line-length
browser = execSync(`FOR /F "skip=2 tokens=3" %A IN ('REG QUERY HKCU\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice /v ProgId') DO @(echo %A)`)
2019-01-03 17:38:17 +00:00
.toString().trim().toLowerCase();
2018-04-16 23:14:13 +00:00
} catch(e) {
console.error(e);
}
2019-09-17 17:14:14 +00:00
const commands = {
2019-01-03 17:38:17 +00:00
chrome: 'chrome.exe -incognito', firefox: 'firefox.exe -private-window', vivaldi: 'vivaldi.exe -incognito',
opera: 'opera.exe -private'
};
let start = 'iexplore.exe -private';
for(const key in commands)
if(browser!.indexOf(key) !== -1) start = commands[<keyof typeof commands>key];
exec(`start ${start} ${url}`);
2018-04-16 23:14:13 +00:00
}
2017-09-02 01:50:31 +00:00
2021-01-21 00:02:15 +00:00
const wordPosSearch = new WordPosSearch();
2021-01-20 20:22:22 +00:00
2018-01-06 16:14:21 +00:00
webContents.on('context-menu', (_, props) => {
const hasText = props.selectionText.trim().length > 0;
const can = (type: string) => (<Electron.EditFlags & {[key: string]: boolean}>props.editFlags)[`can${type}`] && hasText;
const menuTemplate: Electron.MenuItemConstructorOptions[] = [];
if(hasText || props.isEditable)
menuTemplate.push({
id: 'copy',
label: l('action.copy'),
2019-09-17 17:14:14 +00:00
role: can('Copy') ? 'copy' : undefined,
2018-03-28 13:51:05 +00:00
accelerator: 'CmdOrCtrl+C',
2018-01-06 16:14:21 +00:00
enabled: can('Copy')
});
if(props.isEditable)
menuTemplate.push({
id: 'cut',
label: l('action.cut'),
2019-09-17 17:14:14 +00:00
role: can('Cut') ? 'cut' : undefined,
2018-03-28 13:51:05 +00:00
accelerator: 'CmdOrCtrl+X',
2018-01-06 16:14:21 +00:00
enabled: can('Cut')
}, {
id: 'paste',
label: l('action.paste'),
2019-09-17 17:14:14 +00:00
role: props.editFlags.canPaste ? 'paste' : undefined,
2018-03-28 13:51:05 +00:00
accelerator: 'CmdOrCtrl+V',
2018-01-06 16:14:21 +00:00
enabled: props.editFlags.canPaste
});
2018-04-16 23:14:13 +00:00
else if(props.linkURL.length > 0 && props.mediaType === 'none' && props.linkURL.substr(0, props.pageURL.length) !== props.pageURL) {
2018-01-06 16:14:21 +00:00
menuTemplate.push({
id: 'copyLink',
label: l('action.copyLink'),
click(): void {
if(process.platform === 'darwin')
electron.clipboard.writeBookmark(props.linkText, props.linkURL);
else
electron.clipboard.writeText(props.linkURL);
}
});
menuTemplate.push({
id: 'toggleStickyness',
label: 'Toggle Sticky Preview',
click(): void {
EventBus.$emit('imagepreview-toggle-stickyness', {url: props.linkURL});
}
});
2018-04-16 23:14:13 +00:00
if(process.platform === 'win32')
menuTemplate.push({
id: 'incognito',
label: l('action.incognito'),
click: () => openIncognito(props.linkURL)
});
} else if(hasText)
2018-03-28 13:51:05 +00:00
menuTemplate.push({
label: l('action.copyWithoutBBCode'),
enabled: can('Copy'),
accelerator: 'CmdOrCtrl+Shift+C',
click: () => electron.clipboard.writeText(props.selectionText)
});
2018-01-06 16:14:21 +00:00
if(props.misspelledWord !== '') {
2020-04-03 00:46:36 +00:00
const corrections = props.dictionarySuggestions; //spellchecker.getCorrectionsForMisspelling(props.misspelledWord);
menuTemplate.unshift({
label: l('spellchecker.add'),
click: () => electron.ipcRenderer.send('dictionary-add', props.misspelledWord)
}, {type: 'separator'});
if(corrections.length > 0)
menuTemplate.unshift(...corrections.map((correction: string) => ({
label: correction,
click: () => webContents.replaceMisspelling(correction)
})));
else menuTemplate.unshift({enabled: false, label: l('spellchecker.noCorrections')});
2018-08-10 16:59:37 +00:00
} else if(settings.customDictionary.indexOf(props.selectionText) !== -1)
menuTemplate.unshift({
label: l('spellchecker.remove'),
2018-08-10 16:59:37 +00:00
click: () => electron.ipcRenderer.send('dictionary-remove', props.selectionText)
}, {type: 'separator'});
2018-01-06 16:14:21 +00:00
2021-01-21 00:02:15 +00:00
const lookupWord = props.selectionText || wordPosSearch.getLastClickedWord();
if (lookupWord) {
2021-01-20 20:22:22 +00:00
menuTemplate.unshift(
{ type: 'separator' },
{
2021-01-21 00:02:15 +00:00
label: `Look up '${lookupWord}'`,
2021-01-20 20:22:22 +00:00
click: async() => {
2021-01-21 00:02:15 +00:00
EventBus.$emit('word-definition', { lookupWord, x: props.x, y: props.y });
2021-01-20 20:22:22 +00:00
}
}
);
}
2021-09-11 01:59:05 +00:00
if(menuTemplate.length > 0) remote.Menu.buildFromTemplate(menuTemplate).popup({});
2021-01-20 20:22:22 +00:00
2021-09-11 20:43:33 +00:00
log.debug(
'context.text',
{ linkText: props.linkText, misspelledWord: props.misspelledWord, selectionText: props.selectionText, titleText: props.titleText }
);
2017-09-02 01:50:31 +00:00
});
2021-09-11 01:59:05 +00:00
let dictDir = path.join(remote.app.getPath('userData'), 'spellchecker');
2020-04-01 22:19:55 +00:00
2019-01-03 17:38:17 +00:00
if(process.platform === 'win32') //get the path in DOS (8-character) format as special characters cause problems otherwise
exec(`for /d %I in ("${dictDir}") do @echo %~sI`, (_, stdout) => dictDir = stdout.trim());
2020-04-01 22:19:55 +00:00
2020-04-02 00:27:21 +00:00
// electron.webFrame.setSpellCheckProvider(
// '', {spellCheck: (words, callback) => callback(words.filter((x) => spellchecker.isMisspelled(x)))});
2019-01-03 17:38:17 +00:00
2018-08-18 19:37:53 +00:00
function onSettings(s: GeneralSettings): void {
2018-08-10 16:59:37 +00:00
settings = s;
2020-04-01 22:19:55 +00:00
2020-06-30 21:51:06 +00:00
log.transports.file.level = settings.risingSystemLogLevel;
log.transports.console.level = settings.risingSystemLogLevel;
2020-04-01 22:19:55 +00:00
// spellchecker.setDictionary(s.spellcheckLang, dictDir);
// for(const word of s.customDictionary) spellchecker.add(word);
2018-08-18 19:37:53 +00:00
}
2019-01-03 17:38:17 +00:00
2018-08-18 19:37:53 +00:00
electron.ipcRenderer.on('settings', (_: Event, s: GeneralSettings) => onSettings(s));
2018-01-06 16:14:21 +00:00
const params = <{[key: string]: string | undefined}>qs.parse(window.location.search.substr(1));
2018-08-10 16:59:37 +00:00
let settings = <GeneralSettings>JSON.parse(params['settings']!);
2020-06-30 21:51:06 +00:00
// console.log('SETTINGS', settings);
2018-01-06 16:14:21 +00:00
if(params['import'] !== undefined)
try {
if(SlimcatImporter.canImportGeneral() && confirm(l('importer.importGeneral'))) {
SlimcatImporter.importGeneral(settings);
electron.ipcRenderer.send('save-login', settings.account, settings.host);
}
} catch {
alert(l('importer.error'));
}
2018-08-18 19:37:53 +00:00
onSettings(settings);
2019-09-17 17:14:14 +00:00
2020-07-05 17:43:27 +00:00
log.debug('init.chat.core');
2021-09-11 01:59:05 +00:00
const connection = new Connection(`F-Chat 3.0 (${process.platform})`, remote.app.getVersion(), Socket);
2020-06-30 21:51:06 +00:00
initCore(connection, settings, Logs, SettingsStore, Notifications);
2019-09-17 17:14:14 +00:00
2020-07-05 17:43:27 +00:00
log.debug('init.chat.vue');
2018-01-06 16:14:21 +00:00
//tslint:disable-next-line:no-unused-expression
new Index({
el: '#app',
data: {
settings,
hasCompletedUpgrades: JSON.parse(params['hasCompletedUpgrades']!)
}
});
2023-09-04 03:05:46 +00:00
log.debug('init.chat.vue.done');