fix(dropbox): Address code review comments.

This commit is contained in:
hristoterezov 2018-08-15 14:51:51 -05:00 committed by yanas
parent 62544188bd
commit 2704b2f822
13 changed files with 138 additions and 144 deletions

View File

@ -4,7 +4,6 @@
.recording-dialog { .recording-dialog {
.authorization-panel { .authorization-panel {
align-items: center;
border-bottom: 2px solid rgba(0, 0, 0, 0.3); border-bottom: 2px solid rgba(0, 0, 0, 0.3);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -12,18 +11,23 @@
padding-bottom: 10px; padding-bottom: 10px;
.dropbox-sign-in { .dropbox-sign-in {
background-color: #4285f4; align-items: center;
border: 1px solid #4285f4;
background-color: white;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
display: inline-flex; display: inline-flex;
padding: 1px; padding: 10px;
font-size: 18px;
font-weight: 600;
margin: 10px 0px; margin: 10px 0px;
color: #4285f4;
.dropbox-logo { .dropbox-logo {
background-color: white; background-color: white;
border-radius: 2px; border-radius: 2px;
display: inline-block; display: inline-block;
padding: 8px; padding-right: 5px;
height: 18px; height: 18px;
} }
} }

View File

@ -456,10 +456,11 @@
"on": "Recording", "on": "Recording",
"pending": "Preparing to record the meeting...", "pending": "Preparing to record the meeting...",
"rec": "REC", "rec": "REC",
"authDropboxText": "To start the recording you need to first authorize our Dropbox recording app. After the recording is finished the file will be uploaded to your Dropbox account.", "authDropboxText": "Upload your recording to Dropbox.",
"authDropboxCompletedText": "Our Dropbox app has been authorized successfully. You should see the recorded file in your Dropbox account shortly after the recording has finished.", "authDropboxCompletedText": "Your recording file will appear in your Dropbox shortly after the recording has finished.",
"serviceName": "Recording service", "serviceName": "Recording service",
"signOut": "Sign Out", "signOut": "Sign Out",
"signIn": "sign in",
"loggedIn": "Logged in as __userName__", "loggedIn": "Logged in as __userName__",
"availableSpace": "Available space: __spaceLeft__ MB (approximately __duration__ minutes of recording)", "availableSpace": "Available space: __spaceLeft__ MB (approximately __duration__ minutes of recording)",
"startRecordingBody": "Are you sure you would like to start recording?", "startRecordingBody": "Are you sure you would like to start recording?",

View File

@ -1,29 +0,0 @@
// @flow
import { getJitsiMeetGlobalNS } from '../util';
/**
* Executes the oauth flow.
*
* @param {string} authUrl - The URL to oauth service.
* @returns {Promise<string>} - The URL with the authorization details.
*/
export function authorize(authUrl: string): Promise<string> {
const windowName = `oauth${Date.now()}`;
const gloabalNS = getJitsiMeetGlobalNS();
gloabalNS.oauthCallbacks = gloabalNS.oauthCallbacks || {};
return new Promise(resolve => {
const popup = window.open(authUrl, windowName);
gloabalNS.oauthCallbacks[windowName] = () => {
const returnURL = popup.location.href;
popup.close();
delete gloabalNS.oauthCallbacks.windowName;
resolve(returnURL);
};
});
}

View File

@ -1,38 +0,0 @@
// @flow
import { ReducerRegistry } from '../redux';
import { PersistenceRegistry } from '../storage';
import { UPDATE_DROPBOX_TOKEN } from './actionTypes';
/**
* The default state.
*/
const DEFAULT_STATE = {
dropbox: {}
};
/**
* The redux subtree of this feature.
*/
const STORE_NAME = 'features/base/oauth';
/**
* Sets up the persistence of the feature {@code oauth}.
*/
PersistenceRegistry.register(STORE_NAME);
ReducerRegistry.register('features/base/oauth',
(state = DEFAULT_STATE, action) => {
switch (action.type) {
case UPDATE_DROPBOX_TOKEN:
return {
...state,
dropbox: {
token: action.token
}
};
default:
return state;
}
});

View File

@ -2,12 +2,40 @@
import { Dropbox } from 'dropbox'; import { Dropbox } from 'dropbox';
import { getLocationContextRoot, parseStandardURIString } from '../util'; import {
import { parseURLParams } from '../config'; getJitsiMeetGlobalNS,
getLocationContextRoot,
parseStandardURIString
} from '../base/util';
import { parseURLParams } from '../base/config';
import { authorize } from './functions';
import { UPDATE_DROPBOX_TOKEN } from './actionTypes'; import { UPDATE_DROPBOX_TOKEN } from './actionTypes';
/**
* Executes the oauth flow.
*
* @param {string} authUrl - The URL to oauth service.
* @returns {Promise<string>} - The URL with the authorization details.
*/
function authorize(authUrl: string): Promise<string> {
const windowName = `oauth${Date.now()}`;
const gloabalNS = getJitsiMeetGlobalNS();
gloabalNS.oauthCallbacks = gloabalNS.oauthCallbacks || {};
return new Promise(resolve => {
const popup = window.open(authUrl, windowName);
gloabalNS.oauthCallbacks[windowName] = () => {
const returnURL = popup.location.href;
popup.close();
delete gloabalNS.oauthCallbacks.windowName;
resolve(returnURL);
};
});
}
/** /**
* Action to authorize the Jitsi Recording app in dropbox. * Action to authorize the Jitsi Recording app in dropbox.
* *

View File

@ -0,0 +1,39 @@
// @flow
import { Dropbox } from 'dropbox';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Fetches information about the user's dropbox account.
*
* @param {string} token - The dropbox access token.
* @param {string} clientId - The Jitsi Recorder dropbox app ID.
* @returns {Promise<Object|undefined>}
*/
export function getDropboxData(
token: string,
clientId: string
): Promise<?Object> {
const dropboxAPI = new Dropbox({
accessToken: token,
clientId
});
return Promise.all(
[ dropboxAPI.usersGetCurrentAccount(), dropboxAPI.usersGetSpaceUsage() ]
).then(([ account, space ]) => {
const { allocation, used } = space;
const { allocated } = allocation;
return {
userName: account.name.display_name,
spaceLeft: Math.floor((allocated - used) / 1048576)// 1MiB=1048576B
};
}, error => {
logger.error(error);
return undefined;
});
}

View File

@ -1,3 +1,4 @@
export * from './actions'; export * from './actions';
export * from './functions';
import './reducer'; import './reducer';

View File

@ -0,0 +1,28 @@
// @flow
import { ReducerRegistry } from '../base/redux';
import { PersistenceRegistry } from '../base/storage';
import { UPDATE_DROPBOX_TOKEN } from './actionTypes';
/**
* The redux subtree of this feature.
*/
const STORE_NAME = 'features/dropbox';
/**
* Sets up the persistence of the feature {@code dropbox}.
*/
PersistenceRegistry.register(STORE_NAME);
ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
switch (action.type) {
case UPDATE_DROPBOX_TOKEN:
return {
...state,
token: action.token
};
default:
return state;
}
});

View File

@ -11,7 +11,7 @@ import { Dialog } from '../../../base/dialog';
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
import StartRecordingDialogContent from './StartRecordingDialogContent'; import StartRecordingDialogContent from './StartRecordingDialogContent';
import { getDropboxData } from '../../functions'; import { getDropboxData } from '../../../dropbox';
type Props = { type Props = {
@ -41,7 +41,6 @@ type Props = {
t: Function t: Function
} }
type State = { type State = {
/** /**
@ -54,15 +53,15 @@ type State = {
*/ */
isValidating: boolean, isValidating: boolean,
/**
* The display name of the user's Dropbox account.
*/
userName: ?string,
/** /**
* Number of MiB of available space in user's Dropbox account. * Number of MiB of available space in user's Dropbox account.
*/ */
spaceLeft: ?number spaceLeft: ?number,
/**
* The display name of the user's Dropbox account.
*/
userName: ?string
}; };
/** /**
@ -217,14 +216,16 @@ class StartRecordingDialog extends Component<Props, State> {
* @param {Object} state - The Redux state. * @param {Object} state - The Redux state.
* @private * @private
* @returns {{ * @returns {{
* _conference: JitsiConference * _clientId: string,
* _conference: JitsiConference,
* _token: string
* }} * }}
*/ */
function mapStateToProps(state: Object) { function mapStateToProps(state: Object) {
return { return {
_clientId: state['features/base/config'].dropbox.clientId,
_conference: state['features/base/conference'].conference, _conference: state['features/base/conference'].conference,
_token: state['features/base/oauth'].dropbox.token, _token: state['features/dropbox'].token
_clientId: state['features/base/config'].dropbox.clientId
}; };
} }

View File

@ -9,7 +9,7 @@ import {
sendAnalytics sendAnalytics
} from '../../../analytics'; } from '../../../analytics';
import { translate } from '../../../base/i18n'; import { translate } from '../../../base/i18n';
import { authorizeDropbox, updateDropboxToken } from '../../../base/oauth'; import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
type Props = { type Props = {
@ -111,17 +111,21 @@ class StartRecordingDialogContent extends Component<Props> {
* @returns {React$Component} * @returns {React$Component}
*/ */
_renderSignIn() { _renderSignIn() {
const { t } = this.props;
return ( return (
<div> <div>
<div>{ this.props.t('recording.authDropboxText') }</div> <div>{ t('recording.authDropboxText') }</div>
<div <div
className = 'dropbox-sign-in' className = 'dropbox-sign-in'
onClick = { this._onSignInClick }> onClick = { this._onSignInClick }>
<img <img
className = 'dropbox-logo' className = 'dropbox-logo'
src = 'images/dropboxLogo.svg' /> src = 'images/dropboxLogo.svg' />
<span>{ t('recording.signIn') }</span>
</div> </div>
</div>); </div>
);
} }
/** /**
@ -135,7 +139,7 @@ class StartRecordingDialogContent extends Component<Props> {
return ( return (
<div> <div>
<div>{ t('recording.authDropboxCompletedText') }</div> <div>{ t('recording.authDropboxCompletedText') }</div>
<div className = 'logged-in-pannel'> <div className = 'logged-in-panel'>
<div> <div>
{ t('recording.loggedIn', { userName }) }&nbsp;(&nbsp; { t('recording.loggedIn', { userName }) }&nbsp;(&nbsp;
<a onClick = { this._onSignOutClick }> <a onClick = { this._onSignOutClick }>
@ -154,7 +158,8 @@ class StartRecordingDialogContent extends Component<Props> {
} }
</div> </div>
</div> </div>
</div>); </div>
);
} }
_onSignInClick: () => {}; _onSignInClick: () => {};

View File

@ -1,11 +1,7 @@
// @flow // @flow
import { Dropbox } from 'dropbox';
import { JitsiRecordingConstants } from '../base/lib-jitsi-meet'; import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/** /**
* Searches in the passed in redux state for an active recording session of the * Searches in the passed in redux state for an active recording session of the
* passed in mode. * passed in mode.
@ -35,37 +31,3 @@ export function getSessionById(state: Object, id: string) {
return state['features/recording'].sessionDatas.find( return state['features/recording'].sessionDatas.find(
sessionData => sessionData.id === id); sessionData => sessionData.id === id);
} }
/**
* Fetches information about the user's dropbox account.
*
* @param {string} token - The dropbox access token.
* @param {string} clientId - The Jitsi Recorder dropbox app ID.
* @returns {Promise<Object|undefined>}
*/
export function getDropboxData(
token: string,
clientId: string
): Promise<?Object> {
const dropboxAPI = new Dropbox({
accessToken: token,
clientId
});
return Promise.all(
[ dropboxAPI.usersGetCurrentAccount(), dropboxAPI.usersGetSpaceUsage() ]
).then(([ account, space ]) => {
const { allocation, used } = space;
const { allocated } = allocation;
return {
userName: account.name.display_name,
spaceLeft: Math.floor((allocated - used) / 1048576)// 1MiB=1048576B
};
}, error => {
logger.error(error);
return undefined;
});
}

View File

@ -6,30 +6,22 @@
<!--#include virtual="/base.html" --> <!--#include virtual="/base.html" -->
<!--#include virtual="/title.html" --> <!--#include virtual="/title.html" -->
<script> <script>
function getParentWindowCallback() { (function() {
var windowName = window.name; var windowName = window.name;
var parentWindow = window.opener; var parentWindow = window.opener;
if (parentWindow if (parentWindow
&& parentWindow.JitsiMeetJS && parentWindow.JitsiMeetJS
&& parentWindow.JitsiMeetJS.app) { && parentWindow.JitsiMeetJS.app) {
var globalNS = parentWindow.JitsiMeetJS.app; var globalNS = parentWindow.JitsiMeetJS.app;
if( globalNS.oauthCallbacks if (globalNS.oauthCallbacks
&& typeof globalNS.oauthCallbacks[windowName] && typeof globalNS.oauthCallbacks[windowName]
=== 'function') { === 'function') {
return globalNS.oauthCallbacks[windowName]; globalNS.oauthCallbacks[windowName]();
} }
} }
})();
return undefined;
}
var callback = getParentWindowCallback();
if (typeof callback === 'function') {
callback();
} else {
alert('Something went wrong!');
}
</script> </script>
</head> </head>
<body /> <body>
</body>
</html> </html>