Skip to content

Commit

Permalink
feat: Add sample with some more features using Next JS (React + TS)
Browse files Browse the repository at this point in the history
  • Loading branch information
teddyward committed Sep 4, 2024
1 parent d9f63f9 commit 5ae511d
Show file tree
Hide file tree
Showing 17 changed files with 536 additions and 295 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish-sample-add-ons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy sample Add-ons to GitHub Pages
on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]
branches: ["main", "teddy_sample-next-js-advanced"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Build outputs for sample Meet Add-ons
addons-web-sdk/samples/package-lock.json
**/dist
**/node_modules
**/.DS_Store
4 changes: 4 additions & 0 deletions addons-web-sdk/samples/animation-next-js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# next.js
/.next/
next-env.d.ts
out
5 changes: 5 additions & 0 deletions addons-web-sdk/samples/animation-next-js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Pretty Colors: Next JS Add-on

This is a Meet Add-on built in Next JS. This add-on displays an animation that is intended to create a simple "shimmer" effect based on the color that each call participant selects. This add-on only exists to show more features of Google Meet Add-ons than can be found at <https://github.com/googleworkspace/meet/tree/main/addons-web-sdk/samples/hello-world-next-js>. If you find anything about the configuration confusing, please see that more basic example.

You can view a deployed version of this Add-on at <https://googleworkspace.github.io/meet/animation-next-js/sidepanel> and its corresponding [mainstage.html](https://googleworkspace.github.io/meet/animation-next-js/mainstage).
8 changes: 8 additions & 0 deletions addons-web-sdk/samples/animation-next-js/next.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
basePath: '/meet/animation-next-js',
distDir: '../dist/animation-next-js',
output: 'export',
};

export default nextConfig;
23 changes: 23 additions & 0 deletions addons-web-sdk/samples/animation-next-js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "animation-next-js",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@googleworkspace/meet-addons": "^0.10.0",
"next": "14.2.7",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"typescript": "^5"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import { useEffect, useState } from 'react';
import {
meet,
MeetSidePanelClient,
} from '@googleworkspace/meet-addons/meet.addons';
import { CLOUD_PROJECT_NUMBER } from '../../shared/constants';

/**
* This page displays in the Side Panel after the activity has started to allow
* each call participant to modify the base color of the main stage animation.
*/
export default function Page() {
const [sidePanelClient, setSidePanelClient] = useState<MeetSidePanelClient>();

/**
* Sends a newly chosen color to the main stage, using frame-to-frame
* messaging.
* @param newColor Hex code of the new color.
*/
async function updateColor(newColor: string) {
if (!sidePanelClient) {
throw new Error("Side Panel is not yet initialized!");
}

await sidePanelClient.notifyMainStage(newColor);
}

useEffect(() => {
/**
* Initializes the Add-on Side Panel Client.
* https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetsidepanelclient
*/
async function initializeSidePanelClient() {
const session = await meet.addon.createAddonSession({
cloudProjectNumber: CLOUD_PROJECT_NUMBER,
});
const client = await session.createSidePanelClient();
setSidePanelClient(client);
}
initializeSidePanelClient();
}, []);

return (
<>
<label htmlFor="pretty-color">Change the color. Only you will see this:</label>
<input
type="color"
id="pretty-color"
name="pretty-color"
defaultValue="#00ff00"
onChange={(e) => updateColor(e.target.value)} />
</>
)
}
16 changes: 16 additions & 0 deletions addons-web-sdk/samples/animation-next-js/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import { useEffect, useState } from 'react';
import {
meet,
FrameToFrameMessage,
MeetMainStageClient
} from '@googleworkspace/meet-addons/meet.addons';
import { CLOUD_PROJECT_NUMBER } from '../../shared/constants';
import PrettyColors from '@/components/prettyColors';

