@ws-serenity/use-data-transfer
TypeScript icon, indicating that this package has built-in type declarations

0.2.6 • Public • Published

UseDataTransfer

Хук для предоставления единого api для удаленных и локальных файлов.

Валидация: поддерживает api для валидации файлов.
Гибкость: поддерживает любые представления для описаний файлов, ошибок валидации, и конфигураций для скачивания файлов.
Кэширование: кэширует файлы, полученные для предпросмотра, до момента размонтирования компонента.
Состояние загрузки: предоставляет переменную, отражающую текущий процент загрузки каждого из файлов на сервер.
Отложенная синхронизация: имеет возможность отложить синхронизацию состояния с сервером, до вызова соответствующего метода.
Асинхронность: методы хука не блокируют поток.


Быстрый страт

Пример

Обобщения

В начале, нужно определить типы данных, используемые хуком.

TDataImage

TDataImage (image от en - образ, а не от en - изображение) определяет представление, в котором хранится информация о файле (но не сам файл).
Тип обязан иметь поле id: string, его можно или указать вручную (допускается ⚠️), или расширить имеющийся тип DataImage (оптимально ). Рекомендуется разбивать тип на type Additions + type CustomData = DataImage + Additions, тогда, можно будет использовать типизированный Object.assign<DataImage, Additions>(...), для создания TDataImage.

import { UseDataTransfer } from "@ws-serenity/use-data-transfer"; 

// Пусть каждый образ хранит название вложения и его размер
export type CustomDataImageAdditions = {
    name: string
    size: string
}

export type CustomDataImage = CustomDataImageAdditions & UseDataTransfer.DataImage

Хук расширяет интерфейс TDataImage до HookDrivenDataImage<TDataImage>.

const {add, images} = useDataTransfer<CustomDataImage /*..*/>(/*..*/);

add(file);

const hookDrivenImage = images[0];

console.log(hookDrivenImage.state); // Состояние данных, может принимать значения local | remote | uploading
const blob = hookDrivenImage.preview().blob; // Метод для скачивания данных в ОЗУ
hookDrivenImage.download(); // Метод для скачивания данных в ПЗУ
hookDrivenImage.remove(); // Метод для удаления данных

Таким образом, TDataImage не должен содержать следующие зарезервированные под расширение поля или методы: state, remove, preview, download.

TError

TError определяет представление ошибок, используемых при валидации локальных файлов. Может быть представлен любым значением типа Record<any, any>. TError, аналогично, будет расширен до HookDrivenValidationError<TError>.

const {add, images, errors} = useDataTransfer<CustomDataImage, CustomError /*..*/>(/*..*/);

add(file);

const hookDrivenError = errors[0];
console.log(hookDrivenError.id); // Уникальный id ошибка
hookDrivenError.resolve(); // Уадлить ошибку из коллекции

TError не должен содержать поля или методы: id, resolve.

Работа с Id
Хук создает уникальный идентификатор для каждого из добавленных образов. Образы, полученные удаленно, должны иметь свой id, как часть контракта, на котором построена работа с сервером. Когда локальный файл будет успешно отправлен на сервер, его id будет подменен на id присвоенный сервером.
Ошибки валидации, в отличие от образов, не могут прийти с сервера, их id на протяжении всего жизненного цикла контролируется хуком. Поэтому для TDataImage id это необходимое поле, а для TError расширяемое хуком.

TPreviewConfig и TDownloadConfig

TPreviewConfig и TDownloadConfig определяют конфиги, с которыми могут быть запрошены удаленные файлы. Могут быть представлены чем угодно, в том числе и отсутствовать, те, иметь значение, установленное по умолчанию, undefined.

Например:

export type CustomPreviewConfig = 'original' | 'thumbnail'

export type CustomDownloadConfig = {
    width: number
    height: number
}

// Также, может использоваться один тип
export type AnotherDownloadConfig = CustomPreviewConfig

Вспомогательные обработчики

Для того чтобы хук мог работать с TDataImage, ему необходимо предоставить маппер с сигнатурой (file: File, image: DataImage) => TDataImage, называемый genericDataImageBuilder.

// Для определенного выше `CustomDataImage`, может быть представлен так
const customDataImageBuilder = (file: File, image: DataImage) => {
    return Object.assign<DataImage, CustomDataImageAdditions>(image, {
        name: file.name,
        size: file.size
    })
}

Хук использует кэширование для каждого hookDrivenImage.preview(previewConfig) запроса. Кэшируется не только файл, но и конфиг запроса. Так как хук ничего не знает об используемом TPreviewConfig, то ему нужен вспомогательный обработчик cacheWorker: CacheWorker.

export type CacheWorker<TPreviewConfig> = {
    search: (cache, config?) => CachedEntity | undefined // 1
    getLocalFileQualityConfig: (file: File) => TPreviewConfig // 2
}

