mirror of
https://github.com/haiwen/seahub.git
synced 2025-10-21 19:00:12 +00:00
Add edit mode for exdraw (#8312)
* update backend api code * optimize exdraw editor code * add shared edit mode * fix code wraning --------- Co-authored-by: 小强 <shuntian@Mac.lan>
This commit is contained in:
87
frontend/src/pages/excalidraw-editable-viewer/index.css
Normal file
87
frontend/src/pages/excalidraw-editable-viewer/index.css
Normal file
@@ -0,0 +1,87 @@
|
||||
#wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-wrapper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-wrapper .exdraw-editable-viewer-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
border-bottom: 1px solid #e5e6e8;
|
||||
flex-shrink: 0;
|
||||
height: 56px;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-wrapper .exdraw-editable-viewer-content {
|
||||
flex: 1;
|
||||
display: flex
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-header .doc-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-header .doc-info .doc-name {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-header .doc-ops .collaborator-name {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-header .doc-ops .sdocfont {
|
||||
font-size: 14px;
|
||||
display: inline-block;
|
||||
color: #6e7687;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-header .doc-ops .sdocfont:hover {
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.exdraw-editable-viewer-content .excali-container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.exdraw-editable-viewer-content .excali-container .excali-tip-message {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 60px;
|
||||
z-index: 2;
|
||||
color: #999;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.excalidraw .popover {
|
||||
filter: none;
|
||||
--bs-popover-border-color: transparent;
|
||||
}
|
||||
|
||||
.excalidraw .dropdown-menu {
|
||||
display: block;
|
||||
border: unset;
|
||||
--bs-dropdown-border-radius: 10px;
|
||||
--bs-dropdown-padding-x: auto;
|
||||
}
|
106
frontend/src/pages/excalidraw-editable-viewer/index.js
Normal file
106
frontend/src/pages/excalidraw-editable-viewer/index.js
Normal file
@@ -0,0 +1,106 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import context from '../excalidraw-editor/context';
|
||||
import SimpleEditor from '../excalidraw-editor/editor';
|
||||
import SocketManager from '../excalidraw-editor/socket/socket-manager';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import { gettext } from '../../utils/constants';
|
||||
import Rename from './rename';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const { avatarURL, serviceURL } = window.app.config;
|
||||
const { repoID, filePerm, docUuid, docName, docPath, exdrawServerUrl, exdrawAccessToken, name, shareLinkUsername, sharedToken } = window.shared.pageOptions;
|
||||
const userInfo = {
|
||||
name: name || '',
|
||||
username: shareLinkUsername,
|
||||
contact_email: '',
|
||||
};
|
||||
|
||||
// used for support lib
|
||||
window.name = `${docUuid}`;
|
||||
|
||||
window.excalidraw = {
|
||||
serviceURL,
|
||||
userInfo,
|
||||
avatarURL,
|
||||
repoID,
|
||||
filePerm,
|
||||
docUuid,
|
||||
docName,
|
||||
docPath,
|
||||
excalidrawServerUrl: exdrawServerUrl,
|
||||
accessToken: exdrawAccessToken,
|
||||
sharedToken: sharedToken,
|
||||
};
|
||||
|
||||
context.initSettings();
|
||||
|
||||
const updateAppIcon = () => {
|
||||
const { docName } = window.excalidraw;
|
||||
const fileIcon = Utils.getFileIconUrl(docName);
|
||||
document.getElementById('favicon').href = fileIcon;
|
||||
};
|
||||
|
||||
function ExcalidrawEdiableViewer() {
|
||||
|
||||
const canEditNameRef = useRef(name === 'Anonymous');
|
||||
const [isEditName, setIsEditName] = useState(false);
|
||||
const [username, setUsername] = useState(userInfo.name);
|
||||
|
||||
useEffect(() => {
|
||||
updateAppIcon();
|
||||
}, []);
|
||||
|
||||
const onEditNameToggle = useCallback(() => {
|
||||
setIsEditName(true);
|
||||
}, []);
|
||||
|
||||
const onRenameConfirm = useCallback((value) => {
|
||||
setUsername(value);
|
||||
const newUser = {
|
||||
...userInfo,
|
||||
_username: userInfo.username,
|
||||
username: value,
|
||||
avatarURL: avatarURL,
|
||||
};
|
||||
const socketManager = SocketManager.getInstance();
|
||||
socketManager.updateUserInfo(newUser);
|
||||
setIsEditName(false);
|
||||
}, []);
|
||||
|
||||
const onRenameCancel = useCallback(() => {
|
||||
setIsEditName(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="exdraw-editable-viewer-wrapper">
|
||||
<div className="exdraw-editable-viewer-header">
|
||||
<div className='doc-info'>
|
||||
<div className="doc-name">{docName}</div>
|
||||
</div>
|
||||
|
||||
<div className='doc-ops'>
|
||||
<span className="collaborator-name">{gettext('Username')}:</span>
|
||||
{!isEditName && (
|
||||
<span className="collaborator-name">{username}</span>
|
||||
)}
|
||||
{isEditName && (
|
||||
<Rename
|
||||
name={username}
|
||||
onRenameConfirm={onRenameConfirm}
|
||||
onRenameCancel={onRenameCancel}
|
||||
/>
|
||||
)}
|
||||
{canEditNameRef.current && (
|
||||
<i className="sdocfont sdoc-rename" onClick={onEditNameToggle}></i>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="exdraw-editable-viewer-content">
|
||||
<SimpleEditor isSharedView={true} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExcalidrawEdiableViewer;
|
@@ -0,0 +1,3 @@
|
||||
.excalidraw-rename-container {
|
||||
display: inline-block;
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import { toaster } from '@seafile/sdoc-editor';
|
||||
import PropTypes from 'prop-types';
|
||||
import './index.css';
|
||||
|
||||
const propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onRenameConfirm: PropTypes.func.isRequired,
|
||||
onRenameCancel: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
class Rename extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: props.name
|
||||
};
|
||||
this.inputRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.inputRef.current.focus();
|
||||
this.inputRef.current.setSelectionRange(0, -1);
|
||||
}
|
||||
|
||||
onChange = (e) => {
|
||||
this.setState({ name: e.target.value });
|
||||
};
|
||||
|
||||
onKeyDown = (e) => {
|
||||
if (e.keyCode === 13) {
|
||||
this.onRenameConfirm(e);
|
||||
} else if (e.keyCode === 27) {
|
||||
this.onRenameCancel(e);
|
||||
}
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
};
|
||||
|
||||
onRenameConfirm = (e) => {
|
||||
e && e.nativeEvent.stopImmediatePropagation();
|
||||
let newName = this.state.name.trim();
|
||||
if (newName === this.props.name) {
|
||||
this.props.onRenameCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
let { isValid, errMessage } = this.validateInput();
|
||||
if (!isValid) {
|
||||
toaster.danger(errMessage);
|
||||
this.props.onRenameCancel();
|
||||
} else {
|
||||
this.props.onRenameConfirm(newName);
|
||||
}
|
||||
};
|
||||
|
||||
onRenameCancel = (e) => {
|
||||
e.nativeEvent.stopImmediatePropagation();
|
||||
this.props.onRenameCancel();
|
||||
};
|
||||
|
||||
validateInput = () => {
|
||||
let newName = this.state.name.trim();
|
||||
const { t } = this.props;
|
||||
let isValid = true;
|
||||
let errMessage = '';
|
||||
if (!newName) {
|
||||
isValid = false;
|
||||
errMessage = t('Name_is_required');
|
||||
return { isValid, errMessage };
|
||||
}
|
||||
|
||||
return { isValid, errMessage };
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="excalidraw-rename-container">
|
||||
<input
|
||||
ref={this.inputRef}
|
||||
value={this.state.name}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
onBlur={this.onRenameConfirm}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Rename.propTypes = propTypes;
|
||||
|
||||
export default Rename;
|
@@ -1,13 +1,7 @@
|
||||
import Url from 'url-parse';
|
||||
import ExcalidrawServerApi from './api';
|
||||
import editorApi from './api/editor-api';
|
||||
import axios from 'axios';
|
||||
|
||||
const { avatarURL } = window.app.config;
|
||||
const { docUuid, excalidrawServerUrl, server } = window.app.pageOptions;
|
||||
const userInfo = window.app.userInfo;
|
||||
|
||||
|
||||
class Context {
|
||||
constructor() {
|
||||
this.docUuid = '';
|
||||
@@ -16,12 +10,14 @@ class Context {
|
||||
this.accessToken = '';
|
||||
}
|
||||
|
||||
initSettings = async () => {
|
||||
this.docUuid = docUuid;
|
||||
initSettings = () => {
|
||||
this.settings = window.excalidraw;
|
||||
const { serviceURL, excalidrawServerUrl, docUuid, userInfo, accessToken, avatarURL } = this.settings;
|
||||
|
||||
this.serviceURL = serviceURL;
|
||||
this.exdrawServer = excalidrawServerUrl;
|
||||
this.docUuid = docUuid;
|
||||
this.user = { ...userInfo, _username: userInfo.username, username: userInfo.name, avatarUrl: avatarURL };
|
||||
const resResult = await editorApi.getExdrawToken();
|
||||
const accessToken = resResult;
|
||||
this.accessToken = accessToken;
|
||||
this.exdrawApi = new ExcalidrawServerApi({ exdrawUuid: docUuid, exdrawServer: excalidrawServerUrl, accessToken });
|
||||
};
|
||||
@@ -30,7 +26,11 @@ class Context {
|
||||
return this.docUuid;
|
||||
};
|
||||
|
||||
getSettings = () => {
|
||||
getSetting = (key) => {
|
||||
return this.settings[key];
|
||||
};
|
||||
|
||||
getExdrawConfig = () => {
|
||||
return {
|
||||
docUuid: this.docUuid,
|
||||
exdrawServer: new Url(this.exdrawServer).origin,
|
||||
@@ -50,8 +50,8 @@ class Context {
|
||||
uploadExdrawImage = (fileUuid, fileItem) => {
|
||||
const docUuid = this.getDocUuid();
|
||||
const accessToken = this.accessToken;
|
||||
|
||||
const url = `${server}/api/v2.1/exdraw/upload-image/${docUuid}/`;
|
||||
const serviceURL = this.serviceURL;
|
||||
const url = `${serviceURL}/api/v2.1/exdraw/upload-image/${docUuid}/`;
|
||||
const form = new FormData();
|
||||
form.append('image_data', fileItem.dataURL);
|
||||
form.append('image_id', fileUuid);
|
||||
@@ -62,8 +62,8 @@ class Context {
|
||||
downloadExdrawImage = (fileUuid) => {
|
||||
const docUuid = this.getDocUuid();
|
||||
const accessToken = this.accessToken;
|
||||
|
||||
const url = `${server}/api/v2.1/exdraw/download-image/${docUuid}/${fileUuid}`;
|
||||
const serviceURL = this.serviceURL;
|
||||
const url = `${serviceURL}/api/v2.1/exdraw/download-image/${docUuid}/${fileUuid}`;
|
||||
return axios.get(url, { headers: { Authorization: `Token ${accessToken}` } });
|
||||
};
|
||||
|
||||
@@ -71,14 +71,16 @@ class Context {
|
||||
getLocalFiles(p, type) {
|
||||
const docUuid = this.getDocUuid();
|
||||
const accessToken = this.accessToken;
|
||||
const url = `${server}/api/v2.1/seadoc/dir/${docUuid}/?p=${p}&type=${type}&doc_uuid=${docUuid}`;
|
||||
const serviceURL = this.serviceURL;
|
||||
const url = `${serviceURL}/api/v2.1/seadoc/dir/${docUuid}/?p=${p}&type=${type}&doc_uuid=${docUuid}`;
|
||||
return axios.get(url, { headers: { Authorization: `Token ${accessToken}` } });
|
||||
}
|
||||
|
||||
getSearchFilesByFilename(query, page, per_page, search_type) {
|
||||
const docUuid = this.getDocUuid();
|
||||
const accessToken = this.accessToken;
|
||||
const url = server + '/api/v2.1/seadoc/search-filename/' + docUuid + '/?query=' + query + '&page=' + page + '&per_page=' + per_page + '&search_type=' + search_type;
|
||||
const serviceURL = this.serviceURL;
|
||||
const url = serviceURL + '/api/v2.1/seadoc/search-filename/' + docUuid + '/?query=' + query + '&page=' + page + '&per_page=' + per_page + '&search_type=' + search_type;
|
||||
|
||||
return axios.get(url, { headers: { Authorization: `Token ${accessToken}` } });
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import {
|
||||
import { getSyncableElements } from '.';
|
||||
import isUrl from 'is-url';
|
||||
import context from '../context';
|
||||
import { formatImageUrlFromExternalLink } from '../utils/common-utils';
|
||||
|
||||
class ServerScreenCache {
|
||||
static cache = new Map();
|
||||
@@ -103,20 +104,24 @@ export const saveFilesToServer = async (addedFiles) => {
|
||||
|
||||
const getImageUrl = (fileName) => {
|
||||
const docUuid = context.getDocUuid();
|
||||
const { server } = window.app.pageOptions;
|
||||
const url = `${server}/api/v2.1/exdraw/download-image/${docUuid}/${fileName}`;
|
||||
const serviceURL = context.getSetting('serviceURL');
|
||||
const url = `${serviceURL}/api/v2.1/exdraw/download-image/${docUuid}/${fileName}`;
|
||||
return url;
|
||||
};
|
||||
|
||||
export const loadFilesFromServer = async (elements) => {
|
||||
const loadedFiles = [];
|
||||
const erroredFiles = new Map();
|
||||
const sharedToken = context.getSetting('sharedToken');
|
||||
await Promise.all(elements.map(async (element) => {
|
||||
try {
|
||||
const { fileId, filename, dataURL } = element;
|
||||
let imageUrl = getImageUrl(filename);
|
||||
if (dataURL && isUrl(imageUrl)) {
|
||||
imageUrl = element.dataURL;
|
||||
if (sharedToken) { // from external edit mode link
|
||||
imageUrl = formatImageUrlFromExternalLink(imageUrl, sharedToken);
|
||||
}
|
||||
}
|
||||
|
||||
loadedFiles.push({
|
||||
|
@@ -18,8 +18,6 @@ import isHotkey from 'is-hotkey';
|
||||
|
||||
import '@excalidraw/excalidraw/index.css';
|
||||
|
||||
const { docUuid, filePerm } = window.app.pageOptions;
|
||||
window.name = `${docUuid}`;
|
||||
const UIOptions = {
|
||||
canvasActions: {
|
||||
saveToActiveFile: false,
|
||||
@@ -54,8 +52,9 @@ const initializeScene = async () => {
|
||||
};
|
||||
};
|
||||
|
||||
const SimpleEditor = () => {
|
||||
const SimpleEditor = ({ isSharedView = false }) => {
|
||||
|
||||
const filePermRef = useRef(null);
|
||||
const initialStatePromiseRef = useRef({ promise: null });
|
||||
if (!initialStatePromiseRef.current.promise) {
|
||||
initialStatePromiseRef.current.promise = resolvablePromise();
|
||||
@@ -113,19 +112,18 @@ const SimpleEditor = () => {
|
||||
}
|
||||
};
|
||||
|
||||
context.initSettings().then(() => {
|
||||
const config = context.getSettings();
|
||||
initializeScene().then(async (data) => {
|
||||
// init socket
|
||||
SocketManager.getInstance(excalidrawAPI, data.scene, config);
|
||||
loadImages(data, /* isInitialLoad */true);
|
||||
initialStatePromiseRef.current.promise.resolve(data.scene);
|
||||
});
|
||||
const config = context.getExdrawConfig();
|
||||
initializeScene().then(async (data) => {
|
||||
// init socket
|
||||
SocketManager.getInstance(excalidrawAPI, data.scene, config);
|
||||
loadImages(data, /* isInitialLoad */true);
|
||||
initialStatePromiseRef.current.promise.resolve(data.scene);
|
||||
});
|
||||
|
||||
}, [excalidrawAPI]);
|
||||
|
||||
useEffect(() => {
|
||||
filePermRef.current = context.getSetting('filePerm');
|
||||
const handleHotkeySave = (event) => {
|
||||
if (isHotkey('mod+s', event)) {
|
||||
// delete cmd+s
|
||||
@@ -139,7 +137,7 @@ const SimpleEditor = () => {
|
||||
}, []);
|
||||
|
||||
const handleChange = useCallback((elements, appState, files) => {
|
||||
if (filePerm === 'r') return;
|
||||
if (filePermRef.current === 'r') return;
|
||||
const socketManager = SocketManager.getInstance();
|
||||
socketManager.syncLocalElementsToOthers(elements);
|
||||
|
||||
@@ -172,7 +170,7 @@ const SimpleEditor = () => {
|
||||
}, [excalidrawAPI]);
|
||||
|
||||
const handlePointerUpdate = useCallback((payload) => {
|
||||
if (filePerm === 'r') return;
|
||||
if (filePermRef.current === 'r') return;
|
||||
const socketManager = SocketManager.getInstance();
|
||||
socketManager.syncMouseLocationToOthers(payload);
|
||||
}, []);
|
||||
@@ -229,13 +227,15 @@ const SimpleEditor = () => {
|
||||
onPointerUpdate={handlePointerUpdate}
|
||||
UIOptions={UIOptions}
|
||||
langCode={langList[window.app.config.lang] || 'en'}
|
||||
viewModeEnabled={filePerm === 'r'}
|
||||
viewModeEnabled={filePermRef.current === 'r'}
|
||||
>
|
||||
<MainMenu>
|
||||
<MainMenu.DefaultItems.SaveAsImage />
|
||||
<MainMenu.Item className='sf3-font-upload-files sf3-font' onClick={onCustomImageDialogToggle}>
|
||||
{gettext('Link image')}
|
||||
</MainMenu.Item>
|
||||
{!isSharedView && (
|
||||
<MainMenu.Item className='sf3-font-upload-files sf3-font' onClick={onCustomImageDialogToggle}>
|
||||
{gettext('Link image')}
|
||||
</MainMenu.Item>
|
||||
)}
|
||||
<MainMenu.DefaultItems.Help />
|
||||
<MainMenu.DefaultItems.ClearCanvas />
|
||||
<MainMenu.DefaultItems.ToggleTheme />
|
||||
|
@@ -1,24 +1,56 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import SimpleEditor from './editor';
|
||||
import Loading from '../../components/loading';
|
||||
import { Utils } from '../../utils/utils';
|
||||
import context from './context';
|
||||
import editorApi from './api/editor-api';
|
||||
|
||||
import './index.css';
|
||||
|
||||
const { avatarURL, serviceURL } = window.app.config;
|
||||
const { repoID, filePerm, docUuid, docName, docPath, excalidrawServerUrl } = window.app.pageOptions;
|
||||
const userInfo = window.app.userInfo;
|
||||
|
||||
// used for support lib
|
||||
window.name = `${docUuid}`;
|
||||
|
||||
window.excalidraw = {
|
||||
serviceURL,
|
||||
userInfo,
|
||||
avatarURL,
|
||||
repoID,
|
||||
filePerm,
|
||||
docUuid,
|
||||
docName,
|
||||
docPath,
|
||||
excalidrawServerUrl,
|
||||
};
|
||||
|
||||
const updateAppIcon = () => {
|
||||
const { docName } = window.app.pageOptions;
|
||||
const { docName } = window.excalidraw;
|
||||
const fileIcon = Utils.getFileIconUrl(docName);
|
||||
document.getElementById('favicon').href = fileIcon;
|
||||
};
|
||||
|
||||
const ExcaliEditor = () => {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
updateAppIcon();
|
||||
const initExcalidraw = async () => {
|
||||
const accessToken = await editorApi.getExdrawToken();
|
||||
window.excalidraw = { ...window.excalidraw, accessToken: accessToken };
|
||||
setIsLoading(false);
|
||||
updateAppIcon();
|
||||
|
||||
context.initSettings();
|
||||
};
|
||||
initExcalidraw();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="file-view-content flex-1 p-0 border-0">
|
||||
<SimpleEditor />
|
||||
{isLoading ? <Loading /> : <SimpleEditor />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -65,6 +65,15 @@ class SocketManager {
|
||||
});
|
||||
}
|
||||
|
||||
updateUserInfo = (newUser) => {
|
||||
const collaborators = new Map(this.collaborators);
|
||||
this.config.user = newUser;
|
||||
collaborators.set(newUser._username, newUser, { isCurrentUser: true });
|
||||
this.collaborators = collaborators;
|
||||
|
||||
this.excalidrawAPI.updateScene({ collaborators });
|
||||
};
|
||||
|
||||
static getInstance = (excalidrawAPI, document, socketConfig) => {
|
||||
if (this.instance) {
|
||||
return this.instance;
|
||||
@@ -254,7 +263,7 @@ class SocketManager {
|
||||
const { user, ...updates } = params;
|
||||
if (!collaborators.get(user._username)) return;
|
||||
|
||||
const newUser = Object.assign({}, collaborators.get(user._username), updates);
|
||||
const newUser = Object.assign({}, collaborators.get(user._username), { ...updates, username: user.username });
|
||||
collaborators.set(newUser._username, newUser);
|
||||
this.collaborators = collaborators;
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import context from '../context';
|
||||
|
||||
export const getErrorMsg = (error) => {
|
||||
let errorMsg = '';
|
||||
if (error.response) {
|
||||
@@ -14,3 +16,18 @@ export const getErrorMsg = (error) => {
|
||||
}
|
||||
return errorMsg;
|
||||
};
|
||||
|
||||
export const formatImageUrlFromExternalLink = (imageUrl, sharedToken) => {
|
||||
let newImageUrl = imageUrl;
|
||||
const serviceURL = context.getSetting('serviceURL');
|
||||
const repoID = context.getSetting('repoID');
|
||||
const re = new RegExp(serviceURL + '/lib/' + repoID + '/file.*raw=1');
|
||||
if (re.test(newImageUrl)) {
|
||||
// get image path
|
||||
let index = newImageUrl.indexOf('/file');
|
||||
let index2 = newImageUrl.indexOf('?');
|
||||
newImageUrl = newImageUrl.substring(index + 5, index2);
|
||||
}
|
||||
newImageUrl = serviceURL + '/view-image-via-share-link/?token=' + sharedToken + '&path=' + newImageUrl;
|
||||
return newImageUrl;
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Utils } from './utils/utils';
|
||||
import ExcaliViewer from './pages/excalidraw-viewer';
|
||||
import ExcalidrawEdiableViewer from './pages/excalidraw-editable-viewer';
|
||||
|
||||
const { siteRoot, avatarURL } = window.app.config;
|
||||
const { username } = window.app.pageOptions;
|
||||
@@ -36,4 +37,4 @@ window.seafile = {
|
||||
|
||||
|
||||
const root = createRoot(document.getElementById('wrapper'));
|
||||
root.render(<ExcaliViewer />);
|
||||
root.render(canEdit ? <ExcalidrawEdiableViewer /> : <ExcaliViewer />);
|
||||
|
@@ -177,7 +177,7 @@ export const Utils = {
|
||||
permissionOptions.push('download_upload');
|
||||
}
|
||||
} else {
|
||||
if ((this.isEditableOfficeFile(path) || this.isEditableSdocFile(path)) && (permission == 'rw' || permission == 'admin') && canEdit) {
|
||||
if ((this.isEditableOfficeFile(path) || this.isEditableSdocFile(path) || this.isEditableExdrawFile(path)) && (permission == 'rw' || permission == 'admin') && canEdit) {
|
||||
permissionOptions.push('edit_download');
|
||||
}
|
||||
|
||||
@@ -238,6 +238,20 @@ export const Utils = {
|
||||
}
|
||||
},
|
||||
|
||||
isEditableExdrawFile: function (filename) {
|
||||
// no file ext
|
||||
if (filename.lastIndexOf('.') == -1) {
|
||||
return false;
|
||||
}
|
||||
const file_ext = filename.substr(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||
|
||||
if (enableSeadoc && file_ext == 'exdraw') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
// check if a file is a video
|
||||
videoCheck: function (filename) {
|
||||
// no file ext
|
||||
|
Reference in New Issue
Block a user