mirror of
https://github.com/usatiuk/photos.git
synced 2025-10-28 23:37:48 +01:00
show photo in an overlay
This commit is contained in:
@@ -24,6 +24,7 @@ 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";
|
import { Photo } from "~Photos/Photo";
|
||||||
|
import { PhotoRoute } from "~Photos/PhotoRoute";
|
||||||
|
|
||||||
export interface IHomeProps extends RouteComponentProps {
|
export interface IHomeProps extends RouteComponentProps {
|
||||||
user: IUserJSON | null;
|
user: IUserJSON | null;
|
||||||
@@ -103,7 +104,7 @@ export class HomeComponent extends React.PureComponent<IHomeProps> {
|
|||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/photos/:id"
|
path="/photos/:id"
|
||||||
component={Photo}
|
component={PhotoRoute}
|
||||||
/>
|
/>
|
||||||
<Route path="/" component={Overview} />
|
<Route path="/" component={Overview} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
@import "~@blueprintjs/core/lib/scss/variables";
|
@import "~@blueprintjs/core/lib/scss/variables";
|
||||||
|
|
||||||
|
#photoOverlayContainer {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-overlay-enter {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-overlay-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.bp3-overlay-exit {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-overlay-exit-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#overview {
|
#overview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import { photosLoadStart } from "~redux/photos/actions";
|
|||||||
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
||||||
import { LoadingStub } from "~LoadingStub";
|
import { LoadingStub } from "~LoadingStub";
|
||||||
import { PhotoCard } from "./PhotoCard";
|
import { PhotoCard } from "./PhotoCard";
|
||||||
import { Button } from "@blueprintjs/core";
|
import { Button, Classes, Overlay } from "@blueprintjs/core";
|
||||||
import { UploadButton } from "./UploadButton";
|
import { UploadButton } from "./UploadButton";
|
||||||
|
import { Photo } from "./Photo";
|
||||||
|
|
||||||
export interface IOverviewComponentProps {
|
export interface IOverviewComponentProps {
|
||||||
photos: IPhotoReqJSON[] | null;
|
photos: IPhotoReqJSON[] | null;
|
||||||
@@ -23,6 +24,9 @@ export interface IOverviewComponentProps {
|
|||||||
export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps> = (
|
export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps> = (
|
||||||
props,
|
props,
|
||||||
) => {
|
) => {
|
||||||
|
const [selectedPhoto, setSelectedPhoto] = React.useState<number>(0);
|
||||||
|
const [isOverlayOpened, setOverlayOpen] = React.useState<boolean>(false);
|
||||||
|
|
||||||
if (!props.overviewLoaded && !props.overviewFetching) {
|
if (!props.overviewLoaded && !props.overviewFetching) {
|
||||||
props.fetchPhotos();
|
props.fetchPhotos();
|
||||||
}
|
}
|
||||||
@@ -30,17 +34,44 @@ export const OverviewComponent: React.FunctionComponent<IOverviewComponentProps>
|
|||||||
return <LoadingStub spinner={props.overviewFetchingSpinner} />;
|
return <LoadingStub spinner={props.overviewFetchingSpinner} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onCardClick = (id: number) => {
|
||||||
|
setSelectedPhoto(id);
|
||||||
|
setOverlayOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
const photos = props.photos
|
const photos = props.photos
|
||||||
.sort((a, b) => b.shotAt - a.shotAt)
|
.sort((a, b) => b.shotAt - a.shotAt)
|
||||||
.map((photo) => <PhotoCard key={photo.id} photo={photo} />);
|
.map((photo) => (
|
||||||
|
<PhotoCard
|
||||||
|
key={photo.id}
|
||||||
|
photo={photo}
|
||||||
|
onClick={() => onCardClick(photo.id)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="overview">
|
<>
|
||||||
<div id="actionbar">
|
<Overlay
|
||||||
<UploadButton />
|
autoFocus
|
||||||
|
enforceFocus
|
||||||
|
usePortal
|
||||||
|
isOpen={isOverlayOpened}
|
||||||
|
onClose={() => {
|
||||||
|
setOverlayOpen(false);
|
||||||
|
}}
|
||||||
|
lazy
|
||||||
|
>
|
||||||
|
<div id="photoOverlayContainer">
|
||||||
|
<Photo id={selectedPhoto} />
|
||||||
|
</div>
|
||||||
|
</Overlay>
|
||||||
|
<div id="overview">
|
||||||
|
<div id="actionbar">
|
||||||
|
<UploadButton />
|
||||||
|
</div>
|
||||||
|
<div className="list">{photos}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="list">{photos}</div>
|
</>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import "./Photo.scss";
|
import "./Photo.scss";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { RouteComponentProps, withRouter } from "react-router";
|
|
||||||
import { Dispatch } from "redux";
|
import { Dispatch } from "redux";
|
||||||
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
import { IPhotoReqJSON } from "~../../src/entity/Photo";
|
||||||
import { LoadingStub } from "~LoadingStub";
|
import { LoadingStub } from "~LoadingStub";
|
||||||
@@ -10,25 +9,19 @@ import { photoLoadStart } from "~redux/photos/actions";
|
|||||||
import { IPhotoState } from "~redux/photos/reducer";
|
import { IPhotoState } from "~redux/photos/reducer";
|
||||||
import { IAppState } from "~redux/reducers";
|
import { IAppState } from "~redux/reducers";
|
||||||
|
|
||||||
export interface IPhotoComponentProps extends RouteComponentProps {
|
export interface IPhotoComponentProps {
|
||||||
|
id: number;
|
||||||
photo: IPhotoReqJSON | undefined;
|
photo: IPhotoReqJSON | undefined;
|
||||||
photoState: IPhotoState | undefined;
|
photoState: IPhotoState | undefined;
|
||||||
|
|
||||||
fetchPhoto: (id: number) => void;
|
fetchPhoto: (id: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getId(props: RouteComponentProps) {
|
|
||||||
return parseInt((props.match?.params as { id: string }).id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
||||||
props,
|
props,
|
||||||
) => {
|
) => {
|
||||||
const id = getId(props);
|
|
||||||
|
|
||||||
if (!props.photo && !props.photoState?.fetching) {
|
if (!props.photo && !props.photoState?.fetching) {
|
||||||
console.log(props);
|
props.fetchPhoto(props.id);
|
||||||
props.fetchPhoto(id);
|
|
||||||
}
|
}
|
||||||
if (!props.photo) {
|
if (!props.photo) {
|
||||||
return <LoadingStub spinner={false} />;
|
return <LoadingStub spinner={false} />;
|
||||||
@@ -53,8 +46,8 @@ export const PhotoComponent: React.FunctionComponent<IPhotoComponentProps> = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state: IAppState, props: RouteComponentProps) {
|
function mapStateToProps(state: IAppState, props: IPhotoComponentProps) {
|
||||||
const id = getId(props);
|
const { id } = props;
|
||||||
let photo = undefined;
|
let photo = undefined;
|
||||||
let photoState = undefined;
|
let photoState = undefined;
|
||||||
|
|
||||||
@@ -74,6 +67,8 @@ function mapDispatchToProps(dispatch: Dispatch) {
|
|||||||
return { fetchPhoto: (id: number) => dispatch(photoLoadStart(id)) };
|
return { fetchPhoto: (id: number) => dispatch(photoLoadStart(id)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Photo = withRouter(
|
// Because https://github.com/DefinitelyTyped/DefinitelyTyped/issues/16990
|
||||||
connect(mapStateToProps, mapDispatchToProps)(PhotoComponent),
|
export const Photo = connect(
|
||||||
);
|
mapStateToProps,
|
||||||
|
mapDispatchToProps,
|
||||||
|
)(PhotoComponent) as any;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface IPhotoCardComponentProps extends RouteComponentProps {
|
|||||||
|
|
||||||
deletePhoto: (photo: IPhotoReqJSON) => void;
|
deletePhoto: (photo: IPhotoReqJSON) => void;
|
||||||
cancelDelete: (photo: IPhotoReqJSON) => void;
|
cancelDelete: (photo: IPhotoReqJSON) => void;
|
||||||
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ContextMenuTarget
|
@ContextMenuTarget
|
||||||
@@ -48,9 +49,7 @@ export class PhotoCardComponent extends React.PureComponent<
|
|||||||
<Card
|
<Card
|
||||||
className="photoCard"
|
className="photoCard"
|
||||||
interactive={true}
|
interactive={true}
|
||||||
onClick={() =>
|
onClick={() => this.props.onClick()}
|
||||||
this.props.history.push(`/photos/${this.props.photo.id}`)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{fileExists ? (
|
{fileExists ? (
|
||||||
<img
|
<img
|
||||||
|
|||||||
17
frontend/src/Photos/PhotoRoute.tsx
Normal file
17
frontend/src/Photos/PhotoRoute.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { RouteComponentProps, withRouter } from "react-router";
|
||||||
|
import { Photo } from "./Photo";
|
||||||
|
|
||||||
|
function getId(props: RouteComponentProps) {
|
||||||
|
return parseInt((props.match?.params as { id: string }).id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PhotoRouteComponent: React.FunctionComponent<RouteComponentProps> = (
|
||||||
|
props: RouteComponentProps,
|
||||||
|
) => {
|
||||||
|
const id = getId(props);
|
||||||
|
|
||||||
|
return <Photo id={id} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PhotoRoute = withRouter(PhotoRouteComponent);
|
||||||
Reference in New Issue
Block a user