Для разрешения случаев, когда вместо запроса на сервер, можно предоставить кэшированный файл от запроса с другим конфигом, используется search (1) метод. Например, такое поведение может быть полезно, когда кэширован оригинал изображения, и необходима версия с меньшим разрешением.
Если search находит подходящую сущность, то он возвращает её, в ином случае - undefined.

При добавлении локального файла, необходимо знать какому TPreviewConfig он соответствует. Для этого используется метод getLocalFileQualityConfig (2).

Для общего случая TPreviewConfig = undefined, cacheWorker может быть таким:

const cacheWorker: CacheWorker<undefined> = {
    getLocalFileQualityConfig: () => undefined,
    search: (entities) => entities[0]
}

Провайдер для удаленных файлов

Для работы с сервером хуку требуется реализация абстрактного класса RemoteDataProvider.

export abstract class RemoteDataProvider<
    TDataImage extends DataImage = DataImage,
    TPreviewConfig = undefined,
    TDownloadConfig = undefined
> {
    // Отменяет выполняющуюся загрузку файла на сервер
    abstract abort(imageId: string): Promise<void>
    
    // Возвращает описания всех файлов на сервере
    abstract getAllDataImages(): Promise<TDataImage[]>
    
    // Скачивание файлов
    abstract preview(imageId: string, config: TPreviewConfig): Promise<Blob>
    abstract download(imageId: string, config: TDownloadConfig): Promise<void>
    
    // Изменение состояния на сервере
    abstract commit(
        config: {
            // Commit конфиг содержит коллекцию из id файлов, которые необходимо удалить
            toDelete?: string[],
            // и коллекцию { локальный id (присвоенный хуком) -> файл }, которую необходимо загрузить
            toUpload?: Record<string, Blob>
        },
        // Событие для отслеживания состояния загрузки
        onProgress?: (event: ProgressEvent) => void
    // Обязано вернуть коллекцию { локальный id (присвоенный хуком) -> удаленный id (присвоенный сервером) }
    ): Promise<Record<string, string>>
}

Использование хука

Определив все обобщения, подготовив вспомогательные обработчики и реализацию RemoteDataProvider, можно использовать хук.

const {
    images, // Все образы
    errors, // Все ошибки
    resolve, // Удалить ошибку по id
    remove, // Удалить файл по id
    add, // Добавить коллекцию локальных файлов
    dataTransferProgress, // Коллекция { локальный id -> доля отправленных данных }
    isLoading, // true до обработки remoteDataProvider.getAllImages()
    commit // Синхронизирует изменения с сервером
} = useDataTransfer<CustomDataImage, CustomError>(
    remoteDataProvider,
    customDataImageBuilder,
    cacheWorker
)

Конфигурация

Необязательный четвертый параметр useDataTransfer - конфигурация, дополнительно настраивающая поведение хука.

Параметр Тип По умолчанию Описание
readonly boolean false Устанавливает поведение "только для чтения".
strictReadonly boolean true Если true, то, при попытке изменения состояния хука, будет вызвано исключение.
dataTransferBehaviour DataTransferBehavior execute-on-commit Если execute-on-commit, то синхронизация с сервером будет произведена после вызова commit(). Если execute-on-action, то синхронизация с сервером будет происходить после каждого изменения, а вызов commit() приведет к исключению.
dataTransferAccumulationInterval number 500 Период в миллисекундах, с которым будет обновляться состояние dataTransferProgress при наличии активных загрузок.
validators ValidationFunction[] Коллекция правил для валидации файлов.
onErrorResolved ActionCallback Коллбэк для resolve().
onImageRemoved ActionCallback Коллбэк для remove().
onImageAdded ImageCallback Коллбэк для add(), вызывается отдельно для каждого файла.

Валидация

Функция валидации имеет следующую сигнатуру:

export type Context = {
    images: HookDrivenDataImage[]
}

// В случае, если ошибок нет, то функция ничего не возвращает
export type ValidationFunction = (file: File, ctx: Context) => TError | void

Функции валидации будут вызваны в порядке объявления, как только валидатор вернет ошибку, проверка остановится. Например, если файл не удовлетворяет функциям валидации А и Б (объявленным в соответствующем порядке), то в errors попадет только ошибка из валидатора А.


Ограничения

Изменение параметров хука, отличных от config, в реальном времени может привести к некорректному поведению.

Package Sidebar

Install

npm i @ws-serenity/use-data-transfer

Weekly Downloads

3

Version

0.2.6

License

ISC

Unpacked Size

38.5 kB

Total Files

7

Last publish

Collaborators

  • skukartsev
  • ra.vi.an
  • gransly
  • d.duda
  • blablaprincess
  • a.manakina
  • f.ishchenko