mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-16 14:08:12 +00:00
add excalidraw viewer (#7719)
Co-authored-by: 小强 <shuntian@xiaoqiangdeMacBook-Pro.local>
This commit is contained in:
parent
1be01e5186
commit
2e5b2797b3
@ -25,6 +25,7 @@ const entryFiles = {
|
|||||||
sharedFileViewAudio: '/shared-file-view-audio.js',
|
sharedFileViewAudio: '/shared-file-view-audio.js',
|
||||||
sharedFileViewDocument: '/shared-file-view-document.js',
|
sharedFileViewDocument: '/shared-file-view-document.js',
|
||||||
sharedFileViewSpreadsheet: '/shared-file-view-spreadsheet.js',
|
sharedFileViewSpreadsheet: '/shared-file-view-spreadsheet.js',
|
||||||
|
sharedFileViewExdraw: '/shared-file-view-exdraw.js',
|
||||||
sharedFileViewSdoc: '/shared-file-view-sdoc.js',
|
sharedFileViewSdoc: '/shared-file-view-sdoc.js',
|
||||||
sharedFileViewUnknown: '/shared-file-view-unknown.js',
|
sharedFileViewUnknown: '/shared-file-view-unknown.js',
|
||||||
historyTrashFileView: '/history-trash-file-view.js',
|
historyTrashFileView: '/history-trash-file-view.js',
|
||||||
|
15
frontend/src/pages/excalidraw-viewer/constants.js
Normal file
15
frontend/src/pages/excalidraw-viewer/constants.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export const SAVE_INTERVAL_TIME = 3 * 60 * 1000;
|
||||||
|
|
||||||
|
export const langList = {
|
||||||
|
'zh-cn': 'zh-CN',
|
||||||
|
'en': 'en',
|
||||||
|
'zh-tw': 'zh-TW',
|
||||||
|
'ru': 'ru-RU',
|
||||||
|
'it': 'it-IT',
|
||||||
|
'fr': 'fr-FR',
|
||||||
|
'es-ms': 'en',
|
||||||
|
'es-ar': 'en',
|
||||||
|
'es': 'es-ES',
|
||||||
|
'de': 'de-DE',
|
||||||
|
'cs': 'cs-CZ',
|
||||||
|
};
|
26
frontend/src/pages/excalidraw-viewer/editor-api.js
Normal file
26
frontend/src/pages/excalidraw-viewer/editor-api.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
|
import { Utils } from '../../utils/utils';
|
||||||
|
|
||||||
|
const { repoID, filePath, fileName } = window.shared.pageOptions;
|
||||||
|
let dirPath = Utils.getDirName(filePath);
|
||||||
|
|
||||||
|
class EditorApi {
|
||||||
|
|
||||||
|
saveContent(content) {
|
||||||
|
return seafileAPI.getUpdateLink(repoID, dirPath).then((res) => {
|
||||||
|
const uploadLink = res.data;
|
||||||
|
return seafileAPI.updateFile(uploadLink, filePath, fileName, content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileContent = () => {
|
||||||
|
return seafileAPI.getFileDownloadLink(repoID, filePath).then(res => {
|
||||||
|
const downLoadUrl = res.data;
|
||||||
|
return seafileAPI.getFileContent(downLoadUrl);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const editorApi = new EditorApi();
|
||||||
|
|
||||||
|
export default editorApi;
|
8
frontend/src/pages/excalidraw-viewer/index.css
Normal file
8
frontend/src/pages/excalidraw-viewer/index.css
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.dropdown-menu {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.excalidraw .dropdown-menu {
|
||||||
|
--bs-dropdown-padding-x: auto;
|
||||||
|
border: unset;
|
||||||
|
}
|
31
frontend/src/pages/excalidraw-viewer/index.js
Normal file
31
frontend/src/pages/excalidraw-viewer/index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import SimpleViewer from './simple-viewer';
|
||||||
|
import editorApi from './editor-api';
|
||||||
|
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
const ExcaliViewer = () => {
|
||||||
|
const [fileContent, setFileContent] = useState(null);
|
||||||
|
const [isFetching, setIsFetching] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
editorApi.getFileContent().then(res => {
|
||||||
|
if (res.data?.appState?.collaborators && !Array.isArray(res.data.appState.collaborators)) {
|
||||||
|
// collaborators.forEach is not a function
|
||||||
|
res.data['appState']['collaborators'] = [];
|
||||||
|
}
|
||||||
|
setFileContent(res.data);
|
||||||
|
setIsFetching(false);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SimpleViewer
|
||||||
|
isFetching={isFetching}
|
||||||
|
sceneContent={fileContent}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExcaliViewer;
|
50
frontend/src/pages/excalidraw-viewer/simple-viewer.js
Normal file
50
frontend/src/pages/excalidraw-viewer/simple-viewer.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Excalidraw, MainMenu } from '@excalidraw/excalidraw';
|
||||||
|
import CodeMirrorLoading from '../../components/code-mirror-loading';
|
||||||
|
import { langList } from './constants';
|
||||||
|
|
||||||
|
import '@excalidraw/excalidraw/index.css';
|
||||||
|
|
||||||
|
const SimpleViewer = ({ sceneContent = null, isFetching }) => {
|
||||||
|
const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
||||||
|
const UIOptions = {
|
||||||
|
canvasActions: {
|
||||||
|
saveToActiveFile: false,
|
||||||
|
LoadScene: false
|
||||||
|
},
|
||||||
|
tools: { image: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return (
|
||||||
|
<div className='excali-container'>
|
||||||
|
<CodeMirrorLoading />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='excali-container' style={{ height: '100vh', width: '100vw' }}>
|
||||||
|
<Excalidraw
|
||||||
|
initialData={sceneContent}
|
||||||
|
excalidrawAPI={(api) => setExcalidrawAPI(api)}
|
||||||
|
UIOptions={UIOptions}
|
||||||
|
langCode={langList[window.app.config.lang] || 'en'}
|
||||||
|
viewModeEnabled={true}
|
||||||
|
>
|
||||||
|
<MainMenu>
|
||||||
|
<MainMenu.DefaultItems.Export />
|
||||||
|
<MainMenu.DefaultItems.SaveAsImage />
|
||||||
|
<MainMenu.DefaultItems.Help />
|
||||||
|
<MainMenu.DefaultItems.ClearCanvas />
|
||||||
|
<MainMenu.DefaultItems.ToggleTheme />
|
||||||
|
<MainMenu.DefaultItems.ChangeCanvasBackground />
|
||||||
|
</MainMenu>
|
||||||
|
</Excalidraw>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SimpleViewer;
|
39
frontend/src/shared-file-view-exdraw.js
Normal file
39
frontend/src/shared-file-view-exdraw.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import { Utils } from './utils/utils';
|
||||||
|
import ExcaliViewer from './pages/excalidraw-viewer';
|
||||||
|
|
||||||
|
const { siteRoot, avatarURL } = window.app.config;
|
||||||
|
const { username } = window.app.pageOptions;
|
||||||
|
const {
|
||||||
|
repoID,
|
||||||
|
canDownload,
|
||||||
|
canEdit,
|
||||||
|
fileName,
|
||||||
|
assetsUrl,
|
||||||
|
sharedFileDownloadURL,
|
||||||
|
} = window.shared.pageOptions;
|
||||||
|
|
||||||
|
// share permission of this sdoc
|
||||||
|
const sharePermission = { 'can_edit': canEdit, 'can_download': canDownload, 'can_upload': false };
|
||||||
|
const sharePermissionStr = Utils.getShareLinkPermissionStr(sharePermission);
|
||||||
|
const sharePermissionText = Utils.getShareLinkPermissionObject(sharePermissionStr).text;
|
||||||
|
|
||||||
|
window.seafile = {
|
||||||
|
repoID,
|
||||||
|
username,
|
||||||
|
avatarURL,
|
||||||
|
siteRoot,
|
||||||
|
sharePermissionText: sharePermissionText,
|
||||||
|
downloadURL: sharedFileDownloadURL,
|
||||||
|
assetsUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
const fileIcon = Utils.getFileIconUrl(fileName);
|
||||||
|
document.getElementById('favicon').href = fileIcon;
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
const root = createRoot(document.getElementById('wrapper'));
|
||||||
|
root.render(<ExcaliViewer />);
|
@ -5,18 +5,18 @@
|
|||||||
{% block sub_title %}{{file_name}} - {% endblock %}
|
{% block sub_title %}{{file_name}} - {% endblock %}
|
||||||
|
|
||||||
{% block extra_ogp_tags %}
|
{% block extra_ogp_tags %}
|
||||||
<meta property="og:type" content="object" />
|
<meta property="og:type" content="object">
|
||||||
<meta property="og:site_name" content="{{ site_name }}">
|
<meta property="og:site_name" content="{{ site_name }}">
|
||||||
<meta property="og:url" content="{{ service_url }}{{ file_share_link }}" />
|
<meta property="og:url" content="{{ service_url }}{{ file_share_link }}">
|
||||||
<meta property="og:title" content="{{ file_name }}" />
|
<meta property="og:title" content="{{ file_name }}">
|
||||||
<meta property="og:image" content="{{ service_url }}{{ MEDIA_URL }}img/file/{{ icon_path_for_ogp }}" />
|
<meta property="og:image" content="{{ service_url }}{{ MEDIA_URL }}img/file/{{ icon_path_for_ogp }}">
|
||||||
<meta property="og:description" content="{{ desc_for_ogp }}" />
|
<meta property="og:description" content="{{ desc_for_ogp }}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_style %}
|
{% block extra_style %}
|
||||||
{% if filetype == 'Markdown' %}
|
{% if filetype == 'Markdown' %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}seafile-editor/seafile-editor-font.css" />
|
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}seafile-editor/seafile-editor-font.css">
|
||||||
{% render_bundle 'sharedFileViewMarkdown' 'css' %}
|
{% render_bundle 'sharedFileViewMarkdown' 'css' %}
|
||||||
{% elif filetype == 'Text' %}
|
{% elif filetype == 'Text' %}
|
||||||
{% render_bundle 'sharedFileViewText' 'css' %}
|
{% render_bundle 'sharedFileViewText' 'css' %}
|
||||||
{% elif filetype == 'Image' %}
|
{% elif filetype == 'Image' %}
|
||||||
@ -28,20 +28,22 @@
|
|||||||
{% elif filetype == 'Audio' %}
|
{% elif filetype == 'Audio' %}
|
||||||
{% render_bundle 'sharedFileViewAudio' 'css' %}
|
{% render_bundle 'sharedFileViewAudio' 'css' %}
|
||||||
{% elif filetype == 'PDF' %}
|
{% elif filetype == 'PDF' %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}js/pdf/web/viewer.css" />
|
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}js/pdf/web/viewer.css">
|
||||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}js/pdf/web/custom.css" />
|
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}js/pdf/web/custom.css">
|
||||||
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties" />
|
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties">
|
||||||
{% render_bundle 'sharedFileViewPDF' 'css' %}
|
{% render_bundle 'sharedFileViewPDF' 'css' %}
|
||||||
{% elif filetype == 'Document' %}
|
{% elif filetype == 'Document' %}
|
||||||
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties" />
|
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties">
|
||||||
{% render_bundle 'sharedFileViewDocument' 'css' %}
|
{% render_bundle 'sharedFileViewDocument' 'css' %}
|
||||||
{% elif filetype == 'SpreadSheet' %}
|
{% elif filetype == 'SpreadSheet' %}
|
||||||
{% render_bundle 'sharedFileViewSpreadsheet' 'css' %}
|
{% render_bundle 'sharedFileViewSpreadsheet' 'css' %}
|
||||||
{% elif filetype == 'SDoc' %}
|
{% elif filetype == 'SDoc' %}
|
||||||
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}sdoc-editor/sdoc-editor-font.css" />
|
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}sdoc-editor/sdoc-editor-font.css">
|
||||||
{% render_bundle 'sharedFileViewSdoc' 'css' %}
|
{% render_bundle 'sharedFileViewSdoc' 'css' %}
|
||||||
{% elif filetype == 'Unknown' %}
|
{% elif filetype == 'Unknown' %}
|
||||||
{% render_bundle 'sharedFileViewUnknown' 'css' %}
|
{% render_bundle 'sharedFileViewUnknown' 'css' %}
|
||||||
|
{% elif filetype == 'Excalidraw' %}
|
||||||
|
{% render_bundle 'sharedFileViewExdraw' 'css' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not permissions.can_download %}
|
{% if not permissions.can_download %}
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@ -81,7 +83,7 @@ body {
|
|||||||
fileExt: '{{ fileext }}',
|
fileExt: '{{ fileext }}',
|
||||||
commitID: '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}',
|
commitID: '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}',
|
||||||
enableShareLinkReportAbuse: {% if enable_share_link_report_abuse %}true{% else %}false{% endif %},
|
enableShareLinkReportAbuse: {% if enable_share_link_report_abuse %}true{% else %}false{% endif %},
|
||||||
|
|
||||||
// for 'view file in shared dir'
|
// for 'view file in shared dir'
|
||||||
{% if zipped %}
|
{% if zipped %}
|
||||||
zipped: (function() {
|
zipped: (function() {
|
||||||
@ -127,25 +129,27 @@ body {
|
|||||||
{% render_bundle 'sharedFileViewAudio' 'js' %}
|
{% render_bundle 'sharedFileViewAudio' 'js' %}
|
||||||
{% elif filetype == 'PDF' %}
|
{% elif filetype == 'PDF' %}
|
||||||
{% render_bundle 'sharedFileViewPDF' 'js' %}
|
{% render_bundle 'sharedFileViewPDF' 'js' %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var SEAFILE_FILE_URL = '{{ raw_path|escapejs }}';
|
var SEAFILE_FILE_URL = '{{ raw_path|escapejs }}';
|
||||||
var SEAFILE_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
|
var SEAFILE_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
|
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
|
||||||
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/web/viewer.js"></script>
|
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/web/viewer.js"></script>
|
||||||
{% elif filetype == 'Document' %}
|
{% elif filetype == 'Document' %}
|
||||||
{% render_bundle 'sharedFileViewDocument' 'js' %}
|
{% render_bundle 'sharedFileViewDocument' 'js' %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var commit_id = '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}';
|
var commit_id = '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}';
|
||||||
var SEAFILE_FILE_URL = '{{ SITE_ROOT }}office-convert/static/{{ repo.id }}/' + commit_id + '{{ path|urlencode }}/fake.pdf?token={{shared_token}}';
|
var SEAFILE_FILE_URL = '{{ SITE_ROOT }}office-convert/static/{{ repo.id }}/' + commit_id + '{{ path|urlencode }}/fake.pdf?token={{shared_token}}';
|
||||||
var SEAFILE_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
|
var SEAFILE_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
|
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
|
||||||
{% elif filetype == 'SpreadSheet' %}
|
{% elif filetype == 'SpreadSheet' %}
|
||||||
{% render_bundle 'sharedFileViewSpreadsheet' 'js' %}
|
{% render_bundle 'sharedFileViewSpreadsheet' 'js' %}
|
||||||
{% elif filetype == 'SDoc' %}
|
{% elif filetype == 'SDoc' %}
|
||||||
{% render_bundle 'sharedFileViewSdoc' 'js' %}
|
{% render_bundle 'sharedFileViewSdoc' 'js' %}
|
||||||
{% elif filetype == 'Unknown' %}
|
{% elif filetype == 'Unknown' %}
|
||||||
{% render_bundle 'sharedFileViewUnknown' 'js' %}
|
{% render_bundle 'sharedFileViewUnknown' 'js' %}
|
||||||
|
{% elif filetype == 'Excalidraw' %}
|
||||||
|
{% render_bundle 'sharedFileViewExdraw' 'js' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user