Use typescript api for user pictures and allow imports across js files

* Add imports paths for js files in node
* Add a ts version of fetchPaginated
* Update documentation
This commit is contained in:
Antoine Bartuccio 2024-10-09 20:59:12 +02:00
parent 9247696c1c
commit 9199f91151
8 changed files with 111 additions and 10 deletions

View File

@ -1,5 +1,7 @@
import { paginated } from "#core:utils/api";
import { HttpReader, ZipWriter } from "@zip.js/zip.js"; import { HttpReader, ZipWriter } from "@zip.js/zip.js";
import { showSaveFilePicker } from "native-file-system-adapter"; import { showSaveFilePicker } from "native-file-system-adapter";
import { picturesFetchPictures } from "#openapi";
/** /**
* @typedef UserProfile * @typedef UserProfile
@ -28,14 +30,14 @@ import { showSaveFilePicker } from "native-file-system-adapter";
/** /**
* @typedef PicturePageConfig * @typedef PicturePageConfig
* @param {string} apiUrl Url of the api endpoint to fetch pictures from the user * @param {number} userId Id of the user to get the pictures from
**/ **/
/** /**
* Load user picture page with a nice download bar * Load user picture page with a nice download bar
* @param {PicturePageConfig} Configuration * @param {PicturePageConfig} Configuration
**/ **/
window.loadPicturePage = (config) => { window.window.loadPicturePage = (config) => {
document.addEventListener("alpine:init", () => { document.addEventListener("alpine:init", () => {
Alpine.data("user_pictures", () => ({ Alpine.data("user_pictures", () => ({
isDownloading: false, isDownloading: false,
@ -44,8 +46,10 @@ window.loadPicturePage = (config) => {
albums: {}, albums: {},
async init() { async init() {
// biome-ignore lint/correctness/noUndeclaredVariables: imported from script.json this.pictures = await paginated(picturesFetchPictures, {
this.pictures = await fetchPaginated(config.apiUrl); // biome-ignore lint/style/useNamingConvention: api is in snake_case
query: { users_identified: [config.userId] },
});
this.albums = this.pictures.reduce((acc, picture) => { this.albums = this.pictures.reduce((acc, picture) => {
if (!acc[picture.album]) { if (!acc[picture.album]) {
acc[picture.album] = []; acc[picture.album] = [];

View File

@ -0,0 +1,46 @@
import type { Options, RequestResult } from "@hey-api/client-fetch";
interface PaginatedResponse<T> {
count: number;
next: string | null;
previous: string | null;
results: T[];
}
interface PaginatedRequest {
query?: {
page?: number;
// biome-ignore lint/style/useNamingConvention: api is in snake_case
page_size?: number;
};
}
type PaginatedEndpoint<T> = <ThrowOnError extends boolean = false>(
options?: Options<PaginatedRequest, ThrowOnError>,
) => RequestResult<PaginatedResponse<T>, unknown, ThrowOnError>;
export const paginated = async <T>(
endpoint: PaginatedEndpoint<T>,
options?: PaginatedRequest,
) => {
const maxPerPage = 199;
options.query.page_size = maxPerPage;
options.query.page = 1;
const firstPage = (await endpoint(options)).data;
const results = firstPage.results;
const nbElements = firstPage.count;
const nbPages = Math.ceil(nbElements / maxPerPage);
if (nbPages > 1) {
const promises: Promise<T[]>[] = [];
for (let i = 2; i <= nbPages; i++) {
const nextPage = structuredClone(options);
nextPage.query.page = i;
promises.push(endpoint(nextPage).then((res) => res.data.results));
}
results.push(...(await Promise.all(promises)).flat());
}
return results;
};

View File

@ -62,9 +62,7 @@
{{ super() }} {{ super() }}
<script> <script>
window.addEventListener("DOMContentLoaded", () => { window.addEventListener("DOMContentLoaded", () => {
loadPicturePage({ loadPicturePage({ userId: {{ object.id }} });
apiUrl: "{{ url("api:pictures") }}?users_identified={{ object.id }}"
});
}) })
</script> </script>
{% endblock script %} {% endblock script %}

View File

@ -0,0 +1,39 @@
Vous avez ajouté une application et vous voulez y mettre du javascript ?
Vous voulez importer depuis cette nouvelle application dans votre script géré par webpack ?
Eh bien il faut manuellement enregistrer dans node où les trouver et c'est très simple.
D'abord, il faut ajouter dans node via `package.json`:
```json
{
// ...
"imports": {
// ...
"#mon_app:*": "./mon_app/static/webpack/*"
}
// ...
}
```
Ensuite, pour faire fonctionne l'auto-complétion, il faut configurer `tsconfig.json`:
```json
{
"compilerOptions": {
// ...
"paths": {
// ...
"#mon_app:*": ["./mon_app/static/webpack/*"]
}
}
}
```
Et c'est tout !
!!!note
Il se peut qu'il soit nécessaire de redémarrer `./manage.py runserver` pour
que les changements prennent effet.

View File

@ -35,8 +35,9 @@ les fichiers sont à mettre dans un dossier `static/webpack` de l'application à
Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `webpack/` comme prefix. Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `webpack/` comme prefix.
```jinja ```jinja
{# Exemple pour ajouter sith/core/webpack/alpine-index.js #} {# Example pour ajouter sith/core/webpack/alpine-index.js #}
<script src="{{ static('webpack/alpine-index.js') }}" defer></script> <script src="{{ static('webpack/alpine-index.js') }}" defer></script>
<script src="{{ static('webpack/other-index.ts') }}" defer></script>
``` ```
!!!note !!!note
@ -45,6 +46,16 @@ Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en
Les autres fichiers sont disponibles à l'import dans le JavaScript comme Les autres fichiers sont disponibles à l'import dans le JavaScript comme
si ils étaient tous au même niveau. si ils étaient tous au même niveau.
### Les imports au sein des fichiers de webpack
Pour importer au sein de webpack, faut préfixer ses imports de `#app:`.
Example:
```js
import { paginated } from "#core:utils/api";
```
## Comment ça fonctionne le post processing ? ## Comment ça fonctionne le post processing ?
Le post processing est géré par le module `staticfiles`. Les fichiers sont Le post processing est géré par le module `staticfiles`. Les fichiers sont

View File

@ -72,6 +72,7 @@ nav:
- Gérer les migrations: howto/migrations.md - Gérer les migrations: howto/migrations.md
- Gérer les traductions: howto/translation.md - Gérer les traductions: howto/translation.md
- Gérer les statics: howto/statics.md - Gérer les statics: howto/statics.md
- Ajouter un chemin d'import javascript: howto/js-import-paths.md
- Configurer pour la production: howto/prod.md - Configurer pour la production: howto/prod.md
- Ajouter un logo de promo: howto/logo.md - Ajouter un logo de promo: howto/logo.md
- Ajouter une cotisation: howto/subscriptions.md - Ajouter une cotisation: howto/subscriptions.md

View File

@ -14,7 +14,8 @@
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"sideEffects": [".css"], "sideEffects": [".css"],
"imports": { "imports": {
"#openapi": "./staticfiles/generated/openapi/index.ts" "#openapi": "./staticfiles/generated/openapi/index.ts",
"#core:*": "./core/static/webpack/*"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",

View File

@ -9,7 +9,8 @@
"allowJs": true, "allowJs": true,
"moduleResolution": "node", "moduleResolution": "node",
"paths": { "paths": {
"#openapi": ["./staticfiles/generated/openapi/index.ts"] "#openapi": ["./staticfiles/generated/openapi/index.ts"],
"#core:*": ["./core/static/webpack/*"]
} }
} }
} }