diff --git a/frontend/src/app.js b/frontend/src/app.js index b57b687c6e..5e402e174f 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -9,6 +9,7 @@ import DraftContent from './pages/drafts/draft-content'; import ReviewContent from './pages/drafts/review-content'; import FilesActivities from './pages/dashboard/files-activities'; import Starred from './pages/starred/starred'; +import LinkedDevices from './pages/linked-devices/linked-devices'; import editUtilties from './utils/editor-utilties'; import 'seafile-ui'; @@ -86,6 +87,7 @@ class App extends Component { + diff --git a/frontend/src/components/main-side-nav.js b/frontend/src/components/main-side-nav.js index df36e03847..674079c687 100644 --- a/frontend/src/components/main-side-nav.js +++ b/frontend/src/components/main-side-nav.js @@ -164,11 +164,11 @@ class MainSideNav extends React.Component { {gettext('Acitivities')} -
  • - this.tabItemClick('devices')}> +
  • + this.tabItemClick('linked-devices')}> {gettext('Linked Devices')} - +
  • this.tabItemClick('drafts')}> diff --git a/frontend/src/pages/linked-devices/linked-devices.js b/frontend/src/pages/linked-devices/linked-devices.js new file mode 100644 index 0000000000..baa2cb8a07 --- /dev/null +++ b/frontend/src/pages/linked-devices/linked-devices.js @@ -0,0 +1,213 @@ +import moment from 'moment'; +import React, { Component } from 'react'; +import Toast from '../../components/toast'; + +import { seafileAPI } from '../../utils/seafile-api'; +import { Utils } from '../../utils/utils'; +import { gettext, siteRoot, loginUrl } from '../../utils/constants'; + +class Content extends Component { + + render() { + const {loading, errorMsg, items} = this.props.data; + + if (loading) { + return ; + } else if (errorMsg) { + return

    {errorMsg}

    ; + } else { + const desktopThead = ( + + + {gettext("Platform")} + {gettext("Device Name")} + {gettext("IP")} + {gettext("Last Access")} + + + + ); + const mobileThead = ( + + + {gettext("Platform")} + {gettext('Device Name')} + + + + ); + + return ( + + {window.innerWidth >= 768 ? desktopThead : mobileThead} + +
    + ); + } + } +} + +class TableBody extends Component { + + constructor(props) { + super(props); + this.state = { + items: this.props.items + }; + } + + render() { + + let listLinkedDevices = this.state.items.map(function(item, index) { + return ; + }, this); + + return ( + {listLinkedDevices} + ); + } +} + +class Item extends Component { + + constructor(props) { + super(props); + this.state = { + showOpIcon: false, + unlinked: false + }; + + this.handleMouseOver = this.handleMouseOver.bind(this); + this.handleMouseOut = this.handleMouseOut.bind(this); + this.handleClick = this.handleClick.bind(this); + } + + handleMouseOver() { + this.setState({ + showOpIcon: true + }); + } + + handleMouseOut() { + this.setState({ + showOpIcon: false + }); + } + + handleClick(e) { + e.preventDefault(); + + const data = this.props.data; + + seafileAPI.unLinkDevice(data.platform, data.device_id).then((res) => { + this.setState({ + unlinked: true + }); + let msg_s = gettext("Successfully unlink %(name)s."); + msg_s = msg_s.replace('%(name)s', data.device_name); + Toast.success(msg_s); + }).catch((error) => { + let message = gettext("Failed to unlink %(name)s"); + message = message.replace('%(name)s', data.device_name); + Toast.error(message); + }); + } + + render() { + if (this.state.unlinked) { + return null; + } + + const data = this.props.data; + + let opClasses = 'sf2-icon-delete unlink-device op-icon'; + opClasses += this.state.showOpIcon ? '' : ' invisible'; + + const desktopItem = ( + + {data.platform} + {data.device_name} + {data.last_login_ip} + {moment(data.last_accessed).fromNow()} + + + + + ); + + const mobileItem = ( + + {data.platform} + {data.device_name} + + + + + ); + + if (window.innerWidth >= 768) { + return desktopItem; + } else { + return mobileItem; + } + } +} + +class LinkedDevices extends Component { + constructor(props) { + super(props); + this.state = { + loading: true, + errorMsg: '', + items: [] + }; + } + + componentDidMount() { + seafileAPI.listLinkedDevices().then((res) => { + //res: {data: Array(2), status: 200, statusText: "OK", headers: {…}, config: {…}, …} + this.setState({ + loading: false, + items: res.data + }); + }).catch((error) => { + if (error.response) { + if (error.response.status == 403) { + this.setState({ + loading: false, + errorMsg: gettext('Permission denied') + }); + location.href = `${loginUrl}?next=${encodeURIComponent(location.href)}`; + } else { + this.setState({ + loading: false, + errorMsg: gettext('Error') + }); + } + + } else { + this.setState({ + loading: false, + errorMsg: gettext('Please check the network.') + }); + } + }); + } + + render() { + return ( +
    +
    +

    {gettext('Linked Devices')}

    +
    +
    +
    + +
    +
    +
    + ); + } +} + +export default LinkedDevices; diff --git a/seahub/templates/js/templates.html b/seahub/templates/js/templates.html index 1d94de7e82..cda8144b90 100644 --- a/seahub/templates/js/templates.html +++ b/seahub/templates/js/templates.html @@ -1574,7 +1574,7 @@ <% } %> <% } %>
  • - {% trans "Linked Devices" %} + {% trans "Linked Devices" %}
  • {% if enable_guest_invitation and user.permissions.can_invite_guest %}
  • diff --git a/seahub/urls.py b/seahub/urls.py index 6242cddf1d..f8efddf587 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -192,6 +192,7 @@ urlpatterns = [ ### React ### url(r'^dashboard/$', TemplateView.as_view(template_name="react_app.html"), name="dashboard"), url(r'^starred/$', TemplateView.as_view(template_name="react_app.html"), name="starred"), + url(r'^linked-devices/$', linked_devices, name="linked_devices"), ### Ajax ### url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/$', get_dirents, name="get_dirents"), diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py index 22239acae4..40f9e955be 100644 --- a/seahub/views/__init__.py +++ b/seahub/views/__init__.py @@ -1209,3 +1209,7 @@ def choose_register(request): return render(request, 'choose_register.html', { 'login_bg_image_path': login_bg_image_path }) + +@login_required +def linked_devices(request): + return render(request, "react_app.html")