1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-06-15 23:00:36 +02:00
Files
.github
Assets
CliClient
Clipper
ElectronClient
Modules
ReactNativeClient
MarkdownEditor
android
ios
lib
components
screens
NoteTagsDialog.js
config.js
dropbox-login.js
encryption-config.js
folder.js
log.js
note.js
notes.js
onedrive-login.js
search.js
status.js
tags.js
shared
CameraView.js
Dropdown.js
ItemList.js
ModalDialog.js
SafeAreaView.js
SaveIcon.png
action-button.js
app-nav.js
base-screen.js
checkbox.js
global-style.js
note-body-viewer.js
note-item.js
note-list.js
screen-header.js
select-date-time-dialog.js
side-menu-content-note.js
side-menu-content.js
side-menu.js
hooks
images
joplin-renderer
migrations
models
renderers
services
vendor
ArrayUtils.js
AsyncActionQueue.ts
BaseApplication.js
BaseModel.js
BaseSyncTarget.js
Cache.js
ClipperServer.js
CssUtils.js
DropboxApi.js
EventDispatcher.js
HtmlToMd.js
JoplinError.js
JoplinServerApi.ts
ModelCache.js
ObjectUtils.js
SyncTargetDropbox.js
SyncTargetFilesystem.js
SyncTargetMemory.js
SyncTargetNextcloud.js
SyncTargetOneDrive.js
SyncTargetOneDriveDev.js
SyncTargetRegistry.js
SyncTargetWebDAV.js
TaskQueue.js
TemplateUtils.js
WebDavApi.js
WelcomeUtils.js
database-driver-node.js
database-driver-react-native.js
database.js
dialogs.js
file-api-driver-dropbox.js
file-api-driver-local.js
file-api-driver-memory.js
file-api-driver-onedrive.js
file-api-driver-webdav.js
file-api.js
folders-screen-utils.js
fs-driver-base.js
fs-driver-dummy.js
fs-driver-node.js
fs-driver-rn.js
geolocation-node.js
geolocation-react.js
htmlUtils.js
import-enex-html-gen.js
import-enex-md-gen.js
import-enex.js
joplin-database.js
locale.js
logger.js
markJsUtils.js
markdownUtils.js
markupLanguageUtils.js
mime-utils.js
net-utils.js
onedrive-api-node-utils.js
onedrive-api.js
package.json
parameters.js
parseUri.js
path-utils.js
poor-man-intervals.js
promise-utils.js
randomClipperPort.js
react-logger.js
reducer.js
registry.js
reserved-ids.js
resourceUtils.js
shim-init-node.js
shim-init-react.js
shim.js
string-utils-common.js
string-utils.js
synchronizer.js
time-utils.js
urlUtils.js
uuid.js
welcomeAssets.js
locales
tools
.buckconfig
.flowconfig
.gitattributes
.gitignore
.watchmanconfig
PluginAssetsLoader.ts
app.json
clean_build.bat
gulpfile.js
index.android.js
index.ios.js
index.js
main.js
metro.config.js
package-lock.json
package.json
root.js
setUpQuickActions.ts
Tools
docs
patches
readme
.eslintignore
.eslintrc.js
.gitignore
.travis.yml
BUILD.md
CONTRIBUTING.md
Joplin_install_and_update.sh
LICENSE
README.md
SECURITY.md
_config.yml
appveyor.yml
build_tools_check.js
gulpfile.js
joplin.code-workspace
joplin.sublime-project
package-lock.json
package.json
tsconfig.json
joplin/ReactNativeClient/lib/components/screens/config.js

568 lines
19 KiB
JavaScript
Raw Normal View History

