mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-02 12:47:41 +02:00
Mobile: Refactored and made dialog boxes more reliable
This commit is contained in:
parent
b7f5f848f2
commit
f432734338
@ -100,6 +100,7 @@ ElectronClient/gui/ResourceScreen.js
|
|||||||
ElectronClient/gui/ShareNoteDialog.js
|
ElectronClient/gui/ShareNoteDialog.js
|
||||||
ReactNativeClient/lib/AsyncActionQueue.js
|
ReactNativeClient/lib/AsyncActionQueue.js
|
||||||
ReactNativeClient/lib/checkPermissions.js
|
ReactNativeClient/lib/checkPermissions.js
|
||||||
|
ReactNativeClient/lib/components/dialogs.js
|
||||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||||
ReactNativeClient/lib/hooks/usePrevious.js
|
ReactNativeClient/lib/hooks/usePrevious.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -90,6 +90,7 @@ ElectronClient/gui/ResourceScreen.js
|
|||||||
ElectronClient/gui/ShareNoteDialog.js
|
ElectronClient/gui/ShareNoteDialog.js
|
||||||
ReactNativeClient/lib/AsyncActionQueue.js
|
ReactNativeClient/lib/AsyncActionQueue.js
|
||||||
ReactNativeClient/lib/checkPermissions.js
|
ReactNativeClient/lib/checkPermissions.js
|
||||||
|
ReactNativeClient/lib/components/dialogs.js
|
||||||
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
|
||||||
ReactNativeClient/lib/hooks/usePrevious.js
|
ReactNativeClient/lib/hooks/usePrevious.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||||
|
103
ReactNativeClient/lib/components/dialogs.tsx
Normal file
103
ReactNativeClient/lib/components/dialogs.tsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const { forwardRef,useState, useImperativeHandle } = require('react');
|
||||||
|
const RnDialog = require('react-native-dialog').default;
|
||||||
|
const { View } = require('react-native');
|
||||||
|
const { _ } = require('lib/locale');
|
||||||
|
|
||||||
|
let dialogRef_:any = null;
|
||||||
|
|
||||||
|
export function initializeDialogs(ref:any) {
|
||||||
|
dialogRef_ = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Dialog = forwardRef(function(_props:any, ref:any) {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [type, setType] = useState('');
|
||||||
|
const [message, setMessage] = useState('');
|
||||||
|
const [buttons, setButtons] = useState([]);
|
||||||
|
const [resultPromise, setResultPromise] = useState({});
|
||||||
|
|
||||||
|
function dialogTitle(type:string) {
|
||||||
|
if (type === 'info') return _('Information');
|
||||||
|
if (type === 'error') return _('Error');
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => {
|
||||||
|
return {
|
||||||
|
show: async function(type:string, message:string, buttons:any[] = null) {
|
||||||
|
// Shouldn't happen but just to be sure throw an error in this case
|
||||||
|
if (visible) throw new Error('Only one dialog can be visible at a time');
|
||||||
|
|
||||||
|
setVisible(true);
|
||||||
|
setType(type);
|
||||||
|
setMessage(message);
|
||||||
|
setButtons(buttons);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setResultPromise({
|
||||||
|
resolve: resolve,
|
||||||
|
reject: reject,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function onPress(event:any) {
|
||||||
|
setVisible(false);
|
||||||
|
resultPromise.resolve(event ? event.value : true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTitle() {
|
||||||
|
const title = dialogTitle(type);
|
||||||
|
if (!title) return null;
|
||||||
|
return <RnDialog.Title>{title}</RnDialog.Title>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderButtons() {
|
||||||
|
const output = [];
|
||||||
|
|
||||||
|
if (type === 'confirm') {
|
||||||
|
output.push(<RnDialog.Button key="ok" label={_('OK')} onPress={() => onPress({ value: true })} />);
|
||||||
|
output.push(<RnDialog.Button key="cancel" label={_('Cancel')} onPress={() => onPress({ value: false })} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'info' || type === 'error') {
|
||||||
|
output.push(<RnDialog.Button key="ok" label={_('OK')} onPress={onPress} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'pop') {
|
||||||
|
for (const button of buttons) {
|
||||||
|
output.push(<RnDialog.Button key={button.text} label={button.text} onPress={() => onPress({ value: button.id })} />);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<RnDialog.Container visible={visible}>
|
||||||
|
{renderTitle()}
|
||||||
|
<RnDialog.Description>{message}</RnDialog.Description>
|
||||||
|
{renderButtons()}
|
||||||
|
</RnDialog.Container>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
confirm: async function(message:string) {
|
||||||
|
return dialogRef_.current.show('confirm', message);
|
||||||
|
},
|
||||||
|
pop: async function(message:string, buttons:any[]) {
|
||||||
|
return dialogRef_.current.show('pop', message, buttons);
|
||||||
|
},
|
||||||
|
error: async function(message:string) {
|
||||||
|
return dialogRef_.current.show('error', message);
|
||||||
|
},
|
||||||
|
info: async function(message:string) {
|
||||||
|
return dialogRef_.current.show('info', message);
|
||||||
|
},
|
||||||
|
};
|
@ -12,8 +12,7 @@ const Note = require('lib/models/Note.js');
|
|||||||
const Folder = require('lib/models/Folder.js');
|
const Folder = require('lib/models/Folder.js');
|
||||||
const { themeStyle } = require('lib/components/global-style.js');
|
const { themeStyle } = require('lib/components/global-style.js');
|
||||||
const { Dropdown } = require('lib/components/Dropdown.js');
|
const { Dropdown } = require('lib/components/Dropdown.js');
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
|
|
||||||
Icon.loadFont();
|
Icon.loadFont();
|
||||||
|
|
||||||
@ -182,7 +181,7 @@ class ScreenHeaderComponent extends React.PureComponent {
|
|||||||
async deleteButton_press() {
|
async deleteButton_press() {
|
||||||
// Dialog needs to be displayed as a child of the parent component, otherwise
|
// Dialog needs to be displayed as a child of the parent component, otherwise
|
||||||
// it won't be visible within the header component.
|
// it won't be visible within the header component.
|
||||||
const ok = await dialogs.confirm(this.props.parentComponent, _('Delete these notes?'));
|
const ok = await dialogs.confirm(_('Delete these notes?'));
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
const noteIds = this.props.selectedNoteIds;
|
const noteIds = this.props.selectedNoteIds;
|
||||||
@ -400,7 +399,7 @@ class ScreenHeaderComponent extends React.PureComponent {
|
|||||||
|
|
||||||
const folder = await Folder.load(folderId);
|
const folder = await Folder.load(folderId);
|
||||||
|
|
||||||
const ok = noteIds.length > 1 ? await dialogs.confirm(this.props.parentComponent, _('Move %d notes to notebook "%s"?', noteIds.length, folder.title)) : true;
|
const ok = noteIds.length > 1 ? await dialogs.confirm(_('Move %d notes to notebook "%s"?', noteIds.length, folder.title)) : true;
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
this.props.dispatch({ type: 'NOTE_SELECTION_END' });
|
this.props.dispatch({ type: 'NOTE_SELECTION_END' });
|
||||||
@ -480,11 +479,6 @@ class ScreenHeaderComponent extends React.PureComponent {
|
|||||||
{menuComp}
|
{menuComp}
|
||||||
</View>
|
</View>
|
||||||
{warningComps}
|
{warningComps}
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,7 @@ const { connect } = require('react-redux');
|
|||||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
|
||||||
const Shared = require('lib/components/shared/dropbox-login-shared');
|
const Shared = require('lib/components/shared/dropbox-login-shared');
|
||||||
const { themeStyle } = require('lib/components/global-style.js');
|
const { themeStyle } = require('lib/components/global-style.js');
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ class DropboxLoginScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
this.styles_ = {};
|
this.styles_ = {};
|
||||||
|
|
||||||
this.shared_ = new Shared(this, msg => dialogs.info(this, msg), msg => dialogs.error(this, msg));
|
this.shared_ = new Shared(this, msg => dialogs.info(msg), msg => dialogs.error(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
@ -66,12 +65,6 @@ class DropboxLoginScreenComponent extends BaseScreenComponent {
|
|||||||
{/* Add this extra padding to make sure the view is scrollable when the keyboard is visible on small screens (iPhone SE) */}
|
{/* Add this extra padding to make sure the view is scrollable when the keyboard is visible on small screens (iPhone SE) */}
|
||||||
<View style={{ height: 200 }}></View>
|
<View style={{ height: 200 }}></View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@ const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
|||||||
const { themeStyle } = require('lib/components/global-style.js');
|
const { themeStyle } = require('lib/components/global-style.js');
|
||||||
const { time } = require('lib/time-utils.js');
|
const { time } = require('lib/time-utils.js');
|
||||||
const shared = require('lib/components/shared/encryption-config-shared.js');
|
const shared = require('lib/components/shared/encryption-config-shared.js');
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
|
|
||||||
class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
||||||
static navigationOptions() {
|
static navigationOptions() {
|
||||||
@ -141,7 +140,7 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
|||||||
await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password);
|
await EncryptionService.instance().generateMasterKeyAndEnableEncryption(password);
|
||||||
this.setState({ passwordPromptShow: false });
|
this.setState({ passwordPromptShow: false });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dialogs.error(this, error.message);
|
await dialogs.error(error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,13 +211,13 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
const onToggleButtonClick = async () => {
|
const onToggleButtonClick = async () => {
|
||||||
if (this.props.encryptionEnabled) {
|
if (this.props.encryptionEnabled) {
|
||||||
const ok = await dialogs.confirm(this, _('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?'));
|
const ok = await dialogs.confirm(_('Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?'));
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await EncryptionService.instance().disableEncryption();
|
await EncryptionService.instance().disableEncryption();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dialogs.error(this, error.message);
|
await dialogs.error(error.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -285,11 +284,6 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
|||||||
{nonExistingMasterKeySection}
|
{nonExistingMasterKeySection}
|
||||||
<View style={{ flex: 1, height: 20 }}></View>
|
<View style={{ flex: 1, height: 20 }}></View>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ const Folder = require('lib/models/Folder.js');
|
|||||||
const BaseModel = require('lib/BaseModel.js');
|
const BaseModel = require('lib/BaseModel.js');
|
||||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const { themeStyle } = require('lib/components/global-style.js');
|
const { themeStyle } = require('lib/components/global-style.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ class FolderScreenComponent extends BaseScreenComponent {
|
|||||||
try {
|
try {
|
||||||
folder = await Folder.save(folder, { userSideValidation: true });
|
folder = await Folder.save(folder, { userSideValidation: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dialogs.error(this, _('The notebook could not be saved: %s', error.message));
|
dialogs.error(_('The notebook could not be saved: %s', error.message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,11 +108,6 @@ class FolderScreenComponent extends BaseScreenComponent {
|
|||||||
<View style={this.rootStyle(this.props.theme).root}>
|
<View style={this.rootStyle(this.props.theme).root}>
|
||||||
<ScreenHeader title={_('Edit notebook')} showSaveButton={true} saveButtonDisabled={saveButtonDisabled} onSaveButtonPress={() => this.saveFolderButton_press()} showSideMenuButton={false} showSearchButton={false} />
|
<ScreenHeader title={_('Edit notebook')} showSaveButton={true} saveButtonDisabled={saveButtonDisabled} onSaveButtonPress={() => this.saveFolderButton_press()} showSideMenuButton={false} showSearchButton={false} />
|
||||||
<TextInput placeholder={_('Enter notebook title')} placeholderTextColor={theme.colorFaded} underlineColorAndroid={theme.dividerColor} selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} style={this.styles().textInput} autoFocus={true} value={this.state.folder.title} onChangeText={text => this.title_changeText(text)} />
|
<TextInput placeholder={_('Enter notebook title')} placeholderTextColor={theme.colorFaded} underlineColorAndroid={theme.dividerColor} selectionColor={theme.textSelectionColor} keyboardAppearance={theme.keyboardAppearance} style={this.styles().textInput} autoFocus={true} value={this.state.folder.title} onChangeText={text => this.title_changeText(text)} />
|
||||||
<dialogs.DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,7 @@ const { shim } = require('lib/shim.js');
|
|||||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||||
const { themeStyle, editorFont } = require('lib/components/global-style.js');
|
const { themeStyle, editorFont } = require('lib/components/global-style.js');
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
const { NoteBodyViewer } = require('lib/components/note-body-viewer.js');
|
const { NoteBodyViewer } = require('lib/components/note-body-viewer.js');
|
||||||
const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker');
|
const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker');
|
||||||
const ImageResizer = require('react-native-image-resizer').default;
|
const ImageResizer = require('react-native-image-resizer').default;
|
||||||
@ -90,7 +89,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
const saveDialog = async () => {
|
const saveDialog = async () => {
|
||||||
if (this.isModified()) {
|
if (this.isModified()) {
|
||||||
const buttonId = await dialogs.pop(this, _('This note has been modified:'), [{ text: _('Save changes'), id: 'save' }, { text: _('Discard changes'), id: 'discard' }, { text: _('Cancel'), id: 'cancel' }]);
|
const buttonId = await dialogs.pop(_('This note has been modified:'), [{ text: _('Save changes'), id: 'save' }, { text: _('Discard changes'), id: 'discard' }, { text: _('Cancel'), id: 'cancel' }]);
|
||||||
|
|
||||||
if (buttonId == 'cancel') return true;
|
if (buttonId == 'cancel') return true;
|
||||||
if (buttonId == 'save') await this.saveNoteButton_press();
|
if (buttonId == 'save') await this.saveNoteButton_press();
|
||||||
@ -185,7 +184,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
dialogs.error(this, error.message);
|
dialogs.error(error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -398,7 +397,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
const note = this.state.note;
|
const note = this.state.note;
|
||||||
if (!note.id) return;
|
if (!note.id) return;
|
||||||
|
|
||||||
const ok = await dialogs.confirm(this, _('Delete note?'));
|
const ok = await dialogs.confirm(_('Delete note?'));
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
const folderId = note.parent_id;
|
const folderId = note.parent_id;
|
||||||
@ -460,7 +459,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
let mustResize = dimensions.width > maxSize || dimensions.height > maxSize;
|
let mustResize = dimensions.width > maxSize || dimensions.height > maxSize;
|
||||||
|
|
||||||
if (mustResize) {
|
if (mustResize) {
|
||||||
const buttonId = await dialogs.pop(this, _('You are about to attach a large image (%dx%d pixels). Would you like to resize it down to %d pixels before attaching it?', dimensions.width, dimensions.height, maxSize), [
|
const buttonId = await dialogs.pop(_('You are about to attach a large image (%dx%d pixels). Would you like to resize it down to %d pixels before attaching it?', dimensions.width, dimensions.height, maxSize), [
|
||||||
{ text: _('Yes'), id: 'yes' },
|
{ text: _('Yes'), id: 'yes' },
|
||||||
{ text: _('No'), id: 'no' },
|
{ text: _('No'), id: 'no' },
|
||||||
{ text: _('Cancel'), id: 'cancel' },
|
{ text: _('Cancel'), id: 'cancel' },
|
||||||
@ -555,7 +554,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
if (!done) return;
|
if (!done) return;
|
||||||
} else {
|
} else {
|
||||||
if (fileType === 'image') {
|
if (fileType === 'image') {
|
||||||
dialogs.error(this, _('Unsupported image type: %s', mimeType));
|
dialogs.error(_('Unsupported image type: %s', mimeType));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
await shim.fsDriver().copy(localFilePath, targetPath);
|
await shim.fsDriver().copy(localFilePath, targetPath);
|
||||||
@ -569,7 +568,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
reg.logger().warn('Could not attach file:', error);
|
reg.logger().warn('Could not attach file:', error);
|
||||||
await dialogs.error(this, error.message);
|
await dialogs.error(error.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -681,7 +680,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
Linking.openURL(url);
|
Linking.openURL(url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
this.props.dispatch({ type: 'SIDE_MENU_CLOSE' });
|
||||||
await dialogs.error(this, error.message);
|
await dialogs.error(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -692,7 +691,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
try {
|
try {
|
||||||
Linking.openURL(note.source_url);
|
Linking.openURL(note.source_url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dialogs.error(this, error.message);
|
await dialogs.error(error.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,7 +751,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
output.push({
|
output.push({
|
||||||
title: _('Attach...'),
|
title: _('Attach...'),
|
||||||
onPress: async () => {
|
onPress: async () => {
|
||||||
const buttonId = await dialogs.pop(this, _('Choose an option'), [{ text: _('Take photo'), id: 'takePhoto' }, { text: _('Attach photo'), id: 'attachPhoto' }, { text: _('Attach any file'), id: 'attachFile' }]);
|
const buttonId = await dialogs.pop(_('Choose an option'), [{ text: _('Take photo'), id: 'takePhoto' }, { text: _('Attach photo'), id: 'attachPhoto' }, { text: _('Attach any file'), id: 'attachFile' }]);
|
||||||
|
|
||||||
if (buttonId === 'takePhoto') this.takePhoto_onPress();
|
if (buttonId === 'takePhoto') this.takePhoto_onPress();
|
||||||
if (buttonId === 'attachPhoto') this.attachPhoto_onPress();
|
if (buttonId === 'attachPhoto') this.attachPhoto_onPress();
|
||||||
@ -1082,12 +1081,6 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
{!Setting.value('editor.beta') && actionButtonComp}
|
{!Setting.value('editor.beta') && actionButtonComp}
|
||||||
|
|
||||||
<SelectDateTimeDialog shown={this.state.alarmDialogShown} date={dueDate} onAccept={this.onAlarmDialogAccept} onReject={this.onAlarmDialogReject} />
|
<SelectDateTimeDialog shown={this.state.alarmDialogShown} date={dueDate} onAccept={this.onAlarmDialogAccept} onReject={this.onAlarmDialogReject} />
|
||||||
|
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{noteTagDialog}
|
{noteTagDialog}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -12,8 +12,7 @@ const { themeStyle } = require('lib/components/global-style.js');
|
|||||||
const { ScreenHeader } = require('lib/components/screen-header.js');
|
const { ScreenHeader } = require('lib/components/screen-header.js');
|
||||||
const { _ } = require('lib/locale.js');
|
const { _ } = require('lib/locale.js');
|
||||||
const { ActionButton } = require('lib/components/action-button.js');
|
const { ActionButton } = require('lib/components/action-button.js');
|
||||||
const { dialogs } = require('lib/dialogs.js');
|
const dialogs = require('lib/components/dialogs.js').default;
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||||
const { BackButtonService } = require('lib/services/back-button.js');
|
const { BackButtonService } = require('lib/services/back-button.js');
|
||||||
|
|
||||||
@ -64,17 +63,17 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
id: { name: 'showCompletedTodos', value: !Setting.value('showCompletedTodos') },
|
id: { name: 'showCompletedTodos', value: !Setting.value('showCompletedTodos') },
|
||||||
});
|
});
|
||||||
|
|
||||||
const r = await dialogs.pop(this, Setting.settingMetadata('notes.sortOrder.field').label(), buttons);
|
const r = await dialogs.pop(Setting.settingMetadata('notes.sortOrder.field').label(), buttons);
|
||||||
if (!r) return;
|
if (!r) return;
|
||||||
|
|
||||||
Setting.setValue(r.name, r.value);
|
Setting.setValue(r.name, r.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.backHandler = () => {
|
this.backHandler = () => {
|
||||||
if (this.dialogbox.state.isVisible) {
|
// if (this.dialogbox.state.isVisible) {
|
||||||
this.dialogbox.close();
|
// this.dialogbox.close();
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -151,7 +150,7 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteFolder_onPress(folderId) {
|
deleteFolder_onPress(folderId) {
|
||||||
dialogs.confirm(this, _('Delete notebook? All notes and sub-notebooks within this notebook will also be deleted.')).then(ok => {
|
dialogs.confirm(_('Delete notebook? All notes and sub-notebooks within this notebook will also be deleted.')).then(ok => {
|
||||||
if (!ok) return;
|
if (!ok) return;
|
||||||
|
|
||||||
Folder.delete(folderId)
|
Folder.delete(folderId)
|
||||||
@ -236,11 +235,7 @@ class NotesScreenComponent extends BaseScreenComponent {
|
|||||||
<ScreenHeader title={title} showBackButton={false} parentComponent={thisComp} sortButton_press={this.sortButton_press} folderPickerOptions={this.folderPickerOptions()} showSearchButton={true} showSideMenuButton={true} />
|
<ScreenHeader title={title} showBackButton={false} parentComponent={thisComp} sortButton_press={this.sortButton_press} folderPickerOptions={this.folderPickerOptions()} showSearchButton={true} showSideMenuButton={true} />
|
||||||
<NoteList style={this.styles().noteList} />
|
<NoteList style={this.styles().noteList} />
|
||||||
{actionButtonComp}
|
{actionButtonComp}
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ const { NoteItem } = require('lib/components/note-item.js');
|
|||||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||||
const { themeStyle } = require('lib/components/global-style.js');
|
const { themeStyle } = require('lib/components/global-style.js');
|
||||||
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
|
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
|
||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
|
|
||||||
Icon.loadFont();
|
Icon.loadFont();
|
||||||
|
|
||||||
@ -190,11 +189,6 @@ class SearchScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
<FlatList data={this.state.notes} keyExtractor={(item) => item.id} renderItem={event => <NoteItem note={event.item} />} />
|
<FlatList data={this.state.notes} keyExtractor={(item) => item.id} renderItem={event => <NoteItem note={event.item} />} />
|
||||||
</View>
|
</View>
|
||||||
<DialogBox
|
|
||||||
ref={dialogbox => {
|
|
||||||
this.dialogbox = dialogbox;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
const DialogBox = require('react-native-dialogbox').default;
|
|
||||||
const { Keyboard } = require('react-native');
|
|
||||||
|
|
||||||
// Add this at the bottom of the component:
|
|
||||||
//
|
|
||||||
// <DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
|
|
||||||
|
|
||||||
const dialogs = {};
|
|
||||||
|
|
||||||
dialogs.confirm = (parentComponent, message) => {
|
|
||||||
if (!parentComponent) throw new Error('parentComponent is required');
|
|
||||||
if (!('dialogbox' in parentComponent)) throw new Error('A "dialogbox" component must be defined on the parent component!');
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
Keyboard.dismiss();
|
|
||||||
|
|
||||||
parentComponent.dialogbox.confirm({
|
|
||||||
content: message,
|
|
||||||
|
|
||||||
ok: {
|
|
||||||
callback: () => {
|
|
||||||
resolve(true);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel: {
|
|
||||||
callback: () => {
|
|
||||||
resolve(false);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
dialogs.pop = (parentComponent, message, buttons, options = null) => {
|
|
||||||
if (!parentComponent) throw new Error('parentComponent is required');
|
|
||||||
if (!('dialogbox' in parentComponent)) throw new Error('A "dialogbox" component must be defined on the parent component!');
|
|
||||||
|
|
||||||
if (!options) options = {};
|
|
||||||
if (!('buttonFlow' in options)) options.buttonFlow = 'auto';
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
Keyboard.dismiss();
|
|
||||||
|
|
||||||
const btns = [];
|
|
||||||
for (let i = 0; i < buttons.length; i++) {
|
|
||||||
btns.push({
|
|
||||||
text: buttons[i].text,
|
|
||||||
callback: () => {
|
|
||||||
parentComponent.dialogbox.close();
|
|
||||||
resolve(buttons[i].id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
parentComponent.dialogbox.pop({
|
|
||||||
content: message,
|
|
||||||
btns: btns,
|
|
||||||
buttonFlow: options.buttonFlow,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
dialogs.error = (parentComponent, message) => {
|
|
||||||
Keyboard.dismiss();
|
|
||||||
return parentComponent.dialogbox.alert(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
dialogs.info = (parentComponent, message) => {
|
|
||||||
Keyboard.dismiss();
|
|
||||||
return parentComponent.dialogbox.alert(message);
|
|
||||||
};
|
|
||||||
|
|
||||||
dialogs.DialogBox = DialogBox;
|
|
||||||
|
|
||||||
module.exports = { dialogs };
|
|
68
ReactNativeClient/package-lock.json
generated
68
ReactNativeClient/package-lock.json
generated
@ -8739,6 +8739,34 @@
|
|||||||
"prop-types": "^15.5.10"
|
"prop-types": "^15.5.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-animatable": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-animatable/-/react-native-animatable-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-2ckIxZQAsvWn25Ho+DK3d1mXIgj7tITkrS4pYDvx96WyOttSvzzFeQnM2od0+FUMzILbdHDsDEqZvnz1DYNQ1w==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"requires": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prop-types": {
|
||||||
|
"version": "15.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||||
|
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"react-is": "^16.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-camera": {
|
"react-native-camera": {
|
||||||
"version": "2.10.2",
|
"version": "2.10.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-2.10.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-2.10.2.tgz",
|
||||||
@ -8780,12 +8808,13 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-5.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-5.5.1.tgz",
|
||||||
"integrity": "sha512-ceaGnxOAULRzVpx03bPjbayuHD5g21CcdmKKvLK5mV72sbguNZbTHNmnH3//Bx9Hfy1XOm6zHhM5Yfb7Glqplw=="
|
"integrity": "sha512-ceaGnxOAULRzVpx03bPjbayuHD5g21CcdmKKvLK5mV72sbguNZbTHNmnH3//Bx9Hfy1XOm6zHhM5Yfb7Glqplw=="
|
||||||
},
|
},
|
||||||
"react-native-dialogbox": {
|
"react-native-dialog": {
|
||||||
"version": "0.6.10",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-dialogbox/-/react-native-dialogbox-0.6.10.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-dialog/-/react-native-dialog-5.6.0.tgz",
|
||||||
"integrity": "sha512-RlDiFjqpH44Nfd+5ok1Xsf4QkUpzURhsDCq2UDUqpBWBzPO/2GVVIFGhJlLzATZsfxf+yVXUWrgW2qcaxNuoNg==",
|
"integrity": "sha512-pUTxHJHzErMY+JaDRSMKiCbJTEdy2Ik4hcNOwasOlxpj6S6tT5SonLsrLPGBCO0XpTOySE0qVzuikmKgUDZfig==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.7.2",
|
||||||
|
"react-native-modal": "^9.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": {
|
"loose-envify": {
|
||||||
@ -8900,6 +8929,35 @@
|
|||||||
"prop-types": "^15.5.9"
|
"prop-types": "^15.5.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-native-modal": {
|
||||||
|
"version": "9.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-native-modal/-/react-native-modal-9.0.0.tgz",
|
||||||
|
"integrity": "sha512-j4xeIK9noHU/ksp2Ndc8NI1qJvjApToqGvqLEu2wtYeaISanbhtd0S3V4hZkSlCa3DZtegl6aaMZBLeH1q6xfA==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.6.2",
|
||||||
|
"react-native-animatable": "^1.2.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"requires": {
|
||||||
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prop-types": {
|
||||||
|
"version": "15.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||||
|
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.4.0",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"react-is": "^16.8.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-native-popup-dialog": {
|
"react-native-popup-dialog": {
|
||||||
"version": "0.9.38",
|
"version": "0.9.38",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-popup-dialog/-/react-native-popup-dialog-0.9.38.tgz",
|
"resolved": "https://registry.npmjs.org/react-native-popup-dialog/-/react-native-popup-dialog-0.9.38.tgz",
|
||||||
|
@ -59,7 +59,7 @@
|
|||||||
"react-native-camera": "^2.10.2",
|
"react-native-camera": "^2.10.2",
|
||||||
"react-native-datepicker": "^1.6.0",
|
"react-native-datepicker": "^1.6.0",
|
||||||
"react-native-device-info": "^5.5.1",
|
"react-native-device-info": "^5.5.1",
|
||||||
"react-native-dialogbox": "^0.6.10",
|
"react-native-dialog": "^5.6.0",
|
||||||
"react-native-document-picker": "^2.3.0",
|
"react-native-document-picker": "^2.3.0",
|
||||||
"react-native-dropdownalert": "^3.1.2",
|
"react-native-dropdownalert": "^3.1.2",
|
||||||
"react-native-file-viewer": "^1.0.15",
|
"react-native-file-viewer": "^1.0.15",
|
||||||
|
@ -31,6 +31,7 @@ const BaseService = require('lib/services/BaseService.js');
|
|||||||
const ResourceService = require('lib/services/ResourceService');
|
const ResourceService = require('lib/services/ResourceService');
|
||||||
const RevisionService = require('lib/services/RevisionService');
|
const RevisionService = require('lib/services/RevisionService');
|
||||||
const KvStore = require('lib/services/KvStore');
|
const KvStore = require('lib/services/KvStore');
|
||||||
|
const { Dialog, initializeDialogs } = require('lib/components/dialogs.js');
|
||||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||||
const { Database } = require('lib/database.js');
|
const { Database } = require('lib/database.js');
|
||||||
const { NotesScreen } = require('lib/components/screens/notes.js');
|
const { NotesScreen } = require('lib/components/screens/notes.js');
|
||||||
@ -607,6 +608,10 @@ class AppComponent extends React.Component {
|
|||||||
this.onAppStateChange_ = () => {
|
this.onAppStateChange_ = () => {
|
||||||
PoorManIntervals.update();
|
PoorManIntervals.update();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.dialogRef = React.createRef();
|
||||||
|
|
||||||
|
initializeDialogs(this.dialogRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@ -749,6 +754,7 @@ class AppComponent extends React.Component {
|
|||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</MenuContext>
|
</MenuContext>
|
||||||
</SideMenu>
|
</SideMenu>
|
||||||
|
<Dialog ref={this.dialogRef}/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
"strictFunctionTypes": true,
|
"strictFunctionTypes": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
|
// This is needed because otherwise we get errors in node_modules for React
|
||||||
|
// but ideally it should be made to work.
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
Loading…
Reference in New Issue
Block a user