diff --git a/next.config.mjs b/next.config.mjs index 810bb22..533f17b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -8,6 +8,18 @@ const nextConfig = { experimental: { optimizePackageImports: ['shiki'], }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + { + protocol: 'http', + hostname: '**', + }, + ], + }, async headers() { return [ { diff --git a/package.json b/package.json index 203e1e9..7f1aa57 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-scroll-area": "^1.1.0", "@radix-ui/react-select": "^2.1.1", @@ -49,6 +50,7 @@ "minimatch": "^10.0.1", "monaco-editor": "^0.50.0", "next": "14.2.10", + "next-qrcode": "^2.5.1", "nextjs-toploader": "^1.6.12", "perfect-cursors": "^1.0.5", "prettier": "3.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dd874b5..d284d66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@radix-ui/react-icons': specifier: ^1.3.0 version: 1.3.0(react@18.3.1) + '@radix-ui/react-label': + specifier: ^2.1.0 + version: 2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-popover': specifier: ^1.1.1 version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -92,6 +95,9 @@ importers: next: specifier: 14.2.10 version: 14.2.10(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-qrcode: + specifier: ^2.5.1 + version: 2.5.1(react@18.3.1) nextjs-toploader: specifier: ^1.6.12 version: 1.6.12(next@14.2.10(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1818,6 +1824,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-label@2.1.0': + resolution: {integrity: sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-menu@2.1.1': resolution: {integrity: sha512-oa3mXRRVjHi6DZu/ghuzdylyjaMXLymx83irM7hTxutQbD+7IhPKdMdRHD26Rm+kHRrWcrUkkRPv5pd47a2xFQ==} peerDependencies: @@ -3193,6 +3212,10 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001643: resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==} @@ -3289,6 +3312,9 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3568,6 +3594,10 @@ packages: supports-color: optional: true + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -3651,6 +3681,9 @@ packages: diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -5204,6 +5237,12 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + next-qrcode@2.5.1: + resolution: {integrity: sha512-ibiS1+p+myjurC1obVIgo7UCjFhxm0SNYTkikahQVmnyzlfREOdUCf2f77Vbz6W6q2zfx5Eww2/20vbgkLNdLw==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '>=17.0.0' + next@14.2.10: resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==} engines: {node: '>=18.17.0'} @@ -5525,6 +5564,10 @@ packages: pkg-types@1.1.3: resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + pnp-webpack-plugin@1.7.0: resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==} engines: {node: '>=6'} @@ -5677,6 +5720,11 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.11.0: resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} engines: {node: '>=0.6'} @@ -5907,6 +5955,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requireindex@1.2.0: resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} engines: {node: '>=0.10.5'} @@ -6065,6 +6116,9 @@ packages: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -6795,6 +6849,9 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which-typed-array@1.1.15: resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} @@ -6817,6 +6874,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -6916,6 +6977,9 @@ packages: peerDependencies: yjs: ^13.5.6 + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -6940,10 +7004,18 @@ packages: engines: {node: '>= 14'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -8621,6 +8693,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.3 + '@radix-ui/react-label@2.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@radix-ui/react-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -10364,6 +10445,8 @@ snapshots: camelcase-css@2.0.1: {} + camelcase@5.3.1: {} + caniuse-lite@1.0.30001643: {} case-sensitive-paths-webpack-plugin@2.4.0: {} @@ -10458,6 +10541,12 @@ snapshots: client-only@0.0.1: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -10762,6 +10851,8 @@ snapshots: dependencies: ms: 2.1.2 + decamelize@1.2.0: {} + decimal.js@10.4.3: {} dedent@0.7.0: {} @@ -10851,6 +10942,8 @@ snapshots: miller-rabin: 4.0.1 randombytes: 2.1.0 + dijkstrajs@1.0.3: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -12684,6 +12777,11 @@ snapshots: neo-async@2.6.2: {} + next-qrcode@2.5.1(react@18.3.1): + dependencies: + qrcode: 1.5.4 + react: 18.3.1 + next@14.2.10(@babel/core@7.24.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 14.2.10 @@ -13030,6 +13128,8 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + pngjs@5.0.0: {} + pnp-webpack-plugin@1.7.0(typescript@4.9.5): dependencies: ts-pnp: 1.2.0(typescript@4.9.5) @@ -13180,6 +13280,12 @@ snapshots: punycode@2.3.1: {} + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + qs@6.11.0: dependencies: side-channel: 1.0.6 @@ -13453,6 +13559,8 @@ snapshots: require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} + requireindex@1.2.0: {} requires-port@1.0.0: {} @@ -13634,6 +13742,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -14472,6 +14582,8 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.3 + which-module@2.0.1: {} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 @@ -14495,6 +14607,12 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -14579,6 +14697,8 @@ snapshots: - bufferutil - utf-8-validate + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -14591,8 +14711,27 @@ snapshots: yaml@2.5.0: {} + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@17.7.2: dependencies: cliui: 8.0.1 diff --git a/src/app/(main)/(router)/login/page.tsx b/src/app/(main)/(router)/login/page.tsx index ce7570f..5ae866f 100644 --- a/src/app/(main)/(router)/login/page.tsx +++ b/src/app/(main)/(router)/login/page.tsx @@ -386,6 +386,7 @@ const LoginPage: FC = () => { // 登录成功 // TODO 成功提示 setAuth(res.data); + localStorage.setItem('email', state.email); router.push(searchParams.get('redirect') ?? '/'); } else { // 登录失败 diff --git a/src/app/cooperation/(router)/[room]/page.tsx b/src/app/cooperation/(router)/[room]/page.tsx index 2eba2fe..de69068 100644 --- a/src/app/cooperation/(router)/[room]/page.tsx +++ b/src/app/cooperation/(router)/[room]/page.tsx @@ -1,5 +1,7 @@ 'use client'; +import React, { useEffect, useState } from 'react'; + import { TASKS } from '@/utils'; import { CooperationHeader } from '@/components/cooperation/header'; import { TaskList } from '@/components/cooperation/taskList'; @@ -10,21 +12,61 @@ interface CooperationPageProps { params: any; } +async function searchUserByEmail() { + const token = JSON.parse(localStorage.getItem('ONLINE_EDIT_AUTH') ?? '')?.access_token; + + try { + const response = await fetch(`http://localhost:8080/api/v1/user`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const res = await response.json(); + console.log('searchUserByEmail res', res); + + return res.data; + } catch (error) { + console.error('Error:', error); + + return null; + } +} + export default function CooperationPage({ params }: CooperationPageProps) { + const [userInfo, setUserInfo] = useState(null); + + useEffect(() => { + async function fetchUserInfo() { + const user = await searchUserByEmail(); + setUserInfo(user); + } + + fetchUserInfo(); + }, []); + + if (!userInfo) return null; + return ( -
-
- +
+
+
-
-
+
+
-
+
-
- +
+
diff --git a/src/components/cooperation/avatarList/index.tsx b/src/components/cooperation/avatarList/index.tsx new file mode 100644 index 0000000..d04e908 --- /dev/null +++ b/src/components/cooperation/avatarList/index.tsx @@ -0,0 +1,43 @@ +'use client'; + +import React, { useState } from 'react'; + +export const AnimatedTooltip = ({ + items, +}: { + items: { + id: number; + name: string; + designation: string; + image: string; + }[]; +}) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + + return ( +
+ {items.map((item) => ( +
setHoveredIndex(item.id)} + onMouseLeave={() => setHoveredIndex(null)} + > + {hoveredIndex === item.id && ( +
+
+
+
{item.name}
+
{item.designation}
+
+ )} + {item.name} +
+ ))} +
+ ); +}; diff --git a/src/components/cooperation/cooperationEditor/index.tsx b/src/components/cooperation/cooperationEditor/index.tsx index cc62433..43b914c 100644 --- a/src/components/cooperation/cooperationEditor/index.tsx +++ b/src/components/cooperation/cooperationEditor/index.tsx @@ -17,15 +17,18 @@ const Editor = dynamic(() => import('@monaco-editor/react'), { ssr: false }); type CooperationEditorProps = { roomId: string; + userInfo: Record; }; -export const CooperationEditor: React.FC = ({ roomId }) => { - const ydoc = useMemo(() => new Y.Doc(), []); +export const CooperationEditor: React.FC = ({ roomId, userInfo }) => { + const ydoc = useMemo(() => new Y.Doc({}), []); const [editor, setEditor] = useState(null); const [provider, setProvider] = useState(null); const [, setBinding] = useState(null); const [awareness, setAwareness] = useState(); - const { addPersons, removePersons } = useCooperationPerson(); + const { setPersons } = useCooperationPerson(); + + useEffect(() => {}, []); useEffect(() => { if (roomId == null) { @@ -45,7 +48,6 @@ export const CooperationEditor: React.FC = ({ roomId }) }; }, [ydoc, roomId]); - // This effect manages the lifetime of the editor binding useEffect(() => { if (provider == null || editor == null || roomId === null) { return; @@ -55,6 +57,7 @@ export const CooperationEditor: React.FC = ({ roomId }) x: undefined, y: undefined, }); + provider.awareness.setLocalStateField('userInfo', userInfo); const binding = new MonacoBinding( ydoc.getText(), @@ -81,7 +84,6 @@ export const CooperationEditor: React.FC = ({ roomId }) window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseout', handleMouseout); - // Create a single