2024-02-23 19:17:12 +08:00
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
|
|
|
import { useLocalStorage } from "react-use";
|
2024-01-18 15:46:18 +08:00
|
|
|
|
|
|
|
|
|
import { Reference } from "@/utils/global";
|
2024-01-19 15:36:41 +08:00
|
|
|
|
import {
|
|
|
|
|
copyToClipboard,
|
2024-02-15 20:13:26 +08:00
|
|
|
|
getFullReference,
|
2024-02-23 19:17:12 +08:00
|
|
|
|
renderCitation,
|
2024-02-15 20:13:26 +08:00
|
|
|
|
getAllFullReferences,
|
2024-01-20 11:13:12 +08:00
|
|
|
|
delteIndexUpdateBracketNumbersInDeltaKeepSelection,
|
2024-01-19 15:36:41 +08:00
|
|
|
|
} from "@/utils/others/quillutils";
|
2024-02-02 22:59:21 +08:00
|
|
|
|
//删除文献按钮
|
|
|
|
|
import ParagraphDeleteButton from "@/components/ParagraphDeleteInterface";
|
|
|
|
|
|
2024-01-23 10:54:12 +08:00
|
|
|
|
//redux
|
|
|
|
|
import { useAppDispatch, useAppSelector } from "@/app/store";
|
|
|
|
|
import {
|
|
|
|
|
addReferenceRedux,
|
|
|
|
|
removeReferenceRedux,
|
|
|
|
|
clearReferencesRedux,
|
2024-01-29 15:01:43 +08:00
|
|
|
|
swapReferencesRedux,
|
2024-02-23 19:17:12 +08:00
|
|
|
|
setReferencesRedux,
|
2024-01-23 10:54:12 +08:00
|
|
|
|
} from "@/app/store/slices/authSlice";
|
2024-02-23 19:17:12 +08:00
|
|
|
|
import { setCitationStyle } from "@/app/store/slices/stateSlice";
|
2024-02-09 23:01:05 +08:00
|
|
|
|
//supabase
|
|
|
|
|
import { submitPaper } from "@/utils/supabase/supabaseutils";
|
|
|
|
|
import { createClient } from "@/utils/supabase/client";
|
2024-02-12 20:55:14 +08:00
|
|
|
|
//i18n
|
|
|
|
|
import { useTranslation } from "@/app/i18n/client";
|
2024-01-18 15:46:18 +08:00
|
|
|
|
type ReferenceListProps = {
|
2024-01-20 11:13:12 +08:00
|
|
|
|
editor: any;
|
2024-02-12 20:55:14 +08:00
|
|
|
|
lng: string;
|
2024-01-18 15:46:18 +08:00
|
|
|
|
};
|
2024-02-23 19:17:12 +08:00
|
|
|
|
//引用转换
|
|
|
|
|
import Cite from "citation-js";
|
2024-01-18 15:46:18 +08:00
|
|
|
|
|
2024-02-23 19:17:12 +08:00
|
|
|
|
const citationStyles = [
|
|
|
|
|
{ name: "中文", template: "custom-chinese" }, // 假设你有一个自定义的“中文”格式
|
|
|
|
|
{ name: "APA", template: "apa" },
|
|
|
|
|
{ name: "MLA", template: "mla" },
|
|
|
|
|
{ name: "Chicago", template: "chicago" },
|
|
|
|
|
{ name: "Harvard", template: "harvard" },
|
|
|
|
|
{ name: "Vancouver", template: "vancouver" },
|
|
|
|
|
{ name: "IEEE", template: "ieee" },
|
|
|
|
|
];
|
2024-02-12 20:55:14 +08:00
|
|
|
|
function ReferenceList({ editor, lng }: ReferenceListProps) {
|
|
|
|
|
//i18n
|
|
|
|
|
const { t } = useTranslation(lng);
|
2024-02-23 19:17:12 +08:00
|
|
|
|
//自定义文献
|
2024-01-18 15:46:18 +08:00
|
|
|
|
const [newTitle, setNewTitle] = useState("");
|
|
|
|
|
const [newAuthor, setNewAuthor] = useState("");
|
2024-01-20 13:43:31 +08:00
|
|
|
|
const [newYear, setNewYear] = useState("");
|
2024-01-18 15:46:18 +08:00
|
|
|
|
const [newPublisher, setNewPublisher] = useState("");
|
|
|
|
|
const [newUrl, setNewUrl] = useState("");
|
2024-01-23 10:54:12 +08:00
|
|
|
|
//redux
|
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
|
const references = useAppSelector((state) => state.auth.referencesRedux);
|
2024-02-09 23:01:05 +08:00
|
|
|
|
const paperNumberRedux = useAppSelector(
|
|
|
|
|
(state) => state.state.paperNumberRedux
|
|
|
|
|
);
|
2024-02-10 13:26:03 +08:00
|
|
|
|
const isVip = useAppSelector((state) => state.state.isVip);
|
2024-02-23 19:17:12 +08:00
|
|
|
|
const citationStyle = useAppSelector((state) => state.state.citationStyle);
|
2024-02-09 23:01:05 +08:00
|
|
|
|
//supabase
|
|
|
|
|
const supabase = createClient();
|
2024-01-20 11:13:12 +08:00
|
|
|
|
|
|
|
|
|
function moveReferenceUp(index: number) {
|
2024-01-29 15:01:43 +08:00
|
|
|
|
console.log("index", index);
|
2024-01-20 11:13:12 +08:00
|
|
|
|
|
2024-01-29 15:01:43 +08:00
|
|
|
|
if (index <= 0 || index >= references.length) {
|
|
|
|
|
console.log("index", index);
|
|
|
|
|
return; // Index out of bounds or first element
|
|
|
|
|
}
|
|
|
|
|
dispatch(swapReferencesRedux({ indexA: index, indexB: index - 1 }));
|
2024-01-20 11:13:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function moveReferenceDown(index: number) {
|
2024-01-29 15:01:43 +08:00
|
|
|
|
console.log("index", index);
|
|
|
|
|
if (index < 0 || index >= references.length - 1) {
|
|
|
|
|
console.log("index", index);
|
|
|
|
|
return; // Index out of bounds or last element
|
|
|
|
|
}
|
2024-01-20 11:13:12 +08:00
|
|
|
|
|
2024-01-29 15:01:43 +08:00
|
|
|
|
dispatch(swapReferencesRedux({ indexA: index, indexB: index + 1 }));
|
2024-01-20 11:13:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 22:59:21 +08:00
|
|
|
|
function removeReferenceUpdateIndex(index: number, rmPg = false) {
|
2024-01-23 10:54:12 +08:00
|
|
|
|
handleRemoveReference(index);
|
2024-02-02 22:59:21 +08:00
|
|
|
|
delteIndexUpdateBracketNumbersInDeltaKeepSelection(editor, index, rmPg);
|
2024-01-20 11:13:12 +08:00
|
|
|
|
}
|
2024-01-23 10:54:12 +08:00
|
|
|
|
|
|
|
|
|
const handleAddReference = (newReference: Reference) => {
|
|
|
|
|
dispatch(addReferenceRedux(newReference));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemoveReference = (index: number) => {
|
|
|
|
|
dispatch(removeReferenceRedux(index));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClearReferences = () => {
|
|
|
|
|
dispatch(clearReferencesRedux());
|
|
|
|
|
};
|
2024-02-10 13:26:03 +08:00
|
|
|
|
// 状态标志,用于跟踪组件是否首次渲染
|
|
|
|
|
const [isFirstRender, setIsFirstRender] = useState(true);
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
// 当组件首次渲染后,设置 isFirstRender 为 false
|
|
|
|
|
setIsFirstRender(false);
|
|
|
|
|
}, []); // 这个 useEffect 依赖数组为空,所以只会在组件首次渲染后运行
|
2024-02-09 23:01:05 +08:00
|
|
|
|
//监听references,如果发生变化,就提交到服务器
|
|
|
|
|
React.useEffect(() => {
|
2024-02-10 13:26:03 +08:00
|
|
|
|
if (!isFirstRender && isVip) {
|
|
|
|
|
submitPaper(supabase, undefined, references, paperNumberRedux);
|
|
|
|
|
}
|
2024-02-09 23:01:05 +08:00
|
|
|
|
}, [references]);
|
|
|
|
|
|
2024-02-23 19:17:12 +08:00
|
|
|
|
async function generateCitation(doi, style) {
|
|
|
|
|
try {
|
|
|
|
|
const citation = await Cite.async(doi);
|
|
|
|
|
const output = citation.format("bibliography", {
|
|
|
|
|
format: "text",
|
|
|
|
|
template: style,
|
|
|
|
|
lang: "en-US",
|
|
|
|
|
});
|
|
|
|
|
return output;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error generating citation:", error);
|
|
|
|
|
return ""; // Return an empty string in case of error
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const fetchCitations = async () => {
|
|
|
|
|
const updatedReferences = await Promise.all(
|
|
|
|
|
references.map(async (ref) => {
|
|
|
|
|
// 检查是否已经有当前风格的引用
|
|
|
|
|
if (!ref[citationStyle]) {
|
|
|
|
|
// 如果没有,则生成新的引用
|
|
|
|
|
const citationText = await generateCitation(ref.doi, citationStyle);
|
|
|
|
|
return { ...ref, [citationStyle]: citationText }; // 添加新的引用到对象
|
|
|
|
|
}
|
|
|
|
|
return ref; // 如果已有引用,则不做改变
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
dispatch(setReferencesRedux(updatedReferences));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
fetchCitations();
|
|
|
|
|
}, [citationStyle]);
|
|
|
|
|
|
|
|
|
|
const handleStyleChange = (event) => {
|
|
|
|
|
dispatch(setCitationStyle(event.target.value));
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-18 15:46:18 +08:00
|
|
|
|
return (
|
2024-02-11 12:21:15 +08:00
|
|
|
|
<div className=" mx-auto p-4">
|
|
|
|
|
{/* 引用列表显示区域 */}
|
|
|
|
|
<ul>
|
|
|
|
|
{references &&
|
|
|
|
|
references.map(
|
|
|
|
|
(reference, index) =>
|
|
|
|
|
reference && (
|
|
|
|
|
<li key={index} className="mb-3 p-2 border-b">
|
|
|
|
|
{/* 显示序号 */}
|
|
|
|
|
<span className="font-bold mr-2">[{index + 1}].</span>
|
2024-02-23 19:17:12 +08:00
|
|
|
|
{/* {getFullReference(reference)} */}
|
|
|
|
|
{/* 根据当前风格渲染引用 */}
|
|
|
|
|
{renderCitation(reference, citationStyle)}
|
2024-02-11 12:21:15 +08:00
|
|
|
|
{reference.url && (
|
|
|
|
|
<a
|
|
|
|
|
href={reference.url}
|
|
|
|
|
className="text-blue-500 hover:underline"
|
|
|
|
|
target="_blank"
|
|
|
|
|
rel="noopener noreferrer"
|
|
|
|
|
id={`[${(index + 1).toString()}]`}
|
|
|
|
|
>
|
|
|
|
|
{" "}
|
|
|
|
|
({reference.url})
|
|
|
|
|
</a>
|
|
|
|
|
)}
|
|
|
|
|
<button
|
|
|
|
|
className="bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-2 ml-2 rounded"
|
|
|
|
|
onClick={() => moveReferenceUp(index)}
|
|
|
|
|
>
|
|
|
|
|
↑
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className="bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-2 ml-2 rounded"
|
|
|
|
|
onClick={() => moveReferenceDown(index)}
|
|
|
|
|
>
|
|
|
|
|
↓
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className="bg-gray-500 hover:bg-gray-700 text-white font-bold py-1 px-2 ml-2 rounded"
|
2024-02-23 19:17:12 +08:00
|
|
|
|
onClick={() =>
|
|
|
|
|
copyToClipboard(renderCitation(reference, citationStyle))
|
|
|
|
|
}
|
2024-02-11 12:21:15 +08:00
|
|
|
|
>
|
2024-02-12 20:55:14 +08:00
|
|
|
|
{t("复制")}
|
2024-02-11 12:21:15 +08:00
|
|
|
|
</button>
|
|
|
|
|
<ParagraphDeleteButton
|
|
|
|
|
index={index}
|
|
|
|
|
isRemovePaper={true}
|
|
|
|
|
removeReferenceUpdateIndex={removeReferenceUpdateIndex}
|
|
|
|
|
></ParagraphDeleteButton>
|
|
|
|
|
</li>
|
|
|
|
|
)
|
|
|
|
|
)}
|
|
|
|
|
</ul>
|
2024-01-19 15:36:41 +08:00
|
|
|
|
{/* 表单区域 */}
|
2024-01-18 15:46:18 +08:00
|
|
|
|
<form
|
2024-02-11 12:21:15 +08:00
|
|
|
|
id="referenceForm"
|
2024-02-09 23:01:05 +08:00
|
|
|
|
onSubmit={async (e) => {
|
2024-01-18 15:46:18 +08:00
|
|
|
|
e.preventDefault();
|
2024-01-23 10:54:12 +08:00
|
|
|
|
handleAddReference({
|
2024-01-18 15:46:18 +08:00
|
|
|
|
title: newTitle,
|
|
|
|
|
author: newAuthor,
|
|
|
|
|
year: newYear,
|
2024-01-18 23:22:23 +08:00
|
|
|
|
venue: newPublisher,
|
2024-01-18 15:46:18 +08:00
|
|
|
|
url: newUrl,
|
|
|
|
|
});
|
2024-01-19 15:36:41 +08:00
|
|
|
|
// 清空表单
|
2024-01-18 15:46:18 +08:00
|
|
|
|
setNewTitle("");
|
|
|
|
|
setNewAuthor("");
|
2024-01-20 13:43:31 +08:00
|
|
|
|
setNewYear("");
|
2024-01-19 15:36:41 +08:00
|
|
|
|
setNewPublisher("");
|
|
|
|
|
setNewUrl("");
|
2024-02-09 23:01:05 +08:00
|
|
|
|
// submitPaper(supabase, undefined, references, paperNumberRedux);
|
2024-01-18 15:46:18 +08:00
|
|
|
|
}}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
className="mb-6"
|
2024-01-18 15:46:18 +08:00
|
|
|
|
>
|
2024-01-19 15:36:41 +08:00
|
|
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
|
|
|
|
|
<input
|
|
|
|
|
className="border p-2 rounded"
|
|
|
|
|
type="text"
|
|
|
|
|
value={newTitle}
|
|
|
|
|
onChange={(e) => setNewTitle(e.target.value)}
|
2024-02-12 20:55:14 +08:00
|
|
|
|
placeholder={t("Title")}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
className="border p-2 rounded"
|
|
|
|
|
type="text"
|
|
|
|
|
value={newAuthor}
|
|
|
|
|
onChange={(e) => setNewAuthor(e.target.value)}
|
2024-02-12 20:55:14 +08:00
|
|
|
|
placeholder={t("Author")}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
className="border p-2 rounded"
|
|
|
|
|
type="text"
|
|
|
|
|
value={newYear}
|
2024-01-20 13:43:31 +08:00
|
|
|
|
onChange={(e) => setNewYear(e.target.value)}
|
2024-02-12 20:55:14 +08:00
|
|
|
|
placeholder={t("Year")}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
className="border p-2 rounded"
|
|
|
|
|
type="text"
|
|
|
|
|
value={newPublisher}
|
|
|
|
|
onChange={(e) => setNewPublisher(e.target.value)}
|
2024-02-12 20:55:14 +08:00
|
|
|
|
placeholder={t("Publisher")}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
/>
|
|
|
|
|
<input
|
|
|
|
|
className="border p-2 rounded"
|
|
|
|
|
type="text"
|
|
|
|
|
value={newUrl}
|
|
|
|
|
onChange={(e) => setNewUrl(e.target.value)}
|
2024-02-12 20:55:14 +08:00
|
|
|
|
placeholder={t("Url")}
|
2024-01-19 15:36:41 +08:00
|
|
|
|
/>
|
|
|
|
|
</div>
|
2024-01-19 16:27:34 +08:00
|
|
|
|
<div className="container mx-auto p-4">
|
|
|
|
|
<div className="flex justify-between items-center mb-4">
|
|
|
|
|
<button
|
2024-01-22 09:27:09 +08:00
|
|
|
|
className="bg-gray-300 hover:bg-gray-400 text-black font-bold py-2 px-4 rounded "
|
2024-01-19 16:27:34 +08:00
|
|
|
|
type="submit"
|
2024-02-11 12:21:15 +08:00
|
|
|
|
form="referenceForm"
|
2024-01-19 16:27:34 +08:00
|
|
|
|
>
|
2024-02-12 20:55:14 +08:00
|
|
|
|
{t("添加自定义引用")}
|
2024-01-19 16:27:34 +08:00
|
|
|
|
</button>
|
2024-01-19 15:36:41 +08:00
|
|
|
|
|
2024-01-19 16:27:34 +08:00
|
|
|
|
<button
|
2024-01-22 09:27:09 +08:00
|
|
|
|
className="bg-gray-300 hover:bg-gray-400 text-black font-bold py-2 px-4 rounded "
|
2024-01-20 16:58:23 +08:00
|
|
|
|
type="button"
|
2024-02-23 19:52:07 +08:00
|
|
|
|
onClick={() =>
|
|
|
|
|
copyToClipboard(getAllFullReferences(references, citationStyle))
|
|
|
|
|
}
|
2024-01-19 16:27:34 +08:00
|
|
|
|
>
|
2024-02-12 20:55:14 +08:00
|
|
|
|
{t("复制所有引用")}
|
2024-01-19 16:27:34 +08:00
|
|
|
|
</button>
|
|
|
|
|
<button
|
2024-01-20 13:43:31 +08:00
|
|
|
|
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded "
|
2024-01-20 16:58:23 +08:00
|
|
|
|
type="button"
|
2024-01-23 10:54:12 +08:00
|
|
|
|
// onClick={() => setReferences([])} // 设置引用列表为空数组
|
|
|
|
|
onClick={() => handleClearReferences()}
|
2024-01-19 16:27:34 +08:00
|
|
|
|
>
|
2024-02-12 20:55:14 +08:00
|
|
|
|
{t("删除所有引用")}
|
2024-01-19 16:27:34 +08:00
|
|
|
|
</button>
|
2024-02-23 19:17:12 +08:00
|
|
|
|
{/* 下拉框用于更改引用风格 */}
|
|
|
|
|
<div className="mt-4">
|
|
|
|
|
<label
|
|
|
|
|
htmlFor="citation-style"
|
|
|
|
|
className="block text-sm font-medium text-gray-700"
|
|
|
|
|
>
|
|
|
|
|
选择引用格式:
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
id="citation-style"
|
|
|
|
|
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
|
|
|
|
|
value={citationStyle}
|
|
|
|
|
onChange={handleStyleChange}
|
|
|
|
|
>
|
|
|
|
|
<option value="apa">APA</option>
|
|
|
|
|
<option value="mla">MLA</option>
|
|
|
|
|
<option value="chicago">Chicago</option>
|
|
|
|
|
<option value="harvard">Harvard</option>
|
|
|
|
|
<option value="vancouver">Vancouver</option>
|
|
|
|
|
<option value="ieee">IEEE</option>
|
|
|
|
|
<option value="custom-chinese">中文</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2024-01-19 16:27:34 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</form>
|
2024-01-18 15:46:18 +08:00
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default ReferenceList;
|