Skip to content

Commit

Permalink
Merge pull request #19 from dyte-io/feat/chat-widget-sample
Browse files Browse the repository at this point in the history
feat: add chat widget sample app
  • Loading branch information
vaibhavshn authored Dec 4, 2023
2 parents 27e3fab + 0806de0 commit 7f5c267
Show file tree
Hide file tree
Showing 23 changed files with 478 additions and 0 deletions.
9 changes: 9 additions & 0 deletions samples/chat-widget/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
indent_style = tab
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
2 changes: 2 additions & 0 deletions samples/chat-widget/.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_ORG_ID=
VITE_API_KEY=
41 changes: 41 additions & 0 deletions samples/chat-widget/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"env": {
"browser": true,
"es2020": true,
"jest": true,
"node": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:tailwindcss/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 11,
"sourceType": "module"
},
"plugins": ["react", "react-hooks", "@typescript-eslint", "tailwindcss"],
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"tailwindcss/classnames-order": "warn",
"tailwindcss/no-custom-classname": "warn",
"tailwindcss/no-contradicting-classname": "error"
}
}
45 changes: 45 additions & 0 deletions samples/chat-widget/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
dist
dist-ssr
*.local


# dependencies
/node_modules
/.pnp
.pnp.js


# testing
/coverage


# next.js
/.next/
/out/


# production
/build


# misc
.DS_Store
*.pem


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


# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local


# vercel
.vercel
1 change: 1 addition & 0 deletions samples/chat-widget/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
9 changes: 9 additions & 0 deletions samples/chat-widget/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": true,
"singleQuote": true,
"endOfLine": "lf",
"printWidth": 150,
"plugins": ["@trivago/prettier-plugin-sort-imports"]
}
15 changes: 15 additions & 0 deletions samples/chat-widget/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dyte Chat Widget demo

This sample showcases how you can build a Chat widget using Dyte Chat SDK

---

![A screenshot of the DyteChat component](./screenshot.png)

[See source](./src/components/App.tsx)

## Getting started

1. rename `.env.sample` to `.env` and update the variables with your organization details
2. `npm install`
3. `npm run dev`
16 changes: 16 additions & 0 deletions samples/chat-widget/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Dyte Chat Widget demo</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>

</html>
44 changes: 44 additions & 0 deletions samples/chat-widget/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "dyte-chat-widget-demo",
"description": "Dyte Chat Widget demo",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"serve": "vite preview",
"test": "vitest",
"test:ui": "vitest --ui",
"lint": "eslint src --max-warnings=0",
"typecheck": "tsc --project tsconfig.json --noEmit"
},
"dependencies": {
"@dytesdk/web-core": "1.20.0",
"@dytesdk/react-web-core": "1.35.5",
"@dytesdk/react-ui-kit": "1.59.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.2.1",
"@types/node": "^20.8.10",
"@types/react": "^18.2.34",
"@types/react-dom": "^18.2.14",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@vitejs/plugin-react-swc": "^3.4.1",
"autoprefixer": "^10.4.16",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-tailwindcss": "^3.13.0",
"postcss": "^8.4.31",
"prettier": "3.0.3",
"tailwindcss": "^3.3.5",
"typescript": "^5.2.2",
"vite": "^4.5.0",
"vite-tsconfig-paths": "^4.2.1"
}
}
9 changes: 9 additions & 0 deletions samples/chat-widget/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// eslint-disable-next-line no-undef
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
};
6 changes: 6 additions & 0 deletions samples/chat-widget/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/chat-widget/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions samples/chat-widget/src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Widget from './Widget';

function App() {
return (
<div>
Welcome
<Widget />
</div>
);
}

export default App;
40 changes: 40 additions & 0 deletions samples/chat-widget/src/components/ChatPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { DyteChat, DyteMeeting, DyteSpinner } from '@dytesdk/react-ui-kit';
import { useDyteClient } from '@dytesdk/react-web-core';
import { ChatChannel } from '@dytesdk/web-core';
import { useEffect, useState } from 'react';

type DyteMeeting = Awaited<ReturnType<typeof useDyteClient>>[0];

