Private
Public Access
Add resizable room panel, searchable user picker, and direct room membership
Room info panel is now user-resizable (fixing a layout clip at narrow widths), and every user-selection spot (room membership, admin ownership transfer) uses a new searchable UserPicker instead of raw text input or prompt(). Member rows fold role + actions into a single inline dropdown instead of a row of buttons, so the member list stays usable as rooms grow. Room invites (the accept/decline flow) are replaced by adding a user to a room directly -- an admin/owner picks someone and they're a member immediately, with a "you've been added" notification email instead of an invite email. Drops the now-unused room_invites table.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import { useCallback, useEffect, useRef, useState, type PointerEvent } from 'react'
|
||||
|
||||
interface UseResizableWidthOptions {
|
||||
storageKey: string
|
||||
defaultWidth: number
|
||||
min: number
|
||||
max: number
|
||||
}
|
||||
|
||||
// Right-anchored resizable panel: width is the distance from the cursor to
|
||||
// the viewport's right edge, so a handle on the panel's left edge drags
|
||||
// naturally. Persists to localStorage so it survives a reload.
|
||||
export function useResizableWidth({ storageKey, defaultWidth, min, max }: UseResizableWidthOptions) {
|
||||
const [width, setWidth] = useState(() => {
|
||||
const stored = Number(localStorage.getItem(storageKey))
|
||||
return stored >= min && stored <= max ? stored : defaultWidth
|
||||
})
|
||||
const widthRef = useRef(width)
|
||||
widthRef.current = width
|
||||
const draggingRef = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
function handleMove(e: PointerEvent<Window> | globalThis.PointerEvent) {
|
||||
if (!draggingRef.current) return
|
||||
const next = Math.min(max, Math.max(min, window.innerWidth - e.clientX))
|
||||
setWidth(next)
|
||||
}
|
||||
function handleUp() {
|
||||
if (!draggingRef.current) return
|
||||
draggingRef.current = false
|
||||
localStorage.setItem(storageKey, String(widthRef.current))
|
||||
}
|
||||
window.addEventListener('pointermove', handleMove as (e: globalThis.PointerEvent) => void)
|
||||
window.addEventListener('pointerup', handleUp)
|
||||
return () => {
|
||||
window.removeEventListener('pointermove', handleMove as (e: globalThis.PointerEvent) => void)
|
||||
window.removeEventListener('pointerup', handleUp)
|
||||
}
|
||||
}, [max, min, storageKey])
|
||||
|
||||
const startResize = useCallback((e: PointerEvent) => {
|
||||
e.preventDefault()
|
||||
draggingRef.current = true
|
||||
}, [])
|
||||
|
||||
return { width, startResize }
|
||||
}
|
||||
Reference in New Issue
Block a user