Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React/radio controllers #378

Merged
merged 16 commits into from
Jul 13, 2019
1 change: 1 addition & 0 deletions frontend/src/api/RadioApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface IRadio {
youtube_url: string;
podcast_url: string;
image: string;
duration: number;
play_time: string;
published_at: string;
created_at: string;
Expand Down
28 changes: 0 additions & 28 deletions frontend/src/components/atoms/RadioCard/RadioCardContent.tsx

This file was deleted.

76 changes: 56 additions & 20 deletions frontend/src/components/atoms/RadioCard/RadioCardPlayButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,86 @@ import styled from "styled-components";
import { IRadio } from "@/api/RadioApi";
import { color } from "@/constants/styles";

export default (props: Pick<IRadio, "mp3" | "play_time">) => {
const { mp3, play_time } = props;
import useAudio from "@/utils/hooks/useAudio";
import PlayButtonProgress from "@/components/atoms/RadioCard/RadioCardPlayButtonProgress";
import SeekBar from "@/components/atoms/RadioCard/RadioCardSeekBar";

const isPlay = !false;
export default (props: Pick<IRadio, "mp3" | "duration">) => {
const { mp3, duration } = props;
const { isPlay, play, pause, jump, times } = useAudio({
url: mp3.url!,
duration
});

return (
<Wrapper>
<ButtonWrapper>
{isPlay ? (
<PlayButton className="fas fa-play" />
) : (
<PauseButton className="fas fa-pause" />
)}
</ButtonWrapper>
<PlayButtonProgressWrapper>
<ButtonWrapper>
{!isPlay ? (
times.currentTime === times.duration ? (
<RePlayButton className="fas fa-redo-alt" onClick={play} />
) : (
<PlayButton className="fas fa-play" onClick={play} />
)
) : (
<PauseButton className="fas fa-pause" onClick={pause} />
)}
</ButtonWrapper>
<PlayButtonProgress
progress={(() => {
const percent =
Math.round((times.currentTime / times.duration) * 1000) / 10;
return isNaN(percent) ? 0 : percent;
})()}
currentTime={times.currentTime}
/>
</PlayButtonProgressWrapper>
<SeekBar
currentTime={times.currentTime}
duration={times.duration}
jump={jump}
/>
</Wrapper>
);
};

const Wrapper = styled.div`
position: absolute;
top: ${220 - 32 / 2}px;
left: 0;
right: 0;
display: flex;
align-content: center;
margin: 0 auto;
width: 32px;
height: 32px;
background-color: ${color.ORANGE};
border: 3px solid ${color.VIVID_ORANGE};
border-radius: 50%;
padding: 0 20px;
width: 100%;
height: 38px;
display: flex;
`;

const ButtonWrapper = styled.div`
text-align: center;
margin-top: -2px;
z-index: 10;
position: relative;
`;

const ButtonBase = styled.i`
line-height: 32px;
font-size: 0.7rem;
color: ${color.WHITE};
position: absolute;
top: 2px;
left: 0;
right: 0;
margin: 0 auto;
cursor: pointer;
`;

const PlayButton = styled(ButtonBase)`
padding-left: 2.5px;
`;

const PauseButton = styled(ButtonBase)``;

const RePlayButton = styled(ButtonBase)`
font-size: 0.8rem;
padding-top: 1px;
`;

const PlayButtonProgressWrapper = styled.div``;
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from "react";
import styled from "styled-components";

import { color } from "@/constants/styles";

const calcParams = ({
radius,
strokeWidth
}: {
radius: number;
strokeWidth: number;
}) => {
return {
width: radius * 2 + strokeWidth,
height: radius * 2 + strokeWidth,
radius,
strokeWidth,
circumference: 2 * Math.PI * (radius - 2)
};
};

interface IProps {
progress?: number;
currentTime: number;
}

export default ({ progress = 100, currentTime }: IProps) => {
const params = calcParams({ radius: 16, strokeWidth: 4 });

return (
<Wrapper width={params.width} height={params.height}>
<Progress
width={params.width}
height={params.height}
progress={progress}
circumference={params.circumference}
strokeWidth={params.strokeWidth}
currentTime={currentTime}
>
<circle
className="back"
cx={params.width / 2}
cy={params.height / 2}
r={params.radius}
/>
<circle
className="white-bar"
x={1}
y={1}
cx={params.width / 2}
cy={params.height / 2}
r={params.radius - 2.5}
/>
<circle
className="progress"
cx={params.width / 2}
cy={params.height / 2}
r={params.radius - 2}
/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

めちゃくちゃcircleを作っている...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あのデザインは難しかった

</Progress>
</Wrapper>
);
};

const Wrapper = styled.div<{ width: number; height: number }>`
margin: 0 auto;
width: ${props => props.width}px;
height: ${props => props.height}px;
`;

interface IProgress {
currentTime: number;
progress: number;
circumference: number;
strokeWidth: number;
}

const Progress = styled.svg<IProgress>`
transform: rotate(-90deg);

& .progress {
fill: transparent;
stroke: ${props =>
props.currentTime > 0 ? color.VIVID_ORANGE : color.ORANGE};
stroke-width: ${props => props.strokeWidth};
stroke-dasharray: ${props =>
`${(props.progress / 100) * props.circumference} ${props.circumference}`};
}

& .white-bar {
fill: transparent;
stroke: ${color.WHITE};
stroke-width: ${props => (props.currentTime > 0 ? 3 : 0)};
}

& .back {
fill: ${color.ORANGE};
}
`;
74 changes: 74 additions & 0 deletions frontend/src/components/atoms/RadioCard/RadioCardSeekBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from "react";
import styled from "styled-components";

import { color } from "@/constants/styles";
import { IRadio } from "@/api/RadioApi";
import { IUseAudio } from "@/utils/hooks/useAudio";

interface IProps extends Pick<IRadio, "duration">, Pick<IUseAudio, "jump"> {
currentTime: number;
}

export default (props: IProps) => {
const { currentTime, duration, jump } = props;

const [value, setValue] = React.useState(0);

React.useEffect(() => {
setValue(currentTime);
}, [currentTime]);

const handleChange = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const data = Number(e.target.value);
jump(data);
setValue(data);
},
[]
);

return (
<Wrapper>
<SeekBar
type="range"
value={value}
min={0}
max={duration}
onChange={handleChange}
/>
</Wrapper>
);
};

const Wrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
`;

const SeekBar = styled.input`
width: 100%;
height: 4px;
border-radius: 6px;

background-color: #eaeaea;

&:focus,
&:active {
outline: none;
}

&::-webkit-slider-thumb,
-moz-range-thumb {
appearance: none;
cursor: pointer;
position: relative;
width: 13px;
height: 13px;
display: block;
background-color: ${color.ORANGE};
border-radius: 50%;
}
`;
18 changes: 14 additions & 4 deletions frontend/src/components/molecules/RadioCard/RadioCardWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { IRadio } from "@/api/RadioApi";
import { color } from "@/constants/styles";

import CardImage from "@/components/atoms/RadioCard/RadioCardImage";
import CardContent from "@/components/atoms/RadioCard/RadioCardContent";
import PlayButton from "@/components/atoms/RadioCard/RadioCardPlayButton";

export default (props: IRadio) => {
const { title, description, mp3, image, play_time } = props;
const { title, description, mp3, duration, image } = props;

return (
<RadioCardWrapperStyle>
<CardImage image={image} />
<PlayButton mp3={mp3} play_time={play_time} />
<CardContent title={title} description={description} />
<Title>{title}</Title>
<PlayButton mp3={mp3} duration={duration} />
<Description dangerouslySetInnerHTML={{ __html: description }} />
</RadioCardWrapperStyle>
);
};
Expand All @@ -27,3 +27,13 @@ const RadioCardWrapperStyle = styled.div`
border: 3px solid ${color.SKY_BLUE};
position: relative;
`;

const Title = styled.h3`
font-size: 1.2rem;
margin: 0;
margin-top: 5px;
color: ${color.BLUE};
text-align: center;
`;

const Description = styled.p``;
3 changes: 2 additions & 1 deletion frontend/src/stores/RadioStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import { action, observable } from "mobx";
import RadioApi, { IRadio } from "@/api/RadioApi";

export default class RadioStore {
@observable public radios?: IRadio[];
@observable public radios: IRadio[];

public transportLayer: RadioApi;

constructor(transportLayer: RadioApi) {
this.transportLayer = transportLayer;
this.radios = [];
}

public async fetchRadios() {
Expand Down
Loading