From 93a5c3a02ae9ae9fe34c97c12765b44eb62c3cb5 Mon Sep 17 00:00:00 2001 From: Sli Date: Tue, 18 Feb 2025 13:54:48 +0100 Subject: [PATCH] Separate album downloading logic from user display. Allow downloading individual user albums. --- sas/static/bundled/sas/album-index.js | 59 ------------------- sas/static/bundled/sas/album-index.ts | 52 ++++++++++++++++ .../bundled/sas/pictures-download-index.ts | 46 +++++++++++++++ sas/static/bundled/sas/user/pictures-index.ts | 57 +++--------------- sas/templates/sas/album.jinja | 25 ++++---- sas/templates/sas/download_pictures.jinja | 39 ++++++++---- sas/templates/sas/user_pictures.jinja | 16 ++--- 7 files changed, 152 insertions(+), 142 deletions(-) delete mode 100644 sas/static/bundled/sas/album-index.js create mode 100644 sas/static/bundled/sas/album-index.ts create mode 100644 sas/static/bundled/sas/pictures-download-index.ts diff --git a/sas/static/bundled/sas/album-index.js b/sas/static/bundled/sas/album-index.js deleted file mode 100644 index f09fa6b2..00000000 --- a/sas/static/bundled/sas/album-index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { History, initialUrlParams, updateQueryString } from "#core:utils/history"; -import { picturesFetchPictures } from "#openapi"; - -/** - * @typedef AlbumConfig - * @property {number} albumId id of the album to visualize - * @property {number} maxPageSize maximum number of elements to show on a page - **/ - -/** - * Create a family graph of an user - * @param {AlbumConfig} config - **/ -window.loadAlbum = (config) => { - document.addEventListener("alpine:init", () => { - Alpine.data("pictures", () => ({ - pictures: {}, - page: Number.parseInt(initialUrlParams.get("page")) || 1, - pushstate: History.Push /* Used to avoid pushing a state on a back action */, - loading: false, - - async init() { - await this.fetchPictures(); - this.$watch("page", () => { - updateQueryString("page", this.page === 1 ? null : this.page, this.pushstate); - this.pushstate = History.Push; - this.fetchPictures(); - }); - - window.addEventListener("popstate", () => { - this.pushstate = History.Replace; - this.page = - Number.parseInt(new URLSearchParams(window.location.search).get("page")) || - 1; - }); - }, - - async fetchPictures() { - this.loading = true; - this.pictures = ( - await picturesFetchPictures({ - query: { - // biome-ignore lint/style/useNamingConvention: API is in snake_case - album_id: config.albumId, - page: this.page, - // biome-ignore lint/style/useNamingConvention: API is in snake_case - page_size: config.maxPageSize, - }, - }) - ).data; - this.loading = false; - }, - - nbPages() { - return Math.ceil(this.pictures.count / config.maxPageSize); - }, - })); - }); -}; diff --git a/sas/static/bundled/sas/album-index.ts b/sas/static/bundled/sas/album-index.ts new file mode 100644 index 00000000..8f0b119f --- /dev/null +++ b/sas/static/bundled/sas/album-index.ts @@ -0,0 +1,52 @@ +import { paginated } from "#core:utils/api"; +import { History, initialUrlParams, updateQueryString } from "#core:utils/history"; +import { + type PictureSchema, + type PicturesFetchPicturesData, + picturesFetchPictures, +} from "#openapi"; + +interface AlbumConfig { + albumId: number; + maxPageSize: number; +} + +document.addEventListener("alpine:init", () => { + Alpine.data("pictures", (config: AlbumConfig) => ({ + pictures: [] as PictureSchema[], + page: Number.parseInt(initialUrlParams.get("page")) || 1, + pushstate: History.Push /* Used to avoid pushing a state on a back action */, + loading: false, + config: {} as AlbumConfig, + + async init() { + await this.fetchPictures(); + this.$watch("page", () => { + updateQueryString("page", this.page === 1 ? null : this.page, this.pushstate); + this.pushstate = History.Push; + }); + + window.addEventListener("popstate", () => { + this.pushstate = History.Replace; + this.page = + Number.parseInt(new URLSearchParams(window.location.search).get("page")) || 1; + }); + this.config = config; + }, + + async fetchPictures() { + this.loading = true; + this.pictures = await paginated(picturesFetchPictures, { + query: { + // biome-ignore lint/style/useNamingConvention: API is in snake_case + album_id: config.albumId, + } as PicturesFetchPicturesData["query"], + }); + this.loading = false; + }, + + nbPages() { + return Math.ceil(this.pictures.length / config.maxPageSize); + }, + })); +}); diff --git a/sas/static/bundled/sas/pictures-download-index.ts b/sas/static/bundled/sas/pictures-download-index.ts new file mode 100644 index 00000000..21ee9989 --- /dev/null +++ b/sas/static/bundled/sas/pictures-download-index.ts @@ -0,0 +1,46 @@ +import { HttpReader, ZipWriter } from "@zip.js/zip.js"; +import { showSaveFilePicker } from "native-file-system-adapter"; +import type { PictureSchema } from "#openapi"; + +document.addEventListener("alpine:init", () => { + Alpine.data("pictures_download", () => ({ + isDownloading: false, + + async downloadZip() { + this.isDownloading = true; + const bar = this.$refs.progress; + bar.value = 0; + bar.max = this.pictures.length; + + const incrementProgressBar = (_total: number): undefined => { + bar.value++; + return undefined; + }; + + const fileHandle = await showSaveFilePicker({ + _preferPolyfill: false, + suggestedName: interpolate( + gettext("pictures.%(extension)s"), + { extension: "zip" }, + true, + ), + excludeAcceptAllOption: false, + }); + const zipWriter = new ZipWriter(await fileHandle.createWritable()); + + await Promise.all( + this.pictures.map((p: PictureSchema) => { + const imgName = `${p.album}/IMG_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`; + return zipWriter.add(imgName, new HttpReader(p.full_size_url), { + level: 9, + lastModDate: new Date(p.date), + onstart: incrementProgressBar, + }); + }), + ); + + await zipWriter.close(); + this.isDownloading = false; + }, + })); +}); diff --git a/sas/static/bundled/sas/user/pictures-index.ts b/sas/static/bundled/sas/user/pictures-index.ts index 8b5ecda5..d3cd83ae 100644 --- a/sas/static/bundled/sas/user/pictures-index.ts +++ b/sas/static/bundled/sas/user/pictures-index.ts @@ -1,6 +1,4 @@ import { paginated } from "#core:utils/api"; -import { HttpReader, ZipWriter } from "@zip.js/zip.js"; -import { showSaveFilePicker } from "native-file-system-adapter"; import { type PictureSchema, type PicturesFetchPicturesData, @@ -8,26 +6,22 @@ import { } from "#openapi"; interface PagePictureConfig { - userId?: number; - albumId?: number; + userId: number; } document.addEventListener("alpine:init", () => { Alpine.data("user_pictures", (config: PagePictureConfig) => ({ - isDownloading: false, loading: true, pictures: [] as PictureSchema[], albums: {} as Record, async init() { - const query: PicturesFetchPicturesData["query"] = {}; - - if (config.userId) { - query.users_identified = [config.userId]; - } else { - query.album_id = config.albumId; - } - this.pictures = await paginated(picturesFetchPictures, { query: query }); + this.pictures = await paginated(picturesFetchPictures, { + query: { + // biome-ignore lint/style/useNamingConvention: from python api + users_identified: [config.userId], + } as PicturesFetchPicturesData["query"], + }); this.albums = this.pictures.reduce( (acc: Record, picture: PictureSchema) => { @@ -41,42 +35,5 @@ document.addEventListener("alpine:init", () => { ); this.loading = false; }, - - async downloadZip() { - this.isDownloading = true; - const bar = this.$refs.progress; - bar.value = 0; - bar.max = this.pictures.length; - - const incrementProgressBar = (_total: number): undefined => { - bar.value++; - return undefined; - }; - - const fileHandle = await showSaveFilePicker({ - _preferPolyfill: false, - suggestedName: interpolate( - gettext("pictures.%(extension)s"), - { extension: "zip" }, - true, - ), - excludeAcceptAllOption: false, - }); - const zipWriter = new ZipWriter(await fileHandle.createWritable()); - - await Promise.all( - this.pictures.map((p: PictureSchema) => { - const imgName = `${p.album}/IMG_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`; - return zipWriter.add(imgName, new HttpReader(p.full_size_url), { - level: 9, - lastModDate: new Date(p.date), - onstart: incrementProgressBar, - }); - }), - ); - - await zipWriter.close(); - this.isDownloading = false; - }, })); }); diff --git a/sas/templates/sas/album.jinja b/sas/templates/sas/album.jinja index fa2592e8..402881e0 100644 --- a/sas/templates/sas/album.jinja +++ b/sas/templates/sas/album.jinja @@ -7,8 +7,8 @@ {%- endblock -%} {%- block additional_js -%} - - + + {%- endblock -%} {% block title %} @@ -64,15 +64,17 @@
{% endif %} -
- {{ download_button() }} -
+
+ + {{ download_button("Download album") }} -

{% trans %}Pictures{% endtrans %}

-