Skip to content

Commit

Permalink
Merge pull request #378 from kure-kosen/react/radio-controllers
Browse files Browse the repository at this point in the history
React/radio controllers
  • Loading branch information
kobakazu0429 authored Jul 13, 2019
2 parents a2230d2 + 9dd9460 commit ab8b4e0
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 53 deletions.
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}
/>
</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

0 comments on commit ab8b4e0

Please sign in to comment.