AlexAegis/loreplotter

View on GitHub
src/app/lore/store/reducers/scene.reducer.ts

Summary

Maintainability
F
3 days
Test Coverage
import { toUnit } from '@app/function';
import { DeltaProperty, OverridableProperty } from '@app/model';
import {
    bakeCursorOverride,
    bakeFrame,
    bakeFrameEnd,
    bakeFrameStart,
    changeCursorBy,
    changeCursorOverrideTo,
    changeFrameBy,
    changePlayDirection,
    changePlaySpeed,
    clearCursorOverride,
    SceneActions,
    setActorObjectSizeBias,
    setAutoLight,
    setDebugMode,
    setDrawHeight,
    setDrawSize,
    setFrameDeltaTo,
    setFrameEndDeltaTo,
    setFrameEndTo,
    setFrameStartDeltaTo,
    setFrameStartTo,
    setFrameTo,
    setInteractionMode,
    setManualLightAlwaysOn,
    setMediaLarge,
    setPlaying,
    setPlayingFailure,
    setPlayingSuccess,
    setPlaySpeed,
    setSidebarOpen,
    toggleAutoLight,
    toggleDebugMode,
    toggleManualLightAlwaysOn,
    togglePlaying,
    toggleSidebarOpen
} from '@lore/store/actions';
import moment from 'moment';

export interface FrameState {
    start: Partial<DeltaProperty>;
    end: Partial<DeltaProperty>;
}

export interface CursorState {
    unix: Partial<OverridableProperty>;
}

export type InteractionMode = 'draw' | 'move' | 'raise' | 'lower';

export interface SceneState {
    loading: boolean;
    playSpeed: number;
    playing: boolean;
    cursor: CursorState;
    frame: FrameState;
    interactionMode: InteractionMode;
    actorObjectSizeBias: number;
    drawSize: number;
    drawHeight: number;
    manualLight: boolean;
    manualLightAlwaysOn: boolean;
    sidebarOpen: boolean;
    debugMode: boolean;
    mediaLarge: boolean; // Large screen or not (Like mobile)
    title: string; // Large screen or not (Like mobile)
}

export const initialSceneState: SceneState = {
    title: 'DAW',
    loading: false,
    playSpeed: 1200,
    playing: false,
    cursor: {
        unix: {
            original: moment().unix(),
            override: undefined
        }
    },
    frame: {
        start: {
            base: moment()
                .subtract(2, 'week')
                .unix(),
            delta: undefined
        },
        end: {
            base: moment()
                .add(2, 'week')
                .unix(),
            delta: undefined
        }
    },
    actorObjectSizeBias: 0,
    interactionMode: 'move',
    drawSize: 10,
    drawHeight: 1,
    manualLight: true,
    debugMode: false,
    manualLightAlwaysOn: false,
    sidebarOpen: window.innerHeight / window.innerHeight >= 1.8,
    mediaLarge: window.innerHeight / window.innerHeight >= 1.8
};

function cursorReducer(cursor: CursorState, action: SceneActions): CursorState {
    switch (action.type) {
        case changeCursorBy.type: {
            return { ...cursor, unix: { ...cursor.unix, original: Math.floor(cursor.unix.original + action.payload) } };
        }
        case changeCursorOverrideTo.type: {
            return { ...cursor, unix: { ...cursor.unix, override: Math.floor(action.payload) } };
        }
        case bakeCursorOverride.type: {
            return {
                ...cursor,
                unix: {
                    ...cursor.unix,
                    original: Math.floor(cursor.unix.override || cursor.unix.original),
                    override: undefined
                }
            };
        }
        case clearCursorOverride.type: {
            return { ...cursor, unix: { ...cursor.unix, override: undefined } };
        }
        default: {
            return cursor;
        }
    }
}

export const MIN_FRAME_SIZE = 86400;

