Ctrl K
ComunidadGuías…

repositories/image.ts

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

Archivo original ↗Preguntar a KiBot
import localForage from 'localforage';
import type { ImageType, ImageMetaType, ImageSyncStatus } from './image.type';

const imageStore = localForage.createInstance({
  name: 'opennotas',
  storeName: 'images',
});

class ImageRepository {
  async getAllImagesMeta(): Promise<ImageMetaType[]> {
    const metas = JSON.parse(await localForage.getItem('imagesMeta') || '[]');
    return metas as ImageMetaType[];
  }

  async getImagesByNoteId(noteId: string): Promise<ImageMetaType[]> {
    const metas = await this.getAllImagesMeta();
    return metas.filter((meta) => meta.noteId === noteId);
  }

  async getImage(imageId: string): Promise<ImageType | null> {
    const meta = await this.getImageMeta(imageId);
    if (!meta) return null;

    const blob = await imageStore.getItem<Blob>(`image-blob-${imageId}`);
    if (!blob) return null;

    return { ...meta, blob };
  }

  async getImageMeta(imageId: string): Promise<ImageMetaType | null> {
    const metas = await this.getAllImagesMeta();
    return metas.find((meta) => meta.id === imageId) || null;
  }

  async saveImage(image: ImageType): Promise<ImageType> {
    await imageStore.setItem(`image-blob-${image.id}`, image.blob);

    const metas = await this.getAllImagesMeta();
    const existingIndex = metas.findIndex((m) => m.id === image.id);

    const meta: ImageMetaType = {
      id: image.id,
      noteId: image.noteId,
      mimeType: image.mimeType,
      size: image.size,
      syncStatus: image.syncStatus,
      remoteUrl: image.remoteUrl,
      createdAt: image.createdAt,
      updatedAt: image.updatedAt,
    };

    if (existingIndex >= 0) {
      metas[existingIndex] = meta;
    } else {
      metas.push(meta);
    }

    await localForage.setItem('imagesMeta', JSON.stringify(metas));
    return image;
  }

  async updateSyncStatus(
    imageId: string,
    syncStatus: ImageSyncStatus,
    remoteUrl?: string
  ): Promise<ImageMetaType | null> {
    const metas = await this.getAllImagesMeta();
    const index = metas.findIndex((m) => m.id === imageId);

    if (index < 0) return null;

    metas[index] = {
      ...metas[index],
      syncStatus,
      remoteUrl: remoteUrl || metas[index].remoteUrl,
      updatedAt: Date.now(),
    };

    await localForage.setItem('imagesMeta', JSON.stringify(metas));
    return metas[index];
  }

  async getPendingImages(): Promise<ImageType[]> {
    const metas = await this.getAllImagesMeta();
    const pendingMetas = metas.filter((m) => m.syncStatus === 'pending');

    const images: ImageType[] = [];
    for (const meta of pendingMetas) {
      const blob = await imageStore.getItem<Blob>(`image-blob-${meta.id}`);
      if (blob) {
        images.push({ ...meta, blob });
      }
    }

    return images;
  }

  async deleteImage(imageId: string): Promise<boolean> {
    await imageStore.removeItem(`image-blob-${imageId}`);

    const metas = await this.getAllImagesMeta();
    const newMetas = metas.filter((m) => m.id !== imageId);
    await localForage.setItem('imagesMeta', JSON.stringify(newMetas));

    return true;
  }

  async deleteImagesByNoteId(noteId: string): Promise<boolean> {
    const metas = await this.getAllImagesMeta();
    const toDelete = metas.filter((m) => m.noteId === noteId);

    for (const meta of toDelete) {
      await imageStore.removeItem(`image-blob-${meta.id}`);
    }

    const newMetas = metas.filter((m) => m.noteId !== noteId);
    await localForage.setItem('imagesMeta', JSON.stringify(newMetas));

    return true;
  }

  async cleanupOrphanedBlobs(): Promise<void> {
    const metas = await this.getAllImagesMeta();
    const validIds = new Set(metas.map((m) => m.id));

    const keys = await imageStore.keys();
    for (const key of keys) {
      if (key.startsWith('image-blob-')) {
        const id = key.replace('image-blob-', '');
        if (!validIds.has(id)) {
          await imageStore.removeItem(key);
        }
      }
    }
  }

  async saveImageFromRemote(
    imageId: string,
    blob: Blob,
    mimeType: string,
    remoteUrl: string
  ): Promise<ImageMetaType> {
    await imageStore.setItem(`image-blob-${imageId}`, blob);

    const now = Date.now();
    const meta: ImageMetaType = {
      id: imageId,
      noteId: '', // We don't know the noteId when fetching from remote
      mimeType,
      size: blob.size,
      syncStatus: 'synced',
      remoteUrl,
      createdAt: now,
      updatedAt: now,
    };

    const metas = await this.getAllImagesMeta();
    const existingIndex = metas.findIndex((m) => m.id === imageId);

    if (existingIndex >= 0) {
      metas[existingIndex] = meta;
    } else {
      metas.push(meta);
    }

    await localForage.setItem('imagesMeta', JSON.stringify(metas));
    return meta;
  }
}

export default ImageRepository;