galerie/web/www/gallery.html.tsx
2023-02-25 12:24:58 -05:00

303 lines
8.0 KiB
TypeScript

/*
Copyright 2022 0xf8
This file is part of Galerie
Galerie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Galerie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Galerie. If not, see <https://www.gnu.org/licenses/>.
*/
import App from "./app";
import React from "react";
import jQuery from "jquery";
const $ = jQuery;
import Settings, { apiUrl } from "./settings";
const settings = new Settings();
let global_ActiveContextMenu: ContextMenu = null;
let global_ActivePopout: Popout = null;
// ===================
// === ContextMenu ===
// ===================
class ContextMenu {
public menu: JQuery<HTMLElement>;
private overlay: JQuery<HTMLElement>;
constructor(label: string, buttons: JQuery<HTMLElement>[], position: [any, any]) {
this.overlay = $("<div class='overlay'>");
this.menu = $("<div class='contextmenu'>");
let labelElement = $("<span>");
labelElement.text(label);
this.menu.append(labelElement);
for (let _b in buttons) this.menu.append(buttons[_b]);
this.menu.css("top", position[1]);
this.menu.css("left", position[0]);
this.overlay.append(this.menu);
}
public static async createButton(text: string) {
return $("<button class='option'>").text(text);
}
public async create(): Promise<boolean> {
if (global_ActiveContextMenu != null)
return false;
global_ActiveContextMenu = this;
$("root").append(this.overlay);
$(document).one("click", async (ev) => {
ev.preventDefault();
await this.destroy();
})
return true;
}
public async destroy(): Promise<boolean> {
if (global_ActiveContextMenu != this)
return false;
global_ActiveContextMenu = null;
this.overlay.remove();
this.menu.remove();
}
}
class Popout {
private element: JQuery<HTMLElement>;
private overlay: JQuery<HTMLElement>;
constructor(element: JQuery<HTMLElement>, src: string) {
this.element = element.clone();
this.element.removeClass("resourceh resource");
this.element.addClass("popout");
this.overlay = $("<div class='overlay'>")
this.overlay.append(this.element);
this.element.on("contextmenu", Popout.createPopoutMenu(this.element, src));
}
create() {
if (global_ActivePopout != null)
return;
global_ActivePopout = this;
$("root").append(this.overlay);
this.overlay.one("click", (event) => {
event.preventDefault();
this.destroy();
})
}
destroy() {
if (global_ActivePopout != this)
return;
global_ActivePopout = null;
this.overlay.remove();
this.element.remove();
}
static createPopoutMenu(element: JQuery<HTMLElement>, src: string) {
return async (event) => {
event.preventDefault();
let favoriteButton = await ContextMenu.createButton("Add to favorites");
let blacklistButton = await ContextMenu.createButton("Add to blacklist");
let removeFButton = await ContextMenu.createButton("Remove from favorites");
let removeBButton = await ContextMenu.createButton("Remove from blacklist");
let undoLFButton = await ContextMenu.createButton("Undo last favorite");
let undoLBButton = await ContextMenu.createButton("Undo last blacklist");
let clearFButton = await ContextMenu.createButton("Clear favorites");
let clearBButton = await ContextMenu.createButton("Clear blacklist");
let reloadButton = await ContextMenu.createButton("Reload images");
let reloadOFButton = await ContextMenu.createButton("Reload and show only favorites");
let closeMenuButton = await ContextMenu.createButton("Close this menu");
let Menu = new ContextMenu("Manage resource", [
favoriteButton, blacklistButton, $("<hr/>"),
removeFButton, removeBButton, $("<hr/>"),
undoLFButton, undoLBButton, $("<hr/>"),
clearFButton, clearBButton, $("<hr/>"),
reloadButton, reloadOFButton, closeMenuButton
], [event.clientX, event.clientY]);
favoriteButton.on("click", async (ev) => {
ev.preventDefault();
await settings.favorites.push(src);
});
blacklistButton.on("click", async (ev) => {
ev.preventDefault();
element.remove();
await settings.blacklist.push(src);
});
removeFButton.on("click", async (ev) => {
ev.preventDefault();
await settings.favorites.delete(src);
})
removeBButton.on("click", async (ev) => {
ev.preventDefault();
await settings.blacklist.delete(src);
})
undoLFButton.on("click", async (ev) => {
ev.preventDefault();
await settings.favorites.pop();
});
undoLBButton.on("click", async (ev) => {
ev.preventDefault();
await settings.blacklist.pop();
});
clearFButton.on("click", async (ev) => {
ev.preventDefault();
await settings.favorites.clear();
});
clearBButton.on("click", async (ev) => {
ev.preventDefault();
await settings.blacklist.clear();
})
reloadButton.on("click", async (ev) => {
ev.preventDefault();
await Gallery.loadDisplay(await settings.cache.shuffle());
})
reloadOFButton.on("click", async (ev) => {
ev.preventDefault();
await Gallery.loadDisplay(await settings.favorites.shuffle());
})
closeMenuButton.on("click", async (ev) => {
ev.preventDefault();
await Menu.destroy();
})
Menu.create();
}
}
}
let _log = (f, ...m: any) => {
f(...m);
}
let log = (...m: any[]) => {
_log(console.log, ...m);
}
let warn = (...m: any[]) => {
_log(console.warn, ...m);
}
let error = (...m: any[]) => {
_log(console.error, ...m);
}
let debug = (...m: any[]) => {
_log(console.debug, ...m);
}
// ===============
// === Gallery ===
// ===============
class gallery {
constructor() {}
public async load(): Promise<void> {
log("Validating cache");
await settings.cache.load();
}
public async loadDisplay(data: string[]): Promise<void> {
await this.clearDisplay();
if (data.length != 0)
for (let i = 0; i < data.length; i++) {
let f = await data.at(i);
if (await settings.blacklist.contains(f))
continue;
this.createElement("img", f).then((e) => e.appendTo($("div#display")))
}
else {
let t = $("<span style='margin-top: 3em'>There's nothing here. <a style='color: white; text-decoration: none' href='#'>Reload?</a></span>");
t.children("a").on("click", async (e) => {
e.preventDefault();
this.loadDisplay(await settings.cache.shuffle());
});
// let t = <span>There's nothing here. <a href="#" onClick={ async () => this.loadDisplay(await settings.cache.shuffle()) }>Reload?</a></span>
$(t).appendTo($("div#display"))
}
}
private async clearDisplay(): Promise<void> {
$("#display").children().remove();
}
private async createElement(tag: string, src: string): Promise<JQuery<HTMLElement>> {
let element = $(`<${tag} class='resource resourceh'>`);
element.prop("loop", true);
element.prop("nocontrols", true);
element.attr("src", `${apiUrl}img/${src}`);
let popoutHandler = async (event) => {
event.preventDefault();
const popout = new Popout(element, src);
popout.create();
}
element.on("click", popoutHandler);
element.on("contextmenu", Popout.createPopoutMenu(element, src));
return element;
}
};
let Gallery = new gallery();
window.onload = async () => {
await Gallery.load();
await Gallery.loadDisplay(await settings.cache.shuffle());
}
const root = App(() => {
return <>
<div id="container">
<div id="display"></div>
</div>
</>;
})