feat: improve Jitsi Meet interaction and room naming
- Enable pointer events on iframe for direct mouse/touch/pen interaction - Room names now use canvas slug (e.g., mycofi-jeffsi-meet for /mycofi) - All video chats in same canvas room share the same Jitsi room - Support both /:slug and /board/:slug URL patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
08bea8490d
commit
58905067f8
|
|
@ -42,20 +42,31 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
generateRoomName(shapeId: string): string {
|
generateRoomName(): string {
|
||||||
// Extract board ID from URL
|
// Extract room/canvas slug from URL
|
||||||
let boardId = 'default';
|
// Supports both /:slug and /board/:slug patterns
|
||||||
|
let roomSlug = 'default';
|
||||||
const currentUrl = window.location.pathname;
|
const currentUrl = window.location.pathname;
|
||||||
|
|
||||||
|
// First try /board/:slug pattern
|
||||||
const boardMatch = currentUrl.match(/\/board\/([^\/]+)/);
|
const boardMatch = currentUrl.match(/\/board\/([^\/]+)/);
|
||||||
if (boardMatch) {
|
if (boardMatch) {
|
||||||
boardId = boardMatch[1].substring(0, 8); // First 8 chars
|
roomSlug = boardMatch[1];
|
||||||
|
} else {
|
||||||
|
// Try direct /:slug pattern (e.g., /mycofi, /ccc)
|
||||||
|
// Exclude known non-board routes
|
||||||
|
const excludedRoutes = ['login', 'contact', 'inbox', 'debug', 'dashboard', 'presentations', 'google', 'oauth'];
|
||||||
|
const slugMatch = currentUrl.match(/^\/([^\/]+)\/?$/);
|
||||||
|
if (slugMatch && !excludedRoutes.includes(slugMatch[1])) {
|
||||||
|
roomSlug = slugMatch[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean the shape ID (remove 'shape:' prefix and special chars)
|
// Clean the slug (remove special chars, lowercase)
|
||||||
const cleanShapeId = shapeId.replace(/^shape:/, '').replace(/[^A-Za-z0-9]/g, '').substring(0, 8);
|
const cleanSlug = roomSlug.replace(/[^A-Za-z0-9-]/g, '').toLowerCase();
|
||||||
|
|
||||||
// Create a readable room name
|
// Create room name: {slug}-jeffsi-meet
|
||||||
return `canvas-${boardId}-${cleanShapeId}`;
|
return `${cleanSlug}-jeffsi-meet`;
|
||||||
}
|
}
|
||||||
|
|
||||||
component(shape: IVideoChatShape) {
|
component(shape: IVideoChatShape) {
|
||||||
|
|
@ -70,7 +81,7 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
|
||||||
// Initialize room name if not set
|
// Initialize room name if not set
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!roomName) {
|
if (!roomName) {
|
||||||
const newRoomName = this.generateRoomName(shape.id);
|
const newRoomName = this.generateRoomName();
|
||||||
setRoomName(newRoomName);
|
setRoomName(newRoomName);
|
||||||
|
|
||||||
// Update shape props with room name
|
// Update shape props with room name
|
||||||
|
|
@ -123,26 +134,29 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
|
||||||
// Construct Jitsi Meet URL with configuration
|
// Construct Jitsi Meet URL with configuration
|
||||||
const jitsiUrl = new URL(`https://${JITSI_DOMAIN}/${roomName}`)
|
const jitsiUrl = new URL(`https://${JITSI_DOMAIN}/${roomName}`)
|
||||||
|
|
||||||
// Add configuration via URL params (Jitsi supports this)
|
// Add configuration via URL hash params (Jitsi supports this)
|
||||||
const config = {
|
// Build hash string properly to avoid double-hash bug
|
||||||
|
const configParams = [
|
||||||
|
// Enable prejoin to request camera/mic permissions properly
|
||||||
|
'config.prejoinPageEnabled=true',
|
||||||
|
// Start with devices enabled based on props
|
||||||
|
`config.startWithAudioMuted=${props.allowMicrophone ? 'false' : 'true'}`,
|
||||||
|
`config.startWithVideoMuted=${props.allowCamera ? 'false' : 'true'}`,
|
||||||
// UI Configuration
|
// UI Configuration
|
||||||
'config.prejoinPageEnabled': 'false',
|
'config.disableModeratorIndicator=true',
|
||||||
'config.startWithAudioMuted': props.allowMicrophone ? 'false' : 'true',
|
'config.enableWelcomePage=false',
|
||||||
'config.startWithVideoMuted': props.allowCamera ? 'false' : 'true',
|
'config.hideConferenceSubject=true',
|
||||||
'config.disableModeratorIndicator': 'true',
|
|
||||||
'config.enableWelcomePage': 'false',
|
|
||||||
// Interface configuration
|
// Interface configuration
|
||||||
'interfaceConfig.SHOW_JITSI_WATERMARK': 'false',
|
'interfaceConfig.SHOW_JITSI_WATERMARK=false',
|
||||||
'interfaceConfig.SHOW_BRAND_WATERMARK': 'false',
|
'interfaceConfig.SHOW_BRAND_WATERMARK=false',
|
||||||
'interfaceConfig.SHOW_POWERED_BY': 'false',
|
'interfaceConfig.SHOW_POWERED_BY=false',
|
||||||
'interfaceConfig.HIDE_INVITE_MORE_HEADER': 'true',
|
'interfaceConfig.HIDE_INVITE_MORE_HEADER=true',
|
||||||
'interfaceConfig.MOBILE_APP_PROMO': 'false',
|
'interfaceConfig.MOBILE_APP_PROMO=false',
|
||||||
}
|
'interfaceConfig.DISABLE_JOIN_LEAVE_NOTIFICATIONS=true',
|
||||||
|
]
|
||||||
|
|
||||||
// Add config params to URL
|
// Set hash once with all params joined
|
||||||
Object.entries(config).forEach(([key, value]) => {
|
jitsiUrl.hash = configParams.join('&')
|
||||||
jitsiUrl.hash = `${jitsiUrl.hash}${jitsiUrl.hash ? '&' : ''}${key}=${value}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
this.editor.deleteShape(shape.id)
|
this.editor.deleteShape(shape.id)
|
||||||
|
|
@ -235,8 +249,8 @@ export class VideoChatShape extends BaseBoxShapeUtil<IVideoChatShape> {
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
// Only enable pointer events when selected, so canvas can pan when not selected
|
// Always enable pointer events for mouse/touch/pen interaction
|
||||||
pointerEvents: isSelected ? "all" : "none",
|
pointerEvents: "all",
|
||||||
}}
|
}}
|
||||||
allow="camera; microphone; fullscreen; display-capture; autoplay; clipboard-write"
|
allow="camera; microphone; fullscreen; display-capture; autoplay; clipboard-write"
|
||||||
referrerPolicy="no-referrer-when-downgrade"
|
referrerPolicy="no-referrer-when-downgrade"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue