mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-06 12:52:26 +00:00
Refactor UI responsibilities into controllers
This commit is contained in:
1 parent
8ea778f7ca
commit
2059ef87a5
4 files changed
+673
-265
No files matched your search
@@ -0,0 +1,303 @@
|
||||
import { devLogger as defaultLogger } from "../utils/logger.js";
|
||||
import {
|
||||
prepareStaticModal,
|
||||
openStaticModal,
|
||||
closeStaticModal,
|
||||
} from "./components/staticModalAccessibility.js";
|
||||
|
||||
const DEFAULT_LOGOUT_ERROR_MESSAGE = "Failed to logout. Please try again.";
|
||||
|
||||
export default class AppChromeController {
|
||||
constructor({
|
||||
elements = {},
|
||||
callbacks = {},
|
||||
utilities = {},
|
||||
environment = {},
|
||||
logger,
|
||||
} = {}) {
|
||||
this.elements = {
|
||||
logoutButton: elements.logoutButton || null,
|
||||
profileButton: elements.profileButton || null,
|
||||
uploadButton: elements.uploadButton || null,
|
||||
loginButton: elements.loginButton || null,
|
||||
closeLoginModalButton: elements.closeLoginModalButton || null,
|
||||
};
|
||||
|
||||
this.callbacks = {
|
||||
requestLogout: callbacks.requestLogout || null,
|
||||
showError: callbacks.showError || null,
|
||||
showProfileModal: callbacks.showProfileModal || null,
|
||||
openUploadModal: callbacks.openUploadModal || null,
|
||||
onLoginModalOpened: callbacks.onLoginModalOpened || null,
|
||||
onLoginModalClosed: callbacks.onLoginModalClosed || null,
|
||||
flushWatchHistory: callbacks.flushWatchHistory || null,
|
||||
cleanup: callbacks.cleanup || null,
|
||||
hideModal: callbacks.hideModal || null,
|
||||
};
|
||||
|
||||
this.utilities = {
|
||||
prepareLoginModal:
|
||||
typeof utilities.prepareLoginModal === "function"
|
||||
? utilities.prepareLoginModal
|
||||
: () => prepareStaticModal({ id: "loginModal" }) || document.getElementById("loginModal"),
|
||||
prepareApplicationModal:
|
||||
typeof utilities.prepareApplicationModal === "function"
|
||||
? utilities.prepareApplicationModal
|
||||
: () => prepareStaticModal({ id: "nostrFormModal" }) || document.getElementById("nostrFormModal"),
|
||||
openModal:
|
||||
typeof utilities.openModal === "function"
|
||||
? utilities.openModal
|
||||
: (modal, options) => openStaticModal(modal, options),
|
||||
closeModal:
|
||||
typeof utilities.closeModal === "function"
|
||||
? utilities.closeModal
|
||||
: (modalId) => closeStaticModal(modalId),
|
||||
};
|
||||
|
||||
this.document = environment.document || (typeof document !== "undefined" ? document : null);
|
||||
this.window = environment.window || (typeof window !== "undefined" ? window : null);
|
||||
this.logger = logger || defaultLogger;
|
||||
|
||||
this.isInitialized = false;
|
||||
|
||||
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
|
||||
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
||||
this.handlePopstate = this.handlePopstate.bind(this);
|
||||
this.handleDocumentClick = this.handleDocumentClick.bind(this);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (this.isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bindLogoutButton();
|
||||
this.bindProfileButton();
|
||||
this.bindUploadButton();
|
||||
this.bindLoginButton();
|
||||
this.bindCloseLoginButton();
|
||||
this.bindGlobalEvents();
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
getTriggerFromEvent(event) {
|
||||
if (!event) {
|
||||
return null;
|
||||
}
|
||||
return event.currentTarget || event.target || null;
|
||||
}
|
||||
|
||||
bindLogoutButton() {
|
||||
const { logoutButton } = this.elements;
|
||||
if (!logoutButton || typeof logoutButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
logoutButton.addEventListener("click", async () => {
|
||||
try {
|
||||
if (typeof this.callbacks.requestLogout === "function") {
|
||||
await this.callbacks.requestLogout();
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.logger?.error) {
|
||||
this.logger.error("Logout failed:", error);
|
||||
}
|
||||
if (typeof this.callbacks.showError === "function") {
|
||||
this.callbacks.showError(DEFAULT_LOGOUT_ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindProfileButton() {
|
||||
const { profileButton } = this.elements;
|
||||
if (!profileButton || typeof profileButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
profileButton.addEventListener("click", () => {
|
||||
if (typeof this.callbacks.showProfileModal !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => this.callbacks.showProfileModal())
|
||||
.catch((error) => {
|
||||
if (this.logger?.error) {
|
||||
this.logger.error("Failed to open profile modal:", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bindUploadButton() {
|
||||
const { uploadButton } = this.elements;
|
||||
if (!uploadButton || typeof uploadButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
uploadButton.addEventListener("click", (event) => {
|
||||
if (typeof this.callbacks.openUploadModal !== "function") {
|
||||
return;
|
||||
}
|
||||
const triggerElement = this.getTriggerFromEvent(event);
|
||||
try {
|
||||
this.callbacks.openUploadModal({ triggerElement });
|
||||
} catch (error) {
|
||||
if (this.logger?.error) {
|
||||
this.logger.error("Failed to open upload modal:", error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindLoginButton() {
|
||||
const { loginButton } = this.elements;
|
||||
if (!loginButton || typeof loginButton.addEventListener !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
loginButton.addEventListener("click", (event) => {
|
||||
if (this.logger?.log) {
|
||||
this.logger.log("Login button clicked!");
|
||||
}
|
||||
|
||||
const triggerElement = this.getTriggerFromEvent(event);
|
||||
const loginModal =
|
||||
(typeof this.utilities.prepareLoginModal === "function"
|
||||
? this.utilities.prepareLoginModal()
|
||||
: null) || null;
|
||||
|
||||
if (
|
||||
loginModal &&
|
||||
typeof this.utilities.openModal === "function" &&
|
||||
this.utilities.openModal(loginModal, { triggerElement })
|
||||
) {
|
||||
if (typeof this.callbacks.onLoginModalOpened === "function") {
|
||||
this.callbacks.onLoginModalOpened();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindCloseLoginButton() {
|
||||
const { closeLoginModalButton } = this.elements;
|
||||
if (
|
||||
!closeLoginModalButton ||
|
||||
typeof closeLoginModalButton.addEventListener !== "function"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeLoginModalButton.addEventListener("click", () => {
|
||||
if (this.logger?.log) {
|
||||
this.logger.log("[app.js] closeLoginModal button clicked!");
|
||||
}
|
||||
|
||||
if (typeof this.utilities.closeModal === "function") {
|
||||
const wasClosed = this.utilities.closeModal("loginModal");
|
||||
if (wasClosed && typeof this.callbacks.onLoginModalClosed === "function") {
|
||||
this.callbacks.onLoginModalClosed();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bindGlobalEvents() {
|
||||
if (this.window && typeof this.window.addEventListener === "function") {
|
||||
this.window.addEventListener("beforeunload", this.handleBeforeUnload);
|
||||
this.window.addEventListener("popstate", this.handlePopstate);
|
||||
}
|
||||
|
||||
if (this.document && typeof this.document.addEventListener === "function") {
|
||||
this.document.addEventListener("visibilitychange", this.handleVisibilityChange);
|
||||
this.document.addEventListener("click", this.handleDocumentClick);
|
||||
}
|
||||
}
|
||||
|
||||
handleBeforeUnload() {
|
||||
if (typeof this.callbacks.flushWatchHistory === "function") {
|
||||
Promise.resolve()
|
||||
.then(() => this.callbacks.flushWatchHistory("session-end", "beforeunload"))
|
||||
.catch((error) => {
|
||||
if (this.logger?.warn) {
|
||||
this.logger.warn("[beforeunload] Watch history flush failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof this.callbacks.cleanup === "function") {
|
||||
Promise.resolve()
|
||||
.then(() => this.callbacks.cleanup())
|
||||
.catch((error) => {
|
||||
if (this.logger?.error) {
|
||||
this.logger.error("Cleanup before unload failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleVisibilityChange() {
|
||||
if (!this.document || this.document.visibilityState !== "hidden") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.callbacks.flushWatchHistory !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.resolve()
|
||||
.then(() => this.callbacks.flushWatchHistory("session-end", "visibilitychange"))
|
||||
.catch((error) => {
|
||||
if (this.logger?.warn) {
|
||||
this.logger.warn("[visibilitychange] Watch history flush failed:", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async handlePopstate() {
|
||||
if (this.logger?.log) {
|
||||
this.logger.log("[popstate] user navigated back/forward; cleaning modal...");
|
||||
}
|
||||
|
||||
if (typeof this.callbacks.hideModal !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.callbacks.hideModal();
|
||||
} catch (error) {
|
||||
if (this.logger?.warn) {
|
||||
this.logger.warn("[popstate] Failed to hide modal:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleDocumentClick(event) {
|
||||
if (!event?.target || event.target.id !== "openApplicationModal") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof this.utilities.closeModal === "function") {
|
||||
const closed = this.utilities.closeModal("loginModal");
|
||||
if (closed && typeof this.callbacks.onLoginModalClosed === "function") {
|
||||
this.callbacks.onLoginModalClosed();
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof this.utilities.prepareApplicationModal !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
const applicationModal = this.utilities.prepareApplicationModal();
|
||||
if (
|
||||
applicationModal &&
|
||||
typeof this.utilities.openModal === "function"
|
||||
) {
|
||||
this.utilities.openModal(applicationModal, {
|
||||
triggerElement: event.target,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user