mirror of
https://github.com/ae-utbm/sith.git
synced 2024-11-22 06:03:20 +00:00
preload images and identifications
This commit is contained in:
parent
a2a858262a
commit
813bbbb94a
@ -4,23 +4,78 @@
|
|||||||
* @property {UserProfile} user The identified user
|
* @property {UserProfile} user The identified user
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for a picture with the users identified on it
|
||||||
|
* able to prefetch its data.
|
||||||
|
*/
|
||||||
|
class PictureWithIdentifications {
|
||||||
|
identifications = null;
|
||||||
|
image_loading = false;
|
||||||
|
identifications_loading = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Picture} picture
|
||||||
|
*/
|
||||||
|
constructor(picture) {
|
||||||
|
Object.assign(this, picture);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Picture} picture
|
||||||
|
*/
|
||||||
|
static from_picture(picture) {
|
||||||
|
return new PictureWithIdentifications(picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If not already done, fetch the users identified on this picture and
|
||||||
|
* populate the identifications field
|
||||||
|
* @param {?Object=} options
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async load_identifications(options) {
|
||||||
|
if (this.identifications_loading) {
|
||||||
|
return; // The users are already being fetched.
|
||||||
|
}
|
||||||
|
if (!!this.identifications && !options?.force_reload) {
|
||||||
|
// The users are already fetched
|
||||||
|
// and the user does not want to force the reload
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.identifications_loading = true;
|
||||||
|
const url = `/api/sas/picture/${this.id}/identified`;
|
||||||
|
this.identifications = await (await fetch(url)).json();
|
||||||
|
this.identifications_loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preload the photo and the identifications
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async preload() {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = this.compressed_url;
|
||||||
|
if (!img.complete) {
|
||||||
|
this.image_loading = true;
|
||||||
|
img.addEventListener("load", () => {
|
||||||
|
this.image_loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await this.load_identifications();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener("alpine:init", () => {
|
document.addEventListener("alpine:init", () => {
|
||||||
Alpine.data("picture_viewer", () => ({
|
Alpine.data("picture_viewer", () => ({
|
||||||
/**
|
/**
|
||||||
* All the pictures that can be displayed on this picture viewer
|
* All the pictures that can be displayed on this picture viewer
|
||||||
* @type Picture[]
|
* @type PictureWithIdentifications[]
|
||||||
**/
|
**/
|
||||||
pictures: [],
|
pictures: [],
|
||||||
/**
|
|
||||||
* The users identified on the currently displayed picture
|
|
||||||
* @type PictureIdentification[]
|
|
||||||
**/
|
|
||||||
identifications: [],
|
|
||||||
/**
|
/**
|
||||||
* The currently displayed picture
|
* The currently displayed picture
|
||||||
* Default dummy data are pre-loaded to avoid javascript error
|
* Default dummy data are pre-loaded to avoid javascript error
|
||||||
* when loading the page at the beginning
|
* when loading the page at the beginning
|
||||||
* @type Picture
|
* @type PictureWithIdentifications
|
||||||
**/
|
**/
|
||||||
current_picture: {
|
current_picture: {
|
||||||
is_moderated: true,
|
is_moderated: true,
|
||||||
@ -32,15 +87,16 @@ document.addEventListener("alpine:init", () => {
|
|||||||
full_size_url: "",
|
full_size_url: "",
|
||||||
owner: "",
|
owner: "",
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
|
identifications: [],
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* The picture which will be displayed next if the user press the "next" button
|
* The picture which will be displayed next if the user press the "next" button
|
||||||
* @type ?Picture
|
* @type ?PictureWithIdentifications
|
||||||
**/
|
**/
|
||||||
next_picture: null,
|
next_picture: null,
|
||||||
/**
|
/**
|
||||||
* The picture which will be displayed next if the user press the "previous" button
|
* The picture which will be displayed next if the user press the "previous" button
|
||||||
* @type ?Picture
|
* @type ?PictureWithIdentifications
|
||||||
**/
|
**/
|
||||||
previous_picture: null,
|
previous_picture: null,
|
||||||
/**
|
/**
|
||||||
@ -50,7 +106,6 @@ document.addEventListener("alpine:init", () => {
|
|||||||
/**
|
/**
|
||||||
* true if the page is in a loading state, else false
|
* true if the page is in a loading state, else false
|
||||||
**/
|
**/
|
||||||
loading: true,
|
|
||||||
/**
|
/**
|
||||||
* Error message when a moderation operation fails
|
* Error message when a moderation operation fails
|
||||||
* @type string
|
* @type string
|
||||||
@ -64,11 +119,17 @@ document.addEventListener("alpine:init", () => {
|
|||||||
pushstate: History.PUSH,
|
pushstate: History.PUSH,
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
this.pictures = await fetch_paginated(picture_endpoint);
|
this.pictures = (await fetch_paginated(picture_endpoint)).map(
|
||||||
|
PictureWithIdentifications.from_picture,
|
||||||
|
);
|
||||||
this.selector = sithSelect2({
|
this.selector = sithSelect2({
|
||||||
element: $(this.$refs.search),
|
element: $(this.$refs.search),
|
||||||
data_source: remote_data_source("/api/user/search", {
|
data_source: remote_data_source("/api/user/search", {
|
||||||
excluded: () => [...this.identifications.map((i) => i.user.id)],
|
excluded: () => [
|
||||||
|
...(this.current_picture.identifications || []).map(
|
||||||
|
(i) => i.user.id,
|
||||||
|
),
|
||||||
|
],
|
||||||
result_converter: (obj) => Object({ ...obj, text: obj.display_name }),
|
result_converter: (obj) => Object({ ...obj, text: obj.display_name }),
|
||||||
}),
|
}),
|
||||||
picture_getter: (user) => user.profile_pict,
|
picture_getter: (user) => user.profile_pict,
|
||||||
@ -99,8 +160,6 @@ document.addEventListener("alpine:init", () => {
|
|||||||
* the list of identified users are updated.
|
* the list of identified users are updated.
|
||||||
*/
|
*/
|
||||||
async update_picture() {
|
async update_picture() {
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
const update_args = [
|
const update_args = [
|
||||||
{ sas_picture_id: this.current_picture.id },
|
{ sas_picture_id: this.current_picture.id },
|
||||||
"",
|
"",
|
||||||
@ -117,10 +176,13 @@ document.addEventListener("alpine:init", () => {
|
|||||||
const index = this.pictures.indexOf(this.current_picture);
|
const index = this.pictures.indexOf(this.current_picture);
|
||||||
this.previous_picture = this.pictures[index - 1] || null;
|
this.previous_picture = this.pictures[index - 1] || null;
|
||||||
this.next_picture = this.pictures[index + 1] || null;
|
this.next_picture = this.pictures[index + 1] || null;
|
||||||
this.identifications = await (
|
await this.current_picture.load_identifications();
|
||||||
await fetch(`/api/sas/picture/${this.current_picture.id}/identified`)
|
this.$refs.main_picture?.addEventListener("load", () => {
|
||||||
).json();
|
// once the current picture is loaded,
|
||||||
this.loading = false;
|
// start preloading the next and previous pictures
|
||||||
|
this.next_picture?.preload();
|
||||||
|
this.previous_picture?.preload();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async moderate_picture() {
|
async moderate_picture() {
|
||||||
@ -131,8 +193,7 @@ document.addEventListener("alpine:init", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
this.moderation_error =
|
this.moderation_error = `${gettext("Couldn't moderate picture")} : ${res.statusText}`;
|
||||||
gettext("Couldn't moderate picture") + " : " + res.statusText;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.current_picture.is_moderated = true;
|
this.current_picture.is_moderated = true;
|
||||||
@ -161,16 +222,14 @@ document.addEventListener("alpine:init", () => {
|
|||||||
* Send the identification request and update the list of identified users.
|
* Send the identification request and update the list of identified users.
|
||||||
*/
|
*/
|
||||||
async submit_identification() {
|
async submit_identification() {
|
||||||
this.loading = true;
|
|
||||||
const url = `/api/sas/picture/${this.current_picture.id}/identified`;
|
const url = `/api/sas/picture/${this.current_picture.id}/identified`;
|
||||||
await fetch(url, {
|
await fetch(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
body: JSON.stringify(this.selector.val().map((i) => parseInt(i))),
|
body: JSON.stringify(this.selector.val().map((i) => parseInt(i))),
|
||||||
});
|
});
|
||||||
// refresh the identified users list
|
// refresh the identified users list
|
||||||
this.identifications = await (await fetch(url)).json();
|
await this.current_picture.load_identifications({ force_reload: true });
|
||||||
this.selector.empty().trigger("change");
|
this.selector.empty().trigger("change");
|
||||||
this.loading = false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,16 +246,15 @@ document.addEventListener("alpine:init", () => {
|
|||||||
* @param {PictureIdentification} identification
|
* @param {PictureIdentification} identification
|
||||||
*/
|
*/
|
||||||
async remove_identification(identification) {
|
async remove_identification(identification) {
|
||||||
this.loading = true;
|
|
||||||
const res = await fetch(`/api/sas/relation/${identification.id}`, {
|
const res = await fetch(`/api/sas/relation/${identification.id}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok && Array.isArray(this.current_picture.identifications)) {
|
||||||
this.identifications = this.identifications.filter(
|
this.current_picture.identifications =
|
||||||
(i) => i.id !== identification.id,
|
this.current_picture.identifications.filter(
|
||||||
);
|
(i) => i.id !== identification.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.loading = false;
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -58,8 +58,13 @@
|
|||||||
<div class="container" id="pict">
|
<div class="container" id="pict">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
|
|
||||||
<div class="photo" :aria-busy="loading">
|
<div class="photo" :aria-busy="current_picture.image_loading">
|
||||||
<img :src="current_picture.compressed_url" :alt="current_picture.name"/>
|
<img
|
||||||
|
:src="current_picture.compressed_url"
|
||||||
|
:alt="current_picture.name"
|
||||||
|
id="main-picture"
|
||||||
|
x-ref="main_picture"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="general">
|
<div class="general">
|
||||||
@ -137,7 +142,10 @@
|
|||||||
</form>
|
</form>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul>
|
<ul>
|
||||||
<template x-for="identification in identifications" :key="identification.id">
|
<template
|
||||||
|
x-for="identification in (current_picture.identifications || [])"
|
||||||
|
:key="identification.id"
|
||||||
|
>
|
||||||
<li>
|
<li>
|
||||||
<a class="user" :href="identification.user.profile_url">
|
<a class="user" :href="identification.user.profile_url">
|
||||||
<img class="profile-pic" :src="identification.user.profile_pict" alt="image de profil"/>
|
<img class="profile-pic" :src="identification.user.profile_pict" alt="image de profil"/>
|
||||||
@ -148,7 +156,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="loading">
|
<template x-if="current_picture.identifications_loading">
|
||||||
{# shadow element that exists only to put the loading wheel below
|
{# shadow element that exists only to put the loading wheel below
|
||||||
the list of identified people #}
|
the list of identified people #}
|
||||||
<li class="loader" aria-busy="true"></li>
|
<li class="loader" aria-busy="true"></li>
|
||||||
|
Loading…
Reference in New Issue
Block a user