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

Lines changed: 658 additions & 163 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
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

Lines changed: 12 additions & 9 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 18 additions & 3 deletions
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

Lines changed: 43 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 13 additions & 15 deletions
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

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/helpers/i18n-helpers.js

Lines changed: 27 additions & 0 deletions
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+
}

0 commit comments

Comments
 (0)