@powerhousedao/academy 4.1.0-dev.121 → 4.1.0-dev.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/docs/academy/01-GetStarted/00-ExploreDemoPackage.mdx +1 -1
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/01-Prerequisites.md +24 -8
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md +25 -7
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/03-BuilderTools.md +17 -3
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/01-WhatIsADocumentModel.md +5 -2
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md +7 -1
- package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md +5 -5
- package/docs/academy/03-ExampleUsecases/Chatroom/02-CreateNewPowerhouseProject.md +4 -1
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +21 -9
- package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +132 -129
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +50 -44
- package/docs/academy/03-ExampleUsecases/Chatroom/image-4.png +0 -0
- package/docs/academy/03-ExampleUsecases/Chatroom/image-5.png +0 -0
- package/docs/academy/03-ExampleUsecases/Chatroom/images/ChatRoomConnectApp.gif +0 -0
- package/docs/academy/03-ExampleUsecases/Chatroom/images/ChatRoomTest.gif +0 -0
- package/package.json +1 -1
- /package/docs/academy/02-MasteryTrack/03-BuildingUserExperiences/06-DocumentTools/{02-RevisionHistoryTimeline.md → 02-RevisionHistoryTimeline} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 4.1.0-dev.122 (2025-11-18)
|
|
2
|
+
|
|
3
|
+
This was a version bump only for @powerhousedao/academy to align it with other projects, there were no code changes.
|
|
4
|
+
|
|
1
5
|
## 4.1.0-dev.121 (2025-11-17)
|
|
2
6
|
|
|
3
7
|
This was a version bump only for @powerhousedao/academy to align it with other projects, there were no code changes.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Let's set up your machine to start building your first Document Model. Don't worry if this is your first time setting up a development environment - we'll guide you through each step!
|
|
4
4
|
|
|
5
5
|
:::info
|
|
6
|
-
If you've already set up **Git, Node, and pnpm**, your most important step is to install the **Powerhouse CLI** with the command: `pnpm install ph-cmd`. A global install is recommended if you want to use the command from any directory as a power user.
|
|
6
|
+
If you've already set up **Git, Node.js 22, and a package manager (pnpm or npm)**, your most important step is to install the **Powerhouse CLI** with the command: `pnpm install -g ph-cmd` or `npm install -g ph-cmd`. A global install is recommended if you want to use the command from any directory as a power user. The Powerhouse CLI is used to create, build, and run your Document Models and gives you direct access to a series of Powerhouse Builder Tools. Move to the end of this page to [verify your installation.](#verify-installation)
|
|
7
7
|
:::
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -43,13 +43,15 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
43
43
|
- Once the installer downloads, double-click it to start installation
|
|
44
44
|
- Click "Next" through the installation wizard, leaving all settings at their defaults
|
|
45
45
|
|
|
46
|
-
3. **Install pnpm:**
|
|
46
|
+
3. **Install a package manager (pnpm or npm):**
|
|
47
47
|
- Open PowerShell (no need for admin mode)
|
|
48
|
-
-
|
|
48
|
+
- For pnpm (recommended), type this command and press Enter:
|
|
49
49
|
|
|
50
50
|
```powershell
|
|
51
51
|
npm install -g pnpm
|
|
52
52
|
```
|
|
53
|
+
|
|
54
|
+
- Note: Node.js comes with npm by default, so npm is already available after installing Node.js
|
|
53
55
|
|
|
54
56
|
4. **Verify Installation:**
|
|
55
57
|
- Open PowerShell (no need for admin mode)
|
|
@@ -57,9 +59,11 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
57
59
|
```powershell
|
|
58
60
|
node --version
|
|
59
61
|
pnpm --version
|
|
62
|
+
# or
|
|
63
|
+
npm --version
|
|
60
64
|
```
|
|
61
65
|
|
|
62
|
-
- You should see version numbers appear after each command (e.g.,
|
|
66
|
+
- You should see version numbers appear after each command (e.g., v22.x.x for Node.js). If you do, congratulations - Node.js and your package manager are installed!
|
|
63
67
|
|
|
64
68
|
> **Note**: If node.js 22 commands don't work in VS Code, restart VS Code to refresh environment variables.
|
|
65
69
|
|
|
@@ -82,7 +86,7 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
82
86
|
brew install node@22
|
|
83
87
|
```
|
|
84
88
|
|
|
85
|
-
- Then, install pnpm:
|
|
89
|
+
- Then, optionally install pnpm (npm comes with Node.js):
|
|
86
90
|
|
|
87
91
|
```bash
|
|
88
92
|
brew install pnpm
|
|
@@ -93,9 +97,11 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
93
97
|
```bash
|
|
94
98
|
node --version
|
|
95
99
|
pnpm --version
|
|
100
|
+
# or
|
|
101
|
+
npm --version
|
|
96
102
|
```
|
|
97
103
|
|
|
98
|
-
- If you see version numbers, you've successfully installed Node.js and
|
|
104
|
+
- If you see version numbers, you've successfully installed Node.js and your package manager!
|
|
99
105
|
|
|
100
106
|
#### For Linux (Ubuntu/Debian):
|
|
101
107
|
|
|
@@ -109,10 +115,12 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
109
115
|
sudo apt update
|
|
110
116
|
```
|
|
111
117
|
|
|
112
|
-
3. **Install node.js 22 and pnpm:**
|
|
118
|
+
3. **Install node.js 22 and optionally pnpm:**
|
|
113
119
|
|
|
114
120
|
```bash
|
|
115
|
-
sudo apt install nodejs
|
|
121
|
+
sudo apt install nodejs
|
|
122
|
+
# Optionally install pnpm (npm comes with Node.js)
|
|
123
|
+
sudo apt install pnpm
|
|
116
124
|
```
|
|
117
125
|
|
|
118
126
|
4. **Verify Installation:**
|
|
@@ -120,6 +128,8 @@ node.js 22 is a tool that lets us run our application. Let's install it step by
|
|
|
120
128
|
```bash
|
|
121
129
|
node --version
|
|
122
130
|
pnpm --version
|
|
131
|
+
# or
|
|
132
|
+
npm --version
|
|
123
133
|
```
|
|
124
134
|
|
|
125
135
|
- If you see version numbers, you're all set!
|
|
@@ -234,6 +244,12 @@ The Powerhouse CLI (installed via the `ph-cmd` package) is a command-line interf
|
|
|
234
244
|
pnpm install -g ph-cmd
|
|
235
245
|
```
|
|
236
246
|
|
|
247
|
+
Or if you're using npm:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
npm install -g ph-cmd
|
|
251
|
+
```
|
|
252
|
+
|
|
237
253
|
Key commands include:
|
|
238
254
|
|
|
239
255
|
- `ph connect` for running the Connect application locally
|
package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/02-StandardDocumentModelWorkflow.md
CHANGED
|
@@ -10,13 +10,13 @@ Please start with the '**Get Started**' Chapter or '**Document Model Creation**'
|
|
|
10
10
|
<details>
|
|
11
11
|
<summary>Key commands that you'll use in this flow</summary>
|
|
12
12
|
|
|
13
|
-
- `pnpm install -g ph-cmd`: Installs the Powerhouse CLI globally.
|
|
13
|
+
- `pnpm install -g ph-cmd` or `npm install -g ph-cmd`: Installs the Powerhouse CLI globally.
|
|
14
14
|
- `ph init`: Initializes a new Powerhouse project or sets up the local environment.
|
|
15
15
|
- `ph connect`: Runs Connect in Studio Mode for local development and testing.
|
|
16
16
|
- `ph generate <YourModelName.phdm.zip>`: Generates scaffolding code from an exported document model specification.
|
|
17
17
|
- `ph generate --editor YourModelName --document-types powerhouse/YourModelName`: Generates an editor template for a document model.
|
|
18
|
-
- `pnpm build`: Builds the project for production.
|
|
19
|
-
- `pnpm run test`: Runs unit tests.
|
|
18
|
+
- `pnpm build` or `npm run build`: Builds the project for production.
|
|
19
|
+
- `pnpm run test` or `npm test`: Runs unit tests.
|
|
20
20
|
- `npm login`: Logs into your NPM account.
|
|
21
21
|
- `npm publish`: Publishes your package to NPM.
|
|
22
22
|
- `ph install @your-org-ph/your-package-name`: Installs a published package into a local Powerhouse environment.
|
|
@@ -33,8 +33,14 @@ Ensure you have the Powerhouse Command Line Interface (`ph-cmd`) installed. This
|
|
|
33
33
|
pnpm install -g ph-cmd
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
Or if you're using npm:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g ph-cmd
|
|
40
|
+
```
|
|
41
|
+
|
|
36
42
|
:::info **Prerequisites**
|
|
37
|
-
|
|
43
|
+
See the [Prerequisites](/academy/MasteryTrack/BuilderEnvironment/Prerequisites) guide for detailed installation instructions for Node.js 22, package managers (pnpm or npm), and Git if you haven't set them up yet.
|
|
38
44
|
:::
|
|
39
45
|
|
|
40
46
|
### 1.2. Initialize your project environment
|
|
@@ -54,9 +60,9 @@ When installing or using the Powerhouse CLI commands you are able to make use of
|
|
|
54
60
|
|
|
55
61
|
| Command | Description |
|
|
56
62
|
| ---------------------------------- | ----------------------------------------------------- |
|
|
57
|
-
| **pnpm install -g ph-cmd** | Install latest stable version |
|
|
58
|
-
| **pnpm install -g ph-cmd@dev** | Install development version |
|
|
59
|
-
| **pnpm install -g ph-cmd@staging** | Install staging version |
|
|
63
|
+
| **pnpm install -g ph-cmd** or **npm install -g ph-cmd** | Install latest stable version |
|
|
64
|
+
| **pnpm install -g ph-cmd@dev** or **npm install -g ph-cmd@dev** | Install development version |
|
|
65
|
+
| **pnpm install -g ph-cmd@staging** or **npm install -g ph-cmd@staging** | Install staging version |
|
|
60
66
|
| **ph init** | Use latest stable version of the boilerplate |
|
|
61
67
|
| **ph init --dev** | Use development version of the boilerplate |
|
|
62
68
|
| **ph init --staging** | Use staging version of the boilerplate |
|
|
@@ -143,6 +149,12 @@ It's crucial to test your reducer logic. Write unit tests in the `document-model
|
|
|
143
149
|
pnpm run test
|
|
144
150
|
```
|
|
145
151
|
|
|
152
|
+
Or with npm:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm test
|
|
156
|
+
```
|
|
157
|
+
|
|
146
158
|
### 3.4. Implement the document editor
|
|
147
159
|
|
|
148
160
|
A document editor provides the user interface for interacting with your document model in Connect. Generate an editor template:
|
|
@@ -262,6 +274,12 @@ Compile and optimize your project for production:
|
|
|
262
274
|
pnpm build
|
|
263
275
|
```
|
|
264
276
|
|
|
277
|
+
Or with npm:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npm run build
|
|
281
|
+
```
|
|
282
|
+
|
|
265
283
|
This command typically creates a `dist/` or `build/` directory with the compiled assets. Ensure your `package.json`'s `files` array includes this directory and other necessary assets like `manifest.json`, `document-models`, and `editors` if they are not part of the build output but need to be in the package.
|
|
266
284
|
|
|
267
285
|
### 4.5. Version control
|
|
@@ -20,6 +20,12 @@ The Powerhouse CLI (`ph-cmd`) is a command-line interface tool that provides ess
|
|
|
20
20
|
pnpm install -g ph-cmd
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
Or if you're using npm:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install -g ph-cmd
|
|
27
|
+
```
|
|
28
|
+
|
|
23
29
|
Key commands include:
|
|
24
30
|
|
|
25
31
|
- `ph connect` for running the Connect application locally
|
|
@@ -36,9 +42,9 @@ When installing or using the Powerhouse CLI commands you are able to make use of
|
|
|
36
42
|
|
|
37
43
|
| Command | Description |
|
|
38
44
|
| ---------------------------------- | ----------------------------------------------------- |
|
|
39
|
-
| **pnpm install -g ph-cmd** | Install latest stable version |
|
|
40
|
-
| **pnpm install -g ph-cmd@dev** | Install development version |
|
|
41
|
-
| **pnpm install -g ph-cmd@staging** | Install staging version |
|
|
45
|
+
| **pnpm install -g ph-cmd** or **npm install -g ph-cmd ** | Install latest stable version |
|
|
46
|
+
| **pnpm install -g ph-cmd@dev** or **npm install -g ph-cmd@dev** | Install development version |
|
|
47
|
+
| **pnpm install -g ph-cmd@staging** or **npm install -g ph-cmd@staging** | Install staging version |
|
|
42
48
|
| **ph init** | Use latest stable version of the boilerplate |
|
|
43
49
|
| **ph init --dev** | Use development version of the boilerplate |
|
|
44
50
|
| **ph init --staging** | Use staging version of the boilerplate |
|
|
@@ -62,6 +68,8 @@ If you need to perform a clean reinstallation of the Powerhouse CLI (`ph-cmd`),
|
|
|
62
68
|
|
|
63
69
|
```bash
|
|
64
70
|
pnpm uninstall -g ph-cmd
|
|
71
|
+
# or with npm
|
|
72
|
+
npm uninstall -g ph-cmd
|
|
65
73
|
```
|
|
66
74
|
|
|
67
75
|
2. Remove the Powerhouse configuration directory:
|
|
@@ -75,12 +83,18 @@ rm -rf ~/.ph
|
|
|
75
83
|
```bash
|
|
76
84
|
# For the latest stable version
|
|
77
85
|
pnpm install -g ph-cmd
|
|
86
|
+
# or with npm
|
|
87
|
+
npm install -g ph-cmd
|
|
78
88
|
|
|
79
89
|
# For the staging version
|
|
80
90
|
pnpm install -g ph-cmd@staging
|
|
91
|
+
# or with npm
|
|
92
|
+
npm install -g ph-cmd@staging
|
|
81
93
|
|
|
82
94
|
# For a specific version
|
|
83
95
|
pnpm install -g ph-cmd@<version>
|
|
96
|
+
# or with npm
|
|
97
|
+
npm install -g ph-cmd@<version>
|
|
84
98
|
```
|
|
85
99
|
|
|
86
100
|
This process ensures a clean slate by removing both the CLI tool and its configuration files before installing the desired version. It's particularly useful when:
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# What is a document model?
|
|
2
2
|
|
|
3
3
|
:::tip
|
|
4
|
-
This chapter on **Document Model Creation** will help you with an in
|
|
5
|
-
|
|
4
|
+
This chapter on **Document Model Creation** will help you with an in-depth practical understanding while building an **advanced to-do list** document model.
|
|
5
|
+
|
|
6
|
+
If you have completed the [Get Started](/academy/GetStarted/CreateNewPowerhouseProject) tutorial (which includes creating a simple to-do list document model), you will revisit familiar topics and can enhance your existing document model with the advanced features covered in this Mastery Track, such as statistics tracking.
|
|
7
|
+
|
|
8
|
+
Although not required, completing the Get Started tutorial first is highly recommended as it provides a hands-on foundation for the concepts explored in depth here.
|
|
6
9
|
:::
|
|
7
10
|
|
|
8
11
|
:::info **Definition: What is a Document Model?**
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/06-ImplementDocumentModelTests.md
CHANGED
|
@@ -112,6 +112,12 @@ Now, run the tests from your project's root directory to verify your implementat
|
|
|
112
112
|
pnpm run test
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
Or with npm:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm test
|
|
119
|
+
```
|
|
120
|
+
|
|
115
121
|
If all tests pass, you have successfully verified the core logic of your `To-do List` document model. This ensures that the reducers you wrote behave exactly as expected.
|
|
116
122
|
|
|
117
123
|
</details>
|
|
@@ -128,7 +134,7 @@ While the tutorial provides a concrete example, keep these general best practice
|
|
|
128
134
|
- **Assert**: Check if the outcome is as expected using `expect()`.
|
|
129
135
|
- **Test Immutability**: A key assertion is to ensure the state is not mutated directly. You can check that a new array or object was created: `expect(newState.items).not.toBe(oldState.items);`.
|
|
130
136
|
- **Cover Edge Cases**: Test what happens when an operation receives invalid input (e.g., trying to update an item that doesn't exist). Your test should confirm the reducer either throws an error or returns the state unchanged, depending on your implementation.
|
|
131
|
-
- **Run Tests Frequently**: Integrate testing into your development workflow. Run tests after making changes to ensure you haven't broken anything. The `pnpm run test` command is your friend.
|
|
137
|
+
- **Run Tests Frequently**: Integrate testing into your development workflow. Run tests after making changes to ensure you haven't broken anything. The `pnpm run test` (or `npm test`) command is your friend.
|
|
132
138
|
|
|
133
139
|
## Conclusion: The payoff of diligent testing
|
|
134
140
|
|
package/docs/academy/02-MasteryTrack/02-DocumentModelCreation/07-ExampleToDoListRepository.md
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
:::info
|
|
4
4
|
|
|
5
|
-
The Todo-demo
|
|
5
|
+
The Todo-demo is maintained by the Powerhouse Team and serves as a reference for testing and introducing new features. It will be continuously updated alongside the accompanying documentation.
|
|
6
6
|
|
|
7
|
-
https://github.com/powerhouse-inc/todo-demo
|
|
7
|
+
https://github.com/powerhouse-inc/todo-demo
|
|
8
8
|
:::
|
|
9
9
|
|
|
10
10
|
There are several ways to explore this package:
|
|
11
11
|
|
|
12
|
-
### Option 1: Rebuild the Todo-demo
|
|
12
|
+
### Option 1: Rebuild the Todo-demo
|
|
13
13
|
|
|
14
|
-
The Todo-demo
|
|
14
|
+
The Todo-demo and repository are your main reference points during the Mastery Track.
|
|
15
15
|
Follow the steps in the "Mastery Track – Document Model Creation" chapters to build along with the examples.
|
|
16
16
|
|
|
17
17
|
### Option 2: Clone and run the code locally
|
|
@@ -35,5 +35,5 @@ ph connect
|
|
|
35
35
|
Alternatively, you can install this package in a Powerhouse project or in your deployed host apps:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
ph install @powerhousedao/todo-demo
|
|
38
|
+
ph install @powerhousedao/todo-demo
|
|
39
39
|
```
|
|
@@ -73,7 +73,10 @@ Watching local document editors at '/home/you/Powerhouse/ChatRoom/editors'...
|
|
|
73
73
|
|
|
74
74
|
A new browser window will open and you will see the Connect application. If it doesn't open automatically, you can open it manually by navigating to `http://localhost:3000/` in your browser.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
If you don't have a local drive created yet, create one using the Generic Drive Explorer app.
|
|
77
|
+
|
|
78
|
+
Open your Local Drive and create a new document model by clicking the `DocumentModel` button in the "New Document" section. The GIF below shows where to click.
|
|
79
|
+
|
|
77
80
|
|
|
78
81
|

|
|
79
82
|
|
|
@@ -65,11 +65,17 @@ enum ReactionType {
|
|
|
65
65
|
|
|
66
66
|
input AddMessageInput {
|
|
67
67
|
messageId: OID! # ID of the message that is being added
|
|
68
|
-
sender:
|
|
68
|
+
sender: SenderInput! # ID of the user sending the message
|
|
69
69
|
content: String! # Content of the message
|
|
70
70
|
sentAt: DateTime!
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
input SenderInput {
|
|
74
|
+
id: ID! # Unique identifier for the sender
|
|
75
|
+
name: String
|
|
76
|
+
avatarUrl: URL # Allows us to pull the ENS and/or nft of the persons profile
|
|
77
|
+
}
|
|
78
|
+
|
|
73
79
|
input AddEmojiReactionInput {
|
|
74
80
|
messageId: OID! # ID of the message to which the reaction is being added
|
|
75
81
|
reactedBy: ID! # ID of the user adding the reaction
|
|
@@ -106,14 +112,14 @@ The steps below show you how to do this:
|
|
|
106
112
|
|
|
107
113
|
3. In the code editor, you can see the SDL for the document model. Replace the existing SDL with the SDL defined in the [State Schema](#state-schema) section above. Only copy and paste the types, leaving the inputs for the next step. You can however already press 'Sync with schema' button to set the initial state of your document model based on your Schema Definition Language. Verify that your Global State Initial Value looks like this.
|
|
108
114
|
|
|
109
|
-
```
|
|
115
|
+
```json
|
|
110
116
|
{
|
|
111
117
|
"id": "",
|
|
112
118
|
"name": "",
|
|
113
|
-
"description":
|
|
114
|
-
"createdAt":
|
|
115
|
-
"createdBy":
|
|
116
|
-
"messages":
|
|
119
|
+
"description": null,
|
|
120
|
+
"createdAt": null,
|
|
121
|
+
"createdBy": null,
|
|
122
|
+
"messages": []
|
|
117
123
|
}
|
|
118
124
|
```
|
|
119
125
|
|
|
@@ -123,11 +129,17 @@ The steps below show you how to do this:
|
|
|
123
129
|
|
|
124
130
|
```graphql
|
|
125
131
|
input AddMessageInput {
|
|
126
|
-
messageId: OID!
|
|
127
|
-
sender:
|
|
128
|
-
content: String!
|
|
132
|
+
messageId: OID!
|
|
133
|
+
sender: SenderInput!
|
|
134
|
+
content: String!
|
|
129
135
|
sentAt: DateTime!
|
|
130
136
|
}
|
|
137
|
+
|
|
138
|
+
input SenderInput {
|
|
139
|
+
id: ID! # Unique identifier for the sender
|
|
140
|
+
name: String
|
|
141
|
+
avatarUrl: URL # Allows us to pull the ENS and/or nft of the persons profile
|
|
142
|
+
}
|
|
131
143
|
```
|
|
132
144
|
|
|
133
145
|
7. Repeat step 6 for the other input operations based on the [Operations Schema](#operations-schema). If you noticed, you only need to add the name `(ADD_EMOJI_REACTION, EDIT_CHAT_NAME, etc)` of the operation without the `input` suffix. Then it will be generated once you press enter.
|
|
@@ -22,7 +22,7 @@ To write the opearation reducers of the `ChatRoom` document model, you need to g
|
|
|
22
22
|
To do this, run the following command in the terminal:
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
ph generate ChatRoom.phdm.
|
|
25
|
+
ph generate ChatRoom.phdm.phd
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
You will see that this action created a range of files for you. Before diving in we'll look at this simple schema to make you familiar with the structure you've defined in the document model once more. It shows how each type is connected to the next one.
|
|
@@ -47,97 +47,95 @@ Open the `general-operations.ts` file and you should see the code that needs to
|
|
|
47
47
|
* - delete the file and run the code generator again to have it reset
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
|
|
53
|
-
export const reducer: ChatRoomAddMessageOperations = {
|
|
54
|
-
addMessageOperation(state, action, dispatch) {
|
|
55
|
-
if (action.input.content === "") {
|
|
56
|
-
throw new MessageContentCannotBeEmpty(); // Your reducer exception is used here as a custom error.
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
state.messages.push({
|
|
60
|
-
id: action.input.messageId,
|
|
61
|
-
content: action.input.content,
|
|
62
|
-
sender: action.input.sender,
|
|
63
|
-
sentAt: action.input.sentAt,
|
|
64
|
-
reactions: [],
|
|
65
|
-
});
|
|
66
|
-
},
|
|
67
|
-
addEmojiReactionOperation(state, action, dispatch) {
|
|
68
|
-
const message = state.messages.find(
|
|
69
|
-
(message) => message.id === action.input.messageId, // the reducer checks the existence of the message you want to react to.
|
|
70
|
-
);
|
|
50
|
+
import { MessageContentCannotBeEmpty } from "../../gen/general-operations/error.js";
|
|
51
|
+
import type { ChatRoomGeneralOperationsOperations } from "chatroom/document-models/chat-room";
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
export const chatRoomGeneralOperationsOperations: ChatRoomGeneralOperationsOperations =
|
|
54
|
+
{
|
|
55
|
+
addMessageOperation(state, action) {
|
|
56
|
+
if (action.input.content === "") {
|
|
57
|
+
throw new MessageContentCannotBeEmpty();
|
|
58
|
+
}
|
|
75
59
|
|
|
76
|
-
|
|
60
|
+
const newMessage = {
|
|
61
|
+
id: action.input.messageId,
|
|
62
|
+
sender: {
|
|
63
|
+
id: action.input.sender.id,
|
|
64
|
+
name: action.input.sender.name || null,
|
|
65
|
+
avatarUrl: action.input.sender.avatarUrl || null,
|
|
66
|
+
},
|
|
67
|
+
content: action.input.content,
|
|
68
|
+
sentAt: action.input.sentAt,
|
|
69
|
+
reactions: [],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
state.messages.push(newMessage);
|
|
73
|
+
},
|
|
74
|
+
addEmojiReactionOperation(state, action) {
|
|
75
|
+
const message = state.messages.find(
|
|
76
|
+
(m) => m.id === action.input.messageId,
|
|
77
|
+
);
|
|
78
|
+
if (!message) {
|
|
79
|
+
return state;
|
|
80
|
+
}
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
if (!message.reactions) {
|
|
83
|
+
message.reactions = [];
|
|
84
|
+
}
|
|
81
85
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (reaction.type === action.input.type) {
|
|
86
|
-
return {
|
|
87
|
-
...reaction,
|
|
88
|
-
reactedBy: [...reaction.reactedBy, action.input.reactedBy],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
86
|
+
const existingReaction = message.reactions.find(
|
|
87
|
+
(r) => r.type === action.input.type,
|
|
88
|
+
);
|
|
91
89
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
{
|
|
98
|
-
reactedBy: [action.input.reactedBy], // if the message reaction doesn't exist yet a new reaction gets created
|
|
90
|
+
if (existingReaction) {
|
|
91
|
+
if (!existingReaction.reactedBy.includes(action.input.reactedBy)) {
|
|
92
|
+
existingReaction.reactedBy.push(action.input.reactedBy);
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
message.reactions.push({
|
|
99
96
|
type: action.input.type,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
97
|
+
reactedBy: [action.input.reactedBy],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
removeEmojiReactionOperation(state, action) {
|
|
102
|
+
const message = state.messages.find(
|
|
103
|
+
(m) => m.id === action.input.messageId,
|
|
104
|
+
);
|
|
105
|
+
if (!message) {
|
|
106
|
+
return state;
|
|
107
|
+
}
|
|
103
108
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (_message.id === message.id) {
|
|
107
|
-
return message;
|
|
109
|
+
if (!message.reactions) {
|
|
110
|
+
return;
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
state.messages = state.messages.map((message) => {
|
|
116
|
-
if (message.id === action.input.messageId) {
|
|
117
|
-
message.reactions = (message.reactions || []).map((reaction) => {
|
|
118
|
-
if (reaction.type === action.input.type) {
|
|
119
|
-
return {
|
|
120
|
-
...reaction,
|
|
121
|
-
reactedBy: reaction.reactedBy.filter(
|
|
122
|
-
(reactedBy) => reactedBy !== action.input.senderId, // We're removing the sender of the reaction from the the reactedBy object
|
|
123
|
-
),
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return reaction;
|
|
128
|
-
});
|
|
113
|
+
const reactionIndex = message.reactions.findIndex(
|
|
114
|
+
(r) => r.type === action.input.type,
|
|
115
|
+
);
|
|
116
|
+
if (reactionIndex === -1) {
|
|
117
|
+
return;
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
120
|
+
const reaction = message.reactions[reactionIndex];
|
|
121
|
+
const userIndex = reaction.reactedBy.indexOf(action.input.senderId);
|
|
122
|
+
|
|
123
|
+
if (userIndex !== -1) {
|
|
124
|
+
reaction.reactedBy.splice(userIndex, 1);
|
|
125
|
+
|
|
126
|
+
if (reaction.reactedBy.length === 0) {
|
|
127
|
+
message.reactions.splice(reactionIndex, 1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
editChatNameOperation(state, action) {
|
|
132
|
+
state.name = action.input.name || "";
|
|
133
|
+
},
|
|
134
|
+
editChatDescriptionOperation(state, action) {
|
|
135
|
+
state.description = action.input.description || "";
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
141
139
|
```
|
|
142
140
|
|
|
143
141
|
## Write the Operation Reducers Tests
|
|
@@ -156,34 +154,38 @@ Here are the tests for the five operations written in the reducers file.
|
|
|
156
154
|
* - change it by adding new tests or modifying the existing ones
|
|
157
155
|
*/
|
|
158
156
|
|
|
159
|
-
import {
|
|
160
|
-
import {
|
|
161
|
-
|
|
162
|
-
import utils from "../../gen/utils";
|
|
157
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
158
|
+
import { generateId } from "document-model/core";
|
|
163
159
|
import {
|
|
164
|
-
|
|
160
|
+
reducer,
|
|
161
|
+
utils,
|
|
162
|
+
addMessage,
|
|
163
|
+
addEmojiReaction,
|
|
164
|
+
removeEmojiReaction,
|
|
165
|
+
editChatName,
|
|
166
|
+
editChatDescription,
|
|
167
|
+
} from "../../gen/index.js";
|
|
168
|
+
import type {
|
|
169
|
+
ChatRoomDocument,
|
|
165
170
|
AddMessageInput,
|
|
166
171
|
AddEmojiReactionInput,
|
|
167
172
|
RemoveEmojiReactionInput,
|
|
168
173
|
EditChatNameInput,
|
|
169
174
|
EditChatDescriptionInput,
|
|
170
|
-
} from "../../gen/
|
|
171
|
-
import { reducer } from "../../gen/reducer";
|
|
172
|
-
import * as creators from "../../gen/add-message/creators";
|
|
173
|
-
import { ChatRoomDocument } from "../../gen/types";
|
|
175
|
+
} from "../../gen/types.js";
|
|
174
176
|
|
|
175
|
-
describe("
|
|
177
|
+
describe("GeneralOperations Operations", () => {
|
|
176
178
|
let document: ChatRoomDocument;
|
|
177
179
|
|
|
178
180
|
beforeEach(() => {
|
|
179
181
|
document = utils.createDocument();
|
|
180
182
|
});
|
|
181
183
|
|
|
182
|
-
const
|
|
183
|
-
// This is a helper function for our upcoming
|
|
184
|
+
const addMessageHelper = (): [ChatRoomDocument, AddMessageInput] => {
|
|
185
|
+
// This is a helper function for our upcoming tests
|
|
184
186
|
const input: AddMessageInput = {
|
|
185
187
|
content: "Hello, World!",
|
|
186
|
-
messageId:
|
|
188
|
+
messageId: generateId(),
|
|
187
189
|
sender: {
|
|
188
190
|
id: "anon-user",
|
|
189
191
|
name: null,
|
|
@@ -192,17 +194,21 @@ describe("AddMessage Operations", () => {
|
|
|
192
194
|
sentAt: new Date().toISOString(),
|
|
193
195
|
};
|
|
194
196
|
|
|
195
|
-
const updatedDocument = reducer(document,
|
|
197
|
+
const updatedDocument = reducer(document, addMessage(input));
|
|
196
198
|
|
|
197
199
|
return [updatedDocument, input];
|
|
198
200
|
};
|
|
199
201
|
|
|
200
202
|
it("should handle addMessage operation", () => {
|
|
201
|
-
const [updatedDocument, input] =
|
|
203
|
+
const [updatedDocument, input] = addMessageHelper();
|
|
202
204
|
|
|
203
205
|
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the message is being added to the operations history
|
|
204
|
-
expect(updatedDocument.operations.global[0].type).toBe(
|
|
205
|
-
|
|
206
|
+
expect(updatedDocument.operations.global[0].action.type).toBe(
|
|
207
|
+
"ADD_MESSAGE",
|
|
208
|
+
);
|
|
209
|
+
expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
|
|
210
|
+
input,
|
|
211
|
+
);
|
|
206
212
|
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
207
213
|
|
|
208
214
|
expect(updatedDocument.state.global.messages).toHaveLength(1); // We're validating that the message is present in the message state of the document
|
|
@@ -217,7 +223,7 @@ describe("AddMessage Operations", () => {
|
|
|
217
223
|
|
|
218
224
|
it("should handle addEmojiReaction operation", () => {
|
|
219
225
|
// We're validating that we can react using an emoji with a helper function
|
|
220
|
-
const [doc, addMessageInput] =
|
|
226
|
+
const [doc, addMessageInput] = addMessageHelper();
|
|
221
227
|
|
|
222
228
|
let updatedDocument = doc;
|
|
223
229
|
|
|
@@ -229,14 +235,14 @@ describe("AddMessage Operations", () => {
|
|
|
229
235
|
|
|
230
236
|
updatedDocument = reducer(
|
|
231
237
|
updatedDocument,
|
|
232
|
-
|
|
238
|
+
addEmojiReaction(addEmojiReactionInput),
|
|
233
239
|
);
|
|
234
240
|
|
|
235
241
|
expect(updatedDocument.operations.global).toHaveLength(2); // We're validating that the emoji reaction is added to the operation history of the doc.
|
|
236
|
-
expect(updatedDocument.operations.global[1].type).toBe(
|
|
242
|
+
expect(updatedDocument.operations.global[1].action.type).toBe(
|
|
237
243
|
"ADD_EMOJI_REACTION",
|
|
238
244
|
);
|
|
239
|
-
expect(updatedDocument.operations.global[1].input).toStrictEqual(
|
|
245
|
+
expect(updatedDocument.operations.global[1].action.input).toStrictEqual(
|
|
240
246
|
addEmojiReactionInput,
|
|
241
247
|
);
|
|
242
248
|
expect(updatedDocument.operations.global[1].index).toEqual(1);
|
|
@@ -251,28 +257,25 @@ describe("AddMessage Operations", () => {
|
|
|
251
257
|
});
|
|
252
258
|
|
|
253
259
|
it("should handle addEmojiReaction operation to a non existing message", () => {
|
|
254
|
-
// We're testing that
|
|
260
|
+
// We're testing that the state doesn't change when reacting to a non-existing message
|
|
255
261
|
const input: AddEmojiReactionInput = {
|
|
256
262
|
messageId: "invalid-message-id",
|
|
257
263
|
reactedBy: "anon-user",
|
|
258
264
|
type: "THUMBS_UP",
|
|
259
265
|
};
|
|
260
266
|
|
|
261
|
-
const updatedDocument = reducer(document,
|
|
267
|
+
const updatedDocument = reducer(document, addEmojiReaction(input));
|
|
262
268
|
|
|
263
269
|
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
264
|
-
expect(updatedDocument.operations.global[0].type).toBe(
|
|
270
|
+
expect(updatedDocument.operations.global[0].action.type).toBe(
|
|
265
271
|
"ADD_EMOJI_REACTION",
|
|
266
272
|
);
|
|
267
|
-
expect(updatedDocument.operations.global[0].error).toBe(
|
|
268
|
-
"Message not found",
|
|
269
|
-
);
|
|
270
273
|
expect(updatedDocument.state.global.messages).toHaveLength(0);
|
|
271
274
|
});
|
|
272
275
|
|
|
273
276
|
it("should handle removeEmojiReaction operation", () => {
|
|
274
277
|
// We're making use of a helper function to check if we can remove an EmojiReaction
|
|
275
|
-
const [doc, addMessageInput] =
|
|
278
|
+
const [doc, addMessageInput] = addMessageHelper();
|
|
276
279
|
|
|
277
280
|
let updatedDocument = doc;
|
|
278
281
|
|
|
@@ -284,7 +287,7 @@ describe("AddMessage Operations", () => {
|
|
|
284
287
|
|
|
285
288
|
updatedDocument = reducer(
|
|
286
289
|
updatedDocument,
|
|
287
|
-
|
|
290
|
+
addEmojiReaction(addEmojiReactionInput),
|
|
288
291
|
);
|
|
289
292
|
|
|
290
293
|
const input: RemoveEmojiReactionInput = {
|
|
@@ -294,22 +297,19 @@ describe("AddMessage Operations", () => {
|
|
|
294
297
|
type: "THUMBS_UP",
|
|
295
298
|
};
|
|
296
299
|
|
|
297
|
-
updatedDocument = reducer(
|
|
298
|
-
updatedDocument,
|
|
299
|
-
creators.removeEmojiReaction(input),
|
|
300
|
-
);
|
|
300
|
+
updatedDocument = reducer(updatedDocument, removeEmojiReaction(input));
|
|
301
301
|
|
|
302
302
|
expect(updatedDocument.operations.global).toHaveLength(3); // We're validating that the operation was added to the operation history.
|
|
303
|
-
expect(updatedDocument.operations.global[2].type).toBe(
|
|
303
|
+
expect(updatedDocument.operations.global[2].action.type).toBe(
|
|
304
304
|
"REMOVE_EMOJI_REACTION",
|
|
305
305
|
);
|
|
306
|
-
expect(updatedDocument.operations.global[2].input).toStrictEqual(
|
|
306
|
+
expect(updatedDocument.operations.global[2].action.input).toStrictEqual(
|
|
307
|
+
input,
|
|
308
|
+
);
|
|
307
309
|
expect(updatedDocument.operations.global[2].index).toEqual(2);
|
|
308
310
|
|
|
309
|
-
|
|
310
|
-
expect(
|
|
311
|
-
updatedDocument.state.global.messages[0].reactions?.[0]?.reactedBy,
|
|
312
|
-
).toHaveLength(0);
|
|
311
|
+
// When the last user removes their reaction, the entire reaction should be removed
|
|
312
|
+
expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(0);
|
|
313
313
|
});
|
|
314
314
|
|
|
315
315
|
it("should handle editChatName operation", () => {
|
|
@@ -317,11 +317,15 @@ describe("AddMessage Operations", () => {
|
|
|
317
317
|
name: "New Chat Name",
|
|
318
318
|
};
|
|
319
319
|
|
|
320
|
-
const updatedDocument = reducer(document,
|
|
320
|
+
const updatedDocument = reducer(document, editChatName(input));
|
|
321
321
|
|
|
322
322
|
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
|
|
323
|
-
expect(updatedDocument.operations.global[0].type).toBe(
|
|
324
|
-
|
|
323
|
+
expect(updatedDocument.operations.global[0].action.type).toBe(
|
|
324
|
+
"EDIT_CHAT_NAME",
|
|
325
|
+
);
|
|
326
|
+
expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
|
|
327
|
+
input,
|
|
328
|
+
);
|
|
325
329
|
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
326
330
|
|
|
327
331
|
expect(updatedDocument.state.global.name).toBe(input.name);
|
|
@@ -332,16 +336,15 @@ describe("AddMessage Operations", () => {
|
|
|
332
336
|
description: "New Chat Description",
|
|
333
337
|
};
|
|
334
338
|
|
|
335
|
-
const updatedDocument = reducer(
|
|
336
|
-
document,
|
|
337
|
-
creators.editChatDescription(input),
|
|
338
|
-
);
|
|
339
|
+
const updatedDocument = reducer(document, editChatDescription(input));
|
|
339
340
|
|
|
340
341
|
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
|
|
341
|
-
expect(updatedDocument.operations.global[0].type).toBe(
|
|
342
|
+
expect(updatedDocument.operations.global[0].action.type).toBe(
|
|
342
343
|
"EDIT_CHAT_DESCRIPTION",
|
|
343
344
|
);
|
|
344
|
-
expect(updatedDocument.operations.global[0].input).toStrictEqual(
|
|
345
|
+
expect(updatedDocument.operations.global[0].action.input).toStrictEqual(
|
|
346
|
+
input,
|
|
347
|
+
);
|
|
345
348
|
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
346
349
|
|
|
347
350
|
expect(updatedDocument.state.global.description).toBe(input.description);
|
|
@@ -9,7 +9,7 @@ Run below command to generate the editor template for the `Chatroom` document mo
|
|
|
9
9
|
Notice the `--editor` flag which defines the `chatroom` document model editor. And the `--document-types` flag which defines the document type `powerhouse/chatroom`.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
ph generate --
|
|
12
|
+
ph generate --editor ChatRoomEditor --document-types powerhouse/chat-room
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Once complete, navigate to the `editors/chat-room/editor.tsx` file and open it in your editor.
|
|
@@ -37,36 +37,41 @@ The utils function will help you with mapping information from the document mode
|
|
|
37
37
|
Now, let's copy & paste the code below into the `editor.tsx` file located at `editors/chat-room-editor`and save the file.
|
|
38
38
|
|
|
39
39
|
```typescript
|
|
40
|
-
|
|
41
|
-
import {
|
|
40
|
+
import { generateId } from "document-model/core";
|
|
41
|
+
import { useUser } from "@powerhousedao/reactor-browser/connect";
|
|
42
|
+
import { useSelectedChatRoomDocument } from "../../document-models/chat-room/hooks.js";
|
|
42
43
|
import {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} from "../../document-models/chat-room";
|
|
49
|
-
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const disableChatRoom = !
|
|
44
|
+
addMessage,
|
|
45
|
+
addEmojiReaction,
|
|
46
|
+
removeEmojiReaction,
|
|
47
|
+
editChatName,
|
|
48
|
+
editChatDescription,
|
|
49
|
+
} from "../../document-models/chat-room/gen/creators.js";
|
|
50
|
+
import {
|
|
51
|
+
ChatRoom,
|
|
52
|
+
type ChatRoomProps,
|
|
53
|
+
type MessageProps,
|
|
54
|
+
} from "./components/index.js";
|
|
55
|
+
import { reactionKeyToReactionType, mapReactions } from "./utils.js";
|
|
56
|
+
|
|
57
|
+
export default function Editor() {
|
|
58
|
+
const [document, dispatch] = useSelectedChatRoomDocument();
|
|
59
|
+
const user = useUser();
|
|
60
|
+
|
|
61
|
+
const disableChatRoom = !user;
|
|
62
|
+
|
|
63
|
+
if (!document) {
|
|
64
|
+
return <div>Loading...</div>;
|
|
65
|
+
}
|
|
61
66
|
|
|
62
67
|
const messages: ChatRoomProps["messages"] =
|
|
63
|
-
|
|
68
|
+
document.state.global.messages.map((message) => ({
|
|
64
69
|
id: message.id,
|
|
65
70
|
message: message.content || "",
|
|
66
71
|
timestamp: message.sentAt,
|
|
67
72
|
userName: message.sender.name || message.sender.id,
|
|
68
|
-
imgUrl: message.sender.avatarUrl || undefined,
|
|
69
|
-
isCurrentUser: message.sender.id ===
|
|
73
|
+
imgUrl: message.sender.avatarUrl || undefined,
|
|
74
|
+
isCurrentUser: message.sender.id === user?.address,
|
|
70
75
|
reactions: mapReactions(message.reactions),
|
|
71
76
|
}));
|
|
72
77
|
|
|
@@ -75,14 +80,14 @@ export default function Editor(props: IProps) {
|
|
|
75
80
|
return;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
messageId:
|
|
83
|
+
dispatch(
|
|
84
|
+
addMessage({
|
|
85
|
+
messageId: generateId(),
|
|
81
86
|
content: message,
|
|
82
87
|
sender: {
|
|
83
|
-
id:
|
|
84
|
-
name:
|
|
85
|
-
avatarUrl:
|
|
88
|
+
id: user?.address || "anon-user",
|
|
89
|
+
name: user?.ens?.name || null,
|
|
90
|
+
avatarUrl: user?.ens?.avatarUrl || null,
|
|
86
91
|
},
|
|
87
92
|
sentAt: new Date().toISOString(),
|
|
88
93
|
}),
|
|
@@ -92,10 +97,10 @@ export default function Editor(props: IProps) {
|
|
|
92
97
|
const addReaction = (
|
|
93
98
|
messageId: string,
|
|
94
99
|
userId: string,
|
|
95
|
-
reactionType:
|
|
100
|
+
reactionType: "HEART" | "THUMBS_UP" | "THUMBS_DOWN" | "LAUGH" | "CRY",
|
|
96
101
|
) => {
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
dispatch(
|
|
103
|
+
addEmojiReaction({
|
|
99
104
|
messageId,
|
|
100
105
|
reactedBy: userId,
|
|
101
106
|
type: reactionType,
|
|
@@ -106,10 +111,10 @@ export default function Editor(props: IProps) {
|
|
|
106
111
|
const removeReaction = (
|
|
107
112
|
messageId: string,
|
|
108
113
|
userId: string,
|
|
109
|
-
reactionType:
|
|
114
|
+
reactionType: "HEART" | "THUMBS_UP" | "THUMBS_DOWN" | "LAUGH" | "CRY",
|
|
110
115
|
) => {
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
dispatch(
|
|
117
|
+
removeEmojiReaction({
|
|
113
118
|
messageId,
|
|
114
119
|
senderId: userId,
|
|
115
120
|
type: reactionType,
|
|
@@ -117,7 +122,7 @@ export default function Editor(props: IProps) {
|
|
|
117
122
|
);
|
|
118
123
|
};
|
|
119
124
|
|
|
120
|
-
const onClickReaction: MessageProps["onClickReaction"] = (reaction) => {
|
|
125
|
+
const onClickReaction: MessageProps["onClickReaction"] = (reaction) => {
|
|
121
126
|
const message = messages.find(
|
|
122
127
|
(message) => message.id === reaction.messageId,
|
|
123
128
|
);
|
|
@@ -128,7 +133,7 @@ export default function Editor(props: IProps) {
|
|
|
128
133
|
|
|
129
134
|
const messageId = reaction.messageId;
|
|
130
135
|
const reactionType = reactionKeyToReactionType(reaction.type);
|
|
131
|
-
const currentUserId =
|
|
136
|
+
const currentUserId = user?.address || "anon-user";
|
|
132
137
|
|
|
133
138
|
const existingReaction = message.reactions?.find(
|
|
134
139
|
(r) => r.type === reaction.type,
|
|
@@ -146,13 +151,13 @@ export default function Editor(props: IProps) {
|
|
|
146
151
|
};
|
|
147
152
|
|
|
148
153
|
const onSubmitTitle: ChatRoomProps["onSubmitTitle"] = (title) => {
|
|
149
|
-
|
|
154
|
+
dispatch(editChatName({ name: title }));
|
|
150
155
|
};
|
|
151
156
|
|
|
152
157
|
const onSubmitDescription: ChatRoomProps["onSubmitDescription"] = (
|
|
153
158
|
description,
|
|
154
159
|
) => {
|
|
155
|
-
|
|
160
|
+
dispatch(editChatDescription({ description }));
|
|
156
161
|
};
|
|
157
162
|
|
|
158
163
|
return (
|
|
@@ -163,18 +168,19 @@ export default function Editor(props: IProps) {
|
|
|
163
168
|
>
|
|
164
169
|
<ChatRoom
|
|
165
170
|
description={
|
|
166
|
-
|
|
171
|
+
document.state.global.description || "This is a chat room demo"
|
|
167
172
|
}
|
|
168
|
-
disabled={disableChatRoom}
|
|
169
|
-
messages={messages}
|
|
173
|
+
disabled={disableChatRoom}
|
|
174
|
+
messages={messages}
|
|
170
175
|
onClickReaction={onClickReaction}
|
|
171
176
|
onSendMessage={onSendMessage}
|
|
172
177
|
onSubmitDescription={onSubmitDescription}
|
|
173
178
|
onSubmitTitle={onSubmitTitle}
|
|
174
|
-
title={
|
|
179
|
+
title={document.state.global.name || "Chat Room Demo"}
|
|
175
180
|
/>
|
|
176
181
|
</div>
|
|
177
182
|
);
|
|
183
|
+
}
|
|
178
184
|
```
|
|
179
185
|
|
|
180
186
|
Now you can run the Connect app and see the `Chatroom` editor in action.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED