119 lines
4.2 KiB
TypeScript
119 lines
4.2 KiB
TypeScript
/**
|
|
* rMeets module — video meetings powered by Jitsi.
|
|
*
|
|
* Hub page with Quick Meet + room name input,
|
|
* room pages embed Jitsi via renderExternalAppShell.
|
|
*/
|
|
|
|
import { Hono } from "hono";
|
|
import { renderShell, renderExternalAppShell, escapeHtml } from "../../server/shell";
|
|
import { getModuleInfoList } from "../../shared/module";
|
|
import type { RSpaceModule } from "../../shared/module";
|
|
import { renderLanding } from "./landing";
|
|
|
|
const JITSI_URL = process.env.JITSI_URL || "https://jeffsi.localvibe.live";
|
|
const routes = new Hono();
|
|
|
|
// ── Room embed ──
|
|
|
|
routes.get("/room/:room", (c) => {
|
|
const space = c.req.param("space") || "demo";
|
|
const room = c.req.param("room");
|
|
return c.html(renderExternalAppShell({
|
|
title: `${room} — rMeets | rSpace`,
|
|
moduleId: "rmeets",
|
|
spaceSlug: space,
|
|
modules: getModuleInfoList(),
|
|
appUrl: `${JITSI_URL}/${encodeURIComponent(room)}`,
|
|
appName: "Jitsi Meet",
|
|
theme: "dark",
|
|
}));
|
|
});
|
|
|
|
// ── Direct Jitsi lobby ──
|
|
|
|
routes.get("/meet", (c) => {
|
|
const space = c.req.param("space") || "demo";
|
|
return c.html(renderExternalAppShell({
|
|
title: `Jitsi Meet — rMeets | rSpace`,
|
|
moduleId: "rmeets",
|
|
spaceSlug: space,
|
|
modules: getModuleInfoList(),
|
|
appUrl: JITSI_URL,
|
|
appName: "Jitsi Meet",
|
|
theme: "dark",
|
|
}));
|
|
});
|
|
|
|
// ── Hub page ──
|
|
|
|
routes.get("/", (c) => {
|
|
const space = c.req.param("space") || "demo";
|
|
const base = `/${escapeHtml(space)}/rmeets`;
|
|
const randomId = Math.random().toString(36).slice(2, 10);
|
|
return c.html(renderShell({
|
|
title: `rMeets — ${space} | rSpace`,
|
|
moduleId: "rmeets",
|
|
spaceSlug: space,
|
|
modules: getModuleInfoList(),
|
|
theme: "dark",
|
|
styles: `<style>
|
|
.rs-hub{max-width:720px;margin:3rem auto;padding:0 1.5rem}
|
|
.rs-hub h1{font-size:1.8rem;margin-bottom:.5rem}
|
|
.rs-hub p{color:var(--rs-text-secondary,#aaa);margin-bottom:2rem}
|
|
.rs-nav{display:flex;flex-direction:column;gap:1rem}
|
|
.rs-nav a,.rs-nav .room-form{display:flex;align-items:center;gap:1rem;padding:1.25rem 1.5rem;border-radius:12px;background:var(--rs-surface,#1e1e2e);border:1px solid var(--rs-border,#333);text-decoration:none;color:inherit;transition:border-color .15s,background .15s}
|
|
.rs-nav a:hover,.rs-nav .room-form:hover{border-color:var(--rs-accent,#14b8a6);background:var(--rs-surface-hover,#252538)}
|
|
.rs-nav .nav-icon{font-size:2rem;flex-shrink:0}
|
|
.rs-nav .nav-body h3{margin:0 0 .25rem;font-size:1.1rem}
|
|
.rs-nav .nav-body p{margin:0;font-size:.85rem;color:var(--rs-text-secondary,#aaa)}
|
|
.room-form{flex-wrap:wrap}
|
|
.room-form input[type="text"]{flex:1;min-width:140px;padding:.5rem .75rem;border-radius:8px;border:1px solid var(--rs-border,#444);background:var(--rs-bg,#161625);color:inherit;font-size:.95rem}
|
|
.room-form button{padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--rs-accent,#14b8a6);color:#fff;font-weight:600;cursor:pointer;font-size:.95rem}
|
|
.room-form button:hover{opacity:.85}
|
|
</style>`,
|
|
body: `<div class="rs-hub">
|
|
<h1>rMeets</h1>
|
|
<p>Video meetings powered by Jitsi — no account required</p>
|
|
<nav class="rs-nav">
|
|
<a href="${base}/room/${randomId}">
|
|
<span class="nav-icon">🚀</span>
|
|
<div class="nav-body">
|
|
<h3>Quick Meet</h3>
|
|
<p>Start an instant meeting with a random room name</p>
|
|
</div>
|
|
</a>
|
|
<form class="room-form" onsubmit="event.preventDefault();var n=this.querySelector('input').value.trim();if(n)location.href='${base}/room/'+encodeURIComponent(n)">
|
|
<span class="nav-icon">🔗</span>
|
|
<div class="nav-body">
|
|
<h3>Join a Room</h3>
|
|
<p>Enter a room name to join or create a meeting</p>
|
|
</div>
|
|
<input type="text" placeholder="room-name" required>
|
|
<button type="submit">Join</button>
|
|
</form>
|
|
<a href="${base}/meet">
|
|
<span class="nav-icon">📹</span>
|
|
<div class="nav-body">
|
|
<h3>Jitsi Lobby</h3>
|
|
<p>Open the full Jitsi Meet interface directly</p>
|
|
</div>
|
|
</a>
|
|
</nav>
|
|
</div>`,
|
|
}));
|
|
});
|
|
|
|
// ── Module export ──
|
|
|
|
export const meetsModule: RSpaceModule = {
|
|
id: "rmeets",
|
|
name: "rMeets",
|
|
icon: "📹",
|
|
description: "Video meetings powered by Jitsi",
|
|
scoping: { defaultScope: "space", userConfigurable: false },
|
|
routes,
|
|
landingPage: renderLanding,
|
|
externalApp: { url: JITSI_URL, name: "Jitsi Meet" },
|
|
};
|