218 lines
5.8 KiB
JavaScript
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;
|