mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Added navigator and handle OneDrive auth
This commit is contained in:
parent
d763b13e44
commit
8dd41dca96
@ -14,50 +14,96 @@ const { sprintf } = require('sprintf-js');
|
||||
const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
|
||||
const { defaultState } = require('lib/reducer.js');
|
||||
|
||||
const appDefaultState = Object.assign({}, defaultState, {
|
||||
route: {
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Main',
|
||||
params: {},
|
||||
},
|
||||
navHistory: [],
|
||||
});
|
||||
|
||||
class Application extends BaseApplication {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
hasGui() {
|
||||
return true;
|
||||
}
|
||||
|
||||
reducer(state = appDefaultState, action) {
|
||||
let newState = state;
|
||||
|
||||
try {
|
||||
switch (action.type) {
|
||||
|
||||
case 'NAV_BACK':
|
||||
case 'NAV_GO':
|
||||
|
||||
const goingBack = action.type === 'NAV_BACK';
|
||||
|
||||
if (goingBack && !state.navHistory.length) break;
|
||||
|
||||
const currentRoute = state.route;
|
||||
|
||||
newState = Object.assign({}, state);
|
||||
let newNavHistory = state.navHistory.slice();
|
||||
|
||||
if (goingBack) {
|
||||
let newAction = null;
|
||||
while (newNavHistory.length) {
|
||||
newAction = newNavHistory.pop();
|
||||
if (newAction.routeName !== state.route.routeName) break;
|
||||
}
|
||||
|
||||
if (!newAction) break;
|
||||
|
||||
action = newAction;
|
||||
}
|
||||
|
||||
if (!goingBack) newNavHistory.push(currentRoute);
|
||||
newState.navHistory = newNavHistory
|
||||
newState.route = action;
|
||||
break;
|
||||
|
||||
case 'WINDOW_CONTENT_SIZE_SET':
|
||||
|
||||
newState = Object.assign({}, state);
|
||||
newState.windowContentSize = action.size;
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
|
||||
throw error;
|
||||
}
|
||||
|
||||
return super.reducer(newState, action);
|
||||
}
|
||||
|
||||
async start(argv) {
|
||||
argv = await super.start(argv);
|
||||
|
||||
this.initRedux();
|
||||
|
||||
//this.gui_ = new ElectronAppWrapper(this.electronApp_, this, this.store());
|
||||
// Since the settings need to be loaded before the store is created, it will never
|
||||
// receive the SETTINGS_UPDATE_ALL even, which mean state.settings will not be
|
||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||
Setting.dispatchUpdateAll();
|
||||
|
||||
try {
|
||||
// this.gui_.setLogger(this.logger());
|
||||
// await this.gui().start();
|
||||
await FoldersScreenUtils.refreshFolders();
|
||||
|
||||
// Since the settings need to be loaded before the store is created, it will never
|
||||
// receive the SETTINGS_UPDATE_ALL even, which mean state.settings will not be
|
||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||
Setting.dispatchUpdateAll();
|
||||
const tags = await Tag.allWithNotes();
|
||||
|
||||
await FoldersScreenUtils.refreshFolders();
|
||||
this.dispatch({
|
||||
type: 'TAGS_UPDATE_ALL',
|
||||
tags: tags,
|
||||
});
|
||||
|
||||
const tags = await Tag.allWithNotes();
|
||||
|
||||
this.dispatch({
|
||||
type: 'TAGS_UPDATE_ALL',
|
||||
tags: tags,
|
||||
});
|
||||
|
||||
this.store().dispatch({
|
||||
type: 'FOLDERS_SELECT',
|
||||
id: Setting.value('activeFolderId'),
|
||||
});
|
||||
} catch (error) {
|
||||
//await this.gui_.exit();
|
||||
throw error;
|
||||
}
|
||||
this.store().dispatch({
|
||||
type: 'FOLDERS_SELECT',
|
||||
id: Setting.value('activeFolderId'),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@ -65,7 +111,6 @@ class Application extends BaseApplication {
|
||||
let application_ = null;
|
||||
|
||||
function app() {
|
||||
//if (!application_) throw new Error('Application has not been initialized');
|
||||
if (!application_) application_ = new Application();
|
||||
return application_;
|
||||
}
|
||||
|
@ -18,6 +18,11 @@ class Bridge {
|
||||
return { width: s[0], height: s[1] };
|
||||
}
|
||||
|
||||
showMessageBox(options) {
|
||||
const {dialog} = require('electron');
|
||||
return dialog.showMessageBox(options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let bridge_ = null;
|
||||
|
50
ElectronClient/app/gui/MainScreen.jsx
Normal file
50
ElectronClient/app/gui/MainScreen.jsx
Normal file
@ -0,0 +1,50 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { SideBar } = require('./SideBar.min.js');
|
||||
const { NoteList } = require('./NoteList.min.js');
|
||||
const { NoteText } = require('./NoteText.min.js');
|
||||
|
||||
class MainScreenComponent extends React.Component {
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
|
||||
const noteListStyle = {
|
||||
width: Math.floor(style.width / 3),
|
||||
height: style.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const noteTextStyle = {
|
||||
width: noteListStyle.width,
|
||||
height: style.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const sideBarStyle = {
|
||||
width: style.width - (noteTextStyle.width + noteListStyle.width),
|
||||
height: style.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<SideBar style={sideBarStyle}></SideBar>
|
||||
<NoteList itemHeight={40} style={noteListStyle}></NoteList>
|
||||
<NoteText style={noteTextStyle}></NoteText>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const MainScreen = connect(mapStateToProps)(MainScreenComponent);
|
||||
|
||||
module.exports = { MainScreen };
|
34
ElectronClient/app/gui/Navigator.jsx
Normal file
34
ElectronClient/app/gui/Navigator.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { connect } = require('react-redux');
|
||||
|
||||
class NavigatorComponent extends Component {
|
||||
|
||||
render() {
|
||||
if (!this.props.route) throw new Error('Route must not be null');
|
||||
|
||||
const route = this.props.route;
|
||||
const Screen = this.props.screens[route.routeName].screen;
|
||||
|
||||
const screenStyle = {
|
||||
width: this.props.style.width,
|
||||
height: this.props.style.height,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={this.props.style}>
|
||||
<Screen style={screenStyle}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Navigator = connect(
|
||||
(state) => {
|
||||
return {
|
||||
route: state.route,
|
||||
};
|
||||
}
|
||||
)(NavigatorComponent)
|
||||
|
||||
module.exports = { Navigator };
|
103
ElectronClient/app/gui/OneDriveAuthScreen.jsx
Normal file
103
ElectronClient/app/gui/OneDriveAuthScreen.jsx
Normal file
@ -0,0 +1,103 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const shared = require('lib/components/shared/side-menu-shared.js');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
|
||||
class OneDriveAuthScreenComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.webview_ = null;
|
||||
this.authCode_ = null;
|
||||
}
|
||||
|
||||
back_click() {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_BACK',
|
||||
});
|
||||
}
|
||||
|
||||
refresh_click() {
|
||||
if (!this.webview_) return;
|
||||
this.webview_.src = this.startUrl();
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({
|
||||
webviewUrl: this.startUrl(),
|
||||
webviewReady: false,
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.webview_.addEventListener('dom-ready', this.webview_domReady.bind(this));
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.webview_.addEventListener('dom-ready', this.webview_domReady.bind(this));
|
||||
}
|
||||
|
||||
webview_domReady() {
|
||||
this.setState({
|
||||
webviewReady: true,
|
||||
});
|
||||
|
||||
this.webview_.addEventListener('did-navigate', async (event) => {
|
||||
const url = event.url;
|
||||
|
||||
if (this.authCode_) return;
|
||||
if (url.indexOf(this.redirectUrl() + '?code=') !== 0) return;
|
||||
|
||||
let code = url.split('?code=');
|
||||
this.authCode_ = code[1];
|
||||
|
||||
try {
|
||||
await reg.oneDriveApi().execTokenRequest(this.authCode_, this.redirectUrl(), true);
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
reg.scheduleSync(0);
|
||||
} catch (error) {
|
||||
bridge().showMessageBox({
|
||||
type: 'error',
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
this.authCode_ = null;
|
||||
});
|
||||
}
|
||||
|
||||
startUrl() {
|
||||
return reg.oneDriveApi().authCodeUrl(this.redirectUrl());
|
||||
}
|
||||
|
||||
redirectUrl() {
|
||||
return reg.oneDriveApi().nativeClientRedirectUrl();
|
||||
}
|
||||
|
||||
render() {
|
||||
const webviewStyle = {
|
||||
width: this.props.style.width,
|
||||
height: this.props.style.height,
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a href="#" onClick={() => {this.back_click()}}>BACK</a>
|
||||
<a href="#" onClick={() => {this.refresh_click()}}>REFRESH</a>
|
||||
<webview src={this.startUrl()} style={webviewStyle} nodeintegration="1" ref={elem => this.webview_ = elem} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const OneDriveAuthScreen = connect(mapStateToProps)(OneDriveAuthScreenComponent);
|
||||
|
||||
module.exports = { OneDriveAuthScreen };
|
@ -3,9 +3,9 @@ const { render } = require('react-dom');
|
||||
const { createStore } = require('redux');
|
||||
const { connect, Provider } = require('react-redux');
|
||||
|
||||
const { SideBar } = require('./SideBar.min.js');
|
||||
const { NoteList } = require('./NoteList.min.js');
|
||||
const { NoteText } = require('./NoteText.min.js');
|
||||
const { MainScreen } = require('./MainScreen.min.js');
|
||||
const { OneDriveAuthScreen } = require('./OneDriveAuthScreen.min.js');
|
||||
const { Navigator } = require('./Navigator.min.js');
|
||||
|
||||
const { app } = require('../app');
|
||||
|
||||
@ -25,7 +25,7 @@ async function initialize(dispatch) {
|
||||
});
|
||||
}
|
||||
|
||||
class ReactRootComponent extends React.Component {
|
||||
class RootComponent extends React.Component {
|
||||
|
||||
async componentDidMount() {
|
||||
if (this.props.appState == 'starting') {
|
||||
@ -44,38 +44,18 @@ class ReactRootComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = {
|
||||
const navigatorStyle = {
|
||||
width: this.props.size.width,
|
||||
height: this.props.size.height,
|
||||
};
|
||||
|
||||
const noteListStyle = {
|
||||
width: Math.floor(this.props.size.width / 3),
|
||||
height: this.props.size.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const noteTextStyle = {
|
||||
width: noteListStyle.width,
|
||||
height: this.props.size.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const sideBarStyle = {
|
||||
width: this.props.size.width - (noteTextStyle.width + noteListStyle.width),
|
||||
height: this.props.size.height,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
const screens = {
|
||||
Main: { screen: MainScreen },
|
||||
OneDriveAuth: { screen: OneDriveAuthScreen },
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<SideBar style={sideBarStyle}></SideBar>
|
||||
<NoteList itemHeight={40} style={noteListStyle}></NoteList>
|
||||
<NoteText style={noteTextStyle}></NoteText>
|
||||
</div>
|
||||
<Navigator style={navigatorStyle} screens={screens} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -88,13 +68,13 @@ const mapStateToProps = (state) => {
|
||||
};
|
||||
};
|
||||
|
||||
const ReactRoot = connect(mapStateToProps)(ReactRootComponent);
|
||||
const Root = connect(mapStateToProps)(RootComponent);
|
||||
|
||||
const store = app().store();
|
||||
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<ReactRoot />
|
||||
<Root />
|
||||
</Provider>,
|
||||
document.getElementById('react-root')
|
||||
)
|
@ -19,6 +19,13 @@ class SideBarComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
sync_click() {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'OneDriveAuth',
|
||||
});
|
||||
}
|
||||
|
||||
folderItem(folder, selected) {
|
||||
let classes = [];
|
||||
if (selected) classes.push('selected');
|
||||
@ -36,7 +43,7 @@ class SideBarComponent extends React.Component {
|
||||
}
|
||||
|
||||
synchronizeButton(label) {
|
||||
return <div key="sync_button">{label}</div>
|
||||
return <a href="#" key="sync_button" onClick={() => {this.sync_click()}}>{label}</a>
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -231,8 +231,12 @@ class BaseApplication {
|
||||
if (this.store()) return this.store().dispatch(action);
|
||||
}
|
||||
|
||||
reducer(state = defaultState, action) {
|
||||
return reducer(state, action);
|
||||
}
|
||||
|
||||
initRedux() {
|
||||
this.store_ = createStore(reducer, applyMiddleware(this.generalMiddleware()));
|
||||
this.store_ = createStore(this.reducer, applyMiddleware(this.generalMiddleware()));
|
||||
BaseModel.dispatch = this.store().dispatch;
|
||||
FoldersScreenUtils.dispatch = this.store().dispatch;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ class OneDriveLoginScreenComponent extends BaseScreenComponent {
|
||||
}
|
||||
|
||||
redirectUrl() {
|
||||
return 'https://login.microsoftonline.com/common/oauth2/nativeclient';
|
||||
return reg.oneDriveApi().nativeClientRedirectUrl();
|
||||
}
|
||||
|
||||
async webview_load(noIdeaWhatThisIs) {
|
||||
|
@ -47,6 +47,10 @@ class OneDriveApi {
|
||||
return 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
|
||||
}
|
||||
|
||||
nativeClientRedirectUrl() {
|
||||
return 'https://login.microsoftonline.com/common/oauth2/nativeclient';
|
||||
}
|
||||
|
||||
auth() {
|
||||
return this.auth_;
|
||||
}
|
||||
|
@ -23,12 +23,6 @@ const defaultState = {
|
||||
searchQuery: '',
|
||||
settings: {},
|
||||
appState: 'starting',
|
||||
sideMenuOpenPercent: 0,
|
||||
route: {
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Welcome',
|
||||
params: {},
|
||||
},
|
||||
windowContentSize: { width: 0, height: 0 },
|
||||
};
|
||||
|
||||
@ -107,7 +101,6 @@ function defaultNotesParentType(state, exclusion) {
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
let newState = state;
|
||||
let historyGoingBack = false;
|
||||
|
||||
try {
|
||||
switch (action.type) {
|
||||
@ -305,12 +298,6 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.appState = action.state;
|
||||
break;
|
||||
|
||||
case 'WINDOW_CONTENT_SIZE_SET':
|
||||
|
||||
newState = Object.assign({}, state);
|
||||
newState.windowContentSize = action.size;
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
|
||||
|
@ -74,7 +74,16 @@ function historyCanGoBackTo(route) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const appReducer = (state = defaultState, action) => {
|
||||
const appDefaultState = Object.assign({}, defaultState, {
|
||||
sideMenuOpenPercent: 0,
|
||||
route: {
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Welcome',
|
||||
params: {},
|
||||
},
|
||||
});
|
||||
|
||||
const appReducer = (state = appDefaultState, action) => {
|
||||
let newState = state;
|
||||
let historyGoingBack = false;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user