Skip to content

Commit

Permalink
Use react-redux for Position and Repeat
Browse files Browse the repository at this point in the history
  • Loading branch information
captbaritone committed Aug 20, 2019
1 parent cc0e1aa commit 7212b9e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 92 deletions.
93 changes: 42 additions & 51 deletions js/components/MainWindow/Position.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import React from "react";
import { connect } from "react-redux";
import { AppState, Dispatch } from "../../types";

import {
SEEK_TO_PERCENT_COMPLETE,
Expand All @@ -9,25 +7,54 @@ import {
SET_SCRUB_POSITION,
} from "../../actionTypes";
import * as Selectors from "../../selectors";

interface StateProps {
displayedPosition: number;
position: number;
}
import { useTypedSelector, useTypedDispatch } from "../../hooks";

interface DispatchProps {
seekToPercentComplete(e: React.MouseEvent<HTMLInputElement>): void;
setPosition(e: React.MouseEvent<HTMLInputElement>): void;
}

type Props = StateProps & DispatchProps;
type Props = DispatchProps;

function usePosition() {
const duration = useTypedSelector(Selectors.getDuration);
const timeElapsed = useTypedSelector(Selectors.getTimeElapsed);
const position = duration ? (Math.floor(timeElapsed) / duration) * 100 : 0;
const scrubPosition = useTypedSelector(Selectors.getUserInputScrubPosition);
const userInputFocus = useTypedSelector(Selectors.getUserInputFocus);

const displayedPosition =
userInputFocus === "position" ? scrubPosition : position;

return [position, displayedPosition];
}

const Position = React.memo(() => {
const [position, displayedPosition] = usePosition();
const dispatch = useTypedDispatch();

const seekToPercentComplete = React.useCallback(
e => {
dispatch({
type: SEEK_TO_PERCENT_COMPLETE,
percent: Number((e.target as HTMLInputElement).value),
});
dispatch({ type: UNSET_FOCUS });
},
[dispatch]
);

const setPosition = React.useCallback(
e => {
dispatch({ type: SET_FOCUS, input: "position" });
dispatch({
type: SET_SCRUB_POSITION,
position: Number((e.target as HTMLInputElement).value),
});
},
[dispatch]
);

const Position = ({
position,
seekToPercentComplete,
displayedPosition,
setPosition,
}: Props) => {
// In shade mode, the position slider shows up differently depending on if
// it's near the start, middle or end of its progress
let className = "";
Expand Down Expand Up @@ -55,42 +82,6 @@ const Position = ({
title="Seeking Bar"
/>
);
};

const mapStateToProps = (state: AppState): StateProps => {
const duration = Selectors.getDuration(state);
const timeElapsed = Selectors.getTimeElapsed(state);
const userInputFocus = Selectors.getUserInputFocus(state);
const scrubPosition = Selectors.getUserInputScrubPosition(state);
const position = duration ? (Math.floor(timeElapsed) / duration) * 100 : 0;

const displayedPosition =
userInputFocus === "position" ? scrubPosition : position;

return {
displayedPosition,
position,
};
};

const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
seekToPercentComplete: e => {
dispatch({
type: SEEK_TO_PERCENT_COMPLETE,
percent: Number((e.target as HTMLInputElement).value),
});
dispatch({ type: UNSET_FOCUS });
},
setPosition: e => {
dispatch({ type: SET_FOCUS, input: "position" });
dispatch({
type: SET_SCRUB_POSITION,
position: Number((e.target as HTMLInputElement).value),
});
},
});

export default connect(
mapStateToProps,
mapDispatchToProps
)(Position);
export default Position;
65 changes: 24 additions & 41 deletions js/components/MainWindow/Repeat.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,33 @@
import React from "react";
import { connect } from "react-redux";
import classnames from "classnames";
import { toggleRepeat } from "../../actionCreators";
import * as Actions from "../../actionCreators";
import * as Selectors from "../../selectors";
import ContextMenuWraper from "../ContextMenuWrapper";
import { Node } from "../ContextMenu";
import { AppState, Dispatch } from "../../types";
import { useTypedSelector, useActionCreator } from "../../hooks";

interface StateProps {
repeat: boolean;
}

interface DispatchProps {
handleClick(): void;
}

type Props = StateProps & DispatchProps;

const Repeat = ({ repeat, handleClick }: Props) => (
<ContextMenuWraper
renderContents={() => (
<Node
checked={repeat}
label="Repeat"
const Repeat = React.memo(() => {
const repeat = useTypedSelector(Selectors.getRepeat);
const handleClick = useActionCreator(Actions.toggleRepeat);
return (
<ContextMenuWraper
renderContents={() => (
<Node
checked={repeat}
label="Repeat"
onClick={handleClick}
hotkey="(R)"
/>
)}
>
<div
id="repeat"
className={classnames({ selected: repeat })}
onClick={handleClick}
hotkey="(R)"
title="Toggle Repeat"
/>
)}
>
<div
id="repeat"
className={classnames({ selected: repeat })}
onClick={handleClick}
title="Toggle Repeat"
/>
</ContextMenuWraper>
);

const mapStateToProps = (state: AppState): StateProps => ({
repeat: state.media.repeat,
});

const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
handleClick: () => dispatch(toggleRepeat()),
</ContextMenuWraper>
);
});

export default connect(
mapStateToProps,
mapDispatchToProps
)(Repeat);
export default Repeat;
4 changes: 4 additions & 0 deletions js/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ export function useActionCreator<T extends (...args: any[]) => Action | Thunk>(
export function useTypedSelector<T>(selector: (state: AppState) => T): T {
return useSelector(selector);
}

export function useTypedDispatch(): (action: Action | Thunk) => void {
return useDispatch;
}

0 comments on commit 7212b9e

Please sign in to comment.