function frameReducer(frame: FrameState, action: SceneActions): FrameState {
    switch (action.type) {
        case setFrameTo.type: {
            const start = action.payload.start || frame.start.base;
            const end = action.payload.end || frame.end.base;

            if (Math.abs(end - start) <= MIN_FRAME_SIZE || end <= start) {
                return frame;
            } else {
                return {
                    ...frame,
                    start: { ...frame.start, base: start },
                    end: { ...frame.end, base: end }
                };
            }
        }
        case setFrameStartTo.type: {
            const start = action.payload || frame.start.base;
            const end = frame.end.base;

            if (Math.abs(end - start) <= MIN_FRAME_SIZE || end <= start) {
                return frame;
            }
            return { ...frame, start: { ...frame.start, base: action.payload || frame.start.base } };
        }
        case setFrameEndTo.type: {
            const start = frame.start.base;
            const end = action.payload || frame.end.base;

            if (Math.abs(end - start) <= MIN_FRAME_SIZE || end <= start) {
                return frame;
            }
            return { ...frame, end: { ...frame.end, base: action.payload || frame.end.base } };
        }
        case setFrameDeltaTo.type: {
            return {
                ...frame,
                start: { ...frame.start, delta: action.payload.start },
                end: { ...frame.end, delta: action.payload.end }
            };
        }
        case setFrameStartDeltaTo.type: {
            return { ...frame, start: { ...frame.start, delta: action.payload } };
        }
        case setFrameEndDeltaTo.type: {
            return { ...frame, end: { ...frame.end, delta: action.payload } };
        }
        case bakeFrame.type: {
            const bakedStart = frame.start.base + frame.start.delta;
            const bakedEnd = frame.end.base + frame.end.delta;
            return {
                ...frame,
                start: { ...frame.start, base: bakedStart, delta: undefined },
                end: { ...frame.end, base: bakedEnd, delta: undefined }
            };
        }
        case bakeFrameStart.type: {
            const bakedStart = frame.start.base + frame.start.delta;
            return { ...frame, start: { ...frame.start, base: bakedStart, delta: undefined } };
        }
        case bakeFrameEnd.type: {
            const bakedEnd = frame.end.base + frame.end.delta;
            return { ...frame, end: { ...frame.end, base: bakedEnd, delta: undefined } };
        }
        case changeFrameBy.type: {
            const start = (action.payload.start || 0) + frame.start.base;
            const end = (action.payload.end || 0) + frame.end.base;

            if (Math.abs(end - start) <= MIN_FRAME_SIZE || end <= start) {
                return frame;
            }
            return {
                ...frame,
                start: { ...frame.start, base: start },
                end: { ...frame.end, base: end }
            };
        }
        default: {
            return frame;
        }
    }
}

export function sceneReducer(state: SceneState = initialSceneState, action: SceneActions): SceneState {
    switch (action.type) {
        case setPlaySpeed.type: {
            return {
                ...state,
                playSpeed: action.payload.retainDirection
                    ? Math.abs(action.payload.speed) * (toUnit(state.playSpeed) || 1)
                    : action.payload.speed
            };
        }
        case changePlaySpeed.type: {
            const target = state.playSpeed + (toUnit(state.playSpeed) || 1) * action.payload;
            return {
                ...state,
                playSpeed: toUnit(state.playSpeed) === toUnit(target) || state.playSpeed === 0 ? target : 0
            };
        }
        case changePlayDirection.type: {
            return {
                ...state,
                playSpeed: (action.payload || -toUnit(state.playSpeed)) * Math.abs(state.playSpeed)
            };
        }
        case setPlaying.type: {
            return { ...state, loading: true };
        }
        case setPlayingSuccess.type: {
            return { ...state, playing: action.payload, loading: false };
        }
        case setPlayingFailure.type: {
            return { ...state, loading: false };
        }
        case togglePlaying.type: {
            return { ...state, playing: !state.playing };
        }
        case setInteractionMode.type: {
            return { ...state, interactionMode: action.payload };
        }
        case setActorObjectSizeBias.type: {
            return { ...state, actorObjectSizeBias: action.payload };
        }
        case setDrawSize.type: {
            return { ...state, drawSize: action.payload };
        }
        case setDrawHeight.type: {
            return { ...state, drawHeight: action.payload };
        }
        case setAutoLight.type: {
            return { ...state, manualLight: action.payload };
        }
        case setManualLightAlwaysOn.type: {
            return { ...state, manualLightAlwaysOn: action.payload };
        }
        case toggleAutoLight.type: {
            return { ...state, manualLight: !state.manualLight };
        }
        case toggleManualLightAlwaysOn.type: {
            return { ...state, manualLightAlwaysOn: !state.manualLightAlwaysOn };
        }
        case setSidebarOpen.type: {
            return { ...state, sidebarOpen: action.payload };
        }
        case toggleSidebarOpen.type: {
            return { ...state, sidebarOpen: !state.sidebarOpen };
        }
        case setMediaLarge.type: {
            return { ...state, mediaLarge: action.payload };
        }
        case setDebugMode.type: {
            return { ...state, debugMode: action.payload };
        }
        case toggleDebugMode.type: {
            return { ...state, debugMode: !state.debugMode };
        }
        case changeCursorBy.type:
        case changeCursorOverrideTo.type:
        case bakeCursorOverride.type:
        case clearCursorOverride.type: {
            return { ...state, cursor: cursorReducer(state.cursor, action) };
        }
        case setFrameTo.type:
        case setFrameStartTo.type:
        case setFrameEndTo.type:
        case setFrameDeltaTo.type:
        case setFrameStartDeltaTo.type:
        case setFrameEndDeltaTo.type:
        case bakeFrame.type:
        case bakeFrameStart.type:
        case bakeFrameEnd.type:
        case changeFrameBy.type: {
            return { ...state, frame: frameReducer(state.frame, action) };
        }
        default: {
            return state;
        }
    }
}