const ChatPopup = ({ meeting }: { meeting: DyteMeeting }) => {
const [channel, setChannel] = useState<ChatChannel | null>(null);

useEffect(() => {
if (!meeting) return;
if (channel) return;
const createChannel = async () => {
if (!meeting.self.roomJoined) {
await meeting.joinRoom();
}
const memberIds = meeting.participants.all.toArray().map((p) => p.userId);
const createdChannel = await meeting.chat.createChannel(meeting.meta.meetingTitle, memberIds);
setChannel(createdChannel);
};
createChannel();
}, [channel, meeting]);

if (meeting && channel) {
return (
<main className="flex h-96 w-96 flex-col rounded-md p-2">
<DyteChat meeting={meeting} />
</main>
);
}

// loader
return (
<main className="flex w-96 justify-center rounded-md p-2">
<DyteSpinner size="xl" />
</main>
);
};
export default ChatPopup;
46 changes: 46 additions & 0 deletions samples/chat-widget/src/components/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useRef } from 'react';

export default function Form({ onSubmit }: { onSubmit: (args: { name: string; email: string }) => void }) {
const nameRef = useRef<HTMLInputElement>(null);
const emailRef = useRef<HTMLInputElement>(null);

const handleFormData = () => {
const name = nameRef.current?.value;
const email = emailRef.current?.value;
if (name && email) {
onSubmit({ name, email });
}
};

return (
<div className="mx-auto rounded-md bg-white p-6 shadow-sm">
<div className="space-y-4">
<div className="relative">
<input
className="flex h-10 w-full rounded-md border border-gray-300 px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus:border-blue-400 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
id="name"
placeholder="Enter your name"
type="text"
ref={nameRef}
/>
</div>
<div className="relative">
<input
className="flex h-10 w-full rounded-md border border-gray-300 px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus:border-blue-400 focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
id="email"
placeholder="Enter your email"
type="email"
ref={emailRef}
/>
</div>
<button
className="inline-flex w-full items-center justify-center whitespace-nowrap rounded-md bg-blue-500 px-3 py-2 text-sm font-medium text-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
type="button"
onClick={handleFormData}
>
Submit
</button>
</div>
</div>
);
}
46 changes: 46 additions & 0 deletions samples/chat-widget/src/components/Widget.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { createMeeting } from '../server';
import ChatPopup from './ChatPopup';
import Form from './Form';
import WidgetLauncher from './WidgetLauncher';
import { useDyteClient } from '@dytesdk/react-web-core';
import { useState } from 'react';

const STEP = {
FORM: 0,
CHAT: 1,
};

const Widget = () => {
const [step, setStep] = useState(STEP.FORM);
const [popupVisible, setPopupVisible] = useState(false);
const [meeting, initMeeting] = useDyteClient();

const onSubmit = async ({ name, email }: { name: string; email: string }) => {
setStep(STEP.CHAT);
const authToken = await createMeeting(name, email);
initMeeting({
authToken,
defaults: {
audio: false,
video: false,
},
});
};

const togglePopup = () => {
setPopupVisible(!popupVisible);
};

return (
<div className="fixed bottom-10 right-6 flex flex-col items-end gap-2">
{popupVisible && (
<div className="max-w-md rounded-md shadow-md ring-1 ring-gray-200">
{step === STEP.CHAT && <ChatPopup meeting={meeting} />}
{step === STEP.FORM && <Form onSubmit={onSubmit} />}
</div>
)}
<WidgetLauncher onClick={togglePopup} />
</div>
);
};
export default Widget;
13 changes: 13 additions & 0 deletions samples/chat-widget/src/components/WidgetLauncher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from 'react';

const WidgetLauncher = ({ onClick }: { onClick: () => void }) => (
<aside className="h-12 w-12 cursor-pointer rounded-full bg-blue-600 p-2 text-white" onClick={onClick}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height={32} width={32}>
<path
fill="currentColor"
d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10a9.96 9.96 0 0 1-4.587-1.112l-3.826 1.067a1.25 1.25 0 0 1-1.54-1.54l1.068-3.823A9.96 9.96 0 0 1 2 12C2 6.477 6.477 2 12 2Zm0 1.5A8.5 8.5 0 0 0 3.5 12c0 1.47.373 2.883 1.073 4.137l.15.27-1.112 3.984 3.987-1.112.27.15A8.5 8.5 0 1 0 12 3.5ZM8.75 13h4.498a.75.75 0 0 1 .102 1.493l-.102.007H8.75a.75.75 0 0 1-.102-1.493L8.75 13h4.498H8.75Zm0-3.5h6.505a.75.75 0 0 1 .101 1.493l-.101.007H8.75a.75.75 0 0 1-.102-1.493L8.75 9.5h6.505H8.75Z"
/>
</svg>
</aside>
);
export default WidgetLauncher;
3 changes: 3 additions & 0 deletions samples/chat-widget/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
Loading

0 comments on commit 7f5c267

Please sign in to comment.