feat(rmeets): register rMeets as rApp with Jitsi embed
Adds rMeets module with hub page (Quick Meet, Join Room, Jitsi Lobby) and room pages that embed jeffsi.localvibe.live via renderExternalAppShell. Jitsi URL configurable via JITSI_URL env var. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d45aaabea7
commit
0cf1db56f4
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* 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";
|
||||
|
||||
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,
|
||||
externalApp: { url: JITSI_URL, name: "Jitsi Meet" },
|
||||
};
|
||||
|
|
@ -66,6 +66,7 @@ import { dataModule } from "../modules/rdata/mod";
|
|||
import { splatModule } from "../modules/rsplat/mod";
|
||||
import { photosModule } from "../modules/rphotos/mod";
|
||||
import { socialsModule } from "../modules/rsocials/mod";
|
||||
import { meetsModule } from "../modules/rmeets/mod";
|
||||
// import { docsModule } from "../modules/rdocs/mod";
|
||||
// import { designModule } from "../modules/rdesign/mod";
|
||||
import { scheduleModule } from "../modules/rschedule/mod";
|
||||
|
|
@ -111,6 +112,7 @@ registerModule(socialsModule);
|
|||
// registerModule(docsModule); // placeholder — not yet an rApp
|
||||
// registerModule(designModule); // placeholder — not yet an rApp
|
||||
registerModule(scheduleModule);
|
||||
registerModule(meetsModule);
|
||||
|
||||
// ── Config ──
|
||||
const PORT = Number(process.env.PORT) || 3000;
|
||||
|
|
|
|||
Loading…
Reference in New Issue