Skip to content

Commit ac1c9c2

Browse files
authored
Example: Update avatars/cursors to use high-level components (liveblocks#3145)
1 parent 3a75d7c commit ac1c9c2

33 files changed

Lines changed: 4863 additions & 763 deletions
388 KB
Loading

examples/nextjs-comments-ag-grid/README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,27 @@
99

1010
# AG Grid commenting
1111

12+
<p>
13+
<a href="https://liveblocks.io/examples/ag-grid-comments/nextjs-comments-ag-grid/preview">
14+
<img src="https://img.shields.io/badge/live%20preview-message?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2Ljg0OSA0Ljc1SDBsNC44NDggNS4wNzV2Ny4wMDhsMTItMTIuMDgzWk03LjE1IDE5LjI1SDI0bC00Ljg0OS01LjA3NVY3LjE2N2wtMTIgMTIuMDgzWiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==&color=333" alt="Live Preview" />
15+
</a>
16+
<a href="https://codesandbox.io/s/github/liveblocks/liveblocks/tree/main/examples/nextjs-comments-ag-grid">
17+
<img src="https://img.shields.io/badge/open%20in%20codesandbox-message?style=flat&logo=codesandbox&color=333&logoColor=fff" alt="Open in CodeSandbox" />
18+
</a>
19+
<img src="https://img.shields.io/badge/react-message?style=flat&logo=react&color=0bd&logoColor=fff" alt="React" />
20+
<img src="https://img.shields.io/badge/next.js-message?style=flat&logo=next.js&color=07f&logoColor=fff" alt="Next.js" />
21+
</p>
22+
1223
<p>
1324
<img src="https://img.shields.io/badge/react-message?style=flat&logo=react&color=0bd&logoColor=fff" alt="React" />
1425
<img src="https://img.shields.io/badge/next.js-message?style=flat&logo=next.js&color=07f&logoColor=fff" alt="Next.js" />
1526
</p>
1627

17-
This example shows how to build AG Grid commenting with [Liveblocks](https://liveblocks.io), [AG Grid](https://www.ag-grid.com/), and [Next.js](https://nextjs.org/).
28+
This example shows how to build commenting to your
29+
[AG Grid](https://www.ag-grid.com/) table with
30+
[Liveblocks](https://liveblocks.io) and [Next.js](https://nextjs.org/).
31+
32+
<img src="https://raw.githubusercontent.com/liveblocks/liveblocks/main/.github/assets/examples/comments-table.png" width="536" alt="Collaborative Text Editor" />
1833

1934
## Getting started
2035

@@ -24,7 +39,9 @@ Run the following command to try this example locally:
2439
npx create-liveblocks-app@latest --example nextjs-comments-ag-grid --api-key
2540
```
2641

27-
This will download the example and ask permission to open your browser, enabling you to automatically get your API key from your [liveblocks.io](https://liveblocks.io) account.
42+
This will download the example and ask permission to open your browser, enabling
43+
you to automatically get your API key from your
44+
[liveblocks.io](https://liveblocks.io) account.
2845

2946
### Manual setup
3047

@@ -36,8 +53,10 @@ Alternatively, you can set up your project manually:
3653

3754
- Install all dependencies with `npm install`
3855
- Create an account on [liveblocks.io](https://liveblocks.io/dashboard)
39-
- Copy your **secret** key from the [dashboard](https://liveblocks.io/dashboard/apikeys)
40-
- Create an `.env.local` file and add your **secret** key as the `LIVEBLOCKS_SECRET_KEY` environment variable
56+
- Copy your **secret** key from the
57+
[dashboard](https://liveblocks.io/dashboard/apikeys)
58+
- Create an `.env.local` file and add your **secret** key as the
59+
`LIVEBLOCKS_SECRET_KEY` environment variable
4160
- Run `npm run dev` and go to [http://localhost:3000](http://localhost:3000)
4261

4362
</details>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"use client";
2+
3+
import { LiveblocksProvider } from "@liveblocks/react";
4+
import { PropsWithChildren } from "react";
5+
6+
export function Providers({ children }: PropsWithChildren) {
7+
return (
8+
<LiveblocksProvider
9+
authEndpoint="/api/liveblocks-auth"
10+
resolveUsers={async ({ userIds }) => {
11+
const searchParams = new URLSearchParams(
12+
userIds.map((userId) => ["userIds", userId])
13+
);
14+
const response = await fetch(`/api/users?${searchParams}`);
15+
16+
if (!response.ok) {
17+
throw new Error("Problem resolving users");
18+
}
19+
20+
const users = await response.json();
21+
return users;
22+
}}
23+
>
24+
{children}
25+
</LiveblocksProvider>
26+
);
27+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Liveblocks } from "@liveblocks/node";
2+
import { NextRequest } from "next/server";
3+
4+
import { getRandomUser, getUser } from "../../../database";
5+
6+
/**
7+
* Authenticating your Liveblocks application
8+
* https://liveblocks.io/docs/authentication
9+
*/
10+
11+
const liveblocks = new Liveblocks({
12+
secret: process.env.LIVEBLOCKS_SECRET_KEY!,
13+
});
14+
15+
export async function POST(request: NextRequest) {
16+
const body = await request.json().catch(() => ({}));
17+
const userId = body.userId as string | undefined;
18+
const user = userId ? getUser(userId) : getRandomUser();
19+
20+
if (!user) {
21+
return new Response(null, { status: 401 });
22+
}
23+
24+
const session = liveblocks.prepareSession(user.id, {
25+
userInfo: user.info,
26+
});
27+
28+
session.allow(`liveblocks:examples:*`, session.FULL_ACCESS);
29+
30+
const { status, body: responseBody } = await session.authorize();
31+
return new Response(responseBody, { status });
32+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
3+
import { getUser } from "../../../database";
4+
5+
export async function GET(request: NextRequest) {
6+
const { searchParams } = new URL(request.url);
7+
const userIds = searchParams.getAll("userIds");
8+
9+
if (!userIds.length) {
10+
return NextResponse.json(
11+
{ error: "Missing or invalid userIds" },
12+
{ status: 400 }
13+
);
14+
}
15+
16+
return NextResponse.json(
17+
userIds.map((userId) => getUser(userId)?.info ?? null)
18+
);
19+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import "../styles/globals.css";
2+
import { Providers } from "./Providers";
3+
import "@liveblocks/react-ui/styles.css";
4+
import { Suspense } from "react";
5+
6+
export default function RootLayout({
7+
children,
8+
}: {
9+
children: React.ReactNode;
10+
}) {
11+
return (
12+
<html lang="en">
13+
<head>
14+
<title>Liveblocks</title>
15+
<meta name="robots" content="noindex" />
16+
<meta name="viewport" content="width=device-width, user-scalable=no" />
17+
<link
18+
href="https://liveblocks.io/favicon-32x32.png"
19+
rel="icon"
20+
sizes="32x32"
21+
type="image/png"
22+
/>
23+
<link
24+
href="https://liveblocks.io/favicon-16x16.png"
25+
rel="icon"
26+
sizes="16x16"
27+
type="image/png"
28+
/>
29+
</head>
30+
<body>
31+
<Suspense>
32+
<Providers>{children}</Providers>
33+
</Suspense>
34+
</body>
35+
</html>
36+
);
37+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"use client";
2+
3+
import { useMemo } from "react";
4+
import { useSearchParams } from "next/navigation";
5+
import { RoomProvider } from "@liveblocks/react";
6+
import { AvatarStack } from "@liveblocks/react-ui";
7+
8+
function Example() {
9+
return (
10+
<main
11+
style={{
12+
display: "flex",
13+
height: "100vh",
14+
width: "100vw",
15+
justifyContent: "center",
16+
alignItems: "center",
17+
}}
18+
>
19+
<AvatarStack max={5} size={48} />
20+
</main>
21+
);
22+
}
23+
24+
export default function Page() {
25+
const roomId = useExampleRoomId("liveblocks:examples:nextjs-live-avatars");
26+
27+
return (
28+
<RoomProvider id={roomId}>
29+
<Example />
30+
</RoomProvider>
31+
);
32+
}
33+
34+
/**
35+
* This function is used when deploying an example on liveblocks.io.
36+
* You can ignore it completely if you run the example locally.
37+
*/
38+
function useExampleRoomId(roomId: string) {
39+
const searchParams = useSearchParams();
40+
const exampleId = searchParams?.get("exampleId");
41+
42+
const exampleRoomId = useMemo(() => {
43+
return exampleId ? `${roomId}-${exampleId}` : roomId;
44+
}, [roomId, exampleId]);
45+
46+
return exampleRoomId;
47+
}

examples/nextjs-live-avatars/components/Avatar.module.css

Lines changed: 0 additions & 37 deletions
This file was deleted.

examples/nextjs-live-avatars/components/Avatar.tsx

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const USER_INFO: Liveblocks["UserMeta"][] = [
2+
{
3+
id: "charlie.layne@example.com",
4+
info: {
5+
name: "Charlie Layne",
6+
color: "#D583F0",
7+
avatar: "https://liveblocks.io/avatars/avatar-1.png",
8+
},
9+
},
10+
{
11+
id: "mislav.abha@example.com",
12+
info: {
13+
name: "Mislav Abha",
14+
color: "#F08385",
15+
avatar: "https://liveblocks.io/avatars/avatar-2.png",
16+
},
17+
},
18+
{
19+
id: "tatum.paolo@example.com",
20+
info: {
21+
name: "Tatum Paolo",
22+
color: "#F0D885",
23+
avatar: "https://liveblocks.io/avatars/avatar-3.png",
24+
},
25+
},
26+
{
27+
id: "anjali.wanda@example.com",
28+
info: {
29+
name: "Anjali Wanda",
30+
color: "#85EED6",
31+
avatar: "https://liveblocks.io/avatars/avatar-4.png",
32+
},
33+
},
34+
{
35+
id: "jody.hekla@example.com",
36+
info: {
37+
name: "Jody Hekla",
38+
color: "#85BBF0",
39+
avatar: "https://liveblocks.io/avatars/avatar-5.png",
40+
},
41+
},
42+
{
43+
id: "emil.joyce@example.com",
44+
info: {
45+
name: "Emil Joyce",
46+
color: "#8594F0",
47+
avatar: "https://liveblocks.io/avatars/avatar-6.png",
48+
},
49+
},
50+
{
51+
id: "jory.quispe@example.com",
52+
info: {
53+
name: "Jory Quispe",
54+
color: "#85DBF0",
55+
avatar: "https://liveblocks.io/avatars/avatar-7.png",
56+
},
57+
},
58+
{
59+
id: "quinn.elton@example.com",
60+
info: {
61+
name: "Quinn Elton",
62+
color: "#87EE85",
63+
avatar: "https://liveblocks.io/avatars/avatar-8.png",
64+
},
65+
},
66+
];
67+
68+
export function getRandomUser() {
69+
return USER_INFO[Math.floor(Math.random() * 10) % USER_INFO.length];
70+
}
71+
72+
export function getUser(id: string) {
73+
return USER_INFO.find((u) => u.id === id) || null;
74+
}
75+
76+
export function getUsers() {
77+
return USER_INFO;
78+
}

0 commit comments

Comments
 (0)