playback-device-server/www/src/views/main-view.jsx

218 lines
5.8 KiB
JavaScript

import {
createSignal,
mergeProps,
createResource,
createEffect,
onMount,
onCleanup,
} from "solid-js";
import { computePosition, shift, autoUpdate, offset } from "@floating-ui/dom";
import UserService from "../services/user-service.js";
import ModalRegistry from "../modals/modal-registry.jsx";
import UrlUtils from "../tools/url-utils.js";
import SettingsView from "./settings-view.jsx";
import DevicesView from "./devices-view.jsx";
import {
DEVICES_VIEW,
REMOTES_VIEW,
RECORDINGS_VIEW,
SETTINGS_VIEW,
} from "../data/constants.js";
let [activeView, setActiveView] = createSignal(DEVICES_VIEW);
const MainView = function (props) {
props = mergeProps({ onLogout: () => {} }, props);
const [userInfo] = createResource(() => UserService.getUserInfo());
const [username, setUsername] = createSignal("");
createEffect(() => {
if (userInfo.loading) return;
setUsername(userInfo().username);
});
let Modals = ModalRegistry.getModals();
let userButtonRef;
let userMenuRef;
let userMenuBackRef;
let cleanUserMenu;
onMount(() => {
setViewFromUrl();
window.addEventListener("popstate", setViewFromUrl);
});
onCleanup(() => {
window.removeEventListener("popstate", setViewFromUrl);
});
function setViewFromUrl() {
let view = UrlUtils.getQueryParameter("view");
if (view) {
setActiveView(view);
}
}
function handleLogout() {
UserService.logout();
props.onLogout();
}
function toggleUserMenu() {
if (userMenuRef.style.display === "none") {
showUserMenu();
} else {
hideUserMenu();
}
}
function showUserMenu() {
cleanUserMenu = autoUpdate(userButtonRef, userMenuRef, () =>
computePosition(userButtonRef, userMenuRef, {
placement: "bottom-end",
middleware: [offset(6), shift()],
}).then(({ x, y }) => {
Object.assign(userMenuRef.style, {
left: `${x}px`,
top: `${y}px`,
});
})
);
userMenuRef.style.display = "block";
userMenuBackRef.style.display = "block";
}
function hideUserMenu() {
userMenuRef.style.display = "none";
userMenuBackRef.style.display = "none";
cleanUserMenu();
}
function handleViewChange(view) {
if (activeView() === view) return;
setActiveView(view);
let url = UrlUtils.getUrl();
url = UrlUtils.addQueryParameter(url, "view", view);
UrlUtils.setUrl(url);
}
function render() {
return (
<div class="d-flex flex-column" style="height:100vh">
<Modals></Modals>
<HeaderBar></HeaderBar>
<ActiveView class={"bg-body-tertiary"}></ActiveView>
</div>
);
}
function HeaderBar() {
return (
<div>
<div class="container-xl px-3 py-2 d-flex bg-dark-subtle">
<div class="d-flex align-items-center flex-fill">
<button
class={
"btn me-2" +
(activeView() === DEVICES_VIEW ? " btn-secondary" : " btn-dark")
}
onClick={() => handleViewChange(DEVICES_VIEW)}
>
<i class="bi bi-tv me-2"></i>
Devices
</button>
<button
class={
"btn me-2" +
(activeView() === REMOTES_VIEW ? " btn-secondary" : " btn-dark")
}
onClick={() => handleViewChange(REMOTES_VIEW)}
>
<i class="bi bi-wifi me-2"></i>
Remotes
</button>
<button
class={
"btn me-2" +
(activeView() === RECORDINGS_VIEW
? " btn-secondary"
: " btn-dark")
}
onClick={() => handleViewChange(RECORDINGS_VIEW)}
>
<i class="bi bi-record-circle me-2"></i>
Recordings
</button>
</div>
<div class="d-flex align-items-center">
<button
ref={userButtonRef}
class="btn btn-dark rounded-circle p-0"
style="width: 2.4em; height: 2.4em"
onClick={toggleUserMenu}
>
<i class="bi bi-person-fill"></i>
</button>
<div
onClick={hideUserMenu}
ref={userMenuBackRef}
style="position:absolute;width:100vw;height:100vh;left:0;right:0;z-index:1060;display:none"
></div>
<div
class="list-group position-absolute"
style="display: none; z-index: 1070"
ref={userMenuRef}
>
<li class="list-group-item bg-dark-subtle">{username()}</li>
<button
type="button"
class="list-group-item list-group-item-action"
onClick={() => {
handleViewChange(SETTINGS_VIEW);
hideUserMenu();
}}
>
<i class="bi bi-gear-fill me-2"></i>
Settings
</button>
<button
type="button"
class="list-group-item list-group-item-action pe-auto"
onClick={() => handleLogout()}
>
<i class="bi bi-box-arrow-in-right me-2"></i>
Log Out
</button>
</div>
</div>
</div>
</div>
);
}
function ActiveView(props) {
return (
<div class="container-xl p-0 d-flex flex-column flex-fill overflow-hidden">
<Switch>
<Match when={activeView() === SETTINGS_VIEW}>
<SettingsView {...props} />
</Match>
<Match when={activeView() === DEVICES_VIEW}>
<DevicesView {...props} />
</Match>
</Switch>
</div>
);
}
return render();
};
MainView.setActiveView = setActiveView;
export default MainView;