forked from Gitlink/forgeplus-react
284 lines
9.8 KiB
JavaScript
284 lines
9.8 KiB
JavaScript
import React, { Fragment, useState, useMemo, useEffect, useRef } from 'react';
|
||
import { Switch ,Checkbox ,Tooltip,Popover,Radio,Icon} from 'antd';
|
||
import { EditorRenderer } from 'alex';
|
||
import { useLocalStorageState } from 'ahooks';
|
||
import html from './extensions/alex-ext-public.html-language-features-worker.js';
|
||
import css from './extensions/alex-ext-public.css-language-features-worker.js';
|
||
import json from './extensions/alex-ext-public.typescript-language-features-worker.js';
|
||
import typescript from './extensions/alex-ext-public.json-language-features-worker.js';
|
||
import markdown from './extensions/alex-ext-public.markdown-language-features-worker.js';
|
||
import ideTheme from './extensions/alex-ext-public.ide-dark-theme.js';
|
||
import CodeBlame from './extensions/alex-ext-public.editor-plugin-blame.js';
|
||
import { getUrl, returnbar } from 'educoder';
|
||
import changeThemePlugin from './changeTheme';
|
||
import IDEPlugin, { ExtensionCommand } from './plugins/idePlugin';
|
||
// import { FileOperationArea } from './components/file-operation-area';
|
||
import { repoService } from './codeReview/mock/repo.service';
|
||
|
||
|
||
function CloudIDE({ download_url, params: { owner, projectsId, branchName }, filepath , value }) {
|
||
function FileContentStore() {
|
||
const [loading, setLoading] = useState(true);
|
||
const [showIde, setShowIde] = useState(false);
|
||
const [fileSize, setFileSize] = useState('0');
|
||
const [fileName, setFileName] = useState('');
|
||
|
||
return {
|
||
loading,
|
||
setLoading,
|
||
showIde,
|
||
setShowIde,
|
||
fileSize,
|
||
setFileSize,
|
||
fileName,
|
||
setFileName,
|
||
};
|
||
}
|
||
|
||
const { fileSize, fileName } = FileContentStore();
|
||
const fileCache = new Map();
|
||
|
||
const copyFile = () => {
|
||
console.log('复制文件内容');
|
||
};
|
||
|
||
const [settingTipVisible, setSettingTipVisible] = useLocalStorageState(
|
||
'code.file.operation.tip',
|
||
true
|
||
);
|
||
|
||
const [lines, setLines] = useState(0);
|
||
const [sloc, setSloc] = useState(0);
|
||
const [encoding, setEncoding] = useState('UTF-8');
|
||
// const [showMdPreview, setShowPreview] = useState(false);
|
||
const [fileString, setFileString] = useState('');
|
||
const curFilepath = useMemo(() => {
|
||
// 仓库默认页是没有path的,默认解析到readme
|
||
// 当不需要加载文件时,返回空
|
||
// const needLoadFile = treeEntry
|
||
// ? treeEntry.render === FileShowType.text &&
|
||
// treeEntry.size < ReadFileSizeLimit
|
||
// : !!fileName;
|
||
// return needLoadFile ? path || fileName : '';
|
||
}, [branchName, fileName]);
|
||
|
||
const [blameChecked, setBlameChecked] = useState(false);
|
||
// 组件卸载时,清空缓存
|
||
// useUnmount(() => {
|
||
// fileCache.clear()
|
||
// })
|
||
// 由于 editor 组件内部有缓存,已经加载过的文件不会再次请求 readFile 方法
|
||
// 所以当已经加载过该文件时,直接读取文件内容用于展示
|
||
// 暂时前端缓存,文件体积有过大保护,内存溢出风险较低,暂时不做过多设计,待后续接入editor api
|
||
useEffect(() => {
|
||
setBlameChecked(false);
|
||
const fileKey = `${branchName}-${curFilepath}`;
|
||
const fileInfo = fileCache.get(fileKey);
|
||
if (fileInfo) {
|
||
setFileString(fileInfo.fileString);
|
||
setLines(fileInfo.lines);
|
||
setSloc(fileInfo.sloc);
|
||
}
|
||
}, [curFilepath]);
|
||
// 插件
|
||
const [pluginActivated, setPluginActivated] = useState(false);
|
||
const plugin = useRef(
|
||
new IDEPlugin(
|
||
() => setPluginActivated(true),
|
||
(commitId) => window.open(`/${owner}/${projectsId}/commits/${commitId}`)
|
||
)
|
||
);
|
||
// blame
|
||
const onBlame = async (e) => {
|
||
setBlameChecked(e.target.checked);
|
||
if (!e.target.checked ) {
|
||
plugin.current.commands && plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame);
|
||
} else {
|
||
const res = await repoService.getCodeBlame(projectsId, owner, { sha:decodeURIComponent(branchName), filepath })
|
||
plugin.current.commands && plugin.current.commands.executeCommand(ExtensionCommand.toggleBlame, res);
|
||
}
|
||
};
|
||
const [wordWrap, setWordWrap] = useState(false);
|
||
const onWordWrapChange = (value) => {
|
||
const preference = value ? 'on' : 'off';
|
||
localStorage.setItem('code.file.viewer.worlWrap', preference);
|
||
plugin.current.setPerference('editor.wordWrap', preference, true);
|
||
setWordWrap(value);
|
||
};
|
||
|
||
async function changeStyle(v) {
|
||
const commands = changeThemePlugin.commands;
|
||
if (commands) {
|
||
await commands.executeCommand(
|
||
'ChangeThemePlugin.changeTheme',
|
||
v ? 'opensumi-light' : 'opensumi-dark'
|
||
);
|
||
}
|
||
}
|
||
|
||
const content = (
|
||
<Fragment>
|
||
<div className="flex align-center">
|
||
<span className="c-65">{'编码方式'}:</span>
|
||
<span>
|
||
<Radio.Group
|
||
onChange={(e) => {
|
||
setEncoding(e.target.value);
|
||
}}
|
||
value={encoding}
|
||
size="small"
|
||
>
|
||
<Radio value="UTF-8">UTF-8</Radio>
|
||
<Radio value="GBK">GBK</Radio>
|
||
</Radio.Group>
|
||
</span>
|
||
</div>
|
||
<div className="flex align-center">
|
||
<span className="c-65">{'自动换行'}:</span>
|
||
<span>
|
||
<Radio.Group
|
||
onChange={(e) => {
|
||
onWordWrapChange(e.target.value);
|
||
}}
|
||
value={wordWrap}
|
||
size="small"
|
||
>
|
||
<Radio value={false}>{'不自动换行'}</Radio>
|
||
<Radio value={true}>{'自动换行'}</Radio>
|
||
</Radio.Group>
|
||
</span>
|
||
</div>
|
||
</Fragment>
|
||
);
|
||
|
||
return (
|
||
<Fragment>
|
||
<div className="ide-tool-bar">
|
||
{onBlame ? (
|
||
<Checkbox onChange={onBlame} checked={!!blameChecked}>
|
||
Blame
|
||
</Checkbox>
|
||
) : null}
|
||
<Switch checkedChildren="light" unCheckedChildren="dark" defaultChecked onChange={changeStyle}></Switch>
|
||
|
||
<Tooltip
|
||
title={'更改阅读设置'}
|
||
defaultVisible={settingTipVisible}
|
||
onVisibleChange={setSettingTipVisible}
|
||
>
|
||
<Popover
|
||
content={content}
|
||
title={null}
|
||
trigger="click"
|
||
placement="bottomRight"
|
||
arrowPointAtCenter={true}
|
||
>
|
||
{/* <Icon type="ellipsis" /> */}
|
||
<Icon className="read-more" type="read" />
|
||
</Popover>
|
||
</Tooltip>
|
||
</div>
|
||
{/* <FileOperationArea
|
||
lines={lines}
|
||
sloc={sloc}
|
||
size={fileSize}
|
||
refName={branchName}
|
||
path={curFilepath}
|
||
encoding={encoding}
|
||
setEncoding={setEncoding}
|
||
onFileCopy={copyFile}
|
||
blameChecked={blameChecked}
|
||
onBlame={pluginActivated && onBlame}
|
||
wordWrap={wordWrap}
|
||
setWordWrap={onWordWrapChange}
|
||
/> */}
|
||
{
|
||
value ?
|
||
<EditorRenderer
|
||
appConfig={{
|
||
// 工作空间目录
|
||
plugins: [changeThemePlugin, plugin.current],
|
||
workspaceDir: `${owner}/${projectsId}`,
|
||
defaultPreferences: {
|
||
// 默认主题,如果暗色为 opensumi-dark
|
||
// 'general.theme': 'opensumi-light',
|
||
'general.theme': 'opensumi-light',
|
||
// 编辑器只读
|
||
'editor.forceReadOnly': true,
|
||
// 最后一行禁止继续滚动
|
||
'editor.scrollBeyondLastLine': false,
|
||
},
|
||
extensionMetadata: [
|
||
html,
|
||
css,
|
||
json,
|
||
typescript,
|
||
markdown,
|
||
ideTheme,
|
||
CodeBlame],
|
||
}}
|
||
runtimeConfig={{
|
||
// 业务标识
|
||
biz: 'gitlink',
|
||
// 在 editor 下推荐传 null,此时不会持久化缓存工作空间数据
|
||
scenario: null,
|
||
// 启动时显示的编辑器,editor 下传 none 即可
|
||
startupEditor: 'none',
|
||
// 隐藏 editor tab
|
||
hideEditorTab: true,
|
||
}}
|
||
editorConfig={{
|
||
stretchHeight: true,
|
||
disableEditorSearch: true,
|
||
}}
|
||
// 文档模型,以下数据变更时会更新打开的编辑器
|
||
documentModel={{
|
||
// 类型,code 下为 code,保持不变
|
||
type: 'code',
|
||
// 分支或 tag
|
||
ref: branchName,
|
||
// 仓库群组和用户
|
||
owner: owner,
|
||
// 仓库名
|
||
name: projectsId,
|
||
// 仓库文件路径
|
||
filepath: filepath,
|
||
// 读取文件接口
|
||
readFile: async (filepath) => {
|
||
if (!filepath) {
|
||
return;
|
||
}
|
||
// 请求接口
|
||
// const res = await fetch('https://gist.githubusercontent.com/gaearon/0b180827c190fe4fd98b4c7f570ea4a8/raw/b9157ce933c79a4559d2aa9ff3372668cce48de7/LikeButton.js');
|
||
const res = await fetch(`${getUrl()}/attachments/entries/get_file?download_url=${download_url}`);
|
||
setTimeout(()=>{
|
||
onWordWrapChange(localStorage.getItem('code.file.viewer.worlWrap') === 'on');
|
||
},5000)
|
||
return res.arrayBuffer();
|
||
},
|
||
// 文件编码,和标准工作空间支持编码类型一致,简单场景只传 utf8 或 gbk 即可
|
||
// encoding: 'utf8',
|
||
encoding,
|
||
onFilepathChange: (changedFilepath) => {
|
||
if (changedFilepath) {
|
||
console.log(`路由跳转到 ${changedFilepath}`)
|
||
}
|
||
},
|
||
// 指定当前选中多少行
|
||
// lineNumber: [0, 1],
|
||
onLineNumberChange: line => {
|
||
if (typeof line === 'number') {
|
||
console.log(`#L${line}`)
|
||
} else {
|
||
console.log(`#L${line[0]}-${line[1]}`)
|
||
}
|
||
},
|
||
}}
|
||
/>:
|
||
<div className="pl20">空文件</div>
|
||
}
|
||
|
||
</Fragment>
|
||
)
|
||
}
|
||
export default CloudIDE; |