mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-21 19:37:28 +00:00
Ocm frontend (#4754)
* [OCM] modified the icon in 'side nav' * [OCM] share dialog: improved 'Server select', 'user input', 'submit' and etc. * [OCM] share dialog: fixup & improvement for 'delete an item' and etc. * [OCM] 'Shared from other servers' page: fixup & improvement * [OCM] dir view: fixup & improvement
This commit is contained in:
@@ -14,7 +14,8 @@ class ShareItem extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isOperationShow: false
|
||||
isOperationShow: false,
|
||||
isOpFrozen: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,16 +28,23 @@ class ShareItem extends React.Component {
|
||||
}
|
||||
|
||||
deleteShareItem = () => {
|
||||
this.setState({
|
||||
// the 'delete' takes time,
|
||||
// so 'lock' the op icon here to avoid multiple click on it
|
||||
// avoid repeated requests
|
||||
isOpFrozen: true
|
||||
});
|
||||
let item = this.props.item;
|
||||
this.props.deleteShareItem(item);
|
||||
}
|
||||
|
||||
render() {
|
||||
let item = this.props.item;
|
||||
const { isOperationShow, isOpFrozen } = this.state;
|
||||
return (
|
||||
<tr onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
|
||||
<td><a href={item.to_server_url} target="_blank">{item.to_server_name}</a></td>
|
||||
<td className="name">{item.to_user}</td>
|
||||
<td>{item.to_user}</td>
|
||||
<td>{Utils.sharePerms(item.permission)}</td>
|
||||
{/* <td>
|
||||
<SharePermissionEditor
|
||||
@@ -49,7 +57,7 @@ class ShareItem extends React.Component {
|
||||
</td> */}
|
||||
<td>
|
||||
<span
|
||||
className={`sf2-icon-x3 action-icon ${this.state.isOperationShow ? '' : 'hide'}`}
|
||||
className={`sf2-icon-x3 action-icon ${isOperationShow && !isOpFrozen ? '' : 'hide'}`}
|
||||
onClick={this.deleteShareItem}
|
||||
title={gettext('Delete')}
|
||||
>
|
||||
@@ -104,22 +112,14 @@ class ShareToOtherServer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedOption: null,
|
||||
errorMsg: [],
|
||||
permission: 'rw',
|
||||
ocmShares: [],
|
||||
selectedServer: null,
|
||||
toUser: '',
|
||||
toServerURL: '',
|
||||
permission: 'rw',
|
||||
btnDisabled: true,
|
||||
isSubmitting: false,
|
||||
ocmShares: []
|
||||
};
|
||||
this.options = [];
|
||||
this.permissions = ['rw', 'r'];
|
||||
this.UnshareMessage = 'File was unshared';
|
||||
|
||||
}
|
||||
|
||||
handleSelectChange = (option) => {
|
||||
this.setState({selectedOption: option});
|
||||
this.options = [];
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -127,47 +127,67 @@ class ShareToOtherServer extends React.Component {
|
||||
this.setState({ocmShares: res.data.ocm_share_list});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.setState({
|
||||
errorMsg: errMessage
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
startOCMShare = () => {
|
||||
let { repoID, itemPath } = this.props;
|
||||
let { toServerURL, toUser, permission } = this.state;
|
||||
OCMShare = () => {
|
||||
const { repoID, itemPath } = this.props;
|
||||
const { selectedServer, toUser, permission } = this.state;
|
||||
let toServerURL = selectedServer.value;
|
||||
if (!toServerURL.endsWith('/')) {
|
||||
toServerURL += '/';
|
||||
}
|
||||
this.setState({
|
||||
btnDisabled: true,
|
||||
isSubmitting: true
|
||||
});
|
||||
seafileAPI.addOCMSharePrepare(toUser, toServerURL, repoID, itemPath, permission).then((res) => {
|
||||
toaster.success(gettext('share success.'));
|
||||
let ocmShares = this.state.ocmShares;
|
||||
ocmShares.push(res.data);
|
||||
this.setState({ocmShares: ocmShares});
|
||||
ocmShares.unshift(res.data);
|
||||
this.setState({
|
||||
ocmShares: ocmShares,
|
||||
selectedServer: null,
|
||||
toUser: '',
|
||||
permission: 'rw',
|
||||
isSubmitting: false
|
||||
});
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
this.setState({
|
||||
btnDisabled: false,
|
||||
isSubmitting: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleToUserChange = (e) => {
|
||||
const toUser = e.target.value;
|
||||
this.setState({
|
||||
toUser: e.target.value,
|
||||
toUser: toUser,
|
||||
btnDisabled: !this.state.selectedServer || !toUser.trim()
|
||||
});
|
||||
}
|
||||
|
||||
handleURLChange = (e) => {
|
||||
handleServerChange = (selectedServer) => {
|
||||
this.setState({
|
||||
toServerURL: e.value,
|
||||
selectedServer,
|
||||
btnDisabled: !this.state.toUser.trim()
|
||||
});
|
||||
}
|
||||
|
||||
deleteShareItem = (deletedItem) => {
|
||||
let { id } = deletedItem;
|
||||
const { id } = deletedItem;
|
||||
toaster.notify(gettext('It may take some time, please wait.'));
|
||||
seafileAPI.deleteOCMSharePrepare(id).then((res) => {
|
||||
toaster.success(gettext('delete success.'));
|
||||
let ocmShares = this.state.ocmShares.filter(item => {
|
||||
return item.id != id;
|
||||
});
|
||||
this.setState({ocmShares: ocmShares});
|
||||
toaster.success(gettext('Successfully deleted 1 item.'));
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
@@ -178,9 +198,12 @@ class ShareToOtherServer extends React.Component {
|
||||
this.setState({permission: permission});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
let { ocmShares, toUser, toServerURL, permission } = this.state;
|
||||
const {
|
||||
errorMsg, ocmShares,
|
||||
toUser, selectedServer, permission,
|
||||
btnDisabled, isSubmitting
|
||||
} = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
<table>
|
||||
@@ -196,8 +219,10 @@ class ShareToOtherServer extends React.Component {
|
||||
<tr>
|
||||
<td>
|
||||
<Select
|
||||
placeholder={gettext('Select a server...')}
|
||||
value={selectedServer}
|
||||
options={ocmRemoteServers}
|
||||
onChange={this.handleURLChange}
|
||||
onChange={this.handleServerChange}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -216,15 +241,23 @@ class ShareToOtherServer extends React.Component {
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Button onClick={this.startOCMShare}>{gettext('Submit')}</Button>
|
||||
<Button
|
||||
onClick={this.OCMShare}
|
||||
disabled={btnDisabled}
|
||||
className={isSubmitting ? 'btn-loading' : ''}>
|
||||
{gettext('Submit')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ShareList
|
||||
items={ocmShares}
|
||||
deleteShareItem={this.deleteShareItem}
|
||||
/>
|
||||
{errorMsg ?
|
||||
<p className="error text-center mt-4">{errorMsg}</p> :
|
||||
<ShareList
|
||||
items={ocmShares}
|
||||
deleteShareItem={this.deleteShareItem}
|
||||
/>
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
@@ -219,7 +219,7 @@ class MainSideNav extends React.Component {
|
||||
{enableOCM &&
|
||||
<li className="nav-item">
|
||||
<Link to={siteRoot + 'shared-with-ocm/'} className={`nav-link ellipsis ${this.getActiveClass('shared-with-ocm')}`} title={gettext('Shared from other servers')} onClick={(e) => this.tabItemClick(e, 'shared-with-ocm')}>
|
||||
<span className="sf2-icon-share" aria-hidden="true"></span>
|
||||
<span className="sf3-font-share-from-other-servers sf3-font" aria-hidden="true"></span>
|
||||
<span className="nav-text">{gettext('Shared from other servers')}</span>
|
||||
</Link>
|
||||
</li>
|
||||
|
@@ -73,25 +73,10 @@ class DirView extends Component {
|
||||
window.history.replaceState({url: url, path: path}, path, url);
|
||||
});
|
||||
}).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.')
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -32,8 +32,8 @@ class Content extends Component {
|
||||
<tr>
|
||||
<th width="4%"></th>
|
||||
<th width="20%">{gettext('Name')}</th>
|
||||
<th width="20%">{gettext('Shared from')}</th>
|
||||
<th width="26%">{gettext('At site')}</th>
|
||||
<th width="20%">{gettext('Shared by')}</th>
|
||||
<th width="26%">{gettext('At server')}</th>
|
||||
<th width="20%">{gettext('Time')}</th>
|
||||
<th width="10%">{/* operations */}</th>
|
||||
</tr>
|
||||
@@ -43,7 +43,7 @@ class Content extends Component {
|
||||
return <Item
|
||||
key={index}
|
||||
item={item}
|
||||
deleteShare={this.props.deleteShare}
|
||||
leaveShare={this.props.leaveShare}
|
||||
/>;
|
||||
})}
|
||||
</tbody>
|
||||
@@ -66,57 +66,46 @@ class Item extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
showOpIcon: false,
|
||||
isOpMenuOpen: false // for mobile
|
||||
isOpIconShown: false
|
||||
};
|
||||
}
|
||||
|
||||
toggleOpMenu = () => {
|
||||
this.setState({
|
||||
isOpMenuOpen: !this.state.isOpMenuOpen
|
||||
});
|
||||
}
|
||||
|
||||
handleMouseOver = () => {
|
||||
this.setState({
|
||||
showOpIcon: true
|
||||
isOpIconShown: true
|
||||
});
|
||||
}
|
||||
|
||||
handleMouseOut = () => {
|
||||
this.setState({
|
||||
showOpIcon: false
|
||||
isOpIconShown: false
|
||||
});
|
||||
}
|
||||
|
||||
deleteShare = () => {
|
||||
this.props.deleteShare(this.props.item);
|
||||
leaveShare = (e) => {
|
||||
e.preventDefault();
|
||||
this.props.leaveShare(this.props.item);
|
||||
}
|
||||
|
||||
render() {
|
||||
const item = this.props.item;
|
||||
const { isOpIconShown } = this.state;
|
||||
|
||||
item.icon_url = Utils.getLibIconUrl(item);
|
||||
item.icon_title = Utils.getLibIconTitle(item);
|
||||
item.url = `${siteRoot}#shared-libs/lib/${item.repo_id}/`;
|
||||
|
||||
let shareRepoUrl =`${siteRoot}remote-library/${this.props.item.provider_id}/${this.props.item.repo_id}/${Utils.encodePath(this.props.item.repo_name)}/`;
|
||||
let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
|
||||
let deleteIcon = `action-icon sf2-icon-x3 ${iconVisibility ? 'invisible' : ''}`;
|
||||
return (
|
||||
<Fragment>
|
||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||
<td><img src={item.icon_url} title={item.icon_title} alt={item.icon_title} width="24" /></td>
|
||||
<td><Link to={shareRepoUrl}>{item.repo_name}</Link></td>
|
||||
<td>{item.from_user}</td>
|
||||
<td>{item.from_server_url}</td>
|
||||
<td title={moment(item.last_modified).format('llll')}>{moment(item.ctime).fromNow()}</td>
|
||||
<td>
|
||||
<a href="#" className={deleteIcon} title={gettext('Remove')} onClick={this.deleteShare}></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</Fragment>
|
||||
<tr onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
|
||||
<td><img src={item.icon_url} title={item.icon_title} alt={item.icon_title} width="24" /></td>
|
||||
<td><Link to={shareRepoUrl}>{item.repo_name}</Link></td>
|
||||
<td>{item.from_user}</td>
|
||||
<td>{item.from_server_url}</td>
|
||||
<td title={moment(item.last_modified).format('llll')}>{moment(item.ctime).fromNow()}</td>
|
||||
<td>
|
||||
<a href="#" className={`action-icon sf2-icon-x3 ${isOpIconShown ? '' : 'invisible'}`} title={gettext('Leave Share')} onClick={this.leaveShare}></a>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +120,7 @@ class SharedWithOCM extends Component {
|
||||
this.state = {
|
||||
loading: true,
|
||||
errorMsg: '',
|
||||
items: [],
|
||||
items: []
|
||||
};
|
||||
}
|
||||
|
||||
@@ -142,35 +131,21 @@ class SharedWithOCM extends Component {
|
||||
items: res.data.ocm_share_received_list
|
||||
});
|
||||
}).catch((error) => {
|
||||
if (error.response) {
|
||||
if (error.response.status == 403) {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: gettext('Permission denied')
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: gettext('Error')
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: gettext('Please check the network.')
|
||||
});
|
||||
}
|
||||
this.setState({
|
||||
loading: false,
|
||||
errorMsg: Utils.getErrorMsg(error, true) // true: show login tip if 403
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
deleteShare = (item) => {
|
||||
let { id } = item;
|
||||
leaveShare = (item) => {
|
||||
const { id, repo_name } = item;
|
||||
seafileAPI.deleteOCMShareReceived(id).then((res) => {
|
||||
toaster.success(gettext('delete success.'));
|
||||
let items = this.state.items.filter(item => {
|
||||
return item.id != id;
|
||||
});
|
||||
this.setState({items: items});
|
||||
toaster.success(gettext('Successfully unshared {name}').replace('{name}', repo_name));
|
||||
}).catch(error => {
|
||||
let errMessage = Utils.getErrorMsg(error);
|
||||
toaster.danger(errMessage);
|
||||
@@ -190,7 +165,7 @@ class SharedWithOCM extends Component {
|
||||
loading={this.state.loading}
|
||||
errorMsg={this.state.errorMsg}
|
||||
items={this.state.items}
|
||||
deleteShare={this.deleteShare}
|
||||
leaveShare={this.leaveShare}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -203,7 +203,6 @@ class Item extends Component {
|
||||
|
||||
data.icon_url = Utils.getLibIconUrl(data);
|
||||
data.icon_title = Utils.getLibIconTitle(data);
|
||||
data.url = `${siteRoot}#shared-libs/lib/${data.repo_id}/`;
|
||||
|
||||
let iconVisibility = this.state.showOpIcon ? '' : ' invisible';
|
||||
let shareIconClassName = 'op-icon sf2-icon-share repo-share-btn' + iconVisibility;
|
||||
|
Reference in New Issue
Block a user