import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js"; import { toHref } from "../../lib/skeleton/router.js"; import rxjs, { effect, onLoad, onClick } from "../../lib/rx.js"; import ajax from "../../lib/ajax.js"; import { animate } from "../../lib/animate.js"; import { extname } from "../../lib/path.js"; import { qs, safe } from "../../lib/dom.js"; import { get as getConfig } from "../../model/config.js"; import { load as loadPlugin } from "../../model/plugin.js"; import { Chromecast } from "../../model/chromecast.js"; import { loadCSS } from "../../helpers/loader.js"; import { createLoader } from "../../components/loader.js"; import notification from "../../components/notification.js"; import t from "../../locales/index.js"; import ctrlError from "../ctrl_error.js"; import componentInformation, { init as initInformation } from "./application_image/information.js"; import componentPagination, { init as initPagination } from "./application_image/pagination.js"; import componentZoom from "./application_image/zoom.js"; import ctrlDownloader, { init as initDownloader } from "./application_downloader.js"; import { renderMenubar, buttonDownload, buttonFullscreen } from "./component_menubar.js"; class IImage { getSRC() { throw new Error("NOT_IMPLEMENTED"); } } export default function(render, { getFilename, getDownloadUrl, mime, hasMenubar = true, acl$ }) { const $page = createElement(`
`); render($page); const $imgContainer = qs($page, ".images_wrapper"); const $photo = qs($page, "img.photo"); const $menubar = qs($page, "component-menubar"); const removeLoader = createLoader($imgContainer); const load$ = new rxjs.BehaviorSubject(null); const toggleInfo = ($button) => { const $aside = qs($page, ".images_aside"); $aside.classList.toggle("open"); $button.setAttribute("aria-expanded", $aside.classList.contains("open") ? "true" : "false"); componentInformation(createRender(qs($page, ".images_aside")), { toggle: toggleInfo, load$ }); }; renderMenubar( $menubar, buttonDownload(getFilename(), getDownloadUrl()), buttonFullscreen(qs($page, ".component_image_container")), mime === "image/jpeg" && buttonInfo({ toggle: toggleInfo }), ["image/jpeg", "image/png"].indexOf(mime) !== -1 && buttonChromecast(getFilename(), getDownloadUrl()), ); effect(rxjs.from(loadPlugin(mime)).pipe( rxjs.mergeMap(async(loader) => { let src = `${getDownloadUrl()}&size=${window.innerWidth}`; if (loader) { const { response } = await ajax({ url: getDownloadUrl(), responseType: "arraybuffer" }).toPromise(); const img = new (await loader(IImage, { mime, $menubar, getFilename, getDownloadUrl }))({ $photo }); src = await img.getSRC(response); } $photo.setAttribute("src", src); await onLoad($photo).toPromise(); }), rxjs.tap(() => load$.next($photo)), removeLoader, rxjs.tap(() => { const cancel = animate($photo, { onEnter: () => $photo.classList.remove("hidden"), onExit: async() => (await cancel)(), time: 300, easing: "cubic-bezier(.51,.92,.24,1.15)", keyframes: [ { opacity: 0, transform: "scale(.97)" }, { opacity: 1 }, { opacity: 1, transform: "scale(1)" }, ], }); }), rxjs.catchError((err) => { if (err.target instanceof HTMLElement && err.type === "error") { return rxjs.of(initDownloader()).pipe( removeLoader, rxjs.mergeMap(() => { load$.error(err); ctrlDownloader(createRender(qs($page, ".images_wrapper")), { acl$, getFilename, getDownloadUrl, hasMenubar: false }); return rxjs.EMPTY; }), ); } return ctrlError()(err); }), )); effect(load$.pipe( rxjs.first(), rxjs.tap(() => { componentZoom({ $img: $photo, $page, $menubar, load$ }); componentPagination(createRender(qs($page, "[data-bind=\"component_navigation\"]")), { $img: $photo }); }), )); } export function init() { return Promise.all([ loadCSS(import.meta.url, "./application_image.css"), loadCSS(import.meta.url, "./component_menubar.css"), initPagination(), initInformation(), Chromecast.init(), ]); } function buttonInfo({ toggle }) { const $el = createElement(` `); effect(rxjs.merge( onClick($el), rxjs.fromEvent(window, "keydown").pipe(rxjs.filter((e) => e.key === "i")), ).pipe(rxjs.mapTo($el), rxjs.tap(toggle))); return $el; } function buttonChromecast(filename, downloadURL) { const context = Chromecast.context(); if (!context) return; const chromecastSetup = (event) => { switch (event.sessionState) { case window.cast.framework.SessionState.SESSION_STARTED: chromecastLoader(); break; } }; const chromecastLoader = () => { const session = Chromecast.session(); if (!session) return; const link = Chromecast.createLink("/" + toHref(downloadURL)); const media = new window.chrome.cast.media.MediaInfo( link, getConfig("mime", {})[extname(filename)], ); media.metadata = new window.chrome.cast.media.PhotoMediaMetadata(); media.metadata.title = filename; media.metadata.images = [ new window.chrome.cast.Image(location.origin + "/" + toHref("/assets/icons/photo.png")), ]; try { const req = Chromecast.createRequest(media); session.loadMedia(req); } catch (err) { console.error(err); notification.error(t("Cannot establish a connection")); } }; context.addEventListener( window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED, chromecastSetup, ); onDestroy(() => context.removeEventListener( window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED, chromecastSetup, )); const media = Chromecast.media(); if (media && media.media && media.media.mediaCategory === "IMAGE") chromecastLoader(); return document.createElement("google-cast-launcher"); }