@powerhousedao/academy 5.1.0-dev.9 → 5.1.0-staging.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/CHANGELOG.md +37 -0
- package/docs/academy/01-GetStarted/01-CreateNewPowerhouseProject.md +54 -18
- package/docs/academy/01-GetStarted/02-DefineToDoListDocumentModel.md +21 -16
- package/docs/academy/01-GetStarted/03-ImplementOperationReducers.md +1 -24
- package/docs/academy/01-GetStarted/04-WriteDocumentModelTests.md +63 -16
- package/docs/academy/01-GetStarted/05-BuildToDoListEditor.md +22 -25
- package/docs/academy/01-GetStarted/images/Modules.png +0 -0
- package/docs/academy/01-GetStarted/images/VetraStudioDrive.png +0 -0
- package/docs/academy/02-MasteryTrack/01-BuilderEnvironment/05-VetraStudio.md +20 -22
- package/docs/academy/02-MasteryTrack/05-Launch/05-DockerDeployment.md +384 -0
- package/docs/academy/03-ExampleUsecases/Chatroom/03-DefineChatroomDocumentModel.md +35 -98
- package/docs/academy/03-ExampleUsecases/Chatroom/04-ImplementOperationReducers.md +243 -170
- package/docs/academy/03-ExampleUsecases/Chatroom/05-ImplementChatroomEditor.md +257 -49
- package/package.json +1 -1
- package/sidebars.ts +0 -1
|
@@ -36,7 +36,7 @@ Explore the production-ready component structure:
|
|
|
36
36
|
git ls-tree -r --name-only tutorial/main editors/chat-room-editor/components/
|
|
37
37
|
|
|
38
38
|
# View a specific component
|
|
39
|
-
git show tutorial/main:editors/chat-room-editor/components/ChatRoom.tsx
|
|
39
|
+
git show tutorial/main:editors/chat-room-editor/components/ChatRoom/ChatRoom.tsx
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
### Visual comparison with GitHub Desktop
|
|
@@ -91,32 +91,167 @@ We'll build the editor using a component-based approach for better organization
|
|
|
91
91
|
|
|
92
92
|
### Component-based architecture
|
|
93
93
|
|
|
94
|
-
The ChatRoom editor
|
|
94
|
+
The ChatRoom editor uses a modular component structure. Each component has its own folder with an `index.ts` file for clean exports:
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
```
|
|
97
|
+
editors/chat-room-editor/
|
|
98
|
+
├── components/
|
|
99
|
+
│ ├── Avatar/ # User avatar display
|
|
100
|
+
│ │ ├── Avatar.tsx
|
|
101
|
+
│ │ └── index.ts
|
|
102
|
+
│ ├── ChatRoom/ # Main chat container
|
|
103
|
+
│ │ ├── ChatRoom.tsx
|
|
104
|
+
│ │ └── index.ts
|
|
105
|
+
│ ├── Header/ # Chat header with editable title/description
|
|
106
|
+
│ │ ├── EditableLabel.tsx
|
|
107
|
+
│ │ ├── Header.tsx
|
|
108
|
+
│ │ └── index.ts
|
|
109
|
+
│ ├── Message/ # Individual message bubble
|
|
110
|
+
│ │ ├── Message.tsx
|
|
111
|
+
│ │ └── index.ts
|
|
112
|
+
│ ├── MessageItem/ # Message with avatar and reaction dropdown
|
|
113
|
+
│ │ ├── MessageItem.tsx
|
|
114
|
+
│ │ └── index.ts
|
|
115
|
+
│ ├── Reaction/ # Emoji reaction display
|
|
116
|
+
│ │ ├── Reaction.tsx
|
|
117
|
+
│ │ └── index.ts
|
|
118
|
+
│ ├── TextInput/ # Message input field
|
|
119
|
+
│ │ ├── SendIcon.tsx
|
|
120
|
+
│ │ ├── TextInput.tsx
|
|
121
|
+
│ │ └── index.ts
|
|
122
|
+
│ └── index.ts # Central exports
|
|
123
|
+
├── editor.tsx # Main editor component
|
|
124
|
+
├── utils.ts # Utility functions for data mapping
|
|
125
|
+
└── module.ts # Editor module configuration
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Copy components from the reference repository
|
|
129
|
+
|
|
130
|
+
Download the repository of the chatroom-demo as a zip file from https://github.com/powerhouse-inc/chatroom-demo and navigate to `editors/chat-room-editor/` to copy the following:
|
|
131
|
+
|
|
132
|
+
1. **The entire `components/` folder** - Contains all UI components
|
|
133
|
+
2. **The `utils.ts` file** - Contains utility functions for emoji mapping
|
|
102
134
|
|
|
103
|
-
|
|
135
|
+
Here's what each component does:
|
|
104
136
|
|
|
105
|
-
|
|
137
|
+
| Component | Purpose |
|
|
138
|
+
|-----------|---------|
|
|
139
|
+
| `Avatar` | Displays a user avatar image or a deterministic emoji based on the username |
|
|
140
|
+
| `ChatRoom` | Main container that orchestrates the header, messages list, and input field |
|
|
141
|
+
| `Header` | Shows the chat title and description with inline editing capability |
|
|
142
|
+
| `EditableLabel` | Reusable component for inline text editing with edit/cancel icons |
|
|
143
|
+
| `Message` | Renders a single message bubble with styling based on the sender |
|
|
144
|
+
| `MessageItem` | Wraps `Message` with `Avatar` and adds a reaction dropdown menu |
|
|
145
|
+
| `Reaction` | Displays an emoji reaction with a count of users who reacted |
|
|
146
|
+
| `TextInput` | Input field for composing and sending new messages |
|
|
147
|
+
|
|
148
|
+
### The utils.ts file
|
|
149
|
+
|
|
150
|
+
The `utils.ts` file contains helper functions for mapping between document model types and component props:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import type {
|
|
154
|
+
MessageProps,
|
|
155
|
+
ReactionMap,
|
|
156
|
+
} from "./components/Message/Message.js";
|
|
157
|
+
import type {
|
|
158
|
+
Message,
|
|
159
|
+
ReactionType,
|
|
160
|
+
} from "../../document-models/chat-room/gen/schema/types.js";
|
|
161
|
+
|
|
162
|
+
const emojis = [
|
|
163
|
+
"😀", "😂", "🤣", "😍", "😎", "😊", "🙃", "😇", "🤔", "🥳",
|
|
164
|
+
"🤯", "🤗", "😱", "👻", "🎃", "🐱", "🐶", "🐹", "🦊", "🐻",
|
|
165
|
+
"🐼", "🐨", "🐯", "🦁", "🐸", "🐵", "🐔", "🐧", "🐦", "🐤",
|
|
166
|
+
"🐝", "🐞", "🐟", "🐬", "🐳", "🦋", "🌺", "🌸", "🌼", "🍀",
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
export function getEmojiFromString(input: string): string {
|
|
170
|
+
function hashString(str: string): number {
|
|
171
|
+
let hash = 0;
|
|
172
|
+
for (let i = 0; i < str.length; i++) {
|
|
173
|
+
const char = str.charCodeAt(i);
|
|
174
|
+
hash = (hash << 5) - hash + char;
|
|
175
|
+
hash |= 0;
|
|
176
|
+
}
|
|
177
|
+
return Math.abs(hash);
|
|
178
|
+
}
|
|
106
179
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- A header for the chatroom
|
|
111
|
-
- The UI for rendering messages, usernames, and reaction popups
|
|
112
|
-
- The emoji reaction interface
|
|
113
|
-
- A text input field component
|
|
180
|
+
const hash = hashString(input);
|
|
181
|
+
return emojis[hash % emojis.length];
|
|
182
|
+
}
|
|
114
183
|
|
|
115
|
-
|
|
184
|
+
export const reactionTypeToEmoji = (reactionType: ReactionType): string => {
|
|
185
|
+
switch (reactionType) {
|
|
186
|
+
case "HEART":
|
|
187
|
+
return "❤️";
|
|
188
|
+
case "THUMBS_UP":
|
|
189
|
+
return "👍";
|
|
190
|
+
case "THUMBS_DOWN":
|
|
191
|
+
return "👎";
|
|
192
|
+
case "LAUGH":
|
|
193
|
+
return "😂";
|
|
194
|
+
case "CRY":
|
|
195
|
+
return "😢";
|
|
196
|
+
default:
|
|
197
|
+
return "❤️";
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const reactionTypeToReactionKey = (
|
|
202
|
+
reactionType: ReactionType,
|
|
203
|
+
): keyof ReactionMap => {
|
|
204
|
+
switch (reactionType) {
|
|
205
|
+
case "HEART":
|
|
206
|
+
return "heart";
|
|
207
|
+
case "THUMBS_UP":
|
|
208
|
+
return "thumbsUp";
|
|
209
|
+
case "THUMBS_DOWN":
|
|
210
|
+
return "thumbsDown";
|
|
211
|
+
case "LAUGH":
|
|
212
|
+
return "laughing";
|
|
213
|
+
case "CRY":
|
|
214
|
+
return "cry";
|
|
215
|
+
default:
|
|
216
|
+
return "heart";
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export const reactionKeyToReactionType = (
|
|
221
|
+
reactionKey: string,
|
|
222
|
+
): ReactionType => {
|
|
223
|
+
switch (reactionKey) {
|
|
224
|
+
case "heart":
|
|
225
|
+
return "HEART";
|
|
226
|
+
case "thumbsUp":
|
|
227
|
+
return "THUMBS_UP";
|
|
228
|
+
case "thumbsDown":
|
|
229
|
+
return "THUMBS_DOWN";
|
|
230
|
+
case "laughing":
|
|
231
|
+
return "LAUGH";
|
|
232
|
+
case "cry":
|
|
233
|
+
return "CRY";
|
|
234
|
+
default:
|
|
235
|
+
return "HEART";
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const mapReactions = (
|
|
240
|
+
reactions: Message["reactions"],
|
|
241
|
+
): MessageProps["reactions"] => {
|
|
242
|
+
return (reactions || [])
|
|
243
|
+
.map((reaction) => ({
|
|
244
|
+
emoji: reactionTypeToEmoji(reaction.type),
|
|
245
|
+
reactedBy: reaction.reactedBy,
|
|
246
|
+
type: reactionTypeToReactionKey(reaction.type),
|
|
247
|
+
}))
|
|
248
|
+
.filter((reaction) => reaction.reactedBy.length > 0);
|
|
249
|
+
};
|
|
250
|
+
```
|
|
116
251
|
|
|
117
|
-
###
|
|
252
|
+
### The main editor.tsx file
|
|
118
253
|
|
|
119
|
-
|
|
254
|
+
The main `editor.tsx` file connects your document model to the UI components. Replace the generated scaffolding with:
|
|
120
255
|
|
|
121
256
|
```typescript
|
|
122
257
|
import { generateId } from "document-model/core";
|
|
@@ -146,7 +281,6 @@ export default function Editor() {
|
|
|
146
281
|
return <div>Loading...</div>;
|
|
147
282
|
}
|
|
148
283
|
|
|
149
|
-
// Map document messages to component props
|
|
150
284
|
const messages: ChatRoomProps["messages"] =
|
|
151
285
|
document.state.global.messages.map((message) => ({
|
|
152
286
|
id: message.id,
|
|
@@ -158,9 +292,10 @@ export default function Editor() {
|
|
|
158
292
|
reactions: mapReactions(message.reactions),
|
|
159
293
|
}));
|
|
160
294
|
|
|
161
|
-
// Handler for sending messages
|
|
162
295
|
const onSendMessage: ChatRoomProps["onSendMessage"] = (message) => {
|
|
163
|
-
if (!message)
|
|
296
|
+
if (!message) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
164
299
|
|
|
165
300
|
dispatch(
|
|
166
301
|
addMessage({
|
|
@@ -176,7 +311,6 @@ export default function Editor() {
|
|
|
176
311
|
);
|
|
177
312
|
};
|
|
178
313
|
|
|
179
|
-
// Handler for adding reactions
|
|
180
314
|
const addReaction = (
|
|
181
315
|
messageId: string,
|
|
182
316
|
userId: string,
|
|
@@ -191,7 +325,6 @@ export default function Editor() {
|
|
|
191
325
|
);
|
|
192
326
|
};
|
|
193
327
|
|
|
194
|
-
// Handler for removing reactions
|
|
195
328
|
const removeReaction = (
|
|
196
329
|
messageId: string,
|
|
197
330
|
userId: string,
|
|
@@ -206,13 +339,14 @@ export default function Editor() {
|
|
|
206
339
|
);
|
|
207
340
|
};
|
|
208
341
|
|
|
209
|
-
// Handler for clicking on reactions (toggle behavior)
|
|
210
342
|
const onClickReaction: MessageProps["onClickReaction"] = (reaction) => {
|
|
211
343
|
const message = messages.find(
|
|
212
344
|
(message) => message.id === reaction.messageId,
|
|
213
345
|
);
|
|
214
346
|
|
|
215
|
-
if (!message)
|
|
347
|
+
if (!message) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
216
350
|
|
|
217
351
|
const messageId = reaction.messageId;
|
|
218
352
|
const reactionType = reactionKeyToReactionType(reaction.type);
|
|
@@ -233,7 +367,6 @@ export default function Editor() {
|
|
|
233
367
|
}
|
|
234
368
|
};
|
|
235
369
|
|
|
236
|
-
// Handlers for editing chat metadata
|
|
237
370
|
const onSubmitTitle: ChatRoomProps["onSubmitTitle"] = (title) => {
|
|
238
371
|
dispatch(editChatName({ name: title }));
|
|
239
372
|
};
|
|
@@ -245,7 +378,11 @@ export default function Editor() {
|
|
|
245
378
|
};
|
|
246
379
|
|
|
247
380
|
return (
|
|
248
|
-
<div
|
|
381
|
+
<div
|
|
382
|
+
style={{
|
|
383
|
+
height: "calc(100vh - 140px)",
|
|
384
|
+
}}
|
|
385
|
+
>
|
|
249
386
|
<ChatRoom
|
|
250
387
|
description={
|
|
251
388
|
document.state.global.description || "This is a chat room demo"
|
|
@@ -279,6 +416,91 @@ The `useSelectedChatRoomDocument` hook is generated by the Powerhouse CLI. It pr
|
|
|
279
416
|
This hook connects your React components to the document model's state and operations.
|
|
280
417
|
:::
|
|
281
418
|
|
|
419
|
+
## Key components explained
|
|
420
|
+
|
|
421
|
+
### MessageItem component
|
|
422
|
+
|
|
423
|
+
The `MessageItem` component wraps the `Message` component with an avatar and a reaction dropdown menu. It uses the `@powerhousedao/design-system` package for the dropdown:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { Message, type MessageProps } from "../Message/Message.js";
|
|
427
|
+
import { Avatar, type AvatarProps } from "../Avatar/Avatar.js";
|
|
428
|
+
import { useState } from "react";
|
|
429
|
+
import {
|
|
430
|
+
DropdownMenu,
|
|
431
|
+
DropdownMenuContent,
|
|
432
|
+
DropdownMenuTrigger,
|
|
433
|
+
DropdownMenuItem,
|
|
434
|
+
} from "@powerhousedao/design-system";
|
|
435
|
+
|
|
436
|
+
export type MessageItemProps = MessageProps & AvatarProps;
|
|
437
|
+
|
|
438
|
+
export const reactionMap = {
|
|
439
|
+
cry: "😢",
|
|
440
|
+
laughing: "😂",
|
|
441
|
+
heart: "❤️",
|
|
442
|
+
thumbsDown: "👎",
|
|
443
|
+
thumbsUp: "👍",
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
export const MessageItem: React.FC<MessageItemProps> = (props) => {
|
|
447
|
+
const { imgUrl, userName, isCurrentUser, ...messageProps } = props;
|
|
448
|
+
const { disabled = false } = messageProps;
|
|
449
|
+
|
|
450
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
451
|
+
const [open, setOpen] = useState(false);
|
|
452
|
+
|
|
453
|
+
// ... hover and dropdown logic
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<div style={{ display: "flex", gap: "2px", alignItems: "flex-end" }}>
|
|
457
|
+
<Avatar imgUrl={imgUrl} userName={userName} />
|
|
458
|
+
<DropdownMenu>
|
|
459
|
+
<Message isCurrentUser={isCurrentUser} userName={userName} {...messageProps} />
|
|
460
|
+
<DropdownMenuTrigger>🫥</DropdownMenuTrigger>
|
|
461
|
+
<DropdownMenuContent>
|
|
462
|
+
{Object.entries(reactionMap).map(([key, emoji]) => (
|
|
463
|
+
<DropdownMenuItem key={key} onClick={() => /* handle reaction */}>
|
|
464
|
+
{emoji}
|
|
465
|
+
</DropdownMenuItem>
|
|
466
|
+
))}
|
|
467
|
+
</DropdownMenuContent>
|
|
468
|
+
</DropdownMenu>
|
|
469
|
+
</div>
|
|
470
|
+
);
|
|
471
|
+
};
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### EditableLabel component
|
|
475
|
+
|
|
476
|
+
The `EditableLabel` component enables inline editing of text fields (like the chat title and description):
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
export const EditableLabel: React.FC<EditableLabelProps> = ({
|
|
480
|
+
label: initialLabel,
|
|
481
|
+
onSubmit,
|
|
482
|
+
style,
|
|
483
|
+
}) => {
|
|
484
|
+
const [hover, setHover] = useState(false);
|
|
485
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
486
|
+
const [label, setLabel] = useState(initialLabel);
|
|
487
|
+
|
|
488
|
+
// Toggle between read mode (displaying text) and write mode (input field)
|
|
489
|
+
// Press Enter to submit, Escape to cancel
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<div onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}>
|
|
493
|
+
{isEditing ? (
|
|
494
|
+
<input value={label} onChange={(e) => setLabel(e.target.value)} />
|
|
495
|
+
) : (
|
|
496
|
+
<h1>{label}</h1>
|
|
497
|
+
)}
|
|
498
|
+
{(hover || isEditing) && <EditIcon onClick={() => setIsEditing(true)} />}
|
|
499
|
+
</div>
|
|
500
|
+
);
|
|
501
|
+
};
|
|
502
|
+
```
|
|
503
|
+
|
|
282
504
|
## Test your editor
|
|
283
505
|
|
|
284
506
|
Now you can run the Connect app and see the **ChatRoom** editor in action:
|
|
@@ -309,29 +531,15 @@ If you managed to follow this tutorial until this point, you have successfully i
|
|
|
309
531
|
|
|
310
532
|
The tutorial repository includes the complete ChatRoom editor with all components:
|
|
311
533
|
|
|
312
|
-
```
|
|
313
|
-
editors/chat-room-editor/
|
|
314
|
-
├── components/
|
|
315
|
-
│ ├── Avatar.tsx # User avatar display
|
|
316
|
-
│ ├── ChatRoom.tsx # Main chat container
|
|
317
|
-
│ ├── Header.tsx # Chat header with title/description
|
|
318
|
-
│ ├── Message.tsx # Individual message display
|
|
319
|
-
│ ├── EmojiReaction.tsx # Reaction UI
|
|
320
|
-
│ ├── TextInput.tsx # Message input field
|
|
321
|
-
│ └── index.ts # Component exports
|
|
322
|
-
├── editor.tsx # Main editor component
|
|
323
|
-
├── utils.ts # Utility functions for data mapping
|
|
324
|
-
└── module.ts # Editor module configuration
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
**View individual components from the reference:**
|
|
328
|
-
|
|
329
534
|
```bash
|
|
330
535
|
# See the ChatRoom component implementation
|
|
331
|
-
git show tutorial/main:editors/chat-room-editor/components/ChatRoom.tsx
|
|
536
|
+
git show tutorial/main:editors/chat-room-editor/components/ChatRoom/ChatRoom.tsx
|
|
537
|
+
|
|
538
|
+
# Explore the MessageItem component
|
|
539
|
+
git show tutorial/main:editors/chat-room-editor/components/MessageItem/MessageItem.tsx
|
|
332
540
|
|
|
333
|
-
#
|
|
334
|
-
git show tutorial/main:editors/chat-room-editor/components/
|
|
541
|
+
# View the EditableLabel component
|
|
542
|
+
git show tutorial/main:editors/chat-room-editor/components/Header/EditableLabel.tsx
|
|
335
543
|
|
|
336
544
|
# Compare your implementation with the reference
|
|
337
545
|
git diff tutorial/main -- editors/chat-room-editor/
|
package/package.json
CHANGED
package/sidebars.ts
CHANGED
|
@@ -41,7 +41,6 @@ const sidebars = {
|
|
|
41
41
|
items: [
|
|
42
42
|
"academy/MasteryTrack/BuilderEnvironment/Prerequisites",
|
|
43
43
|
"academy/MasteryTrack/BuilderEnvironment/StandardDocumentModelWorkflow",
|
|
44
|
-
"academy/MasteryTrack/BuilderEnvironment/BuilderTools",
|
|
45
44
|
"academy/MasteryTrack/BuilderEnvironment/VetraStudio",
|
|
46
45
|
],
|
|
47
46
|
},
|