1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-11 03:41:12 +00:00

[Activities] mobile: redesigned it (#3913)

This commit is contained in:
llj
2019-07-25 15:06:58 +08:00
committed by Daniel Pan
parent 2fba71bf1e
commit 9a43e08279
2 changed files with 144 additions and 97 deletions

View File

@@ -1,16 +1,20 @@
table tr .activity-date { .activity-details {
border-top: 0;
}
table .activity-detail {
word-break: break-word;
}
table .activity-detail div {
display: inline;
}
table .activity-detail span {
color: #888;
}
table .activity-detail span:hover {
color: #333;
text-decoration: underline; text-decoration: underline;
cursor: pointer;
}
.activity-details:hover {
color: #333;
}
.mobile-activity-op {
display: inline-block;
margin: 0 0 .2em .8em;
padding: 0 .5em;
background: #ffbd6f;
border-radius: 2px;
color: #fff;
font-size: 0.75rem;
}
.mobile-activity-time {
display: inline-block;
margin-bottom: .2em;
} }

View File

@@ -8,6 +8,7 @@ import Loading from '../../components/loading';
import Activity from '../../models/activity'; import Activity from '../../models/activity';
import ListCreatedFileDialog from '../../components/dialog/list-created-files-dialog'; import ListCreatedFileDialog from '../../components/dialog/list-created-files-dialog';
import ModalPortal from '../../components/modal-portal'; import ModalPortal from '../../components/modal-portal';
import '../../css/files-activities.css'; import '../../css/files-activities.css';
moment.locale(window.app.config.lang); moment.locale(window.app.config.lang);
@@ -17,20 +18,39 @@ const contentPropTypes = {
items: PropTypes.array.isRequired, items: PropTypes.array.isRequired,
}; };
const isDesktop = window.innerWidth >= 768;
class FileActivitiesContent extends Component { class FileActivitiesContent extends Component {
render() { render() {
let { items, isLoadingMore } = this.props; let { items, isLoadingMore } = this.props;
const desktopThead = (
<thead>
<tr>
<th width="8%">{/* avatar */}</th>
<th width="15%">{gettext('User')}</th>
<th width="20%">{gettext('Operation')}</th>
<th width="37%">{gettext('File')} / {gettext('Library')}</th>
<th width="20%">{gettext('Time')}</th>
</tr>
</thead>
);
const mobileThead = (
<thead>
<tr>
<th width="15%"></th>
<th width="53%"></th>
<th width="32%"></th>
</tr>
</thead>
);
return ( return (
<Fragment> <Fragment>
<table width="100%" className="table table-hover table-vcenter"> <table className="table-hover table-thead-hidden">
<colgroup> {isDesktop ? desktopThead : mobileThead}
<col width="8%" />
<col width="15%" />
<col width="20%" />
<col width="37%" />
<col width="20%" />
</colgroup>
<tbody> <tbody>
{items.map((item, index) => { {items.map((item, index) => {
return ( return (
@@ -75,7 +95,7 @@ class ActivityItem extends Component {
render() { render() {
let {item, index, items} = this.props; let {item, index, items} = this.props;
let op, details; let op, details, moreDetails = false;
let userProfileURL = `${siteRoot}profile/${encodeURIComponent(item.author_email)}/`; let userProfileURL = `${siteRoot}profile/${encodeURIComponent(item.author_email)}/`;
let libURL = siteRoot + 'library/' + item.repo_id + '/' + encodeURIComponent(item.repo_name) + '/'; let libURL = siteRoot + 'library/' + item.repo_id + '/' + encodeURIComponent(item.repo_name) + '/';
@@ -86,34 +106,36 @@ class ActivityItem extends Component {
switch(item.op_type) { switch(item.op_type) {
case 'create': case 'create':
op = gettext('Created library'); op = gettext('Created library');
details = <td>{libLink}</td>; details = libLink;
break; break;
case 'rename': case 'rename':
op = gettext('Renamed library'); op = gettext('Renamed library');
details = <td>{item.old_repo_name} => {libLink}</td>; details = <span>{item.old_repo_name} => {libLink}</span>;
break; break;
case 'delete': case 'delete':
op = gettext('Deleted library'); op = gettext('Deleted library');
details = <td>{item.repo_name}</td>; details = item.repo_name;
break; break;
case 'recover': case 'recover':
op = gettext('Restored library'); op = gettext('Restored library');
details = <td>{libLink}</td>; details = libLink;
break; break;
case 'clean-up-trash': case 'clean-up-trash':
if (item.days == 0) {
details = <td>{gettext('Removed all items from trash.')}<br />{libLink}</td>;
} else {
details = <td>{gettext('Removed items older than {n} days from trash.').replace('{n}', item.days)}<br />{libLink}</td>;
}
op = gettext('Cleaned trash'); op = gettext('Cleaned trash');
if (item.days == 0) {
details = gettext('Removed all items from trash.');
} else {
details = gettext('Removed items older than {n} days from trash.').replace('{n}', item.days);
}
moreDetails = true;
break; break;
} }
} else if (item.obj_type == 'draft') { } else if (item.obj_type == 'draft') {
let fileURL = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`; let fileURL = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`;
let fileLink = <a href={fileURL} target="_blank">{item.name}</a>; let fileLink = <a href={fileURL} target="_blank">{item.name}</a>;
op = gettext('Publish draft'); op = gettext('Publish draft');
details = <td>{fileLink}<br />{smallLibLink}</td>; details = fileLink;
moreDetails = true;
} else if (item.obj_type == 'files') { } else if (item.obj_type == 'files') {
let fileURL = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`; let fileURL = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`;
if (item.name.endsWith('(draft).md')) { if (item.name.endsWith('(draft).md')) {
@@ -124,65 +146,56 @@ class ActivityItem extends Component {
fileLink = item.name; fileLink = item.name;
} }
let fileCount = item.createdFilesCount - 1; let fileCount = item.createdFilesCount - 1;
let firstLine = gettext('{file} and {n} other files'); let firstLine = gettext('{file} and {n} other files')
firstLine = firstLine.replace('{file}', fileLink); .replace('{file}', fileLink)
firstLine = firstLine.replace('{n}', fileCount); .replace('{n}', fileCount);
op = gettext('Created {n} files').replace('{n}', item.createdFilesCount); op = gettext('Created {n} files').replace('{n}', item.createdFilesCount);
details = details = (
<td className="activity-detail"> <Fragment>
<div dangerouslySetInnerHTML={{__html: firstLine}}></div>{' '} <p className="m-0 d-inline" dangerouslySetInnerHTML={{__html: firstLine}}></p>
<span onClick={this.onListCreatedFilesToggle} className="cursor-pointer">{gettext('details')}</span> {isDesktop && <span onClick={this.onListCreatedFilesToggle} className="activity-details text-secondary ml-2">{gettext('details')}</span>}
<br />{smallLibLink} </Fragment>
</td>; );
moreDetails = true;
} else if (item.obj_type == 'file') { } else if (item.obj_type == 'file') {
let fileURL = `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`; const isDraft = item.name.endsWith('(draft).md');
if (item.name.endsWith('(draft).md')) { const fileURL = isDraft ? serviceURL + '/drafts/' + item.draft_id + '/' :
fileURL = serviceURL + '/drafts/' + item.draft_id + '/'; `${siteRoot}lib/${item.repo_id}/file${Utils.encodePath(item.path)}`;
}
let fileLink = <a href={fileURL} target="_blank">{item.name}</a>; let fileLink = <a href={fileURL} target="_blank">{item.name}</a>;
if (item.name.endsWith('(draft).md') && !item.draft_id) { if (isDraft && !item.draft_id) {
fileLink = item.name; fileLink = item.name;
} }
switch (item.op_type) { switch (item.op_type) {
case 'create': case 'create':
if (item.name.endsWith('(draft).md')) { op = isDraft ? gettext('Created draft') : gettext('Created file');
op = gettext('Created draft'); details = fileLink;
details = <td>{fileLink}<br />{smallLibLink}</td>; moreDetails = true;
break;
}
op = gettext('Created file');
details = <td>{fileLink}<br />{smallLibLink}</td>;
break; break;
case 'delete': case 'delete':
if (item.name.endsWith('(draft).md')) { op = isDraft ? gettext('Deleted draft') : gettext('Deleted file');
op = gettext('Deleted draft'); details = item.name;
details = <td>{item.name}<br />{smallLibLink}</td>; moreDetails = true;
break;
}
op = gettext('Deleted file');
details = <td>{item.name}<br />{smallLibLink}</td>;
break; break;
case 'recover': case 'recover':
op = gettext('Restored file'); op = gettext('Restored file');
details = <td>{fileLink}<br />{smallLibLink}</td>; details = fileLink;
moreDetails = true;
break; break;
case 'rename': case 'rename':
op = gettext('Renamed file'); op = gettext('Renamed file');
details = <td>{item.old_name} => {fileLink}<br />{smallLibLink}</td>; details = <span>{item.old_name} => {fileLink}</span>;
moreDetails = true;
break; break;
case 'move': case 'move':
var filePathLink = <a href={fileURL}>{item.path}</a>; const filePathLink = <a href={fileURL}>{item.path}</a>;
op = gettext('Moved file'); op = gettext('Moved file');
details = <td>{item.old_path} => {filePathLink}<br />{smallLibLink}</td>; details = <span>{item.old_path} => {filePathLink}</span>;
moreDetails = true;
break; break;
case 'edit': // update case 'edit': // update
if (item.name.endsWith('(draft).md')) { op = isDraft ? gettext('Updated draft') : gettext('Updated file');
op = gettext('Updated draft'); details = fileLink;
details = <td>{fileLink}<br />{smallLibLink}</td>; moreDetails = true;
break;
}
op = gettext('Updated file');
details = <td>{fileLink}<br />{smallLibLink}</td>;
break; break;
} }
} else { // dir } else { // dir
@@ -191,24 +204,29 @@ class ActivityItem extends Component {
switch (item.op_type) { switch (item.op_type) {
case 'create': case 'create':
op = gettext('Created folder'); op = gettext('Created folder');
details = <td>{dirLink}<br />{smallLibLink}</td>; details = dirLink;
moreDetails = true;
break; break;
case 'delete': case 'delete':
op = gettext('Deleted folder'); op = gettext('Deleted folder');
details = <td>{item.name}<br />{smallLibLink}</td>; details = item.name;
moreDetails = true;
break; break;
case 'recover': case 'recover':
op = gettext('Restored folder'); op = gettext('Restored folder');
details = <td>{dirLink}<br />{smallLibLink}</td>; details = dirLink;
moreDetails = true;
break; break;
case 'rename': case 'rename':
op = gettext('Renamed folder'); op = gettext('Renamed folder');
details = <td>{item.old_name} => {dirLink}<br />{smallLibLink}</td>; details = <span>{item.old_name} => {dirLink}</span>;
moreDetails = true;
break; break;
case 'move': case 'move':
var dirPathLink = <a href={dirURL}>{item.path}</a>; const dirPathLink = <a href={dirURL}>{item.path}</a>;
op = gettext('Moved folder'); op = gettext('Moved folder');
details = <td>{item.old_path} => {dirPathLink}<br />{smallLibLink}</td>; details = <span>{item.old_path} => {dirPathLink}</span>;
moreDetails = true;
break; break;
} }
} }
@@ -223,22 +241,47 @@ class ActivityItem extends Component {
<Fragment> <Fragment>
{isShowDate && {isShowDate &&
<tr> <tr>
<td colSpan='5' className="activity-date">{moment(item.time).format('YYYY-MM-DD')}</td> <td colSpan={isDesktop ? 5 : 3} className="border-top-0">{moment(item.time).format('YYYY-MM-DD')}</td>
</tr> </tr>
} }
{isDesktop ? (
<tr> <tr>
<td className="text-center"> <td className="text-center">
<img src={item.avatar_url} alt="" width="36px" height="36px" className="avatar" /> <img src={item.avatar_url} alt="" width="32" height="32" className="avatar" />
</td> </td>
<td> <td>
<a href={userProfileURL}>{item.author_name}</a> <a href={userProfileURL}>{item.author_name}</a>
</td> </td>
<td><span className="activity-op">{op}</span></td> <td>{op}</td>
<td>
{details} {details}
{moreDetails && <br /> }
{moreDetails && smallLibLink}
</td>
<td className="text-secondary"> <td className="text-secondary">
<time datetime={item.time} is="relative-time" title={moment(item.time).format('llll')}>{moment(item.time).fromNow()}</time> <time datetime={item.time} is="relative-time" title={moment(item.time).format('llll')}>{moment(item.time).fromNow()}</time>
</td> </td>
</tr> </tr>
) : (
<tr>
<td className="text-center">
<img src={item.avatar_url} alt="" width="32" height="32" className="avatar" />
</td>
<td>
<a href={userProfileURL}>{item.author_name}</a>
<span className="mobile-activity-op">{op}</span>
<br />{details}
</td>
<td className="text-right align-top">
<span className="text-secondary mobile-activity-time">
<time datetime={item.time} is="relative-time" title={moment(item.time).format('llll')}>{moment(item.time).fromNow()}</time>
</span>
{moreDetails && <br /> }
{moreDetails && libLink}
</td>
</tr>
)}
{this.state.isListCreatedFiles && {this.state.isListCreatedFiles &&
<ModalPortal> <ModalPortal>
<ListCreatedFileDialog <ListCreatedFileDialog