Skip to content

Commit

Permalink
BaseMusic
Browse files Browse the repository at this point in the history
  • Loading branch information
hhhello0507 committed Jan 18, 2025
1 parent cd40bad commit 3f5e037
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 49 deletions.
12 changes: 6 additions & 6 deletions src/component/template/component/GalleryTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ function GallerySlide(

return (
<S.slideWrapper>
<S.scroll ref={scrollContainerRef} slideStyle={slideStyle}>
<S.scroll ref={scrollContainerRef} $slideStyle={slideStyle}>
{imgList.map((img, index) => (
<S.slideImg
key={index}
src={img}
$rootWidth={rootRef.current?.getBoundingClientRect().width ?? 0}
slideStyle={slideStyle}
$slideStyle={slideStyle}
/>
))}
</S.scroll>
Expand Down Expand Up @@ -179,12 +179,12 @@ const S = {
align-self: stretch;
overflow-x: hidden;
`,
scroll: styled.div<{ slideStyle: GallerySlideStyle }>`
scroll: styled.div<{ $slideStyle: GallerySlideStyle }>`
scroll-snap-type: x mandatory;
overflow-x: scroll;
overflow-y: hidden;
display: flex;
${({slideStyle}) => slideStyle === 'style1' && css`
${({$slideStyle}) => $slideStyle === 'style1' && css`
gap: 8px;
`};
${hideScrollBar};
Expand All @@ -197,10 +197,10 @@ const S = {
`,
slideImg: styled.img<{
$rootWidth: number,
slideStyle: GallerySlideStyle
$slideStyle: GallerySlideStyle
}>`
display: flex;
${({$rootWidth, slideStyle}) => slideStyle === 'style1' ? css`
${({$rootWidth, $slideStyle}) => $slideStyle === 'style1' ? css`
width: ${$rootWidth - 34 * 2}px;
&:first-child {
Expand Down
2 changes: 0 additions & 2 deletions src/component/template/component/PreviewTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ function PreviewTemplate(
}: PreviewTemplateProps
) {
const dateString = `${weddingSchedule.weddingDate} ${weddingSchedule.weddingTime}`;
console.log(dateString)
const date = parse(dateString, 'yyyy-MM-dd HH:mm', new Date());
console.log(date)
const isValidDate = !isNaN(date.getTime());

switch (template.templateName) {
Expand Down
8 changes: 6 additions & 2 deletions src/designsystem/component/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {
CSSProperties,
ForwardedRef,
forwardRef,
forwardRef, useEffect,
useImperativeHandle,
useRef,
useState
Expand Down Expand Up @@ -38,6 +38,10 @@ function Checkbox(
const [localChecked, setLocalChecked] = useState(checked);
const checkboxRef = useRef<HTMLInputElement>(null);

useEffect(() => {
setLocalChecked(checked);
}, [checked]);

useImperativeHandle(ref, () => ({
value: localChecked,
focus: () => {
Expand Down Expand Up @@ -85,7 +89,7 @@ function Checkbox(
}}
/>}
</S.checkbox>
{label && <S.title>{label}</S.title>}
{label && <S.title style={{cursor: 'pointer'}} onClick={() => onChange?.(!checked)}>{label}</S.title>}
</S.container>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ ${baseInfo.groomName}, ${baseInfo.brideName}님의 링크메리 모바일 청첩
<S.container>
<S.content>
<S.image src={weddingInfo.img} alt=""/>
<Column gap={12} $alignItems={'stretch'} padding={20} background={colors.g100}>
<Column gap={12} $alignItems={'stretch'} padding={'20px'} background={colors.g100}>
<Column gap={4} $alignItems={'stretch'}>
<Row gap={8}>
<S.urlLabel>{weddingInfo.url}</S.urlLabel>
Expand Down
180 changes: 148 additions & 32 deletions src/page/invitation/design/option/BaseMusicOption.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef, useState} from 'react';
import React, {ChangeEvent, useEffect, useRef, useState} from 'react';
import styled from "styled-components";
import {Column, Row} from "@designsystem/component/flexLayout";
import HorizontalDivider from "@designsystem/component/horizontalDivider";
Expand All @@ -9,6 +9,9 @@ import Icon, {IconType} from "@designsystem/foundation/icon";
import OptionSegmentedButton from "@page/invitation/design/component/OptionSegmentedButton";
import Text from "@designsystem/component/text";
import BaseMusic from "@remote/value/BaseMusic";
import fileApi from "@remote/api/FileApi";
import Music, {getMusicName} from "@remote/value/Music";
import LoadingOverlay from "@src/component/LoadingOverlay";

type SelectMode = 'select' | 'direct';
const selectModeRecord: Record<SelectMode, string> = {
Expand All @@ -29,46 +32,148 @@ function BaseMusicOption(
onChange
}: BaseMusicOptionProps
) {
const audioRef = useRef(null);
const [selectedAudio, setSelectedAudio] = useState<string>();
const audioRef = useRef<HTMLAudioElement>(null);
const [selectedPlayingMusicUrl, setSelectedPlayingMusicUrl] = useState<string>();
const [selectedSelectMode, setSelectedSelectMode] = useState(0);
const [musics, setMusics] = useState<Music[]>();
const imageFieldRef = useRef<HTMLInputElement>(null);
const [isFetching, setIsFetching] = useState(false);
const currentSelectMode = baseMusic.musicUrl === '' ? 0 : musics?.map(i => i.url).includes(baseMusic.musicUrl) ? 0 : 1;

const uploadAudio = async (event: ChangeEvent<HTMLInputElement>) => {
const files = event.target.files;
if (!files) return;

const file = files[0];
if (!file) return;

setIsFetching(true);
const {data} = await fileApi.upload(file);

onChange({...baseMusic, musicUrl: data.url});
setIsFetching(false);
};

useEffect(() => {
(async () => {
const {data} = await fileApi.getMusics();
setMusics(data);
})();
}, []);

const onClickPlayMusic = async (music: Music) => {
const audio = audioRef.current;
if (!audio) return;

if (!selectedPlayingMusicUrl) {
setSelectedPlayingMusicUrl(music.url);
audio.src = music.url;
await audio.play();
return;
}

if (music.url === selectedPlayingMusicUrl) {
setSelectedPlayingMusicUrl(undefined);
audio.src = '';
audio.pause();
} else {
setSelectedPlayingMusicUrl(music.url);
audio.src = music.url;
await audio.play();
}
}

const onClickPlayCustomMusic = async () => {
const audio = audioRef.current;
if (!audio) return;

setSelectedPlayingMusicUrl(baseMusic.musicUrl);
audio.src = baseMusic.musicUrl;
await audio.play();
}

const selectModeContent = () => {
switch (selectModes[selectedSelectMode]) {
case 'select':
return <Column gap={16}>
<audio ref={audioRef} src={''}/>
{/* TODO: DUMMY */}
{['Wedding opening', 'Wow', 'Hello World'].map((music, index) => (
<Row key={index} gap={16} $alignItems={'center'}>
<Checkbox rounded={true} label={music}/>
<Icon
style={{
cursor: 'pointer',
}}
type={IconType.LoudSpeaker}
size={20}
tint={colors.g400}
onClick={() => {
setSelectedAudio(music)
}}
/>
</Row>
))}
</Column>
case 'direct':
return <S.addAudioFileContainer $alignSelf={'stretch'} gap={20} $alignItems={'center'}>
<Icon type={IconType.AddLine} size={24} tint={colors.g600}/>
<Column gap={4} $alignItems={'center'}>
<Text type={'caption1'} color={colors.g400}>음원 파일 추가</Text>
<Text type={'caption1'} color={colors.g400}>최대 4.5MB MP3 파일만 가능</Text>
return (
<Column gap={16}>
{musics && (
musics.map((music, index) => (
<Row key={index} gap={12} $alignItems={'center'}>
<Checkbox
checked={music.url === baseMusic.musicUrl}
label={getMusicName(music)} rounded={true}
onChange={() => onChange({...baseMusic, musicUrl: music.url})}
/>
<Row $alignItems={'center'} $justifyContent={'center'} padding={'4px'}>
<Icon
type={IconType.LoudSpeaker} size={20}
tint={(music.url === selectedPlayingMusicUrl) ? colors.p800 : colors.g400}
style={{cursor: 'pointer'}}
onClick={async () => await onClickPlayMusic(music)}
/>
</Row>
</Row>
))
)}
</Column>
</S.addAudioFileContainer>;
);
case 'direct':
return (
<S.addAudioFileContainer
htmlFor={'choose-audio'}
style={{
cursor: currentSelectMode === 0 ? 'pointer' : undefined
}}
>
{currentSelectMode === 0 ? (
<>
<Icon type={IconType.AddLine} size={24} tint={colors.g600}/>
<Column gap={4} $alignItems={'center'}>
<Text type={'caption1'} color={colors.g400}>음원 파일 추가</Text>
<Text type={'caption1'} color={colors.g400}>최대 20MB MP3 파일만 가능</Text>
</Column>
<S.voidInput
id={'choose-audio'}
ref={imageFieldRef}
onChange={uploadAudio}
type={'file'}
accept={'audio/*'}
/>
</>
) : (
<Row gap={12} $alignItems={'center'} padding={'8px 16px'} background={colors.white} style={{
borderRadius: 8
}}>
<Text type={'p5'} color={colors.g500}>배경음악 선택됨</Text>
<Icon
type={IconType.LoudSpeaker} size={20}
tint={(baseMusic.musicUrl === selectedPlayingMusicUrl) ? colors.p800 : colors.g400}
style={{cursor: 'pointer'}}
onClick={onClickPlayCustomMusic}
/>
<Icon
type={IconType.CrossLine} size={20}
tint={colors.g600}
style={{cursor: 'pointer'}}
onClick={event => {
event.preventDefault();
onChange({...baseMusic, musicUrl: ''});
audioRef.current?.pause();
setSelectedPlayingMusicUrl(undefined);
}}
/>
</Row>
)}
{isFetching && <LoadingOverlay/>}
</S.addAudioFileContainer>
);
}
};

return (
<S.container>
<audio ref={audioRef}/>
<Column gap={32} flex={1} $alignItems={'stretch'}>
<Column gap={20}>
<OptionSegmentedButton
Expand Down Expand Up @@ -103,12 +208,23 @@ const S = {
display: flex;
padding: 36px;
`,
addAudioFileContainer: styled(Column)`
addAudioFileContainer: styled.label`
display: flex;
flex-direction: column;
align-self: stretch;
gap: 20px;
align-items: center;
position: relative;
background: ${colors.g100};
border-radius: 8px;
padding: 30px 0;
cursor: pointer;
`,
voidInput: styled.input`
display: none;
width: 0;
height: 0;
`,

}

export default BaseMusicOption;
12 changes: 8 additions & 4 deletions src/page/invitation/design/option/TemplateOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ function TemplateOption(
<Row gap={12}>
<OptionLabel label={'디자인'} style={{alignSelf: 'flex-start'}}/>
<S.designWrapper>
{templateNames.map(templateName => (
<SegmentedButton selected={templateName === template.templateName} style={{width: 126}} onClick={() => {
onChange({...template, templateName});
}}>
{templateNames.map((templateName, index) => (
<SegmentedButton
key={index}
selected={templateName === template.templateName}
style={{width: 126}}
onClick={() => {
onChange({...template, templateName});
}}>
{templateName}
</SegmentedButton>
))}
Expand Down
2 changes: 1 addition & 1 deletion src/page/templates/component/TemplateCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function TemplateCell(
src={'https://talkimg.imbc.com/TVianUpload/tvian/TViews/image/2024/02/26/fbc84c43-1b58-463b-9c8a-9855e3d1bb00.jpg'}
alt=""
/>
<Column gap={4} padding={20}>
<Column gap={4} padding={'20px'}>
<Text type={'p2'}>{title}</Text>
{/*<Text text={'title'} type={'p5}/>*/}
</Column>
Expand Down
3 changes: 2 additions & 1 deletion src/remote/api/FileApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {ResponseData} from "@remote/value/Response";
import Upload from "@remote/value/Upload";
import customApi from "@remote/api/foundation/customApi";
import Music from "@remote/value/Music";

class FileApi {
static PATH = 'file';
Expand All @@ -12,7 +13,7 @@ class FileApi {
return data;
}

async getMusics(): Promise<ResponseData<string[]>> {
async getMusics(): Promise<ResponseData<Music[]>> {
const {data} = await customApi.get(`${FileApi.PATH}/music`);
return data;
}
Expand Down
11 changes: 11 additions & 0 deletions src/remote/value/Music.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default interface Music {
name: string;
url: string;
}

export function getMusicName(music: Music) {
const lst = music.name.split('.')
.map(i => i.replaceAll('_', ' '));
lst.pop();
return lst.join('.');
}

0 comments on commit 3f5e037

Please sign in to comment.