@powerhousedao/academy 0.1.0-dev.0
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/.vscode/settings.json +3 -0
- package/CHANGELOG.md +9 -0
- package/Dockerfile +31 -0
- package/ProcFile +1 -0
- package/README.md +43 -0
- package/babel.config.js +3 -0
- package/blog/BeyondCommunication-ABlueprintForDevelopment.md +88 -0
- package/blog/TheChallengeOfChange.md +86 -0
- package/blog/images/Iteration.png +0 -0
- package/blog/images/RAD.png +0 -0
- package/docs/academy/01-GetStarted/00-GetStarted.mdx +181 -0
- package/docs/academy/01-GetStarted/01_InstallDemoPackage.md +38 -0
- package/docs/academy/01-GetStarted/02-ToDoList/01-CreateNewPowerhouseProject.md +97 -0
- package/docs/academy/01-GetStarted/02-ToDoList/02-DefineToDoListDocumentModel.md +86 -0
- package/docs/academy/01-GetStarted/02-ToDoList/03-ImplementOperationReducers.md +201 -0
- package/docs/academy/01-GetStarted/02-ToDoList/04-BuildToDoListEditor.md +494 -0
- package/docs/academy/01-GetStarted/02-ToDoList/_category_.json +8 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/DocumentModelHeader.png +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/DocumentModelOperations.png +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/OpenDocumentModelEditor.gif +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/completeEditor.png +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/connectApp.gif +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/form.png +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/mytodolist.gif +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/reducers.png +0 -0
- package/docs/academy/01-GetStarted/02-ToDoList/images/vscode.png +0 -0
- package/docs/academy/01-GetStarted/styles.module.css +99 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/00-BuilderTools.md +234 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/01-SetupBuilderEnvironment.md +247 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/04-UtilitiesAndTips.md +79 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/01-SetupBuilderEnvironment.md +216 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/02-CreateNewPowerhouseProject.md +78 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/03-DefineChatroomDocumentModel.md +139 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/04-ImplementOperationReducers.md +364 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/05-ImplementChatroomEditor.md +194 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/06-LaunchALocalReactor.md +15 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image-1.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image-2.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image-3.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image-4.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image-5.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/image.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/ChatRoomConnectApp.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/ChatRoomTest.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/completeEditor.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/form.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/reducers.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/vscode.png +0 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/01-Create/02-StandardDocumentModelWorkflow.md +229 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/01-BuildingBeautifulDocumentEditors.md +109 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/02-ConfiguringDrives.md +51 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/03-BuildingADriveExplorer.md +174 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/01-OperationHistory.md +67 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/02-RevisionHistoryTimeline.md +132 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/_category_.json +7 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/images/committer-address-popup.png +0 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/images/revision-hash-popup.png +0 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/images/revision-history-list.png +0 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/07-DocumentTools/images/signature-details-popup.png +0 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/images/CreateDrive.png +0 -0
- package/docs/academy/02-AdvancedTutorial/03-BuildingUserExperiences/images/mytodolist.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/01-ReadingAndWritingThroughTheAPI.mdx +121 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/02-GraphQLAtPowerhouse.md +156 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/03-WorkingWithSubgraphs/02-GraphQLAndSubgraphs.mdx +119 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/03-WorkingWithSubgraphs/03-WorkingWithSubgraphs.md +312 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/03-WorkingWithSubgraphs/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/04-analytics-processor.md +342 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/01-SetupBuilderEnvironment.md +215 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/02-CreateNewPowerhouseProject.md +55 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/03-GenerateAnAnalyticsProcessor.md +173 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/04-UpdateAnalyticsProcessor.md +223 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/Create-SPV.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/Create-a-new-asset.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/Create-a-transaction.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/Transaction-table.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/create-a-new-RWA-document.gif +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/05-AnalyticsProcessorTutorial/images/granularity.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/GraphQL References/QueryingADocumentWithGraphQL.md +244 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/GraphQL References/rwa-reports/listener-raw.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/GraphQL References/rwa-reports/raw-reports1.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/GraphQL References/rwa-reports/raw-reports2.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/GraphQL References/rwa-reports/rwaRegister.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/apse.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/best-practices.md +60 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/filter.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/filteroptions.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/graphql/index.md +166 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/graphql/integration.md +75 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/dbs.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/high-level.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/indexeddb.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/libs-core.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/libs.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/lod.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/logo.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/navbar.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-1.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-2.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-3.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-4.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-5.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/overview-6.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/paths-1.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/images/paths-2.jpg +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/intro.md +149 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/benchmarks.md +27 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/browser.md +77 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/compatibility.md +14 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/index.md +230 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/memory.md +72 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/pg.md +63 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/schema.md +14 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/typescript/utilities.md +102 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/use-cases/index.md +7 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/use-cases/maker.md +652 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/06-Analytics Engine/use-cases/processors.md +3 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/images/OperationHistory.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/images/OperationsQuery.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/images/QueryDocumentID.png +0 -0
- package/docs/academy/02-AdvancedTutorial/04-WorkWithData/images/SwitchboardButton.png +0 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/00-IntegrateInAFront-End +3 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/01-IntroducingFusion +18 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/02-IntroductionToPackages.md +79 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/02-PublishYourProject.md +230 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/03-RunOnACloudServer.md +279 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/03-SetupEnvironment.md +436 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/04-GraphQLNamespacing +44 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/05-LaunchYourBackend.md +3 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/06-LaunchYourFrontend.md +3 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/_category_.json +8 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/images/SSHConnection.png +0 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/images/homedesign.png +0 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/images/keyconcepts.png +0 -0
- package/docs/academy/02-AdvancedTutorial/05-Launch/images/tutorialschema.png +0 -0
- package/docs/academy/02-AdvancedTutorial/06-Authorization/Authorization.md +100 -0
- package/docs/academy/02-AdvancedTutorial/_category_.json +7 -0
- package/docs/academy/03-APIReferences/00-PowerhouseCLI.md +1 -0
- package/docs/academy/03-APIReferences/_category_.json +7 -0
- package/docs/academy/04-ComponentLibrary/01-PowerhouseDesignSystem.md +94 -0
- package/docs/academy/04-ComponentLibrary/02-BuildingWithScalars.md +54 -0
- package/docs/academy/04-ComponentLibrary/03-Scalar-Components/01-phid-field.mdx +72 -0
- package/docs/academy/04-ComponentLibrary/03-Scalar-Components/02-input-field.mdx +0 -0
- package/docs/academy/04-ComponentLibrary/04-Complex-Components/01-sidebar.mdx +36 -0
- package/docs/academy/04-ComponentLibrary/05-Layout-Components/01-test-toupdate.mdx +61 -0
- package/docs/academy/04-ComponentLibrary/06-Fragments/01-test-toupdate.mdx +61 -0
- package/docs/academy/04-ComponentLibrary/_category_.json +8 -0
- package/docs/academy/05-Architecture/00-PowerhouseArchitecture.md +50 -0
- package/docs/academy/05-Architecture/01-WorkingWithTheReactor.md +48 -0
- package/docs/academy/05-Architecture/02-ReferencingMonorepoPackages +65 -0
- package/docs/academy/05-Architecture/04-MovingBeyondCRUD +61 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/01-WhatIsADocumentModel.md +188 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/02-DAOandDocumentsModelsQ+A.md +177 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/02-domain-modeling.md +103 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/03-BenefitsOfDocumentModels.md +95 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/05-best-practices.md +257 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/_category_.json +8 -0
- package/docs/academy/05-Architecture/05-DocumentModelTheory/three-data-layers.png +0 -0
- package/docs/academy/05-Architecture/_category_.json +7 -0
- package/docs/academy/05-Architecture/images/image.png +0 -0
- package/docs/academy/06-Cookbook.md +905 -0
- package/docs/academy/07-Glossary.md +50 -0
- package/docs/bookofpowerhouse/01-Overview.md +29 -0
- package/docs/bookofpowerhouse/02-GeneralFrameworkAndPhilosophy.md +15 -0
- package/docs/bookofpowerhouse/03-PowerhouseSoftwareArchitecture.md +33 -0
- package/docs/bookofpowerhouse/04-DevelopmentApproaches.md +36 -0
- package/docs/bookofpowerhouse/05-SNOsandANewModelForOSSandPublicGoods.md +73 -0
- package/docs/bookofpowerhouse/06-SNOsInActionAndPlatformEconomies.md +17 -0
- package/docs/renown/01-intro.md +18 -0
- package/docs/renown/02-renown-login-flow.md +60 -0
- package/docusaurus +0 -0
- package/docusaurus.config.ts +170 -0
- package/package.json +50 -0
- package/powerhouse-docs@0.0.0 +0 -0
- package/sidebars.ts +33 -0
- package/src/components/HomepageFeatures/index.tsx +250 -0
- package/src/components/HomepageFeatures/styles.module.css +267 -0
- package/src/css/custom.css +450 -0
- package/src/pages/index.module.css +37 -0
- package/src/pages/index.tsx +42 -0
- package/src/pages/markdown-page.md +7 -0
- package/static/.nojekyll +0 -0
- package/static/fonts/FranieBold.otf +0 -0
- package/static/fonts/FranieRegular.otf +0 -0
- package/static/img/Powerhouse Website Drive.png +0 -0
- package/static/img/Powerhouse Website Storage Layer (1).png +0 -0
- package/static/img/Powerhouse Website Storage Layer.png +0 -0
- package/static/img/Powerhouse-main-light.svg +13 -0
- package/static/img/Powerhouse-main.svg +13 -0
- package/static/img/Renown Intro Diagram.png +0 -0
- package/static/img/Union.svg +3 -0
- package/static/img/academy/icons/Advanced.svg +4 -0
- package/static/img/academy/icons/Book.svg +5 -0
- package/static/img/academy/icons/Cookbook.svg +3 -0
- package/static/img/academy/icons/Create.svg +3 -0
- package/static/img/academy/icons/Data.svg +3 -0
- package/static/img/academy/icons/Editor.svg +3 -0
- package/static/img/academy/icons/Flash.svg +3 -0
- package/static/img/academy/icons/Launch.svg +3 -0
- package/static/img/academy-icon.png +0 -0
- package/static/img/connect-icon.png +0 -0
- package/static/img/connect.png +0 -0
- package/static/img/docusaurus-social-card.jpg +0 -0
- package/static/img/docusaurus.png +0 -0
- package/static/img/empty-background.png +0 -0
- package/static/img/favicon.ico +0 -0
- package/static/img/fusion-icon.png +0 -0
- package/static/img/fusion.png +0 -0
- package/static/img/ph-icon-light.svg +3 -0
- package/static/img/powerhouse-layer.png +0 -0
- package/static/img/powerhouse-storage-layer.png +0 -0
- package/static/img/reactor.png +0 -0
- package/static/img/renown-icon.png +0 -0
- package/static/img/renown.png +0 -0
- package/static/img/switchboard-icon.png +0 -0
- package/static/img/switchboard.png +0 -0
- package/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/static/img/undraw_docusaurus_react.svg +170 -0
- package/static/img/undraw_docusaurus_tree.svg +40 -0
- package/static/img/video-placeholder.svg +16 -0
- package/static.json +7 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
|
|
2
|
+
# Implement Operation Reducers
|
|
3
|
+
|
|
4
|
+
In this section, we will implement and test the operation reducers for the `ChatRoom` document model. In order to do this, you have to export the document model from the Connect application and import it into your powerhouse project directory.
|
|
5
|
+
|
|
6
|
+
To export the document model, follow the steps in the `Define Chatroom Document Model` section.
|
|
7
|
+
|
|
8
|
+
## Import Document Model and Generate Code
|
|
9
|
+
|
|
10
|
+
To import the document model into your powerhouse project, you can either:
|
|
11
|
+
|
|
12
|
+
- Copy&Paste the file directly into the root of your powerhouse project.
|
|
13
|
+
- Or drag&drop the file into the powerhouse project directory in the VSCode editor as seen in the image below:
|
|
14
|
+
|
|
15
|
+
Either step will import the document model into your powerhouse project.
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
The next steps will take place in the VSCode editor. Make sure to have it open and the terminal window inside vscode open as well.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
To write the opearation reducers of the `ChatRoom` document model, you need to generate the document model code from the document model file you have exported into the powerhouse project directory.
|
|
23
|
+
|
|
24
|
+
To do this, run the following command in the terminal:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
ph generate ChatRoom.phdm.zip
|
|
28
|
+
```
|
|
29
|
+
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.
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+
Now you can navigate to `/document-models/chat-room/src/reducers/general-operations.ts` and start writing the operation reducers.
|
|
34
|
+
|
|
35
|
+
Open the `general-operations.ts` file and you should see the code that needs to be filled for the five operations you have defined earlier. Image below shows the code that needs to be filled:
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
## Write the Operation Reducers
|
|
40
|
+
|
|
41
|
+
1. Copy&paste the code below into the `general-operations.ts` file in the `reducers` folder.
|
|
42
|
+
2. Save the `general-operations.ts` file.
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
/**
|
|
47
|
+
* This is a scaffold file meant for customization:
|
|
48
|
+
* - modify it by implementing the reducer functions
|
|
49
|
+
* - delete the file and run the code generator again to have it reset
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
import { ChatRoomAddMessageOperations } from "../../gen/add-message/operations";
|
|
53
|
+
import { MessageContentCannotBeEmpty } from "../../gen/add-message/error";
|
|
54
|
+
|
|
55
|
+
export const reducer: ChatRoomAddMessageOperations = {
|
|
56
|
+
addMessageOperation(state, action, dispatch) {
|
|
57
|
+
if (action.input.content === "") {
|
|
58
|
+
throw new MessageContentCannotBeEmpty(); // Your reducer exception is used here as a custom error.
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
state.messages.push({
|
|
62
|
+
id: action.input.messageId,
|
|
63
|
+
content: action.input.content,
|
|
64
|
+
sender: action.input.sender,
|
|
65
|
+
sentAt: action.input.sentAt,
|
|
66
|
+
reactions: [],
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
addEmojiReactionOperation(state, action, dispatch) {
|
|
70
|
+
const message = state.messages.find(
|
|
71
|
+
(message) => message.id === action.input.messageId, // the reducer checks the existence of the message you want to react to.
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!message) {
|
|
75
|
+
throw new Error("Message not found");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const reactions = message.reactions || [];
|
|
79
|
+
|
|
80
|
+
const existingReaction = reactions.find(
|
|
81
|
+
(reaction) => reaction.type === action.input.type,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (existingReaction) { // if the message reaction exists a new reactedBy gets added.
|
|
85
|
+
message.reactions = reactions.map((reaction) => {
|
|
86
|
+
if (reaction.type === action.input.type) {
|
|
87
|
+
return {
|
|
88
|
+
...reaction,
|
|
89
|
+
reactedBy: [...reaction.reactedBy, action.input.reactedBy],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return reaction;
|
|
94
|
+
});
|
|
95
|
+
} else {
|
|
96
|
+
message.reactions = [
|
|
97
|
+
...reactions,
|
|
98
|
+
{
|
|
99
|
+
reactedBy: [action.input.reactedBy], // if the message reaction doesn't exist yet a new reaction gets created
|
|
100
|
+
type: action.input.type,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
state.messages = state.messages.map((_message) => { // the state of the chatroom documents messages gets updated
|
|
106
|
+
if (_message.id === message.id) {
|
|
107
|
+
return message;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return _message;
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
removeEmojiReactionOperation(state, action, dispatch) { // To remove a reaction the address is removed from the reactedBy object in the reactions type.
|
|
114
|
+
state.messages = state.messages.map((message) => {
|
|
115
|
+
if (message.id === action.input.messageId) {
|
|
116
|
+
message.reactions = (message.reactions || []).map((reaction) => {
|
|
117
|
+
if (reaction.type === action.input.type) {
|
|
118
|
+
return {
|
|
119
|
+
...reaction,
|
|
120
|
+
reactedBy: reaction.reactedBy.filter(
|
|
121
|
+
(reactedBy) => reactedBy !== action.input.senderId, // We're removing the sender of the reaction from the the reactedBy object
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return reaction;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return message; // We're updating the document state with our changes
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
editChatNameOperation(state, action, dispatch) {
|
|
134
|
+
state.name = action.input.name || "";
|
|
135
|
+
},
|
|
136
|
+
editChatDescriptionOperation(state, action, dispatch) {
|
|
137
|
+
state.description = action.input.description || "";
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Write the Operation Reducers Tests
|
|
143
|
+
|
|
144
|
+
In order to make sure the operation reducers are working as expected before implementing an editor interface, you need to write tests for them.
|
|
145
|
+
|
|
146
|
+
The auto generated test will only validate if an action or message in our case is included but will not verify if the reducer mutation is succesfull. This is the type of test you'll have to write as a developer.
|
|
147
|
+
|
|
148
|
+
Navigate to `/document-models/chat-room/src/tests/general-operations.test.ts` and copy&paste the code below into the file. Save the file.
|
|
149
|
+
|
|
150
|
+
Here are the tests for the five operations written in the reducers file.
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
/**
|
|
154
|
+
* This is a scaffold file meant for customization:
|
|
155
|
+
* - change it by adding new tests or modifying the existing ones
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
import { generateMock } from "@powerhousedao/codegen";
|
|
159
|
+
import { utils as documentModelUtils } from "document-model/document";
|
|
160
|
+
|
|
161
|
+
import utils from "../../gen/utils";
|
|
162
|
+
import {
|
|
163
|
+
z,
|
|
164
|
+
AddMessageInput,
|
|
165
|
+
AddEmojiReactionInput,
|
|
166
|
+
RemoveEmojiReactionInput,
|
|
167
|
+
EditChatNameInput,
|
|
168
|
+
EditChatDescriptionInput,
|
|
169
|
+
} from "../../gen/schema";
|
|
170
|
+
import { reducer } from "../../gen/reducer";
|
|
171
|
+
import * as creators from "../../gen/add-message/creators";
|
|
172
|
+
import { ChatRoomDocument } from "../../gen/types";
|
|
173
|
+
|
|
174
|
+
describe("AddMessage Operations", () => {
|
|
175
|
+
let document: ChatRoomDocument;
|
|
176
|
+
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
document = utils.createDocument();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const addMessage = (): [ChatRoomDocument, AddMessageInput] => { // This is a helper function for our upcoming test
|
|
182
|
+
const input: AddMessageInput = {
|
|
183
|
+
content: "Hello, World!",
|
|
184
|
+
messageId: documentModelUtils.hashKey(),
|
|
185
|
+
sender: {
|
|
186
|
+
id: "anon-user",
|
|
187
|
+
name: null,
|
|
188
|
+
avatarUrl: null,
|
|
189
|
+
},
|
|
190
|
+
sentAt: new Date().toISOString(),
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const updatedDocument = reducer(document, creators.addMessage(input));
|
|
194
|
+
|
|
195
|
+
return [updatedDocument, input];
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
it("should handle addMessage operation", () => {
|
|
199
|
+
const [updatedDocument, input] = addMessage();
|
|
200
|
+
|
|
201
|
+
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the message is being added to the operations history
|
|
202
|
+
expect(updatedDocument.operations.global[0].type).toBe("ADD_MESSAGE");
|
|
203
|
+
expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
|
|
204
|
+
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
205
|
+
|
|
206
|
+
expect(updatedDocument.state.global.messages).toHaveLength(1); // We're validating that the message is present in the message state of the document
|
|
207
|
+
expect(updatedDocument.state.global.messages[0]).toMatchObject({
|
|
208
|
+
id: input.messageId,
|
|
209
|
+
content: input.content,
|
|
210
|
+
sender: input.sender,
|
|
211
|
+
sentAt: input.sentAt,
|
|
212
|
+
reactions: [], // We also want to make sure that reaction object is an empty array
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("should handle addEmojiReaction operation", () => { // We're validating that we can react using an emoji with a helper function
|
|
217
|
+
const [doc, addMessageInput] = addMessage();
|
|
218
|
+
|
|
219
|
+
let updatedDocument = doc;
|
|
220
|
+
|
|
221
|
+
const addEmojiReactionInput: AddEmojiReactionInput = {
|
|
222
|
+
messageId: addMessageInput.messageId,
|
|
223
|
+
reactedBy: "anon-user",
|
|
224
|
+
type: "THUMBS_UP",
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
updatedDocument = reducer(
|
|
228
|
+
updatedDocument,
|
|
229
|
+
creators.addEmojiReaction(addEmojiReactionInput),
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect(updatedDocument.operations.global).toHaveLength(2); // We're validating that the emoji reaction is added to the operation history of the doc.
|
|
233
|
+
expect(updatedDocument.operations.global[1].type).toBe(
|
|
234
|
+
"ADD_EMOJI_REACTION",
|
|
235
|
+
);
|
|
236
|
+
expect(updatedDocument.operations.global[1].input).toStrictEqual(
|
|
237
|
+
addEmojiReactionInput,
|
|
238
|
+
);
|
|
239
|
+
expect(updatedDocument.operations.global[1].index).toEqual(1);
|
|
240
|
+
|
|
241
|
+
expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(1); // We're validating that the message we created has only one reaction
|
|
242
|
+
expect(
|
|
243
|
+
updatedDocument.state.global.messages[0].reactions?.[0],
|
|
244
|
+
).toMatchObject({
|
|
245
|
+
reactedBy: [addEmojiReactionInput.reactedBy], // We're validating that reactedBy object only contains the right address
|
|
246
|
+
type: addEmojiReactionInput.type,
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it("should handle addEmojiReaction operation to a non existing message", () => { // We're testing that an error is thrown when reacting to a non-existing message
|
|
251
|
+
const input: AddEmojiReactionInput = {
|
|
252
|
+
messageId: "invalid-message-id",
|
|
253
|
+
reactedBy: "anon-user",
|
|
254
|
+
type: "THUMBS_UP",
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const updatedDocument = reducer(document, creators.addEmojiReaction(input));
|
|
258
|
+
|
|
259
|
+
expect(updatedDocument.operations.global).toHaveLength(1);
|
|
260
|
+
expect(updatedDocument.operations.global[0].type).toBe(
|
|
261
|
+
"ADD_EMOJI_REACTION",
|
|
262
|
+
);
|
|
263
|
+
expect(updatedDocument.operations.global[0].error).toBe(
|
|
264
|
+
"Message not found",
|
|
265
|
+
);
|
|
266
|
+
expect(updatedDocument.state.global.messages).toHaveLength(0);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("should handle removeEmojiReaction operation", () => { // We're making use of a helper function to check if we can remove an EmojiReaction
|
|
270
|
+
const [doc, addMessageInput] = addMessage();
|
|
271
|
+
|
|
272
|
+
let updatedDocument = doc;
|
|
273
|
+
|
|
274
|
+
const addEmojiReactionInput: AddEmojiReactionInput = {
|
|
275
|
+
messageId: addMessageInput.messageId,
|
|
276
|
+
reactedBy: "anon-user",
|
|
277
|
+
type: "THUMBS_UP",
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
updatedDocument = reducer(
|
|
281
|
+
updatedDocument,
|
|
282
|
+
creators.addEmojiReaction(addEmojiReactionInput),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const input: RemoveEmojiReactionInput = { // We're validating the removal of a message by our anon-user with a specific messageId
|
|
286
|
+
messageId: addMessageInput.messageId,
|
|
287
|
+
senderId: "anon-user",
|
|
288
|
+
type: "THUMBS_UP",
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
updatedDocument = reducer(
|
|
292
|
+
updatedDocument,
|
|
293
|
+
creators.removeEmojiReaction(input),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect(updatedDocument.operations.global).toHaveLength(3); // We're validating that the operation was added to the operation history.
|
|
297
|
+
expect(updatedDocument.operations.global[2].type).toBe(
|
|
298
|
+
"REMOVE_EMOJI_REACTION",
|
|
299
|
+
);
|
|
300
|
+
expect(updatedDocument.operations.global[2].input).toStrictEqual(input);
|
|
301
|
+
expect(updatedDocument.operations.global[2].index).toEqual(2);
|
|
302
|
+
|
|
303
|
+
expect(updatedDocument.state.global.messages[0].reactions).toHaveLength(1); // The reaction should still exist but no-one should have reacted to it
|
|
304
|
+
expect(
|
|
305
|
+
updatedDocument.state.global.messages[0].reactions?.[0]?.reactedBy,
|
|
306
|
+
).toHaveLength(0);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it("should handle editChatName operation", () => {
|
|
310
|
+
const input: EditChatNameInput = {
|
|
311
|
+
name: "New Chat Name",
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const updatedDocument = reducer(document, creators.editChatName(input));
|
|
315
|
+
|
|
316
|
+
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
|
|
317
|
+
expect(updatedDocument.operations.global[0].type).toBe("EDIT_CHAT_NAME");
|
|
318
|
+
expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
|
|
319
|
+
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
320
|
+
|
|
321
|
+
expect(updatedDocument.state.global.name).toBe(input.name);
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("should handle editChatDescription operation", () => {
|
|
325
|
+
const input: EditChatDescriptionInput = {
|
|
326
|
+
description: "New Chat Description",
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const updatedDocument = reducer(
|
|
330
|
+
document,
|
|
331
|
+
creators.editChatDescription(input),
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
expect(updatedDocument.operations.global).toHaveLength(1); // We're validating that the operation is added to the operations history
|
|
335
|
+
expect(updatedDocument.operations.global[0].type).toBe(
|
|
336
|
+
"EDIT_CHAT_DESCRIPTION",
|
|
337
|
+
);
|
|
338
|
+
expect(updatedDocument.operations.global[0].input).toStrictEqual(input);
|
|
339
|
+
expect(updatedDocument.operations.global[0].index).toEqual(0);
|
|
340
|
+
|
|
341
|
+
expect(updatedDocument.state.global.description).toBe(input.description);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
Now you can run the tests to make sure the operation reducers are working as expected.
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
npm run test
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Output should be as follows:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
Test Files 2 passed (2)
|
|
358
|
+
Tests 7 passed (7)
|
|
359
|
+
Start at 15:19:52
|
|
360
|
+
Duration 3.61s (transform 77ms, setup 0ms, collect 3.50s, tests 14ms, environment 0ms, prepare 474ms)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
If you got the same output, you have successfully implemented the operation reducers and tests for the `ChatRoom` document model.
|
|
364
|
+
Continue to the next section to learn how to implement the document model editor so you can see a simple user interface for the `ChatRoom` document model in action.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
|
|
2
|
+
# Implement Chatroom Editor
|
|
3
|
+
|
|
4
|
+
In this section you will implement the `Chatroom` document model editor. This means you will create a simple user interface for the `Chatroom` document model which will be used inside the Connect app to visualise our chatroom, send messages and emoji reactions.
|
|
5
|
+
|
|
6
|
+
## Generate the editor template
|
|
7
|
+
|
|
8
|
+
Run below command to generate the editor template for the `Chatroom` document model. This command reads the `Chatroom` document model definition from the `document-models` folder and generates the editor template in the `editors/chat-room/editor.tsx` folder.
|
|
9
|
+
|
|
10
|
+
Notice the `--editor` flag which defines the `chatroom` document model editor. And the `--document-types` flag which defines the document type `powerhouse/chatroom`.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
ph generate -- --editor ChatRoomEditor --document-types powerhouse/chat-room
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Once complete, navigate to the `editors/chat-room/editor.tsx` file and open it in your editor.
|
|
17
|
+
|
|
18
|
+
As you'll see you will need to add more complex logic to make the chatroom functional and interact with our document model.
|
|
19
|
+
|
|
20
|
+
## Add the necessary components for your editor first
|
|
21
|
+
|
|
22
|
+
Download the repository of the chatroom-demo as a zip file https://github.com/powerhouse-inc/chatroom-demo
|
|
23
|
+
and navigate to .../chatroom-demo-main/editors/chat-room-editor to copy both the components folder & utils function. In this repository you will also find all of the other code snippets we've been using in this tutorial.
|
|
24
|
+
|
|
25
|
+
Drag the folder with react components & utils functions into your VSCode of your chat-room-editor.
|
|
26
|
+
|
|
27
|
+
In this folder you'll find:
|
|
28
|
+
- An avatar to be set for each chat room participant
|
|
29
|
+
- The chatroom environment itself
|
|
30
|
+
- A header for the chatroom
|
|
31
|
+
- The UI for rendering the message, username and reaction popup.
|
|
32
|
+
- The emoji reaction interface
|
|
33
|
+
- A UI for a text input field
|
|
34
|
+
|
|
35
|
+
The utils function will help you with mapping information from the document model to your chatroom components. Such as your emoji values to the relevant emoji to be displayed.
|
|
36
|
+
|
|
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
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
/* eslint-disable react/jsx-no-bind */
|
|
41
|
+
import { EditorProps } from "document-model/document";
|
|
42
|
+
import {
|
|
43
|
+
ChatRoomState,
|
|
44
|
+
ChatRoomAction,
|
|
45
|
+
ChatRoomLocalState,
|
|
46
|
+
ReactionType,
|
|
47
|
+
actions,
|
|
48
|
+
} from "../../document-models/chat-room";
|
|
49
|
+
import { utils as documentModelUtils } from "document-model/document";
|
|
50
|
+
import { ChatRoom, ChatRoomProps, MessageProps } from "./components";
|
|
51
|
+
import { reactionKeyToReactionType, mapReactions } from "./utils";
|
|
52
|
+
|
|
53
|
+
export type IProps = EditorProps<
|
|
54
|
+
ChatRoomState,
|
|
55
|
+
ChatRoomAction,
|
|
56
|
+
ChatRoomLocalState
|
|
57
|
+
>;
|
|
58
|
+
|
|
59
|
+
export default function Editor(props: IProps) {
|
|
60
|
+
const disableChatRoom = !props.context.user; // we're disabling the chatroom when a user is not logged in.
|
|
61
|
+
|
|
62
|
+
const messages: ChatRoomProps["messages"] =
|
|
63
|
+
props.document.state.global.messages.map((message) => ({ // this object comes from the document state with a mapping that validates which message which user has send.
|
|
64
|
+
id: message.id,
|
|
65
|
+
message: message.content || "",
|
|
66
|
+
timestamp: message.sentAt,
|
|
67
|
+
userName: message.sender.name || message.sender.id,
|
|
68
|
+
imgUrl: message.sender.avatarUrl || undefined, // if the user has an avatar set we'll use it.
|
|
69
|
+
isCurrentUser: message.sender.id === props.context.user?.address,
|
|
70
|
+
reactions: mapReactions(message.reactions),
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
const onSendMessage: ChatRoomProps["onSendMessage"] = (message) => {
|
|
74
|
+
if (!message) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
props.dispatch(
|
|
79
|
+
actions.addMessage({
|
|
80
|
+
messageId: documentModelUtils.hashKey(),
|
|
81
|
+
content: message,
|
|
82
|
+
sender: {
|
|
83
|
+
id: props.context.user?.address || "anon-user",
|
|
84
|
+
name: props.context.user?.ens?.name || null, // The context of the editor allows us to get hold of the users profile information.
|
|
85
|
+
avatarUrl: props.context.user?.ens?.avatarUrl || null,
|
|
86
|
+
},
|
|
87
|
+
sentAt: new Date().toISOString(),
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const addReaction = (
|
|
93
|
+
messageId: string,
|
|
94
|
+
userId: string,
|
|
95
|
+
reactionType: ReactionType,
|
|
96
|
+
) => {
|
|
97
|
+
props.dispatch(
|
|
98
|
+
actions.addEmojiReaction({
|
|
99
|
+
messageId,
|
|
100
|
+
reactedBy: userId,
|
|
101
|
+
type: reactionType,
|
|
102
|
+
}),
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const removeReaction = (
|
|
107
|
+
messageId: string,
|
|
108
|
+
userId: string,
|
|
109
|
+
reactionType: ReactionType,
|
|
110
|
+
) => {
|
|
111
|
+
props.dispatch(
|
|
112
|
+
actions.removeEmojiReaction({
|
|
113
|
+
messageId,
|
|
114
|
+
senderId: userId,
|
|
115
|
+
type: reactionType,
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const onClickReaction: MessageProps["onClickReaction"] = (reaction) => { // This allows us to increase the reactions on a emoji that already has been used as a reaction to a message.
|
|
121
|
+
const message = messages.find(
|
|
122
|
+
(message) => message.id === reaction.messageId,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (!message) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const messageId = reaction.messageId;
|
|
130
|
+
const reactionType = reactionKeyToReactionType(reaction.type);
|
|
131
|
+
const currentUserId = props.context.user?.address || "anon-user";
|
|
132
|
+
|
|
133
|
+
const existingReaction = message.reactions?.find(
|
|
134
|
+
(r) => r.type === reaction.type,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (existingReaction) {
|
|
138
|
+
const dispatchAction = existingReaction.reactedBy.includes(currentUserId)
|
|
139
|
+
? removeReaction
|
|
140
|
+
: addReaction;
|
|
141
|
+
|
|
142
|
+
dispatchAction(messageId, currentUserId, reactionType);
|
|
143
|
+
} else {
|
|
144
|
+
addReaction(messageId, currentUserId, reactionType);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const onSubmitTitle: ChatRoomProps["onSubmitTitle"] = (title) => {
|
|
149
|
+
props.dispatch(actions.editChatName({ name: title }));
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const onSubmitDescription: ChatRoomProps["onSubmitDescription"] = (
|
|
153
|
+
description,
|
|
154
|
+
) => {
|
|
155
|
+
props.dispatch(actions.editChatDescription({ description }));
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div
|
|
160
|
+
style={{
|
|
161
|
+
height: "calc(100vh - 140px)",
|
|
162
|
+
}}
|
|
163
|
+
>
|
|
164
|
+
<ChatRoom
|
|
165
|
+
description={
|
|
166
|
+
props.document.state.global.description || "This is a chat room demo"
|
|
167
|
+
}
|
|
168
|
+
disabled={disableChatRoom} // we're disabling the chatroom when a user is not logged in.
|
|
169
|
+
messages={messages} // the list of messages users are submitting
|
|
170
|
+
onClickReaction={onClickReaction}
|
|
171
|
+
onSendMessage={onSendMessage}
|
|
172
|
+
onSubmitDescription={onSubmitDescription}
|
|
173
|
+
onSubmitTitle={onSubmitTitle}
|
|
174
|
+
title={props.document.state.global.name || "Chat Room Demo"}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Now you can run the Connect app and see the `Chatroom` editor in action.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
ph connect
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
In connect, in the bottom right corner you'll find a new Document Model that you can create: `ChatRoom`. Click on it to create a new Chat Room document. A warning will prompt you to login before you are able to send messages.
|
|
187
|
+
|
|
188
|
+
Login with an ethereum address via Renown to start sending messages.
|
|
189
|
+
|
|
190
|
+
Below GIF shows the `Chatroom` editor in action.
|
|
191
|
+
|
|
192
|
+

|
|
193
|
+
|
|
194
|
+
If you managed to follow this tutorial until this point, you have successfully implemented the `ChatRoom` document model with its reducer operations and editor. In the next chapter we'll be showing you how to simulate a synchronisation between two nodes or users making use of the chatroom by starting up a local reactor.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Launch a local Reactor
|
|
2
|
+
|
|
3
|
+
Now we'll create a local Reactor instance to simulate the behaviour of the chatroom synchronising messages between two nodes or users in the network.
|
|
4
|
+
|
|
5
|
+
Wind down connect and run the following command in your terminal.
|
|
6
|
+
|
|
7
|
+
`ph reactor`
|
|
8
|
+
|
|
9
|
+
This spins up a reactor, which represents a node that is usally hosted as a remote server, but in our case we run it locally on our machine. The reactor you've created will become available as a public drive in your left hand side bar.
|
|
10
|
+
|
|
11
|
+
If you would now open up two different browser windows you will be able to login into two version of connect, with two different ethereum addresses to start validating the functionality of your chatroom.
|
|
12
|
+
|
|
13
|
+
Please go ahead now and send a message to yourself, give yourself a thumbs up or try to make your ENS name appear as your profile when logging in. Also remind yourself that you are able to see the revision history of your document and all it's operation history in the top right corner.
|
|
14
|
+
|
|
15
|
+
Congratulations! You've now explored one of the many ways to use document models within the powerhouse ecosystem.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"label": "Chatroom",
|
|
3
|
+
"position": 2,
|
|
4
|
+
"link": {
|
|
5
|
+
"type": "generated-index",
|
|
6
|
+
"description": "Get started with the Chatroom tutorial! Jump into creating a new Powerhouse project if you have NodeJs, VSCode, and Git installed. Whe are going to create a new document model that represents a chat. So you as a user can post messages into that chat room, react to the messages. This chatroom will be synchronized between different connect instances. Let's explore the potential of the tools available in the powerhouse toolkit"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/ChatRoomTest.gif
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/reducers.png
ADDED
|
Binary file
|
package/docs/academy/02-AdvancedTutorial/01-Create/02-MoreTutorials/Chatroom/images/vscode.png
ADDED
|
Binary file
|