Ctrl K
ComunidadGuías…

src/lib/picture-edits.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { authAPI } from "@/lib/api/auth";
import type { PictureCrop, PictureKind } from "@/lib/picture";

/**
 * The image a pending edit is cropped from: a picked file, or an original fetched from
 * the API. The size is the decoded, upright one the crop is measured in.
 */
export interface PictureSource {
  blob: Blob;
  width: number;
  height: number;
}

/**
 * What saving the form will do to its picture, or nothing at all when the diver has not
 * touched it. Each carries what its one request needs, and the three that show a
 * picture carry what the preview draws it from.
 *
 * `previewUrl` rides on the edit rather than being derived where it is drawn because an
 * object URL has a lifetime somebody has to own: the document holds the bytes until it
 * is revoked. `usePictureEdit` revokes it when the edit is replaced or dropped, and
 * sweeps whatever is left when the form goes.
 */
export type PictureEdit =
  | {
      kind: "replace";
      source: PictureSource;
      /** What the original is stored under: the picked file's own name. */
      filename: string;
      crop: PictureCrop;
      previewUrl: string;
    }
  | {
      /** A new crop over the original the API already holds. */
      kind: "adjust";
      source: PictureSource;
      crop: PictureCrop;
      previewUrl: string;
    }
  | {
      /** The portrait's only: the avatar's original, at a crop of its own. */
      kind: "copy";
      source: PictureSource;
      crop: PictureCrop;
      previewUrl: string;
    }
  | { kind: "remove" };

/** The edits that put a picture on screen, as opposed to taking one off. */
export type PictureEditWithSource = Exclude<PictureEdit, { kind: "remove" }>;

export function pictureEditPreviewUrl(edit: PictureEdit | null): string | null {
  return edit && edit.kind !== "remove" ? edit.previewUrl : null;
}

/**
 * Sends the one request a form's picture edit needs, once the form's fields have saved.
 *
 * **Nothing here runs before the save**, which is the whole reason the form holds an
 * edit rather than uploading on pick: a diver who cancels leaves the stored picture
 * exactly as they found it. Throws what the API answered, for the caller to name the
 * picture in.
 */
export async function applyPictureEdit(
  picture: PictureKind,
  edit: PictureEdit,
): Promise<void> {
  switch (edit.kind) {
    case "replace":
      // `PUT` replaces whatever is there, so it needs no delete in front of it.
      await authAPI.uploadPicture(
        picture,
        edit.source.blob,
        edit.filename,
        edit.crop,
      );
      return;
    case "adjust":
      await authAPI.adjustPicture(picture, edit.crop);
      return;
    case "copy":
      if (picture !== "portrait") {
        throw new Error("Only the portrait is copied from another picture.");
      }
      await authAPI.copyAvatarToPortrait(edit.crop);
      return;
    case "remove":
      await authAPI.removePicture(picture);
      return;
  }
}