1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Getting Dropbox to work in mobile app

This commit is contained in:
Laurent Cozic 2018-03-27 00:55:44 +01:00
parent 6e994fd8b9
commit 96fb7c2087
9 changed files with 162 additions and 70 deletions

View File

@ -6,71 +6,22 @@ const { Header } = require('./Header.min.js');
const { themeStyle } = require('../theme.js');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const { _ } = require('lib/locale.js');
const Shared = require('lib/components/shared/dropbox-login-shared');
class DropboxLoginScreenComponent extends React.Component {
constructor() {
super();
this.dropboxApi_ = null;
this.state = {
loginUrl: '',
authCode: '',
checkingAuthToken: false,
};
this.loginUrl_click = () => {
if (!this.state.loginUrl) return;
bridge().openExternal(this.state.loginUrl)
}
this.authCodeInput_change = (event) => {
this.setState({
authCode: event.target.value
});
}
this.submit_click = async () => {
this.setState({ checkingAuthToken: true });
const api = await this.dropboxApi();
try {
const response = await api.execAuthToken(this.state.authCode);
Setting.setValue('sync.' + this.syncTargetId() + '.auth', response.access_token);
api.setAuthToken(response.access_token);
bridge().showInfoMessageBox(_('The application has been authorised!'));
this.props.dispatch({ type: 'NAV_BACK' });
} catch (error) {
bridge().showErrorMessageBox(_('Could not authorise application:\n\n%s\n\nPlease try again.', error.message));
} finally {
this.setState({ checkingAuthToken: false });
}
}
this.shared_ = new Shared(
this,
(msg) => bridge().showInfoMessageBox(msg),
(msg) => bridge().showErrorMessageBox(msg)
);
}
componentWillMount() {
this.refreshUrl();
}
syncTargetId() {
return SyncTargetRegistry.nameToId('dropbox');
}
async dropboxApi() {
if (this.dropboxApi_) return this.dropboxApi_;
const syncTarget = reg.syncTarget(this.syncTargetId());
this.dropboxApi_ = await syncTarget.api();
return this.dropboxApi_;
}
async refreshUrl() {
const api = await this.dropboxApi();
this.setState({
loginUrl: api.loginUrl(),
});
this.shared_.refreshUrl();
}
render() {
@ -89,10 +40,10 @@ class DropboxLoginScreenComponent extends React.Component {
<div style={{padding: theme.margin}}>
<p style={theme.textStyle}>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</p>
<p style={theme.textStyle}>{_('Step 1: Open this URL in your browser to authorise the application:')}</p>
<a style={theme.textStyle} href="#" onClick={this.loginUrl_click}>{this.state.loginUrl}</a>
<a style={theme.textStyle} href="#" onClick={this.shared_.loginUrl_click}>{this.state.loginUrl}</a>
<p style={theme.textStyle}>{_('Step 2: Enter the code provided by Dropbox:')}</p>
<p><input type="text" value={this.state.authCode} onChange={this.authCodeInput_change} style={inputStyle}/></p>
<button disabled={this.state.checkingAuthToken} onClick={this.submit_click}>{_('Submit')}</button>
<p><input type="text" value={this.state.authCode} onChange={this.shared_.authCodeInput_change} style={inputStyle}/></p>
<button disabled={this.state.checkingAuthToken} onClick={this.shared_.submit_click}>{_('Submit')}</button>
</div>
</div>
);

View File

@ -0,0 +1,57 @@
const React = require('react'); const Component = React.Component;
const { View, Button, Text, TextInput, TouchableOpacity } = 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 DialogBox = require('react-native-dialogbox').default;
const { dialogs } = require('lib/dialogs.js');
const Shared = require('lib/components/shared/dropbox-login-shared');
class DropboxLoginScreenComponent extends BaseScreenComponent {
constructor() {
super();
this.shared_ = new Shared(
this,
(msg) => dialogs.info(this, msg),
(msg) => dialogs.error(this, msg)
);
}
componentWillMount() {
this.shared_.refreshUrl();
}
render() {
return (
<View style={this.styles().screen}>
<ScreenHeader title={_('Login with Dropbox')}/>
<Text>{_('To allow Joplin to synchronise with Dropbox, please follow the steps below:')}</Text>
<Text>{_('Step 1: Open this URL in your browser to authorise the application:')}</Text>
<View>
<TouchableOpacity onPress={this.shared_.loginUrl_click}>
<Text>{this.state.loginUrl}</Text>
</TouchableOpacity>
</View>
<Text>{_('Step 2: Enter the code provided by Dropbox:')}</Text>
<TextInput value={this.state.authCode} onChangeText={this.shared_.authCodeInput_change}/>
<Button disabled={this.state.checkingAuthToken} title={_("Submit")} onPress={this.shared_.submit_click}></Button>
<DialogBox ref={dialogbox => { this.dialogbox = dialogbox }}/>
</View>
);
}
}
const DropboxLoginScreen = connect(
(state) => {
return {};
}
)(DropboxLoginScreenComponent)
module.exports = { DropboxLoginScreen };

View File

@ -0,0 +1,74 @@
const { shim } = require('lib/shim');
const SyncTargetRegistry = require('lib/SyncTargetRegistry');
const { reg } = require('lib/registry.js');
const { _ } = require('lib/locale.js');
const Setting = require('lib/models/Setting');
class Shared {
constructor(comp, showInfoMessageBox, showErrorMessageBox) {
this.comp_ = comp;
this.dropboxApi_ = null;
this.comp_.state = {
loginUrl: '',
authCode: '',
checkingAuthToken: false,
};
this.loginUrl_click = () => {
if (!this.comp_.state.loginUrl) return;
shim.openUrl(this.comp_.state.loginUrl);
}
this.authCodeInput_change = (event) => {
this.comp_.setState({
authCode: typeof event === 'object' ? event.target.value : event
});
}
this.submit_click = async () => {
this.comp_.setState({ checkingAuthToken: true });
const api = await this.dropboxApi();
try {
const response = await api.execAuthToken(this.comp_.state.authCode);
Setting.setValue('sync.' + this.syncTargetId() + '.auth', response.access_token);
api.setAuthToken(response.access_token);
await showInfoMessageBox(_('The application has been authorised!'));
this.comp_.props.dispatch({ type: 'NAV_BACK' });
reg.scheduleSync();
} catch (error) {
console.error(error);
await showErrorMessageBox(_('Could not authorise application:\n\n%s\n\nPlease try again.', error.message));
} finally {
this.comp_.setState({ checkingAuthToken: false });
}
}
}
syncTargetId() {
return SyncTargetRegistry.nameToId('dropbox');
}
async dropboxApi() {
if (this.dropboxApi_) return this.dropboxApi_;
const syncTarget = reg.syncTarget(this.syncTargetId());
this.dropboxApi_ = await syncTarget.api();
return this.dropboxApi_;
}
async refreshUrl() {
const api = await this.dropboxApi();
this.comp_.setState({
loginUrl: api.loginUrl(),
});
}
}
module.exports = Shared;

View File

@ -67,6 +67,11 @@ dialogs.error = (parentComponent, message) => {
return parentComponent.dialogbox.alert(message);
}
dialogs.info = (parentComponent, message) => {
Keyboard.dismiss();
return parentComponent.dialogbox.alert(message);
}
dialogs.DialogBox = DialogBox
module.exports = { dialogs };

View File

@ -150,7 +150,7 @@ class FileApiDriverDropbox {
async put(path, content, options = null) {
// See https://github.com/facebook/react-native/issues/14445#issuecomment-352965210
if (typeof content === 'string') content = Buffer.from(content, 'utf8')
if (typeof content === 'string') content = shim.Buffer.from(content, 'utf8')
await this.api().exec('POST', 'files/upload', content, {
'Dropbox-API-Arg': JSON.stringify({

View File

@ -183,6 +183,11 @@ function shimInit() {
shim.Buffer = Buffer;
shim.openUrl = (url) => {
const { bridge } = require('electron').remote.require('./bridge');
bridge().openExternal(url)
}
}
module.exports = { shimInit };

View File

@ -6,6 +6,7 @@ const { generateSecureRandom } = require('react-native-securerandom');
const FsDriverRN = require('lib/fs-driver-rn.js').FsDriverRN;
const urlValidator = require('valid-url');
const { Buffer } = require('buffer');
const { Linking } = require('react-native');
function shimInit() {
shim.Geolocation = GeolocationReact;
@ -118,6 +119,10 @@ function shimInit() {
}
shim.Buffer = Buffer;
shim.openUrl = (url) => {
Linking.openURL(url);
}
}
module.exports = { shimInit };

View File

@ -130,5 +130,6 @@ shim.stringByteLength = function(string) { throw new Error('Not implemented'); }
shim.detectAndSetLocale = null;
shim.attachFileToNote = async (note, filePath) => {}
shim.Buffer = null;
shim.openUrl = () => { throw new Error('Not implemented'); }
module.exports = { shim };

View File

@ -36,6 +36,7 @@ const { WelcomeScreen } = require('lib/components/screens/welcome.js');
const { SearchScreen } = require('lib/components/screens/search.js');
const { OneDriveLoginScreen } = require('lib/components/screens/onedrive-login.js');
const { EncryptionConfigScreen } = require('lib/components/screens/encryption-config.js');
const { DropboxLoginScreen } = require('lib/components/screens/dropbox-login.js');
const Setting = require('lib/models/Setting.js');
const { MenuContext } = require('react-native-popup-menu');
const { SideMenu } = require('lib/components/side-menu.js');
@ -55,10 +56,12 @@ const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
const SyncTargetOneDriveDev = require('lib/SyncTargetOneDriveDev.js');
const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js');
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
const SyncTargetDropbox = require('lib/SyncTargetDropbox.js');
SyncTargetRegistry.addClass(SyncTargetOneDrive);
SyncTargetRegistry.addClass(SyncTargetOneDriveDev);
SyncTargetRegistry.addClass(SyncTargetNextcloud);
SyncTargetRegistry.addClass(SyncTargetWebDAV);
SyncTargetRegistry.addClass(SyncTargetDropbox);
// Disabled because not fully working
//SyncTargetRegistry.addClass(SyncTargetFilesystem);
@ -365,16 +368,6 @@ async function initialize(dispatch) {
await db.open({ name: 'joplin.sqlite' })
} else {
await db.open({ name: 'joplin-68.sqlite' })
//await db.open({ name: 'joplin-67.sqlite' })
// await db.exec('DELETE FROM notes');
// await db.exec('DELETE FROM folders');
// await db.exec('DELETE FROM tags');
// await db.exec('DELETE FROM note_tags');
// await db.exec('DELETE FROM resources');
// await db.exec('DELETE FROM deleted_items');
// await db.exec('UPDATE notes SET is_conflict = 1 where id like "546f%"');
}
reg.logger().info('Database is ready.');
@ -559,6 +552,7 @@ class AppComponent extends React.Component {
Note: { screen: NoteScreen },
Folder: { screen: FolderScreen },
OneDriveLogin: { screen: OneDriveLoginScreen },
DropboxLogin: { screen: DropboxLoginScreen },
EncryptionConfig: { screen: EncryptionConfigScreen },
Log: { screen: LogScreen },
Status: { screen: StatusScreen },