undo and redo functionality

This commit is contained in:
Fedor Katurov 2020-01-22 11:53:19 +07:00
parent 8df7d7d27d
commit 4915744c84
11 changed files with 216 additions and 47 deletions

View file

@ -123,9 +123,26 @@ export const editorSetRouter = (router: Partial<IEditorState['router']>) => ({
export const editorSetNominatim = (nominatim: Partial<IEditorState['nominatim']>) => ({
type: EDITOR_ACTIONS.SET_NOMINATIM,
nominatim,
})
});
export const editorSearchNominatim = (search: IEditorState['nominatim']['search']) => ({
type: EDITOR_ACTIONS.SEARCH_NOMINATIM,
search,
})
});
export const editorSetHistory = (history: Partial<IEditorState['history']>) => ({
type: EDITOR_ACTIONS.SET_HISTORY,
history,
});
export const editorUndo = () => ({
type: EDITOR_ACTIONS.UNDO,
});
export const editorRedo = () => ({
type: EDITOR_ACTIONS.REDO,
});
export const editorCaptureHistory = () => ({
type: EDITOR_ACTIONS.CAPTURE_HIPSTORY,
});

View file

@ -1,5 +1,7 @@
const P = 'EDITOR';
export const EDITOR_HISTORY_LENGTH = 100;
export const EDITOR_ACTIONS = {
SET_EDITING: `${P}-SET_EDITING`,
CHANGE_MODE: `${P}-CHANGE_MODE`,
@ -47,4 +49,9 @@ export const EDITOR_ACTIONS = {
SET_ROUTER: `${P}-SET_ROUTER`,
SET_NOMINATIM: `${P}-SET_NOMINATIM`,
SEARCH_NOMINATIM: `${P}-SEARCH_NOMINATIM`,
SET_HISTORY: `${P}-SET_HISTORY`,
UNDO: `${P}-UNDO`,
REDO: `${P}-REDO`,
CAPTURE_HIPSTORY: `${P}-CAPTURE_HIPSTORY`,
};

View file

@ -168,6 +168,17 @@ const setNominatim = (
},
});
const setHistory = (
state,
{ history }: ReturnType<typeof ACTIONS.editorSetHistory>
): IEditorState => ({
...state,
history: {
...state.history,
...history,
},
});
export const EDITOR_HANDLERS = {
[EDITOR_ACTIONS.SET_EDITING]: setEditing,
[EDITOR_ACTIONS.SET_CHANGED]: setChanged,
@ -196,4 +207,6 @@ export const EDITOR_HANDLERS = {
[EDITOR_ACTIONS.SET_IS_ROUTING]: setIsRouting,
[EDITOR_ACTIONS.SET_ROUTER]: setRouter,
[EDITOR_ACTIONS.SET_NOMINATIM]: setNominatim,
[EDITOR_ACTIONS.SET_HISTORY]: setHistory,
};

View file

@ -4,6 +4,12 @@ import { MODES } from '~/constants/modes';
import { EDITOR_HANDLERS } from './handlers';
import { ILatLng } from '../map/types';
import { INominatimResult } from '~/redux/types';
import { IMapReducer } from '../map';
export interface IEditorHistoryItem {
route: IMapReducer['route'];
stickers: IMapReducer['stickers'];
}
export interface IEditorState {
changed: boolean;
@ -57,6 +63,10 @@ export interface IEditorState {
processing: boolean;
loading: boolean;
};
history: {
records: IEditorHistoryItem[];
position: number;
};
}
export const EDITOR_INITIAL_STATE = {
@ -109,6 +119,11 @@ export const EDITOR_INITIAL_STATE = {
processing: false,
loading: false,
},
history: {
records: [{ route: [], stickers: [] }],
position: 0,
},
};
export const editor = createReducer(EDITOR_INITIAL_STATE, EDITOR_HANDLERS);

View file

@ -27,6 +27,9 @@ import {
editorSetNominatim,
editorSetMode,
editorSetRouter,
editorSetHistory,
editorUndo,
editorRedo,
} from '~/redux/editor/actions';
import { getUrlData, pushPath } from '~/utils/history';
import { MODES } from '~/constants/modes';
@ -34,7 +37,7 @@ import { checkOSRMService, checkNominatimService, searchNominatim } from '~/util
import { LatLng } from 'leaflet';
import { searchSetTab } from '../user/actions';
import { TABS, DIALOGS } from '~/constants/dialogs';
import { EDITOR_ACTIONS } from './constants';
import { EDITOR_ACTIONS, EDITOR_HISTORY_LENGTH } from './constants';
import { getGPXString, downloadGPXTrack } from '~/utils/gpx';
import {
getTilePlacement,
@ -53,7 +56,7 @@ import { selectMap, selectMapRoute } from '../map/selectors';
import { selectUser } from '../user/selectors';
import { LOGOS } from '~/constants/logos';
import { loadMapFromPath } from '../map/sagas';
import { mapClicked, mapSetRoute } from '../map/actions';
import { mapClicked, mapSetRoute, mapSet } from '../map/actions';
import { MAP_ACTIONS } from '../map/constants';
import { OsrmRouter } from '~/utils/map/OsrmRouter';
import path from 'ramda/es/path';
@ -236,6 +239,10 @@ function* keyPressedSaga({ key, target }: ReturnType<typeof editorKeyPressed>) {
} else {
yield put(editorChangeMode(MODES.TRASH));
}
} else if (key === 'z') {
yield put(editorUndo())
} else if (key === 'u') {
yield put(editorRedo())
}
}
@ -316,6 +323,51 @@ function* changeMode({ mode }: ReturnType<typeof editorChangeMode>) {
}
}
function* changeHistory() {
const { history }: ReturnType<typeof selectEditor> = yield select(selectEditor);
const { route, stickers }: ReturnType<typeof selectMap> = yield select(selectMap);
const current =
history.records.length - 1 > history.position
? history.records.slice(0, history.position + 1)
: history.records;
const records = [...current, { route, stickers }];
const min = Math.max(0, records.length - EDITOR_HISTORY_LENGTH - 1);
const max = Math.min(records.length, EDITOR_HISTORY_LENGTH + 2);
yield put(
editorSetHistory({
records: records.slice(min, max),
position: records.slice(min, max).length - 1,
})
);
}
function* undoHistory() {
const { history }: ReturnType<typeof selectEditor> = yield select(selectEditor);
if (history.position === 0 || history.records.length <= 1) return;
const route = history.records[history.position - 1].route;
const stickers = history.records[history.position - 1].stickers;
yield put(mapSet({ route, stickers }));
yield put(editorSetHistory({ position: history.position - 1 }));
}
function* redoHistory() {
const { history }: ReturnType<typeof selectEditor> = yield select(selectEditor);
if (history.position >= history.records.length - 1) return;
const route = history.records[history.position + 1].route;
const stickers = history.records[history.position + 1].stickers;
yield put(mapSet({ route, stickers }));
yield put(editorSetHistory({ position: history.position + 1 }));
}
export function* editorSaga() {
yield takeEvery(EDITOR_ACTIONS.LOCATION_CHANGED, locationChangeSaga);
@ -330,4 +382,19 @@ export function* editorSaga() {
yield takeLatest(EDITOR_ACTIONS.CANCEL_SAVE, cancelSave);
yield takeLeading(EDITOR_ACTIONS.SEARCH_NOMINATIM, searchNominatimSaga);
yield takeLeading(EDITOR_ACTIONS.CHANGE_MODE, changeMode);
yield takeEvery(
[
MAP_ACTIONS.ADD_STICKER,
MAP_ACTIONS.DROP_STICKER,
MAP_ACTIONS.SET_STICKERS,
MAP_ACTIONS.SET_STICKER,
MAP_ACTIONS.SET_ROUTE,
EDITOR_ACTIONS.CAPTURE_HIPSTORY,
],
changeHistory
);
yield takeEvery(EDITOR_ACTIONS.UNDO, undoHistory);
yield takeEvery(EDITOR_ACTIONS.REDO, redoHistory);
}

View file

@ -31,6 +31,8 @@ import {
editorSendSaveRequest,
editorSetSave,
editorClearAll,
editorSetHistory,
editorCaptureHistory,
} from '~/redux/editor/actions';
import { pushLoaderState, getUrlData, pushPath } from '~/utils/history';
import { getStoredMap, postMap } from '~/utils/api';
@ -86,6 +88,7 @@ export function* loadMapSaga(path) {
})
);
yield put(editorSetHistory({ records: [{ route: route.route, stickers: route.stickers }] }));
return { route, random_url };
}
@ -113,6 +116,7 @@ export function* startEmptyEditorSaga() {
export function* loadMapFromPath() {
const { path, mode } = getUrlData();
yield put(editorSetHistory({ records: [{ route: [], stickers: [] }], position: 0 }));
if (path) {
const map = yield call(loadMapSaga, path);
@ -133,9 +137,6 @@ export function* mapInitSaga() {
pushLoaderState(90);
const { hash } = getUrlData();
const {
user: { id },
}: ReturnType<typeof selectUser> = yield select(selectUser);
const provider: ReturnType<typeof selectMapProvider> = yield select(selectMapProvider);
yield put(mapSetProvider(provider));
@ -190,8 +191,8 @@ function* clearAllSaga() {
if (!stickers.length && !route.length) return;
yield put(editorSetChanged(false));
yield put(mapSetRoute([]));
yield put(mapSetStickers([]));
yield put(mapSet({ route: [], stickers: [] }));
yield put(editorCaptureHistory());
}
function* clearSaga({ type }) {

View file

@ -342,13 +342,8 @@ export function* updateUserRoutes() {
yield put(searchSetTab(TABS.MY));
}
// function* getUserLocation() {
// yield call(watchLocation, ActionCreators.setUserLocation);
// }
export function* userSaga() {
yield takeLatest(REHYDRATE, authCheckSaga);
// yield takeLatest(REHYDRATE, getUserLocation);
yield takeEvery(USER_ACTIONS.USER_LOGOUT, userLogoutSaga);
yield takeLatest(USER_ACTIONS.GOT_VK_USER, gotVkUserSaga);