diff --git a/src/components/dev/apps/ApplicationsIndex.js b/src/components/dev/apps/ApplicationsIndex.js index e83dde1..8774048 100644 --- a/src/components/dev/apps/ApplicationsIndex.js +++ b/src/components/dev/apps/ApplicationsIndex.js @@ -6,7 +6,6 @@ import { FormattedMessage as Message } from 'react-intl'; import { Helmet } from 'react-helmet'; import { LinkButton } from 'components/ui/form'; import { COLOR_GREEN, COLOR_BLUE } from 'components/ui'; -import { restoreScroll } from 'components/ui/scroll/scroll'; import { ContactLink } from 'components/contact'; import styles from './applicationsIndex.scss'; @@ -14,127 +13,23 @@ import messages from './ApplicationsIndex.intl.json'; import cubeIcon from './icons/cube.svg'; import loadingCubeIcon from './icons/loading-cube.svg'; import toolsIcon from './icons/tools.svg'; -import ApplicationItem from './ApplicationItem'; +import ApplicationsList from './list'; type Props = { clientId?: ?string, displayForGuest: bool, applications: Array, isLoading: bool, - deleteApp: (string) => Promise, - resetApp: (string, bool) => Promise, + deleteApp: string => Promise, + resetApp: (string, bool) => Promise }; -type State = { - expandedApp: ?string, -}; - -export default class ApplicationsIndex extends Component { - state = { - expandedApp: null, - }; - - appsRefs: {[key: string]: ?HTMLDivElement} = {}; - - componentDidUpdate(prevProps: Props) { - const { applications, isLoading, clientId } = this.props; - - if (isLoading !== prevProps.isLoading && applications.length) { - if (clientId && applications.some((app) => app.clientId === clientId)) { - requestAnimationFrame(() => this.onTileClick(clientId)); - } - } - } - +export default class ApplicationsIndex extends Component { render() { - const { displayForGuest, applications, isLoading, resetApp, deleteApp } = this.props; - const { expandedApp } = this.state; - - let content: Node; - - if (displayForGuest) { - content = ( -
- -
-
- -
-
- -
-
- - -
- ); - } else if (isLoading) { - content = ( -
- -
- ); - } else if (applications.length > 0) { - content = ( -
-
-
- -
- -
-
- {applications.map((app: OauthAppResponse) => ( -
{this.appsRefs[app.clientId] = elem;}}> - -
- ))} -
-
- ); - } else { - content = ( -
- -
-
- -
-
- -
-
- - -
- ); - } - return (
- + {(pageTitle: string) => (

@@ -144,38 +39,120 @@ export default class ApplicationsIndex extends Component {
- - - - ), - }} /> + + + + ) + }} + />
- - - - ), - }} /> + + + + ) + }} + />
- {content} + {this.getContent()}

); } - onTileClick = (clientId: string) => { - const expandedApp = this.state.expandedApp === clientId ? null : clientId; + getContent() { + const { + displayForGuest, + applications, + isLoading, + resetApp, + deleteApp, + clientId + } = this.props; - this.setState({expandedApp}, () => { - if (expandedApp !== null) { - // TODO: @SleepWalker: мб у тебя есть идея, как это сделать более правильно и менее дёргано? - setTimeout(() => restoreScroll(this.appsRefs[clientId]), 150); - } - }); - }; + if (displayForGuest) { + return ; + } else if (isLoading) { + return ; + } else if (applications.length > 0) { + return ( + + ); + } + + return ; + } +} + +function Loader() { + return ( +
+ +
+ ); +} + +function Guest() { + return ( +
+ +
+
+ +
+
+ +
+
+ + +
+ ); +} + +function NoApps() { + return ( +
+ +
+
+ +
+
+ +
+
+ + +
+ ); } diff --git a/src/components/dev/apps/ApplicationItem.js b/src/components/dev/apps/list/ApplicationItem.js similarity index 63% rename from src/components/dev/apps/ApplicationItem.js rename to src/components/dev/apps/list/ApplicationItem.js index 76a2061..c238cab 100644 --- a/src/components/dev/apps/ApplicationItem.js +++ b/src/components/dev/apps/list/ApplicationItem.js @@ -9,44 +9,162 @@ import { SKIN_LIGHT, COLOR_BLACK, COLOR_RED } from 'components/ui'; import { Input, Button } from 'components/ui/form'; import Collapse from 'components/ui/collapse'; -import styles from './applicationsIndex.scss'; -import messages from './ApplicationsIndex.intl.json'; +import styles from '../applicationsIndex.scss'; +import messages from '../ApplicationsIndex.intl.json'; const ACTION_REVOKE_TOKENS = 'revoke-tokens'; const ACTION_RESET_SECRET = 'reset-secret'; const ACTION_DELETE = 'delete'; +const actionButtons = [ + { + type: ACTION_REVOKE_TOKENS, + label: messages.revokeAllTokens + }, + { + type: ACTION_RESET_SECRET, + label: messages.resetClientSecret + }, + { + type: ACTION_DELETE, + label: messages.delete + } +]; -export default class ApplicationItem extends Component<{ - application: OauthAppResponse, - expand: bool, - onTileClick: (string) => void, - onResetSubmit: (string, bool) => Promise<*>, - onDeleteSubmit: (string) => Promise<*>, -}, { - selectedAction: ?string, - isActionPerforming: bool, - detailsHeight: number, -}> { +export default class ApplicationItem extends Component< + { + application: OauthAppResponse, + expand: bool, + onTileClick: string => void, + onResetSubmit: (string, bool) => Promise<*>, + onDeleteSubmit: string => Promise<*> + }, + { + selectedAction: ?string, + isActionPerforming: bool, + detailsHeight: number + } +> { state = { selectedAction: null, isActionPerforming: false, - detailsHeight: 0, + detailsHeight: 0 }; render() { const { application: app, expand } = this.props; - const { selectedAction, isActionPerforming } = this.state; + const { selectedAction } = this.state; - let actionContent: Node; + return ( +
+
+
+
{app.name}
+
+ Client ID: {app.clientId} + {typeof app.countUsers !== 'undefined' && ( + + {' | '} + + + )} +
+
+
+
+
+
+ + +
+
+ + + ) + }} + /> + + +
+ +
+ +
+ +
+ +
+ +
+ {actionButtons.map(({ type, label }) => ( +
+ + {this.getActionContent()} +
+
+
+ ); + } + + getActionContent() { + const { selectedAction, isActionPerforming } = this.state; switch (selectedAction) { case ACTION_REVOKE_TOKENS: case ACTION_RESET_SECRET: - actionContent = ( + return (
- {' '} - + {' '} +
@@ -73,13 +194,17 @@ export default class ApplicationItem extends Component<{
); - break; + case ACTION_DELETE: - actionContent = ( + return (
- {' '} - + {' '} +
); - break; + default: - actionContent = null; - break; + return null; } - - return ( -
-
-
-
- {app.name} -
-
- Client ID: {app.clientId} - {typeof app.countUsers !== 'undefined' && ( - - {' | '} - - - )} -
-
-
-
-
-
- -
-
- - , - }} /> - - -
- -
- -
- -
- -
- -
-
- - {actionContent} -
-
-
- ); } onTileToggle = () => { @@ -211,33 +246,33 @@ export default class ApplicationItem extends Component<{ onCollapseRest = () => { if (!this.props.expand && this.state.selectedAction) { this.setState({ - selectedAction: null, + selectedAction: null }); } }; onActionButtonClick = (type: ?string) => () => { this.setState({ - selectedAction: type === this.state.selectedAction ? null : type, + selectedAction: type === this.state.selectedAction ? null : type }); }; onResetSubmit = (resetClientSecret: bool) => async () => { const { onResetSubmit, application } = this.props; this.setState({ - isActionPerforming: true, + isActionPerforming: true }); await onResetSubmit(application.clientId, resetClientSecret); this.setState({ isActionPerforming: false, - selectedAction: null, + selectedAction: null }); }; onSubmitDelete = () => { const { onDeleteSubmit, application } = this.props; this.setState({ - isActionPerforming: true, + isActionPerforming: true }); onDeleteSubmit(application.clientId); }; diff --git a/src/components/dev/apps/list/ApplicationsList.js b/src/components/dev/apps/list/ApplicationsList.js new file mode 100644 index 0000000..7a2de2d --- /dev/null +++ b/src/components/dev/apps/list/ApplicationsList.js @@ -0,0 +1,102 @@ +// @flow +import type { OauthAppResponse } from 'services/api/oauth'; +import React from 'react'; +import { restoreScroll } from 'components/ui/scroll/scroll'; +import { FormattedMessage as Message } from 'react-intl'; +import { LinkButton } from 'components/ui/form'; +import { COLOR_GREEN } from 'components/ui'; + +import messages from '../ApplicationsIndex.intl.json'; +import styles from '../applicationsIndex.scss'; +import ApplicationItem from './ApplicationItem'; + +type Props = { + applications: Array, + deleteApp: string => Promise, + resetApp: (string, bool) => Promise, + clientId: ?string +}; + +type State = { + expandedApp: ?string +}; + +export default class ApplicationsList extends React.Component { + state = { + expandedApp: null + }; + + appsRefs: { [key: string]: ?HTMLDivElement } = {}; + + componentDidMount() { + this.checkForActiveApp(); + } + + componentDidUpdate() { + this.checkForActiveApp(); + } + + render() { + const { applications, resetApp, deleteApp } = this.props; + const { expandedApp } = this.state; + + return ( +
+
+
+ +
+ +
+
+ {applications.map((app) => ( +
{ + this.appsRefs[app.clientId] = elem; + }} + > + +
+ ))} +
+
+ ); + } + + checkForActiveApp() { + const { applications, clientId } = this.props; + const { expandedApp } = this.state; + + if ( + clientId + && expandedApp !== clientId + && applications.some((app) => app.clientId === clientId) + ) { + requestAnimationFrame(() => this.onTileClick(clientId)); + } + } + + onTileClick = (clientId: string) => { + const expandedApp + = this.state.expandedApp === clientId ? null : clientId; + + this.setState({ expandedApp }, () => { + if (expandedApp !== null) { + // TODO: @SleepWalker: мб у тебя есть идея, как это сделать более правильно и менее дёргано? + setTimeout(() => restoreScroll(this.appsRefs[clientId]), 150); + } + }); + }; +} diff --git a/src/components/dev/apps/list/index.js b/src/components/dev/apps/list/index.js new file mode 100644 index 0000000..1a047aa --- /dev/null +++ b/src/components/dev/apps/list/index.js @@ -0,0 +1,3 @@ +// @flow + +export { default } from './ApplicationsList';