const React = require('react'); const Component = React.Component;
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 { _, setLocale } = 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 SearchEngine = require('lib/services/SearchEngine');
const RNFS = require('react-native-fs');
import Slider from '@react-native-community/slider';
class ConfigScreenComponent extends BaseScreenComponent {
static navigationOptions(options) {
return { header: null };
}
constructor() {
super();
this.styles_ = {};
this.state = {
creatingReport: false,
};
shared.init(this);
this.checkSyncConfig_ = async () => {
await shared.checkSyncConfig(this, this.state.settings);
}
this.e2eeConfig_ = () => {
NavService.go('EncryptionConfig');
}
this.saveButton_press = () => {
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' });
const filePath = RNFS.ExternalDirectoryPath + '/syncReport-' + (new Date()).getTime() + '.txt';
const finalText = [logItemCsv, itemListCsv].join("\n================================================================================\n");
await RNFS.writeFile(filePath, finalText);
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.logButtonPress_ = () => {
NavService.go('Log');
}
}
UNSAFE_componentWillMount() {
this.setState({ settings: this.props.settings });
}
styles() {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
if (this.styles_[themeId]) return this.styles_[themeId];
this.styles_ = {};
let styles = {
body: {
flex: 1,
justifyContent: 'flex-start',
flexDirection: 'column',
},
settingContainer: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
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,
},
descriptionText: {
color: theme.color,
fontSize: theme.fontSize,
flex: 1,
},
sliderUnits: {
color: theme.color,
fontSize: theme.fontSize,
marginRight: 10,
},
settingDescriptionText: {
color: theme.color,
fontSize: theme.fontSize,
flex: 1,
paddingLeft: theme.marginLeft,
paddingRight: theme.marginRight,
paddingBottom: theme.marginBottom,
},
permissionText: {
color: theme.color,
fontSize: theme.fontSize,
flex: 1,
marginTop: 10,
},
settingControl: {
color: theme.color,
flex: 1,
},
}
styles.settingContainerNoBottomBorder = Object.assign({}, styles.settingContainer, {
borderBottomWidth: 0,
paddingBottom: theme.marginBottom / 2,
});
styles.settingControl.borderBottomWidth = 1;
styles.settingControl.borderBottomColor = theme.strongDividerColor;
styles.switchSettingText = Object.assign({}, styles.settingText);
styles.switchSettingText.width = '80%';
styles.switchSettingContainer = Object.assign({}, styles.settingContainer);
styles.switchSettingContainer.flexDirection = 'row';
styles.switchSettingContainer.justifyContent = 'space-between';
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)
styles.switchSettingControl = Object.assign({}, styles.settingControl);
delete styles.switchSettingControl.color;
//styles.switchSettingControl.width = '20%';
styles.switchSettingControl.flex = 0;
this.styles_[themeId] = StyleSheet.create(styles);
return this.styles_[themeId];
}
renderHeader(key, title) {
const theme = themeStyle(this.props.theme);
return (
{title}
);
}
renderButton(key, title, clickHandler, options = null) {
if (!options) options = {};
let descriptionComp = null;
if (options.description) {
descriptionComp = (
{options.description}
);
}
return (
{ options.statusComp }
{ descriptionComp }
);
}
sectionToComponent(key, section, settings) {
const theme = themeStyle(this.props.theme);
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 : (
{messages[0]}
{messages.length >= 1 ? ({messages[1]}) : null}
);
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_));
}
const headerWrapperStyle = this.styles().headerWrapperStyle;
return (
{this.renderHeader(section.name, Setting.sectionNameToLabel(section.name))}
{settingComps}
);
}
settingToComponent(key, value) {
const themeId = this.props.theme;
const theme = themeStyle(themeId);
let output = null;
const updateSettingValue = (key, value) => {
return shared.updateSettingValue(this, key, value);
}
const md = Setting.settingMetadata(key);
const settingDescription = md.description ? md.description() : '';
if (md.isEnum) {
value = value.toString();
let items = [];
const settingOptions = md.options();
for (let k in settingOptions) {
if (!settingOptions.hasOwnProperty(k)) continue;
items.push({ label: settingOptions[k], value: k.toString() });
}
const descriptionComp = !settingDescription ? null : {settingDescription}
const containerStyle = !settingDescription ? this.styles().settingContainer : this.styles().settingContainerNoBottomBorder;
return (
{md.label()}
{ updateSettingValue(key, itemValue); }}
/>
{descriptionComp}
);
} else if (md.type == Setting.TYPE_BOOL) {
return (
{md.label()}
updateSettingValue(key, value)} />
);
} else if (md.type == Setting.TYPE_INT) {
const unitLabel = md.unitLabel ? md.unitLabel(value) : value;
return (
{md.label()}
{unitLabel}
updateSettingValue(key, value)} />
);
} else if (md.type == Setting.TYPE_STRING) {
return (
{md.label()}
updateSettingValue(key, value)} secureTextEntry={!!md.secure} />
);
} else {
//throw new Error('Unsupported setting type: ' + md.type);
}
return output;
}
render() {
const settings = this.state.settings;
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_));
settingComps.push(this.renderButton('export_report_button', this.state.creatingReport ? _('Creating report...') : _('Export Debug Report'), this.exportDebugButtonPress_, { disabled: this.state.creatingReport }));
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 some times depending on the number of notes.') }));
settingComps.push(this.renderHeader('moreInfo', _('More information')));
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(
{_('To work correctly, the app needs the following permissions. Please enable them in your phone settings, in Apps > Joplin > Permissions')}
{_('- Storage: to allow attaching files to notes and to enable filesystem synchronisation.')}
{_('- Camera: to allow taking a picture and attaching it to a note.')}
{_('- Location: to allow attaching geo-location information to a note.')}
);
}
settingComps.push(
{ Linking.openURL('https://joplinapp.org/donate/') }}>
{_('Make a donation')}
);
settingComps.push(
{ Linking.openURL('https://joplinapp.org/') }}>
{_('Joplin website')}
);
settingComps.push(
{ Linking.openURL('https://joplinapp.org/privacy/') }}>
Privacy Policy
);
settingComps.push(
{"Joplin " + VersionInfo.appVersion}
);
settingComps.push(
{_('Database v%s', reg.db().version())}
);
settingComps.push(
{_('FTS enabled: %d', this.props.settings['db.ftsEnabled'])}
);
return (
{ settingComps }
);
}
}
const ConfigScreen = connect(
(state) => {
return {
settings: state.settings,
theme: state.settings.theme,
};
}
)(ConfigScreenComponent)
module.exports = { ConfigScreen };