owncast/webroot/js/components/chat/chat-menu.js

130 lines
3.7 KiB
JavaScript
Raw Normal View History

import { h, createContext } from '/js/web_modules/preact.js';
import htm from '/js/web_modules/htm.js';
import { useState, useEffect, useRef } from '/js/web_modules/preact/hooks.js';
2022-04-21 08:21:35 +03:00
import UsernameForm from './username.js';
import { ChatIcon, UserIcon, CaretDownIcon } from '../icons/index.js';
const html = htm.bind(h);
const moderatorFlag = html`
2022-04-21 08:21:35 +03:00
<img src="/img/moderator-nobackground.svg" class="moderator-flag" />
`;
2022-04-21 08:21:35 +03:00
const Context = createContext();
export const ChatMenu = (props) => {
2022-04-21 08:21:35 +03:00
const {
username,
isModerator,
chatDisabled,
noVideoContent,
handleChatPanelToggle,
onUsernameChange,
onFocus,
onBlur,
} = props;
const [chatMenuOpen, setChatMenuOpen] = useState(false);
2022-04-21 08:21:35 +03:00
const [view, setView] = useState('main');
2022-04-21 08:21:35 +03:00
const chatMenuRef = useRef(undefined);
closeOnOutsideClick(chatMenuRef, setChatMenuOpen);
useEffect(() => {
2022-04-21 08:21:35 +03:00
if (chatMenuOpen) setView('main');
}, [chatMenuOpen]);
return html`
<${Context.Provider} value=${props}>
<div class="chat-menu p-2 relative shadow-lg" ref=${chatMenuRef}>
<button
id="chat-menu-button"
class="flex items-center p-1 bg-transparent rounded-md overflow-hidden text-gray-200 transition duration-150"
onClick="${() => setChatMenuOpen(!chatMenuOpen)}"
>
2022-04-21 08:21:35 +03:00
${
!isModerator
? html`<${UserIcon} className="w-6 h-6 mr-2" />`
: moderatorFlag
}
<span
id="username-display"
2022-04-21 08:21:35 +03:00
class="text-indigo-100 text-sm font-bold truncate overflow-hidden whitespace-no-wrap ${
isModerator && 'moderator-flag'
}"
>
${username}
</span>
<${CaretDownIcon} className="w-8 h-8"/>
</button>
2022-04-21 08:21:35 +03:00
${
chatMenuOpen &&
html` <div
class="chat-menu-popout shadow-2xl text-gray-100 absolute w-max top-full right-0 z-50 rounded-md p-2 bg-gray-900 fadeIn "
style=${{ minWidth: '20rem' }}
>
${view === 'main' &&
html`<ul class="chat-menu-options w-max">
<li>
<${UsernameForm}
username=${username}
isModerator=${isModerator}
onUsernameChange=${onUsernameChange}
onFocus=${onFocus}
onBlur=${onBlur}
/>
</li>
<li>
2022-04-21 08:21:35 +03:00
<button
type="button"
id="chat-toggle"
onClick=${handleChatPanelToggle}
style=${{
display: chatDisabled || noVideoContent ? 'none' : 'flex',
}}
>
<span>Toggle Chat</span>
<span><${ChatIcon} /></span>
</button>
</li>
</ul>`}
2022-04-21 08:21:35 +03:00
${view != 'main' &&
html`<${SubMenuView} view=${view} setView=${setView} />`}
</div>`
}
</div>
2022-04-21 08:21:35 +03:00
</${Context.Provider}>`;
};
const SubMenuView = ({ view, setView }) => {
return html`
2022-04-21 08:21:35 +03:00
<div className=${`chat-view fadeInRight`}>
<ul>
<li>
<button onClick=${() => setView('main')}>
<span
><${CaretDownIcon} className="w-6 h-6 transform rotate-90"
/></span>
<span>Back</span>
</button>
</li>
</ul>
${view === 'change_username' && html`<${ChangeUsernameView} />`}
</div>
`;
};
function closeOnOutsideClick(ref, setter) {
useEffect(() => {
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
2022-04-21 08:21:35 +03:00
setter(undefined);
}
}
2022-04-21 08:21:35 +03:00
document.addEventListener('mousedown', handleClickOutside);
return () => {
2022-04-21 08:21:35 +03:00
document.removeEventListener('mousedown', handleClickOutside);
};
}, [ref]);
}