Skip to content

Commit 3d87c0e

Browse files
committed
feat(App): Improved spell checker & context menu
1 parent 8b64855 commit 3d87c0e

File tree

21 files changed

+1188
-353
lines changed

21 files changed

+1188
-353
lines changed

package-lock.json

+658-163
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,22 @@
3636
"classnames": "^2.2.5",
3737
"debug-electron": "^0.0.4",
3838
"du": "^0.1.0",
39+
"electron-dl": "1.12.0",
3940
"electron-fetch": "1.3.0",
41+
"electron-hunspell": "0.1.1",
4042
"electron-react-titlebar": "0.8.1",
41-
"electron-spellchecker": "^1.1.2",
4243
"electron-updater": "^4.0.4",
4344
"electron-window-state": "^4.1.0",
4445
"fs-extra": "7.0.1",
4546
"gulp-cli": "1.2.2",
47+
"hunspell-dict-downloader": "1.0.0",
4648
"ini": "^1.3.4",
4749
"jshashes": "^1.0.6",
4850
"jsonwebtoken": "^7.4.1",
4951
"keymaster": "^1.6.2",
5052
"lodash": "^4.17.4",
5153
"mdi": "^1.9.33",
54+
"mime-types": "2.1.21",
5255
"minimist": "^1.2.0",
5356
"mkdirp": "^0.5.1",
5457
"mobx": "^3.1.0",

src/components/settings/settings/EditSettingsForm.js

