diff --git a/src/containers/dialogs/EditorDialog/index.tsx b/src/containers/dialogs/EditorDialog/index.tsx index 0edbbae3..71f2117a 100644 --- a/src/containers/dialogs/EditorDialog/index.tsx +++ b/src/containers/dialogs/EditorDialog/index.tsx @@ -1,5 +1,5 @@ import React, { - FC, useState, useCallback, useEffect + FC, useState, useCallback, useEffect, } from 'react'; import { connect } from 'react-redux'; import assocPath from 'ramda/es/assocPath'; @@ -20,6 +20,7 @@ import { moveArrItem } from '~/utils/fn'; import { IFile, IFileWithUUID } from '~/redux/types'; import * as UPLOAD_ACTIONS from '~/redux/uploads/actions'; import { selectUploads } from '~/redux/uploads/selectors'; +import { UPLOAD_TARGETS, UPLOAD_TYPES, UPLOAD_SUBJECTS } from '~/redux/uploads/constants'; const mapStateToProps = (state) => { const { editor } = selectNode(state); @@ -35,19 +36,29 @@ const mapDispatchToProps = { type IProps = IDialogProps & ReturnType & typeof mapDispatchToProps & {}; const EditorDialogUnconnected: FC = ({ - onRequestClose, editor, uploadUploadFiles, files, statuses + onRequestClose, + editor, + uploadUploadFiles, + files, + statuses, }) => { const [data, setData] = useState(editor); const eventPreventer = useCallback(event => event.preventDefault(), []); const [temp, setTemp] = useState([]); - const onFileMove = useCallback((old_index: number, new_index: number) => { - setData(assocPath(['files'], moveArrItem(old_index, new_index, data.files), data)); - }, [data, setData]); + const onFileMove = useCallback( + (old_index: number, new_index: number) => { + setData(assocPath(['files'], moveArrItem(old_index, new_index, data.files), data)); + }, + [data, setData] + ); - const onFileAdd = useCallback((file: IFile) => { - setData(assocPath(['files'], append(file, data.files), data)); - }, [data, setData]); + const onFileAdd = useCallback( + (file: IFile) => { + setData(assocPath(['files'], append(file, data.files), data)); + }, + [data, setData] + ); const onDrop = useCallback( (event: React.DragEvent) => { @@ -59,7 +70,9 @@ const EditorDialogUnconnected: FC = ({ (file: File): IFileWithUUID => ({ file, temp_id: uuid(), - subject: 'editor' + subject: UPLOAD_SUBJECTS.EDITOR, + target: UPLOAD_TARGETS.NODES, + type: UPLOAD_TYPES.IMAGE, }) ); @@ -81,7 +94,9 @@ const EditorDialogUnconnected: FC = ({ (file: File): IFileWithUUID => ({ file, temp_id: uuid(), - subject: 'editor' + subject: UPLOAD_SUBJECTS.EDITOR, + target: UPLOAD_TARGETS.NODES, + type: UPLOAD_TYPES.IMAGE, }) ); @@ -124,11 +139,7 @@ const EditorDialogUnconnected: FC = ({ const buttons = ( - + diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts index 83149cfd..ff5d751c 100644 --- a/src/redux/auth/sagas.ts +++ b/src/redux/auth/sagas.ts @@ -1,5 +1,5 @@ import { - call, put, takeLatest, select + call, put, takeLatest, select, } from 'redux-saga/effects'; import { SagaIterator } from 'redux-saga'; import { push } from 'connected-react-router'; @@ -15,10 +15,14 @@ import { IResultWithStatus } from '../types'; import { IUser } from './types'; export function* reqWrapper(requestAction, props = {}): ReturnType { - const { access } = yield select(selectToken); + const access = yield select(selectToken); + + console.log('firing reqWrapper'); const result = yield call(requestAction, { access, ...props }); + console.log('at reqWrapper', { result }); + if (result && result.status === 401) { yield put(push(URLS.BASE)); yield put(modalShowDialog(DIALOGS.LOGIN)); @@ -26,15 +30,29 @@ export function* reqWrapper(requestAction, props = {}): ReturnType) { +function* sendLoginRequestSaga({ + username, + password, +}: ReturnType) { if (!username || !password) return; - const { error, data: { token, user } }: IResultWithStatus<{ token: string; user: IUser }> = yield call(apiUserLogin, { username, password }); + const { + error, + data: { token, user }, + }: IResultWithStatus<{ token: string; user: IUser }> = yield call(apiUserLogin, { + username, + password, + }); - if (error) { yield put(userSetLoginError(error)); return; } + if (error) { + yield put(userSetLoginError(error)); + return; + } yield put(authSetToken(token)); yield put(authSetUser({ ...user, is_user: true })); diff --git a/src/redux/types.ts b/src/redux/types.ts index 839a5156..e9589ea5 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -7,8 +7,8 @@ export interface ITag { } export type IInputTextProps = DetailedHTMLProps< -InputHTMLAttributes, -HTMLInputElement + InputHTMLAttributes, + HTMLInputElement > & { wrapperClassName?: string; handler?: (value: string) => void; @@ -46,6 +46,8 @@ export interface IResultWithStatus { export type UUID = string; +export type IUploadType = 'image' | 'text' | 'audio' | 'video' | 'other'; + export interface IFile { id?: UUID; temp_id?: UUID; @@ -58,7 +60,7 @@ export interface IFile { url: string; size: number; - type: 'image' | 'text' | 'audio' | 'video'; + type: IUploadType; mime: string; createdAt?: string; @@ -68,6 +70,7 @@ export interface IFile { export interface IFileWithUUID { temp_id?: UUID; file: File; + subject?: string; target: string; type: string; } diff --git a/src/redux/uploads/constants.ts b/src/redux/uploads/constants.ts index 2cea0fcc..9bebc9bf 100644 --- a/src/redux/uploads/constants.ts +++ b/src/redux/uploads/constants.ts @@ -1,4 +1,4 @@ -import { IFile } from '~/redux/types'; +import { IFile, IUploadType } from '~/redux/types'; import { IUploadState, IUploadStatus } from './reducer'; const prefix = 'UPLOAD.'; @@ -11,7 +11,7 @@ export const UPLOAD_ACTIONS = { DROP_STATUS: `${prefix}DROP_STATUS`, SET_STATUS: `${prefix}SET_STATUS`, - ADD_FILE: `${prefix}ADD_FILE` + ADD_FILE: `${prefix}ADD_FILE`, }; export const EMPTY_FILE: IFile = { @@ -25,7 +25,7 @@ export const EMPTY_FILE: IFile = { url: 'https://cdn.arstechnica.net/wp-content/uploads/2017/09/mario-collage-800x450.jpg', size: 2400000, type: 'image', - mime: 'image/jpeg' + mime: 'image/jpeg', }; export const EMPTY_UPLOAD_STATUS: IUploadStatus = { @@ -37,5 +37,26 @@ export const EMPTY_UPLOAD_STATUS: IUploadStatus = { progress: 0, thumbnail_url: null, type: null, - temp_id: null + temp_id: null, +}; + +// for targeted cancellation +export const UPLOAD_SUBJECTS = { + EDITOR: 'editor', + COMMENT: 'comment', + AVATAR: 'avatar', +}; + +export const UPLOAD_TARGETS = { + NODES: 'nodes', + COMMENTS: 'comments', + PROFILES: 'profiles', + OTHER: 'other', +}; + +export const UPLOAD_TYPES: Record = { + IMAGE: 'image', + AUDIO: 'audio', + VIDEO: 'video', + OTHER: 'other', }; diff --git a/src/redux/uploads/sagas.ts b/src/redux/uploads/sagas.ts index 0d4a900e..ab64576c 100644 --- a/src/redux/uploads/sagas.ts +++ b/src/redux/uploads/sagas.ts @@ -1,28 +1,35 @@ -import { takeEvery, all, spawn, call, put, take, fork, race } from 'redux-saga/effects'; +import { + takeEvery, all, spawn, call, put, take, fork, race, +} from 'redux-saga/effects'; import { postUploadFile } from './api'; import { UPLOAD_ACTIONS } from '~/redux/uploads/constants'; import { - uploadUploadFiles, uploadSetStatus, uploadAddStatus, uploadDropStatus, uploadAddFile + uploadUploadFiles, + uploadSetStatus, + uploadAddStatus, + uploadDropStatus, + uploadAddFile, } from './actions'; import { reqWrapper } from '../auth/sagas'; -import { createUploader, uploadGetThumb, fakeUploader } from '~/utils/uploader'; +import { createUploader, uploadGetThumb } from '~/utils/uploader'; import { HTTP_RESPONSES } from '~/utils/api'; import { VALIDATORS } from '~/utils/validators'; -import { UUID, IFileWithUUID, IFile, IUploadProgressHandler } from '../types'; +import { IFileWithUUID, IFile, IUploadProgressHandler } from '../types'; -function* uploadCall({ file, temp_id, target, type, onProgress }: IFileWithUUID & { onProgress: IUploadProgressHandler }) { - // return yield call(reqWrapper, fakeUploader, { file: { url: 'some', error: 'cant do this boss' }, onProgress, mustSucceed: true }); - return yield call( - reqWrapper, - postUploadFile, - { - file, - temp_id, - type, - target, - onProgress, - } - ); +function* uploadCall({ + file, + temp_id, + target, + type, + onProgress, +}: IFileWithUUID & { onProgress: IUploadProgressHandler }) { + return yield call(reqWrapper, postUploadFile, { + file, + temp_id, + type, + target, + onProgress, + }); } function* onUploadProgress(chan) { @@ -42,19 +49,27 @@ function* uploadCancelWorker(id) { return true; } -function* uploadWorker({ - file, temp_id, target, type, +function* uploadWorker({ + file, temp_id, target, type, }: IFileWithUUID) { - const [promise, chan] = createUploader, Partial>(uploadCall, { temp_id, target, type }); - - yield fork(onUploadProgress, chan); + const [promise, chan] = createUploader, Partial>( + uploadCall, + { temp_id, target, type } + ); + + fork(onUploadProgress, chan); return yield call(promise, { - temp_id, file, target, type, + temp_id, + file, + target, + type, }); } -function* uploadFile({ file, temp_id, type, target }: IFileWithUUID) { +function* uploadFile({ + file, temp_id, type, target, subject, +}: IFileWithUUID) { if (!file.type || !VALIDATORS.IS_IMAGE_MIME(file.type)) { return { error: 'File_Not_Image', status: HTTP_RESPONSES.BAD_REQUEST, data: {} }; } @@ -74,10 +89,13 @@ function* uploadFile({ file, temp_id, type, target }: IFileWithUUID) { ) ); - const { - result, cancel, cancel_editing, save_inventory, - } = yield race({ - result: call(uploadWorker, { file, temp_id, target, type }), + const { result, cancel, cancel_editing } = yield race({ + result: call(uploadWorker, { + file, + temp_id, + target, + type, + }), cancel: call(uploadCancelWorker, temp_id), // subject_cancel: call(uploadSubjectCancelWorker, subject) // add here CANCEL_UPLOADS worker, that will watch for subject @@ -85,7 +103,7 @@ function* uploadFile({ file, temp_id, type, target }: IFileWithUUID) { // save_inventory: take(INVENTORY_ACTIONS.SAVE_INVENTORY), }) as any; - if (cancel || cancel_editing || save_inventory) { + if (cancel || cancel_editing) { return yield put(uploadDropStatus(temp_id)); } @@ -97,8 +115,6 @@ function* uploadFile({ file, temp_id, type, target }: IFileWithUUID) { ); } - console.log('upload', data); - yield put( uploadSetStatus(temp_id, { is_uploading: false,