Electron built-in spell checker
This commit is contained in:
		
							parent
							
								
									165007d1c1
								
							
						
					
					
						commit
						a599e17e1b
					
				@ -35,6 +35,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
    import Sortable = require('sortablejs'); //tslint:disable-line:no-require-imports
 | 
					    import Sortable = require('sortablejs'); //tslint:disable-line:no-require-imports
 | 
				
			||||||
 | 
					    import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    import {Component, Hook} from '@f-list/vue-ts';
 | 
					    import {Component, Hook} from '@f-list/vue-ts';
 | 
				
			||||||
    import * as electron from 'electron';
 | 
					    import * as electron from 'electron';
 | 
				
			||||||
@ -44,6 +45,7 @@
 | 
				
			|||||||
    import Vue from 'vue';
 | 
					    import Vue from 'vue';
 | 
				
			||||||
    import l from '../chat/localize';
 | 
					    import l from '../chat/localize';
 | 
				
			||||||
    import {GeneralSettings} from './common';
 | 
					    import {GeneralSettings} from './common';
 | 
				
			||||||
 | 
					    import { getSafeLanguages } from './language';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const browserWindow = electron.remote.getCurrentWindow();
 | 
					    const browserWindow = electron.remote.getCurrentWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -87,15 +89,26 @@
 | 
				
			|||||||
            // top bar devtools
 | 
					            // top bar devtools
 | 
				
			||||||
            // browserWindow.webContents.openDevTools({ mode: 'detach' });
 | 
					            // browserWindow.webContents.openDevTools({ mode: 'detach' });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            browserWindow.webContents.session.setSpellCheckerLanguages(getSafeLanguages(this.settings.spellcheckLang));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await this.addTab();
 | 
					            await this.addTab();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            electron.ipcRenderer.on('settings', (_: Event, settings: GeneralSettings) => this.settings = settings);
 | 
					            electron.ipcRenderer.on('settings', (_e: Event, settings: GeneralSettings) => this.settings = settings);
 | 
				
			||||||
            electron.ipcRenderer.on('allow-new-tabs', (_: Event, allow: boolean) => this.canOpenTab = allow);
 | 
					            electron.ipcRenderer.on('allow-new-tabs', (_e: Event, allow: boolean) => this.canOpenTab = allow);
 | 
				
			||||||
            electron.ipcRenderer.on('open-tab', () => this.addTab());
 | 
					            electron.ipcRenderer.on('open-tab', () => this.addTab());
 | 
				
			||||||
            electron.ipcRenderer.on('update-available', (_: Event, available: boolean) => this.hasUpdate = available);
 | 
					            electron.ipcRenderer.on('update-available', (_e: Event, available: boolean) => this.hasUpdate = available);
 | 
				
			||||||
            electron.ipcRenderer.on('fix-logs', () => this.activeTab!.view.webContents.send('fix-logs'));
 | 
					            electron.ipcRenderer.on('fix-logs', () => this.activeTab!.view.webContents.send('fix-logs'));
 | 
				
			||||||
            electron.ipcRenderer.on('quit', () => this.destroyAllTabs());
 | 
					            electron.ipcRenderer.on('quit', () => this.destroyAllTabs());
 | 
				
			||||||
            electron.ipcRenderer.on('connect', (_: Event, id: number, name: string) => {
 | 
					
 | 
				
			||||||
 | 
					            electron.ipcRenderer.on('update-dictionaries', (_e: Event, langs: string[]) => {
 | 
				
			||||||
 | 
					                browserWindow.webContents.session.setSpellCheckerLanguages(langs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (const t of this.tabs) {
 | 
				
			||||||
 | 
					                    t.view.webContents.session.setSpellCheckerLanguages(langs);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            electron.ipcRenderer.on('connect', (_e: Event, id: number, name: string) => {
 | 
				
			||||||
                const tab = this.tabMap[id];
 | 
					                const tab = this.tabMap[id];
 | 
				
			||||||
                tab.user = name;
 | 
					                tab.user = name;
 | 
				
			||||||
                tab.tray.setToolTip(`${l('title')} - ${tab.user}`);
 | 
					                tab.tray.setToolTip(`${l('title')} - ${tab.user}`);
 | 
				
			||||||
@ -103,7 +116,7 @@
 | 
				
			|||||||
                menu.unshift({label: tab.user, enabled: false}, {type: 'separator'});
 | 
					                menu.unshift({label: tab.user, enabled: false}, {type: 'separator'});
 | 
				
			||||||
                tab.tray.setContextMenu(electron.remote.Menu.buildFromTemplate(menu));
 | 
					                tab.tray.setContextMenu(electron.remote.Menu.buildFromTemplate(menu));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            electron.ipcRenderer.on('disconnect', (_: Event, id: number) => {
 | 
					            electron.ipcRenderer.on('disconnect', (_e: Event, id: number) => {
 | 
				
			||||||
                const tab = this.tabMap[id];
 | 
					                const tab = this.tabMap[id];
 | 
				
			||||||
                if(tab.hasNew) {
 | 
					                if(tab.hasNew) {
 | 
				
			||||||
                    tab.hasNew = false;
 | 
					                    tab.hasNew = false;
 | 
				
			||||||
@ -113,18 +126,18 @@
 | 
				
			|||||||
                tab.tray.setToolTip(l('title'));
 | 
					                tab.tray.setToolTip(l('title'));
 | 
				
			||||||
                tab.tray.setContextMenu(electron.remote.Menu.buildFromTemplate(this.createTrayMenu(tab)));
 | 
					                tab.tray.setContextMenu(electron.remote.Menu.buildFromTemplate(this.createTrayMenu(tab)));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            electron.ipcRenderer.on('has-new', (_: Event, id: number, hasNew: boolean) => {
 | 
					            electron.ipcRenderer.on('has-new', (_e: Event, id: number, hasNew: boolean) => {
 | 
				
			||||||
                const tab = this.tabMap[id];
 | 
					                const tab = this.tabMap[id];
 | 
				
			||||||
                tab.hasNew = hasNew;
 | 
					                tab.hasNew = hasNew;
 | 
				
			||||||
                electron.ipcRenderer.send('has-new', this.tabs.reduce((cur, t) => cur || t.hasNew, false));
 | 
					                electron.ipcRenderer.send('has-new', this.tabs.reduce((cur, t) => cur || t.hasNew, false));
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            browserWindow.on('maximize', () => this.isMaximized = true);
 | 
					            browserWindow.on('maximize', () => this.isMaximized = true);
 | 
				
			||||||
            browserWindow.on('unmaximize', () => this.isMaximized = false);
 | 
					            browserWindow.on('unmaximize', () => this.isMaximized = false);
 | 
				
			||||||
            electron.ipcRenderer.on('switch-tab', (_: Event) => {
 | 
					            electron.ipcRenderer.on('switch-tab', (_e: Event) => {
 | 
				
			||||||
                const index = this.tabs.indexOf(this.activeTab!);
 | 
					                const index = this.tabs.indexOf(this.activeTab!);
 | 
				
			||||||
                this.show(this.tabs[index + 1 === this.tabs.length ? 0 : index + 1]);
 | 
					                this.show(this.tabs[index + 1 === this.tabs.length ? 0 : index + 1]);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            electron.ipcRenderer.on('show-tab', (_: Event, id: number) => {
 | 
					            electron.ipcRenderer.on('show-tab', (_e: Event, id: number) => {
 | 
				
			||||||
                this.show(this.tabMap[id]);
 | 
					                this.show(this.tabMap[id]);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            document.addEventListener('click', () => this.activeTab!.view.webContents.focus());
 | 
					            document.addEventListener('click', () => this.activeTab!.view.webContents.focus());
 | 
				
			||||||
@ -195,12 +208,14 @@
 | 
				
			|||||||
            if(this.lockTab) return;
 | 
					            if(this.lockTab) return;
 | 
				
			||||||
            const tray = new electron.remote.Tray(trayIcon);
 | 
					            const tray = new electron.remote.Tray(trayIcon);
 | 
				
			||||||
            tray.setToolTip(l('title'));
 | 
					            tray.setToolTip(l('title'));
 | 
				
			||||||
            tray.on('click', (_) => this.trayClicked(tab));
 | 
					            tray.on('click', (_e) => this.trayClicked(tab));
 | 
				
			||||||
            const view = new electron.remote.BrowserView({webPreferences: {webviewTag: true, nodeIntegration: true, spellcheck: true}});
 | 
					            const view = new electron.remote.BrowserView({webPreferences: {webviewTag: true, nodeIntegration: true, spellcheck: true}});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // tab devtools
 | 
					            // tab devtools
 | 
				
			||||||
            // view.webContents.openDevTools();
 | 
					            // view.webContents.openDevTools();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            view.webContents.session.setSpellCheckerLanguages(getSafeLanguages(this.settings.spellcheckLang));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            view.setAutoResize({width: true, height: true});
 | 
					            view.setAutoResize({width: true, height: true});
 | 
				
			||||||
            electron.ipcRenderer.send('tab-added', view.webContents.id);
 | 
					            electron.ipcRenderer.send('tab-added', view.webContents.id);
 | 
				
			||||||
            const tab = {active: false, view, user: undefined, hasNew: false, tray};
 | 
					            const tab = {active: false, view, user: undefined, hasNew: false, tray};
 | 
				
			||||||
 | 
				
			|||||||
@ -161,17 +161,17 @@ webContents.on('context-menu', (_, props) => {
 | 
				
			|||||||
            click: () => electron.clipboard.writeText(props.selectionText)
 | 
					            click: () => electron.clipboard.writeText(props.selectionText)
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    if(props.misspelledWord !== '') {
 | 
					    if(props.misspelledWord !== '') {
 | 
				
			||||||
        // const corrections = spellchecker.getCorrectionsForMisspelling(props.misspelledWord);
 | 
					        const corrections = props.dictionarySuggestions; //spellchecker.getCorrectionsForMisspelling(props.misspelledWord);
 | 
				
			||||||
        // menuTemplate.unshift({
 | 
					        menuTemplate.unshift({
 | 
				
			||||||
        //     label: l('spellchecker.add'),
 | 
					            label: l('spellchecker.add'),
 | 
				
			||||||
        //     click: () => electron.ipcRenderer.send('dictionary-add', props.misspelledWord)
 | 
					            click: () => electron.ipcRenderer.send('dictionary-add', props.misspelledWord)
 | 
				
			||||||
        // }, {type: 'separator'});
 | 
					        }, {type: 'separator'});
 | 
				
			||||||
        // if(corrections.length > 0)
 | 
					        if(corrections.length > 0)
 | 
				
			||||||
        //     menuTemplate.unshift(...corrections.map((correction: string) => ({
 | 
					            menuTemplate.unshift(...corrections.map((correction: string) => ({
 | 
				
			||||||
        //         label: correction,
 | 
					                label: correction,
 | 
				
			||||||
        //         click: () => webContents.replaceMisspelling(correction)
 | 
					                click: () => webContents.replaceMisspelling(correction)
 | 
				
			||||||
        //     })));
 | 
					            })));
 | 
				
			||||||
        // else menuTemplate.unshift({enabled: false, label: l('spellchecker.noCorrections')});
 | 
					        else menuTemplate.unshift({enabled: false, label: l('spellchecker.noCorrections')});
 | 
				
			||||||
    } else if(settings.customDictionary.indexOf(props.selectionText) !== -1)
 | 
					    } else if(settings.customDictionary.indexOf(props.selectionText) !== -1)
 | 
				
			||||||
        menuTemplate.unshift({
 | 
					        menuTemplate.unshift({
 | 
				
			||||||
            label: l('spellchecker.remove'),
 | 
					            label: l('spellchecker.remove'),
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ export class GeneralSettings {
 | 
				
			|||||||
    profileViewer = true;
 | 
					    profileViewer = true;
 | 
				
			||||||
    host = defaultHost;
 | 
					    host = defaultHost;
 | 
				
			||||||
    logDirectory = path.join(electron.app.getPath('userData'), 'data');
 | 
					    logDirectory = path.join(electron.app.getPath('userData'), 'data');
 | 
				
			||||||
    spellcheckLang: string | undefined = 'en_GB';
 | 
					    spellcheckLang: string[] | string | undefined = 'en_GB';
 | 
				
			||||||
    theme = 'default';
 | 
					    theme = 'default';
 | 
				
			||||||
    version = electron.app.getVersion();
 | 
					    version = electron.app.getVersion();
 | 
				
			||||||
    beta = false;
 | 
					    beta = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import log from 'electron-log';  //tslint:disable-line:match-default-export-name
 | 
				
			|||||||
import * as fs from 'fs';
 | 
					import * as fs from 'fs';
 | 
				
			||||||
import * as path from 'path';
 | 
					import * as path from 'path';
 | 
				
			||||||
import {promisify} from 'util';
 | 
					import {promisify} from 'util';
 | 
				
			||||||
 | 
					import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dictDir = path.join(electron.app.getPath('userData'), 'spellchecker');
 | 
					const dictDir = path.join(electron.app.getPath('userData'), 'spellchecker');
 | 
				
			||||||
fs.mkdirSync(dictDir, {recursive: true});
 | 
					fs.mkdirSync(dictDir, {recursive: true});
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										67
									
								
								electron/language.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								electron/language.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getSafeLanguages(langs: string | string[] | undefined): string[] {
 | 
				
			||||||
 | 
					    return langs ? _.castArray(langs) : [];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const knownLanguageNames = {
 | 
				
			||||||
 | 
					    af: 'Afrikaans',
 | 
				
			||||||
 | 
					    bg: 'Bulgarian',
 | 
				
			||||||
 | 
					    ca: 'Catalan',
 | 
				
			||||||
 | 
					    cs: 'Czech',
 | 
				
			||||||
 | 
					    cy: 'Welsh',
 | 
				
			||||||
 | 
					    da: 'Danish',
 | 
				
			||||||
 | 
					    de: 'German',
 | 
				
			||||||
 | 
					    el: 'Greek',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'en-AU': 'English, Australian',
 | 
				
			||||||
 | 
					    'en-CA': 'English, Canadian',
 | 
				
			||||||
 | 
					    'en-GB': 'English, British',
 | 
				
			||||||
 | 
					    'en-US': 'English, American',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    es: 'Spanish',
 | 
				
			||||||
 | 
					    'es-419': 'Spanish, Latin America and Caribbean',
 | 
				
			||||||
 | 
					    'es-AR': 'Spanish, Argentine',
 | 
				
			||||||
 | 
					    'es-ES': 'Spanish, Castilian',
 | 
				
			||||||
 | 
					    'es-MX': 'Spanish, Mexican',
 | 
				
			||||||
 | 
					    'es-US': 'Spanish, American',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    et: 'Estonian',
 | 
				
			||||||
 | 
					    fa: 'Persian',
 | 
				
			||||||
 | 
					    fi: 'Finnish',
 | 
				
			||||||
 | 
					    fo: 'Faroese',
 | 
				
			||||||
 | 
					    fr: 'French',
 | 
				
			||||||
 | 
					    he: 'Hebrew',
 | 
				
			||||||
 | 
					    hi: 'Hindi',
 | 
				
			||||||
 | 
					    hr: 'Croatian',
 | 
				
			||||||
 | 
					    hu: 'Hungarian',
 | 
				
			||||||
 | 
					    hy: 'Armenian',
 | 
				
			||||||
 | 
					    id: 'Indonesian',
 | 
				
			||||||
 | 
					    it: 'Italian',
 | 
				
			||||||
 | 
					    ko: 'Korean',
 | 
				
			||||||
 | 
					    lt: 'Lithuanian',
 | 
				
			||||||
 | 
					    lv: 'Latvian',
 | 
				
			||||||
 | 
					    nb: 'Norwegian',
 | 
				
			||||||
 | 
					    nl: 'Dutch',
 | 
				
			||||||
 | 
					    pl: 'Polish',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    'pt-BR': 'Portuguese, Brazilian',
 | 
				
			||||||
 | 
					    'pt-PT': 'Portuguese, European',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ro: 'Romanian',
 | 
				
			||||||
 | 
					    ru: 'Russian',
 | 
				
			||||||
 | 
					    sh: 'Serbo-Croatian',
 | 
				
			||||||
 | 
					    sk: 'Slovak',
 | 
				
			||||||
 | 
					    sl: 'Slovenian',
 | 
				
			||||||
 | 
					    sq: 'Albanian',
 | 
				
			||||||
 | 
					    sr: 'Serbian',
 | 
				
			||||||
 | 
					    sv: 'Swedish',
 | 
				
			||||||
 | 
					    ta: 'Tamil',
 | 
				
			||||||
 | 
					    tg: 'Tajik',
 | 
				
			||||||
 | 
					    tr: 'Turkish',
 | 
				
			||||||
 | 
					    uk: 'Ukranian',
 | 
				
			||||||
 | 
					    vi: 'Vietnamese'
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										156
									
								
								electron/main.ts
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								electron/main.ts
									
									
									
									
									
								
							@ -41,13 +41,14 @@ import * as path from 'path';
 | 
				
			|||||||
// import * as url from 'url';
 | 
					// import * as url from 'url';
 | 
				
			||||||
import l from '../chat/localize';
 | 
					import l from '../chat/localize';
 | 
				
			||||||
import {defaultHost, GeneralSettings} from './common';
 | 
					import {defaultHost, GeneralSettings} from './common';
 | 
				
			||||||
import {ensureDictionary, getAvailableDictionaries} from './dictionaries';
 | 
					import { getSafeLanguages, knownLanguageNames } from './language';
 | 
				
			||||||
import * as windowState from './window_state';
 | 
					import * as windowState from './window_state';
 | 
				
			||||||
import BrowserWindow = Electron.BrowserWindow;
 | 
					import BrowserWindow = Electron.BrowserWindow;
 | 
				
			||||||
import MenuItem = Electron.MenuItem;
 | 
					import MenuItem = Electron.MenuItem;
 | 
				
			||||||
import { ElectronBlocker } from '@cliqz/adblocker-electron';
 | 
					import { ElectronBlocker } from '@cliqz/adblocker-electron';
 | 
				
			||||||
import fetch from 'node-fetch';
 | 
					import fetch from 'node-fetch';
 | 
				
			||||||
import MenuItemConstructorOptions = Electron.MenuItemConstructorOptions;
 | 
					import MenuItemConstructorOptions = Electron.MenuItemConstructorOptions;
 | 
				
			||||||
 | 
					import * as _ from 'lodash';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Module to control application life.
 | 
					// Module to control application life.
 | 
				
			||||||
const app = electron.app;
 | 
					const app = electron.app;
 | 
				
			||||||
@ -80,10 +81,46 @@ if(!settings.hwAcceleration) {
 | 
				
			|||||||
    app.disableHardwareAcceleration();
 | 
					    app.disableHardwareAcceleration();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function setDictionary(lang: string | undefined): Promise<void> {
 | 
					// async function setDictionary(lang: string | undefined): Promise<void> {
 | 
				
			||||||
    if(lang !== undefined) await ensureDictionary(lang);
 | 
					//     if(lang !== undefined) await ensureDictionary(lang);
 | 
				
			||||||
    settings.spellcheckLang = lang;
 | 
					//     settings.spellcheckLang = lang;
 | 
				
			||||||
 | 
					//     setGeneralSettings(settings);
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function updateSpellCheckerLanguages(langs: string[]): void {
 | 
				
			||||||
 | 
					    // console.log('Language support:', langs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const w of windows) {
 | 
				
			||||||
 | 
					        // console.log('LANG SEND');
 | 
				
			||||||
 | 
					        w.webContents.send('update-dictionaries', langs);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    electron.session.defaultSession.setSpellCheckerLanguages(langs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function toggleDictionary(lang: string): Promise<void> {
 | 
				
			||||||
 | 
					    const activeLangs = getSafeLanguages(settings.spellcheckLang);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // console.log('INITIAL LANG', activeLangs, lang);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let newLangs: string[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (_.indexOf(activeLangs, lang) >= 0) {
 | 
				
			||||||
 | 
					        newLangs = _.reject(activeLangs, (al) => (al === lang));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        activeLangs.push(lang);
 | 
				
			||||||
 | 
					        newLangs = activeLangs;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    settings.spellcheckLang = newLangs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setGeneralSettings(settings);
 | 
					    setGeneralSettings(settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // console.log('NEW LANG', newLangs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateSpellCheckerLanguages(newLangs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setGeneralSettings(value: GeneralSettings): void {
 | 
					function setGeneralSettings(value: GeneralSettings): void {
 | 
				
			||||||
@ -93,20 +130,23 @@ function setGeneralSettings(value: GeneralSettings): void {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function addSpellcheckerItems(menu: Electron.Menu): Promise<void> {
 | 
					async function addSpellcheckerItems(menu: Electron.Menu): Promise<void> {
 | 
				
			||||||
    if(settings.spellcheckLang !== undefined) await ensureDictionary(settings.spellcheckLang);
 | 
					    const selected = getSafeLanguages(settings.spellcheckLang);
 | 
				
			||||||
    const dictionaries = await getAvailableDictionaries();
 | 
					    const langs = electron.session.defaultSession.availableSpellCheckerLanguages;
 | 
				
			||||||
    const selected = settings.spellcheckLang;
 | 
					
 | 
				
			||||||
    menu.append(new electron.MenuItem({
 | 
					    const sortedLangs = _.sortBy(
 | 
				
			||||||
        type: 'radio',
 | 
					        _.map(
 | 
				
			||||||
        label: l('settings.spellcheck.disabled'),
 | 
					            langs,
 | 
				
			||||||
        click: async() => setDictionary(undefined)
 | 
					            (lang) => ({lang, name: (lang in knownLanguageNames) ? `${(knownLanguageNames as any)[lang]} (${lang})` : lang})
 | 
				
			||||||
    }));
 | 
					        ),
 | 
				
			||||||
    for(const lang of dictionaries)
 | 
					        'name'
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const lang of sortedLangs)
 | 
				
			||||||
        menu.append(new electron.MenuItem({
 | 
					        menu.append(new electron.MenuItem({
 | 
				
			||||||
            type: 'radio',
 | 
					            type: 'checkbox',
 | 
				
			||||||
            label: lang,
 | 
					            label: lang.name,
 | 
				
			||||||
            checked: lang === selected,
 | 
					            checked: (_.indexOf(selected, lang.lang) >= 0),
 | 
				
			||||||
            click: async() => setDictionary(lang)
 | 
					            click: async() => toggleDictionary(lang.lang)
 | 
				
			||||||
        }));
 | 
					        }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -140,6 +180,9 @@ function createWindow(): Electron.BrowserWindow | undefined {
 | 
				
			|||||||
    const window = new electron.BrowserWindow(windowProperties);
 | 
					    const window = new electron.BrowserWindow(windowProperties);
 | 
				
			||||||
    windows.push(window);
 | 
					    windows.push(window);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const safeLanguages = settings.spellcheckLang ? _.castArray(settings.spellcheckLang) : [];
 | 
				
			||||||
 | 
					    electron.session.defaultSession.setSpellCheckerLanguages(safeLanguages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // tslint:disable-next-line:no-floating-promises
 | 
					    // tslint:disable-next-line:no-floating-promises
 | 
				
			||||||
    ElectronBlocker.fromLists(
 | 
					    ElectronBlocker.fromLists(
 | 
				
			||||||
        fetch,
 | 
					        fetch,
 | 
				
			||||||
@ -160,31 +203,31 @@ function createWindow(): Electron.BrowserWindow | undefined {
 | 
				
			|||||||
        (blocker) => {
 | 
					        (blocker) => {
 | 
				
			||||||
            blocker.enableBlockingInSession(electron.session.defaultSession);
 | 
					            blocker.enableBlockingInSession(electron.session.defaultSession);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // console.log('Got this far!!!!');
 | 
					            // // console.log('Got this far!!!!');
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('request-blocked', (request: Request) => {
 | 
					            // blocker.on('request-blocked', (request: Request) => {
 | 
				
			||||||
                console.log('blocked', request.url);
 | 
					            //     console.log('blocked', request.url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('request-redirected', (request: Request) => {
 | 
					            // blocker.on('request-redirected', (request: Request) => {
 | 
				
			||||||
                console.log('redirected', request.url);
 | 
					            //     console.log('redirected', request.url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('request-whitelisted', (request: Request) => {
 | 
					            // blocker.on('request-whitelisted', (request: Request) => {
 | 
				
			||||||
                console.log('whitelisted', request.url);
 | 
					            //     console.log('whitelisted', request.url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('csp-injected', (request: Request) => {
 | 
					            // blocker.on('csp-injected', (request: Request) => {
 | 
				
			||||||
                console.log('csp', request.url);
 | 
					            //     console.log('csp', request.url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('script-injected', (script: string, url: string) => {
 | 
					            // blocker.on('script-injected', (script: string, url: string) => {
 | 
				
			||||||
                console.log('script', script.length, url);
 | 
					            //     console.log('script', script.length, url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
 | 
					            //
 | 
				
			||||||
            blocker.on('style-injected', (style: string, url: string) => {
 | 
					            // blocker.on('style-injected', (style: string, url: string) => {
 | 
				
			||||||
                console.log('style', style.length, url);
 | 
					            //     console.log('style', style.length, url);
 | 
				
			||||||
            });
 | 
					            // });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -302,14 +345,14 @@ function onReady(): void {
 | 
				
			|||||||
                {label: l('action.newWindow'), click: createWindow, accelerator: 'CmdOrCtrl+n'},
 | 
					                {label: l('action.newWindow'), click: createWindow, accelerator: 'CmdOrCtrl+n'},
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    label: l('action.newTab'),
 | 
					                    label: l('action.newTab'),
 | 
				
			||||||
                    click: (_: Electron.MenuItem, w: Electron.BrowserWindow) => {
 | 
					                    click: (_m: Electron.MenuItem, w: Electron.BrowserWindow) => {
 | 
				
			||||||
                        if(tabCount < 3) w.webContents.send('open-tab');
 | 
					                        if(tabCount < 3) w.webContents.send('open-tab');
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    accelerator: 'CmdOrCtrl+t'
 | 
					                    accelerator: 'CmdOrCtrl+t'
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    label: l('settings.logDir'),
 | 
					                    label: l('settings.logDir'),
 | 
				
			||||||
                    click: (_, window: BrowserWindow) => {
 | 
					                    click: (_m, window: BrowserWindow) => {
 | 
				
			||||||
                        const dir = electron.dialog.showOpenDialogSync(
 | 
					                        const dir = electron.dialog.showOpenDialogSync(
 | 
				
			||||||
                            {defaultPath: settings.logDirectory, properties: ['openDirectory']});
 | 
					                            {defaultPath: settings.logDirectory, properties: ['openDirectory']});
 | 
				
			||||||
                        if(dir !== undefined) {
 | 
					                        if(dir !== undefined) {
 | 
				
			||||||
@ -367,14 +410,14 @@ function onReady(): void {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }, {
 | 
					                }, {
 | 
				
			||||||
                    label: l('fixLogs.action'),
 | 
					                    label: l('fixLogs.action'),
 | 
				
			||||||
                    click: (_, window: BrowserWindow) => window.webContents.send('fix-logs')
 | 
					                    click: (_m, window: BrowserWindow) => window.webContents.send('fix-logs')
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {type: 'separator'},
 | 
					                {type: 'separator'},
 | 
				
			||||||
                {role: 'minimize'},
 | 
					                {role: 'minimize'},
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    accelerator: process.platform === 'darwin' ? 'Cmd+Q' : undefined,
 | 
					                    accelerator: process.platform === 'darwin' ? 'Cmd+Q' : undefined,
 | 
				
			||||||
                    label: l('action.quit'),
 | 
					                    label: l('action.quit'),
 | 
				
			||||||
                    click(_: Electron.MenuItem, window: Electron.BrowserWindow): void {
 | 
					                    click(_m: Electron.MenuItem, window: Electron.BrowserWindow): void {
 | 
				
			||||||
                        if(characters.length === 0) return app.quit();
 | 
					                        if(characters.length === 0) return app.quit();
 | 
				
			||||||
                        const button = electron.dialog.showMessageBoxSync(window, {
 | 
					                        const button = electron.dialog.showMessageBoxSync(window, {
 | 
				
			||||||
                            message: l('chat.confirmLeave'),
 | 
					                            message: l('chat.confirmLeave'),
 | 
				
			||||||
@ -426,7 +469,7 @@ function onReady(): void {
 | 
				
			|||||||
            ]
 | 
					            ]
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ]));
 | 
					    ]));
 | 
				
			||||||
    electron.ipcMain.on('tab-added', (_: Event, id: number) => {
 | 
					    electron.ipcMain.on('tab-added', (_event: Event, id: number) => {
 | 
				
			||||||
        const webContents = electron.webContents.fromId(id);
 | 
					        const webContents = electron.webContents.fromId(id);
 | 
				
			||||||
        setUpWebContents(webContents);
 | 
					        setUpWebContents(webContents);
 | 
				
			||||||
        ++tabCount;
 | 
					        ++tabCount;
 | 
				
			||||||
@ -437,7 +480,7 @@ function onReady(): void {
 | 
				
			|||||||
        --tabCount;
 | 
					        --tabCount;
 | 
				
			||||||
        for(const w of windows) w.webContents.send('allow-new-tabs', true);
 | 
					        for(const w of windows) w.webContents.send('allow-new-tabs', true);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    electron.ipcMain.on('save-login', (_: Event, account: string, host: string) => {
 | 
					    electron.ipcMain.on('save-login', (_event: Event, account: string, host: string) => {
 | 
				
			||||||
        settings.account = account;
 | 
					        settings.account = account;
 | 
				
			||||||
        settings.host = host;
 | 
					        settings.host = host;
 | 
				
			||||||
        setGeneralSettings(settings);
 | 
					        setGeneralSettings(settings);
 | 
				
			||||||
@ -447,16 +490,17 @@ function onReady(): void {
 | 
				
			|||||||
        characters.push(character);
 | 
					        characters.push(character);
 | 
				
			||||||
        e.returnValue = true;
 | 
					        e.returnValue = true;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    electron.ipcMain.on('dictionary-add', (_: Event, word: string) => {
 | 
					    electron.ipcMain.on('dictionary-add', (_event: Event, word: string) => {
 | 
				
			||||||
        if(settings.customDictionary.indexOf(word) !== -1) return;
 | 
					        // if(settings.customDictionary.indexOf(word) !== -1) return;
 | 
				
			||||||
        settings.customDictionary.push(word);
 | 
					        // settings.customDictionary.push(word);
 | 
				
			||||||
        setGeneralSettings(settings);
 | 
					        // setGeneralSettings(settings);
 | 
				
			||||||
 | 
					        for(const w of windows) w.webContents.session.addWordToSpellCheckerDictionary(word);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    electron.ipcMain.on('dictionary-remove', (_: Event, word: string) => {
 | 
					    electron.ipcMain.on('dictionary-remove', (_event: Event /*, word: string*/) => {
 | 
				
			||||||
        settings.customDictionary.splice(settings.customDictionary.indexOf(word), 1);
 | 
					        // settings.customDictionary.splice(settings.customDictionary.indexOf(word), 1);
 | 
				
			||||||
        setGeneralSettings(settings);
 | 
					        // setGeneralSettings(settings);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    electron.ipcMain.on('disconnect', (_: Event, character: string) => {
 | 
					    electron.ipcMain.on('disconnect', (_event: Event, character: string) => {
 | 
				
			||||||
        const index = characters.indexOf(character);
 | 
					        const index = characters.indexOf(character);
 | 
				
			||||||
        if(index !== -1) characters.splice(index, 1);
 | 
					        if(index !== -1) characters.splice(index, 1);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
				
			|||||||
@ -20,12 +20,13 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0
 | 
				
			|||||||
*   Ad auto-posting
 | 
					*   Ad auto-posting
 | 
				
			||||||
    *    Manage channel ad settings via "Tab Settings"
 | 
					    *    Manage channel ad settings via "Tab Settings"
 | 
				
			||||||
    *    Automatically re-post ads every 11-18 minutes (randomized) for up to 180 minutes
 | 
					    *    Automatically re-post ads every 11-18 minutes (randomized) for up to 180 minutes
 | 
				
			||||||
    *    Rotate multiple ads on a single channel by entering multiple ads in "Tab Settings"
 | 
					    *    Rotate multiple ads on a single channel by entering multiple ads in "Ad Settings"
 | 
				
			||||||
*   Ad ratings
 | 
					*   Ad ratings
 | 
				
			||||||
    *    LFP ads are automatically rated and matched against your profile
 | 
					    *    LFP ads are automatically rated (great/good/maybe/no) and matched against your profile
 | 
				
			||||||
*   Link previews
 | 
					*   Link previews
 | 
				
			||||||
    *    Hover cursor over any `[url]` to see a preview of it
 | 
					    *    Hover cursor over any `[url]` to see a preview of it
 | 
				
			||||||
    *    Middle click any `[url]` to turn the preview into a sticky / interactive mode
 | 
					    *    Middle click any `[url]` to turn the preview into a sticky / interactive mode
 | 
				
			||||||
 | 
					    *    Link preview has an ad-blocker to minimize page load times and protect against unfriendly scripts 
 | 
				
			||||||
*   Profile
 | 
					*   Profile
 | 
				
			||||||
    *    Kinks are auto-compared when viewing character profile
 | 
					    *    Kinks are auto-compared when viewing character profile
 | 
				
			||||||
    *    Custom kink explanations can be expanded inline
 | 
					    *    Custom kink explanations can be expanded inline
 | 
				
			||||||
@ -51,6 +52,10 @@ This repository contains a heavily customized version of the mainline F-Chat 3.0
 | 
				
			|||||||
    *    Conversation dialog can be opened by typing in a character name
 | 
					    *    Conversation dialog can be opened by typing in a character name
 | 
				
			||||||
    *    Message search matches character names
 | 
					    *    Message search matches character names
 | 
				
			||||||
    *    PM list shows characters' online status as a colored icon
 | 
					    *    PM list shows characters' online status as a colored icon
 | 
				
			||||||
 | 
					*   Details for Nerds
 | 
				
			||||||
 | 
					    *    Upgraded to Electron 8.x
 | 
				
			||||||
 | 
					    *    Replaced node-spellchecker with the built-in spellchecker of Electron 8
 | 
				
			||||||
 | 
					    *    Multi-language support for spell checking (Windows only – language support is fully automatic on MacOS) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## How to Set Up Ads
 | 
					## How to Set Up Ads
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user