Skip to content

Commit

Permalink
feat: show media on light-box (#33)
Browse files Browse the repository at this point in the history
* feat(web): show media on lightbox

* feat(web): move to next/prev item
  • Loading branch information
KazuyaHara authored Nov 19, 2022
1 parent a1b8956 commit 20f3be4
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 30 deletions.
8 changes: 8 additions & 0 deletions web/src/adapters/stores/item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable import/prefer-default-export */
import create from 'zustand';

import { ItemWithURL } from '../../../domains/item';

type ItemState = { items: ItemWithURL[] };

export const useItemStore = create<ItemState>(() => ({ items: [] }));
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';

import {
ArrowBack,
ChevronLeft,
ChevronRight,
CloudDownload,
DeleteForever,
} from '@mui/icons-material';
import { AppBar, Box, Button, CircularProgress, Dialog, IconButton, Toolbar } from '@mui/material';

import { ItemWithURL } from '../../../../../domains/item';

type Props = {
item: ItemWithURL;
loading: boolean;
onClose: () => void;
onDelete: () => void;
onDownload: () => void;
onNavigateNext?: () => void;
onNavigatePrev?: () => void;
};

export default function Lightbox({
item,
loading,
onClose,
onDelete,
onDownload,
onNavigateNext,
onNavigatePrev,
}: Props) {
return (
<Dialog
fullWidth
maxWidth={false}
onClose={onClose}
open
PaperProps={{ sx: { bgcolor: 'black', height: '100%' } }}
>
<Box alignItems="center" bgcolor="black" display="flex" height="100%" justifyContent="center">
<AppBar color="transparent" elevation={0} position="absolute">
<Toolbar>
<Box flexGrow={1}>
<Button
onClick={onClose}
size="large"
startIcon={<ArrowBack />}
sx={{ color: 'white' }}
>
戻る
</Button>
</Box>
<Box>
<IconButton onClick={onDownload} sx={{ color: 'white' }}>
{loading ? <CircularProgress color="inherit" size={20} /> : <CloudDownload />}
</IconButton>
<IconButton disabled={loading} onClick={onDelete} sx={{ color: 'white' }}>
<DeleteForever />
</IconButton>
</Box>
</Toolbar>
</AppBar>
<Box
alignItems="center"
display="flex"
height="100%"
justifyContent="space-between"
px={3}
width="100%"
>
<IconButton
disabled={loading || !onNavigateNext}
onClick={onNavigateNext}
sx={{ color: 'white' }}
>
<ChevronLeft fontSize="large" />
</IconButton>
<Box component="img" maxHeight="100%" src={item.url} />
<IconButton
disabled={loading || !onNavigatePrev}
onClick={onNavigatePrev}
sx={{ color: 'white' }}
>
<ChevronRight fontSize="large" />
</IconButton>
</Box>
</Box>
</Dialog>
);
}
48 changes: 23 additions & 25 deletions web/src/adapters/userInterface/components/pages/item/get/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import React, { useEffect, useState } from 'react';

import { CloudDownload } from '@mui/icons-material';
import { LoadingButton } from '@mui/lab';
import { Box, Button } from '@mui/material';
import { saveAs } from 'file-saver';
import { Navigate, useNavigate, useParams } from 'react-router-dom';

Expand All @@ -12,10 +9,13 @@ import { ItemWithURL } from '../../../../../../domains/item';
import itemRepository from '../../../../../repositories/item';
import mediumRepository from '../../../../../repositories/medium';
import { useAlertStore } from '../../../../../stores/alert';
import { useItemStore } from '../../../../../stores/item';
import Dialog from '../../../molecules/dialog/item/delete';
import Lightbox from '../../../organisms/lightbox';
import Loading from '../../loading';

export default function ItemGet() {
const { items } = useItemStore();
const { get: getItem, softDelete: softDeleteItem } = useItemUseCase(
itemRepository(),
mediumRepository()
Expand All @@ -25,16 +25,25 @@ export default function ItemGet() {
const { id } = useParams<{ id: string }>();
const [item, setItem] = useState<ItemWithURL | null>();
const [loading, setLoading] = useState(false);
const [nextId, setNextId] = useState<string>();
const [openDialog, setOpenDialog] = useState(false);
const [prevId, setPrevId] = useState<string>();

useEffect(() => {
if (id) {
getItem(id).then(setItem);
const index = items.findIndex((element) => element.id === id);
setNextId(items[index - 1]?.id);
setPrevId(items[index + 1]?.id);
} else {
navigate('/media');
}
}, [id]);

const navigateList = () => navigate('/media');
const navigateNext = nextId ? () => navigate(`/media/${nextId}`) : undefined;
const navigatePrev = prevId ? () => navigate(`/media/${prevId}`) : undefined;

const onDelete = async () => {
if (!id) return navigate('/media');

Expand All @@ -54,7 +63,7 @@ export default function ItemGet() {
});
};

const onSave = async () => {
const onDownload = async () => {
if (!item) return navigate('/media');

setLoading(true);
Expand All @@ -73,27 +82,16 @@ export default function ItemGet() {
if (!item) return <Navigate to="/media" />;
return (
<>
<Box display="flex" justifyContent="flex-end" mb={3}>
<LoadingButton
disableElevation
loading={loading}
onClick={onSave}
startIcon={<CloudDownload />}
sx={{ borderRadius: 2 }}
variant="contained"
>
ダウンロード
</LoadingButton>
</Box>
<Box pb={3}>
<Box component="img" src={item.url} />
<Box display="flex" justifyContent="flex-end" mt={3}>
<Button color="error" disabled={loading} onClick={toggleDialog} size="small">
このメディアを削除
</Button>
</Box>
<Dialog onClose={toggleDialog} onSubmit={onDelete} open={openDialog} />
</Box>
<Lightbox
item={item}
loading={loading}
onClose={navigateList}
onDelete={toggleDialog}
onDownload={onDownload}
onNavigateNext={navigateNext}
onNavigatePrev={navigatePrev}
/>
<Dialog onClose={toggleDialog} onSubmit={onDelete} open={openDialog} />
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ import { Box, Button } from '@mui/material';
import { Link } from 'react-router-dom';

import useItemUseCase from '../../../../../../application/useCases/item';
import { ItemWithURL } from '../../../../../../domains/item';
import itemRepository from '../../../../../repositories/item';
import mediumRepository from '../../../../../repositories/medium';
import { useItemStore } from '../../../../../stores/item';
import ItemSectionList from '../../../organisms/sectionList/item';
import Loading from '../../loading';

export default function ItemList() {
const { items } = useItemStore();
const { subscribe } = useItemUseCase(itemRepository(), mediumRepository());
const [items, setItems] = useState<ItemWithURL[]>();
const [limit] = useState(100);

useEffect(() => {
const unsubscribe = subscribe(limit, setItems);
const unsubscribe = subscribe(limit, (nextItems) =>
useItemStore.setState({ items: nextItems })
);
return () => unsubscribe();
}, []);

if (typeof items === 'undefined') return <Loading />;
return (
<>
<Box display="flex" justifyContent="flex-end" mb={3}>
Expand Down

0 comments on commit 20f3be4

Please sign in to comment.