1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-04-28 03:10:45 +00:00

add excalidraw viewer (#7719)

Co-authored-by: 小强 <shuntian@xiaoqiangdeMacBook-Pro.local>
This commit is contained in:
杨顺强 2025-04-09 11:16:01 +08:00 committed by GitHub
parent 1be01e5186
commit 2e5b2797b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 197 additions and 23 deletions

View File

@ -25,6 +25,7 @@ const entryFiles = {
sharedFileViewAudio: '/shared-file-view-audio.js',
sharedFileViewDocument: '/shared-file-view-document.js',
sharedFileViewSpreadsheet: '/shared-file-view-spreadsheet.js',
sharedFileViewExdraw: '/shared-file-view-exdraw.js',
sharedFileViewSdoc: '/shared-file-view-sdoc.js',
sharedFileViewUnknown: '/shared-file-view-unknown.js',
historyTrashFileView: '/history-trash-file-view.js',

View 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',
};

View 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;

View File

@ -0,0 +1,8 @@
.dropdown-menu {
display: block !important;
}
.excalidraw .dropdown-menu {
--bs-dropdown-padding-x: auto;
border: unset;
}

View 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;

View 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;

View 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 />);

View File

@ -5,18 +5,18 @@
{% block sub_title %}{{file_name}} - {% endblock %}
{% 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:url" content="{{ service_url }}{{ file_share_link }}" />
<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:description" content="{{ desc_for_ogp }}" />
<meta property="og:url" content="{{ service_url }}{{ file_share_link }}">
<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:description" content="{{ desc_for_ogp }}">
{% endblock %}
{% block extra_style %}
{% if filetype == 'Markdown' %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}seafile-editor/seafile-editor-font.css" />
{% render_bundle 'sharedFileViewMarkdown' 'css' %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}seafile-editor/seafile-editor-font.css">
{% render_bundle 'sharedFileViewMarkdown' 'css' %}
{% elif filetype == 'Text' %}
{% render_bundle 'sharedFileViewText' 'css' %}
{% elif filetype == 'Image' %}
@ -28,20 +28,22 @@
{% elif filetype == 'Audio' %}
{% render_bundle 'sharedFileViewAudio' 'css' %}
{% 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/custom.css" />
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties" />
{% render_bundle 'sharedFileViewPDF' '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="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties">
{% render_bundle 'sharedFileViewPDF' 'css' %}
{% elif filetype == 'Document' %}
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties" />
{% render_bundle 'sharedFileViewDocument' 'css' %}
<link rel="resource" type="application/l10n" href="{{ MEDIA_URL }}js/pdf/web/locale/locale.properties">
{% render_bundle 'sharedFileViewDocument' 'css' %}
{% elif filetype == 'SpreadSheet' %}
{% render_bundle 'sharedFileViewSpreadsheet' 'css' %}
{% elif filetype == 'SDoc' %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}sdoc-editor/sdoc-editor-font.css" />
{% render_bundle 'sharedFileViewSdoc' 'css' %}
<link rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}sdoc-editor/sdoc-editor-font.css">
{% render_bundle 'sharedFileViewSdoc' 'css' %}
{% elif filetype == 'Unknown' %}
{% render_bundle 'sharedFileViewUnknown' 'css' %}
{% elif filetype == 'Excalidraw' %}
{% render_bundle 'sharedFileViewExdraw' 'css' %}
{% endif %}
{% if not permissions.can_download %}
<style type="text/css">
@ -81,7 +83,7 @@ body {
fileExt: '{{ fileext }}',
commitID: '{{ current_commit.id }}' || '{{ repo.head_cmmt_id }}',
enableShareLinkReportAbuse: {% if enable_share_link_report_abuse %}true{% else %}false{% endif %},
// for 'view file in shared dir'
{% if zipped %}
zipped: (function() {
@ -127,25 +129,27 @@ body {
{% render_bundle 'sharedFileViewAudio' 'js' %}
{% elif filetype == 'PDF' %}
{% render_bundle 'sharedFileViewPDF' 'js' %}
<script type="text/javascript">
<script type="text/javascript">
var SEAFILE_FILE_URL = '{{ raw_path|escapejs }}';
var SEAFILE_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
</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>
<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>
{% elif filetype == 'Document' %}
{% render_bundle 'sharedFileViewDocument' 'js' %}
<script type="text/javascript">
<script type="text/javascript">
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_PDFJS_DIR = '{{MEDIA_URL}}js/pdf';
</script>
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
</script>
<script type="text/javascript" src="{{MEDIA_URL}}js/pdf/build/pdf.js"></script>
{% elif filetype == 'SpreadSheet' %}
{% render_bundle 'sharedFileViewSpreadsheet' 'js' %}
{% elif filetype == 'SDoc' %}
{% render_bundle 'sharedFileViewSdoc' 'js' %}
{% elif filetype == 'Unknown' %}
{% render_bundle 'sharedFileViewUnknown' 'js' %}
{% elif filetype == 'Excalidraw' %}
{% render_bundle 'sharedFileViewExdraw' 'js' %}
{% endif %}
{% endblock %}