1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-12 13:24:52 +00:00

change markdown view in metadata (#6746)

* change markdown viewer in metadata

* fix side nav click event
This commit is contained in:
Michael An
2024-09-11 10:13:10 +08:00
committed by GitHub
parent b166c5d3c3
commit 57954854d0
9 changed files with 96 additions and 71 deletions

View File

@@ -4,4 +4,5 @@ export const EVENT_BUS_TYPE = {
SEARCH_LIBRARY_CONTENT: 'search_library_content', SEARCH_LIBRARY_CONTENT: 'search_library_content',
RESTORE_IMAGE: 'restore_image', RESTORE_IMAGE: 'restore_image',
OPEN_MARKDOWN_DIALOG: 'open_markdown_dialog',
}; };

View File

@@ -281,11 +281,12 @@ class DirColumnView extends React.Component {
} }
{this.props.isViewFile && {this.props.isViewFile &&
<MarkdownViewerDialog <MarkdownViewerDialog
path={this.props.path}
repoID={this.props.repoID} repoID={this.props.repoID}
filePath={this.props.getMarkDownFilePath()}
fileName={this.props.getMarkDownFileName()}
openMarkdownFile={this.props.openMarkdownFile}
isFileLoading={this.props.isFileLoading} isFileLoading={this.props.isFileLoading}
content={this.props.content} content={this.props.content}
currentDirent={this.props.currentDirent}
lastModified={this.props.lastModified} lastModified={this.props.lastModified}
latestContributor={this.props.latestContributor} latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick} onLinkClick={this.props.onLinkClick}

View File

@@ -1,37 +1,27 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Modal, ModalBody } from 'reactstrap'; import { Modal, ModalBody } from 'reactstrap';
import { Utils } from '../../utils/utils'; import { mediaUrl } from '../../utils/constants';
import { siteRoot, mediaUrl } from '../../utils/constants';
import SeafileMarkdownViewer from '../seafile-markdown-viewer'; import SeafileMarkdownViewer from '../seafile-markdown-viewer';
import './markdown-viewer-dialog.css'; import './markdown-viewer-dialog.css';
const propTypes = { const propTypes = {
path: PropTypes.string.isRequired, filePath: PropTypes.string.isRequired,
fileName: PropTypes.string.isRequired,
repoID: PropTypes.string.isRequired, repoID: PropTypes.string.isRequired,
isFileLoading: PropTypes.bool.isRequired, isFileLoading: PropTypes.bool.isRequired,
content: PropTypes.string, content: PropTypes.string,
lastModified: PropTypes.string, lastModified: PropTypes.string,
latestContributor: PropTypes.string, latestContributor: PropTypes.string,
onLinkClick: PropTypes.func.isRequired, onLinkClick: PropTypes.func.isRequired,
currentDirent: PropTypes.object, onCloseMarkdownViewDialog: PropTypes.func,
onCloseMarkdownViewDialog: PropTypes.func openMarkdownFile: PropTypes.func,
}; };
class MarkdownViewerDialog extends React.Component { class MarkdownViewerDialog extends React.Component {
onOpenFile = (e) => {
e.preventDefault();
let { path, repoID, currentDirent } = this.props;
let { name } = currentDirent || {};
let newUrl = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(path) + (path.endsWith('/') ? '' : '/') + name;
window.open(newUrl, '_blank');
};
render() { render() {
const { currentDirent } = this.props;
const { name } = currentDirent || {};
return ( return (
<Modal <Modal
isOpen={true} isOpen={true}
@@ -43,10 +33,10 @@ class MarkdownViewerDialog extends React.Component {
<div className='seafile-markdown-viewer-modal-header'> <div className='seafile-markdown-viewer-modal-header'>
<div className='seafile-markdown-viewer-modal-header-left-name'> <div className='seafile-markdown-viewer-modal-header-left-name'>
<span><img src={`${mediaUrl}img/file/256/md.png`} width='24' alt='' /></span> <span><img src={`${mediaUrl}img/file/256/md.png`} width='24' alt='' /></span>
<span>{name}</span> <span>{this.props.fileName}</span>
</div> </div>
<div className='seafile-markdown-viewer-modal-header-right-tool'> <div className='seafile-markdown-viewer-modal-header-right-tool'>
<span className='sf3-font sf3-font-open' onClick={this.onOpenFile}></span> <span className='sf3-font sf3-font-open' onClick={this.props.openMarkdownFile}></span>
<span className='sf3-font sf3-font-x-01' onClick={this.props.onCloseMarkdownViewDialog}></span> <span className='sf3-font sf3-font-x-01' onClick={this.props.onCloseMarkdownViewDialog}></span>
</div> </div>
</div> </div>
@@ -59,7 +49,7 @@ class MarkdownViewerDialog extends React.Component {
latestContributor={this.props.latestContributor} latestContributor={this.props.latestContributor}
onLinkClick={this.props.onLinkClick} onLinkClick={this.props.onLinkClick}
repoID={this.props.repoID} repoID={this.props.repoID}
path={this.props.path} path={this.props.filePath}
> >
</SeafileMarkdownViewer> </SeafileMarkdownViewer>
</ModalBody> </ModalBody>

View File

@@ -152,7 +152,7 @@ class MainSideNav extends React.Component {
className={`nav sub-nav nav-pills flex-column ${this.state.sharedExtended ? 'side-panel-slide-share-admin' : 'side-panel-slide-up-share-admin'}`} className={`nav sub-nav nav-pills flex-column ${this.state.sharedExtended ? 'side-panel-slide-share-admin' : 'side-panel-slide-up-share-admin'}`}
style={style} style={style}
> >
{canAddRepo && canShareRepo && ( {canAddRepo && canShareRepo && height !== 0 && (
<li className={`nav-item ${this.getActiveClass('share-admin-libs')}`}> <li className={`nav-item ${this.getActiveClass('share-admin-libs')}`}>
<Link to={siteRoot + 'share-admin-libs/'} className={`nav-link ellipsis ${this.getActiveClass('share-admin-libs')}`} title={gettext('Libraries')} onClick={(e) => this.tabItemClick(e, 'share-admin-libs')}> <Link to={siteRoot + 'share-admin-libs/'} className={`nav-link ellipsis ${this.getActiveClass('share-admin-libs')}`} title={gettext('Libraries')} onClick={(e) => this.tabItemClick(e, 'share-admin-libs')}>
<span aria-hidden="true" className="sharp">#</span> <span aria-hidden="true" className="sharp">#</span>
@@ -160,7 +160,7 @@ class MainSideNav extends React.Component {
</Link> </Link>
</li> </li>
)} )}
{canShareRepo && ( {canShareRepo && height !== 0 && (
<li className={`nav-item ${this.getActiveClass('share-admin-folders')}`}> <li className={`nav-item ${this.getActiveClass('share-admin-folders')}`}>
<Link to={siteRoot + 'share-admin-folders/'} className={`nav-link ellipsis ${this.getActiveClass('share-admin-folders')}`} title={gettext('Folders')} onClick={(e) => this.tabItemClick(e, 'share-admin-folders')}> <Link to={siteRoot + 'share-admin-folders/'} className={`nav-link ellipsis ${this.getActiveClass('share-admin-folders')}`} title={gettext('Folders')} onClick={(e) => this.tabItemClick(e, 'share-admin-folders')}>
<span aria-hidden="true" className="sharp">#</span> <span aria-hidden="true" className="sharp">#</span>
@@ -168,7 +168,7 @@ class MainSideNav extends React.Component {
</Link> </Link>
</li> </li>
)} )}
{linksNavItem} { height !== 0 && linksNavItem}
</ul> </ul>
); );
} }

View File

@@ -4,6 +4,7 @@ import { ModalPortal } from '@seafile/sf-metadata-ui-component';
import { PRIVATE_COLUMN_KEY } from '../../_basic'; import { PRIVATE_COLUMN_KEY } from '../../_basic';
import { Utils } from '../../../../utils/utils'; import { Utils } from '../../../../utils/utils';
import ImageDialog from '../../../../components/dialog/image-dialog'; import ImageDialog from '../../../../components/dialog/image-dialog';
import { EVENT_BUS_TYPE } from '../../../../components/common/event-bus-type';
import { siteRoot, thumbnailSizeForOriginal } from '../../../../utils/constants'; import { siteRoot, thumbnailSizeForOriginal } from '../../../../utils/constants';
const FileNameEditor = ({ column, record, table, onCommitCancel }) => { const FileNameEditor = ({ column, record, table, onCommitCancel }) => {
@@ -59,7 +60,7 @@ const FileNameEditor = ({ column, record, table, onCommitCancel }) => {
const suffix = fileName.slice(index).toLowerCase(); const suffix = fileName.slice(index).toLowerCase();
if (suffix.indexOf(' ') > -1) return ''; if (suffix.indexOf(' ') > -1) return '';
if (Utils.imageCheck(fileName)) return 'image'; if (Utils.imageCheck(fileName)) return 'image';
if (Utils.isMarkdownFile(fileName)) return 'file'; if (Utils.isMarkdownFile(fileName)) return 'markdown';
if (Utils.isSdocFile(fileName)) return 'sdoc'; if (Utils.isSdocFile(fileName)) return 'sdoc';
return ''; return '';
}, [_isDir, fileName]); }, [_isDir, fileName]);
@@ -92,6 +93,19 @@ const FileNameEditor = ({ column, record, table, onCommitCancel }) => {
setImageIndex((prevState) => (prevState + 1) % imageItemsLength); setImageIndex((prevState) => (prevState + 1) % imageItemsLength);
}; };
useEffect(() => {
if (fileType === 'markdown') {
const fileName = record[PRIVATE_COLUMN_KEY.FILE_NAME];
const parentDir = record[PRIVATE_COLUMN_KEY.PARENT_DIR];
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, parentDir, fileName);
}
return () => {};
}, [record, fileType]);
if (fileType === 'markdown') {
return null;
}
if (fileType === 'image') { if (fileType === 'image') {
return ( return (
<ModalPortal> <ModalPortal>

View File

@@ -8,11 +8,7 @@ import { gettext } from '../../../../../../../../utils';
import './index.css'; import './index.css';
const CellOperationBtn = ({ const CellOperationBtn = ({ isDir, column, value }) => {
isDir,
column,
value,
}) => {
const openFile = useCallback((event) => { const openFile = useCallback((event) => {
event.stopPropagation(); event.stopPropagation();
@@ -20,27 +16,23 @@ const CellOperationBtn = ({
window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER); window.sfMetadataContext.eventBus.dispatch(EVENT_BUS_TYPE.OPEN_EDITOR, EDITOR_TYPE.PREVIEWER);
}, []); }, []);
if (!value) return null; if (!value || column.type !== CellType.FILE_NAME) return null;
const { type } = column;
if (type === CellType.FILE_NAME) { return (
const target = 'sf-metadata-cell-open-file-btn'; <>
return ( <IconBtn id={'sf-metadata-cell-open-file-btn'} className="sf-metadata-cell-operation-btn" size={20} iconName="open-file" onClick={openFile} />
<> <UncontrolledTooltip
<IconBtn id={target} className="sf-metadata-cell-operation-btn" size={20} iconName="open-file" onClick={openFile} /> hideArrow
<UncontrolledTooltip target={'sf-metadata-cell-open-file-btn'}
hideArrow placement="bottom"
target={target} fade={false}
placement="bottom" delay={{ show: 0, hide: 0 }}
fade={false} modifiers={{ preventOverflow: { boundariesElement: document.body } }}
delay={{ show: 0, hide: 0 }} >
modifiers={{ preventOverflow: { boundariesElement: document.body } }} {isDir ? gettext('Open folder') : gettext('Open file')}
> </UncontrolledTooltip>
{gettext(isDir ? 'Open folder' : 'Open file')} </>
</UncontrolledTooltip> );
</>
);
}
return null;
}; };
CellOperationBtn.propTypes = { CellOperationBtn.propTypes = {

View File

@@ -1011,16 +1011,15 @@ class InteractionMasks extends React.Component {
renderSingleCellSelectView = () => { renderSingleCellSelectView = () => {
const { columns } = this.props; const { columns } = this.props;
const { const { isEditorEnabled, selectedPosition } = this.state;
isEditorEnabled,
selectedPosition,
} = this.state;
const isDragEnabled = this.isSelectedCellEditable(); const isDragEnabled = this.isSelectedCellEditable();
const canEdit = window.sfMetadataContext.canModifyRows(); const canEdit = window.sfMetadataContext.canModifyRows();
const showDragHandle = (isDragEnabled && canEdit); const showDragHandle = (isDragEnabled && canEdit);
const column = getSelectedColumn({ selectedPosition, columns }); const column = getSelectedColumn({ selectedPosition, columns });
const { type: columnType } = column || {}; const { type: columnType } = column || {};
if (isEditorEnabled && columnType !== CellType.RATE && columnType !== CellType.CHECKBOX && columnType !== CellType.FILE_NAME) return null; if (isEditorEnabled && columnType !== CellType.RATE && columnType !== CellType.CHECKBOX && columnType !== CellType.FILE_NAME) {
return null;
}
if (!this.isGridSelected()) return null; if (!this.isGridSelected()) return null;
const props = { const props = {
@@ -1030,12 +1029,7 @@ class InteractionMasks extends React.Component {
}; };
return ( return (
<SelectionMask {...props}> <SelectionMask {...props}>
{showDragHandle ? {showDragHandle && <DragHandler onDragStart={this.handleDragStart} onDragEnd={this.handleDragEnd} />}
<DragHandler
onDragStart={this.handleDragStart}
onDragEnd={this.handleDragEnd}
/>
: null}
</SelectionMask> </SelectionMask>
); );
}; };
@@ -1056,12 +1050,7 @@ class InteractionMasks extends React.Component {
rowHeight={rowHeight} rowHeight={rowHeight}
getSelectedRangeDimensions={this.getSelectedRangeDimensions} getSelectedRangeDimensions={this.getSelectedRangeDimensions}
> >
{showDragHandle ? {showDragHandle && <DragHandler onDragStart={this.handleDragStart} onDragEnd={this.handleDragEnd} />}
<DragHandler
onDragStart={this.handleDragStart}
onDragEnd={this.handleDragEnd}
/>
: null}
</SelectionRangeMask>, </SelectionRangeMask>,
<SelectionMask <SelectionMask
key="selection-mask" key="selection-mask"
@@ -1093,8 +1082,7 @@ class InteractionMasks extends React.Component {
getSelectedRangeDimensions={this.getSelectedRangeDimensions} getSelectedRangeDimensions={this.getSelectedRangeDimensions}
/> />
)} )}
{isSelectedSingleCell && this.renderSingleCellSelectView()} {isSelectedSingleCell ? this.renderSingleCellSelectView() : this.renderCellRangeSelectView()}
{!isSelectedSingleCell && this.renderCellRangeSelectView()}
{isEditorEnabled && ( {isEditorEnabled && (
<EditorPortal target={editorPortalTarget}> <EditorPortal target={editorPortalTarget}>
<EditorContainer <EditorContainer

View File

@@ -343,6 +343,9 @@ class LibContentContainer extends React.Component {
isDirentDetailShow={this.props.isDirentDetailShow} isDirentDetailShow={this.props.isDirentDetailShow}
eventBus={this.props.eventBus} eventBus={this.props.eventBus}
onCloseMarkdownViewDialog={this.props.onCloseMarkdownViewDialog} onCloseMarkdownViewDialog={this.props.onCloseMarkdownViewDialog}
getMarkDownFilePath={this.props.getMarkDownFilePath}
getMarkDownFileName={this.props.getMarkDownFileName}
openMarkdownFile={this.props.openMarkdownFile}
/> />
)} )}
{this.props.isDirentDetailShow && ( {this.props.isDirentDetailShow && (

View File

@@ -99,6 +99,9 @@ class LibContentView extends React.Component {
this.isNeedUpdateHistoryState = true; // Load, refresh page, switch mode for the first time, no need to set historyState this.isNeedUpdateHistoryState = true; // Load, refresh page, switch mode for the first time, no need to set historyState
this.currentMoveItemName = ''; this.currentMoveItemName = '';
this.currentMoveItemPath = ''; this.currentMoveItemPath = '';
this.markdownFileName = '';
this.markdownFileParentDir = '';
this.unsubscribeEventBus = null;
} }
showDirentDetail = (direntDetailPanelTab) => { showDirentDetail = (direntDetailPanelTab) => {
@@ -129,6 +132,7 @@ class LibContentView extends React.Component {
}; };
componentDidMount() { componentDidMount() {
this.unsubscribeEvent = this.props.eventBus.subscribe(EVENT_BUS_TYPE.SEARCH_LIBRARY_CONTENT, this.onSearchedClick);
this.calculatePara(this.props); this.calculatePara(this.props);
} }
@@ -139,8 +143,7 @@ class LibContentView extends React.Component {
} }
calculatePara = async (props) => { calculatePara = async (props) => {
const { repoID, eventBus } = props; const { repoID } = props;
this.unsubscribeEvent = eventBus.subscribe(EVENT_BUS_TYPE.SEARCH_LIBRARY_CONTENT, this.onSearchedClick);
const path = this.getPathFromLocation(repoID); const path = this.getPathFromLocation(repoID);
@@ -227,6 +230,7 @@ class LibContentView extends React.Component {
window.onpopstate = this.oldonpopstate; window.onpopstate = this.oldonpopstate;
collabServer.unwatchRepo(this.props.repoID, this.onRepoUpdateEvent); collabServer.unwatchRepo(this.props.repoID, this.onRepoUpdateEvent);
this.unsubscribeEvent(); this.unsubscribeEvent();
this.unsubscribeEventBus && this.unsubscribeEventBus();
this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, { this.props.eventBus.dispatch(EVENT_BUS_TYPE.CURRENT_LIBRARY_CHANGED, {
repoID: '', repoID: '',
repoName: '', repoName: '',
@@ -457,6 +461,13 @@ class LibContentView extends React.Component {
window.history.pushState({ url: url, path: path }, path, url); window.history.pushState({ url: url, path: path }, path, url);
}; };
openMarkDownDialog = (parentDir, fileName) => {
this.markdownFileParentDir = parentDir;
this.markdownFileName = fileName;
const markdownFilePath = Utils.joinPath(parentDir, fileName);
this.showFile(markdownFilePath, true);
};
showFile = (filePath, noRedirection) => { showFile = (filePath, noRedirection) => {
let repoID = this.props.repoID; let repoID = this.props.repoID;
@@ -519,6 +530,10 @@ class LibContentView extends React.Component {
path: filePath, path: filePath,
viewId: viewId, viewId: viewId,
isDirentDetailShow: false isDirentDetailShow: false
}, () => {
setTimeout(() => {
this.unsubscribeEventBus = window.sfMetadataContext.eventBus.subscribe(EVENT_BUS_TYPE.OPEN_MARKDOWN_DIALOG, this.openMarkDownDialog);
}, 1);
}); });
const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}/?view=${encodeURIComponent(viewId)}`; const url = `${siteRoot}library/${repoID}/${encodeURIComponent(repoInfo.repo_name)}/?view=${encodeURIComponent(viewId)}`;
window.history.pushState({ url: url, path: '' }, '', url); window.history.pushState({ url: url, path: '' }, '', url);
@@ -2003,9 +2018,27 @@ class LibContentView extends React.Component {
this.setState({ this.setState({
currentMode: cookie.load('seafile_view_mode') || LIST_MODE, currentMode: cookie.load('seafile_view_mode') || LIST_MODE,
}); });
this.markdownFileName = '';
this.markdownFileParentDir = '';
} }
}; };
getMarkDownFilePath = () => {
return this.markdownFileParentDir || this.state.path || '';
};
getMarkDownFileName = () => {
return this.markdownFileName || this.state.currentDirent.name || '';
};
openMarkdownFile = () => {
let { repoID } = this.props;
let path = this.getMarkDownFilePath();
let name = this.getMarkDownFileName();
let newUrl = siteRoot + 'lib/' + repoID + '/file' + Utils.encodePath(path) + (path.endsWith('/') ? '' : '/') + name;
window.open(newUrl, '_blank');
};
recalculateSelectedDirents = (unSelectNames, newDirentList) => { recalculateSelectedDirents = (unSelectNames, newDirentList) => {
let selectedDirentList = this.state.selectedDirentList.slice(0); let selectedDirentList = this.state.selectedDirentList.slice(0);
if (selectedDirentList.length > 0) { if (selectedDirentList.length > 0) {
@@ -2289,6 +2322,9 @@ class LibContentView extends React.Component {
onUploadFolder={this.onUploadFolder} onUploadFolder={this.onUploadFolder}
eventBus={this.props.eventBus} eventBus={this.props.eventBus}
onCloseMarkdownViewDialog={this.onCloseMarkdownViewDialog} onCloseMarkdownViewDialog={this.onCloseMarkdownViewDialog}
getMarkDownFilePath={this.getMarkDownFilePath}
getMarkDownFileName={this.getMarkDownFileName}
openMarkdownFile={this.openMarkdownFile}
/> />
{canUpload && this.state.pathExist && !this.state.isViewFile && ( {canUpload && this.state.pathExist && !this.state.isViewFile && (
<FileUploader <FileUploader