mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 11:03:04 +00:00 
			
		
		
		
	Add helper function to export ts functions to html
This commit is contained in:
		@@ -4,3 +4,17 @@ declare global {
 | 
				
			|||||||
  const Alpine: AlpineType;
 | 
					  const Alpine: AlpineType;
 | 
				
			||||||
  const gettext: (text: string) => string;
 | 
					  const gettext: (text: string) => string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Helper function to export typescript functions to regular html and jinja files
 | 
				
			||||||
 | 
					 * Without it, you either have to use the any keyword and suppress warnings or do a
 | 
				
			||||||
 | 
					 * very painful type conversion workaround which is only here to please the linter
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is only useful if you're using typescript, this is equivalent to doing
 | 
				
			||||||
 | 
					 * window.yourFunction = yourFunction
 | 
				
			||||||
 | 
					 **/
 | 
				
			||||||
 | 
					// biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
 | 
				
			||||||
 | 
					export function exportToHtml(name: string, func: any) {
 | 
				
			||||||
 | 
					  // biome-ignore lint/suspicious/noExplicitAny: Avoid strange tricks to export functions
 | 
				
			||||||
 | 
					  (window as any)[name] = func;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { makeUrl, paginated } from "#core:utils/api";
 | 
					import { makeUrl, paginated } from "#core:utils/api";
 | 
				
			||||||
 | 
					import { exportToHtml } from "#core:utils/globals";
 | 
				
			||||||
import { History } from "#core:utils/history";
 | 
					import { History } from "#core:utils/history";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  type AjaxResponse,
 | 
					  type AjaxResponse,
 | 
				
			||||||
@@ -97,228 +98,223 @@ interface ViewerConfig {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Load user picture page with a nice download bar
 | 
					 * Load user picture page with a nice download bar
 | 
				
			||||||
 **/
 | 
					 **/
 | 
				
			||||||
(window as unknown as { loadViewer: (config: ViewerConfig) => undefined }).loadViewer =
 | 
					exportToHtml("loadViewer", (config: ViewerConfig) => {
 | 
				
			||||||
  (config: ViewerConfig) => {
 | 
					  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 PictureWithIdentifications[]
 | 
				
			||||||
         * @type PictureWithIdentifications[]
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      pictures: [],
 | 
				
			||||||
        pictures: [],
 | 
					      /**
 | 
				
			||||||
        /**
 | 
					       * 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 PictureWithIdentifications
 | 
				
			||||||
         * @type PictureWithIdentifications
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      currentPicture: {
 | 
				
			||||||
        currentPicture: {
 | 
					        // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        is_moderated: true,
 | 
				
			||||||
          is_moderated: true,
 | 
					        id: null,
 | 
				
			||||||
          id: null,
 | 
					        name: "",
 | 
				
			||||||
          name: "",
 | 
					        // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        display_name: "",
 | 
				
			||||||
          display_name: "",
 | 
					        // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        compressed_url: "",
 | 
				
			||||||
          compressed_url: "",
 | 
					        // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        profile_url: "",
 | 
				
			||||||
          profile_url: "",
 | 
					        // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        full_size_url: "",
 | 
				
			||||||
          full_size_url: "",
 | 
					        owner: "",
 | 
				
			||||||
          owner: "",
 | 
					        date: new Date(),
 | 
				
			||||||
          date: new Date(),
 | 
					        identifications: [],
 | 
				
			||||||
          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 ?PictureWithIdentifications
 | 
				
			||||||
         * @type ?PictureWithIdentifications
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      nextPicture: null,
 | 
				
			||||||
        nextPicture: 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 ?PictureWithIdentifications
 | 
				
			||||||
         * @type ?PictureWithIdentifications
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      previousPicture: null,
 | 
				
			||||||
        previousPicture: null,
 | 
					      /**
 | 
				
			||||||
        /**
 | 
					       * The select2 component used to identify users
 | 
				
			||||||
         * The select2 component used to identify users
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      selector: undefined,
 | 
				
			||||||
        selector: undefined,
 | 
					      /**
 | 
				
			||||||
        /**
 | 
					       * true if the page is in a loading state, else false
 | 
				
			||||||
         * true if the page is in a loading state, else false
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      /**
 | 
				
			||||||
        /**
 | 
					       * Error message when a moderation operation fails
 | 
				
			||||||
         * Error message when a moderation operation fails
 | 
					       * @type string
 | 
				
			||||||
         * @type string
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      moderationError: "",
 | 
				
			||||||
        moderationError: "",
 | 
					      /**
 | 
				
			||||||
        /**
 | 
					       * Method of pushing new url to the browser history
 | 
				
			||||||
         * Method of pushing new url to the browser history
 | 
					       * Used by popstate event and always reset to it's default value when used
 | 
				
			||||||
         * Used by popstate event and always reset to it's default value when used
 | 
					       * @type History
 | 
				
			||||||
         * @type History
 | 
					       **/
 | 
				
			||||||
         **/
 | 
					      pushstate: History.Push,
 | 
				
			||||||
        pushstate: History.Push,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async init() {
 | 
					      async init() {
 | 
				
			||||||
          this.pictures = (
 | 
					        this.pictures = (
 | 
				
			||||||
            await paginated(picturesFetchPictures, {
 | 
					          await paginated(picturesFetchPictures, {
 | 
				
			||||||
              // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					            // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
              query: { album_id: config.albumId },
 | 
					            query: { album_id: config.albumId },
 | 
				
			||||||
            } as PicturesFetchPicturesData)
 | 
					          } as PicturesFetchPicturesData)
 | 
				
			||||||
          ).map(PictureWithIdentifications.fromPicture);
 | 
					        ).map(PictureWithIdentifications.fromPicture);
 | 
				
			||||||
          this.selector = sithSelect2({
 | 
					        this.selector = sithSelect2({
 | 
				
			||||||
            element: $(this.$refs.search) as unknown as HTMLElement,
 | 
					          element: $(this.$refs.search) as unknown as HTMLElement,
 | 
				
			||||||
            dataSource: remoteDataSource(await makeUrl(userSearchUsers), {
 | 
					          dataSource: remoteDataSource(await makeUrl(userSearchUsers), {
 | 
				
			||||||
              excluded: () => [
 | 
					            excluded: () => [
 | 
				
			||||||
                ...(this.currentPicture.identifications || []).map(
 | 
					              ...(this.currentPicture.identifications || []).map(
 | 
				
			||||||
                  (i: IdentifiedUserSchema) => i.user.id,
 | 
					                (i: IdentifiedUserSchema) => i.user.id,
 | 
				
			||||||
                ),
 | 
					              ),
 | 
				
			||||||
              ],
 | 
					            ],
 | 
				
			||||||
              resultConverter: (obj: AjaxResponse) => {
 | 
					            resultConverter: (obj: AjaxResponse) => {
 | 
				
			||||||
                return { ...obj, text: (obj as UserProfileSchema).display_name };
 | 
					              return { ...obj, text: (obj as UserProfileSchema).display_name };
 | 
				
			||||||
              },
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
            pictureGetter: (user: RemoteResult) => user.profile_pict,
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          this.currentPicture = this.pictures.find(
 | 
					 | 
				
			||||||
            (i: PictureSchema) => i.id === config.firstPictureId,
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
          this.$watch(
 | 
					 | 
				
			||||||
            "currentPicture",
 | 
					 | 
				
			||||||
            (current: PictureSchema, previous: PictureSchema) => {
 | 
					 | 
				
			||||||
              if (current === previous) {
 | 
					 | 
				
			||||||
                /* Avoid recursive updates */
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
              this.updatePicture();
 | 
					 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
          );
 | 
					          }),
 | 
				
			||||||
          window.addEventListener("popstate", async (event) => {
 | 
					          pictureGetter: (user: RemoteResult) => user.profile_pict,
 | 
				
			||||||
            if (!event.state || event.state.sasPictureId === undefined) {
 | 
					        });
 | 
				
			||||||
 | 
					        this.currentPicture = this.pictures.find(
 | 
				
			||||||
 | 
					          (i: PictureSchema) => i.id === config.firstPictureId,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        this.$watch(
 | 
				
			||||||
 | 
					          "currentPicture",
 | 
				
			||||||
 | 
					          (current: PictureSchema, previous: PictureSchema) => {
 | 
				
			||||||
 | 
					            if (current === previous) {
 | 
				
			||||||
 | 
					              /* Avoid recursive updates */
 | 
				
			||||||
              return;
 | 
					              return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            this.pushstate = History.Replace;
 | 
					            this.updatePicture();
 | 
				
			||||||
            this.currentPicture = this.pictures.find(
 | 
					          },
 | 
				
			||||||
              (i: PictureSchema) => i.id === Number.parseInt(event.state.sasPictureId),
 | 
					        );
 | 
				
			||||||
            );
 | 
					        window.addEventListener("popstate", async (event) => {
 | 
				
			||||||
          });
 | 
					          if (!event.state || event.state.sasPictureId === undefined) {
 | 
				
			||||||
          this.pushstate = History.Replace; /* Avoid first url push */
 | 
					 | 
				
			||||||
          await this.updatePicture();
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /**
 | 
					 | 
				
			||||||
         * Update the page.
 | 
					 | 
				
			||||||
         * Called when the `currentPicture` property changes.
 | 
					 | 
				
			||||||
         *
 | 
					 | 
				
			||||||
         * The url is modified without reloading the page,
 | 
					 | 
				
			||||||
         * and the previous picture, the next picture and
 | 
					 | 
				
			||||||
         * the list of identified users are updated.
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        async updatePicture() {
 | 
					 | 
				
			||||||
          const updateArgs = {
 | 
					 | 
				
			||||||
            data: { sasPictureId: this.currentPicture.id },
 | 
					 | 
				
			||||||
            unused: "",
 | 
					 | 
				
			||||||
            url: `/sas/picture/${this.currentPicture.id}/`,
 | 
					 | 
				
			||||||
          };
 | 
					 | 
				
			||||||
          if (this.pushstate === History.Replace) {
 | 
					 | 
				
			||||||
            window.history.replaceState(
 | 
					 | 
				
			||||||
              updateArgs.data,
 | 
					 | 
				
			||||||
              updateArgs.unused,
 | 
					 | 
				
			||||||
              updateArgs.url,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
            this.pushstate = History.Push;
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            window.history.pushState(
 | 
					 | 
				
			||||||
              updateArgs.data,
 | 
					 | 
				
			||||||
              updateArgs.unused,
 | 
					 | 
				
			||||||
              updateArgs.url,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          this.moderationError = "";
 | 
					 | 
				
			||||||
          const index = this.pictures.indexOf(this.currentPicture);
 | 
					 | 
				
			||||||
          this.previousPicture = this.pictures[index - 1] || null;
 | 
					 | 
				
			||||||
          this.nextPicture = this.pictures[index + 1] || null;
 | 
					 | 
				
			||||||
          await this.currentPicture.loadIdentifications();
 | 
					 | 
				
			||||||
          this.$refs.mainPicture?.addEventListener("load", () => {
 | 
					 | 
				
			||||||
            // once the current picture is loaded,
 | 
					 | 
				
			||||||
            // start preloading the next and previous pictures
 | 
					 | 
				
			||||||
            this.nextPicture?.preload();
 | 
					 | 
				
			||||||
            this.previousPicture?.preload();
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        async moderatePicture() {
 | 
					 | 
				
			||||||
          const res = await picturesModeratePicture({
 | 
					 | 
				
			||||||
            // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					 | 
				
			||||||
            path: { picture_id: this.currentPicture.id },
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          if (res.error) {
 | 
					 | 
				
			||||||
            this.moderationError = `${gettext("Couldn't moderate picture")} : ${(res.error as { detail: string }).detail}`;
 | 
					 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          this.currentPicture.is_moderated = true;
 | 
					          this.pushstate = History.Replace;
 | 
				
			||||||
          this.currentPicture.askedForRemoval = false;
 | 
					          this.currentPicture = this.pictures.find(
 | 
				
			||||||
        },
 | 
					            (i: PictureSchema) => i.id === Number.parseInt(event.state.sasPictureId),
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        this.pushstate = History.Replace; /* Avoid first url push */
 | 
				
			||||||
 | 
					        await this.updatePicture();
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        async deletePicture() {
 | 
					      /**
 | 
				
			||||||
          const res = await picturesDeletePicture({
 | 
					       * Update the page.
 | 
				
			||||||
 | 
					       * Called when the `currentPicture` property changes.
 | 
				
			||||||
 | 
					       *
 | 
				
			||||||
 | 
					       * The url is modified without reloading the page,
 | 
				
			||||||
 | 
					       * and the previous picture, the next picture and
 | 
				
			||||||
 | 
					       * the list of identified users are updated.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      async updatePicture() {
 | 
				
			||||||
 | 
					        const updateArgs = {
 | 
				
			||||||
 | 
					          data: { sasPictureId: this.currentPicture.id },
 | 
				
			||||||
 | 
					          unused: "",
 | 
				
			||||||
 | 
					          url: `/sas/picture/${this.currentPicture.id}/`,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if (this.pushstate === History.Replace) {
 | 
				
			||||||
 | 
					          window.history.replaceState(
 | 
				
			||||||
 | 
					            updateArgs.data,
 | 
				
			||||||
 | 
					            updateArgs.unused,
 | 
				
			||||||
 | 
					            updateArgs.url,
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          this.pushstate = History.Push;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          window.history.pushState(updateArgs.data, updateArgs.unused, updateArgs.url);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.moderationError = "";
 | 
				
			||||||
 | 
					        const index = this.pictures.indexOf(this.currentPicture);
 | 
				
			||||||
 | 
					        this.previousPicture = this.pictures[index - 1] || null;
 | 
				
			||||||
 | 
					        this.nextPicture = this.pictures[index + 1] || null;
 | 
				
			||||||
 | 
					        await this.currentPicture.loadIdentifications();
 | 
				
			||||||
 | 
					        this.$refs.mainPicture?.addEventListener("load", () => {
 | 
				
			||||||
 | 
					          // once the current picture is loaded,
 | 
				
			||||||
 | 
					          // start preloading the next and previous pictures
 | 
				
			||||||
 | 
					          this.nextPicture?.preload();
 | 
				
			||||||
 | 
					          this.previousPicture?.preload();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      async moderatePicture() {
 | 
				
			||||||
 | 
					        const res = await picturesModeratePicture({
 | 
				
			||||||
 | 
					          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
 | 
					          path: { picture_id: this.currentPicture.id },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if (res.error) {
 | 
				
			||||||
 | 
					          this.moderationError = `${gettext("Couldn't moderate picture")} : ${(res.error as { detail: string }).detail}`;
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.currentPicture.is_moderated = true;
 | 
				
			||||||
 | 
					        this.currentPicture.askedForRemoval = false;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      async deletePicture() {
 | 
				
			||||||
 | 
					        const res = await picturesDeletePicture({
 | 
				
			||||||
 | 
					          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
 | 
					          path: { picture_id: this.currentPicture.id },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        if (res.error) {
 | 
				
			||||||
 | 
					          this.moderationError = `${gettext("Couldn't delete picture")} : ${(res.error as { detail: string }).detail}`;
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.pictures.splice(this.pictures.indexOf(this.currentPicture), 1);
 | 
				
			||||||
 | 
					        if (this.pictures.length === 0) {
 | 
				
			||||||
 | 
					          // The deleted picture was the only one in the list.
 | 
				
			||||||
 | 
					          // As the album is now empty, go back to the parent page
 | 
				
			||||||
 | 
					          document.location.href = config.albumUrl;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        this.currentPicture = this.nextPicture || this.previousPicture;
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /**
 | 
				
			||||||
 | 
					       * Send the identification request and update the list of identified users.
 | 
				
			||||||
 | 
					       */
 | 
				
			||||||
 | 
					      async submitIdentification() {
 | 
				
			||||||
 | 
					        await picturesIdentifyUsers({
 | 
				
			||||||
 | 
					          path: {
 | 
				
			||||||
            // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					            // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
            path: { picture_id: this.currentPicture.id },
 | 
					            picture_id: this.currentPicture.id,
 | 
				
			||||||
          });
 | 
					          },
 | 
				
			||||||
          if (res.error) {
 | 
					          body: this.selector.val().map((i: string) => Number.parseInt(i)),
 | 
				
			||||||
            this.moderationError = `${gettext("Couldn't delete picture")} : ${(res.error as { detail: string }).detail}`;
 | 
					        });
 | 
				
			||||||
            return;
 | 
					        // refresh the identified users list
 | 
				
			||||||
          }
 | 
					        await this.currentPicture.loadIdentifications({ forceReload: true });
 | 
				
			||||||
          this.pictures.splice(this.pictures.indexOf(this.currentPicture), 1);
 | 
					        this.selector.empty().trigger("change");
 | 
				
			||||||
          if (this.pictures.length === 0) {
 | 
					      },
 | 
				
			||||||
            // The deleted picture was the only one in the list.
 | 
					 | 
				
			||||||
            // As the album is now empty, go back to the parent page
 | 
					 | 
				
			||||||
            document.location.href = config.albumUrl;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          this.currentPicture = this.nextPicture || this.previousPicture;
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					      /**
 | 
				
			||||||
         * Send the identification request and update the list of identified users.
 | 
					       * Check if an identification can be removed by the currently logged user
 | 
				
			||||||
         */
 | 
					       * @param {PictureIdentification} identification
 | 
				
			||||||
        async submitIdentification() {
 | 
					       * @return {boolean}
 | 
				
			||||||
          await picturesIdentifyUsers({
 | 
					       */
 | 
				
			||||||
            path: {
 | 
					      canBeRemoved(identification: IdentifiedUserSchema) {
 | 
				
			||||||
              // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					        return config.userIsSasAdmin || identification.user.id === config.userId;
 | 
				
			||||||
              picture_id: this.currentPicture.id,
 | 
					      },
 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            body: this.selector.val().map((i: string) => Number.parseInt(i)),
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
          // refresh the identified users list
 | 
					 | 
				
			||||||
          await this.currentPicture.loadIdentifications({ forceReload: true });
 | 
					 | 
				
			||||||
          this.selector.empty().trigger("change");
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /**
 | 
					      /**
 | 
				
			||||||
         * Check if an identification can be removed by the currently logged user
 | 
					       * Untag a user from the current picture
 | 
				
			||||||
         * @param {PictureIdentification} identification
 | 
					       * @param {PictureIdentification} identification
 | 
				
			||||||
         * @return {boolean}
 | 
					       */
 | 
				
			||||||
         */
 | 
					      async removeIdentification(identification: IdentifiedUserSchema) {
 | 
				
			||||||
        canBeRemoved(identification: IdentifiedUserSchema) {
 | 
					        const res = await usersidentifiedDeleteRelation({
 | 
				
			||||||
          return config.userIsSasAdmin || identification.user.id === config.userId;
 | 
					          // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
				
			||||||
        },
 | 
					          path: { relation_id: identification.id },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        /**
 | 
					        if (!res.error && Array.isArray(this.currentPicture.identifications)) {
 | 
				
			||||||
         * Untag a user from the current picture
 | 
					          this.currentPicture.identifications =
 | 
				
			||||||
         * @param {PictureIdentification} identification
 | 
					            this.currentPicture.identifications.filter(
 | 
				
			||||||
         */
 | 
					              (i: IdentifiedUserSchema) => i.id !== identification.id,
 | 
				
			||||||
        async removeIdentification(identification: IdentifiedUserSchema) {
 | 
					            );
 | 
				
			||||||
          const res = await usersidentifiedDeleteRelation({
 | 
					        }
 | 
				
			||||||
            // biome-ignore lint/style/useNamingConvention: api is in snake_case
 | 
					      },
 | 
				
			||||||
            path: { relation_id: identification.id },
 | 
					    }));
 | 
				
			||||||
          });
 | 
					  });
 | 
				
			||||||
          if (!res.error && Array.isArray(this.currentPicture.identifications)) {
 | 
					});
 | 
				
			||||||
            this.currentPicture.identifications =
 | 
					 | 
				
			||||||
              this.currentPicture.identifications.filter(
 | 
					 | 
				
			||||||
                (i: IdentifiedUserSchema) => i.id !== identification.id,
 | 
					 | 
				
			||||||
              );
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      }));
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user