mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
refactored playerbar component
This commit is contained in:
parent
6ca4c514bf
commit
df217b41f7
9 changed files with 78 additions and 53 deletions
|
@ -1,26 +1,21 @@
|
||||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState, VFC } from 'react';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { PLAYER_STATES } from '~/redux/player/constants';
|
import { PlayerState } from '~/redux/player/constants';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { pick } from 'ramda';
|
|
||||||
import { selectPlayer } from '~/redux/player/selectors';
|
|
||||||
import * as PLAYER_ACTIONS from '~/redux/player/actions';
|
|
||||||
import { IPlayerProgress, Player } from '~/utils/player';
|
|
||||||
import { path } from 'ramda';
|
import { path } from 'ramda';
|
||||||
|
import { IPlayerProgress, Player } from '~/utils/player';
|
||||||
import { IFile } from '~/redux/types';
|
import { IFile } from '~/redux/types';
|
||||||
|
|
||||||
const mapStateToProps = state => pick(['status', 'file'], selectPlayer(state));
|
interface Props {
|
||||||
const mapDispatchToProps = {
|
status: PlayerState;
|
||||||
playerPlay: PLAYER_ACTIONS.playerPlay,
|
file?: IFile;
|
||||||
playerPause: PLAYER_ACTIONS.playerPause,
|
playerPlay: () => void;
|
||||||
playerSeek: PLAYER_ACTIONS.playerSeek,
|
playerPause: () => void;
|
||||||
playerStop: PLAYER_ACTIONS.playerStop,
|
playerSeek: (pos: number) => void;
|
||||||
};
|
playerStop: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
const PlayerBar: VFC<Props> = ({
|
||||||
|
|
||||||
const PlayerBarUnconnected: FC<IProps> = ({
|
|
||||||
status,
|
status,
|
||||||
playerPlay,
|
playerPlay,
|
||||||
playerPause,
|
playerPause,
|
||||||
|
@ -31,7 +26,7 @@ const PlayerBarUnconnected: FC<IProps> = ({
|
||||||
const [progress, setProgress] = useState<IPlayerProgress>({ progress: 0, current: 0, total: 0 });
|
const [progress, setProgress] = useState<IPlayerProgress>({ progress: 0, current: 0, total: 0 });
|
||||||
|
|
||||||
const onClick = useCallback(() => {
|
const onClick = useCallback(() => {
|
||||||
if (status === PLAYER_STATES.PLAYING) return playerPause();
|
if (status === PlayerState.PLAYING) return playerPause();
|
||||||
return playerPlay();
|
return playerPlay();
|
||||||
}, [playerPlay, playerPause, status]);
|
}, [playerPlay, playerPause, status]);
|
||||||
|
|
||||||
|
@ -61,7 +56,7 @@ const PlayerBarUnconnected: FC<IProps> = ({
|
||||||
};
|
};
|
||||||
}, [onProgress]);
|
}, [onProgress]);
|
||||||
|
|
||||||
if (status === PLAYER_STATES.UNSET) return null;
|
if (status === PlayerState.UNSET) return null;
|
||||||
|
|
||||||
const metadata: IFile['metadata'] = path(['metadata'], file);
|
const metadata: IFile['metadata'] = path(['metadata'], file);
|
||||||
const title =
|
const title =
|
||||||
|
@ -73,7 +68,7 @@ const PlayerBarUnconnected: FC<IProps> = ({
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<div className={styles.status}>
|
<div className={styles.status}>
|
||||||
<div className={styles.playpause} onClick={onClick}>
|
<div className={styles.playpause} onClick={onClick}>
|
||||||
{status === PLAYER_STATES.PLAYING ? <Icon icon="pause" /> : <Icon icon="play" />}
|
{status === PlayerState.PLAYING ? <Icon icon="pause" /> : <Icon icon="play" />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
|
@ -93,7 +88,4 @@ const PlayerBarUnconnected: FC<IProps> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PlayerBar = connect(
|
export { PlayerBar };
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(PlayerBarUnconnected);
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ import { connect } from 'react-redux';
|
||||||
import { selectPlayer } from '~/redux/player/selectors';
|
import { selectPlayer } from '~/redux/player/selectors';
|
||||||
import * as PLAYER_ACTIONS from '~/redux/player/actions';
|
import * as PLAYER_ACTIONS from '~/redux/player/actions';
|
||||||
import { IFile } from '~/redux/types';
|
import { IFile } from '~/redux/types';
|
||||||
import { PLAYER_STATES } from '~/redux/player/constants';
|
|
||||||
import { IPlayerProgress, Player } from '~/utils/player';
|
import { IPlayerProgress, Player } from '~/utils/player';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
|
import { PlayerState } from '~/redux/player/constants';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
player: selectPlayer(state),
|
player: selectPlayer(state),
|
||||||
|
@ -52,7 +52,7 @@ const AudioPlayerUnconnected = memo(
|
||||||
if (isEditing) return;
|
if (isEditing) return;
|
||||||
|
|
||||||
if (current && current.id === file.id) {
|
if (current && current.id === file.id) {
|
||||||
if (status === PLAYER_STATES.PLAYING) return playerPause();
|
if (status === PlayerState.PLAYING) return playerPause();
|
||||||
return playerPlay();
|
return playerPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,11 +122,7 @@ const AudioPlayerUnconnected = memo(
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.playpause}>
|
<div className={styles.playpause}>
|
||||||
{playing && status === PLAYER_STATES.PLAYING ? (
|
{playing && status === PlayerState.PLAYING ? <Icon icon="pause" /> : <Icon icon="play" />}
|
||||||
<Icon icon="pause" />
|
|
||||||
) : (
|
|
||||||
<Icon icon="play" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { PlayerBar } from '~/components/bars/PlayerBar';
|
import { PlayerView } from '~/views/player/PlayerView';
|
||||||
|
|
||||||
type IProps = {};
|
type IProps = {};
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ const BottomContainer: FC<IProps> = () => (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.padder}>
|
<div className={styles.padder}>
|
||||||
<PlayerBar />
|
<PlayerView />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,8 +16,8 @@ export const PLAYER_ACTIONS = {
|
||||||
GET_YOUTUBE_INFO: `${prefix}GET_YOUTUBE_INFO`,
|
GET_YOUTUBE_INFO: `${prefix}GET_YOUTUBE_INFO`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PLAYER_STATES = {
|
export enum PlayerState {
|
||||||
PLAYING: 'PLAYING',
|
PLAYING = 'PLAYING',
|
||||||
PAUSED: 'PAUSED',
|
PAUSED = 'PAUSED',
|
||||||
UNSET: 'UNSET',
|
UNSET = 'UNSET',
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { createReducer } from '~/utils/reducer';
|
import { createReducer } from '~/utils/reducer';
|
||||||
import { PLAYER_HANDLERS } from './handlers';
|
import { PLAYER_HANDLERS } from './handlers';
|
||||||
import { PLAYER_STATES } from './constants';
|
import { PlayerState } from './constants';
|
||||||
import { IFile, IEmbed } from '../types';
|
import { IEmbed, IFile } from '../types';
|
||||||
|
|
||||||
export type IPlayerState = Readonly<{
|
export type IPlayerState = Readonly<{
|
||||||
status: typeof PLAYER_STATES[keyof typeof PLAYER_STATES];
|
status: PlayerState;
|
||||||
file?: IFile;
|
file?: IFile;
|
||||||
youtubes: Record<string, IEmbed>;
|
youtubes: Record<string, IEmbed>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const INITIAL_STATE: IPlayerState = {
|
const INITIAL_STATE: IPlayerState = {
|
||||||
status: PLAYER_STATES.UNSET,
|
status: PlayerState.UNSET,
|
||||||
file: undefined,
|
file: undefined,
|
||||||
youtubes: {},
|
youtubes: {},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { takeLatest, put, fork, race, take, delay, call, select } from 'redux-saga/effects';
|
import { call, delay, fork, put, race, select, take, takeLatest } from 'redux-saga/effects';
|
||||||
import { PLAYER_ACTIONS, PLAYER_STATES } from './constants';
|
import { PLAYER_ACTIONS, PlayerState } from './constants';
|
||||||
import {
|
import {
|
||||||
playerSetFile,
|
|
||||||
playerSeek,
|
|
||||||
playerSetStatus,
|
|
||||||
playerGetYoutubeInfo,
|
playerGetYoutubeInfo,
|
||||||
|
playerSeek,
|
||||||
playerSet,
|
playerSet,
|
||||||
|
playerSetFile,
|
||||||
|
playerSetStatus,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { Player } from '~/utils/player';
|
import { Player } from '~/utils/player';
|
||||||
import { getURL } from '~/utils/dom';
|
import { getURL } from '~/utils/dom';
|
||||||
|
@ -41,7 +41,7 @@ function seekSaga({ seek }: ReturnType<typeof playerSeek>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* stoppedSaga() {
|
function* stoppedSaga() {
|
||||||
yield put(playerSetStatus(PLAYER_STATES.UNSET));
|
yield put(playerSetStatus(PlayerState.UNSET));
|
||||||
yield put(playerSetFile(undefined));
|
yield put(playerSetFile(undefined));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/utils/hooks/player/usePlayer.ts
Normal file
17
src/utils/hooks/player/usePlayer.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
|
import { selectPlayer } from '~/redux/player/selectors';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { playerPause, playerPlay, playerSeek, playerStop } from '~/redux/player/actions';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
export const usePlayer = () => {
|
||||||
|
const { status, file } = useShallowSelect(selectPlayer);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const onPlayerPlay = useCallback(() => dispatch(playerPlay()), [dispatch]);
|
||||||
|
const onPlayerPause = useCallback(() => dispatch(playerPause()), [dispatch]);
|
||||||
|
const onPlayerSeek = useCallback((pos: number) => dispatch(playerSeek(pos)), [dispatch]);
|
||||||
|
const onPlayerStop = useCallback(() => dispatch(playerStop()), [dispatch]);
|
||||||
|
|
||||||
|
return { status, file, onPlayerPlay, onPlayerSeek, onPlayerPause, onPlayerStop };
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
import { store } from '~/redux/store';
|
import { store } from '~/redux/store';
|
||||||
import { playerSetStatus, playerStopped } from '~/redux/player/actions';
|
import { playerSetStatus, playerStopped } from '~/redux/player/actions';
|
||||||
import { PLAYER_STATES } from '~/redux/player/constants';
|
import { PlayerState } from '~/redux/player/constants';
|
||||||
|
|
||||||
type PlayerEventType = keyof HTMLMediaElementEventMap;
|
type PlayerEventType = keyof HTMLMediaElementEventMap;
|
||||||
|
|
||||||
|
@ -81,10 +81,8 @@ export class PlayerClass {
|
||||||
|
|
||||||
const Player = new PlayerClass();
|
const Player = new PlayerClass();
|
||||||
|
|
||||||
// Player.element.addEventListener('playprogress', ({ detail }: CustomEvent) => console.log(detail));
|
Player.on('play', () => store.dispatch(playerSetStatus(PlayerState.PLAYING)));
|
||||||
|
Player.on('pause', () => store.dispatch(playerSetStatus(PlayerState.PAUSED)));
|
||||||
Player.on('play', () => store.dispatch(playerSetStatus(PLAYER_STATES.PLAYING)));
|
|
||||||
Player.on('pause', () => store.dispatch(playerSetStatus(PLAYER_STATES.PAUSED)));
|
|
||||||
Player.on('stop', () => store.dispatch(playerStopped()));
|
Player.on('stop', () => store.dispatch(playerStopped()));
|
||||||
Player.on('error', () => store.dispatch(playerStopped()));
|
Player.on('error', () => store.dispatch(playerStopped()));
|
||||||
|
|
||||||
|
|
22
src/views/player/PlayerView/index.tsx
Normal file
22
src/views/player/PlayerView/index.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React, { VFC } from 'react';
|
||||||
|
import { PlayerBar } from '~/components/bars/PlayerBar';
|
||||||
|
import { usePlayer } from '~/utils/hooks/player/usePlayer';
|
||||||
|
|
||||||
|
interface PlayerViewProps {}
|
||||||
|
|
||||||
|
const PlayerView: VFC<PlayerViewProps> = () => {
|
||||||
|
const { status, file, onPlayerSeek, onPlayerStop, onPlayerPause, onPlayerPlay } = usePlayer();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PlayerBar
|
||||||
|
status={status}
|
||||||
|
playerPlay={onPlayerPlay}
|
||||||
|
playerPause={onPlayerPause}
|
||||||
|
playerSeek={onPlayerSeek}
|
||||||
|
playerStop={onPlayerStop}
|
||||||
|
file={file}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { PlayerView };
|
Loading…
Add table
Add a link
Reference in a new issue