summaryrefslogtreecommitdiffstats
path: root/webapp/components/activity_log_modal.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/components/activity_log_modal.jsx')
-rw-r--r--webapp/components/activity_log_modal.jsx299
1 files changed, 299 insertions, 0 deletions
diff --git a/webapp/components/activity_log_modal.jsx b/webapp/components/activity_log_modal.jsx
new file mode 100644
index 000000000..9a4ff3ef2
--- /dev/null
+++ b/webapp/components/activity_log_modal.jsx
@@ -0,0 +1,299 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import $ from 'jquery';
+import ReactDOM from 'react-dom';
+import UserStore from 'stores/user_store.jsx';
+import * as Client from 'utils/client.jsx';
+import * as AsyncClient from 'utils/async_client.jsx';
+import {Modal} from 'react-bootstrap';
+import LoadingScreen from './loading_screen.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+import {FormattedMessage, FormattedTime, FormattedDate} from 'react-intl';
+
+import React from 'react';
+
+export default class ActivityLogModal extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.submitRevoke = this.submitRevoke.bind(this);
+ this.onListenerChange = this.onListenerChange.bind(this);
+ this.handleMoreInfo = this.handleMoreInfo.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onShow = this.onShow.bind(this);
+
+ let state = this.getStateFromStores();
+ state.moreInfo = [];
+
+ this.state = state;
+ }
+ getStateFromStores() {
+ return {
+ sessions: UserStore.getSessions(),
+ serverError: null,
+ clientError: null
+ };
+ }
+ submitRevoke(altId, e) {
+ e.preventDefault();
+ var modalContent = $(e.target).closest('.modal-content');
+ modalContent.addClass('animation--highlight');
+ setTimeout(() => {
+ modalContent.removeClass('animation--highlight');
+ }, 1500);
+ Client.revokeSession(altId,
+ function handleRevokeSuccess() {
+ AsyncClient.getSessions();
+ },
+ function handleRevokeError(err) {
+ let state = this.getStateFromStores();
+ state.serverError = err;
+ this.setState(state);
+ }.bind(this)
+ );
+ }
+ onShow() {
+ AsyncClient.getSessions();
+
+ if ($(window).width() > 768) {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 200);
+ } else {
+ $(ReactDOM.findDOMNode(this.refs.modalBody)).css('max-height', $(window).height() - 150);
+ }
+ }
+ onHide() {
+ this.setState({moreInfo: []});
+ this.props.onHide();
+ }
+ componentDidMount() {
+ UserStore.addSessionsChangeListener(this.onListenerChange);
+
+ if (this.props.show) {
+ this.onShow();
+ }
+ }
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.onShow();
+ }
+ }
+ componentWillUnmount() {
+ UserStore.removeSessionsChangeListener(this.onListenerChange);
+ }
+ onListenerChange() {
+ const newState = this.getStateFromStores();
+ if (!Utils.areObjectsEqual(newState.sessions, this.state.sessions)) {
+ this.setState(newState);
+ }
+ }
+ handleMoreInfo(index) {
+ let newMoreInfo = this.state.moreInfo;
+ newMoreInfo[index] = true;
+ this.setState({moreInfo: newMoreInfo});
+ }
+ render() {
+ let activityList = [];
+
+ for (let i = 0; i < this.state.sessions.length; i++) {
+ const currentSession = this.state.sessions[i];
+ const lastAccessTime = new Date(currentSession.last_activity_at);
+ const firstAccessTime = new Date(currentSession.create_at);
+ let devicePlatform = currentSession.props.platform;
+ let devicePicture = '';
+
+ if (currentSession.props.platform === 'Windows') {
+ devicePicture = 'fa fa-windows';
+ } else if (currentSession.device_id && currentSession.device_id.indexOf('apple:') === 0) {
+ devicePicture = 'fa fa-apple';
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.iphoneNativeApp'
+ defaultMessage='iPhone Native App'
+ />
+ );
+ } else if (currentSession.device_id && currentSession.device_id.indexOf('android:') === 0) {
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.androidNativeApp'
+ defaultMessage='Android Native App'
+ />
+ );
+ devicePicture = 'fa fa-android';
+ } else if (currentSession.props.platform === 'Macintosh' ||
+ currentSession.props.platform === 'iPhone') {
+ devicePicture = 'fa fa-apple';
+ } else if (currentSession.props.platform === 'Linux') {
+ if (currentSession.props.os.indexOf('Android') >= 0) {
+ devicePlatform = (
+ <FormattedMessage
+ id='activity_log_modal.android'
+ defaultMessage='Android'
+ />
+ );
+ devicePicture = 'fa fa-android';
+ } else {
+ devicePicture = 'fa fa-linux';
+ }
+ }
+
+ let moreInfo;
+ if (this.state.moreInfo[i]) {
+ moreInfo = (
+ <div>
+ <div>
+ <FormattedMessage
+ id='activity_log.firstTime'
+ defaultMessage='First time active: {date}, {time}'
+ values={{
+ date: (
+ <FormattedDate
+ value={firstAccessTime}
+ day='2-digit'
+ month='long'
+ year='numeric'
+ />
+ ),
+ time: (
+ <FormattedTime
+ value={firstAccessTime}
+ hour='2-digit'
+ minute='2-digit'
+ />
+ )
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.os'
+ defaultMessage='OS: {os}'
+ values={{
+ os: currentSession.props.os
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.browser'
+ defaultMessage='Browser: {browser}'
+ values={{
+ browser: currentSession.props.browser
+ }}
+ />
+ </div>
+ <div>
+ <FormattedMessage
+ id='activity_log.sessionId'
+ defaultMessage='Session ID: {id}'
+ values={{
+ id: currentSession.id
+ }}
+ />
+ </div>
+ </div>
+ );
+ } else {
+ moreInfo = (
+ <a
+ className='theme'
+ href='#'
+ onClick={this.handleMoreInfo.bind(this, i)}
+ >
+ <FormattedMessage
+ id='activity_log.moreInfo'
+ defaultMessage='More info'
+ />
+ </a>
+ );
+ }
+
+ activityList[i] = (
+ <div
+ key={'activityLogEntryKey' + i}
+ className='activity-log__table'
+ >
+ <div className='activity-log__report'>
+ <div className='report__platform'><i className={devicePicture}/>{devicePlatform}</div>
+ <div className='report__info'>
+ <div>
+ <FormattedMessage
+ id='activity_log.lastActivity'
+ defaultMessage='Last activity: {date}, {time}'
+ values={{
+ date: (
+ <FormattedDate
+ value={lastAccessTime}
+ day='2-digit'
+ month='long'
+ year='numeric'
+ />
+ ),
+ time: (
+ <FormattedTime
+ value={lastAccessTime}
+ hour='2-digit'
+ minute='2-digit'
+ />
+ )
+ }}
+ />
+ </div>
+ {moreInfo}
+ </div>
+ </div>
+ <div className='activity-log__action'>
+ <button
+ onClick={this.submitRevoke.bind(this, currentSession.id)}
+ className='btn btn-primary'
+ >
+ <FormattedMessage
+ id='activity_log.logout'
+ defaultMessage='Logout'
+ />
+ </button>
+ </div>
+ </div>
+ );
+ }
+
+ let content;
+ if (this.state.sessions.loading) {
+ content = <LoadingScreen/>;
+ } else {
+ content = <form role='form'>{activityList}</form>;
+ }
+
+ return (
+ <Modal
+ show={this.props.show}
+ onHide={this.onHide}
+ bsSize='large'
+ >
+ <Modal.Header closeButton={true}>
+ <Modal.Title>
+ <FormattedMessage
+ id='activity_log.activeSessions'
+ defaultMessage='Active Sessions'
+ />
+ </Modal.Title>
+ </Modal.Header>
+ <Modal.Body ref='modalBody'>
+ <p className='session-help-text'>
+ <FormattedMessage
+ id='activity_log.sessionsDescription'
+ defaultMessage="Sessions are created when you log in to a new browser on a device. Sessions let you use Mattermost without having to log in again for a time period specified by the System Admin. If you want to log out sooner, use the 'Logout' button below to end a session."
+ />
+ </p>
+ {content}
+ </Modal.Body>
+ </Modal>
+ );
+ }
+}
+
+ActivityLogModal.propTypes = {
+ show: React.PropTypes.bool.isRequired,
+ onHide: React.PropTypes.func.isRequired
+};