mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-28 20:40:39 +00:00
Change how actions are typed
If we want to use the new version of react-redux we need to be compatible with their types. Sadly it does not seem to be possible to create a Dispatchable type which can be eiher a plain action or a thunk and still be compatible with react-redux's types becuase they use redux's Store type under the hood which assumes you have an object with a string type property.
This commit is contained in:
parent
9c7d6e2bda
commit
c106ac2a52
10 changed files with 120 additions and 136 deletions
|
|
@ -6,7 +6,7 @@ import {
|
|||
SET_BAND_VALUE,
|
||||
SET_EQ_AUTO,
|
||||
} from "../actionTypes";
|
||||
import { Band, Dispatchable } from "../types";
|
||||
import { Band, Thunk, Action } from "../types";
|
||||
|
||||
const BAND_SNAP_DISTANCE = 5;
|
||||
const BAND_MID_POINT_VALUE = 50;
|
||||
|
|
@ -21,11 +21,11 @@ function _snapBandValue(value: number): number {
|
|||
return value;
|
||||
}
|
||||
|
||||
export function setEqBand(band: Band, value: number): Dispatchable {
|
||||
export function setEqBand(band: Band, value: number): Action {
|
||||
return { type: SET_BAND_VALUE, band, value: _snapBandValue(value) };
|
||||
}
|
||||
|
||||
function _setEqTo(value: number): Dispatchable {
|
||||
function _setEqTo(value: number): Thunk {
|
||||
return dispatch => {
|
||||
Object.values(BANDS).forEach(band => {
|
||||
dispatch({
|
||||
|
|
@ -37,23 +37,23 @@ function _setEqTo(value: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function setEqToMax(): Dispatchable {
|
||||
export function setEqToMax(): Thunk {
|
||||
return _setEqTo(100);
|
||||
}
|
||||
|
||||
export function setEqToMid(): Dispatchable {
|
||||
export function setEqToMid(): Thunk {
|
||||
return _setEqTo(50);
|
||||
}
|
||||
|
||||
export function setEqToMin(): Dispatchable {
|
||||
export function setEqToMin(): Thunk {
|
||||
return _setEqTo(0);
|
||||
}
|
||||
|
||||
export function setPreamp(value: number): Dispatchable {
|
||||
export function setPreamp(value: number): Action {
|
||||
return { type: SET_BAND_VALUE, band: "preamp", value: _snapBandValue(value) };
|
||||
}
|
||||
|
||||
export function toggleEq(): Dispatchable {
|
||||
export function toggleEq(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
if (getState().equalizer.on) {
|
||||
dispatch({ type: SET_EQ_OFF });
|
||||
|
|
@ -63,7 +63,7 @@ export function toggleEq(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function toggleEqAuto(): Dispatchable {
|
||||
export function toggleEqAuto(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({ type: SET_EQ_AUTO, value: !getState().equalizer.auto });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import LoadQueue from "../loadQueue";
|
|||
|
||||
import { removeAllTracks } from "./playlist";
|
||||
import { setPreamp, setEqBand } from "./equalizer";
|
||||
import { LoadStyle, Dispatchable, Track, EqfPreset } from "../types";
|
||||
import { LoadStyle, Thunk, Track, EqfPreset, SkinData } from "../types";
|
||||
|
||||
// Lower is better
|
||||
const DURATION_VISIBLE_PRIORITY = 5;
|
||||
|
|
@ -47,7 +47,7 @@ export function addTracksFromReferences(
|
|||
fileReferences: FileList,
|
||||
loadStyle: LoadStyle,
|
||||
atIndex: number | undefined
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
const tracks: Track[] = Array.from(fileReferences).map(file => ({
|
||||
blob: file,
|
||||
defaultName: file.name,
|
||||
|
|
@ -61,7 +61,7 @@ export function loadFilesFromReferences(
|
|||
fileReferences: FileList,
|
||||
loadStyle: LoadStyle = LOAD_STYLE.PLAY,
|
||||
atIndex: number | undefined = undefined
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return dispatch => {
|
||||
if (fileReferences.length < 1) {
|
||||
return;
|
||||
|
|
@ -79,9 +79,7 @@ export function loadFilesFromReferences(
|
|||
};
|
||||
}
|
||||
|
||||
export function setSkinFromBlob(
|
||||
arrayBuffer: Blob | Promise<Blob>
|
||||
): Dispatchable {
|
||||
export function setSkinFromBlob(blob: Blob | Promise<Blob>): Thunk {
|
||||
return async (dispatch, getState, { requireJSZip }) => {
|
||||
if (!requireJSZip) {
|
||||
alert("Webamp has not been configured to support custom skins.");
|
||||
|
|
@ -98,8 +96,7 @@ export function setSkinFromBlob(
|
|||
return;
|
||||
}
|
||||
try {
|
||||
const skinData = await skinParser(arrayBuffer, JSZip);
|
||||
// @ts-ignore TODO: We still need to type skinParser.
|
||||
const skinData = await skinParser(blob, JSZip);
|
||||
dispatch({
|
||||
type: SET_SKIN_DATA,
|
||||
data: {
|
||||
|
|
@ -110,7 +107,7 @@ export function setSkinFromBlob(
|
|||
skinRegion: skinData.region,
|
||||
skinGenLetterWidths: skinData.genLetterWidths,
|
||||
skinGenExColors: skinData.genExColors,
|
||||
},
|
||||
} as SkinData,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
@ -120,7 +117,7 @@ export function setSkinFromBlob(
|
|||
};
|
||||
}
|
||||
|
||||
export function setSkinFromUrl(url: string): Dispatchable {
|
||||
export function setSkinFromUrl(url: string): Thunk {
|
||||
return async dispatch => {
|
||||
dispatch({ type: LOADING });
|
||||
try {
|
||||
|
|
@ -140,18 +137,18 @@ export function setSkinFromUrl(url: string): Dispatchable {
|
|||
// This function is private, since Winamp consumers can provide means for
|
||||
// opening files via other methods. Only use the file type specific
|
||||
// versions below, since they can defer to the user-defined behavior.
|
||||
function _openFileDialog(accept: string | null): Dispatchable {
|
||||
function _openFileDialog(accept: string | null): Thunk {
|
||||
return async dispatch => {
|
||||
const fileReferences = await promptForFileReferences({ accept });
|
||||
dispatch(loadFilesFromReferences(fileReferences));
|
||||
};
|
||||
}
|
||||
|
||||
export function openEqfFileDialog(): Dispatchable {
|
||||
export function openEqfFileDialog(): Thunk {
|
||||
return _openFileDialog(".eqf");
|
||||
}
|
||||
|
||||
export function openMediaFileDialog(): Dispatchable {
|
||||
export function openMediaFileDialog(): Thunk {
|
||||
return _openFileDialog(null);
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +156,7 @@ export function openSkinFileDialog() {
|
|||
return _openFileDialog(".zip, .wsz");
|
||||
}
|
||||
|
||||
export function fetchMediaDuration(url: string, id: number): Dispatchable {
|
||||
export function fetchMediaDuration(url: string, id: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
loadQueue.push(
|
||||
async () => {
|
||||
|
|
@ -185,7 +182,7 @@ export function loadMedia(
|
|||
e: React.DragEvent<HTMLDivElement>,
|
||||
loadStyle: LoadStyle = LOAD_STYLE.NONE,
|
||||
atIndex = 0
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
const { files } = e.dataTransfer;
|
||||
return async (dispatch, getState, { handleTrackDropEvent }) => {
|
||||
if (handleTrackDropEvent) {
|
||||
|
|
@ -204,7 +201,7 @@ export function loadMediaFiles(
|
|||
tracks: Track[],
|
||||
loadStyle: LoadStyle = LOAD_STYLE.NONE,
|
||||
atIndex = 0
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return dispatch => {
|
||||
if (loadStyle === LOAD_STYLE.PLAY) {
|
||||
// I'm the worst. It just so happens that in every case that we autoPlay,
|
||||
|
|
@ -222,7 +219,7 @@ export function loadMediaFile(
|
|||
track: Track,
|
||||
priority: LoadStyle = LOAD_STYLE.NONE,
|
||||
atIndex = 0
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return dispatch => {
|
||||
const id = Utils.uniqueId();
|
||||
const { defaultName, metaData, duration } = track;
|
||||
|
|
@ -285,7 +282,7 @@ export function loadMediaFile(
|
|||
};
|
||||
}
|
||||
|
||||
function queueFetchingMediaTags(id: number): Dispatchable {
|
||||
function queueFetchingMediaTags(id: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const track = getTracks(getState())[id];
|
||||
loadQueue.push(
|
||||
|
|
@ -300,7 +297,7 @@ function queueFetchingMediaTags(id: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function fetchMediaTags(file: string | Blob, id: number): Dispatchable {
|
||||
export function fetchMediaTags(file: string | Blob, id: number): Thunk {
|
||||
return async (dispatch, getState, { requireMusicMetadata }) => {
|
||||
dispatch({ type: MEDIA_TAG_REQUEST_INITIALIZED, id });
|
||||
|
||||
|
|
@ -332,7 +329,7 @@ export function fetchMediaTags(file: string | Blob, id: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function setEqFromFileReference(fileReference: File): Dispatchable {
|
||||
export function setEqFromFileReference(fileReference: File): Thunk {
|
||||
return async dispatch => {
|
||||
const arrayBuffer = await genArrayBufferFromFileReference(fileReference);
|
||||
const eqf = parser(arrayBuffer);
|
||||
|
|
@ -341,7 +338,7 @@ export function setEqFromFileReference(fileReference: File): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function setEqFromObject(preset: EqfPreset): Dispatchable {
|
||||
export function setEqFromObject(preset: EqfPreset): Thunk {
|
||||
return dispatch => {
|
||||
dispatch(setPreamp(Utils.normalizeEqBand(preset.preamp)));
|
||||
BANDS.forEach(band => {
|
||||
|
|
@ -351,7 +348,7 @@ export function setEqFromObject(preset: EqfPreset): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function downloadPreset(): Dispatchable {
|
||||
export function downloadPreset(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const data = getEqfData(state);
|
||||
|
|
@ -362,7 +359,7 @@ export function downloadPreset(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function downloadHtmlPlaylist(): Dispatchable {
|
||||
export function downloadHtmlPlaylist(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const uri = getPlaylistURL(getState());
|
||||
Utils.downloadURI(uri, "Winamp Playlist.html");
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
SET_MILKDROP_FULLSCREEN,
|
||||
} from "../actionTypes";
|
||||
import { WINDOWS } from "../constants";
|
||||
import { Dispatchable } from "../types";
|
||||
import { Thunk, Action } from "../types";
|
||||
import { SerializedStateV1 } from "../serializedStates/v1Types";
|
||||
import * as Selectors from "../selectors";
|
||||
import { ensureWindowsAreOnScreen, showWindow, hideWindow } from "./windows";
|
||||
|
|
@ -112,7 +112,7 @@ export {
|
|||
scheduleMilkdropMessage,
|
||||
} from "./milkdrop";
|
||||
|
||||
export function close(): Dispatchable {
|
||||
export function close(): Thunk {
|
||||
return dispatch => {
|
||||
// TODO: This could probably be improved by adding a "PREVENT_CLOSE" action
|
||||
// or something, but this works okay for now.
|
||||
|
|
@ -128,41 +128,41 @@ export function close(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function open(): Dispatchable {
|
||||
export function open(): Action {
|
||||
return { type: OPEN_WINAMP };
|
||||
}
|
||||
|
||||
export function toggleVisualizerStyle(): Dispatchable {
|
||||
export function toggleVisualizerStyle(): Action {
|
||||
return { type: TOGGLE_VISUALIZER_STYLE };
|
||||
}
|
||||
|
||||
export function minimize(): Dispatchable {
|
||||
export function minimize(): Action {
|
||||
return { type: MINIMIZE_WINAMP };
|
||||
}
|
||||
|
||||
export function setFocus(input: string): Dispatchable {
|
||||
export function setFocus(input: string): Action {
|
||||
return { type: SET_FOCUS, input };
|
||||
}
|
||||
|
||||
export function unsetFocus(): Dispatchable {
|
||||
export function unsetFocus(): Action {
|
||||
return { type: UNSET_FOCUS };
|
||||
}
|
||||
|
||||
export function loadSerializedState(
|
||||
// In the future this type should be the union of all versioned types.
|
||||
serializedState: SerializedStateV1
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return dispatch => {
|
||||
dispatch({ type: LOAD_SERIALIZED_STATE, serializedState });
|
||||
dispatch(ensureWindowsAreOnScreen());
|
||||
};
|
||||
}
|
||||
|
||||
export function loadDefaultSkin(): Dispatchable {
|
||||
export function loadDefaultSkin(): Action {
|
||||
return { type: LOAD_DEFAULT_SKIN };
|
||||
}
|
||||
|
||||
export function toggleMilkdropDesktop(): Dispatchable {
|
||||
export function toggleMilkdropDesktop(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
if (Selectors.getMilkdropDesktopEnabled(getState())) {
|
||||
dispatch(showWindow(WINDOWS.MILKDROP));
|
||||
|
|
@ -174,10 +174,10 @@ export function toggleMilkdropDesktop(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function setMilkdropFullscreen(enabled: boolean): Dispatchable {
|
||||
export function setMilkdropFullscreen(enabled: boolean): Action {
|
||||
return { type: SET_MILKDROP_FULLSCREEN, enabled };
|
||||
}
|
||||
export function toggleMilkdropFullscreen(): Dispatchable {
|
||||
export function toggleMilkdropFullscreen(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(
|
||||
setMilkdropFullscreen(!Selectors.getMilkdropFullscreenEnabled(getState()))
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import {
|
|||
|
||||
import { MEDIA_STATUS } from "../constants";
|
||||
import { openMediaFileDialog } from "./";
|
||||
import { GetState, Dispatch, Dispatchable } from "../types";
|
||||
import { GetState, Dispatch, Thunk, Action } from "../types";
|
||||
import * as Selectors from "../selectors";
|
||||
|
||||
export function playTrack(id: number): Dispatchable {
|
||||
export function playTrack(id: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const isStopped = Selectors.getMediaStatus(state) === MEDIA_STATUS.STOPPED;
|
||||
|
|
@ -31,7 +31,7 @@ export function playTrack(id: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function play(): Dispatchable {
|
||||
export function play(): Thunk {
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
if (
|
||||
|
|
@ -46,7 +46,7 @@ export function play(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function pause(): Dispatchable {
|
||||
export function pause(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const { status } = getState().media;
|
||||
if (status === MEDIA_STATUS.PLAYING) {
|
||||
|
|
@ -57,11 +57,11 @@ export function pause(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function stop(): Dispatchable {
|
||||
export function stop(): Action {
|
||||
return { type: STOP };
|
||||
}
|
||||
|
||||
export function nextN(n: number): Dispatchable {
|
||||
export function nextN(n: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const nextTrackId = Selectors.getNextTrackId(getState(), n);
|
||||
if (nextTrackId == null) {
|
||||
|
|
@ -72,15 +72,15 @@ export function nextN(n: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function next(): Dispatchable {
|
||||
export function next(): Thunk {
|
||||
return nextN(1);
|
||||
}
|
||||
|
||||
export function previous(): Dispatchable {
|
||||
export function previous(): Thunk {
|
||||
return nextN(-1);
|
||||
}
|
||||
|
||||
export function seekToTime(seconds: number): Dispatchable {
|
||||
export function seekToTime(seconds: number): Thunk {
|
||||
return function(dispatch, getState) {
|
||||
const state = getState();
|
||||
const duration = Selectors.getDuration(state);
|
||||
|
|
@ -93,34 +93,32 @@ export function seekToTime(seconds: number): Dispatchable {
|
|||
});
|
||||
};
|
||||
}
|
||||
export function seekForward(seconds: number): Dispatchable {
|
||||
export function seekForward(seconds: number): Thunk {
|
||||
return function(dispatch, getState) {
|
||||
const timeElapsed = Selectors.getTimeElapsed(getState());
|
||||
dispatch(seekToTime(timeElapsed + seconds));
|
||||
};
|
||||
}
|
||||
|
||||
export function seekBackward(seconds: number): Dispatchable {
|
||||
export function seekBackward(seconds: number): Thunk {
|
||||
return seekForward(-seconds);
|
||||
}
|
||||
|
||||
export function setVolume(volume: number): Dispatchable {
|
||||
export function setVolume(volume: number): Action {
|
||||
return {
|
||||
type: SET_VOLUME,
|
||||
volume: clamp(volume, 0, 100),
|
||||
};
|
||||
}
|
||||
|
||||
export function adjustVolume(volumeDiff: number): Dispatchable {
|
||||
export function adjustVolume(volumeDiff: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const currentVolume = getState().media.volume;
|
||||
return dispatch(setVolume(currentVolume + volumeDiff));
|
||||
};
|
||||
}
|
||||
|
||||
export function scrollVolume(
|
||||
e: React.WheelEvent<HTMLDivElement>
|
||||
): Dispatchable {
|
||||
export function scrollVolume(e: React.WheelEvent<HTMLDivElement>): Thunk {
|
||||
e.preventDefault();
|
||||
return (dispatch, getState) => {
|
||||
const currentVolume = getState().media.volume;
|
||||
|
|
@ -129,7 +127,7 @@ export function scrollVolume(
|
|||
};
|
||||
}
|
||||
|
||||
export function setBalance(balance: number): Dispatchable {
|
||||
export function setBalance(balance: number): Action {
|
||||
balance = clamp(balance, -100, 100);
|
||||
// The balance clips to the center
|
||||
if (Math.abs(balance) < 25) {
|
||||
|
|
@ -141,14 +139,14 @@ export function setBalance(balance: number): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function toggleRepeat(): Dispatchable {
|
||||
export function toggleRepeat(): Action {
|
||||
return { type: TOGGLE_REPEAT };
|
||||
}
|
||||
|
||||
export function toggleShuffle(): Dispatchable {
|
||||
export function toggleShuffle(): Action {
|
||||
return { type: TOGGLE_SHUFFLE };
|
||||
}
|
||||
|
||||
export function toggleTimeMode(): Dispatchable {
|
||||
export function toggleTimeMode(): Action {
|
||||
return { type: TOGGLE_TIME_MODE };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,12 @@ import {
|
|||
} from "../actionTypes";
|
||||
import * as Selectors from "../selectors";
|
||||
import {
|
||||
Dispatchable,
|
||||
TransitionType,
|
||||
Preset,
|
||||
ButterchurnOptions,
|
||||
StatePreset,
|
||||
Thunk,
|
||||
Action,
|
||||
} from "../types";
|
||||
import * as FileUtils from "../fileUtils";
|
||||
|
||||
|
|
@ -46,9 +47,7 @@ function normalizePresetTypes(preset: Preset): StatePreset {
|
|||
throw new Error("Invalid preset object");
|
||||
}
|
||||
|
||||
export function initializePresets(
|
||||
presetOptions: ButterchurnOptions
|
||||
): Dispatchable {
|
||||
export function initializePresets(presetOptions: ButterchurnOptions): Thunk {
|
||||
return async dispatch => {
|
||||
const { getPresets, importButterchurn } = presetOptions;
|
||||
importButterchurn().then(butterchurn => {
|
||||
|
|
@ -61,7 +60,7 @@ export function initializePresets(
|
|||
};
|
||||
}
|
||||
|
||||
export function loadPresets(presets: StatePreset[]): Dispatchable {
|
||||
export function loadPresets(presets: StatePreset[]): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const presetsLength = state.milkdrop.presets.length;
|
||||
|
|
@ -76,7 +75,7 @@ export function loadPresets(presets: StatePreset[]): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function appendPresetFileList(fileList: FileList): Dispatchable {
|
||||
export function appendPresetFileList(fileList: FileList): Thunk {
|
||||
return async (dispatch, getState, { convertPreset }) => {
|
||||
const presets: StatePreset[] = Array.from(fileList)
|
||||
.map(file => {
|
||||
|
|
@ -114,7 +113,7 @@ export function appendPresetFileList(fileList: FileList): Dispatchable {
|
|||
|
||||
export function selectNextPreset(
|
||||
transitionType: TransitionType = TransitionType.DEFAULT
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
if (Selectors.getRandomizePresets(state)) {
|
||||
|
|
@ -131,7 +130,7 @@ export function selectNextPreset(
|
|||
|
||||
export function selectPreviousPreset(
|
||||
transitionType: TransitionType = TransitionType.DEFAULT
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const { presetHistory } = state.milkdrop;
|
||||
|
|
@ -147,7 +146,7 @@ export function selectPreviousPreset(
|
|||
|
||||
export function selectRandomPreset(
|
||||
transitionType: TransitionType = TransitionType.DEFAULT
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
// TODO: Make this a selector.
|
||||
|
|
@ -164,7 +163,7 @@ export function requestPresetAtIndex(
|
|||
index: number,
|
||||
transitionType: TransitionType,
|
||||
addToHistory: boolean
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const preset = state.milkdrop.presets[index];
|
||||
|
|
@ -187,22 +186,22 @@ export function requestPresetAtIndex(
|
|||
};
|
||||
}
|
||||
|
||||
export function handlePresetDrop(e: React.DragEvent): Dispatchable {
|
||||
export function handlePresetDrop(e: React.DragEvent): Thunk {
|
||||
return appendPresetFileList(e.dataTransfer.files);
|
||||
}
|
||||
|
||||
export function togglePresetOverlay(): Dispatchable {
|
||||
export function togglePresetOverlay(): Action {
|
||||
return { type: TOGGLE_PRESET_OVERLAY };
|
||||
}
|
||||
|
||||
export function toggleRandomizePresets(): Dispatchable {
|
||||
export function toggleRandomizePresets(): Action {
|
||||
return { type: TOGGLE_RANDOMIZE_PRESETS };
|
||||
}
|
||||
|
||||
export function togglePresetCycling(): Dispatchable {
|
||||
export function togglePresetCycling(): Action {
|
||||
return { type: TOGGLE_PRESET_CYCLING };
|
||||
}
|
||||
|
||||
export function scheduleMilkdropMessage(message: string): Dispatchable {
|
||||
export function scheduleMilkdropMessage(message: string): Action {
|
||||
return { type: SCHEDULE_MILKDROP_MESSAGE, message };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ import {
|
|||
SET_PLAYLIST_SCROLL_POSITION,
|
||||
DRAG_SELECTED,
|
||||
} from "../actionTypes";
|
||||
import { Dispatchable } from "../types";
|
||||
import { Thunk, Action } from "../types";
|
||||
|
||||
export function cropPlaylist(): Dispatchable {
|
||||
export function cropPlaylist(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
if (Selectors.getSelectedTrackObjects(state).length === 0) {
|
||||
|
|
@ -33,7 +33,7 @@ export function cropPlaylist(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function removeSelectedTracks(): Dispatchable {
|
||||
export function removeSelectedTracks(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: REMOVE_TRACKS,
|
||||
|
|
@ -44,7 +44,7 @@ export function removeSelectedTracks(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function removeAllTracks(): Dispatchable {
|
||||
export function removeAllTracks(): Thunk {
|
||||
return dispatch => {
|
||||
// It's a bit funky that we need to do both of these.
|
||||
dispatch({ type: STOP });
|
||||
|
|
@ -52,15 +52,15 @@ export function removeAllTracks(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function reverseList(): Dispatchable {
|
||||
export function reverseList(): Action {
|
||||
return { type: REVERSE_LIST };
|
||||
}
|
||||
|
||||
export function randomizeList(): Dispatchable {
|
||||
export function randomizeList(): Action {
|
||||
return { type: RANDOMIZE_LIST };
|
||||
}
|
||||
|
||||
export function sortListByTitle(): Dispatchable {
|
||||
export function sortListByTitle(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const tracks = Selectors.getTracks(state);
|
||||
|
|
@ -71,11 +71,11 @@ export function sortListByTitle(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function setPlaylistScrollPosition(position: number): Dispatchable {
|
||||
export function setPlaylistScrollPosition(position: number): Action {
|
||||
return { type: SET_PLAYLIST_SCROLL_POSITION, position };
|
||||
}
|
||||
|
||||
export function scrollNTracks(n: number): Dispatchable {
|
||||
export function scrollNTracks(n: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const overflow = Selectors.getOverflowTrackCount(state);
|
||||
|
|
@ -90,7 +90,7 @@ export function scrollNTracks(n: number): Dispatchable {
|
|||
|
||||
export function scrollPlaylistByDelta(
|
||||
e: React.WheelEvent<HTMLDivElement>
|
||||
): Dispatchable {
|
||||
): Thunk {
|
||||
e.preventDefault();
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
|
@ -110,15 +110,15 @@ export function scrollPlaylistByDelta(
|
|||
};
|
||||
}
|
||||
|
||||
export function scrollUpFourTracks(): Dispatchable {
|
||||
export function scrollUpFourTracks(): Thunk {
|
||||
return scrollNTracks(-4);
|
||||
}
|
||||
|
||||
export function scrollDownFourTracks(): Dispatchable {
|
||||
export function scrollDownFourTracks(): Thunk {
|
||||
return scrollNTracks(4);
|
||||
}
|
||||
|
||||
export function dragSelected(offset: number): Dispatchable {
|
||||
export function dragSelected(offset: number): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const tracks = Selectors.getTracks(state);
|
||||
|
|
|
|||
|
|
@ -17,13 +17,7 @@ import {
|
|||
|
||||
import { getPositionDiff, SizeDiff } from "../resizeUtils";
|
||||
import { applyDiff } from "../snapUtils";
|
||||
import {
|
||||
Action,
|
||||
Dispatchable,
|
||||
WindowId,
|
||||
WindowPositions,
|
||||
Dispatch,
|
||||
} from "../types";
|
||||
import { Action, Thunk, WindowId, WindowPositions, Dispatch } from "../types";
|
||||
|
||||
// Dispatch an action and, if needed rearrange the windows to preserve
|
||||
// the existing edge relationship.
|
||||
|
|
@ -31,7 +25,7 @@ import {
|
|||
// Works by checking the edges before the action is dispatched. Then,
|
||||
// after disatching, calculating what position change would be required
|
||||
// to restore those relationships.
|
||||
function withWindowGraphIntegrity(action: Action): Dispatchable {
|
||||
function withWindowGraphIntegrity(action: Action): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const graph = Selectors.getWindowGraph(state);
|
||||
|
|
@ -61,70 +55,70 @@ function withWindowGraphIntegrity(action: Action): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function toggleDoubleSizeMode(): Dispatchable {
|
||||
export function toggleDoubleSizeMode(): Thunk {
|
||||
return withWindowGraphIntegrity({ type: TOGGLE_DOUBLESIZE_MODE });
|
||||
}
|
||||
|
||||
export function toggleLlamaMode(): Dispatchable {
|
||||
export function toggleLlamaMode(): Action {
|
||||
return { type: TOGGLE_LLAMA_MODE };
|
||||
}
|
||||
|
||||
export function toggleEqualizerShadeMode(): Dispatchable {
|
||||
export function toggleEqualizerShadeMode(): Thunk {
|
||||
return withWindowGraphIntegrity({
|
||||
type: TOGGLE_WINDOW_SHADE_MODE,
|
||||
windowId: "equalizer",
|
||||
});
|
||||
}
|
||||
|
||||
export function toggleMainWindowShadeMode(): Dispatchable {
|
||||
export function toggleMainWindowShadeMode(): Thunk {
|
||||
return withWindowGraphIntegrity({
|
||||
type: TOGGLE_WINDOW_SHADE_MODE,
|
||||
windowId: "main",
|
||||
});
|
||||
}
|
||||
|
||||
export function togglePlaylistShadeMode(): Dispatchable {
|
||||
export function togglePlaylistShadeMode(): Thunk {
|
||||
return withWindowGraphIntegrity({
|
||||
type: TOGGLE_WINDOW_SHADE_MODE,
|
||||
windowId: "playlist",
|
||||
});
|
||||
}
|
||||
|
||||
export function closeWindow(windowId: WindowId): Dispatchable {
|
||||
export function closeWindow(windowId: WindowId): Action {
|
||||
return { type: CLOSE_WINDOW, windowId };
|
||||
}
|
||||
|
||||
export function hideWindow(windowId: WindowId): Dispatchable {
|
||||
export function hideWindow(windowId: WindowId): Action {
|
||||
return { type: SET_WINDOW_VISIBILITY, windowId, hidden: true };
|
||||
}
|
||||
|
||||
export function showWindow(windowId: WindowId): Dispatchable {
|
||||
export function showWindow(windowId: WindowId): Action {
|
||||
return { type: SET_WINDOW_VISIBILITY, windowId, hidden: false };
|
||||
}
|
||||
|
||||
export function setFocusedWindow(window: WindowId): Dispatchable {
|
||||
export function setFocusedWindow(window: WindowId): Action {
|
||||
return { type: SET_FOCUSED_WINDOW, window };
|
||||
}
|
||||
|
||||
export function setWindowSize(
|
||||
windowId: WindowId,
|
||||
size: [number, number]
|
||||
): Dispatchable {
|
||||
): Action {
|
||||
return { type: WINDOW_SIZE_CHANGED, windowId, size };
|
||||
}
|
||||
|
||||
export function toggleWindow(windowId: WindowId): Dispatchable {
|
||||
export function toggleWindow(windowId: WindowId): Action {
|
||||
return { type: TOGGLE_WINDOW, windowId };
|
||||
}
|
||||
|
||||
export function updateWindowPositions(
|
||||
positions: WindowPositions,
|
||||
absolute?: boolean
|
||||
): Dispatchable {
|
||||
): Action {
|
||||
return { type: UPDATE_WINDOW_POSITIONS, positions, absolute };
|
||||
}
|
||||
|
||||
export function centerWindowsInContainer(container: HTMLElement): Dispatchable {
|
||||
export function centerWindowsInContainer(container: HTMLElement): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
if (!Selectors.getPositionsAreRelative(getState())) {
|
||||
return;
|
||||
|
|
@ -135,7 +129,7 @@ export function centerWindowsInContainer(container: HTMLElement): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function centerWindowsInView(): Dispatchable {
|
||||
export function centerWindowsInView(): Thunk {
|
||||
return centerWindows({
|
||||
left: window.scrollX,
|
||||
top: window.scrollY,
|
||||
|
|
@ -149,7 +143,7 @@ export function centerWindows(box: {
|
|||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}): Dispatchable {
|
||||
}): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const windowsInfo = Selectors.getWindowsInfo(state);
|
||||
|
|
@ -188,18 +182,18 @@ export function centerWindows(box: {
|
|||
export function browserWindowSizeChanged(size: {
|
||||
height: number;
|
||||
width: number;
|
||||
}) {
|
||||
}): Thunk {
|
||||
return (dispatch: Dispatch) => {
|
||||
dispatch({ type: BROWSER_WINDOW_SIZE_CHANGED, ...size });
|
||||
dispatch(ensureWindowsAreOnScreen());
|
||||
};
|
||||
}
|
||||
|
||||
export function resetWindowSizes(): Dispatchable {
|
||||
export function resetWindowSizes(): Action {
|
||||
return { type: RESET_WINDOW_SIZES };
|
||||
}
|
||||
|
||||
export function stackWindows(): Dispatchable {
|
||||
export function stackWindows(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(
|
||||
updateWindowPositions(Selectors.getStackedLayoutPositions(getState()))
|
||||
|
|
@ -207,7 +201,7 @@ export function stackWindows(): Dispatchable {
|
|||
};
|
||||
}
|
||||
|
||||
export function ensureWindowsAreOnScreen(): Dispatchable {
|
||||
export function ensureWindowsAreOnScreen(): Thunk {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { createStore, applyMiddleware } from "redux";
|
|||
import thunk from "redux-thunk";
|
||||
import { LOAD_SERIALIZED_STATE } from "./actionTypes";
|
||||
import { SerializedStateV1 } from "./serializedStates/v1Types";
|
||||
import { Dispatchable, AppState } from "./types";
|
||||
import { AppState, Action } from "./types";
|
||||
|
||||
function fixturePath(name: string) {
|
||||
return path.join(
|
||||
|
|
@ -29,7 +29,7 @@ function readFixture(name: string): SerializedStateV1 {
|
|||
|
||||
interface SerializationTestParams<T> {
|
||||
name: string;
|
||||
action: Dispatchable;
|
||||
action: Action;
|
||||
selector(state: AppState): T;
|
||||
expected: T;
|
||||
}
|
||||
|
|
@ -132,6 +132,7 @@ describe("can serialize", () => {
|
|||
/* Equalizer */
|
||||
testSerialization({
|
||||
name: "equalizer on",
|
||||
// @ts-ignore
|
||||
action: Actions.toggleEq(),
|
||||
selector: Selectors.getEqualizerEnabled,
|
||||
expected: false,
|
||||
|
|
@ -139,6 +140,7 @@ describe("can serialize", () => {
|
|||
|
||||
testSerialization({
|
||||
name: "equalizer auto",
|
||||
// @ts-ignore
|
||||
action: Actions.toggleEqAuto(),
|
||||
selector: Selectors.getEqualizerAuto,
|
||||
expected: true,
|
||||
|
|
@ -161,6 +163,7 @@ describe("can serialize", () => {
|
|||
/* Display */
|
||||
testSerialization({
|
||||
name: "double mode",
|
||||
// @ts-ignore
|
||||
action: Actions.toggleDoubleSizeMode(),
|
||||
selector: Selectors.getDoubled,
|
||||
expected: true,
|
||||
|
|
@ -207,8 +210,9 @@ describe("can serialize", () => {
|
|||
|
||||
testSerialization({
|
||||
name: "window shade",
|
||||
// @ts-ignore
|
||||
action: Actions.toggleEqualizerShadeMode(),
|
||||
selector: state => Selectors.getWindowShade(state)("equalizer"),
|
||||
selector: state => Boolean(Selectors.getWindowShade(state)("equalizer")),
|
||||
expected: true,
|
||||
});
|
||||
|
||||
|
|
|
|||
17
js/types.ts
17
js/types.ts
|
|
@ -2,6 +2,7 @@ import { PlaylistState } from "./reducers/playlist";
|
|||
import { SettingsState } from "./reducers/settings";
|
||||
import { UserInputState } from "./reducers/userInput";
|
||||
import { MediaState } from "./reducers/media";
|
||||
import { ThunkDispatch, ThunkAction } from "redux-thunk";
|
||||
import { DisplayState } from "./reducers/display";
|
||||
import { WindowsState, WindowPositions } from "./reducers/windows";
|
||||
import { EqualizerState } from "./reducers/equalizer";
|
||||
|
|
@ -110,7 +111,7 @@ export interface SkinGenExColors {
|
|||
export type WindowId = string;
|
||||
|
||||
// TODO: Fill these out once we actually use them.
|
||||
type SkinData = {
|
||||
export type SkinData = {
|
||||
skinImages: SkinImages;
|
||||
skinColors: string[];
|
||||
skinPlaylistStyle: PlaylistStyle;
|
||||
|
|
@ -661,19 +662,9 @@ export interface Extras {
|
|||
|
||||
export type GetState = () => AppState;
|
||||
|
||||
export type Thunk = (
|
||||
dispatch: Dispatch,
|
||||
getState: GetState,
|
||||
extras: Extras
|
||||
) => void | Promise<void>;
|
||||
export type Thunk = ThunkAction<void, AppState, Extras, Action>;
|
||||
|
||||
export type Dispatchable = Action | Thunk;
|
||||
|
||||
export interface DispatchObject {
|
||||
[prop: string]: (...args: any[]) => Dispatchable;
|
||||
}
|
||||
|
||||
export type Dispatch = (action: Dispatchable) => void;
|
||||
export type Dispatch = ThunkDispatch<AppState, Extras, Action>;
|
||||
|
||||
export type Reducer = (state: AppState, action: Action) => AppState;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
Middleware,
|
||||
WindowPosition,
|
||||
ButterchurnOptions,
|
||||
Action,
|
||||
} from "./types";
|
||||
import getStore from "./store";
|
||||
import App from "./components/App";
|
||||
|
|
@ -418,7 +419,7 @@ class Winamp {
|
|||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={this.store}>
|
||||
<Provider<Action> store={this.store}>
|
||||
<App
|
||||
media={this.media}
|
||||
container={node}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue