mirror of
https://github.com/usatiuk/photos.git
synced 2025-10-28 23:37:48 +01:00
simple photo viewer
This commit is contained in:
@@ -23,6 +23,7 @@ import { Overview } from "~Photos/Overview";
|
|||||||
import { toggleDarkMode } from "~redux/localSettings/actions";
|
import { toggleDarkMode } from "~redux/localSettings/actions";
|
||||||
import { IAppState } from "~redux/reducers";
|
import { IAppState } from "~redux/reducers";
|
||||||
import { logoutUser } from "~redux/user/actions";
|
import { logoutUser } from "~redux/user/actions";
|
||||||
|
import { Photo } from "~Photos/Photo";
|
||||||
|
|
||||||
export interface IHomeProps extends RouteComponentProps {
|
export interface IHomeProps extends RouteComponentProps {
|
||||||
user: IUserJSON | null;
|
user: IUserJSON | null;
|
||||||
@@ -100,6 +101,10 @@ export class HomeComponent extends React.PureComponent<IHomeProps> {
|
|||||||
path="/account"
|
path="/account"
|
||||||
component={Account}
|
component={Account}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/photos/:id"
|
||||||
|
component={Photo}
|
||||||
|
/>
|
||||||
<Route path="/" component={Overview} />
|
<Route path="/" component={Overview} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</animated.div>
|
</animated.div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import "./Photos.scss";
|
import "./Overview.scss";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Dispatch } from "redux";
|
import { Dispatch } from "redux";
|
||||||
@@ -12,9 +12,10 @@ import { UploadButton } from "./UploadButton";
|
|||||||
|
|
||||||
export interface IOverviewComponentProps {
|
export interface IOverviewComponentProps {
|
||||||
photos: IPhotoReqJSON[] | null;
|
photos: IPhotoReqJSON[] | null;
|
||||||
fetching: boolean;
|
overviewLoaded: boolean;
|
||||||
fetchingError: string | null;
|
overviewFetching: boolean;
|
||||||
fetchingSpinner: boolean;
|
overviewFetchingError: string | null;
|
||||||
|
overviewFetchingSpinner: boolean;
|
||||||
|
|
||||||
fetchPhotos: () => void;
|
fetchPhotos: () => void;
|
||||||
}
|
}
|
||||||
@@ -22,11 +23,11 @@ export interface IOverviewComponentProps {
|
|||||||
export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps> = (
|
export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps> = (
|
||||||
props,
|
props,
|
||||||
) => {
|
) => {
|
||||||
if (!props.photos && !props.fetching) {
|
if (!props.overviewLoaded && !props.overviewFetching) {
|
||||||
props.fetchPhotos();
|
props.fetchPhotos();
|
||||||
}
|
}
|
||||||
if (!props.photos) {
|
if (!props.photos) {
|
||||||
return <LoadingStub spinner={props.fetchingSpinner} />;
|
return <LoadingStub spinner={props.overviewFetchingSpinner} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const photos = props.photos
|
const photos = props.photos
|
||||||
@@ -46,9 +47,10 @@ export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps>
|
|||||||
function mapStateToProps(state: IAppState) {
|
function mapStateToProps(state: IAppState) {
|
||||||
return {
|
return {
|
||||||
photos: state.photos.photos,
|
photos: state.photos.photos,
|
||||||
fetching: state.photos.fetching,
|
overviewLoaded: state.photos.overviewLoaded,
|
||||||
fetchingError: state.photos.fetchingError,
|
overviewFetching: state.photos.overviewFetching,
|
||||||
fetchingSpinner: state.photos.fetchingSpinner,
|
overviewFetchingError: state.photos.overviewFetchingError,
|
||||||
|
overviewFetchingSpinner: state.photos.overviewFetchingSpinner,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
frontend/src/Photos/Photo.scss
Normal file
17
frontend/src/Photos/Photo.scss
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
@import "~@blueprintjs/core/lib/scss/variables";
|
||||||
|
|
||||||
|
#photoView {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
#photo {
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-dark {
|
||||||
|
#photoView {}
|
||||||
|
}
|
||||||
79
frontend/src/Photos/Photo.tsx
Normal file
79
frontend/src/Photos/Photo.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import "./Photo.scss";
|
||||||
|
import * as React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { RouteComponentProps, withRouter } from "react-router";
|
||||||
|
import { Dispatch } from "redux";
|
||||||
|
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
||||||
|
import { LoadingStub } from "~LoadingStub";
|
||||||
|
import { getPhotoImgPath } from "~redux/api/photos";
|
||||||
|
import { photoLoadStart } from "~redux/photos/actions";
|
||||||
|
import { IPhotoState } from "~redux/photos/reducer";
|
||||||
|
import { IAppState } from "~redux/reducers";
|
||||||
|
|
||||||
|
export interface IPhotoComponentProps extends RouteComponentProps {
|
||||||
|
photo: IPhotoReqJSON | undefined;
|
||||||
|
photoState: IPhotoState | undefined;
|
||||||
|
|
||||||
|
fetchPhoto: (id: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getId(props: RouteComponentProps) {
|
||||||
|
return parseInt((props.match?.params as { id: string }).id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
||||||
|
props,
|
||||||
|
) => {
|
||||||
|
const id = getId(props);
|
||||||
|
|
||||||
|
if (!props.photo && !props.photoState?.fetching) {
|
||||||
|
console.log(props);
|
||||||
|
props.fetchPhoto(id);
|
||||||
|
}
|
||||||
|
if (!props.photo) {
|
||||||
|
return <LoadingStub spinner={false} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileExists = props.photo.uploaded;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{fileExists ? (
|
||||||
|
<div id="photoView">
|
||||||
|
<img
|
||||||
|
id="photo"
|
||||||
|
loading="lazy"
|
||||||
|
src={getPhotoImgPath(props.photo)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>Photo not uploaded yet</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function mapStateToProps(state: IAppState, props: RouteComponentProps) {
|
||||||
|
const id = getId(props);
|
||||||
|
let photo = undefined;
|
||||||
|
let photoState = undefined;
|
||||||
|
|
||||||
|
if (state.photos.photos) {
|
||||||
|
photo = state.photos.photos.find((p) => p.id === id);
|
||||||
|
}
|
||||||
|
if (state.photos.photoStates[id]) {
|
||||||
|
photoState = state.photos.photoStates[id];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
photo,
|
||||||
|
photoState,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapDispatchToProps(dispatch: Dispatch) {
|
||||||
|
return { fetchPhoto: (id: number) => dispatch(photoLoadStart(id)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Photo = withRouter(
|
||||||
|
connect(mapStateToProps, mapDispatchToProps)(PhotoComponent),
|
||||||
|
);
|
||||||
@@ -13,8 +13,9 @@ import { Dispatch } from "redux";
|
|||||||
import { photoDeleteCancel, photoDeleteStart } from "~redux/photos/actions";
|
import { photoDeleteCancel, photoDeleteStart } from "~redux/photos/actions";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { LoadingStub } from "~LoadingStub";
|
import { LoadingStub } from "~LoadingStub";
|
||||||
|
import { RouteComponentProps, withRouter } from "react-router";
|
||||||
|
|
||||||
export interface IPhotoCardComponentProps {
|
export interface IPhotoCardComponentProps extends RouteComponentProps {
|
||||||
photo: IPhotoReqJSON;
|
photo: IPhotoReqJSON;
|
||||||
|
|
||||||
deletePhoto: (photo: IPhotoReqJSON) => void;
|
deletePhoto: (photo: IPhotoReqJSON) => void;
|
||||||
@@ -47,11 +48,9 @@ export class PhotoCardComponent extends React.PureComponent<
|
|||||||
<Card
|
<Card
|
||||||
className="photoCard"
|
className="photoCard"
|
||||||
interactive={true}
|
interactive={true}
|
||||||
/*
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
this.props.history.push(`/docs/${this.props.doc.id}`)
|
this.props.history.push(`/photos/${this.props.photo.id}`)
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
>
|
>
|
||||||
{fileExists ? (
|
{fileExists ? (
|
||||||
<img
|
<img
|
||||||
@@ -91,4 +90,6 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PhotoCard = connect(null, mapDispatchToProps)(PhotoCardComponent);
|
export const PhotoCard = withRouter(
|
||||||
|
connect(null, mapDispatchToProps)(PhotoCardComponent),
|
||||||
|
);
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ const fetchPhotosFn = jest.fn();
|
|||||||
|
|
||||||
const overviewComponentDefaultProps: IOverviewComponentProps = {
|
const overviewComponentDefaultProps: IOverviewComponentProps = {
|
||||||
photos: null,
|
photos: null,
|
||||||
fetching: false,
|
overviewFetching: false,
|
||||||
fetchingError: null,
|
overviewFetchingError: null,
|
||||||
fetchingSpinner: false,
|
overviewFetchingSpinner: false,
|
||||||
|
|
||||||
fetchPhotos: fetchPhotosFn,
|
fetchPhotos: fetchPhotosFn,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
||||||
import {
|
import {
|
||||||
IPhotosByIDDeleteRespBody,
|
IPhotosByIDDeleteRespBody,
|
||||||
|
IPhotosByIDGetRespBody,
|
||||||
IPhotosListRespBody,
|
IPhotosListRespBody,
|
||||||
IPhotosNewRespBody,
|
IPhotosNewRespBody,
|
||||||
IPhotosUploadRespBody,
|
IPhotosUploadRespBody,
|
||||||
@@ -22,6 +23,10 @@ export async function fetchPhotosList(): Promise<IPhotosListRespBody> {
|
|||||||
return fetchJSONAuth("/photos/list", "GET");
|
return fetchJSONAuth("/photos/list", "GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchPhoto(id: number): Promise<IPhotosByIDGetRespBody> {
|
||||||
|
return fetchJSONAuth(`/photos/byID/${id}`, "GET");
|
||||||
|
}
|
||||||
|
|
||||||
export async function createPhoto(
|
export async function createPhoto(
|
||||||
hash: string,
|
hash: string,
|
||||||
size: string,
|
size: string,
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ export enum PhotoTypes {
|
|||||||
PHOTOS_LOAD_START = "PHOTOS_LOAD",
|
PHOTOS_LOAD_START = "PHOTOS_LOAD",
|
||||||
PHOTOS_LOAD_SUCCESS = "PHOTOS_LOAD_SUCCESS",
|
PHOTOS_LOAD_SUCCESS = "PHOTOS_LOAD_SUCCESS",
|
||||||
PHOTOS_LOAD_FAIL = "PHOTOS_LOAD_FAIL",
|
PHOTOS_LOAD_FAIL = "PHOTOS_LOAD_FAIL",
|
||||||
|
PHOTO_LOAD_START = "PHOTO_LOAD",
|
||||||
|
PHOTO_LOAD_SUCCESS = "PHOTO_LOAD_SUCCESS",
|
||||||
|
PHOTO_LOAD_FAIL = "PHOTO_LOAD_FAIL",
|
||||||
PHOTOS_UPLOAD_START = "PHOTOS_UPLOAD",
|
PHOTOS_UPLOAD_START = "PHOTOS_UPLOAD",
|
||||||
PHOTO_CREATE_SUCCESS = "PHOTO_CREATE_SUCCESS",
|
PHOTO_CREATE_SUCCESS = "PHOTO_CREATE_SUCCESS",
|
||||||
PHOTO_CREATE_FAIL = "PHOTO_CREATE_FAIL",
|
PHOTO_CREATE_FAIL = "PHOTO_CREATE_FAIL",
|
||||||
@@ -36,6 +39,22 @@ export interface IPhotosLoadFailAction extends Action {
|
|||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IPhotoLoadStartAction extends Action {
|
||||||
|
type: PhotoTypes.PHOTO_LOAD_START;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPhotoLoadSuccessAction extends Action {
|
||||||
|
type: PhotoTypes.PHOTO_LOAD_SUCCESS;
|
||||||
|
photo: IPhotoReqJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPhotoLoadFailAction extends Action {
|
||||||
|
type: PhotoTypes.PHOTO_LOAD_FAIL;
|
||||||
|
id: number;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IPhotosUploadStartAction extends Action {
|
export interface IPhotosUploadStartAction extends Action {
|
||||||
type: PhotoTypes.PHOTOS_UPLOAD_START;
|
type: PhotoTypes.PHOTOS_UPLOAD_START;
|
||||||
files: FileList;
|
files: FileList;
|
||||||
@@ -92,6 +111,10 @@ export function photosLoadStart(): IPhotosLoadStartAction {
|
|||||||
return { type: PhotoTypes.PHOTOS_LOAD_START };
|
return { type: PhotoTypes.PHOTOS_LOAD_START };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function photoLoadStart(id: number): IPhotoLoadStartAction {
|
||||||
|
return { type: PhotoTypes.PHOTO_LOAD_START, id };
|
||||||
|
}
|
||||||
|
|
||||||
export function photosUploadStart(files: FileList): IPhotosUploadStartAction {
|
export function photosUploadStart(files: FileList): IPhotosUploadStartAction {
|
||||||
return { type: PhotoTypes.PHOTOS_UPLOAD_START, files };
|
return { type: PhotoTypes.PHOTOS_UPLOAD_START, files };
|
||||||
}
|
}
|
||||||
@@ -143,6 +166,16 @@ export function photosLoadFail(error: string): IPhotosLoadFailAction {
|
|||||||
return { type: PhotoTypes.PHOTOS_LOAD_FAIL, error };
|
return { type: PhotoTypes.PHOTOS_LOAD_FAIL, error };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function photoLoadSuccess(
|
||||||
|
photo: IPhotoReqJSON,
|
||||||
|
): IPhotoLoadSuccessAction {
|
||||||
|
return { type: PhotoTypes.PHOTO_LOAD_SUCCESS, photo };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function photoLoadFail(id:number,error: string): IPhotoLoadFailAction {
|
||||||
|
return { type: PhotoTypes.PHOTO_LOAD_FAIL,id, error };
|
||||||
|
}
|
||||||
|
|
||||||
export function photoDeleteStart(
|
export function photoDeleteStart(
|
||||||
photo: IPhotoReqJSON,
|
photo: IPhotoReqJSON,
|
||||||
): IPhotoDeleteStartAction {
|
): IPhotoDeleteStartAction {
|
||||||
@@ -183,4 +216,7 @@ export type PhotoAction =
|
|||||||
| IPhotoDeleteFailAction
|
| IPhotoDeleteFailAction
|
||||||
| IPhotoDeleteStartAction
|
| IPhotoDeleteStartAction
|
||||||
| IPhotoDeleteSuccessAction
|
| IPhotoDeleteSuccessAction
|
||||||
| IPhotoDeleteCancelAction;
|
| IPhotoDeleteCancelAction
|
||||||
|
| IPhotoLoadFailAction
|
||||||
|
| IPhotoLoadStartAction
|
||||||
|
| IPhotoLoadSuccessAction;
|
||||||
|
|||||||
@@ -3,20 +3,32 @@ import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
|||||||
import { UserAction, UserTypes } from "~redux/user/actions";
|
import { UserAction, UserTypes } from "~redux/user/actions";
|
||||||
import { PhotoAction, PhotoTypes } from "./actions";
|
import { PhotoAction, PhotoTypes } from "./actions";
|
||||||
|
|
||||||
export interface IPhotosState {
|
export interface IPhotoState {
|
||||||
photos: IPhotoReqJSON[] | null;
|
|
||||||
fetching: boolean;
|
fetching: boolean;
|
||||||
fetchingError: string | null;
|
fetchingError: string | null;
|
||||||
fetchingSpinner: boolean;
|
}
|
||||||
|
|
||||||
|
export interface IPhotosState {
|
||||||
|
photos: IPhotoReqJSON[] | null;
|
||||||
|
|
||||||
|
photoStates: Record<number, IPhotoState>;
|
||||||
|
|
||||||
|
overviewFetching: boolean;
|
||||||
|
overviewLoaded: boolean;
|
||||||
|
overviewFetchingError: string | null;
|
||||||
|
overviewFetchingSpinner: boolean;
|
||||||
|
|
||||||
deleteCache: Record<number, IPhotoReqJSON>;
|
deleteCache: Record<number, IPhotoReqJSON>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPhotosState: IPhotosState = {
|
const defaultPhotosState: IPhotosState = {
|
||||||
photos: null,
|
photos: null,
|
||||||
fetching: false,
|
overviewLoaded: false,
|
||||||
fetchingError: null,
|
overviewFetching: false,
|
||||||
fetchingSpinner: false,
|
overviewFetchingError: null,
|
||||||
|
overviewFetchingSpinner: false,
|
||||||
|
|
||||||
|
photoStates: {},
|
||||||
|
|
||||||
deleteCache: {},
|
deleteCache: {},
|
||||||
};
|
};
|
||||||
@@ -31,15 +43,67 @@ export const photosReducer: Reducer<IPhotosState, PhotoAction> = (
|
|||||||
case PhotoTypes.PHOTOS_LOAD_START:
|
case PhotoTypes.PHOTOS_LOAD_START:
|
||||||
return {
|
return {
|
||||||
...defaultPhotosState,
|
...defaultPhotosState,
|
||||||
fetching: true,
|
overviewFetching: true,
|
||||||
fetchingSpinner: false,
|
overviewFetchingSpinner: false,
|
||||||
};
|
};
|
||||||
case PhotoTypes.PHOTOS_START_FETCHING_SPINNER:
|
case PhotoTypes.PHOTOS_START_FETCHING_SPINNER:
|
||||||
return { ...state, fetchingSpinner: true };
|
return { ...state, overviewFetchingSpinner: true };
|
||||||
case PhotoTypes.PHOTOS_LOAD_SUCCESS:
|
case PhotoTypes.PHOTOS_LOAD_SUCCESS:
|
||||||
return { ...defaultPhotosState, photos: action.photos };
|
return {
|
||||||
|
...defaultPhotosState,
|
||||||
|
photos: action.photos,
|
||||||
|
overviewLoaded: true,
|
||||||
|
};
|
||||||
case PhotoTypes.PHOTOS_LOAD_FAIL:
|
case PhotoTypes.PHOTOS_LOAD_FAIL:
|
||||||
return { ...defaultPhotosState, fetchingError: action.error };
|
return {
|
||||||
|
...defaultPhotosState,
|
||||||
|
overviewFetchingError: action.error,
|
||||||
|
};
|
||||||
|
|
||||||
|
case PhotoTypes.PHOTO_LOAD_START: {
|
||||||
|
const { photoStates } = state;
|
||||||
|
photoStates[action.id] = {
|
||||||
|
fetching: true,
|
||||||
|
fetchingError: null,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
photoStates,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case PhotoTypes.PHOTO_LOAD_SUCCESS: {
|
||||||
|
const { photoStates } = state;
|
||||||
|
photoStates[action.photo.id] = {
|
||||||
|
fetching: false,
|
||||||
|
fetchingError: null,
|
||||||
|
};
|
||||||
|
if (state.photos) {
|
||||||
|
const photos = state.photos;
|
||||||
|
const photosNoDup = photos.filter(
|
||||||
|
(p) => p.id !== action.photo.id,
|
||||||
|
);
|
||||||
|
const updPhotos = [action.photo, ...photosNoDup];
|
||||||
|
return { ...state, photos: updPhotos, photoStates };
|
||||||
|
} else {
|
||||||
|
const photos = [action.photo];
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
photos,
|
||||||
|
photoStates,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PhotoTypes.PHOTO_LOAD_FAIL: {
|
||||||
|
const { photoStates } = state;
|
||||||
|
photoStates[action.id] = {
|
||||||
|
fetching: false,
|
||||||
|
fetchingError: action.error,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
photoStates,
|
||||||
|
};
|
||||||
|
}
|
||||||
case PhotoTypes.PHOTO_CREATE_SUCCESS:
|
case PhotoTypes.PHOTO_CREATE_SUCCESS:
|
||||||
if (state.photos) {
|
if (state.photos) {
|
||||||
const photos = state.photos;
|
const photos = state.photos;
|
||||||
|
|||||||
@@ -14,16 +14,20 @@ import * as SparkMD5 from "spark-md5";
|
|||||||
import {
|
import {
|
||||||
createPhoto,
|
createPhoto,
|
||||||
deletePhoto,
|
deletePhoto,
|
||||||
|
fetchPhoto,
|
||||||
fetchPhotosList,
|
fetchPhotosList,
|
||||||
uploadPhoto,
|
uploadPhoto,
|
||||||
} from "~redux/api/photos";
|
} from "~redux/api/photos";
|
||||||
import {
|
import {
|
||||||
IPhotoDeleteStartAction,
|
IPhotoDeleteStartAction,
|
||||||
|
IPhotoLoadStartAction,
|
||||||
IPhotosUploadStartAction,
|
IPhotosUploadStartAction,
|
||||||
photoCreateFail,
|
photoCreateFail,
|
||||||
photoCreateSuccess,
|
photoCreateSuccess,
|
||||||
photoDeleteFail,
|
photoDeleteFail,
|
||||||
photoDeleteSuccess,
|
photoDeleteSuccess,
|
||||||
|
photoLoadFail,
|
||||||
|
photoLoadSuccess,
|
||||||
photosLoadFail,
|
photosLoadFail,
|
||||||
photosLoadSuccess,
|
photosLoadSuccess,
|
||||||
photosStartFetchingSpinner,
|
photosStartFetchingSpinner,
|
||||||
@@ -130,6 +134,32 @@ function* photosLoad() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* photoLoad(action: IPhotoLoadStartAction) {
|
||||||
|
try {
|
||||||
|
//const spinner = yield fork(startSpinner);
|
||||||
|
|
||||||
|
const { response, timeout } = yield race({
|
||||||
|
response: call(fetchPhoto, action.id),
|
||||||
|
timeout: delay(10000),
|
||||||
|
});
|
||||||
|
|
||||||
|
//yield cancel(spinner);
|
||||||
|
|
||||||
|
if (timeout) {
|
||||||
|
yield put(photoLoadFail(action.id, "Timeout"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.data) {
|
||||||
|
const photo = response.data;
|
||||||
|
yield put(photoLoadSuccess(photo));
|
||||||
|
} else {
|
||||||
|
yield put(photoLoadFail(action.id, response.error));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
yield put(photoLoadFail(action.id, "Internal error"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function* photoUpload(f: File) {
|
function* photoUpload(f: File) {
|
||||||
try {
|
try {
|
||||||
const hash = yield call(computeChecksumMd5, f);
|
const hash = yield call(computeChecksumMd5, f);
|
||||||
@@ -218,6 +248,7 @@ export function* photosSaga() {
|
|||||||
yield all([
|
yield all([
|
||||||
takeLatest(PhotoTypes.PHOTOS_LOAD_START, photosLoad),
|
takeLatest(PhotoTypes.PHOTOS_LOAD_START, photosLoad),
|
||||||
takeLatest(PhotoTypes.PHOTOS_UPLOAD_START, photosUpload),
|
takeLatest(PhotoTypes.PHOTOS_UPLOAD_START, photosUpload),
|
||||||
|
takeLatest(PhotoTypes.PHOTO_LOAD_START, photoLoad),
|
||||||
takeEvery(PhotoTypes.PHOTO_DELETE_START, photoDelete),
|
takeEvery(PhotoTypes.PHOTO_DELETE_START, photoDelete),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user