/**
* See: https://developers.google.com/meet/add-ons/guides/overview#main-stage
*/
export default function Page() {
const [color, setColor] = useState<string>("#00ff00");

/**
* Creates a MeetMainStageClient to control the main stage of the add-on.
* https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetmainstageclient
*/
async function initializeMainStageClient(): Promise<MeetMainStageClient> {
const session = await meet.addon.createAddonSession({
cloudProjectNumber: CLOUD_PROJECT_NUMBER,
});
return await session.createMainStageClient();
}

/**
* Parses the collaboration starting state from the side panel, and updates
* the color used for the main animation.
*/
async function setStartingState(mainStageClient: MeetMainStageClient) {
const startingState = await mainStageClient.getActivityStartingState();
const additionalData = JSON.parse(startingState.additionalData ?? '{}');
setColor(additionalData.startingColor);
}

/**
* Listens for new frame-to-frame messages from the side panel that update
* the color used for the main animation.
*/
function awaitNewColor(mainStageClient: MeetMainStageClient) {
mainStageClient.on('frameToFrameMessage', (message: FrameToFrameMessage) => {
setColor(message.payload);
});
}

useEffect(() => {
/**
* Initialize the main stage by initializing the client, then using that
* client to get the starting state (color), and observe any new colors
* passed from the Side Panel.
*/
async function initializeMainStage() {
const client = await initializeMainStageClient();
setStartingState(client);
awaitNewColor(client);
}
initializeMainStage();
}, []);

return (
<>
<PrettyColors baseColor={color}></PrettyColors>
</>
)
}
32 changes: 32 additions & 0 deletions addons-web-sdk/samples/animation-next-js/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"use client";

import { useEffect } from 'react';
import { meet } from '@googleworkspace/meet-addons/meet.addons.screenshare';
import { CLOUD_PROJECT_NUMBER, SIDE_PANEL_URL } from '../shared/constants';

export default function App() {
/**
* Screensharing this page will prompt you to install/open this add-on.
* When it is opened, it will prompt you to set up the add-on in the side
* panel before starting the activity for everyone.
* See: https://developers.google.com/meet/add-ons/guides/screen-sharing
*
* Note that in React, this must be wrapped in `useEffect`, so that `window`
* is available.
*/
useEffect(() => {
meet.addon.screensharing.exposeToMeetWhenScreensharing(
{
cloudProjectNumber: CLOUD_PROJECT_NUMBER,
sidePanelUrl: SIDE_PANEL_URL,
startActivityOnOpen: false,
}
);
})

return (
<>
<div>Screenshare this page to promote an add-on.</div>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import { useEffect, useState } from 'react';
import { meet, MeetSidePanelClient } from '@googleworkspace/meet-addons/meet.addons';
import {
ACTIVITY_SIDE_PANEL_URL,
CLOUD_PROJECT_NUMBER,
MAIN_STAGE_URL
} from '../../shared/constants';

/**
* See: https://developers.google.com/meet/add-ons/guides/overview#side-panel
*/
export default function Page() {
const [sidePanelClient, setSidePanelClient] = useState<MeetSidePanelClient>();

/**
* Starts the add-on activity and passes the selected color to the Main Stage,
* as part of the activity starting state.
*/
async function startCollaboration(e: unknown) {
if (!sidePanelClient) {
throw new Error("Side Panel is not yet initialized!");
}

const startingColor = (
document.getElementById('starting-color')! as HTMLInputElement
).value;
await sidePanelClient.startActivity({
mainStageUrl: MAIN_STAGE_URL,
sidePanelUrl: ACTIVITY_SIDE_PANEL_URL,
// Pass the selected color to customize the initial display.
additionalData: `{\"startingColor\": \"${startingColor}\"}`,
});
window.location.replace(ACTIVITY_SIDE_PANEL_URL + window.location.search);
}

useEffect(() => {
/**
* Initializes the Add-on Side Panel Client.
* https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetsidepanelclient
*/
async function initializeSidePanelClient() {
const session = await meet.addon.createAddonSession({
cloudProjectNumber: CLOUD_PROJECT_NUMBER,
});
const client = await session.createSidePanelClient();
setSidePanelClient(client);
}
initializeSidePanelClient();
}, []);

return (
<>
<div>
Welcome to Pretty Colors! This is a contrived demo add-on that lets you
look at an animation involving your favorite color.
</div>
<label htmlFor="starting-color">Pick a color you like. Everyone will see this:</label>
<input type="color" id="starting-color" name="starting-color" defaultValue="#00ff00" />
<br />
<button onClick={startCollaboration}>
Start the animation!
</button>
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
html, body {
height: 100%;
margin: 0;
width: 100%;
}

.prettyColorsContainer {
height: 100%;
overflow: hidden;
position: relative;
width: 100%;
}

.prettyLine {
animation-name: spin;
animation-direction: normal;
animation-duration: 10000ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
/* border-width: 8px 0 8px 0;
border-style: solid; */
height: 10px;
position: absolute;
width: 40%;
}

.prettyLine:hover {
animation-direction: reverse;
}

@keyframes spin {
from {transform:rotate(0deg);}
to {transform:rotate(360deg);}
}
Loading

0 comments on commit 5ae511d

Please sign in to comment.