Skip to content

Commit

Permalink
Merge pull request #110 from Na-o-man/feat/#89
Browse files Browse the repository at this point in the history
Feat/#89
  • Loading branch information
NekoGroove01 authored Aug 23, 2024
2 parents 35aa20e + 05f257f commit 67d620b
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 42 deletions.
67 changes: 67 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"express": "^4.19.2",
"jszip": "^3.10.1",
"react": "^18.3.1",
"react-cookie": "^7.2.0",
"react-dom": "^18.3.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import {
isModalState,
selectedImageState,
} from 'recoil/states/share_group';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import ShareGroupBottomBar from '../ShareGroupBottomBar/ShareGroupBottomBar';
import { deletePhoto } from 'apis/deletePhoto';
import { useLocation, useNavigate } from 'react-router-dom';
import { getPhotos, getPhotosAll, getPhotosEtc } from 'apis/getPhotos';
import { addedAgendaPhotos, addedAgendaSrcs } from 'recoil/states/vote';
import {
addedAgendaPhotos,
addedAgendaSrcs,
choiceMode,
} from 'recoil/states/vote';

export interface itemProp {
createdAt: string;
Expand Down Expand Up @@ -53,7 +57,7 @@ const ShareGroupImageList = ({
const [hasMore, setHasMore] = useState<boolean>(true);
// 사진 칸 observer
const containerRef = useRef<HTMLDivElement>(null);
const choiceMode = state.choiceMode;
const mode: boolean = useRecoilValue(choiceMode);
const nav = useNavigate();
const [photos, setPhotos] = useRecoilState(addedAgendaPhotos);
const [sources, setSources] = useRecoilState(addedAgendaSrcs);
Expand All @@ -75,7 +79,7 @@ const ShareGroupImageList = ({
...sources,
...checkedImg.filter((id) => !sources.includes(id)),
];
if (choiceMode && newPhotos.length > 6) {
if (mode && newPhotos.length > 6) {
alert('사진의 최대 등록 개수는 6장입니다');
nav(-1);
return;
Expand Down Expand Up @@ -127,7 +131,7 @@ const ShareGroupImageList = ({
};

useEffect(() => {
if (choiceMode) setIsChecked(true);
if (mode) setIsChecked(true);
if (!isChecked) setCheckedImg([]);
}, [isChecked]);

Expand Down Expand Up @@ -240,7 +244,7 @@ const ShareGroupImageList = ({
))}
</S.PhotoLayout>
</S.Layout>
{choiceMode ? (
{mode ? (
<>
<ShareGroupBottomBar srcs={srcs} />
{checkedImg.length > 0 ? (
Expand Down
7 changes: 4 additions & 3 deletions src/components/Vote/PhotoContainer/PhotoAddBtn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
filteredProfile,
profile,
} from 'pages/ShareGroup/ShareGroupFolder/ShareGroupFolder';
import { choiceMode } from 'recoil/states/vote';

const PhotoAddBtn = () => {
const nav = useNavigate();
Expand All @@ -25,6 +26,7 @@ const PhotoAddBtn = () => {
const setPhotoRequest = useSetRecoilState(photoRequestState);
const setType = useSetRecoilState(photoTypeState);
const setShareGroupMember = useSetRecoilState(shareGroupMemberListState);
const [mode, setMode] = useRecoilState(choiceMode);

useEffect(() => {
getShareGroupMembers(groupId).then((res) => {
Expand Down Expand Up @@ -74,9 +76,8 @@ const PhotoAddBtn = () => {
}
const data = { shareGroupId: groupId, profileId: id, size: 20 };
setPhotoRequest(data);
nav('/group/detail', {
state: { choiceMode: true },
});
setMode(true);
nav(`/group/${groupId}/${id}`);
};
return (
<S.ButtonLayout>
Expand Down
22 changes: 13 additions & 9 deletions src/pages/ShareGroup/ShareGroupDetailPage/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ import {
photoTypeState,
shareGroupMemberListState,
} from 'recoil/states/share_group';
import { useNavigate, useParams } from 'react-router-dom';

interface DropDownProps {
groupId: number;
}

const DropDown: React.FC<DropDownProps> = ({ groupId }) => {
const DropDown: React.FC = () => {
const [isClicked, setIsClicked] = useState(false);
const { id, profileId } = useParams();
const navigator = useNavigate();
const members = useRecoilValue(shareGroupMemberListState);
const names = members
?.filter((mem) => mem.memberId !== null)
Expand All @@ -29,7 +28,8 @@ const DropDown: React.FC<DropDownProps> = ({ groupId }) => {
setIsClicked(!isClicked);
};

const handleItemClick = (idx: number, profileId: number, name: string) => {
const handleItemClick = (idx: number, name: string) => {
navigator(`/group/${id}/${names[idx].profileId}`);
if (name === '모든 사진') {
setPhotoType('all');
} else if (name === '기타 사진') {
Expand All @@ -39,7 +39,11 @@ const DropDown: React.FC<DropDownProps> = ({ groupId }) => {
}
setIsClicked(false);
setTitle(names[idx].name);
const newData = { shareGroupId: groupId, profileId: profileId, size: 20 };
const newData = {
shareGroupId: Number(id),
profileId: Number(profileId),
size: 20,
};
setRequestState(newData);
};

Expand All @@ -55,15 +59,15 @@ const DropDown: React.FC<DropDownProps> = ({ groupId }) => {
names[i].name === title ? (
<S.DropDownItem
key={i}
onClick={() => handleItemClick(i, name.profileId, name.name)}
onClick={() => handleItemClick(i, name.name)}
style={{ fontWeight: '700' }}
>
{name.name}
</S.DropDownItem>
) : (
<S.DropDownItem
key={i}
onClick={() => handleItemClick(i, name.profileId, name.name)}
onClick={() => handleItemClick(i, name.name)}
>
{name.name}
</S.DropDownItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
shareGroupId,
} from 'recoil/states/share_group';
import { getPhotos, getPhotosAll, getPhotosEtc } from 'apis/getPhotos';
import { choiceMode } from 'recoil/states/vote';

const ShareGroupDetailPage: React.FC = () => {
const [isLoading, setIsLoading] = useState(false);
const { id, profileId } = useParams<{ id: string; profileId: string }>();
const nav = useNavigate();
const location = useLocation();
const mode = location.state.choiceMode;
const mode = useRecoilValue(choiceMode);
const groupId = useRecoilValue(shareGroupId);
const [requestData, setRequestData] = useRecoilState(photoRequestState);
const requestType = useRecoilValue(photoTypeState);
Expand Down Expand Up @@ -101,7 +101,7 @@ const ShareGroupDetailPage: React.FC = () => {
<S.TopRectContainer>
<S.TopRect />
<S.DropDownContainer>
<DropDown groupId={groupId} />
<DropDown />
</S.DropDownContainer>
</S.TopRectContainer>
<Header backarrow checkbtn />
Expand Down
6 changes: 6 additions & 0 deletions src/recoil/states/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,9 @@ export const addedAgendaSrcs = atom<number[]>({
default: [],
effects_UNSTABLE: [persistAtom],
});

export const choiceMode = atom<boolean>({
key: 'choiceMode',
default: false,
effects_UNSTABLE: [persistAtom],
});
52 changes: 31 additions & 21 deletions src/utils/ImageZipDownloader.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
// 이미지들을 jpeg로 변환하여 다운로드
const imageZipDownloader = async (imageUrls: string[]) => {
imageUrls.forEach((url, index) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.src = url;
img.onload = () => {
// create Canvas
const canvas = document.createElement('canvas');
const ctx: CanvasRenderingContext2D | null = canvas.getContext('2d');
if (!ctx) return;
canvas.width = img.width;
canvas.height = img.height;
ctx?.drawImage(img, 0, 0);
// for create tag anchor
const a = document.createElement('a');
a.download = `image-${index}-download`;
a.href = canvas.toDataURL('image/jpeg');
a.click();
};
});
import JSZip from 'jszip';

// 이미지들을 jpeg로 변환하여 zip 파일로 다운로드
const imageZipDownloader = async ({
imageUrls,
}: {
imageUrls: string[];
}): Promise<void> => {
const zip = new JSZip();

for (const url of imageUrls) {
try {
const response = await fetch(url);
const blob = await response.blob();
const fileName = url.split('/').pop() || 'image';
zip.file(fileName, blob);
} catch (error) {
console.error('Error processing image:', error);
throw error;
}
}

const zipBlob = await zip.generateAsync({ type: 'blob' });
const url = window.URL.createObjectURL(zipBlob);
const a = document.createElement('a');
a.href = url;
a.download = 'images.zip';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
};

export default imageZipDownloader;

0 comments on commit 67d620b

Please sign in to comment.