From 03dd8df0f1491b226719673b9a25c8186f1fb579 Mon Sep 17 00:00:00 2001 From: Ahmad <103906421+ahmadk953@users.noreply.github.com> Date: Sat, 17 Feb 2024 14:43:59 -0500 Subject: [PATCH] Started Adding Liveblocks --- app/api/liveblocks-auth/route.ts | 26 +++++++ liveblocks.config.ts | 127 +++++++++++++++++++++++++++++++ package-lock.json | 89 ++++++++++++++++++++++ package.json | 9 ++- 4 files changed, 248 insertions(+), 3 deletions(-) create mode 100644 app/api/liveblocks-auth/route.ts create mode 100644 liveblocks.config.ts diff --git a/app/api/liveblocks-auth/route.ts b/app/api/liveblocks-auth/route.ts new file mode 100644 index 0000000..75cd044 --- /dev/null +++ b/app/api/liveblocks-auth/route.ts @@ -0,0 +1,26 @@ +import { Liveblocks } from '@liveblocks/node'; +import { useUser, auth } from '@clerk/nextjs'; + +const liveblocks = new Liveblocks({ + secret: process.env.LIVEBLOCKS_SECRET_DEV_API_KEY!, +}); + +export async function POST(req: Request) { + const { user } = useUser(); + const { orgId } = auth(); + + if (!orgId || !user) return new Response('Unauthorized', { status: 401 }); + + // Start an auth session inside your endpoint + const session = liveblocks.prepareSession(user.id); + + // Implement your own security, and give the user access to the room + const { room } = await req.json(); + if (room) { + session.allow(room, session.FULL_ACCESS); + } + + // Authorize the user and return the result + const { status, body } = await session.authorize(); + return new Response(body, { status }); +} diff --git a/liveblocks.config.ts b/liveblocks.config.ts new file mode 100644 index 0000000..42d25ec --- /dev/null +++ b/liveblocks.config.ts @@ -0,0 +1,127 @@ +import { createClient } from '@liveblocks/client'; +import { createRoomContext } from '@liveblocks/react'; + +const client = createClient({ + // publicApiKey: process.env.LIVEBLOCKS_PUBLIC_DEV_API_KEY!, + authEndpoint: '/api/liveblocks-auth', + // throttle: 100, +}); + +// Presence represents the properties that exist on every user in the Room +// and that will automatically be kept in sync. Accessible through the +// `user.presence` property. Must be JSON-serializable. +type Presence = { + // cursor: { x: number, y: number } | null, + // ... +}; + +// Optionally, Storage represents the shared document that persists in the +// Room, even after all users leave. Fields under Storage typically are +// LiveList, LiveMap, LiveObject instances, for which updates are +// automatically persisted and synced to all connected clients. +type Storage = { + // author: LiveObject<{ firstName: string, lastName: string }>, + // ... +}; + +// Optionally, UserMeta represents static/readonly metadata on each user, as +// provided by your own custom auth back end (if used). Useful for data that +// will not change during a session, like a user's name or avatar. +type UserMeta = { + id?: string; // Accessible through `user.id` + // info?: Json, // Accessible through `user.info` +}; + +// Optionally, the type of custom events broadcast and listened to in this +// room. Use a union for multiple events. Must be JSON-serializable. +type RoomEvent = { + // type: "NOTIFICATION", + // ... +}; + +// Optionally, when using Comments, ThreadMetadata represents metadata on +// each thread. Can only contain booleans, strings, and numbers. +export type ThreadMetadata = { + // resolved: boolean; + // quote: string; + // time: number; +}; + +export const { + suspense: { + RoomProvider, + useRoom, + useMyPresence, + useUpdateMyPresence, + useSelf, + useOthers, + useOthersMapped, + useOthersConnectionIds, + useOther, + useBroadcastEvent, + useEventListener, + useErrorListener, + useStorage, + useObject, + useMap, + useList, + useBatch, + useHistory, + useUndo, + useRedo, + useCanUndo, + useCanRedo, + useMutation, + useStatus, + useLostConnectionListener, + useThreads, + useUser, + useCreateThread, + useEditThreadMetadata, + useCreateComment, + useEditComment, + useDeleteComment, + useAddReaction, + useRemoveReaction, + }, +} = createRoomContext( + client, + { + async resolveUsers({ userIds }) { + // Used only for Comments. Return a list of user information retrieved + // from `userIds`. This info is used in comments, mentions etc. + + // const usersData = await __fetchUsersFromDB__(userIds); + // + // return usersData.map((userData) => ({ + // name: userData.name, + // avatar: userData.avatar.src, + // })); + + return []; + }, + async resolveMentionSuggestions({ text, roomId }) { + // Used only for Comments. Return a list of userIds that match `text`. + // These userIds are used to create a mention list when typing in the + // composer. + // + // For example when you type "@jo", `text` will be `"jo"`, and + // you should to return an array with John and Joanna's userIds: + // ["john@example.com", "joanna@example.com"] + + // const userIds = await __fetchAllUserIdsFromDB__(roomId); + // + // Return all userIds if no `text` + // if (!text) { + // return userIds; + // } + // + // Otherwise, filter userIds for the search `text` and return + // return userIds.filter((userId) => + // userId.toLowerCase().includes(text.toLowerCase()) + // ); + + return []; + }, + } +); diff --git a/package-lock.json b/package-lock.json index fef7267..90a5b16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,9 @@ "dependencies": { "@clerk/nextjs": "^4.29.3", "@hello-pangea/dnd": "^16.5.0", + "@liveblocks/client": "^1.9.8", + "@liveblocks/node": "^1.9.8", + "@liveblocks/react": "^1.9.8", "@prisma/client": "^5.9.1", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.4", @@ -480,6 +483,44 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@liveblocks/client": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@liveblocks/client/-/client-1.9.8.tgz", + "integrity": "sha512-qNFHMKOLLPNlo0HaVL7jfh877PngWYIxuZrpfnFdttR/NtDlt1gH9cL/xoyV5roULf6a5tONcMt2NqJXAFkVEw==", + "dependencies": { + "@liveblocks/core": "1.9.8" + } + }, + "node_modules/@liveblocks/core": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@liveblocks/core/-/core-1.9.8.tgz", + "integrity": "sha512-sIt/pqQzdMSePYxT/9YAhsR8KKDSGDo9uO8S4LJKp7wR2AgzjfXJfEFMXUe2W3D+ugzo5RZODccJS6OdgGzEnQ==" + }, + "node_modules/@liveblocks/node": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@liveblocks/node/-/node-1.9.8.tgz", + "integrity": "sha512-bK88vp0k3P9UngomiZjqhsiyUWKbyUvHmqeCgeSSksu1pjoivVDFu22WJrJRQws4g7+DdfCXN7kzQMD5QKVSzg==", + "dependencies": { + "@liveblocks/core": "1.9.8", + "@stablelib/base64": "^1.0.1", + "fast-sha256": "^1.3.0", + "node-fetch": "^2.6.1" + } + }, + "node_modules/@liveblocks/react": { + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@liveblocks/react/-/react-1.9.8.tgz", + "integrity": "sha512-8ZXHeG8StX5g/YZ5LYejgHBPC5WAQX8vqxshsT0kVyvP+vZCId0FCA/74uAp5DTzP0UVyQ28mINWsHqVC7t/9w==", + "dependencies": { + "@liveblocks/client": "1.9.8", + "@liveblocks/core": "1.9.8", + "nanoid": "^3", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17 || ^18" + } + }, "node_modules/@next/env": { "version": "14.0.4", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.0.4.tgz", @@ -1471,6 +1512,11 @@ "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", "dev": true }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==" + }, "node_modules/@swc/helpers": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", @@ -3555,6 +3601,11 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4823,6 +4874,25 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-fetch-native": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz", @@ -6403,6 +6473,11 @@ "to-no-case": "^1.0.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/ts-api-utils": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", @@ -6711,6 +6786,20 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index e85248e..e276068 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,9 @@ "dependencies": { "@clerk/nextjs": "^4.29.3", "@hello-pangea/dnd": "^16.5.0", + "@liveblocks/client": "^1.9.8", + "@liveblocks/node": "^1.9.8", + "@liveblocks/react": "^1.9.8", "@prisma/client": "^5.9.1", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-avatar": "^1.0.4", @@ -42,20 +45,20 @@ "zustand": "^4.4.7" }, "devDependencies": { + "@next/eslint-plugin-next": "^14.1.0", "@types/lodash": "^4.14.202", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", - "autoprefixer": "^10.0.1", - "@next/eslint-plugin-next": "^14.1.0", "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", + "autoprefixer": "^10.0.1", "eslint": "^8.56.0", "eslint-config-next": "^14.1.0", "eslint-config-prettier": "^9.1.0", + "postcss": "^8", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.11", - "postcss": "^8", "prisma": "^5.9.1", "tailwindcss": "^3.3.0", "typescript": "^5"