Skip to content

Commit 3346c1f

Browse files
committed
༼ つ ◕_◕ ༽つ give instldraw
1 parent 3b7c866 commit 3346c1f

22 files changed

+2904
-251
lines changed

README.md

Lines changed: 136 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1-
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
1+
# 🎨 [instldraw](https://draw.instantdb.com/): Multiplayer Drawings with Instant 🤝 tldraw
2+
3+
Welcome! We took [tldraw](https://tldraw.dev/)'s infinite canvas and added real-time team collaboration powered by Instant's [graph database](https://www.instantdb.com/docs/instaml) and [presence](https://www.instantdb.com/docs/presence-and-topics). If you’re curious, check out [this essay](https://www.instantdb.com/essays/next_firebase) to learn more about “why Instant”.
4+
5+
https://github.com/jsventures/instldraw/assets/1624703/bde87c77-10b5-4267-9c82-20ac6755fabc
6+
7+
### 🎁 Features
8+
9+
- Auth via ["magic code" login](https://www.instantdb.com/docs/auth#magic-codes).
10+
- A full-fledged teams/memberships/invites [data model](https://www.instantdb.com/docs/modeling-data) and secured by [permissions](https://www.instantdb.com/docs/permissions).
11+
- Multiplayer cursors via [presence](https://www.instantdb.com/docs/presence-and-topics).
12+
13+
## 🛣️ Getting Started
214

3-
## Getting Started
15+
### ⚡ 1. Create a free account on [Instant](https://www.instantdb.com/)
416

5-
First, run the development server:
17+
Head on over to the [Instant dashboard](https://www.instantdb.com/dash), grab your app's ID, and plop it into a `.env.development.local` file:
18+
19+
```bash
20+
NEXT_PUBLIC_INSTANT_APP_ID=__YOUR_APP_ID__
21+
```
22+
23+
### 🔥 2. Install and Run
24+
25+
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
26+
27+
Install dependencies with your package manager of choice, then start the development server:
628

729
```bash
830
npm run dev
@@ -14,27 +36,125 @@ pnpm dev
1436
bun dev
1537
```
1638

17-
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
39+
That's it! 🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Next.js will live-update the page as you edit the app's code.
40+
41+
Finally, to add a layer of security to your app, copy `resources/instant-perms.json` into the [Instant Permissions editor](https://www.instantdb.com/dash?s=main&t=perms).
42+
43+
## 📚 Reference
44+
45+
### 📂 Structure
1846

19-
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
47+
The app is broken up into two pages:
2048

21-
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
49+
- An index page `/`, which serves as a dashboard and directory of teams and drawings.
50+
- A drawing page `/drawings/:id` where we render the [tldraw](https://tldraw.dev/) canvas.
2251

23-
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
52+
Both pages load data from Instant with `db.useQuery` and write data using functions from `src/mutators.ts`.
2453

25-
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
54+
### 📄 Notable Files
2655

27-
## Learn More
56+
- `src/pages/index.tsx`: The main dashboard: list and manage teams, teammates, and drawings.
57+
- `src/pages/drawings/[id].tsx`: The canvas! Uses `useInstantStore` and `useInstantPresence` to add multiplayer.
58+
- `src/lib/useInstantStore.tsx`: A collaborative backend for tldraw built on top of Instant's real-time database. Uses InstaML's [merge()](https://www.instantdb.com/docs/instaml#merge) for fine-grained updates to the drawing state.
59+
- `src/lib/useInstantPresence.tsx`: A React hook responsible for keeping tldraw's editor state in sync with [Instant's real-time presence API](https://www.instantdb.com/docs/presence-and-topics).
60+
- `src/mutators.ts`: All functions that update Instant's database live here. You can inspect and edit your database using the [Instant Explorer](https://www.instantdb.com/dash?s=main&t=explorer).
2861

29-
To learn more about Next.js, take a look at the following resources:
62+
### 🧩 Data Model
3063

31-
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
32-
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
64+
```graphql
65+
drawings {
66+
team: @has_one(teams)
67+
state: DrawingState{} # see src/types.ts
68+
name: string
69+
}
3370

34-
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
71+
teams {
72+
drawings: @has_many(drawings)
73+
invites: @has_many(invites)
74+
memberships: @has_many(memberships)
75+
name: string
76+
creatorId: string
77+
}
3578

36-
## Deploy on Vercel
79+
invites {
80+
team: @has_one(teams)
81+
teamId: string
82+
teamName: string
83+
userEmail: string
84+
}
3785

38-
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
86+
memberships {
87+
team: @has_one(teams)
88+
teamId: string
89+
userId: string
90+
userEmail: string
91+
}
92+
```
93+
94+
### 🛡️ Permissions
95+
96+
Instant provides a [permissions layer](https://www.instantdb.com/docs/permissions) to define access control for all entities in your database using [CEL](https://github.com/google/cel-spec/blob/master/doc/langdef.md). Copy the contents of [`resources/instant-perms.json`](https://github.com/jsventures/instldraw/blob/main/resources/instant-perms.json) below into your app's [permissions editor](https://www.instantdb.com/dash?s=main&t=perms).
97+
98+
```json
99+
{
100+
"teams": {
101+
"bind": [
102+
"isCreator",
103+
"auth.id == data.creatorId",
104+
"isMember",
105+
"auth.id in data.ref('memberships.userId')"
106+
],
107+
"allow": {
108+
"view": "isMember",
109+
"create": "isCreator",
110+
"delete": "isCreator",
111+
"update": "isCreator"
112+
}
113+
},
114+
"invites": {
115+
"bind": [
116+
"isMember",
117+
"auth.id in data.ref('team.memberships.userId')",
118+
"isInvitee",
119+
"auth.email == data.userEmail"
120+
],
121+
"allow": {
122+
"view": "isInvitee",
123+
"create": "isMember",
124+
"delete": "isMember",
125+
"update": "false"
126+
}
127+
},
128+
"drawings": {
129+
"bind": ["isMember", "auth.id in data.ref('team.memberships.userId')"],
130+
"allow": {
131+
"view": "isMember",
132+
"create": "isMember",
133+
"delete": "isMember",
134+
"update": "isMember"
135+
}
136+
},
137+
"memberships": {
138+
"bind": [
139+
"isMember",
140+
"auth.id in data.ref('team.memberships.userId')",
141+
"isInviteeOrCreator",
142+
"size(data.ref('team.invites.id')) == 0 ? auth.id in data.ref('team.creatorId') : auth.email in data.ref('team.invites.userEmail')",
143+
"isUser",
144+
"auth.id == data.userId"
145+
],
146+
"allow": {
147+
"view": "isMember",
148+
"create": "isInviteeOrCreator",
149+
"delete": "isUser",
150+
"update": "false"
151+
}
152+
}
153+
}
154+
```
39155

40-
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
156+
- `data` is a variable provided by Instant that references the object being operated upon.
157+
- `auth` is a variable provided by Instant that you can use to access the current user's ID and email.
158+
- You can leverage Instant's graph database in your rule definitions using `data.ref(<key_path>)`! `ref` will traverse an entity's links and collect the values of your specified property path.
159+
- For example, `teams.bind:isMember` above uses `data.ref('memberships.userId')` to collect all linked memberships for a given team, then select the `userId` property for each membership.
160+
- You can use the `bind` param to define abstractions (`isMember`, `isInvitee`) that can be re-used across rules.

package.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,21 @@
99
"lint": "next lint"
1010
},
1111
"dependencies": {
12+
"@instantdb/react": "^0.10.27",
13+
"lodash": "^4.17.21",
14+
"next": "14.2.4",
1215
"react": "^18",
1316
"react-dom": "^18",
14-
"next": "14.2.4"
17+
"tldraw": "^2.2.1"
1518
},
1619
"devDependencies": {
17-
"typescript": "^5",
20+
"@types/lodash": "^4.17.6",
1821
"@types/node": "^20",
1922
"@types/react": "^18",
2023
"@types/react-dom": "^18",
2124
"postcss": "^8",
22-
"tailwindcss": "^3.4.1"
25+
"tailwindcss": "^3.4.1",
26+
"typescript": "^5"
2327
},
2428
"packageManager": "pnpm@8.15.6+sha512.77b89e9be77a2b06ad8f403a19cae5e22976f61023f98ad323d5c30194958ebc02ee0a6ae5d13ee454f6134e4e8caf29a05f0b1a0e1d2b17bca6b6a1f1159f86"
2529
}

0 commit comments

Comments
 (0)