Separate album downloading logic from user display. Allow downloading individual user albums.

This commit is contained in:
2025-02-18 13:54:48 +01:00
parent e46cba7a06
commit 93a5c3a02a
7 changed files with 152 additions and 142 deletions

View File

@ -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);
},
}));
});
};

View File

@ -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);
},
}));
});

View File

@ -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;
},
}));
});

View File

@ -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<string, PictureSchema[]>,
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<string, PictureSchema[]>, 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;
},
}));
});