From f7c034cfc2d5fd7737f572a54b5aebeac52025ff Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Sat, 17 Aug 2024 22:05:16 +0900 Subject: [PATCH 1/5] feat(Yorkie): Add yorkie co-editing system and client --- package-lock.json | 43 +++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + src/pages/Map/Map.tsx | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/package-lock.json b/package-lock.json index ac0e3b4..77bea29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "typescript": "^4.9.5", "typescript-cookie": "^1.0.6", "web-vitals": "^2.1.4", + "yorkie-js-sdk": "^0.4.28", "zustand": "^4.5.4" }, "devDependencies": { @@ -2210,6 +2211,28 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@bufbuild/protobuf": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz", + "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==" + }, + "node_modules/@connectrpc/connect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-1.4.0.tgz", + "integrity": "sha512-vZeOkKaAjyV4+RH3+rJZIfDFJAfr+7fyYr6sLDKbYX3uuTVszhFe9/YKf5DNqrDb5cKdKVlYkGn6DTDqMitAnA==", + "peerDependencies": { + "@bufbuild/protobuf": "^1.4.2" + } + }, + "node_modules/@connectrpc/connect-web": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@connectrpc/connect-web/-/connect-web-1.4.0.tgz", + "integrity": "sha512-13aO4psFbbm7rdOFGV0De2Za64DY/acMspgloDlcOKzLPPs0yZkhp1OOzAQeiAIr7BM/VOHIA3p8mF0inxCYTA==", + "peerDependencies": { + "@bufbuild/protobuf": "^1.4.2", + "@connectrpc/connect": "1.4.0" + } + }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -14074,6 +14097,11 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -21092,6 +21120,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yorkie-js-sdk": { + "version": "0.4.28", + "resolved": "https://registry.npmjs.org/yorkie-js-sdk/-/yorkie-js-sdk-0.4.28.tgz", + "integrity": "sha512-laDG5LVXV1mW085F4BdagUgIgU4hd+QBD0kon8kAeVA086ZGwXRMlFSYhEZtY1bB+RTSDSe4i78vVGG2Kv6bcg==", + "dependencies": { + "@bufbuild/protobuf": "^1.6.0", + "@connectrpc/connect": "^1.4.0", + "@connectrpc/connect-web": "^1.4.0", + "long": "^5.2.0" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=7.1.0" + } + }, "node_modules/zustand": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.4.tgz", diff --git a/package.json b/package.json index 38d2a1b..e5dddce 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "typescript": "^4.9.5", "typescript-cookie": "^1.0.6", "web-vitals": "^2.1.4", + "yorkie-js-sdk": "^0.4.28", "zustand": "^4.5.4" }, "scripts": { diff --git a/src/pages/Map/Map.tsx b/src/pages/Map/Map.tsx index 7520099..ad293f7 100644 --- a/src/pages/Map/Map.tsx +++ b/src/pages/Map/Map.tsx @@ -1,3 +1,4 @@ +import yorkie, { Client, Document } from 'yorkie-js-sdk'; import { useEffect, useState } from 'react'; import { useLocation, useNavigate, useParams } from 'react-router-dom'; import styles from './Map.module.scss'; @@ -32,6 +33,8 @@ const Map = () => { const { registerStatus, loginNeeded, setLoginNeededStatus } = useRegisterStore(); const [dimmed, setDimmed] = useState(false); + const [client, setClient] = useState(); + const [doc, setDoc] = useState>(); useEffect(() => { if (mapMode === MapMode.UNVALID) { @@ -74,6 +77,36 @@ const Map = () => { navigate(prevUrl); }; + useEffect(() => { + const YORKIE_CLIENT_API_KEY = process.env.REACT_APP_YORKIE_API_KEY; + const YORKIE_ENDPOINT = process.env.REACT_APP_YORKIE_URL; + + if (!mapName || !YORKIE_CLIENT_API_KEY || !YORKIE_ENDPOINT) { + return; + } + + const initializeYorkie = async () => { + const client = new yorkie.Client(YORKIE_ENDPOINT, { + apiKey: YORKIE_CLIENT_API_KEY, + }); + await client.activate(); + + const doc = new yorkie.Document(mapName); + await client.attach(doc); + + setClient(client); + setDoc(doc); + }; + + initializeYorkie(); + + return () => { + if (client) { + client.deactivate(); + } + }; + }, [mapName]); + // map return (
From 5956d07098413b540ca6bf4e2ad8794643e0e20d Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Sun, 18 Aug 2024 11:53:08 +0900 Subject: [PATCH 2/5] chore(Yorkie): Redefine map object type & Add document initializing --- src/pages/Map/Map.tsx | 8 ++++++- src/types/map/object/ObjectInfo.ts | 37 ++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/pages/Map/Map.tsx b/src/pages/Map/Map.tsx index ad293f7..bdf3b1d 100644 --- a/src/pages/Map/Map.tsx +++ b/src/pages/Map/Map.tsx @@ -12,6 +12,7 @@ import BaseMap from '../../components/map/BaseMap/BaseMap'; import EditDesignPanel from '../../components/map/BaseMap/EditDesignPanel'; import GlobalNavigationBar from '../../components/global/GlobalNavigationBar'; import { MapMode } from '../../types/enum/MapMode'; +import { YorkieDocType } from '../../types/map/object/ObjectInfo'; //"editor" //마이페이지 > 편집 가능한 지도에서의 접근이므로, 이미 로그인 된 상태일 것. @@ -91,9 +92,14 @@ const Map = () => { }); await client.activate(); - const doc = new yorkie.Document(mapName); + const doc = new yorkie.Document(mapName); await client.attach(doc); + doc.update((root) => { + if (!root.informationAttributes) root.informationAttributes = []; + if (!root.objects) root.objects = []; + }, 'initialize document'); + setClient(client); setDoc(doc); }; diff --git a/src/types/map/object/ObjectInfo.ts b/src/types/map/object/ObjectInfo.ts index 21e951c..2d169cc 100644 --- a/src/types/map/object/ObjectInfo.ts +++ b/src/types/map/object/ObjectInfo.ts @@ -1,16 +1,29 @@ import { ObjectShape } from '../../enum/ObjectShape'; -import { ObjectOutline } from './ObjectOutline'; -import { StarRating } from './StarRating'; -export interface ObjectInfo { - shape: ObjectShape; +export interface MapObject { + objectId: string; + type: ObjectShape; name: string; - roadNameAddress: string; - detailAddress: string; - length?: string; //길이 - 선 - perimeter?: string; //둘레 - 면 - area?: string; //면적 - 면 - connections: ObjectOutline[]; - tags: string[]; // 최대 10글자 - starRatings: StarRating[]; + shape: any; + geoAttribute: { + roadNameAddress: string; + length?: string; //길이 - 선 + perimeter?: string; //둘레 - 면 + area?: string; //면적 - 면 + }; + userAttribute: { + detailAddress: string; + [attributeID: string]: any; + }; +} + +export interface InfoAttribute { + id: string; + name: string; + type: 'stars' | 'relations' | 'tags'; +} + +export interface YorkieDocType { + informationAttributes: InfoAttribute[]; + objects: MapObject[]; } From 1126af7a952a0ab2eb8c90b76095e7fb033e5ffb Mon Sep 17 00:00:00 2001 From: "Hyeonjae.kim" Date: Mon, 19 Aug 2024 22:41:22 +0900 Subject: [PATCH 3/5] refactor: Restructure MapObject type & MapInfoStore --- .../PublishLinkContainer.tsx | 17 +- src/stores/mapInfoStore.ts | 181 ++++++------------ src/types/enum/ObjectShape.ts | 6 +- src/types/map/object/ObjectInfo.ts | 7 +- 4 files changed, 70 insertions(+), 141 deletions(-) diff --git a/src/components/map/ObjectInfoPanel/PublishLinkContainer/PublishLinkContainer.tsx b/src/components/map/ObjectInfoPanel/PublishLinkContainer/PublishLinkContainer.tsx index efd8ac8..e2f1ed4 100644 --- a/src/components/map/ObjectInfoPanel/PublishLinkContainer/PublishLinkContainer.tsx +++ b/src/components/map/ObjectInfoPanel/PublishLinkContainer/PublishLinkContainer.tsx @@ -10,13 +10,12 @@ interface Props { } const PublishLinkContainer: React.FC = ({ mode }) => { - const { isPublished, swithIsPublished, publicLink, objectOutlineList } = - useMapInfoStore(); + const { mapInfo, innerData } = useMapInfoStore(); const handleCopyLink = async () => { //클립보드에 공유 링크 복사 try { - await navigator.clipboard.writeText(`${publicLink}`); + await navigator.clipboard.writeText(`${mapInfo.publicLink}`); } catch (e) { //TODO: 에러 상태로 설정 alert('공유 링크 복사에 실패하였습니다.'); @@ -25,7 +24,7 @@ const PublishLinkContainer: React.FC = ({ mode }) => { const handleSwitchIsPublished = () => { //TODO: 게시 여부 설정 api 호출 - swithIsPublished(); + // swithIsPublished(); }; //editor @@ -34,14 +33,14 @@ const PublishLinkContainer: React.FC = ({ mode }) => {
- {isPublished ? ( + {mapInfo.isPublished ? (
지도 게시 취소하기 공개된 지도가 비공개됩니다
- ) : objectOutlineList.length === 0 ? ( + ) : !innerData.objects ? (
= ({ mode }) => {
)} - {isPublished ? ( + {mapInfo.isPublished ? ( - ) : objectOutlineList.length === 0 ? ( + ) : !innerData.objects ? (
- {!isMine && } + {!mapInfo.isMine && } {!loginNeeded && } diff --git a/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectList.tsx b/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectList.tsx index 4fdb04a..1296973 100644 --- a/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectList.tsx +++ b/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectList.tsx @@ -3,7 +3,7 @@ import ObjectOutlineBtn from './ObjectOutlineBtn'; import useMapInfoStore from '../../../../stores/mapInfoStore'; const ObjectList = () => { - const { isMine, objectOutlineList } = useMapInfoStore(); + const { mapInfo, innerData } = useMapInfoStore(); return (
@@ -12,14 +12,14 @@ const ObjectList = () => {
- {objectOutlineList && - objectOutlineList.map((object) => ( - + {innerData.objects && + innerData.objects.map((object) => ( + ))}
diff --git a/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectOutlineBtn.tsx b/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectOutlineBtn.tsx index 7fdd97f..ae7158d 100644 --- a/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectOutlineBtn.tsx +++ b/src/components/map/MapInfoPanel/ObjectOutlineList/ObjectOutlineBtn.tsx @@ -1,20 +1,26 @@ import styles from './ObjectOutlineBtn.module.scss'; -import { ObjectOutline } from '../../../../types/map/object/ObjectOutline'; +import { MapObject } from '../../../../types/map/object/ObjectInfo'; import { ObjectShape } from '../../../../types/enum/ObjectShape'; import Point from '../../../../assets/map/ico_point_gray.svg'; import Line from '../../../../assets/map/ico_line_gray.svg'; import Plane from '../../../../assets/map/ico_plane_gray.svg'; +import useMapInfoStore from '../../../../stores/mapInfoStore'; interface Props { - object: ObjectOutline; + object: MapObject; } const ObjectOutlineBtn: React.FC = ({ object }) => { + const { setSelectedObjectId } = useMapInfoStore(); return ( -