1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-16 23:29:49 +00:00

For mobile (#6523)

* ['notice' popover, 'all notice' dialog] fixup for mobile

* ['Files' page] fixed library icon size for mobile

* ['settings' page] fixup for 'Email notification'

* ['my libs'] fixed 'star/unstar', 'share', 'delete'

* ['deleted repos' dialog] modified column width of the table for mobile

* ['share' dialog] modified 'link details' panel for mobile

* [side panel resizer] removed it for mobile

* [group/department, shared with all] fixed the operation menu for mobile

* ['dir view'] don't offer 'repo history' page for mobile

* ['help'] fixup for page with large images, for mobile

- fixup for http://127.0.0.1:8000/help/drive_client_2.0_for_windows_10/
  (in mobile, the content of it overflowed to the right, and was cut off)

* ['file view'] removed 'history' page for mobile

* ['markdown file view'] removed 'history' page for small screen, mobile

* ['dir view'] added a gray layer to close the 'file/folder tree' panel in mobile

* ['dir view'] fixup for the 'path' bar in mobile

* ['dir view', 'shared dir view'] don't display the 'tag list' in root for mobile
This commit is contained in:
llj
2024-08-12 12:14:03 +08:00
committed by GitHub
parent 980d0922eb
commit 172ab0d2ad
24 changed files with 106 additions and 81 deletions

View File

@@ -289,7 +289,7 @@ class App extends Component {
toggleFoldSideNav={this.toggleFoldSideNav}
style={sidePanelStyle}
/>
{!isSidePanelFolded &&
{Utils.isDesktop() && !isSidePanelFolded &&
<ResizeBar
resizeBarRef={this.resizeBarRef}
dragHandlerRef={this.dragHandlerRef}

View File

@@ -6,12 +6,18 @@
position: absolute;
background: #fff;
width: 320px;
right: -10px;
right: -16px;
top: -1px;
border-radius: 3px;
box-shadow: 0 0 5px #ccc;
}
@media (min-width: 768px) {
.notification-container {
right: -10px;
}
}
.notification-container .notification-header {
display: flex;
align-items: center;

View File

@@ -63,10 +63,10 @@ class MyLibsDeleted extends Component {
<table>
<thead>
<tr>
<th width="8%">{/* img*/}</th>
<th width="52%">{gettext('Name')}</th>
<th width="30%">{gettext('Deleted Time')}</th>
<th width="10%"></th>
<th width="10%">{/* img*/}</th>
<th width="50%">{gettext('Name')}</th>
<th width="28%">{gettext('Deleted Time')}</th>
<th width="12%"></th>
</tr>
</thead>
<tbody>

View File

@@ -1,6 +1,7 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { gettext, siteRoot } from '../../utils/constants';
import { Utils } from '../../utils/utils';
import TreeSection from '../tree-section';
import TrashDialog from '../dialog/trash-dialog';
@@ -26,6 +27,7 @@ const DirOthers = ({ userPerm, repoID, currentRepoInfo }) => {
</div>
</div>
}
{Utils.isDesktop() &&
<div className='tree-node-inner text-nowrap' title={gettext('History')} onClick={() => location.href = historyUrl}>
<div className="tree-node-text">{gettext('History')}</div>
<div className="left-icon">
@@ -34,6 +36,7 @@ const DirOthers = ({ userPerm, repoID, currentRepoInfo }) => {
</div>
</div>
</div>
}
{showTrashDialog && (
<TrashDialog
repoID={repoID}

View File

@@ -244,13 +244,6 @@ class FileToolbar extends React.Component {
{gettext('Share')}
</DropdownItem>
)}
{filePerm == 'rw' && (
<DropdownItem>
<a href={`${siteRoot}repo/file_revisions/${repoID}/?p=${encodeURIComponent(filePath)}&referer=${encodeURIComponent(location.href)}`} className="text-inherit">
{gettext('History')}
</a>
</DropdownItem>
)}
{canDownloadFile && (
<DropdownItem>
<a href="?dl=1" className="text-inherit">

View File

@@ -1,16 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../utils/constants';
function LibsMobileThead() {
return (
<thead>
<tr>
<th width="12%"><span className="sr-only">{gettext('Library Type')}</span></th>
<th width="80%"></th>
<th width="8%"><span className="sr-only">{gettext('Actions')}</span></th>
</tr>
</thead>
);
const propTypes = {
inAllLibs: PropTypes.bool // for 'Libraries' in 'Files' page
};
class LibsMobileThead extends React.Component {
render() {
const { inAllLibs = false } = this.props;
const widthList = inAllLibs ? ['14%', '78%', '8%'] : ['12%', '80%', '8%'];
return (
<thead>
<tr>
<th width={widthList[0]}><span className="sr-only">{gettext('Library Type')}</span></th>
<th width={widthList[1]}></th>
<th width={widthList[2]}><span className="sr-only">{gettext('Actions')}</span></th>
</tr>
</thead>
);
}
}
LibsMobileThead.propTypes = propTypes;
export default LibsMobileThead;

View File

@@ -193,7 +193,7 @@ class LinkDetails extends React.Component {
<>
<dt className="text-secondary font-weight-normal">{gettext('Password')}</dt>
<dd>
<InputGroup className="w-50">
<InputGroup className="share-link-details-item">
{this.state.storedPasswordVisible ?
<Input type="text" readOnly={true} value={sharedLinkInfo.password} /> :
<Input type="text" readOnly={true} value={'***************'} />
@@ -233,7 +233,7 @@ class LinkDetails extends React.Component {
</div>
</div>
) : (
<InputGroup className="w-50">
<InputGroup className="share-link-details-item">
<Input type="text" readOnly={true} value={moment(sharedLinkInfo.expire_date).format('YYYY-MM-DD HH:mm:ss')} />
<InputGroupAddon addonType="append">
<Button
@@ -253,7 +253,7 @@ class LinkDetails extends React.Component {
<>
<dt className="text-secondary font-weight-normal">{gettext('Permission')}</dt>
<dd>
<div className="w-50">
<div className="share-link-details-item">
<SelectEditor
isTextMode={false}
isEditIconShow={false}
@@ -268,8 +268,8 @@ class LinkDetails extends React.Component {
)}
<>
<dt className="text-secondary font-weight-normal">{gettext('Scope')}</dt>
<dd className="d-flex align-items-center">
<div className="w-50 mr-2">
<dd className="d-flex align-items-center flex-wrap">
<div className="share-link-details-item mr-2">
<ShareLinkScopeEditor
isTextMode={false}
isEditIconShow={false}

View File

@@ -249,18 +249,15 @@ class SharedRepoListItem extends React.Component {
this.setState({ isHistorySettingDialogShow: !this.state.isHistorySettingDialogShow });
};
onItemShare = (e) => {
e.preventDefault();
onItemShare = () => {
this.setState({ isShowSharedDialog: true });
};
onItemUnshare = (e) => {
e.preventDefault();
onItemUnshare = () => {
this.props.onItemUnshare(this.props.repo);
};
onItemDeleteToggle = (e) => {
e.preventDefault();
onItemDeleteToggle = () => {
this.setState({ isDeleteDialogShow: !this.state.isDeleteDialogShow });
};
@@ -454,7 +451,7 @@ class SharedRepoListItem extends React.Component {
operations.push('Unshare');
}
} else {
operations = this.generatorOperations();
operations = this.generatorOperations().filter(item => item != 'Divider');
if (this.isDeparementOnwerGroupMember) {
operations.unshift('Unshare');
operations.unshift('Share');
@@ -499,9 +496,9 @@ class SharedRepoListItem extends React.Component {
} else {
operations = this.generatorOperations();
}
const shareOperation = <a href="#" className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onItemShare}></a>;
const unshareOperation = <a href="#" className="op-icon sf2-icon-x3" title={gettext('Unshare')} role="button" aria-label={gettext('Unshare')} onClick={this.onItemUnshare}></a>;
const deleteOperation = <a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDeleteToggle}></a>;
const shareOperation = <i className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onItemShare}></i>;
const unshareOperation = <i className="op-icon sf2-icon-x3" title={gettext('Unshare')} role="button" aria-label={gettext('Unshare')} onClick={this.onItemUnshare}></i>;
const deleteOperation = <i className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onItemDeleteToggle}></i>;
if (this.isDeparementOnwerGroupMember) {
const advancedOperations = this.getAdvancedOperations();

View File

@@ -22,6 +22,7 @@ const propTypes = {
hasNextPage: PropTypes.bool,
onMonitorRepo: PropTypes.func,
theadHidden: PropTypes.bool,
inAllLibs: PropTypes.bool,
};
class SharedRepoListView extends React.Component {
@@ -139,9 +140,10 @@ class SharedRepoListView extends React.Component {
};
renderMobileUI = () => {
const { inAllLibs = false } = this.props;
return (
<table className="table-thead-hidden">
<LibsMobileThead />
<LibsMobileThead inAllLibs={inAllLibs} />
<tbody>
{this.renderRepoListView()}
</tbody>

View File

@@ -289,7 +289,14 @@ class DirOperationToolbar extends React.Component {
} else {
content = (
<Dropdown isOpen={this.state.isMobileOpMenuOpen} toggle={this.toggleMobileOpMenu}>
<DropdownToggle tag="span" className="sf2-icon-plus mobile-toolbar-icon" />
<DropdownToggle
tag="div"
role="button"
className="path-item"
>
{this.props.children}
<i className="sf3-font-down sf3-font ml-1 path-item-dropdown-toggle"></i>
</DropdownToggle>
<DropdownMenu>
{canUpload && (
<DropdownItem onClick={this.onUploadFile}>{gettext('Upload')}</DropdownItem>

View File

@@ -66,34 +66,30 @@ class EmailNotice extends React.Component {
return (
<div className="setting-item" id="email-notice">
<h3 className="setting-item-heading">{gettext('Email Notification')}</h3>
<h6 className="">{gettext('Notifications of file changes')}</h6>
<p className="mb-1">{gettext('The list of added, deleted and modified files will be sent to your mailbox.')}</p>
<form method="post" action="" id="set-email-notice-interval-form" onSubmit={this.formSubmit}>
<h4 className="h6">{gettext('Notifications of file changes')}</h4>
<p className="mb-1">{gettext('The list of added, deleted and modified files will be sent to your mailbox.')}</p>
{this.fileUpdatesOptions.map((item, index) => {
return (
<React.Fragment key={`file-updates-${index}`}>
<input type="radio" name="interval" value={item.interval} className="align-middle" id={`file-updates-interval-option${index + 1}`} checked={fileUpdatesEmailInterval == item.interval} onChange={this.inputFileUpdatesEmailIntervalChange} />
<label className="align-middle m-0 ml-2" htmlFor={`interval-option${index + 1}`}>{item.text}</label>
<br />
</React.Fragment>
<div className="d-flex" key={`file-updates-${index}`}>
<input type="radio" name="file-interval" value={item.interval} id={`file-updates-interval-option-${index + 1}`} checked={fileUpdatesEmailInterval == item.interval} onChange={this.inputFileUpdatesEmailIntervalChange} />
<label className="m-0 ml-2" htmlFor={`file-updates-interval-option-${index + 1}`}>{item.text}</label>
</div>
);
})}
</form>
<h6 className="mt-4">{gettext('Notifications of collaboration')}</h6>
<p className="mb-1">{gettext('Whether the notifications of collaboration such as sharing library or joining group should be sent to your mailbox.')}</p>
<form method="post" action="" id="set-email-notice-interval-form" onSubmit={this.formSubmit}>
<h4 className="mt-3 h6">{gettext('Notifications of collaboration')}</h4>
<p className="mb-1">{gettext('Whether the notifications of collaboration such as sharing library or joining group should be sent to your mailbox.')}</p>
{this.collaborateOptions.map((item, index) => {
return (
<React.Fragment key={`collaborate-${index}`}>
<input type="radio" name="interval" value={item.interval} className="align-middle" id={`collaborate-interval-option${index + 1}`} checked={collaborateEmailInterval == item.interval} onChange={this.inputCollaborateEmailIntervalChange} />
<label className="align-middle m-0 ml-2" htmlFor={`interval-option${index + 1}`}>{item.text}</label>
<br />
</React.Fragment>
<div className="d-flex align-items-start" key={`collaborate-${index}`}>
<input type="radio" name="col-interval" value={item.interval} className="mt-1" id={`collaborate-interval-option-${index + 1}`} checked={collaborateEmailInterval == item.interval} onChange={this.inputCollaborateEmailIntervalChange} />
<label className="m-0 ml-2" htmlFor={`collaborate-interval-option-${index + 1}`}>{item.text}</label>
</div>
);
})}
<button type="submit" className="btn btn-outline-primary mt-4">{gettext('Submit')}</button>
</form>
<button type="submit" className="btn btn-outline-primary mt-2" onClick={this.formSubmit}>{gettext('Submit')}</button>
</div>
);
}

View File

@@ -15,6 +15,10 @@
padding: 0.3125rem 0.25rem;
}
.share-link-details-item {
width: 75%;
}
@media (min-width: 768px) {
.share-dialog .share-dialog-content {
flex-direction: row;
@@ -32,6 +36,10 @@
padding: 0.3125rem 0.5rem;
margin: 0;
}
.share-link-details-item {
width: 50%;
}
}
.share-dialog-content .share-dialog-main {

View File

@@ -1,6 +1,6 @@
.notification-list-dialog {
width: 720px;
max-width: 720px;
max-width: calc(100% - 1rem);
height: calc(100% - 56px);
}

View File

@@ -10,6 +10,7 @@ import CreateRepoDialog from '../../components/dialog/create-repo-dialog';
import Repo from '../../models/repo';
const propTypes = {
inAllLibs: PropTypes.bool,
currentViewMode: PropTypes.string,
group: PropTypes.object.isRequired,
updateGroup: PropTypes.func.isRequired
@@ -106,7 +107,7 @@ class GroupItem extends React.Component {
render() {
const { group, currentViewMode = 'list' } = this.props;
const { inAllLibs = false, group, currentViewMode = 'list' } = this.props;
const { parent_group_id, admins } = group;
const emptyTip = <p className={`libraries-empty-tip-in-${currentViewMode}-mode`}>{gettext('No libraries')}</p>;
@@ -127,6 +128,7 @@ class GroupItem extends React.Component {
{group.repos.length === 0 ?
emptyTip :
<SharedRepoListView
inAllLibs={inAllLibs}
theadHidden={true}
isShowRepoOwner={false}
currentGroup={this.props.group}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Utils } from '../../utils/utils';
import { gettext } from '../../utils/constants';
import CurDirPath from '../../components/cur-dir-path';
import Detail from '../../components/dirent-detail';
@@ -172,7 +173,7 @@ class LibContentContainer extends React.Component {
const { path, repoID, usedRepoTags } = this.props;
let isRepoInfoBarShow = false;
if (path === '/') {
if (usedRepoTags.length !== 0) {
if (Utils.isDesktop() && usedRepoTags.length !== 0) {
isRepoInfoBarShow = true;
}
}

View File

@@ -2,6 +2,8 @@ import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import cookie from 'react-cookies';
import moment from 'moment';
import MediaQuery from 'react-responsive';
import { Modal } from 'reactstrap';
import { navigate } from '@gatsbyjs/reach-router';
import { gettext, siteRoot, username, enableVideoThumbnail, enablePDFThumbnail } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
@@ -2276,6 +2278,9 @@ class LibContentView extends React.Component {
toggleDialog={this.toggleDeleteFolderDialog}
/>
)}
<MediaQuery query="(max-width: 767.8px)">
<Modal zIndex="1030" isOpen={!Utils.isDesktop() && this.state.isTreePanelShown} toggle={this.toggleTreePanel} contentClassName="d-none"></Modal>
</MediaQuery>
</Fragment>
);
}

View File

@@ -322,6 +322,7 @@ class Libraries extends Component {
return (
<GroupItem
key={index}
inAllLibs={true}
group={group}
updateGroup={this.updateGroup}
currentViewMode={currentViewMode}

View File

@@ -68,9 +68,6 @@ class MoreMenu extends React.PureComponent {
}
{isSmall && <DropdownItem onClick={this.props.openParentDirectory}>{gettext('Open parent directory')}</DropdownItem>}
{isSmall && canGenerateShareLink && <DropdownItem onClick={this.props.toggleShareLinkDialog}>{gettext('Share')}</DropdownItem>}
{(isSmall && this.props.showFileHistory) &&
<DropdownItem onClick={this.props.toggleHistory}>{gettext('History')}</DropdownItem>
}
{isSmall && canDownloadFile &&
<DropdownItem onClick={this.downloadFile}>{gettext('Download')}</DropdownItem>
}

View File

@@ -148,8 +148,7 @@ class MylibRepoListItem extends React.Component {
}
};
onToggleStarRepo = (e) => {
e.preventDefault();
onToggleStarRepo = () => {
const repoName = this.props.repo.repo_name;
if (this.state.isStarred) {
seafileAPI.unstarItem(this.props.repo.repo_id, '/').then(() => {
@@ -194,17 +193,11 @@ class MylibRepoListItem extends React.Component {
});
};
onShareToggle = (e) => {
// when close share dialog after send share link email,
// there is no event
if (e != undefined) {
e.preventDefault();
}
onShareToggle = () => {
this.setState({ isShareDialogShow: !this.state.isShareDialogShow });
};
onDeleteToggle = (e) => {
e.preventDefault();
onDeleteToggle = () => {
this.setState({ isDeleteDialogShow: !this.state.isDeleteDialogShow });
};
@@ -359,8 +352,8 @@ class MylibRepoListItem extends React.Component {
<td>
{(repo.repo_name && this.state.isOpIconShow) && (
<div>
<a href="#" className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onShareToggle}></a>
<a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onDeleteToggle}></a>
<i className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onShareToggle}></i>
<i className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onDeleteToggle}></i>
<MylibRepoMenu
isPC={true}
repo={this.props.repo}
@@ -413,8 +406,8 @@ class MylibRepoListItem extends React.Component {
</div>
{(repo.repo_name && this.state.isOpIconShow) && (
<div className="flex-shrink-0">
<a href="#" className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onShareToggle}></a>
<a href="#" className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onDeleteToggle}></a>
<i className="op-icon sf3-font-share sf3-font" title={gettext('Share')} role="button" aria-label={gettext('Share')} onClick={this.onShareToggle}></i>
<i className="op-icon sf3-font-delete1 sf3-font" title={gettext('Delete')} role="button" aria-label={gettext('Delete')} onClick={this.onDeleteToggle}></i>
<MylibRepoMenu
isPC={true}
repo={this.props.repo}

View File

@@ -109,9 +109,10 @@ class MylibRepoListView extends React.Component {
};
renderMobileUI = () => {
const { inAllLibs } = this.props;
return (
<table className="table-thead-hidden">
<LibsMobileThead />
<LibsMobileThead inAllLibs={inAllLibs} />
<tbody>
{this.renderRepoListView()}
</tbody>

View File

@@ -106,7 +106,7 @@ class Content extends Component {
);
const content = currentViewMode == 'list' ? (
<table className={(isDesktop && !theadHidden) ? '' : 'table-thead-hidden'}>
{isDesktop ? desktopThead : <LibsMobileThead />}
{isDesktop ? desktopThead : <LibsMobileThead inAllLibs={inAllLibs} />}
<tbody>
{itemsContent}
</tbody>

View File

@@ -145,6 +145,7 @@ class PublicSharedView extends React.Component {
onItemDelete={this.onItemDelete}
theadHidden={inAllLibs}
currentViewMode={currentViewMode}
inAllLibs={inAllLibs}
/>
}
</>

View File

@@ -421,7 +421,7 @@ class SharedDirView extends React.Component {
}
});
this.setState({ usedRepoTags: usedRepoTags });
if (usedRepoTags.length != 0 && relativePath == '/') {
if (Utils.isDesktop() && usedRepoTags.length != 0 && relativePath == '/') {
this.setState({ isRepoInfoBarShow: true });
}
}).catch(error => {

View File

@@ -36,7 +36,7 @@
{% block main_class %}d-flex ovhd{% endblock %}
{% block main_content %}
<div class="row flex-1 d-flex">
<div class="row flex-1 w-100 d-flex">
<div class="side-textnav col-md-3 side-nav help-side-nav h100">
<div class="d-block d-md-none logo-container">
<a href="{{ SITE_ROOT }}">