Skip to content

Commit

Permalink
PF-1391 - Adding breakout room example app (#226)
Browse files Browse the repository at this point in the history
* PF-1391 - Adding breakout room example app

* PF-1391 - Creating breakout rooms

* PF-1391 - Initiate timer feature in breakout room

* PF-1391 - Timer UI

* PF-1391 - Adding TODO in styles

* PF-1391 - Implementing ticking time

* PF-1391 - Small adjustments in hooks

* PF-1391 - Fixing session end hooks

* Adding demo video

* PF-1391 - Iterating on README with folder structure

* Remove video from selfie

* PF-1391 - Bumping @mirohq/[email protected]

* PF-1391 - Using multiple sessions

* UX iterations

* Adjusting styles

* Use avatars

* Minor fixes

* Update README.md

* Remove notes

* Update README.md

* Update sdk types package

* PF-1391 - Update README with new folder structured

* PF-1391 - Adding real-time events doc links

* PF-1391 - Bumping @mirohq/sdk-types package to 2.9.10

* Update examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx

Co-authored-by: Mettin Parzinski <[email protected]>

* PF-1391 - Styling using class names

* PF-1391 - Reorganizing styles

* PF-1391 - Cleaning up imports

* PF-1391 - Treat facilitator differently

* Update README.md with video and note about DS

* PF-1391 - Bump SDK version

* PF-1391 - Runs prettier

* PF-1391 - Bump package

---------

Co-authored-by: Mettin Parzinski <[email protected]>
  • Loading branch information
fredcido and mettin authored Nov 29, 2023
1 parent 466025f commit a6058d8
Show file tree
Hide file tree
Showing 44 changed files with 4,495 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ npx create-miro-app@latest
| [blob-maker](examples/blob-maker) | This example shows you how to create a drag and drop blobmaker using Miro's Web SDK. |
| [youtube-room](examples/youtube-room) | This example shows you how to sync a YouTube player across multiple users through Socket.IO. |
| [custom-actions](examples/custom-actions) | This example shows you how register [custom actions](https://developers.miro.com/docs/add-custom-actions-to-your-app) in the item context menu. |
| [breakout-rooms](examples/breakout-rooms) | This example shows you how use collaborative features (real-time storage, events, sessions, etc) |

<p>&nbsp;</p>

Expand Down
24 changes: 24 additions & 0 deletions examples/breakout-rooms/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.next

# testing
/coverage

# misc
.DS_Store
*.pem
.idea

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env
dist
5 changes: 5 additions & 0 deletions examples/breakout-rooms/APP_SUBMISSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Submission to Miro Marketplace

Congrats! You have finished building your app & you'd like to publish it for
users. You can submit your app on the
[Miro Marketplace](https://developers.miro.com/docs/submit-your-app) for review.
121 changes: 121 additions & 0 deletions examples/breakout-rooms/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Miro Breakout rooms

This example shows you how leverage collaborative and real-time features, including sessions and real-time events and storage.

# 👨🏻‍💻 App Demo

https://github.com/miroapp/app-examples/assets/7162412/e03987e3-f85b-48ab-86b8-f4314c3c5e76

# 📒 Table of Contents

- [Included Features](#features)
- [Tools and Technologies](#tools)
- [Prerequisites](#prerequisites)
- [Associated Developer Tutorial](#tutorial)
- [Run the app locally](#run)
- [Folder Structure](#folder)
- [License](#license)

# ⚙️ Included Features <a name="features"></a>

- [Miro Web SDK](https://developers.miro.com/docs/web-sdk-reference)
- [Collaborative sessions](https://developers.miro.com/docs/websdk-reference-session)
- [Attention Management](https://developers.miro.com/docs/websdk-reference-collaboration)
- [Real-time events](https://developers.miro.com/docs/websdk-reference-events)
- [Real-time storage](https://developers.miro.com/docs/websdk-reference-storage)

# 🛠️ Tools and Technologies <a name="tools"></a>

- [React](https://react.dev/)
- [TypeScript](https://www.typescriptlang.org/)
- [Vite](https://vitejs.dev/)
- [Miro Design System](https://www.npmjs.com/package/@mirohq/design-system)

**Note**: Miro Design System can be installed from [npmjs](https://www.npmjs.com/) but it is internally supported only, you can use this example as guidance, but the team is working on public documentation, but with no ETA. Feel free to use [Mirotone](https://www.mirotone.xyz/css) in the meanwhile.

# ✅ Prerequisites <a name="prerequisites"></a>

- You have a [Miro account](https://miro.com/signup/).
- You're [signed in to Miro](https://miro.com/login/).
- Your Miro account has a [Developer team](https://developers.miro.com/docs/create-a-developer-team).
- Your development environment includes [Node.js 14.13](https://nodejs.org/en/download) or a later version.
- All examples use `npm` as a package manager and `npx` as a package runner.

# 📖 Associated Developer Tutorial <a name="tutorial"></a>

> To view a more in depth developer tutorial
> of this app (including code explanations) see the [custom actions tutorial](https://developers.miro.com/docs/add-custom-actions-to-your-app) on Miro's Developer documentation.
# 🏃🏽‍♂️ Run the app locally <a name="run"></a>

1. Run `npm install` to install dependencies.
2. Run `npm start` to start developing. \
Your URL should be similar to this example:
```
http://localhost:3000
```
3. Open the [app manifest editor](https://developers.miro.com/docs/manually-create-an-app#step-2-configure-your-app-in-miro) by clicking **Edit in Manifest**. \
In the app manifest editor, configure the app as follows:

- [`sdkUri`](https://developers.miro.com/docs/app-manifest#sdkuri): assign `http://localhost:3000` as a value for this property. \
It defines the entry point of the app, and it corresponds to the URL of the server that the app runs on.
- [`scopes`](https://developers.miro.com/docs/app-manifest#scopes): add the permission scopes that users need to grant the app when they install it. \
To enable the app to read from and write to the board, add the following permissions:
- `boards:read`
- `boards:write`
- `identity:read`

#### Example of app yaml

```yaml
# See https://developers.miro.com/docs/app-manifest on how to use this
appName: Breakout rooms
sdkVersion: SDK_V2
sdkUri: http://localhost:3000
scopes:
- boards:read
- boards:write
- identity:read
```
4. Go back to your app home page, and under the `Permissions` section, you will see a blue button that says `Install app and get OAuth token`. Click that button. Then click on `Add` as shown in the video below. <b>In the video we install a different app, but the process is the same regardless of the app.</b>

> ⚠️ We recommend to install your app on a [developer team](https://developers.miro.com/docs/create-a-developer-team) while you are developing or testing apps.⚠️

https://github.com/miroapp/app-examples/assets/10428517/1e6862de-8617-46ef-b265-97ff1cbfe8bf

5. Go to your developer team, and open your boards.
6. Click on the app icon on the left sidebar.

# 🗂️ Folder structure <a name="folder"></a>

```
.
├── components
│ ├── Avatar
│ │ ├── Avatar.css
│ │ ├── Avatar.tsx
│ │ └── index.tsx
│ ├── BreakoutManager <-- Main React component displayed in the panel to facilitators
│ ├── BreakoutStarter <-- Component displayed when no rooms were configured
│ ├── RoomConfig <-- Component displayed for each configured room
│ ├── Timer <-- Timer controller component
│ ├── WaitingList <-- Component with unassigned users in the sessiin.
│ ├── WaitingRoom <-- Page displayed to participants while facilitator prepares the session.
│ ├── app.tsx <-- The app typescript entrypoint used in the panel.
│ ├── hooks.tsx <-- React hooks used in the app, including useCurrentUser, useBreakout, useTimer and some others.
│ ├── index.ts <-- The app main typescript entrypoint, rendered inside the headless iframe.
│ ├── types.ts <-- The app typescript types.
│ └── utils.ts <-- General code utilities, such as formatting and converting time in different units.
├── app.html <-- The app content displayed in the content when user clicks on the app icon on Miro boards.
├── index.html <-- The app entry point. This is the value you assign to 'sdkUri' in the app manifest file.

```
# 🫱🏻‍🫲🏽 Contributing <a name="contributing"></a>
If you want to contribute to this example, or any other Miro Open Source project, please review [Miro's contributing guide](https://github.com/miroapp/app-examples/blob/main/CONTRIBUTING.md).
# 🪪 License <a name="license"></a>
[MIT License](https://github.com/miroapp/app-examples/blob/main/LICENSE).
8 changes: 8 additions & 0 deletions examples/breakout-rooms/app-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# See https://developers.miro.com/docs/app-manifest on how to use this
appName: Breakout rooms
sdkVersion: SDK_V2
sdkUri: http://localhost:3000
scopes:
- boards:read
- boards:write
- identity:read
15 changes: 15 additions & 0 deletions examples/breakout-rooms/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://miro.com/app/static/sdk/v2/miro.js"></script>
<link rel="stylesheet" href="/src/styles.css" />
<title>Miro - Breakout rooms</title>
</head>
<body>
<div id="root"></div>

<script type="module" src="/src/app.tsx"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions examples/breakout-rooms/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// https://vitejs.dev/guide/features.html#typescript-compiler-options
/// <reference types="vite/client" />
12 changes: 12 additions & 0 deletions examples/breakout-rooms/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://miro.com/app/static/sdk/v2/miro.js"></script>
<title>Miro - Breakout rooms</title>
</head>
<body>
<script type="module" src="/src/index.ts"></script>
</body>
</html>
38 changes: 38 additions & 0 deletions examples/breakout-rooms/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "breakout-rooms",
"version": "0.0.1",
"license": "MIT",
"description": "This example shows you how leverage collaborative and real-time features, including sessions and real-time events and storage.",
"keywords": [
"Miro SDK",
"Vite",
"React",
"TypeSript",
"Collaborative",
"Breakout rooms",
"Real-time",
"Attention Management"
],
"scripts": {
"start": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@mirohq/design-system": "^0.18.2",
"@stitches/react": "^1.2.8",
"classnames": "^2.3.2",
"mirotone": "5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@mirohq/websdk-types": "latest",
"@types/node": "^18.8.2",
"@types/react": "^18.0.24",
"@types/react-dom": "^18.0.8",
"@vitejs/plugin-react": "^2.2.0",
"typescript": "4.9.5",
"vite": "3.0.3"
}
}
24 changes: 24 additions & 0 deletions examples/breakout-rooms/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import * as React from "react";
import { useBreakout } from "./hooks";
import { BreakoutManager } from "./components/BreakoutManager";
import { WaitingRoom } from "./components/WaitingRoom/WaitingRoom";
import { createRoot } from "react-dom/client";
import { ErrorBoundary } from "./components/ErrorBoundary";

const App: React.FC = () => {
const { isFacilitator, breakout } = useBreakout();

const areYouReady = isFacilitator || !breakout;

return (
<ErrorBoundary>
{areYouReady ? <BreakoutManager /> : <WaitingRoom />}
</ErrorBoundary>
);
};

const container = document.getElementById("root")!;

Check warning on line 22 in examples/breakout-rooms/src/app.tsx

View workflow job for this annotation

GitHub Actions / Run linters

Forbidden non-null assertion
const root = createRoot(container);
root.render(<App />);
16 changes: 16 additions & 0 deletions examples/breakout-rooms/src/components/Avatar/Avatar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.avatar {
border-radius: 100%;
flex-shrink: 0;
background: #817f99;
box-shadow: 0 0 0 6px var(--color, transparent);
outline: 4px solid #fff;
width: 2.2em;
height: 2.2em;
font-size: 14px;
color: #fff;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
font-weight: 600;
}
48 changes: 48 additions & 0 deletions examples/breakout-rooms/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from "react";
import { OnlineUserInfo } from "@mirohq/websdk-types";
import { initials } from "../../utils";

export type Props = {
user: OnlineUserInfo;
color?: string;
};

import "./Avatar.css";

const colors = [
"var(--colors-blue-600)",
"var(--colors-green-600)",
"var(--colors-red-600)",
"var(--colors-yellow-600)",
];
function* colorGenerator(colors: string[]) {
let index = 0;
const totalColors = colors.length;

while (true) {
yield colors[index];
index = (index + 1) % totalColors;
}
}

const getNextColor = colorGenerator(colors);

const userColors = new Map<OnlineUserInfo["id"], string>();
const getUserColor = (user: OnlineUserInfo): string => {
let color = userColors.get(user.id);
if (!color) {
color = getNextColor.next().value ?? colors[0];
userColors.set(user.id, color);
}

return color;
};

export const Avatar: React.FC<Props> = ({ user }) => {
const style = { "--color": getUserColor(user) } as React.CSSProperties;
return (
<div className="avatar" key={user.id} style={style}>
{initials(user.name)}
</div>
);
};
1 change: 1 addition & 0 deletions examples/breakout-rooms/src/components/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Avatar";
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.manager-container {
display: flex;
height: 100%;
flex-direction: column;
gap: 1em;
}

.validation-messages {
background: #f5f5f3;
padding: 1em;
}

.validation-messages .validatino-messages-title {
font-size: 1em;
font-weight: 600;
line-height: 1em;
margin: 0;
color: #656b81;
}

.validation-messages .validatino-messages-items {
font-size: 0.9em;
}

.toolbar {
display: flex;
flex-direction: column;
justify-content: center;
gap: 1em;
}

.toolbar button {
justify-content: center;
margin: 0;
width: 100%;
padding: 1em;
}
Loading

2 comments on commit a6058d8

@vercel
Copy link

@vercel vercel bot commented on a6058d8 Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

webhooks-manager – ./examples/webhooks-manager

webhooks-manager-git-main-miro-web.vercel.app
webhooks-manager-miro-web.vercel.app
webhooks-manager-sepia.vercel.app

@vercel
Copy link

@vercel vercel bot commented on a6058d8 Nov 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

app-examples-wordle – ./examples/wordle

app-examples-wordle.vercel.app
app-examples-wordle-anthonyroux.vercel.app
app-examples-wordle-git-main-anthonyroux.vercel.app

Please sign in to comment.