+12-9
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ export default @observer class EditSettingsForm extends Component {
168168
{/* Language */}
169169
<h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2>
170170
<Select field={form.$('locale')} showLabel={false} />
171+
<PremiumFeatureContainer
172+
condition={isSpellcheckerPremiumFeature}
173+
>
174+
<div>
175+
<Toggle
176+
field={form.$('enableSpellchecking')}
177+
/>
178+
{form.$('enableSpellchecking').value && (
179+
<Select field={form.$('spellcheckerLanguage')} />
180+
)}
181+
</div>
182+
</PremiumFeatureContainer>
171183
<a
172184
href={FRANZ_TRANSLATION}
173185
target="_blank"
@@ -178,17 +190,8 @@ export default @observer class EditSettingsForm extends Component {
178190

179191
{/* Advanced */}
180192
<h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2>
181-
<PremiumFeatureContainer
182-
condition={isSpellcheckerPremiumFeature}
183-
>
184-
<Toggle
185-
field={form.$('enableSpellchecking')}
186-
disabled
187-
/>
188-
</PremiumFeatureContainer>
189193
<Toggle field={form.$('enableGPUAcceleration')} />
190194
<p className="settings__help">{intl.formatMessage(messages.enableGPUAccelerationInfo)}</p>
191-
{/* <Select field={form.$('spellcheckingLanguage')} /> */}
192195
<div className="settings__settings-group">
193196
<h3>
194197
{intl.formatMessage(messages.subheadlineCache)}

src/config.js

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const DEFAULT_APP_SETTINGS = {
1717
showDisabledServices: true,
1818
showMessageBadgeWhenMuted: true,
1919
enableSpellchecking: true,
20+
spellcheckerLanguage: 'en-us',
2021
darkMode: false,
2122
locale: '',
2223
fallbackLocale: 'en-US',
@@ -35,3 +36,5 @@ export const FILE_SYSTEM_SETTINGS_TYPES = [
3536
];
3637

3738
export const SETTINGS_PATH = path.join(app.getPath('userData'), 'config');
39+
40+
export const DICTIONARY_PATH = path.join(app.getPath('userData'), 'dicts');

src/containers/settings/EditSettingsScreen.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import AppStore from '../../stores/AppStore';
77
import SettingsStore from '../../stores/SettingsStore';
88
import UserStore from '../../stores/UserStore';
99
import Form from '../../lib/Form';
10-
import { APP_LOCALES } from '../../i18n/languages';
10+
import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages';
1111
import { gaPage } from '../../lib/analytics';
1212
import { DEFAULT_APP_SETTINGS } from '../../config';
1313
import { config as spellcheckerConfig } from '../../features/spellchecker';
@@ -60,8 +60,8 @@ const messages = defineMessages({
6060
id: 'settings.app.form.enableGPUAcceleration',
6161
defaultMessage: '!!!Enable GPU Acceleration',
6262
},
63-
spellcheckingLanguage: {
64-
id: 'settings.app.form.spellcheckingLanguage',
63+
spellcheckerLanguage: {
64+
id: 'settings.app.form.spellcheckerLanguage',
6565
defaultMessage: '!!!Language for spell checking',
6666
},
6767
beta: {
@@ -98,6 +98,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
9898
darkMode: settingsData.darkMode,
9999
showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted,
100100
enableSpellchecking: settingsData.enableSpellchecking,
101+
spellcheckerLanguage: settingsData.spellcheckerLanguage,
101102
beta: settingsData.beta, // we need this info in the main process as well
102103
locale: settingsData.locale, // we need this info in the main process as well
103104
},
@@ -123,6 +124,14 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
123124
});
124125
});
125126

127+
const spellcheckingLanguages = [];
128+
Object.keys(SPELLCHECKER_LOCALES).sort(Intl.Collator().compare).forEach((key) => {
129+
spellcheckingLanguages.push({
130+
value: key,
131+
label: SPELLCHECKER_LOCALES[key],
132+
});
133+
});
134+
126135
const config = {
127136
fields: {
128137
autoLaunchOnStart: {
@@ -165,6 +174,12 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
165174
value: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremiumFeature ? false : settings.all.app.enableSpellchecking,
166175
default: !this.props.stores.user.data.isPremium && spellcheckerConfig.isPremiumFeature ? false : DEFAULT_APP_SETTINGS.enableSpellchecking,
167176
},
177+
spellcheckerLanguage: {
178+
label: intl.formatMessage(messages.spellcheckerLanguage),
179+
value: settings.all.app.spellcheckerLanguage,
180+
options: spellcheckingLanguages,
181+
default: DEFAULT_APP_SETTINGS.spellcheckerLanguage,
182+
},
168183
darkMode: {
169184
label: intl.formatMessage(messages.darkMode),
170185
value: settings.all.app.darkMode,

src/electron/ipc-api/download.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ipcMain, dialog } from 'electron';
2+
import { download } from 'electron-dl';
3+
import mime from 'mime-types';
4+
import fs from 'fs-extra';
5+
6+
const debug = require('debug')('Franz:ipcApi:download');
7+
8+
function decodeBase64Image(dataString) {
9+
const matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
10+
11+
if (matches.length !== 3) {
12+
return new Error('Invalid input string');
13+
}
14+
15+
return new Buffer(matches[2], 'base64');
16+
}
17+
18+
export default (params) => {
19+
ipcMain.on('download-file', async (event, { url, content, fileOptions = {} }) => {
20+
try {
21+
if (!content) {
22+
const dl = await download(params.mainWindow, url, {
23+
saveAs: true,
24+
});
25+
debug('File saved to', dl.getSavePath());
26+
} else {
27+
const extension = mime.extension(fileOptions.mime);
28+
const filename = `${fileOptions.name}.${extension}`;
29+
30+
dialog.showSaveDialog(params.mainWindow, {
31+
defaultPath: filename,
32+
}, (name) => {
33+
const binaryImage = decodeBase64Image(content);
34+
fs.writeFileSync(name, binaryImage, 'binary');
35+
36+
debug('File blob saved to', name);
37+
});
38+
}
39+
} catch (e) {
40+
console.error(e);
41+
}
42+
});
43+
};

src/electron/ipc-api/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import autoUpdate from './autoUpdate';
22
import settings from './settings';
33
import appIndicator from './appIndicator';
4+
import download from './download';
45

56
export default (params) => {
67
settings(params);
78
autoUpdate(params);
89
appIndicator(params);
10+
download(params);
911
};

src/features/spellchecker/index.js

+13-15
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,24 @@ export default function init(stores) {
1212
reaction(
1313
() => stores.features.features.isSpellcheckerPremiumFeature,
1414
(enabled, r) => {
15-
if (enabled) {
16-
debug('Initializing `spellchecker` feature');
15+
debug('Initializing `spellchecker` feature');
1716

18-
// Dispose the reaction to run this only once
19-
r.dispose();
17+
// Dispose the reaction to run this only once
18+
r.dispose();
2019

21-
const { isSpellcheckerPremiumFeature } = stores.features.features;
20+
const { isSpellcheckerPremiumFeature } = stores.features.features;
2221

23-
config.isPremiumFeature = isSpellcheckerPremiumFeature !== undefined ? isSpellcheckerPremiumFeature : DEFAULT_IS_PREMIUM_FEATURE;
22+
config.isPremiumFeature = isSpellcheckerPremiumFeature !== undefined ? isSpellcheckerPremiumFeature : DEFAULT_IS_PREMIUM_FEATURE;
2423

25-
autorun(() => {
26-
if (!stores.user.data.isPremium && config.isPremiumFeature) {
27-
debug('Override settings.spellcheckerEnabled flag to false');
24+
autorun(() => {
25+
if (!stores.user.data.isPremium && config.isPremiumFeature) {
26+
debug('Override settings.spellcheckerEnabled flag to false');
2827

29-
Object.assign(stores.settings.all.app, {
30-
enableSpellchecker: false,
31-
});
32-
}
33-
});
34-
}
28+
Object.assign(stores.settings.all.app, {
29+
enableSpellchecker: false,
30+
});
31+
}
32+
});
3533
},
3634
);
3735
}

src/features/spellchecker/styles.js

-26
This file was deleted.

src/helpers/i18n-helpers.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export function getLocale({ locale, locales, defaultLocale, fallbackLocale }) {
2+
let localeStr = locale;
3+
if (locales[locale] === undefined) {
4+
let localeFuzzy;
5+
Object.keys(locales).forEach((localStr) => {
6+
if (locales && Object.hasOwnProperty.call(locales, localStr)) {
7+
if (locale.substring(0, 2) === localStr.substring(0, 2)) {
8+
localeFuzzy = localStr;
9+
}
10+
}
11+
});
12+
13+
if (localeFuzzy !== undefined) {
14+
localeStr = localeFuzzy;
15+
}
16+
}
17+
18+
if (locales[localeStr] === undefined) {
19+
localeStr = defaultLocale;
20+
}
21+
22+
if (!localeStr) {
23+
localeStr = fallbackLocale;
24+
}
25+
26+
return localeStr;
27+
}

src/i18n/languages.js

+39-41
Original file line numberDiff line numberDiff line change
@@ -27,45 +27,43 @@ export const APP_LOCALES = {
2727
es: 'Español',
2828
};
2929

30-
export default APP_LOCALES;
30+
// Hunspell compatible keys
31+
export const SPELLCHECKER_LOCALES = {
32+
'bg-bg': 'български език',
33+
'ca-es': 'Català',
34+
'cs-cz': 'Čeština',
35+
'da-dk': 'Dansk',
36+
'de-de': 'Deutsch',
37+
'el-gr': 'λληνικά (Greek)',
38+
'en-us': 'English',
39+
'es-es': 'Español',
40+
'et-ee': 'Estonian',
41+
'fa-ir': 'فارسی (Persian)',
42+
'fo-fo': 'Faroese',
43+
'fr-fr': 'Français',
44+
'he-il': 'עברית (Hebrew)',
45+
'hr-hr': 'Hrvatski jezik',
46+
'hu-hu': 'Magyar',
47+
'it-it': 'Italiano',
48+
ko: 'Korean',
49+
'lt-lt': 'Lietuvių kalba',
50+
'lv-lv': 'Latviešu valoda',
51+
'nb-no': 'Norsk bokmål',
52+
'nl-nl': 'Nederlands',
53+
'pl-pl': 'Język polski',
54+
'pt-br': 'Português (Brazil)',
55+
'pt-pt': 'Português',
56+
'ro-ro': 'Limba română',
57+
'ru-ru': 'Русский (Russian)',
58+
'sk-sk': 'Slovenčina',
59+
'sl-si': 'Slovenski jezik',
60+
sr: 'Српски језик (Serbian)',
61+
'sv-se': 'Svenska',
62+
'ta-in': 'தமிழ் (Tamil)',
63+
'tg-tg': 'Тоҷикӣ (Tajik)',
64+
tr: 'Türkçe',
65+
'uk-ua': 'Українська (Ukrainian)',
66+
vi: 'Tiếng Việt',
67+
};
3168

32-
// export const SPELLCHECKER_LOCALES = {
33-
// af: 'Afrikaans',
34-
// sq: 'Albanian',
35-
// ar: 'Arabic',
36-
// bg: 'Bulgarian',
37-
// zh: 'Chinese',
38-
// hr: 'Croatian',
39-
// cs: 'Czech',
40-
// da: 'Danish',
41-
// nl: 'Dutch',
42-
// en: 'English',
43-
// 'en-AU': 'English (AU)',
44-
// 'en-CA': 'English (CA)',
45-
// 'en-GB': 'English (GB)',
46-
// fi: 'Finnish',
47-
// fr: 'French',
48-
// ka: 'Georgian',
49-
// de: 'German',
50-
// el: 'Greek, Modern',
51-
// hi: 'Hindi',
52-
// hu: 'Hungarian',
53-
// id: 'Indonesian',
54-
// it: 'Italian',
55-
// ja: 'Japanese',
56-
// jv: 'Javanese',
57-
// ko: 'Korean',
58-
// lt: 'Lithuanian',
59-
// lv: 'Latvian',
60-
// ms: 'Malay',
61-
// no: 'Norwegian',
62-
// pl: 'Polish',
63-
// pt: 'Portuguese',
64-
// ro: 'Romanian, Moldavian, Moldovan',
65-
// ru: 'Russian',
66-
// sk: 'Slovak',
67-
// es: 'Spanish',
68-
// sv: 'Swedish',
69-
// uk: 'Ukrainian',
70-
// vi: 'Vietnamese',
71-
// };
69+
export default APP_LOCALES;

src/i18n/locales/en-US.json

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
"settings.app.form.runInBackground": "Keep Franz in background when closing the window",
175175
"settings.app.form.language": "Language",
176176
"settings.app.form.enableSpellchecking": "Enable spell checking",
177+
"settings.app.form.spellcheckerLanguage": "Spell checking language",
177178
"settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration",
178179
"settings.app.form.showDisabledServices": "Display disabled services tabs",
179180
"settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled",

0 commit comments

Comments
 (0)