diff --git a/src/redux/tag/api.ts b/src/redux/tag/api.ts index d657b9b7..b03f9400 100644 --- a/src/redux/tag/api.ts +++ b/src/redux/tag/api.ts @@ -1,33 +1,18 @@ -import { INode, IResultWithStatus } from '~/redux/types'; -import { api, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api'; +import { api, cleanResult } from '~/utils/api'; import { API } from '~/constants/api'; +import { + ApiGetNodesOfTagRequest, + ApiGetNodesOfTagResult, + ApiGetTagSuggestionsRequest, + ApiGetTagSuggestionsResult, +} from '~/redux/tag/types'; -export const getTagNodes = ({ - access, - tag, - offset, - limit, -}: { - access: string; - tag: string; - offset: number; - limit: number; -}): Promise> => +export const apiGetNodesOfTag = ({ tag, offset, limit }: ApiGetNodesOfTagRequest) => api - .get(API.TAG.NODES, configWithToken(access, { params: { name: tag, offset, limit } })) - .then(resultMiddleware) - .catch(errorMiddleware); + .get(API.TAG.NODES, { params: { name: tag, offset, limit } }) + .then(cleanResult); -export const getTagAutocomplete = ({ - search, - exclude, - access, -}: { - access: string; - search: string; - exclude: string[]; -}): Promise> => +export const apiGetTagSuggestions = ({ search, exclude }: ApiGetTagSuggestionsRequest) => api - .get(API.TAG.AUTOCOMPLETE, configWithToken(access, { params: { search, exclude } })) - .then(resultMiddleware) - .catch(errorMiddleware); + .get(API.TAG.AUTOCOMPLETE, { params: { search, exclude } }) + .then(cleanResult); diff --git a/src/redux/tag/sagas.ts b/src/redux/tag/sagas.ts index 00bcdf35..eb1c3f16 100644 --- a/src/redux/tag/sagas.ts +++ b/src/redux/tag/sagas.ts @@ -6,48 +6,43 @@ import { tagSetAutocomplete, tagSetNodes, } from '~/redux/tag/actions'; -import { wrap } from '~/redux/auth/sagas'; import { selectTagNodes } from '~/redux/tag/selectors'; -import { getTagAutocomplete, getTagNodes } from '~/redux/tag/api'; +import { apiGetTagSuggestions, apiGetNodesOfTag } from '~/redux/tag/api'; import { Unwrap } from '~/redux/types'; function* loadTagNodes({ tag }: ReturnType) { - yield put(tagSetNodes({ isLoading: true })); + yield put(tagSetNodes({ isLoading: true, list: [] })); try { const { list }: ReturnType = yield select(selectTagNodes); - const { data, error }: Unwrap = yield call(wrap, getTagNodes, { + const data: Unwrap = yield call(apiGetNodesOfTag, { tag, limit: 18, offset: list.length, }); - if (error) throw new Error(error); - - yield put(tagSetNodes({ isLoading: false, list: [...list, ...data.nodes], count: data.count })); - } catch (e) { - console.log(e); + yield put(tagSetNodes({ list: [...list, ...data.nodes], count: data.count })); + } catch { + } finally { yield put(tagSetNodes({ isLoading: false })); } } function* loadAutocomplete({ search, exclude }: ReturnType) { - if (search.length < 3) return; + if (search.length < 2) return; try { yield put(tagSetAutocomplete({ isLoading: true })); - yield delay(100); + yield delay(200); - const { data, error }: Unwrap = yield call( - wrap, - getTagAutocomplete, - { search, exclude } - ); + const data: Unwrap = yield call(apiGetTagSuggestions, { + search, + exclude, + }); - if (error) throw new Error(error); - - yield put(tagSetAutocomplete({ options: data.tags, isLoading: false })); - } catch (e) { + yield put(tagSetAutocomplete({ options: data.tags })); + } catch { + } finally { yield put(tagSetAutocomplete({ isLoading: false })); } } diff --git a/src/redux/tag/types.ts b/src/redux/tag/types.ts new file mode 100644 index 00000000..9f7f41e7 --- /dev/null +++ b/src/redux/tag/types.ts @@ -0,0 +1,16 @@ +import { INode } from '~/redux/types'; + +export type ApiGetNodesOfTagRequest = { + tag: string; + offset: number; + limit: number; +}; +export type ApiGetNodesOfTagResult = { nodes: INode[]; count: number }; + +export type ApiGetTagSuggestionsRequest = { + search: string; + exclude: string[]; +}; +export type ApiGetTagSuggestionsResult = { + tags: string[]; +}; diff --git a/src/redux/uploads/api.ts b/src/redux/uploads/api.ts index 2697c75a..756ac8ac 100644 --- a/src/redux/uploads/api.ts +++ b/src/redux/uploads/api.ts @@ -1,31 +1,20 @@ -import { - IResultWithStatus, IFile, IUploadProgressHandler, IFileWithUUID, -} from '~/redux/types'; -import { - api, configWithToken, resultMiddleware, errorMiddleware, -} from '~/utils/api'; +import { api, cleanResult } from '~/utils/api'; import { API } from '~/constants/api'; +import { ApiUploadFileRequest, ApiUploadFIleResult } from '~/redux/uploads/types'; -export const postUploadFile = ({ - access, +export const apiUploadFile = ({ file, target = 'others', type = 'image', onProgress, -}: IFileWithUUID & { - access: string; - onProgress: IUploadProgressHandler; -}): Promise> => { +}: ApiUploadFileRequest) => { const data = new FormData(); data.append('file', file); return api - .post( - API.USER.UPLOAD(target, type), - data, - configWithToken(access, { onUploadProgress: onProgress }) - ) - .then(resultMiddleware) - .catch(errorMiddleware); + .post(API.USER.UPLOAD(target, type), data, { + onUploadProgress: onProgress, + }) + .then(cleanResult); }; diff --git a/src/redux/uploads/sagas.ts b/src/redux/uploads/sagas.ts index e832d16f..c7b168fd 100644 --- a/src/redux/uploads/sagas.ts +++ b/src/redux/uploads/sagas.ts @@ -1,17 +1,17 @@ -import { takeEvery, all, spawn, call, put, take, fork, race } from 'redux-saga/effects'; -import { postUploadFile } from './api'; -import { UPLOAD_ACTIONS, FILE_MIMES } from '~/redux/uploads/constants'; +import { SagaIterator } from 'redux-saga'; +import { all, call, fork, put, race, spawn, take, takeEvery } from 'redux-saga/effects'; +import { apiUploadFile } from './api'; +import { FILE_MIMES, UPLOAD_ACTIONS } from '~/redux/uploads/constants'; import { - uploadUploadFiles, - uploadSetStatus, + uploadAddFile, uploadAddStatus, uploadDropStatus, - uploadAddFile, + uploadSetStatus, + uploadUploadFiles, } from './actions'; -import { wrap } from '../auth/sagas'; import { createUploader, uploadGetThumb } from '~/utils/uploader'; import { HTTP_RESPONSES } from '~/utils/api'; -import { IFileWithUUID, IFile, IUploadProgressHandler } from '../types'; +import { IFileWithUUID, IUploadProgressHandler, Unwrap } from '../types'; function* uploadCall({ file, @@ -20,13 +20,15 @@ function* uploadCall({ type, onProgress, }: IFileWithUUID & { onProgress: IUploadProgressHandler }) { - return yield call(wrap, postUploadFile, { + const data: Unwrap = yield call(apiUploadFile, { file, temp_id, type, target, onProgress, }); + + return data; } function* onUploadProgress(chan) { @@ -46,7 +48,12 @@ function* uploadCancelWorker(id) { return true; } -function* uploadWorker({ file, temp_id, target, type }: IFileWithUUID) { +function* uploadWorker({ + file, + temp_id, + target, + type, +}: IFileWithUUID): SagaIterator> { const [promise, chan] = createUploader, Partial>( uploadCall, { temp_id, target, type } @@ -63,77 +70,74 @@ function* uploadWorker({ file, temp_id, target, type }: IFileWithUUID) { } function* uploadFile({ file, temp_id, type, target, onSuccess, onFail }: IFileWithUUID) { - if (!file.type || !FILE_MIMES[type] || !FILE_MIMES[type].includes(file.type)) { - return { - error: 'File_Not_Image', - status: HTTP_RESPONSES.BAD_REQUEST, - data: {}, - }; - } + if (!temp_id) return; - const preview = yield call(uploadGetThumb, file); + try { + if (!file.type || !FILE_MIMES[type] || !FILE_MIMES[type].includes(file.type)) { + return { + error: 'File_Not_Image', + status: HTTP_RESPONSES.BAD_REQUEST, + data: {}, + }; + } - yield put( - uploadAddStatus( - // replace with the one, what adds file upload status - temp_id, - { - preview, + const preview: Unwrap = yield call(uploadGetThumb, file); + + yield put( + uploadAddStatus(temp_id, { + preview: preview.toString(), is_uploading: true, temp_id, type, name: file.name, - } - ) - ); + }) + ); - const { result, cancel, cancel_editing } = yield race({ - result: call(uploadWorker, { - file, - temp_id, - target, - type, - }), - cancel: call(uploadCancelWorker, temp_id), - }); + const [result, cancel]: [ + Unwrap, + Unwrap + ] = yield race([ + call(uploadWorker, { + file, + temp_id, + target, + type, + }), + call(uploadCancelWorker, temp_id), + ]); - if (cancel || cancel_editing) { - if (onFail) onFail(); - return yield put(uploadDropStatus(temp_id)); - } + if (cancel || !result) { + if (onFail) onFail(); + return yield put(uploadDropStatus(temp_id)); + } - const { data, error }: { data: IFile & { detail: string }; error: string } = result; + yield put( + uploadSetStatus(temp_id, { + is_uploading: false, + error: '', + uuid: result.id, + url: result.full_path, + type, + thumbnail_url: result.full_path, + progress: 1, + name: file.name, + }) + ); - if (error) { + yield put(uploadAddFile(result)); + + if (onSuccess) onSuccess(result); + } catch (error) { if (onFail) onFail(); return yield put( uploadSetStatus(temp_id, { is_uploading: false, - error: data.detail || error, + error, type, }) ); } - - yield put( - uploadSetStatus(temp_id, { - is_uploading: false, - error: null, - uuid: data.id, - url: data.full_path, - type, - thumbnail_url: data.full_path, - progress: 1, - name: file.name, - }) - ); - - yield put(uploadAddFile(data)); - - if (onSuccess) onSuccess(data); - - return { error: null, status: HTTP_RESPONSES.CREATED, data: {} }; // add file here as data } function* uploadFiles({ files }: ReturnType) { diff --git a/src/redux/uploads/types.ts b/src/redux/uploads/types.ts new file mode 100644 index 00000000..410acb22 --- /dev/null +++ b/src/redux/uploads/types.ts @@ -0,0 +1,6 @@ +import { IFile, IFileWithUUID, IUploadProgressHandler } from '~/redux/types'; + +export type ApiUploadFileRequest = IFileWithUUID & { + onProgress: IUploadProgressHandler; +}; +export type ApiUploadFIleResult = IFile; diff --git a/src/utils/uploader.ts b/src/utils/uploader.ts index f161cd66..c1ad5941 100644 --- a/src/utils/uploader.ts +++ b/src/utils/uploader.ts @@ -1,7 +1,7 @@ import uuid from 'uuid4'; -import { eventChannel, END, EventChannel } from 'redux-saga'; +import { END, eventChannel, EventChannel } from 'redux-saga'; import { VALIDATORS } from '~/utils/validators'; -import { IResultWithStatus, IFile } from '~/redux/types'; +import { IFile, IResultWithStatus } from '~/redux/types'; import { HTTP_RESPONSES } from './api'; import { EMPTY_FILE, FILE_MIMES, UPLOAD_TYPES } from '~/redux/uploads/constants'; @@ -33,13 +33,11 @@ export function createUploader( export const uploadGetThumb = async file => { if (!file.type || !VALIDATORS.IS_IMAGE_MIME(file.type)) return ''; - const thumb = await new Promise(resolve => { + return new Promise(resolve => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result || ''); reader.readAsDataURL(file); }); - - return thumb; }; export const fakeUploader = ({ @@ -73,9 +71,6 @@ export const fakeUploader = ({ }); }; -export const getFileType = (file: File): keyof typeof UPLOAD_TYPES => { - return ( - (file.type && Object.keys(FILE_MIMES).find(mime => FILE_MIMES[mime].includes(file.type))) || - null - ); -}; +export const getFileType = (file: File): keyof typeof UPLOAD_TYPES | undefined => + (file.type && Object.keys(FILE_MIMES).find(mime => FILE_MIMES[mime].includes(file.type))) || + undefined;