diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 08111cd6..3d96bcd1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -14,6 +14,8 @@
"@fortawesome/free-regular-svg-icons": "6.5.1",
"@fortawesome/free-solid-svg-icons": "6.5.1",
"@fortawesome/react-fontawesome": "0.2.0",
+ "chart.js": "^4.4.3",
+ "chartjs-plugin-datalabels": "^2.2.0",
"http-proxy-middleware": "^2.0.6",
"jquery": "^3.7.1",
"prosemirror-example-setup": "^1.2.2",
@@ -25,6 +27,7 @@
"prosemirror-utils": "^1.2.1-0",
"prosemirror-view": "^1.33.1",
"react": "17.0.2",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "17.0.2",
"react-router-dom": "6.21.3",
"react-scripts": "5.0.1",
@@ -3346,6 +3349,11 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
+ },
"node_modules/@leichtgewicht/ip-codec": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
@@ -5721,6 +5729,25 @@
"node": ">=10"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz",
+ "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
+ "node_modules/chartjs-plugin-datalabels": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/chartjs-plugin-datalabels/-/chartjs-plugin-datalabels-2.2.0.tgz",
+ "integrity": "sha512-14ZU30lH7n89oq+A4bWaJPnAG8a7ZTk7dKf48YAzMvJjQtjrgg5Dpk9f+LbjCF6bpx3RAGTeL13IXpKQYyRvlw==",
+ "peerDependencies": {
+ "chart.js": ">=3.0.0"
+ }
+ },
"node_modules/check-types": {
"version": "11.2.3",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz",
@@ -14827,6 +14854,15 @@
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 38ec32bf..b6c6f006 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -9,6 +9,8 @@
"@fortawesome/free-regular-svg-icons": "6.5.1",
"@fortawesome/free-solid-svg-icons": "6.5.1",
"@fortawesome/react-fontawesome": "0.2.0",
+ "chart.js": "^4.4.3",
+ "chartjs-plugin-datalabels": "^2.2.0",
"http-proxy-middleware": "^2.0.6",
"jquery": "^3.7.1",
"prosemirror-example-setup": "^1.2.2",
@@ -20,6 +22,7 @@
"prosemirror-utils": "^1.2.1-0",
"prosemirror-view": "^1.33.1",
"react": "17.0.2",
+ "react-chartjs-2": "^5.2.0",
"react-dom": "17.0.2",
"react-router-dom": "6.21.3",
"react-scripts": "5.0.1",
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 564af9eb..81934b38 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -6,6 +6,7 @@ import SignupPage from "./Component/Auth/SignupPage";
import NotePage from "./Component/Note/NotePage";
import UserProfileEdit from "./Component/Auth/UserProfileEdit";
import Page from "./Component/Page/Page";
+import Contribution from "./Component/Contribution/ContributionPage";
import EmailTokenHandler from "./Component/Utils/EmailTokenHandler";
import { BrowserRouter, Routes, Route } from "react-router-dom";
@@ -15,16 +16,14 @@ export default function App() {
} />
- } />
} />
} />
+ } />
} />
- } />
- } />
} />
} />
- } />
- } />
+ } />
+ } />
diff --git a/frontend/src/Component/Page/Page.js b/frontend/src/Component/Page/Page.js
index a687bbc0..ce7c4c55 100644
--- a/frontend/src/Component/Page/Page.js
+++ b/frontend/src/Component/Page/Page.js
@@ -52,6 +52,7 @@ function Page() {
const editorRef = useRef(null);
const ydocRef = useRef(new Y.Doc());
const ydocProviderRef = useRef(null);
+ const yDocInitialized = useRef(null);
const blockLikeRef = useRef(null);
const blockLockRef = useRef(null);
@@ -270,6 +271,7 @@ function Page() {
if (!ydocRef.current) return;
ydocRef.current = new Y.Doc();
+ yDocInitialized.current = false;
ydocProviderRef.current = new WebsocketProvider(
// "wss://demos.yjs.dev/ws", // yjs 데모 서버 주소
// "ws://localhost:4000",
@@ -297,12 +299,13 @@ function Page() {
} else {
if (isSynced) {
handleUserConnection();
- ydocProviderRef.current.connect();
- }
+ ydocProviderRef.current.connect();
+ }
}
// setisloaded(true); // 딜레이 없음
setTimeout(() => {
setisloaded(true);
+ yDocInitialized.current = true;
}, 300); // 딜레이 있음
});
@@ -575,7 +578,7 @@ function Page() {
yUndoPlugin(),
hoverButtonPlugin(blockLikeRef, blockLockRef),
inlinePlaceholderPlugin(),
- generateBlockIdPlugin(),
+ generateBlockIdPlugin({ yDocInitialized }),
imagePlugin({
...imageSettings,
resizeCallback: (el, updateCallback) => {
diff --git a/frontend/src/Component/Page/utils/editor/plugin/generateBlockIdPlugin.js b/frontend/src/Component/Page/utils/editor/plugin/generateBlockIdPlugin.js
index d91680f7..0cc0cb1c 100644
--- a/frontend/src/Component/Page/utils/editor/plugin/generateBlockIdPlugin.js
+++ b/frontend/src/Component/Page/utils/editor/plugin/generateBlockIdPlugin.js
@@ -1,14 +1,19 @@
import { Plugin } from "prosemirror-state";
import { v4 as uuidv4 } from "uuid";
-export const generateBlockIdPlugin = (guidGenerator = uuidv4) => {
- return new Plugin({
+export const generateBlockIdPlugin = ({ yDocInitialized, guidGenerator = uuidv4 }) => {
+ return new Plugin({
appendTransaction: (transactions, prevState, nextState) => {
+ // Yjs 문서가 초기화되지 않은 경우 동작하지 않도록 함
+ if (!yDocInitialized.current) {
+ return null;
+ }
+
const tr = nextState.tr;
let modified = false;
const generatedIds = new Set();
const userId = localStorage.getItem('userId');
-
+
if (transactions.some(transaction => transaction.docChanged)) {
const { paragraph, image } = nextState.schema.nodes;
let prevNode = null; // 이전 노드를 추적하기 위한 변수
diff --git a/frontend/src/Component/Page/utils/editor/plugin/hoverButtonPlugin.js b/frontend/src/Component/Page/utils/editor/plugin/hoverButtonPlugin.js
index f0ef035d..1f0a35ee 100644
--- a/frontend/src/Component/Page/utils/editor/plugin/hoverButtonPlugin.js
+++ b/frontend/src/Component/Page/utils/editor/plugin/hoverButtonPlugin.js
@@ -74,10 +74,6 @@ export function hoverButtonPlugin(blockLikeRef, blockLockRef) {
toastr.warning("내용이 없는 블록입니다.");
} else {
await blockLikeRef.current.toggleLike(guid, liker, writer);
- if (liker !== writer) {
- this.classList.toggle("hoverButton_like");
- this.classList.toggle("hoverButton_like_fullRedHeart");
- }
}
} else {
console.log('No UUID found for this node.');
@@ -133,10 +129,10 @@ export function hoverButtonPlugin(blockLikeRef, blockLockRef) {
// 새 노드 삽입
const newNode = state.schema.nodes.paragraph.create();
- tr = state.doc.content.size === $clickPos.end($clickPos.depth) ? tr.insert(insertPos - 1, newNode) : tr.insert(insertPos, newNode);
+ tr = state.doc.content.size === $clickPos.end($clickPos.depth) && !isImageNode ? tr.insert(insertPos - 1, newNode) : tr.insert(insertPos, newNode);
// 삽입된 노드 내부에 커서 위치시키기
- const newPos = state.doc.content.size === $clickPos.end($clickPos.depth) ? insertPos : insertPos + 1; // 노드 삽입 후 새로운 위치 조정
+ const newPos = state.doc.content.size === $clickPos.end($clickPos.depth) && !isImageNode ? insertPos : insertPos + 1; // 노드 삽입 후 새로운 위치 조정
tr = tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
// 트랜잭션 적용
diff --git a/frontend/src/Component/Page/utils/yjs/BlockLike.js b/frontend/src/Component/Page/utils/yjs/BlockLike.js
index 3d0aa2de..71a08687 100644
--- a/frontend/src/Component/Page/utils/yjs/BlockLike.js
+++ b/frontend/src/Component/Page/utils/yjs/BlockLike.js
@@ -8,10 +8,11 @@ const BlockLike = forwardRef(({ ydocRef }, ref) => {
const pathSegments = location.pathname.split('/').filter(Boolean);
const organizationId = pathSegments[1];
const noteId = pathSegments[2];
+ const hoverButton_like = document.querySelector(".hoverButton_like");
const userId = localStorage.getItem('userId');
const yLikeList = ydocRef.current.getMap(`yLikeList_${userId}`);
-
+
const toggleLike = async (blockId, lover, heartReceiver) => {
if (lover !== heartReceiver) {
const currentLikeState = yLikeList.get(blockId);
@@ -34,8 +35,10 @@ const BlockLike = forwardRef(({ ydocRef }, ref) => {
if (response.ok) {
if (responseData.includes("좋아요 성공!")) {
toastr.success(responseData);
+ hoverButton_like.classList.replace('hoverButton_like', 'hoverButton_like_fullRedHeart');
} else {
toastr.info(responseData);
+ hoverButton_like.classList.replace('hoverButton_like_fullRedHeart', 'hoverButton_like');
}
} else {
toastr.error(responseData);
diff --git a/frontend/src/Component/Page/utils/yjs/BlockLock.js b/frontend/src/Component/Page/utils/yjs/BlockLock.js
index a3a0cb37..387c549b 100644
--- a/frontend/src/Component/Page/utils/yjs/BlockLock.js
+++ b/frontend/src/Component/Page/utils/yjs/BlockLock.js
@@ -18,13 +18,14 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
const yRequestUnLock = ydocRef.current.getMap('yRequestUnLock');
const yConnectedUserList = ydocRef.current.getMap('connectedUsers');
const yUnLockInfo = ydocRef.current.getMap('yUnLockInfo');
+ const yRecentUnLockBlock = ydocRef.current.getArray('yRecentUnLockBlock');
const yResultUnLock = ydocRef.current.getMap('yResultUnLock');
const yReceivedMessage = ydocRef.current.getMap(`${nickname}_message`);
const baseSwal = Swal.mixin({
showCancelButton: true,
- confirmButtonColor: "#3085d6",
- cancelButtonColor: "#d33",
+ confirmButtonColor: "#28a745",
+ cancelButtonColor: "#6c757d",
confirmButtonText: '확인',
cancelButtonText: '취소'
});
@@ -42,10 +43,14 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
}
};
+ function updateHoverDivPosition(hoverDiv, change) {
+ const hoverDivcurrentTop = parseFloat(window.getComputedStyle(hoverDiv)?.top || "0");
+ const newTop = window.matchMedia("(max-width: 768px)").matches ? hoverDivcurrentTop + change : hoverDivcurrentTop + (change * 2);
+ hoverDiv.style.top = `${newTop}px`;
+ }
+
function addIdToParagraph(uuid) {
const hoverDiv = document.querySelector(".hoverDiv");
- const hoverDivcurrentTop = parseFloat(window.getComputedStyle(hoverDiv).top);
-
const view = editorRef.current.view;
const { state, dispatch } = view;
const { tr } = state;
@@ -62,15 +67,15 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
if (paragraphNode) {
const { node, pos } = paragraphNode;
const paragraphWithId = node.type.create({ ...node.attrs, id: 'locked' }, node.content, node.marks);
- dispatch(tr.replaceWith(pos, pos + node.nodeSize, paragraphWithId));
- view.updateState(state.apply(tr));
- hoverDiv.style.top = window.matchMedia("(max-width: 768px)").matches ? `${hoverDivcurrentTop + 2}px` : `${hoverDivcurrentTop + 4}px`;
+ tr.replaceWith(pos, pos + node.nodeSize, paragraphWithId);
+ dispatch(tr);
+ updateHoverDivPosition(hoverDiv, 2);
}
}
function removeIdFromParagraph(uuid) {
const hoverDiv = document.querySelector(".hoverDiv");
- const hoverDivcurrentTop = parseFloat(window.getComputedStyle(hoverDiv).top); const view = editorRef.current.view;
+ const view = editorRef.current.view;
const { state, dispatch } = view;
const { tr } = state;
@@ -88,12 +93,12 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
if (!node.isText && !node.isInline) {
const { id, ...attrsWithoutId } = node.attrs;
const newAttrs = { ...attrsWithoutId, id: "non-locked" };
- dispatch(tr.setNodeMarkup(pos, null, newAttrs));
- view.updateState(state.apply(tr));
- hoverDiv.style.top = window.matchMedia("(max-width: 768px)").matches ? `${hoverDivcurrentTop - 2}px` : `${hoverDivcurrentTop - 4}px`;
+ tr.setNodeMarkup(pos, null, newAttrs);
+ dispatch(tr);
+ updateHoverDivPosition(hoverDiv, -2);
}
}
- }
+ }
// 특정 UUID로 노드를 찾아 해당 노드의 위치로 커서를 이동시키는 함수
function moveCursorToNodeWithUUID(uuid) {
@@ -122,7 +127,10 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
const selfUnlockBlock = (locker, myLockedBlockId) => {
const unlockRequestor = yRequestUnLock.get(locker)?.requestor;
- if (yResultUnLock.get(`${unlockRequestor}`)?.result === "deny") yResultUnLock.set(`${unlockRequestor}`, { responser: locker, result: "lateAccept", unlockedBlockID: myLockedBlockId });
+ if (yResultUnLock.get(`${unlockRequestor}`)?.result === "deny") {
+ yResultUnLock.set(`${unlockRequestor}`, { responser: locker, result: "lateAccept", unlockedBlockID: myLockedBlockId });
+ yRecentUnLockBlock.push([`${myLockedBlockId}`]);
+ }
removeYjsMapUnLockData(nickname);
removeIdFromParagraph(myLockedBlockId);
yLineLocks.delete(myLockedBlockId);
@@ -131,9 +139,12 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
const removeYjsMapUnLockData = (locker) => {
const unlockRequestor = yRequestUnLock.get(locker)?.requestor;
+ const unlockedBlockID = yResultUnLock.get(unlockRequestor)?.unlockedBlockID;
+ const yRecentUnLockBlockIndex = yRecentUnLockBlock.toArray().indexOf(unlockedBlockID?.toString())
if (yResultUnLock.has(`${unlockRequestor}`)) yResultUnLock.delete(`${unlockRequestor}`);
if (yRequestUnLock.has(locker)) yRequestUnLock.delete(locker);
if (yUnLockInfo.has(locker)) yUnLockInfo.delete(locker);
+ if (yRecentUnLockBlockIndex !== -1) yRecentUnLockBlock.delete(`${yRecentUnLockBlockIndex}`, 1);
};
const checkRequestUnLockTimer = async (locker) => {
@@ -146,7 +157,7 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
let timerInterval;
// 시간 차이를 밀리초 단위로 계산 (1분 = 60,000밀리초)
if (timeDifference < expirationTime) {
- const result = await baseSwal.fire({ html: `${locker} 이(가) 해당 블록의 잠금 해제 요청을 거절했습니다.`,
+ const result = await baseSwal.fire({ html: `${locker} 이(가) 해당 블록의 잠금 해제 요청을 거절했습니다.`,
footer: `추가적인 요청은 초 후에 가능합니다.`,
icon: "error",
timer: expirationTime - timeDifference,
@@ -184,8 +195,8 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
if (unlockRequestor && myLockedBlockId && unlockRequestor !== nickname) {
let timerInterval;
let forcedModalClose = false;
- const expirationTime = yRequestUnLock.get(nickname)?.expirationTime * 1000;
- const result = await baseSwal.fire({ html: `${unlockRequestor} 이(가) 블록 잠금 해제를 요청하였습니다.
+ const expirationTime = 30000; // 요청 만료 시간(30초)
+ const result = await baseSwal.fire({ html: `${unlockRequestor} 이(가) 블록 잠금 해제를 요청하였습니다.
최근 설정한 블록 잠금을 해제하시겠습니까?
@@ -244,6 +255,7 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
yUserLocks.delete(nickname);
removeIdFromParagraph(myLockedBlockId);
toastr.info(`편집 잠금이 해제되었습니다.`);
+ yRecentUnLockBlock.push([`${myLockedBlockId}`]);
yResultUnLock.set(`${unlockRequestor}`, { responser: nickname, result: "accept", unlockedBlockID: myLockedBlockId });
} else if (forcedModalClose === false) {
const yReceivedMessage = ydocRef.current.getMap(`${unlockRequestor}_message`);
@@ -300,77 +312,88 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
};
const toggleLineLock = async (guid) => {
- const locker = yLineLocks.get(guid.toString());
- const myLockedBlockId = yUserLocks.get(nickname);
+ try {
+ const locker = yLineLocks.get(guid.toString());
+ const myLockedBlockId = yUserLocks.get(nickname);
- isLoggedIn();
- toastr.remove();
+ isLoggedIn();
+ toastr.remove();
- if (locker && locker !== nickname && myLockedBlockId && myLockedBlockId !== guid.toString()) {
- const result = await baseSwal.fire({
- title: "🔓",
- html: `기존에 설정한 잠금을 해제 후 요청을 보내시겠습니까?`,
- });
-
- if (result.isConfirmed) {
- removeIdFromParagraph(myLockedBlockId);
- yLineLocks.delete(myLockedBlockId);
- yUserLocks.delete(nickname);
- } else {
- return;
+ if (yRecentUnLockBlock.toArray().indexOf(guid.toString()) !== -1) { toastr.warning(`잠시 후 시도하세요.
사유: 블록 잠금 정보가 남아있음`); return; }
+
+ if (locker && locker !== nickname && myLockedBlockId && myLockedBlockId !== guid.toString()) {
+ const result = await baseSwal.fire({
+ title: "🔓",
+ html: `기존에 설정한 잠금을 해제 후 요청을 보내시겠습니까?`,
+ });
+
+ if (result.isConfirmed) {
+ removeIdFromParagraph(myLockedBlockId);
+ yLineLocks.delete(myLockedBlockId);
+ yUserLocks.delete(nickname);
+ } else {
+ return;
+ }
}
- }
- if (!locker && myLockedBlockId && myLockedBlockId !== guid.toString()) {
- const result = await baseSwal.fire({
- title: "최대 1개의 블록 잠금이 허용됩니다.",
- text: "이전에 설정한 잠금을 해제하시겠습니까?",
- icon: "warning",
- });
+ if (!locker && myLockedBlockId && myLockedBlockId !== guid.toString()) {
+ const result = await baseSwal.fire({
+ title: "최대 1개의 블록 잠금이 허용됩니다.",
+ text: "이전에 설정한 잠금을 해제하시겠습니까?",
+ icon: "warning",
+ });
+
+ if (result.isConfirmed) {
+ selfUnlockBlock(nickname, myLockedBlockId)
+ } else {
+ return;
+ }
+ }
- if (result.isConfirmed) {
- selfUnlockBlock(nickname, myLockedBlockId)
+ if (locker) {
+ if (locker === nickname) {
+ selfUnlockBlock(locker, myLockedBlockId)
+ toastr.info(`편집 잠금이 해제되었습니다.`);
+ return;
+ }
+ if (yRequestUnLock.has(locker) && !yUnLockInfo.has(locker)) {
+ const requestor = yRequestUnLock.get(locker)?.requestor
+ const message = requestor === nickname ? `이전 요청을 처리 중입니다...` : `${requestor} 이(가) 잠금 해제 요청 중입니다.`;
+ toastr.warning(message);
+ } else {
+ const beforeRequest = await checkRequestUnLockTimer(locker);
+ if(beforeRequest) {
+ const result = await baseSwal.fire({
+ title: "✉️",
+ html: `${locker} 에게 블록 잠금 해제를 요청합니다.`,
+ });
+ if (result.isConfirmed) {
+ if (!yUserLocks.has(locker) || guid?.toString() !== yUserLocks.get(locker)?.toString()) {
+ toastr.remove();
+ toastr.warning(`다시 시도하세요.
사유: 블록 잠금 정보가 변경됨`);
+ } else if (yRequestUnLock.has(locker)) {
+ const requestor = yRequestUnLock.get(locker)?.requestor;
+ toastr.remove();
+ toastr.warning(`${requestor} 이(가) 이미 요청했습니다.`);
+ } else {
+ yRequestUnLock.set(locker, { requestor: nickname });
+ }
+ }
+ }
+ }
} else {
- return;
+ removeYjsMapUnLockData(nickname);
+ yLineLocks.set(guid.toString(), nickname);
+ yUserLocks.set(nickname, guid.toString());
+ addIdToParagraph(guid.toString());
+ toastr.success(`블록 편집 잠금이 설정되었습니다.`);
}
- }
-
- if (locker) {
- if (locker === nickname) {
- selfUnlockBlock(locker, myLockedBlockId)
- toastr.info(`편집 잠금이 해제되었습니다.`);
- return;
- }
- if (yRequestUnLock.has(locker) && !yUnLockInfo.has(locker)) {
- const requestor = yRequestUnLock.get(locker)?.requestor
- const message = requestor === nickname ? `이전 요청을 처리 중입니다...` : `${requestor} 이(가) 잠금 해제 요청 중입니다.`;
- toastr.warning(message);
- } else {
- const beforeRequest = await checkRequestUnLockTimer(locker);
- if(beforeRequest) {
- const result = await baseSwal.fire({
- title: "✉️",
- html: `${locker} 에게 블록 잠금 해제를 요청합니다.
-
- 요청 만료 시간(초)을 설정해주세요.`,
- input: "range",
- inputAttributes: {
- min: "15",
- max: "30",
- step: "5"
- },
- inputValue: 15
- });
- if (result.isConfirmed) {
- yRequestUnLock.set(locker, { requestor: nickname, expirationTime: result.value });
- }
- }
- }
- } else {
- yLineLocks.set(guid.toString(), nickname);
- yUserLocks.set(nickname, guid.toString());
- addIdToParagraph(guid.toString());
- toastr.success(`블록 편집 잠금이 설정되었습니다.`);
+ } catch (error) {
+ const hoverDiv = document.querySelector(".hoverDiv");
+ hoverDiv.style.visibility = "hidden";
+ toastr.remove();
+ toastr.warning(`다시 시도하세요.`);
+ console.error(error);
}
};
@@ -395,7 +418,6 @@ const BlockLock = forwardRef(({ ydocRef, editorRef }, ref) => {
}
});
};
- removeYjsMapUnLockData(nickname);
window.addEventListener('popstate', handlePopState);
yRequestUnLock.observe(checkRequestUnLockWrapper);
yResultUnLock.observe(checkResultUnLockWrapper);