const React = require('react');
const { Platform, TouchableOpacity, Linking, View, Switch, StyleSheet, Text, Button, ScrollView, TextInput, Alert } = require('react-native');
const { connect } = require('react-redux');
const { ScreenHeader } = require('lib/components/screen-header.js');
const { _ } = require('lib/locale.js');
const { BaseScreenComponent } = require('lib/components/base-screen.js');
const { Dropdown } = require('lib/components/Dropdown.js');
const { themeStyle } = require('lib/components/global-style.js');
const Setting = require('lib/models/Setting.js');
const shared = require('lib/components/shared/config-shared.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const { reg } = require('lib/registry.js');
const NavService = require('lib/services/NavService.js');
const VersionInfo = require('react-native-version-info').default;
const { ReportService } = require('lib/services/report.js');
const { time } = require('lib/time-utils');
const { shim } = require('lib/shim');
const SearchEngine = require('lib/services/SearchEngine');
const RNFS = require('react-native-fs');
2017-07-23 19:26:50 +01:00
import { PermissionsAndroid } from 'react-native';
2019-06-14 22:59:27 +01:00
import Slider from '@react-native-community/slider';
2017-08-01 17:59:01 +00:00
class ConfigScreenComponent extends BaseScreenComponent {
static navigationOptions() {
2017-08-01 17:59:01 +00:00
return { header: null };
2017-07-30 23:04:26 +02:00
}
2017-07-23 19:26:50 +01:00
2017-08-01 17:59:01 +00:00
constructor() {
super();
this.styles_ = {};
this.state = {
creatingReport: false,
profileExportStatus: 'idle',
profileExportPath: '',
};
shared.init(this);
this.checkSyncConfig_ = async () => {
await shared.checkSyncConfig(this, this.state.settings);
};
this.e2eeConfig_ = () => {
NavService.go('EncryptionConfig');
};
this.saveButton_press = async () => {
if (this.state.changedSettingKeys.includes('sync.target') && this.state.settings['sync.target'] === SyncTargetRegistry.nameToId('filesystem') && !(await this.checkFilesystemPermission())) {
Alert.alert(_('Warning'), _('In order to use file system synchronisation your permission to write to external storage is required.'));
// Save settings anyway, even if permission has not been granted
}
return shared.saveSettings(this);
};
this.syncStatusButtonPress_ = () => {
NavService.go('Status');
};
this.exportDebugButtonPress_ = async () => {
this.setState({ creatingReport: true });
const service = new ReportService();
const logItems = await reg.logger().lastEntries(null);
const logItemRows = [['Date', 'Level', 'Message']];
for (let i = 0; i < logItems.length; i++) {
const item = logItems[i];
logItemRows.push([time.formatMsToLocal(item.timestamp, 'MM-DDTHH:mm:ss'), item.level, item.message]);
}
const logItemCsv = service.csvCreate(logItemRows);
const itemListCsv = await service.basicItemList({ format: 'csv' });
2019-09-19 22:51:18 +01:00
const filePath = `${RNFS.ExternalDirectoryPath}/syncReport-${new Date().getTime()}.txt`;
const finalText = [logItemCsv, itemListCsv].join('\n================================================================================\n');
await RNFS.writeFile(filePath, finalText);
2019-09-19 22:51:18 +01:00
alert(`Debug report exported to ${filePath}`);
this.setState({ creatingReport: false });
};
this.fixSearchEngineIndexButtonPress_ = async () => {
this.setState({ fixingSearchIndex: true });
await SearchEngine.instance().rebuildIndex();
this.setState({ fixingSearchIndex: false });
};
this.exportProfileButtonPress_ = async () => {
const p = this.state.profileExportPath ? this.state.profileExportPath : `${RNFS.ExternalStorageDirectoryPath}/JoplinProfileExport`;
this.setState({
profileExportStatus: 'prompt',
profileExportPath: p,
});
};
this.exportProfileButtonPress2_ = async () => {
this.setState({ profileExportStatus: 'exporting' });
const dbPath = '/data/data/net.cozic.joplin/databases';
const exportPath = this.state.profileExportPath;
const resourcePath = `${exportPath}/resources`;
try {
{
const copyFiles = async (source, dest) => {
await shim.fsDriver().mkdir(dest);
const files = await shim.fsDriver().readDirStats(source);
for (const file of files) {
const source_ = `${source}/${file.path}`;
const dest_ = `${dest}/${file.path}`;
if (!file.isDirectory()) {
reg.logger().info(`Copying profile: ${source_} => ${dest_}`);
await shim.fsDriver().copy(source_, dest_);
} else {
await copyFiles(source_, dest_);
}
}
};
await copyFiles(dbPath, exportPath);
await copyFiles(Setting.value('resourceDir'), resourcePath);
}
alert('Profile has been exported!');
} catch (error) {
alert(`Could not export files: ${error.message}`);
} finally {
this.setState({ profileExportStatus: 'idle' });
}
};
this.logButtonPress_ = () => {
NavService.go('Log');
};
}
async checkFilesystemPermission() {
if (Platform.OS !== 'android') {
// Not implemented yet
return true;
}
const hasPermission = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (hasPermission) {
return true;
}
const requestResult = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, {
title: _('Information'),
message: _('In order to use file system synchronisation your permission to write to external storage is required.'),
buttonPositive: _('OK'),
});
return requestResult === PermissionsAndroid.RESULTS.GRANTED;
}
UNSAFE_componentWillMount() {
this.setState({ settings: this.props.settings });
2017-08-01 17:59:01 +00:00
}
2017-07-30 23:04:26 +02:00
2017-08-01 17:59:01 +00:00
styles() {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
if (this.styles_[themeId]) return this.styles_[themeId];
this.styles_ = {};
const styles = {
body: {
flex: 1,
justifyContent: 'flex-start',
flexDirection: 'column',
},
2017-08-01 17:59:01 +00:00
settingContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
2017-08-01 17:59:01 +00:00
borderBottomWidth: 1,
borderBottomColor: theme.dividerColor,
paddingTop: theme.marginTop,
paddingBottom: theme.marginBottom,
paddingLeft: theme.marginLeft,
paddingRight: theme.marginRight,
},
settingText: {
color: theme.color,
fontSize: theme.fontSize,
flex: 1,
paddingRight: 5,
2017-08-01 17:59:01 +00:00
},
descriptionText: {
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 03:50:45 -07:00
color: theme.colorFaded,
fontSize: theme.fontSizeSmaller,
flex: 1,
},
sliderUnits: {
color: theme.color,
fontSize: theme.fontSize,
marginRight: 10,
},
settingDescriptionText: {
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 03:50:45 -07:00
color: theme.colorFaded,
fontSize: theme.fontSizeSmaller,
flex: 1,
paddingLeft: theme.marginLeft,
paddingRight: theme.marginRight,
paddingBottom: theme.marginBottom,
},
permissionText: {
color: theme.color,
fontSize: theme.fontSize,
flex: 1,
marginTop: 10,
},
2017-08-01 17:59:01 +00:00
settingControl: {
color: theme.color,
flex: 1,
2017-08-01 17:59:01 +00:00
},
textInput: {
color: theme.color,
},
};
styles.settingContainerNoBottomBorder = Object.assign({}, styles.settingContainer, {
borderBottomWidth: 0,
paddingBottom: theme.marginBottom / 2,
});
styles.settingControl.borderBottomWidth = 1;
styles.settingControl.borderBottomColor = theme.strongDividerColor;
2017-08-01 17:59:01 +00:00
styles.switchSettingText = Object.assign({}, styles.settingText);
styles.switchSettingText.width = '80%';
2017-08-01 17:59:01 +00:00
styles.switchSettingContainer = Object.assign({}, styles.settingContainer);
styles.switchSettingContainer.flexDirection = 'row';
styles.switchSettingContainer.justifyContent = 'space-between';
2017-07-23 19:26:50 +01:00
styles.linkText = Object.assign({}, styles.settingText);
styles.linkText.borderBottomWidth = 1;
styles.linkText.borderBottomColor = theme.color;
styles.linkText.flex = 0;
styles.linkText.fontWeight = 'normal';
styles.headerWrapperStyle = Object.assign({}, styles.settingContainer, theme.headerWrapperStyle);
2017-08-01 17:59:01 +00:00
styles.switchSettingControl = Object.assign({}, styles.settingControl);
delete styles.switchSettingControl.color;
2019-10-09 21:35:13 +02:00
// styles.switchSettingControl.width = '20%';
2017-11-19 00:23:18 +00:00
styles.switchSettingControl.flex = 0;
2017-08-01 17:59:01 +00:00
this.styles_[themeId] = StyleSheet.create(styles);
return this.styles_[themeId];
2017-07-23 19:26:50 +01:00
}
renderHeader(key, title) {
const theme = themeStyle(this.props.theme);
return (
<View key={key} style={this.styles().headerWrapperStyle}>
<Text style={theme.headerStyle}>{title}</Text>
</View>
);
}
renderButton(key, title, clickHandler, options = null) {
if (!options) options = {};
let descriptionComp = null;
if (options.description) {
descriptionComp = (
<View style={{ flex: 1, marginTop: 10 }}>
<Text style={this.styles().descriptionText}>{options.description}</Text>
</View>
);
}
return (
<View key={key} style={this.styles().settingContainer}>
<View style={{ flex: 1, flexDirection: 'column' }}>
<View style={{ flex: 1 }}>
<Button title={title} onPress={clickHandler} disabled={!!options.disabled} />
</View>
{options.statusComp}
{descriptionComp}
</View>
</View>
);
}
sectionToComponent(key, section, settings) {
const settingComps = [];
for (let i = 0; i < section.metadatas.length; i++) {
const md = section.metadatas[i];
if (section.name === 'sync' && md.key === 'sync.resourceDownloadMode') {
const syncTargetMd = SyncTargetRegistry.idToMetadata(settings['sync.target']);
if (syncTargetMd.supportsConfigCheck) {
const messages = shared.checkSyncConfigMessages(this);
const statusComp = !messages.length ? null : (
<View style={{ flex: 1, marginTop: 10 }}>
<Text style={this.styles().descriptionText}>{messages[0]}</Text>
{messages.length >= 1 ? (
<View style={{ marginTop: 10 }}>
<Text style={this.styles().descriptionText}>{messages[1]}</Text>
</View>
) : null}
</View>
);
settingComps.push(this.renderButton('check_sync_config_button', _('Check synchronisation configuration'), this.checkSyncConfig_, { statusComp: statusComp }));
}
}
const settingComp = this.settingToComponent(md.key, settings[md.key]);
settingComps.push(settingComp);
}
if (section.name === 'sync') {
settingComps.push(this.renderButton('e2ee_config_button', _('Encryption Config'), this.e2eeConfig_));
}
if (!settingComps.length) return null;
return (
<View key={key}>
{this.renderHeader(section.name, Setting.sectionNameToLabel(section.name))}
<View>{settingComps}</View>
</View>
);
}
2017-07-31 22:51:24 +02:00
settingToComponent(key, value) {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
const output = null;
2017-07-23 19:26:50 +01:00
const updateSettingValue = (key, value) => {
return shared.updateSettingValue(this, key, value);
};
2017-07-23 19:26:50 +01:00
2017-07-31 22:51:24 +02:00
const md = Setting.settingMetadata(key);
const settingDescription = md.description ? md.description() : '';
2017-07-31 22:51:24 +02:00
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 03:50:45 -07:00
const descriptionComp = !settingDescription ? null : <Text style={this.styles().settingDescriptionText}>{settingDescription}</Text>;
const containerStyle = !settingDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder;
2017-07-31 22:51:24 +02:00
if (md.isEnum) {
value = value.toString();
2017-07-23 19:26:50 +01:00
const items = [];
2017-07-31 22:51:24 +02:00
const settingOptions = md.options();
for (const k in settingOptions) {
2017-07-23 19:26:50 +01:00
if (!settingOptions.hasOwnProperty(k)) continue;
items.push({ label: settingOptions[k], value: k.toString() });
2017-07-23 19:26:50 +01:00
}
return (
<View key={key} style={{ flexDirection: 'column', borderBottomWidth: 1, borderBottomColor: theme.dividerColor }}>
<View style={containerStyle}>
<Text key="label" style={this.styles().settingText}>
{md.label()}
</Text>
<Dropdown
key="control"
style={this.styles().settingControl}
items={items}
selectedValue={value}
itemListStyle={{
backgroundColor: theme.backgroundColor,
}}
headerStyle={{
color: theme.color,
fontSize: theme.fontSize,
}}
itemStyle={{
color: theme.color,
fontSize: theme.fontSize,
}}
onValueChange={(itemValue) => {
updateSettingValue(key, itemValue);
}}
/>
</View>
{descriptionComp}
2017-07-23 19:26:50 +01:00
</View>
);
2017-07-31 22:51:24 +02:00
} else if (md.type == Setting.TYPE_BOOL) {
return (
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 03:50:45 -07:00
<View key={key}>
<View style={containerStyle}>
<Text key="label" style={this.styles().switchSettingText}>
{md.label()}
</Text>
<Switch key="control" style={this.styles().switchSettingControl} trackColor={{ false: theme.strongDividerColor }} value={value} onValueChange={value => updateSettingValue(key, value)} />
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 03:50:45 -07:00
</View>
{descriptionComp}
</View>
);
2017-07-31 22:51:24 +02:00
} else if (md.type == Setting.TYPE_INT) {
const unitLabel = md.unitLabel ? md.unitLabel(value) : value;
// Note: Do NOT add the minimumTrackTintColor and maximumTrackTintColor props
// on the Slider as they are buggy and can crash the app on certain devices.
// https://github.com/laurent22/joplin/issues/2733
// https://github.com/react-native-community/react-native-slider/issues/161
2017-07-25 22:55:26 +01:00
return (
2017-08-01 17:59:01 +00:00
<View key={key} style={this.styles().settingContainer}>
<Text key="label" style={this.styles().settingText}>
{md.label()}
</Text>
<View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', flex: 1 }}>
<Text style={this.styles().sliderUnits}>{unitLabel}</Text>
<Slider key="control" style={{ flex: 1 }} step={md.step} minimumValue={md.minimum} maximumValue={md.maximum} value={value} onValueChange={value => updateSettingValue(key, value)} />
</View>
2017-07-25 22:55:26 +01:00
</View>
);
} else if (md.type == Setting.TYPE_STRING) {
return (
<View key={key} style={this.styles().settingContainer}>
<Text key="label" style={this.styles().settingText}>
{md.label()}
</Text>
<TextInput autoCorrect={false} autoCompleteType="off" selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} autoCapitalize="none" key="control" style={this.styles().settingControl} value={value} onChangeText={value => updateSettingValue(key, value)} secureTextEntry={!!md.secure} />
</View>
);
} else {
2019-10-09 21:35:13 +02:00
// throw new Error('Unsupported setting type: ' + md.type);
2017-07-23 19:26:50 +01:00
}
return output;
}
render() {
const settings = this.state.settings;
2017-07-23 19:26:50 +01:00
const theme = themeStyle(this.props.theme);
const settingComps = shared.settingsToComponents2(this, 'mobile', settings);
settingComps.push(this.renderHeader('tools', _('Tools')));
settingComps.push(this.renderButton('status_button', _('Sync Status'), this.syncStatusButtonPress_));
settingComps.push(this.renderButton('log_button', _('Log'), this.logButtonPress_));
if (Platform.OS === 'android') {
settingComps.push(this.renderButton('export_report_button', this.state.creatingReport ? _('Creating report...') : _('Export Debug Report'), this.exportDebugButtonPress_, { disabled: this.state.creatingReport }));
}
2019-07-21 22:27:13 +00:00
settingComps.push(this.renderButton('fix_search_engine_index', this.state.fixingSearchIndex ? _('Fixing search index...') : _('Fix search index'), this.fixSearchEngineIndexButtonPress_, { disabled: this.state.fixingSearchIndex, description: _('Use this to rebuild the search index if there is a problem with search. It may take a long time depending on the number of notes.') }));
if (shim.mobilePlatform() === 'android') {
settingComps.push(this.renderButton('export_data', this.state.profileExportStatus === 'exporting' ? _('Exporting profile...') : _('Export profile'), this.exportProfileButtonPress_, { disabled: this.state.profileExportStatus === 'exporting', description: _('For debugging purpose only: export your profile to an external SD card.') }));
if (this.state.profileExportStatus === 'prompt') {
const profileExportPrompt = (
<View style={this.styles().settingContainer}>
<Text style={this.styles().settingText}>Path:</Text>
<TextInput style={{ ...this.styles().textInput, paddingRight: 20 }} onChange={(event) => this.setState({ profileExportPath: event.nativeEvent.text })} value={this.state.profileExportPath} placeholder="/path/to/sdcard" keyboardAppearance={theme.keyboardAppearance}></TextInput>
<Button title="OK" onPress={this.exportProfileButtonPress2_}></Button>
</View>
);
settingComps.push(profileExportPrompt);
}
}
settingComps.push(this.renderHeader('moreInfo', _('More information')));
2018-03-09 09:09:13 +00:00
if (Platform.OS === 'android' && Platform.Version >= 23) {
// Note: `PermissionsAndroid` doesn't work so we have to ask the user to manually
// set these permissions. https://stackoverflow.com/questions/49771084/permission-always-returns-never-ask-again
settingComps.push(
<View key="permission_info" style={this.styles().settingContainer}>
<View key="permission_info_wrapper">
<Text key="perm1a" style={this.styles().settingText}>
{_('To work correctly, the app needs the following permissions. Please enable them in your phone settings, in Apps > Joplin > Permissions')}
</Text>
<Text key="perm2" style={this.styles().permissionText}>
{_('- Storage: to allow attaching files to notes and to enable filesystem synchronisation.')}
</Text>
<Text key="perm3" style={this.styles().permissionText}>
{_('- Camera: to allow taking a picture and attaching it to a note.')}
</Text>
<Text key="perm4" style={this.styles().permissionText}>
{_('- Location: to allow attaching geo-location information to a note.')}
</Text>
</View>
</View>
);
}
2018-03-09 09:09:13 +00:00
settingComps.push(
<View key="donate_link" style={this.styles().settingContainer}>
<TouchableOpacity
onPress={() => {
Linking.openURL('https://joplinapp.org/donate/');
}}
>
<Text key="label" style={this.styles().linkText}>
{_('Make a donation')}
</Text>
2018-03-09 09:09:13 +00:00
</TouchableOpacity>
</View>
);
2018-11-01 20:43:42 -04:00
settingComps.push(
<View key="website_link" style={this.styles().settingContainer}>
<TouchableOpacity
onPress={() => {
Linking.openURL('https://joplinapp.org/');
}}
>
<Text key="label" style={this.styles().linkText}>
{_('Joplin website')}
</Text>
</TouchableOpacity>
</View>
);
settingComps.push(
<View key="privacy_link" style={this.styles().settingContainer}>
<TouchableOpacity
onPress={() => {
Linking.openURL('https://joplinapp.org/privacy/');
}}
>
<Text key="label" style={this.styles().linkText}>
Privacy Policy
</Text>
</TouchableOpacity>
</View>
);
2017-07-23 19:26:50 +01:00
2018-11-01 20:43:42 -04:00
settingComps.push(
<View key="version_info_app" style={this.styles().settingContainer}>
2019-09-19 22:51:18 +01:00
<Text style={this.styles().settingText}>{`Joplin ${VersionInfo.appVersion}`}</Text>
</View>
);
settingComps.push(
<View key="version_info_db" style={this.styles().settingContainer}>
<Text style={this.styles().settingText}>{_('Database v%s', reg.db().version())}</Text>
</View>
);
settingComps.push(
<View key="version_info_fts" style={this.styles().settingContainer}>
<Text style={this.styles().settingText}>{_('FTS enabled: %d', this.props.settings['db.ftsEnabled'])}</Text>
2018-11-01 20:43:42 -04:00
</View>
);
2017-07-23 19:26:50 +01:00
return (
2017-08-01 17:59:01 +00:00
<View style={this.rootStyle(this.props.theme).root}>
<ScreenHeader title={_('Configuration')} showSaveButton={true} showSearchButton={false} showSideMenuButton={false} saveButtonDisabled={!this.state.changedSettingKeys.length} onSaveButtonPress={this.saveButton_press} />
<ScrollView>{settingComps}</ScrollView>
2017-07-23 19:26:50 +01:00
</View>
);
}
}
const ConfigScreen = connect(state => {
return {
settings: state.settings,
theme: state.settings.theme,
};
})(ConfigScreenComponent);
2017-07-23 19:26:50 +01:00
2018-11-01 20:43:42 -04:00
module.exports = { ConfigScreen };