1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-03 08:35:29 +02:00
joplin/ReactNativeClient/lib/models/Setting.js

1126 lines
41 KiB
JavaScript
Raw Normal View History

2017-12-14 20:12:14 +02:00
const BaseModel = require('lib/BaseModel.js');
const { Database } = require('lib/database.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
const { time } = require('lib/time-utils.js');
const { sprintf } = require('sprintf-js');
2018-02-21 21:58:28 +02:00
const ObjectUtils = require('lib/ObjectUtils');
const { toTitleCase } = require('lib/string-utils.js');
const { rtrimSlashes, toSystemSlashes } = require('lib/path-utils.js');
const { _, supportedLocalesToLanguages, defaultLocale } = require('lib/locale.js');
const { shim } = require('lib/shim');
2017-05-12 22:17:23 +02:00
class Setting extends BaseModel {
static tableName() {
return 'settings';
}
2017-07-03 21:50:45 +02:00
static modelType() {
return BaseModel.TYPE_SETTING;
2017-06-19 21:26:27 +02:00
}
static metadata() {
if (this.metadata_) return this.metadata_;
const platform = shim.platformName();
const mobilePlatform = shim.mobilePlatform();
let wysiwygYes = '';
let wysiwygNo = '';
if (shim.isElectron()) {
wysiwygYes = ` ${_('(wysiwyg: %s)', _('yes'))}`;
wysiwygNo = ` ${_('(wysiwyg: %s)', _('no'))}`;
}
2019-04-18 15:59:17 +02:00
const emptyDirWarning = _('Attention: If you change this location, make sure you copy all your content to it before syncing, otherwise all files will be removed! See the FAQ for more details: %s', 'https://joplinapp.org/faq/');
// A "public" setting means that it will show up in the various config screens (or config command for the CLI tool), however
// if if private a setting might still be handled and modified by the app. For instance, the settings related to sorting notes are not
// public for the mobile and desktop apps because they are handled separately in menus.
this.metadata_ = {
'clientId': {
value: '',
type: Setting.TYPE_STRING,
public: false,
},
'editor.keyboardMode': {
value: 'default',
type: Setting.TYPE_STRING,
public: true,
appTypes: ['desktop'],
isEnum: true,
label: () => _('Keyboard Mode'),
options: () => {
const output = {};
output['default'] = _('Default');
output['emacs'] = _('Emacs');
output['vim'] = _('Vim');
return output;
},
},
2019-07-29 15:43:53 +02:00
'sync.target': {
value: SyncTargetRegistry.nameToId('dropbox'),
type: Setting.TYPE_INT,
isEnum: true,
public: true,
section: 'sync',
label: () => _('Synchronisation target'),
description: appType => {
return appType !== 'cli' ? null : _('The target to synchonise to. Each sync target may have additional parameters which are named as `sync.NUM.NAME` (all documented below).');
},
options: () => {
return SyncTargetRegistry.idAndLabelPlainObject(platform);
2019-07-29 15:43:53 +02:00
},
},
'sync.2.path': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
try {
return settings['sync.target'] == SyncTargetRegistry.nameToId('filesystem');
} catch (error) {
return false;
}
},
filter: value => {
return value ? rtrimSlashes(value) : '';
},
public: true,
label: () => _('Directory to synchronise with (absolute path)'),
description: () => emptyDirWarning,
},
'sync.5.path': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud');
},
public: true,
label: () => _('Nextcloud WebDAV URL'),
description: () => emptyDirWarning,
},
'sync.5.username': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud');
},
public: true,
label: () => _('Nextcloud username'),
},
'sync.5.password': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('nextcloud');
},
public: true,
label: () => _('Nextcloud password'),
secure: true,
},
'sync.6.path': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('webdav');
},
public: true,
label: () => _('WebDAV URL'),
description: () => emptyDirWarning,
},
'sync.6.username': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('webdav');
},
public: true,
label: () => _('WebDAV username'),
},
'sync.6.password': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
show: settings => {
return settings['sync.target'] == SyncTargetRegistry.nameToId('webdav');
},
public: true,
label: () => _('WebDAV password'),
secure: true,
},
'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.7.auth': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.1.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.2.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.3.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.4.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.7.context': { value: '', type: Setting.TYPE_STRING, public: false },
'sync.5.syncTargets': { value: {}, type: Setting.TYPE_OBJECT, public: false },
2019-07-29 15:43:53 +02:00
'sync.resourceDownloadMode': {
value: 'always',
type: Setting.TYPE_STRING,
section: 'sync',
public: true,
advanced: true,
2019-07-29 15:43:53 +02:00
isEnum: true,
appTypes: ['mobile', 'desktop'],
label: () => _('Attachment download behaviour'),
description: () => _('In "Manual" mode, attachments are downloaded only when you click on them. In "Auto", they are downloaded when you open the note. In "Always", all the attachments are downloaded whether you open the note or not.'),
options: () => {
return {
always: _('Always'),
manual: _('Manual'),
auto: _('Auto'),
};
},
},
'sync.maxConcurrentConnections': { value: 5, type: Setting.TYPE_INT, public: true, advanced: true, section: 'sync', label: () => _('Max concurrent connections'), minimum: 1, maximum: 20, step: 1 },
2019-07-29 15:43:53 +02:00
activeFolderId: { value: '', type: Setting.TYPE_STRING, public: false },
firstStart: { value: true, type: Setting.TYPE_BOOL, public: false },
locale: {
value: defaultLocale(),
type: Setting.TYPE_STRING,
isEnum: true,
public: true,
label: () => _('Language'),
options: () => {
return ObjectUtils.sortByValue(supportedLocalesToLanguages({ includeStats: true }));
},
},
dateFormat: {
value: Setting.DATE_FORMAT_1,
type: Setting.TYPE_STRING,
isEnum: true,
public: true,
label: () => _('Date format'),
options: () => {
const options = {};
2019-07-29 15:43:53 +02:00
const now = new Date('2017-01-30T12:00:00').getTime();
options[Setting.DATE_FORMAT_1] = time.formatMsToLocal(now, Setting.DATE_FORMAT_1);
options[Setting.DATE_FORMAT_2] = time.formatMsToLocal(now, Setting.DATE_FORMAT_2);
options[Setting.DATE_FORMAT_3] = time.formatMsToLocal(now, Setting.DATE_FORMAT_3);
options[Setting.DATE_FORMAT_4] = time.formatMsToLocal(now, Setting.DATE_FORMAT_4);
options[Setting.DATE_FORMAT_5] = time.formatMsToLocal(now, Setting.DATE_FORMAT_5);
options[Setting.DATE_FORMAT_6] = time.formatMsToLocal(now, Setting.DATE_FORMAT_6);
options[Setting.DATE_FORMAT_7] = time.formatMsToLocal(now, Setting.DATE_FORMAT_7);
2019-07-29 15:43:53 +02:00
return options;
},
},
timeFormat: {
value: Setting.TIME_FORMAT_1,
type: Setting.TYPE_STRING,
isEnum: true,
public: true,
label: () => _('Time format'),
options: () => {
const options = {};
2019-07-29 15:43:53 +02:00
const now = new Date('2017-01-30T20:30:00').getTime();
options[Setting.TIME_FORMAT_1] = time.formatMsToLocal(now, Setting.TIME_FORMAT_1);
options[Setting.TIME_FORMAT_2] = time.formatMsToLocal(now, Setting.TIME_FORMAT_2);
return options;
},
},
theme: {
value: Setting.THEME_LIGHT,
type: Setting.TYPE_INT,
public: true,
appTypes: ['mobile', 'desktop'],
isEnum: true,
label: () => _('Theme'),
section: 'appearance',
options: () => {
const output = {};
2019-07-29 15:43:53 +02:00
output[Setting.THEME_LIGHT] = _('Light');
output[Setting.THEME_DARK] = _('Dark');
if (platform !== mobilePlatform) {
output[Setting.THEME_DRACULA] = _('Dracula');
output[Setting.THEME_SOLARIZED_LIGHT] = _('Solarised Light');
output[Setting.THEME_SOLARIZED_DARK] = _('Solarised Dark');
output[Setting.THEME_NORD] = _('Nord');
output[Setting.THEME_ARITIM_DARK] = _('Aritim Dark');
} else {
output[Setting.THEME_OLED_DARK] = _('OLED Dark');
}
2019-07-29 15:43:53 +02:00
return output;
},
},
2019-11-11 10:44:54 +02:00
showNoteCounts: { value: true, type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Show note counts') },
layoutButtonSequence: {
value: Setting.LAYOUT_ALL,
type: Setting.TYPE_INT,
public: false,
appTypes: ['desktop'],
isEnum: true,
options: () => ({
[Setting.LAYOUT_ALL]: _('%s / %s / %s', _('Editor'), _('Viewer'), _('Split View')),
[Setting.LAYOUT_EDITOR_VIEWER]: _('%s / %s', _('Editor'), _('Viewer')),
[Setting.LAYOUT_EDITOR_SPLIT]: _('%s / %s', _('Editor'), _('Split View')),
[Setting.LAYOUT_VIEWER_SPLIT]: _('%s / %s', _('Viewer'), _('Split View')),
Desktop: Resolves #176: Added experimental WYSIWYG editor (#2556) * Trying to get TuiEditor to work * Tests with TinyMCE * Fixed build * Improved asset loading * Added support for Joplin source blocks * Added support for Joplin source blocks * Better integration * Make sure noteDidUpdate event is always dispatched at the right time * Minor tweaks * Fixed tests * Add support for checkboxes * Minor refactoring * Added support for file attachments * Add support for fenced code blocks * Fix new line issue on code block * Added support for Fountain scripts * Refactoring * Better handling of saving and loading notes * Fix saving and loading ntoes * Handle multi-note selection and fixed new note creation issue * Fixed newline issue in test * Fixed newline issue in test * Improve saving and loading * Improve saving and loading note * Removed undeeded prop * Fixed issue when new note being saved is incorrectly reloaded * Refactoring and improve saving of note when unmounting component * Fixed TypeScript error * Small changes * Improved further handling of saving and loading notes * Handle provisional notes and fixed various saving and loading bugs * Adding back support for HTML notes * Added support for HTML notes * Better handling of editable nodes * Preserve image HTML tag when the size is set * Handle switching between editor when the note has note finished saving * Handle templates * Handle templates * Handle loading note that is being saved * Handle note being reloaded via sync * Clean up * Clean up and improved logging * Fixed TS error * Fixed a few issues * Fixed test * Logging * Various improvements * Add blockquote support * Moved CWD operation to shim * Removed deleted files * Added support for Joplin commands
2020-03-10 01:24:57 +02:00
[Setting.LAYOUT_SPLIT_WYSIWYG]: _('%s / %s', _('Split'), 'WYSIWYG (Experimental)'),
}),
},
2019-07-29 15:43:53 +02:00
uncompletedTodosOnTop: { value: true, type: Setting.TYPE_BOOL, section: 'note', public: true, appTypes: ['cli'], label: () => _('Uncompleted to-dos on top') },
showCompletedTodos: { value: true, type: Setting.TYPE_BOOL, section: 'note', public: true, appTypes: ['cli'], label: () => _('Show completed to-dos') },
'notes.sortOrder.field': {
value: 'user_updated_time',
type: Setting.TYPE_STRING,
section: 'note',
isEnum: true,
public: true,
appTypes: ['cli'],
label: () => _('Sort notes by'),
options: () => {
const Note = require('lib/models/Note');
const noteSortFields = ['user_updated_time', 'user_created_time', 'title'];
const options = {};
for (let i = 0; i < noteSortFields.length; i++) {
options[noteSortFields[i]] = toTitleCase(Note.fieldToLabel(noteSortFields[i]));
}
return options;
},
},
'editor.autoMatchingBraces': {
value: true,
type: Setting.TYPE_BOOL,
public: true,
section: 'note',
appTypes: ['desktop'],
label: () => _('Auto-pair braces, parenthesis, quotations, etc.'),
},
'notes.sortOrder.reverse': { value: true, type: Setting.TYPE_BOOL, section: 'note', public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] },
2019-07-29 15:43:53 +02:00
'folders.sortOrder.field': {
value: 'title',
type: Setting.TYPE_STRING,
isEnum: true,
public: true,
appTypes: ['cli'],
label: () => _('Sort notebooks by'),
options: () => {
const Folder = require('lib/models/Folder');
const folderSortFields = ['title', 'last_note_user_updated_time'];
const options = {};
for (let i = 0; i < folderSortFields.length; i++) {
options[folderSortFields[i]] = toTitleCase(Folder.fieldToLabel(folderSortFields[i]));
}
return options;
},
},
'folders.sortOrder.reverse': { value: false, type: Setting.TYPE_BOOL, public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] },
2019-07-29 15:43:53 +02:00
trackLocation: { value: true, type: Setting.TYPE_BOOL, section: 'note', public: true, label: () => _('Save geo-location with notes') },
Mobile: Add toolbar, list continuation and Markdown preview to editor (#2224) * The basic editor is working! No list continuation still though * List continuation is working! Now to delete when entering again and not typing on line + handle ordered lists * Supports checkboxes + attempted at setting font * Editor font works now; now need to fix the delete (look at past state) * Fix deletion problem * Add ordered list handler * Add comments * Extract insertListLine * End lists on enter for empty bullets * Add MarkdownView (renders badly though) * Save edited text from MarkdownEditor * Cleanup * Refactor react-native-markdown-editor/ * Rename react-native-markdown-editor/ => MarkdownEditor/ * Cleanup * Fix preview styles; still need to fix checkbox problem * Fix keyboard padding * Change name back to #body_changeText * Incorporate PR feedback from @laurent22 * wip: Move MarkdownEditor/ from ReactNativeClient/lib/ to ReactNativeClient/ * Move MarkdownEditor/ from ReactNativeClient/lib/ to ReactNativeClient/ * Remove log statement * Focus TextInput in MarkdownEditor from grandparent * Make eslint happy * Extract textInputRefName to shared variable * Remove accidental #setState * Cleanup * Cleanup * Run linter * Cleanup * Update button order * Improve styles for config descriptions * Allow descriptions to be added to BOOL type Setting configs * Add editorBeta Setting * Move FailSafe details to description text * Update descriptionText styles * Put the editor under the beta flag toggle * Incorporate PR feedback from @laurent22 * Refactor Markdown editor focusing * Cleanup * Reorder MarkdownEditor formats * Make applyListFormat behavior more intuitive * Add comment * Show MarkdownEditor with preview by default * Show preview by default, then hide on typing * Fix MarkdownEditor selection bug * Cleanup * Update Markdown button styles * Make Markdown button colors theme-conscious * Fix merge conflict resolution mistake * Fix broken import * Delete package-lock.json * Reset package-lock.json Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2020-03-25 12:50:45 +02:00
'editor.beta': {
value: false,
type: Setting.TYPE_BOOL,
section: 'note',
public: true,
appTypes: ['mobile'],
label: () => 'Opt-in to the editor beta',
description: () => 'This beta adds list continuation, Markdown preview, and Markdown shortcuts. If you find bugs, please report them in the Discourse forum.',
},
2019-07-29 15:43:53 +02:00
newTodoFocus: {
value: 'title',
type: Setting.TYPE_STRING,
section: 'note',
isEnum: true,
public: true,
appTypes: ['desktop'],
label: () => _('When creating a new to-do:'),
options: () => {
return {
title: _('Focus title'),
body: _('Focus body'),
};
},
},
newNoteFocus: {
value: 'body',
type: Setting.TYPE_STRING,
section: 'note',
isEnum: true,
public: true,
appTypes: ['desktop'],
label: () => _('When creating a new note:'),
options: () => {
return {
title: _('Focus title'),
body: _('Focus body'),
};
},
},
// Deprecated - use markdown.plugin.*
'markdown.softbreaks': { value: false, type: Setting.TYPE_BOOL, public: false, appTypes: ['mobile', 'desktop'] },
'markdown.typographer': { value: false, type: Setting.TYPE_BOOL, public: false, appTypes: ['mobile', 'desktop'] },
// Deprecated
'markdown.plugin.softbreaks': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable soft breaks')}${wysiwygYes}` },
'markdown.plugin.typographer': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable typographer support')}${wysiwygYes}` },
'markdown.plugin.katex': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable math expressions')}${wysiwygYes}` },
'markdown.plugin.fountain': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Fountain syntax support')}${wysiwygYes}` },
'markdown.plugin.mermaid': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable Mermaid diagrams support')}${wysiwygYes}` },
'markdown.plugin.mark': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ==mark== syntax')}${wysiwygNo}` },
'markdown.plugin.footnote': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable footnotes')}${wysiwygNo}` },
'markdown.plugin.toc': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable table of contents extension')}${wysiwygNo}` },
'markdown.plugin.sub': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ~sub~ syntax')}${wysiwygNo}` },
'markdown.plugin.sup': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ^sup^ syntax')}${wysiwygNo}` },
'markdown.plugin.deflist': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable deflist syntax')}${wysiwygNo}` },
'markdown.plugin.abbr': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable abbreviation syntax')}${wysiwygNo}` },
'markdown.plugin.emoji': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable markdown emoji')}${wysiwygNo}` },
'markdown.plugin.insert': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ++insert++ syntax')}${wysiwygNo}` },
'markdown.plugin.multitable': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable multimarkdown table extension')}${wysiwygNo}` },
// Tray icon (called AppIndicator) doesn't work in Ubuntu
// http://www.webupd8.org/2017/04/fix-appindicator-not-working-for.html
// Might be fixed in Electron 18.x but no non-beta release yet. So for now
// by default we disable it on Linux.
2019-07-29 15:43:53 +02:00
showTrayIcon: {
value: platform !== 'linux',
type: Setting.TYPE_BOOL,
section: 'application',
public: true,
appTypes: ['desktop'],
label: () => _('Show tray icon'),
description: () => {
return platform === 'linux' ? _('Note: Does not work in all desktop environments.') : _('This will allow Joplin to run in the background. It is recommended to enable this setting so that your notes are constantly being synchronised, thus reducing the number of conflicts.');
},
},
startMinimized: { value: false, type: Setting.TYPE_BOOL, section: 'application', public: true, appTypes: ['desktop'], label: () => _('Start application minimised in the tray icon') },
collapsedFolderIds: { value: [], type: Setting.TYPE_ARRAY, public: false },
'db.ftsEnabled': { value: -1, type: Setting.TYPE_INT, public: false },
2017-12-12 23:58:57 +02:00
'encryption.enabled': { value: false, type: Setting.TYPE_BOOL, public: false },
'encryption.activeMasterKeyId': { value: '', type: Setting.TYPE_STRING, public: false },
'encryption.passwordCache': { value: {}, type: Setting.TYPE_OBJECT, public: false, secure: true },
'encryption.shouldReencrypt': {
value: -1, // will be set on app startup
type: Setting.TYPE_INT,
public: false,
},
Desktop: Resolves #2162: Added zoom controls to the application menu commit 2285000a6ac09eed12d4215d71b4f88f1660411a Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:25:12 2020 +0000 Deprecate style.zoom commit 3a6da4ffee280dd93eee1f4ae8891a72ecaea8e3 Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:13:01 2020 +0000 Fix zoom branch commit c46c080a069d213e4f75c261a12cbed47ed8de8f Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:37:10 2019 -0500 Using componentDidUpdate rather than deprecated componentWillRecieveProps https://github.com/laurent22/joplin/pull/2165#discussion_r357441917 commit 069444fd02e18f6542e6483d9fffae8a941ab59c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:33:23 2019 -0500 Zoom factor is saved to private setting https://github.com/laurent22/joplin/pull/2165#issuecomment-565258704 commit 34a1b2dc3e65f6a5b72a59608f4dddd9bad4bd08 Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:30:29 2019 -0500 Basing new zoom value off redux state https://github.com/laurent22/joplin/pull/2165#discussion_r357441406 https://github.com/laurent22/joplin/pull/2165#discussion_r357441512 commit 7ec15ff4c4b334fd96003aaf04c119e013bb748c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Fri Dec 13 12:50:11 2019 -0500 Reducer shouldn't have any side effects https://github.com/laurent22/joplin/pull/2165#discussion_r357440767 commit 9e676ece1369e60496ba72cd953ba141b93afd6a Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 8 11:11:28 2019 -0500 Added zoom options to the view menu
2020-02-12 14:41:32 +02:00
// Deprecated in favour of windowContentZoomFactor
'style.zoom': { value: 100, type: Setting.TYPE_INT, public: false, appTypes: ['desktop'], section: 'appearance', label: () => '', minimum: 50, maximum: 500, step: 10 },
2019-07-29 15:43:53 +02:00
'style.editor.fontSize': { value: 13, type: Setting.TYPE_INT, public: true, appTypes: ['desktop'], section: 'appearance', label: () => _('Editor font size'), minimum: 4, maximum: 50, step: 1 },
'style.editor.fontFamily':
2019-09-19 23:51:18 +02:00
(mobilePlatform) ?
({
value: Setting.FONT_DEFAULT,
type: Setting.TYPE_STRING,
isEnum: true,
public: true,
label: () => _('Editor font'),
appTypes: ['mobile'],
section: 'appearance',
options: () => {
// IMPORTANT: The font mapping must match the one in global-styles.js::editorFont()
if (mobilePlatform === 'ios') {
return {
[Setting.FONT_DEFAULT]: 'Default',
[Setting.FONT_MENLO]: 'Menlo',
[Setting.FONT_COURIER_NEW]: 'Courier New',
[Setting.FONT_AVENIR]: 'Avenir',
};
}
return {
[Setting.FONT_DEFAULT]: 'Default',
[Setting.FONT_MONOSPACE]: 'Monospace',
};
},
}) : {
value: '',
type: Setting.TYPE_STRING,
public: true,
appTypes: ['desktop'],
section: 'appearance',
label: () => _('Editor font family'),
description: () =>
_('This must be *monospace* font or it will not work properly. If the font ' +
'is incorrect or empty, it will default to a generic monospace font.'),
},
2019-07-29 15:43:53 +02:00
'style.sidebar.width': { value: 150, minimum: 80, maximum: 400, type: Setting.TYPE_INT, public: false, appTypes: ['desktop'] },
'style.noteList.width': { value: 150, minimum: 80, maximum: 400, type: Setting.TYPE_INT, public: false, appTypes: ['desktop'] },
// TODO: Is there a better way to do this? The goal here is to simply have
// a way to display a link to the customizable stylesheets, not for it to
// serve as a customizable Setting. But because the Setting page is auto-
// generated from this list of settings, there wasn't a really elegant way
// to do that directly in the React markup.
'style.customCss.renderedMarkdown': {
onClick: () => {
const dir = Setting.value('profileDir');
const filename = Setting.custom_css_files.RENDERED_MARKDOWN;
const filepath = `${dir}/${filename}`;
const defaultContents = '/* For styling the rendered Markdown */';
shim.openOrCreateFile(filepath, defaultContents);
},
type: Setting.TYPE_BUTTON,
public: true,
appTypes: ['desktop'],
label: () => _('Custom stylesheet for rendered Markdown'),
section: 'appearance',
advanced: true,
},
'style.customCss.joplinApp': {
onClick: () => {
const dir = Setting.value('profileDir');
const filename = Setting.custom_css_files.JOPLIN_APP;
const filepath = `${dir}/${filename}`;
const defaultContents = `/* For styling the entire Joplin app (except the rendered Markdown, which is defined in \`${Setting.custom_css_files.RENDERED_MARKDOWN}\`) */`;
shim.openOrCreateFile(filepath, defaultContents);
},
type: Setting.TYPE_BUTTON,
public: true,
appTypes: ['desktop'],
label: () => _('Custom stylesheet for Joplin-wide app styles'),
section: 'appearance',
advanced: true,
description: () => 'CSS file support is provided for your convenience, but they are advanced settings, and styles you define may break from one version to the next. If you want to use them, please know that it might require regular development work from you to keep them working. The Joplin team cannot make a commitment to keep the application HTML structure stable.',
},
2019-07-29 15:43:53 +02:00
autoUpdateEnabled: { value: true, type: Setting.TYPE_BOOL, section: 'application', public: true, appTypes: ['desktop'], label: () => _('Automatically update the application') },
'autoUpdate.includePreReleases': { value: false, type: Setting.TYPE_BOOL, section: 'application', public: true, appTypes: ['desktop'], label: () => _('Get pre-releases when checking for updates'), description: () => _('See the pre-release page for more details: %s', 'https://joplinapp.org/prereleases') },
'clipperServer.autoStart': { value: false, type: Setting.TYPE_BOOL, public: false },
2019-07-29 15:43:53 +02:00
'sync.interval': {
value: 300,
type: Setting.TYPE_INT,
section: 'sync',
isEnum: true,
public: true,
label: () => _('Synchronisation interval'),
options: () => {
return {
0: _('Disabled'),
300: _('%d minutes', 5),
600: _('%d minutes', 10),
1800: _('%d minutes', 30),
3600: _('%d hour', 1),
43200: _('%d hours', 12),
86400: _('%d hours', 24),
};
},
},
noteVisiblePanes: { value: ['editor', 'viewer'], type: Setting.TYPE_ARRAY, public: false, appTypes: ['desktop'] },
sidebarVisibility: { value: true, type: Setting.TYPE_BOOL, public: false, appTypes: ['desktop'] },
noteListVisibility: { value: true, type: Setting.TYPE_BOOL, public: false, appTypes: ['desktop'] },
2019-07-29 15:43:53 +02:00
tagHeaderIsExpanded: { value: true, type: Setting.TYPE_BOOL, public: false, appTypes: ['desktop'] },
folderHeaderIsExpanded: { value: true, type: Setting.TYPE_BOOL, public: false, appTypes: ['desktop'] },
editor: { value: '', type: Setting.TYPE_STRING, subType: 'file_path_and_args', public: true, appTypes: ['cli', 'desktop'], label: () => _('Text editor command'), description: () => _('The editor command (may include arguments) that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
'export.pdfPageSize': { value: 'A4', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['desktop'], label: () => _('Page size for PDF export'), options: () => {
return {
'A4': _('A4'),
'Letter': _('Letter'),
'A3': _('A3'),
'A5': _('A5'),
'Tabloid': _('Tabloid'),
'Legal': _('Legal'),
};
} },
'export.pdfPageOrientation': { value: 'portrait', type: Setting.TYPE_STRING, isEnum: true, public: true, appTypes: ['desktop'], label: () => _('Page orientation for PDF export'), options: () => {
return {
'portrait': _('Portrait'),
'landscape': _('Landscape'),
};
} },
2019-07-29 15:43:53 +02:00
'net.customCertificates': {
value: '',
type: Setting.TYPE_STRING,
section: 'sync',
advanced: true,
2019-07-29 15:43:53 +02:00
show: settings => {
return [SyncTargetRegistry.nameToId('nextcloud'), SyncTargetRegistry.nameToId('webdav')].indexOf(settings['sync.target']) >= 0;
},
public: true,
appTypes: ['desktop', 'cli'],
label: () => _('Custom TLS certificates'),
description: () => _('Comma-separated list of paths to directories to load the certificates from, or path to individual cert files. For example: /my/cert_dir, /other/custom.pem. Note that if you make changes to the TLS settings, you must save your changes before clicking on "Check synchronisation configuration".'),
},
'net.ignoreTlsErrors': {
value: false,
type: Setting.TYPE_BOOL,
advanced: true,
2019-07-29 15:43:53 +02:00
section: 'sync',
show: settings => {
return [SyncTargetRegistry.nameToId('nextcloud'), SyncTargetRegistry.nameToId('webdav')].indexOf(settings['sync.target']) >= 0;
},
public: true,
appTypes: ['desktop', 'cli'],
label: () => _('Ignore TLS certificate errors'),
},
Mobile: Add toolbar, list continuation and Markdown preview to editor (#2224) * The basic editor is working! No list continuation still though * List continuation is working! Now to delete when entering again and not typing on line + handle ordered lists * Supports checkboxes + attempted at setting font * Editor font works now; now need to fix the delete (look at past state) * Fix deletion problem * Add ordered list handler * Add comments * Extract insertListLine * End lists on enter for empty bullets * Add MarkdownView (renders badly though) * Save edited text from MarkdownEditor * Cleanup * Refactor react-native-markdown-editor/ * Rename react-native-markdown-editor/ => MarkdownEditor/ * Cleanup * Fix preview styles; still need to fix checkbox problem * Fix keyboard padding * Change name back to #body_changeText * Incorporate PR feedback from @laurent22 * wip: Move MarkdownEditor/ from ReactNativeClient/lib/ to ReactNativeClient/ * Move MarkdownEditor/ from ReactNativeClient/lib/ to ReactNativeClient/ * Remove log statement * Focus TextInput in MarkdownEditor from grandparent * Make eslint happy * Extract textInputRefName to shared variable * Remove accidental #setState * Cleanup * Cleanup * Run linter * Cleanup * Update button order * Improve styles for config descriptions * Allow descriptions to be added to BOOL type Setting configs * Add editorBeta Setting * Move FailSafe details to description text * Update descriptionText styles * Put the editor under the beta flag toggle * Incorporate PR feedback from @laurent22 * Refactor Markdown editor focusing * Cleanup * Reorder MarkdownEditor formats * Make applyListFormat behavior more intuitive * Add comment * Show MarkdownEditor with preview by default * Show preview by default, then hide on typing * Fix MarkdownEditor selection bug * Cleanup * Update Markdown button styles * Make Markdown button colors theme-conscious * Fix merge conflict resolution mistake * Fix broken import * Delete package-lock.json * Reset package-lock.json Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
2020-03-25 12:50:45 +02:00
'sync.wipeOutFailSafe': {
value: true,
type: Setting.TYPE_BOOL,
advanced: true,
public: true,
section: 'sync',
label: () => _('Fail-safe'),
description: () => _('Fail-safe: Do not wipe out local data when sync target is empty (often the result of a misconfiguration or bug)'),
},
2018-09-28 20:24:57 +02:00
'api.token': { value: null, type: Setting.TYPE_STRING, public: false },
'api.port': { value: null, type: Setting.TYPE_INT, public: true, appTypes: ['cli'], description: () => _('Specify the port that should be used by the API server. If not set, a default will be used.') },
'resourceService.lastProcessedChangeId': { value: 0, type: Setting.TYPE_INT, public: false },
2018-12-10 20:54:46 +02:00
'searchEngine.lastProcessedChangeId': { value: 0, type: Setting.TYPE_INT, public: false },
'revisionService.lastProcessedChangeId': { value: 0, type: Setting.TYPE_INT, public: false },
2019-07-29 15:43:53 +02:00
2019-01-13 18:05:07 +02:00
'searchEngine.initialIndexingDone': { value: false, type: Setting.TYPE_BOOL, public: false },
'revisionService.enabled': { section: 'revisionService', value: true, type: Setting.TYPE_BOOL, public: true, label: () => _('Enable note history') },
2019-07-29 15:43:53 +02:00
'revisionService.ttlDays': {
section: 'revisionService',
value: 90,
type: Setting.TYPE_INT,
public: true,
minimum: 1,
maximum: 365 * 2,
step: 1,
unitLabel: (value = null) => {
return value === null ? _('days') : _('%d days', value);
},
label: () => _('Keep note history for'),
},
'revisionService.intervalBetweenRevisions': { section: 'revisionService', value: 1000 * 60 * 10, type: Setting.TYPE_INT, public: false },
'revisionService.oldNoteInterval': { section: 'revisionService', value: 1000 * 60 * 60 * 24 * 7, type: Setting.TYPE_INT, public: false },
'welcome.wasBuilt': { value: false, type: Setting.TYPE_BOOL, public: false },
'welcome.enabled': { value: true, type: Setting.TYPE_BOOL, public: false },
'camera.type': { value: 0, type: Setting.TYPE_INT, public: false, appTypes: ['mobile'] },
'camera.ratio': { value: '4:3', type: Setting.TYPE_STRING, public: false, appTypes: ['mobile'] },
Desktop: Resolves #2162: Added zoom controls to the application menu commit 2285000a6ac09eed12d4215d71b4f88f1660411a Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:25:12 2020 +0000 Deprecate style.zoom commit 3a6da4ffee280dd93eee1f4ae8891a72ecaea8e3 Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:13:01 2020 +0000 Fix zoom branch commit c46c080a069d213e4f75c261a12cbed47ed8de8f Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:37:10 2019 -0500 Using componentDidUpdate rather than deprecated componentWillRecieveProps https://github.com/laurent22/joplin/pull/2165#discussion_r357441917 commit 069444fd02e18f6542e6483d9fffae8a941ab59c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:33:23 2019 -0500 Zoom factor is saved to private setting https://github.com/laurent22/joplin/pull/2165#issuecomment-565258704 commit 34a1b2dc3e65f6a5b72a59608f4dddd9bad4bd08 Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:30:29 2019 -0500 Basing new zoom value off redux state https://github.com/laurent22/joplin/pull/2165#discussion_r357441406 https://github.com/laurent22/joplin/pull/2165#discussion_r357441512 commit 7ec15ff4c4b334fd96003aaf04c119e013bb748c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Fri Dec 13 12:50:11 2019 -0500 Reducer shouldn't have any side effects https://github.com/laurent22/joplin/pull/2165#discussion_r357440767 commit 9e676ece1369e60496ba72cd953ba141b93afd6a Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 8 11:11:28 2019 -0500 Added zoom options to the view menu
2020-02-12 14:41:32 +02:00
windowContentZoomFactor: {
value: 100,
type: Setting.TYPE_INT,
public: false,
appTypes: ['desktop'],
minimum: 30,
maximum: 300,
step: 10,
},
};
return this.metadata_;
}
2017-07-24 20:58:11 +02:00
static settingMetadata(key) {
const metadata = this.metadata();
2019-09-19 23:51:18 +02:00
if (!(key in metadata)) throw new Error(`Unknown key: ${key}`);
const output = Object.assign({}, metadata[key]);
2017-05-12 22:17:23 +02:00
output.key = key;
return output;
}
2017-10-30 02:29:10 +02:00
static keyExists(key) {
return key in this.metadata();
2017-10-30 02:29:10 +02:00
}
static keyDescription(key, appType = null) {
const md = this.settingMetadata(key);
if (!md.description) return null;
return md.description(appType);
}
2017-08-22 19:57:35 +02:00
static keys(publicOnly = false, appType = null) {
if (!this.keys_) {
const metadata = this.metadata();
2017-08-22 19:57:35 +02:00
this.keys_ = [];
for (const n in metadata) {
if (!metadata.hasOwnProperty(n)) continue;
2017-08-22 19:57:35 +02:00
this.keys_.push(n);
}
2017-05-16 23:46:21 +02:00
}
2017-08-22 19:57:35 +02:00
if (appType || publicOnly) {
const output = [];
2017-08-22 19:57:35 +02:00
for (let i = 0; i < this.keys_.length; i++) {
const md = this.settingMetadata(this.keys_[i]);
if (publicOnly && !md.public) continue;
if (appType && md.appTypes && md.appTypes.indexOf(appType) < 0) continue;
output.push(md.key);
}
return output;
} else {
return this.keys_;
2017-06-27 22:16:03 +02:00
}
}
2017-07-31 22:51:24 +02:00
static isPublic(key) {
2017-08-22 19:57:35 +02:00
return this.keys(true).indexOf(key) >= 0;
2017-07-31 22:51:24 +02:00
}
2017-05-12 22:17:23 +02:00
static load() {
this.cancelScheduleSave();
2017-05-12 22:17:23 +02:00
this.cache_ = [];
2019-07-29 15:43:53 +02:00
return this.modelSelectAll('SELECT * FROM settings').then(rows => {
this.cache_ = [];
2017-07-24 22:36:49 +02:00
for (let i = 0; i < rows.length; i++) {
const c = rows[i];
2017-10-30 02:29:10 +02:00
if (!this.keyExists(c.key)) continue;
2017-07-25 23:55:26 +02:00
c.value = this.formatValue(c.key, c.value);
c.value = this.filterValue(c.key, c.value);
2017-07-25 23:55:26 +02:00
this.cache_.push(c);
2017-07-24 22:36:49 +02:00
}
2017-10-21 18:53:43 +02:00
this.dispatchUpdateAll();
});
}
static toPlainObject() {
2017-10-21 18:53:43 +02:00
const keys = this.keys();
const keyToValues = {};
2017-10-21 18:53:43 +02:00
for (let i = 0; i < keys.length; i++) {
keyToValues[keys[i]] = this.value(keys[i]);
}
return keyToValues;
}
static dispatchUpdateAll() {
2017-10-21 18:53:43 +02:00
this.dispatch({
2017-11-08 23:22:24 +02:00
type: 'SETTING_UPDATE_ALL',
settings: this.toPlainObject(),
2017-05-12 22:17:23 +02:00
});
}
2017-06-24 20:51:43 +02:00
static setConstant(key, value) {
2019-09-19 23:51:18 +02:00
if (!(key in this.constants_)) throw new Error(`Unknown constant key: ${key}`);
2017-06-24 20:51:43 +02:00
this.constants_[key] = value;
}
2017-05-12 22:17:23 +02:00
static setValue(key, value) {
2017-06-19 20:58:49 +02:00
if (!this.cache_) throw new Error('Settings have not been initialized!');
2017-07-31 22:51:24 +02:00
value = this.formatValue(key, value);
value = this.filterValue(key, value);
2017-05-12 22:17:23 +02:00
for (let i = 0; i < this.cache_.length; i++) {
const c = this.cache_[i];
2017-07-24 20:58:11 +02:00
if (c.key == key) {
const md = this.settingMetadata(key);
2017-07-25 23:55:26 +02:00
if (md.isEnum === true) {
2017-07-24 20:58:11 +02:00
if (!this.isAllowedEnumOption(key, value)) {
throw new Error(_('Invalid option value: "%s". Possible values are: %s.', value, this.enumOptionsDoc(key)));
}
}
if (c.value === value) return;
2017-07-25 23:55:26 +02:00
// Don't log this to prevent sensitive info (passwords, auth tokens...) to end up in logs
// this.logger().info('Setting: ' + key + ' = ' + c.value + ' => ' + value);
2017-07-25 23:55:26 +02:00
2019-07-29 15:43:53 +02:00
if ('minimum' in md && value < md.minimum) value = md.minimum;
if ('maximum' in md && value > md.maximum) value = md.maximum;
2019-02-16 03:12:43 +02:00
2017-11-14 20:02:58 +02:00
c.value = value;
this.dispatch({
2017-11-08 23:22:24 +02:00
type: 'SETTING_UPDATE_ONE',
key: key,
value: c.value,
});
this.scheduleSave();
2017-05-12 22:17:23 +02:00
return;
}
}
2017-07-25 23:55:26 +02:00
this.cache_.push({
key: key,
value: this.formatValue(key, value),
});
this.dispatch({
2017-11-08 23:22:24 +02:00
type: 'SETTING_UPDATE_ONE',
key: key,
value: this.formatValue(key, value),
});
this.scheduleSave();
}
Desktop: Resolves #2162: Added zoom controls to the application menu commit 2285000a6ac09eed12d4215d71b4f88f1660411a Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:25:12 2020 +0000 Deprecate style.zoom commit 3a6da4ffee280dd93eee1f4ae8891a72ecaea8e3 Author: Laurent Cozic <laurent@cozic.net> Date: Tue Feb 11 22:13:01 2020 +0000 Fix zoom branch commit c46c080a069d213e4f75c261a12cbed47ed8de8f Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:37:10 2019 -0500 Using componentDidUpdate rather than deprecated componentWillRecieveProps https://github.com/laurent22/joplin/pull/2165#discussion_r357441917 commit 069444fd02e18f6542e6483d9fffae8a941ab59c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:33:23 2019 -0500 Zoom factor is saved to private setting https://github.com/laurent22/joplin/pull/2165#issuecomment-565258704 commit 34a1b2dc3e65f6a5b72a59608f4dddd9bad4bd08 Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 15 10:30:29 2019 -0500 Basing new zoom value off redux state https://github.com/laurent22/joplin/pull/2165#discussion_r357441406 https://github.com/laurent22/joplin/pull/2165#discussion_r357441512 commit 7ec15ff4c4b334fd96003aaf04c119e013bb748c Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Fri Dec 13 12:50:11 2019 -0500 Reducer shouldn't have any side effects https://github.com/laurent22/joplin/pull/2165#discussion_r357440767 commit 9e676ece1369e60496ba72cd953ba141b93afd6a Author: Elizabeth Schafer <elizabeth.schafer.wenk@gmail.com> Date: Sun Dec 8 11:11:28 2019 -0500 Added zoom options to the view menu
2020-02-12 14:41:32 +02:00
static incValue(key, inc) {
return this.setValue(key, this.value(key) + inc);
}
static setObjectKey(settingKey, objectKey, value) {
let o = this.value(settingKey);
if (typeof o !== 'object') o = {};
o[objectKey] = value;
this.setValue(settingKey, o);
}
static deleteObjectKey(settingKey, objectKey) {
const o = this.value(settingKey);
if (typeof o !== 'object') return;
delete o[objectKey];
this.setValue(settingKey, o);
}
static valueToString(key, value) {
const md = this.settingMetadata(key);
value = this.formatValue(key, value);
if (md.type == Setting.TYPE_INT) return value.toFixed(0);
if (md.type == Setting.TYPE_BOOL) return value ? '1' : '0';
if (md.type == Setting.TYPE_ARRAY) return value ? JSON.stringify(value) : '[]';
2017-11-14 20:02:58 +02:00
if (md.type == Setting.TYPE_OBJECT) return value ? JSON.stringify(value) : '{}';
2019-09-19 23:51:18 +02:00
if (md.type == Setting.TYPE_STRING) return value ? `${value}` : '';
2019-09-19 23:51:18 +02:00
throw new Error(`Unhandled value type: ${md.type}`);
2017-05-12 22:17:23 +02:00
}
static filterValue(key, value) {
const md = this.settingMetadata(key);
return md.filter ? md.filter(value) : value;
}
2017-07-25 23:55:26 +02:00
static formatValue(key, value) {
const md = this.settingMetadata(key);
2018-01-07 21:29:57 +02:00
if (md.type == Setting.TYPE_INT) return !value ? 0 : Math.floor(Number(value));
if (md.type == Setting.TYPE_BOOL) {
2017-07-26 23:07:27 +02:00
if (typeof value === 'string') {
value = value.toLowerCase();
if (value === 'true') return true;
if (value === 'false') return false;
value = Number(value);
}
return !!value;
}
if (md.type === Setting.TYPE_ARRAY) {
2017-11-14 20:02:58 +02:00
if (!value) return [];
if (Array.isArray(value)) return value;
if (typeof value === 'string') return JSON.parse(value);
return [];
}
2017-11-14 20:02:58 +02:00
if (md.type === Setting.TYPE_OBJECT) {
if (!value) return {};
if (typeof value === 'object') return value;
if (typeof value === 'string') return JSON.parse(value);
return {};
}
if (md.type === Setting.TYPE_STRING) {
if (!value) return '';
2019-09-19 23:51:18 +02:00
return `${value}`;
}
2019-09-19 23:51:18 +02:00
throw new Error(`Unhandled value type: ${md.type}`);
2017-07-25 20:57:06 +02:00
}
2017-05-12 22:17:23 +02:00
static value(key) {
// Need to copy arrays and objects since in setValue(), the old value and new one is compared
// with strict equality and the value is updated only if changed. However if the caller acquire
// and object and change a key, the objects will be detected as equal. By returning a copy
// we avoid this problem.
function copyIfNeeded(value) {
if (value === null || value === undefined) return value;
if (Array.isArray(value)) return value.slice();
if (typeof value === 'object') return Object.assign({}, value);
return value;
}
if (key in this.constants_) {
2017-11-20 00:08:58 +02:00
const v = this.constants_[key];
const output = typeof v === 'function' ? v() : v;
2019-09-19 23:51:18 +02:00
if (output == 'SET_ME') throw new Error(`Setting constant has not been set: ${key}`);
return output;
}
2017-06-24 20:51:43 +02:00
2017-06-19 20:58:49 +02:00
if (!this.cache_) throw new Error('Settings have not been initialized!');
2017-05-12 22:17:23 +02:00
for (let i = 0; i < this.cache_.length; i++) {
if (this.cache_[i].key == key) {
return copyIfNeeded(this.cache_[i].value);
2017-05-12 22:17:23 +02:00
}
}
2017-07-25 23:55:26 +02:00
const md = this.settingMetadata(key);
return copyIfNeeded(md.value);
2017-05-12 22:17:23 +02:00
}
2017-07-24 20:58:11 +02:00
static isEnum(key) {
const md = this.settingMetadata(key);
2017-07-25 23:55:26 +02:00
return md.isEnum === true;
2017-07-24 20:58:11 +02:00
}
static enumOptionValues(key) {
const options = this.enumOptions(key);
const output = [];
for (const n in options) {
2017-07-24 20:58:11 +02:00
if (!options.hasOwnProperty(n)) continue;
output.push(n);
}
return output;
}
static enumOptionLabel(key, value) {
const options = this.enumOptions(key);
for (const n in options) {
2017-07-24 20:58:11 +02:00
if (n == value) return options[n];
}
return '';
}
static enumOptions(key) {
const metadata = this.metadata();
2019-09-19 23:51:18 +02:00
if (!metadata[key]) throw new Error(`Unknown key: ${key}`);
if (!metadata[key].options) throw new Error(`No options for: ${key}`);
return metadata[key].options();
2017-07-24 20:58:11 +02:00
}
static enumOptionsDoc(key, templateString = null) {
if (templateString === null) templateString = '%s: %s';
2017-07-24 20:58:11 +02:00
const options = this.enumOptions(key);
const output = [];
for (const n in options) {
2017-07-24 20:58:11 +02:00
if (!options.hasOwnProperty(n)) continue;
output.push(sprintf(templateString, n, options[n]));
2017-07-24 20:58:11 +02:00
}
return output.join(', ');
}
static isAllowedEnumOption(key, value) {
const options = this.enumOptions(key);
return !!options[value];
}
// For example, if settings is:
// { sync.5.path: 'http://example', sync.5.username: 'testing' }
// and baseKey is 'sync.5', the function will return
// { path: 'http://example', username: 'testing' }
static subValues(baseKey, settings, options = null) {
const includeBaseKeyInName = !!options && !!options.includeBaseKeyInName;
const output = {};
for (const key in settings) {
if (!settings.hasOwnProperty(key)) continue;
if (key.indexOf(baseKey) === 0) {
const subKey = includeBaseKeyInName ? key : key.substr(baseKey.length + 1);
output[subKey] = settings[key];
2017-05-16 23:46:21 +02:00
}
}
return output;
}
static async saveAll() {
if (!this.saveTimeoutId_) return Promise.resolve();
2017-05-19 21:12:09 +02:00
2017-06-25 12:41:03 +02:00
this.logger().info('Saving settings...');
clearTimeout(this.saveTimeoutId_);
this.saveTimeoutId_ = null;
2017-05-19 21:12:09 +02:00
const queries = [];
2017-06-11 23:11:14 +02:00
queries.push('DELETE FROM settings');
for (let i = 0; i < this.cache_.length; i++) {
const s = Object.assign({}, this.cache_[i]);
s.value = this.valueToString(s.key, s.value);
queries.push(Database.insertQuery(this.tableName(), s));
2017-06-11 23:11:14 +02:00
}
await BaseModel.db().transactionExecBatch(queries);
this.logger().info('Settings have been saved.');
2017-05-19 21:12:09 +02:00
}
static scheduleSave() {
if (!Setting.autoSaveEnabled) return;
if (this.saveTimeoutId_) clearTimeout(this.saveTimeoutId_);
2017-05-19 21:12:09 +02:00
this.saveTimeoutId_ = setTimeout(() => {
2017-05-19 21:12:09 +02:00
this.saveAll();
2017-05-12 22:17:23 +02:00
}, 500);
}
static cancelScheduleSave() {
if (this.saveTimeoutId_) clearTimeout(this.saveTimeoutId_);
this.saveTimeoutId_ = null;
2017-06-19 21:26:27 +02:00
}
2017-07-23 20:26:50 +02:00
static publicSettings(appType) {
if (!appType) throw new Error('appType is required');
const metadata = this.metadata();
const output = {};
for (const key in metadata) {
if (!metadata.hasOwnProperty(key)) continue;
const s = Object.assign({}, metadata[key]);
2017-07-23 20:26:50 +02:00
if (!s.public) continue;
if (s.appTypes && s.appTypes.indexOf(appType) < 0) continue;
s.value = this.value(key);
output[key] = s;
}
return output;
}
static typeToString(typeId) {
if (typeId === Setting.TYPE_INT) return 'int';
if (typeId === Setting.TYPE_STRING) return 'string';
if (typeId === Setting.TYPE_BOOL) return 'bool';
if (typeId === Setting.TYPE_ARRAY) return 'array';
2017-11-14 20:02:58 +02:00
if (typeId === Setting.TYPE_OBJECT) return 'object';
}
static groupMetadatasBySections(metadatas) {
const sections = [];
const generalSection = { name: 'general', metadatas: [] };
const nameToSections = {};
nameToSections['general'] = generalSection;
sections.push(generalSection);
for (let i = 0; i < metadatas.length; i++) {
const md = metadatas[i];
if (!md.section) {
generalSection.metadatas.push(md);
} else {
if (!nameToSections[md.section]) {
nameToSections[md.section] = { name: md.section, metadatas: [] };
sections.push(nameToSections[md.section]);
}
nameToSections[md.section].metadatas.push(md);
}
}
2019-07-29 15:43:53 +02:00
return sections;
}
static sectionNameToLabel(name) {
if (name === 'general') return _('General');
if (name === 'sync') return _('Synchronisation');
if (name === 'appearance') return _('Appearance');
if (name === 'note') return _('Note');
if (name === 'plugins') return _('Plugins');
if (name === 'application') return _('Application');
if (name === 'revisionService') return _('Note History');
if (name === 'encryption') return _('Encryption');
if (name === 'server') return _('Web Clipper');
return name;
}
static sectionDescription(name) {
if (name === 'plugins') return _('These plugins enhance the Markdown renderer with additional features. Please note that, while these features might be useful, they are not standard Markdown and thus most of them will only work in Joplin. Additionally, some of them are *incompatible* with the WYSIWYG editor. If you open a note that uses one of these plugins in that editor, you will lose the plugin formatting. It is indicated below which plugins are compatible or not with the WYSIWYG editor.');
if (name === 'general') return _('Notes and settings are stored in: %s', toSystemSlashes(this.value('profileDir'), process.platform));
return '';
}
static sectionNameToIcon(name) {
if (name === 'general') return 'fa-sliders';
if (name === 'sync') return 'fa-refresh';
if (name === 'appearance') return 'fa-pencil';
if (name === 'note') return 'fa-file-text-o';
if (name === 'plugins') return 'fa-puzzle-piece';
if (name === 'application') return 'fa-cog';
if (name === 'revisionService') return 'fa-archive-org';
if (name === 'encryption') return 'fa-key-modern';
if (name === 'server') return 'fa-hand-scissors-o';
return name;
}
2019-02-23 17:53:14 +02:00
static appTypeToLabel(name) {
// Not translated for now because only used on Welcome notes (which are not translated)
if (name === 'cli') return 'CLI';
return name[0].toUpperCase() + name.substr(1).toLowerCase();
}
2017-05-12 22:17:23 +02:00
}
2017-07-25 23:55:26 +02:00
Setting.TYPE_INT = 1;
Setting.TYPE_STRING = 2;
Setting.TYPE_BOOL = 3;
Setting.TYPE_ARRAY = 4;
2017-11-14 20:02:58 +02:00
Setting.TYPE_OBJECT = 5;
Setting.TYPE_BUTTON = 6;
2017-07-25 23:55:26 +02:00
2017-07-31 22:51:24 +02:00
Setting.THEME_LIGHT = 1;
Setting.THEME_DARK = 2;
Setting.THEME_OLED_DARK = 22;
Setting.THEME_SOLARIZED_LIGHT = 3;
Setting.THEME_SOLARIZED_DARK = 4;
Setting.THEME_DRACULA = 5;
Setting.THEME_NORD = 6;
Setting.THEME_ARITIM_DARK = 7;
2017-07-31 22:51:24 +02:00
Setting.FONT_DEFAULT = 0;
Setting.FONT_MENLO = 1;
Setting.FONT_COURIER_NEW = 2;
Setting.FONT_AVENIR = 3;
Setting.FONT_MONOSPACE = 4;
Setting.LAYOUT_ALL = 0;
Setting.LAYOUT_EDITOR_VIEWER = 1;
Setting.LAYOUT_EDITOR_SPLIT = 2;
Setting.LAYOUT_VIEWER_SPLIT = 3;
Desktop: Resolves #176: Added experimental WYSIWYG editor (#2556) * Trying to get TuiEditor to work * Tests with TinyMCE * Fixed build * Improved asset loading * Added support for Joplin source blocks * Added support for Joplin source blocks * Better integration * Make sure noteDidUpdate event is always dispatched at the right time * Minor tweaks * Fixed tests * Add support for checkboxes * Minor refactoring * Added support for file attachments * Add support for fenced code blocks * Fix new line issue on code block * Added support for Fountain scripts * Refactoring * Better handling of saving and loading notes * Fix saving and loading ntoes * Handle multi-note selection and fixed new note creation issue * Fixed newline issue in test * Fixed newline issue in test * Improve saving and loading * Improve saving and loading note * Removed undeeded prop * Fixed issue when new note being saved is incorrectly reloaded * Refactoring and improve saving of note when unmounting component * Fixed TypeScript error * Small changes * Improved further handling of saving and loading notes * Handle provisional notes and fixed various saving and loading bugs * Adding back support for HTML notes * Added support for HTML notes * Better handling of editable nodes * Preserve image HTML tag when the size is set * Handle switching between editor when the note has note finished saving * Handle templates * Handle templates * Handle loading note that is being saved * Handle note being reloaded via sync * Clean up * Clean up and improved logging * Fixed TS error * Fixed a few issues * Fixed test * Logging * Various improvements * Add blockquote support * Moved CWD operation to shim * Removed deleted files * Added support for Joplin commands
2020-03-10 01:24:57 +02:00
Setting.LAYOUT_SPLIT_WYSIWYG = 4;
2019-07-29 15:43:53 +02:00
Setting.DATE_FORMAT_1 = 'DD/MM/YYYY';
Setting.DATE_FORMAT_2 = 'DD/MM/YY';
Setting.DATE_FORMAT_3 = 'MM/DD/YYYY';
Setting.DATE_FORMAT_4 = 'MM/DD/YY';
Setting.DATE_FORMAT_5 = 'YYYY-MM-DD';
Setting.DATE_FORMAT_6 = 'DD.MM.YYYY';
Setting.DATE_FORMAT_7 = 'YYYY.MM.DD';
Setting.TIME_FORMAT_1 = 'HH:mm';
Setting.TIME_FORMAT_2 = 'h:mm A';
2020-03-14 02:52:28 +02:00
Setting.SHOULD_REENCRYPT_NO = 0; // Data doesn't need to be re-encrypted
Setting.SHOULD_REENCRYPT_YES = 1; // Data should be re-encrypted
Setting.SHOULD_REENCRYPT_NOTIFIED = 2; // Data should be re-encrypted, and user has been notified
Setting.custom_css_files = {
JOPLIN_APP: 'userchrome.css',
RENDERED_MARKDOWN: 'userstyle.css',
};
2017-06-24 20:51:43 +02:00
// Contains constants that are set by the application and
// cannot be modified by the user:
Setting.constants_ = {
env: 'SET_ME',
isDemo: false,
appName: 'joplin',
appId: 'SET_ME', // Each app should set this identifier
appType: 'SET_ME', // 'cli' or 'mobile'
resourceDirName: '',
resourceDir: '',
profileDir: '',
templateDir: '',
tempDir: '',
2019-12-17 19:06:55 +02:00
flagOpenDevTools: false,
syncVersion: 1,
2019-07-29 15:43:53 +02:00
};
2017-06-24 20:51:43 +02:00
Setting.autoSaveEnabled = true;
module.exports = Setting;