mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 11:06:40 +07:00
separated map and user reducers
This commit is contained in:
parent
9f8cb1a875
commit
b75c028ce1
14 changed files with 849 additions and 768 deletions
|
@ -24,6 +24,11 @@ export const mapSetSticker = (index: number, sticker: IStickerDump) => ({
|
|||
sticker,
|
||||
});
|
||||
|
||||
export const mapAddSticker = (sticker: IStickerDump) => ({
|
||||
type: MAP_ACTIONS.ADD_STICKER,
|
||||
sticker,
|
||||
});
|
||||
|
||||
export const mapDropSticker = (index: number) => ({
|
||||
type: MAP_ACTIONS.DROP_STICKER,
|
||||
index,
|
||||
|
@ -33,3 +38,28 @@ export const mapClicked = (latlng: ILatLng) => ({
|
|||
type: MAP_ACTIONS.MAP_CLICKED,
|
||||
latlng,
|
||||
});
|
||||
|
||||
export const mapSetTitle = (title: string) => ({
|
||||
type: MAP_ACTIONS.SET_TITLE,
|
||||
title,
|
||||
});
|
||||
|
||||
export const mapSetDescription = (description: string) => ({
|
||||
type: MAP_ACTIONS.SET_DESCRIPTION,
|
||||
description,
|
||||
});
|
||||
|
||||
export const mapSetAddress = (address: string) => ({
|
||||
type: MAP_ACTIONS.SET_ADDRESS,
|
||||
address,
|
||||
});
|
||||
|
||||
export const mapSetOwner = (owner: IMapReducer['owner']) => ({
|
||||
type: MAP_ACTIONS.SET_DESCRIPTION,
|
||||
owner,
|
||||
});
|
||||
|
||||
export const mapSetPublic = (is_public: IMapReducer['is_public']) => ({
|
||||
type: MAP_ACTIONS.SET_PUBLIC,
|
||||
is_public,
|
||||
});
|
||||
|
|
|
@ -4,7 +4,13 @@ export const MAP_ACTIONS = {
|
|||
SET_MAP: `${P}-SET_MAP`,
|
||||
SET_PROVIDER: `${P}-SET_PROVIDER`,
|
||||
SET_ROUTE: `${P}-SET_ROUTE`,
|
||||
SET_TITLE: `${P}-SET_TILE`,
|
||||
SET_DESCRIPTION: `${P}-SETDESCRIPTION`,
|
||||
SET_ADDRESS: `${P}-SET_ADDRESS`,
|
||||
SET_OWNER: `${P}-SET_OWNER`,
|
||||
SET_PUBLIC: `${P}-SET_PUBLIC`,
|
||||
|
||||
ADD_STICKER: `${P}-ADD_STICKER`,
|
||||
SET_STICKER: `${P}-SET_STICKER`,
|
||||
DROP_STICKER: `${P}-DROP_STICKER`,
|
||||
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
import { MAP_ACTIONS } from "./constants";
|
||||
import { IMapReducer } from ".";
|
||||
import { mapSet, mapSetProvider, mapSetRoute, mapSetSticker } from "./actions";
|
||||
import { MAP_ACTIONS } from './constants';
|
||||
import { IMapReducer } from '.';
|
||||
import {
|
||||
mapSet,
|
||||
mapSetProvider,
|
||||
mapSetRoute,
|
||||
mapSetSticker,
|
||||
mapAddSticker,
|
||||
mapSetTitle,
|
||||
mapSetAddress,
|
||||
mapSetDescription,
|
||||
mapSetOwner,
|
||||
mapSetPublic,
|
||||
} from './actions';
|
||||
|
||||
const setMap = (
|
||||
state: IMapReducer,
|
||||
{ map }: ReturnType<typeof mapSet>
|
||||
): IMapReducer => ({
|
||||
const setMap = (state: IMapReducer, { map }: ReturnType<typeof mapSet>): IMapReducer => ({
|
||||
...state,
|
||||
...map
|
||||
...map,
|
||||
});
|
||||
|
||||
const setProvider = (
|
||||
|
@ -15,15 +23,12 @@ const setProvider = (
|
|||
{ provider }: ReturnType<typeof mapSetProvider>
|
||||
): IMapReducer => ({
|
||||
...state,
|
||||
provider
|
||||
provider,
|
||||
});
|
||||
|
||||
const setRoute = (
|
||||
state: IMapReducer,
|
||||
{ route }: ReturnType<typeof mapSetRoute>
|
||||
): IMapReducer => ({
|
||||
const setRoute = (state: IMapReducer, { route }: ReturnType<typeof mapSetRoute>): IMapReducer => ({
|
||||
...state,
|
||||
route
|
||||
route,
|
||||
});
|
||||
|
||||
const setSticker = (
|
||||
|
@ -31,7 +36,7 @@ const setSticker = (
|
|||
{ sticker, index }: ReturnType<typeof mapSetSticker>
|
||||
): IMapReducer => ({
|
||||
...state,
|
||||
stickers: state.stickers.map((item, i) => (i === index ? sticker : item))
|
||||
stickers: state.stickers.map((item, i) => (i === index ? sticker : item)),
|
||||
});
|
||||
|
||||
const dropSticker = (
|
||||
|
@ -39,7 +44,40 @@ const dropSticker = (
|
|||
{ index }: ReturnType<typeof mapSetSticker>
|
||||
): IMapReducer => ({
|
||||
...state,
|
||||
stickers: state.stickers.filter((_, i) => i !== index)
|
||||
stickers: state.stickers.filter((_, i) => i !== index),
|
||||
});
|
||||
|
||||
const addSticker = (
|
||||
state: IMapReducer,
|
||||
{ sticker }: ReturnType<typeof mapAddSticker>
|
||||
): IMapReducer => ({
|
||||
...state,
|
||||
stickers: [...state.stickers, sticker],
|
||||
});
|
||||
|
||||
const setTitle = (state: IMapReducer, { title }: ReturnType<typeof mapSetTitle>): IMapReducer => ({
|
||||
...state,
|
||||
title,
|
||||
});
|
||||
|
||||
const setAddress = (state: IMapReducer, { address }: ReturnType<typeof mapSetAddress>): IMapReducer => ({
|
||||
...state,
|
||||
address,
|
||||
});
|
||||
|
||||
const setDescription = (state: IMapReducer, { description }: ReturnType<typeof mapSetDescription>): IMapReducer => ({
|
||||
...state,
|
||||
description,
|
||||
});
|
||||
|
||||
const setOwner = (state: IMapReducer, { owner }: ReturnType<typeof mapSetOwner>): IMapReducer => ({
|
||||
...state,
|
||||
owner,
|
||||
});
|
||||
|
||||
const setPublic = (state: IMapReducer, { is_public }: ReturnType<typeof mapSetPublic>): IMapReducer => ({
|
||||
...state,
|
||||
is_public,
|
||||
});
|
||||
|
||||
export const MAP_HANDLERS = {
|
||||
|
@ -48,4 +86,10 @@ export const MAP_HANDLERS = {
|
|||
[MAP_ACTIONS.SET_ROUTE]: setRoute,
|
||||
[MAP_ACTIONS.SET_STICKER]: setSticker,
|
||||
[MAP_ACTIONS.DROP_STICKER]: dropSticker,
|
||||
[MAP_ACTIONS.ADD_STICKER]: addSticker,
|
||||
[MAP_ACTIONS.SET_TITLE]: setTitle,
|
||||
[MAP_ACTIONS.SET_ADDRESS]: setAddress,
|
||||
[MAP_ACTIONS.SET_DESCRIPTION]: setDescription,
|
||||
[MAP_ACTIONS.SET_OWNER]: setOwner,
|
||||
[MAP_ACTIONS.SET_PUBLIC]: setPublic,
|
||||
};
|
||||
|
|
|
@ -7,13 +7,23 @@ import { IStickerDump } from '$modules/Sticker';
|
|||
export interface IMapReducer {
|
||||
provider: string;
|
||||
route: IMapRoute;
|
||||
stickers: IStickerDump[]
|
||||
stickers: IStickerDump[];
|
||||
title: string;
|
||||
address: string;
|
||||
description: string;
|
||||
owner: { id: string };
|
||||
is_public: boolean;
|
||||
}
|
||||
|
||||
export const MAP_INITIAL_STATE = {
|
||||
export const MAP_INITIAL_STATE: IMapReducer = {
|
||||
provider: DEFAULT_PROVIDER,
|
||||
route: [],
|
||||
stickers: [],
|
||||
title: '',
|
||||
address: '',
|
||||
description: '',
|
||||
owner: { id: null },
|
||||
is_public: false,
|
||||
}
|
||||
|
||||
export const map = createReducer(MAP_INITIAL_STATE, MAP_HANDLERS)
|
|
@ -1,40 +1,323 @@
|
|||
import { takeEvery, select, put } from "redux-saga/effects";
|
||||
import { MAP_ACTIONS } from "./constants";
|
||||
import { mapClicked, mapSet } from "./actions";
|
||||
import { selectUserMode, selectUserActiveSticker } from "$redux/user/selectors";
|
||||
import { IRootReducer } from "$redux/user";
|
||||
import { MODES } from "$constants/modes";
|
||||
import { selectMapStickers } from "./selectors";
|
||||
import { setActiveSticker, setMode } from "$redux/user/actions";
|
||||
import { takeEvery, select, put, call, TakeEffect, race, take, takeLatest } from 'redux-saga/effects';
|
||||
import { MAP_ACTIONS } from './constants';
|
||||
import { mapClicked, mapAddSticker, mapSetProvider, mapSet, mapSetTitle, mapSetAddress, mapSetDescription, mapSetOwner, mapSetPublic } from './actions';
|
||||
import { selectUserMode, selectUserActiveSticker, selectUser, selectUserUser } from '$redux/user/selectors';
|
||||
import { MODES } from '$constants/modes';
|
||||
import {
|
||||
setMode,
|
||||
setChanged,
|
||||
setAddressOrigin,
|
||||
setEditing,
|
||||
setReady,
|
||||
setActiveSticker,
|
||||
setSaveError,
|
||||
setSaveLoading,
|
||||
sendSaveRequest,
|
||||
setSaveSuccess,
|
||||
setSaveOverwrite,
|
||||
} from '$redux/user/actions';
|
||||
import { pushLoaderState, getUrlData, pushPath, replacePath } from '$utils/history';
|
||||
import { setReadySaga, searchSetSagaWorker } from '$redux/user/sagas';
|
||||
import { getStoredMap, postMap } from '$utils/api';
|
||||
import { Unwrap } from '$utils/middleware';
|
||||
import { DEFAULT_PROVIDER } from '$constants/providers';
|
||||
import { USER_ACTIONS } from '$redux/user/constants';
|
||||
import { selectMap } from './selectors';
|
||||
import { TIPS } from '$constants/tips';
|
||||
import { delay } from 'redux-saga';
|
||||
|
||||
function* onMapClick({ latlng }: ReturnType<typeof mapClicked>) {
|
||||
const mode = yield select(selectUserMode);
|
||||
const { set, sticker } = yield select(selectUserActiveSticker);
|
||||
const stickers = yield select(selectMapStickers);
|
||||
|
||||
switch (mode) {
|
||||
case MODES.STICKERS:
|
||||
yield put(
|
||||
mapSet({
|
||||
stickers: [
|
||||
...stickers,
|
||||
{
|
||||
latlng,
|
||||
set,
|
||||
sticker,
|
||||
text: "",
|
||||
angle: 0,
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
yield put(setMode(MODES.NONE))
|
||||
yield put(mapAddSticker({ latlng, set, sticker, text: '', angle: 0 }));
|
||||
yield put(setMode(MODES.NONE));
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
export function* mapSaga() {
|
||||
yield takeEvery(MAP_ACTIONS.MAP_CLICKED, onMapClick);
|
||||
// function* changeProviderSaga({ provider }: ReturnType<typeof changeProvider>) {
|
||||
// const { provider: current_provider } = yield select(selectUser);
|
||||
|
||||
// yield put(mapSetProvider(provider));
|
||||
|
||||
// if (current_provider === provider) return;
|
||||
|
||||
// yield put(setChanged(true));
|
||||
|
||||
// return put(setMode(MODES.NONE));
|
||||
// }
|
||||
|
||||
function* setLogoSaga({ logo }: { type: string; logo: string }) {
|
||||
const { mode } = yield select(selectUser);
|
||||
|
||||
yield put(setChanged(true));
|
||||
|
||||
if (mode === MODES.LOGO) {
|
||||
yield put(setMode(MODES.NONE));
|
||||
}
|
||||
}
|
||||
|
||||
export function* replaceAddressIfItsBusy(destination, original) {
|
||||
if (original) {
|
||||
yield put(setAddressOrigin(original));
|
||||
}
|
||||
|
||||
pushPath(`/${destination}/edit`);
|
||||
}
|
||||
|
||||
export function* loadMapSaga(path) {
|
||||
const {
|
||||
data: { route, error, random_url },
|
||||
}: Unwrap<typeof getStoredMap> = yield call(getStoredMap, { name: path });
|
||||
|
||||
if (route && !error) {
|
||||
// TODO: set initial data
|
||||
// TODO: fit bounds
|
||||
|
||||
yield put(
|
||||
mapSet({
|
||||
provider: route.provider,
|
||||
route: route.route,
|
||||
stickers: route.stickers,
|
||||
title: route.title,
|
||||
})
|
||||
);
|
||||
|
||||
return { route, random_url };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function* startEmptyEditorSaga() {
|
||||
const {
|
||||
user: { id, random_url },
|
||||
provider = DEFAULT_PROVIDER,
|
||||
} = yield select(selectUser);
|
||||
|
||||
// TODO: set owner { id }
|
||||
pushPath(`/${random_url}/edit`);
|
||||
|
||||
yield put(setChanged(false));
|
||||
yield put(setEditing(true));
|
||||
|
||||
return yield call(setReadySaga);
|
||||
}
|
||||
|
||||
export function* mapInitSaga() {
|
||||
pushLoaderState(90);
|
||||
|
||||
const { path, mode, hash } = getUrlData();
|
||||
const {
|
||||
provider,
|
||||
user: { id },
|
||||
} = yield select(selectUser);
|
||||
|
||||
yield put(mapSetProvider(provider));
|
||||
|
||||
if (hash && /^#map/.test(hash)) {
|
||||
const [, newUrl] = hash.match(/^#map[:/?!](.*)$/);
|
||||
|
||||
if (newUrl) {
|
||||
yield pushPath(`/${newUrl}`);
|
||||
yield call(setReadySaga);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (path) {
|
||||
const map = yield call(loadMapSaga, path);
|
||||
|
||||
if (map && map.route) {
|
||||
if (mode && mode === 'edit') {
|
||||
if (map && map.route && map.route.owner && mode === 'edit' && map.route.owner !== id) {
|
||||
yield call(setReadySaga);
|
||||
yield call(replaceAddressIfItsBusy, map.random_url, map.address);
|
||||
} else {
|
||||
yield put(setAddressOrigin(''));
|
||||
}
|
||||
|
||||
yield put(setEditing(true));
|
||||
// TODO: start editing
|
||||
} else {
|
||||
yield put(setEditing(false));
|
||||
// TODO: stop editing
|
||||
}
|
||||
|
||||
yield call(setReadySaga);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
yield call(startEmptyEditorSaga);
|
||||
yield put(setReady(true));
|
||||
|
||||
pushLoaderState(100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function* setActiveStickerSaga() {
|
||||
yield put(setMode(MODES.STICKERS));
|
||||
}
|
||||
|
||||
function* setTitleSaga({ title }: ReturnType<typeof mapSetTitle>) {
|
||||
if (title) {
|
||||
document.title = `${title} | Редактор маршрутов`;
|
||||
}
|
||||
}
|
||||
|
||||
function* clearSaga({ type }) {
|
||||
switch (type) {
|
||||
case USER_ACTIONS.CLEAR_POLY:
|
||||
// TODO: clear router waypoints
|
||||
yield put(
|
||||
mapSet({
|
||||
route: [],
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case USER_ACTIONS.CLEAR_STICKERS:
|
||||
yield put(
|
||||
mapSet({
|
||||
stickers: [],
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
case USER_ACTIONS.CLEAR_ALL:
|
||||
yield put(setChanged(false));
|
||||
yield put(
|
||||
mapSet({
|
||||
route: [],
|
||||
stickers: [],
|
||||
})
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
yield put(setActiveSticker(null)); // TODO: move to maps
|
||||
yield put(setMode(MODES.NONE));
|
||||
}
|
||||
|
||||
function* sendSaveRequestSaga({
|
||||
title,
|
||||
address,
|
||||
force,
|
||||
is_public,
|
||||
description,
|
||||
}: ReturnType<typeof sendSaveRequest>) {
|
||||
const { route, stickers, provider } = yield select(selectMap);
|
||||
|
||||
if (!route.length && !stickers.length) {
|
||||
return yield put(setSaveError(TIPS.SAVE_EMPTY)); // TODO: move setSaveError to editor
|
||||
}
|
||||
|
||||
const { logo, distance } = yield select(selectUser);
|
||||
const { token } = yield select(selectUserUser);
|
||||
|
||||
yield put(setSaveLoading(true)); // TODO: move setSaveLoading to maps
|
||||
|
||||
const {
|
||||
result,
|
||||
timeout,
|
||||
cancel,
|
||||
}: {
|
||||
result: Unwrap<typeof postMap>;
|
||||
timeout: boolean;
|
||||
cancel: TakeEffect;
|
||||
} = yield race({
|
||||
result: postMap({
|
||||
token,
|
||||
route,
|
||||
stickers,
|
||||
title,
|
||||
force,
|
||||
address,
|
||||
logo,
|
||||
distance,
|
||||
provider,
|
||||
is_public,
|
||||
description,
|
||||
}),
|
||||
timeout: delay(10000),
|
||||
cancel: take(USER_ACTIONS.RESET_SAVE_DIALOG),
|
||||
});
|
||||
|
||||
yield put(setSaveLoading(false));
|
||||
|
||||
if (cancel) return yield put(setMode(MODES.NONE));
|
||||
|
||||
if (result && result.data.code === 'already_exist') return yield put(setSaveOverwrite()); // TODO: move setSaveOverwrite to editor
|
||||
if (result && result.data.code === 'conflict') return yield put(setSaveError(TIPS.SAVE_EXISTS));
|
||||
if (timeout || !result || !result.data.route || !result.data.route.address)
|
||||
return yield put(setSaveError(TIPS.SAVE_TIMED_OUT));
|
||||
|
||||
return yield put( // TODO: move setSaveSuccess to editor
|
||||
setSaveSuccess({
|
||||
address: result.data.route.address,
|
||||
title: result.data.route.title,
|
||||
is_public: result.data.route.is_public,
|
||||
description: result.data.route.description,
|
||||
|
||||
save_error: TIPS.SAVE_SUCCESS,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function* setSaveSuccessSaga({
|
||||
address,
|
||||
title,
|
||||
is_public,
|
||||
description,
|
||||
}: ReturnType<typeof setSaveSuccess>) {
|
||||
const { id } = yield select(selectUser);
|
||||
const { dialog_active } = yield select(selectUser);
|
||||
|
||||
replacePath(`/${address}/edit`);
|
||||
|
||||
yield put(mapSetTitle(title));
|
||||
yield put(mapSetAddress(address));
|
||||
yield put(mapSetPublic(is_public));
|
||||
yield put(mapSetDescription(description));
|
||||
yield put(setChanged(false));
|
||||
yield put(mapSetOwner({ id }));
|
||||
|
||||
if (dialog_active) {
|
||||
yield call(searchSetSagaWorker);
|
||||
}
|
||||
|
||||
// yield editor.setInitialData();
|
||||
// TODO: set initial data here
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
export function* mapSaga() {
|
||||
yield takeEvery(USER_ACTIONS.SET_ACTIVE_STICKER, setActiveStickerSaga); // TODO: move active sticker to maps
|
||||
yield takeEvery(MAP_ACTIONS.MAP_CLICKED, onMapClick);
|
||||
yield takeEvery(MAP_ACTIONS.SET_TITLE, setTitleSaga);
|
||||
yield takeEvery(USER_ACTIONS.SET_LOGO, setLogoSaga);
|
||||
yield takeLatest(USER_ACTIONS.SEND_SAVE_REQUEST, sendSaveRequestSaga);
|
||||
yield takeLatest(USER_ACTIONS.SET_SAVE_SUCCESS, setSaveSuccessSaga);
|
||||
|
||||
yield takeEvery(
|
||||
// TODO: move all actions to MAP
|
||||
[
|
||||
USER_ACTIONS.CLEAR_POLY,
|
||||
USER_ACTIONS.CLEAR_STICKERS,
|
||||
USER_ACTIONS.CLEAR_ALL,
|
||||
USER_ACTIONS.CLEAR_CANCEL,
|
||||
],
|
||||
clearSaga
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { IState } from "$redux/store";
|
||||
|
||||
export const selectMap = (state: IState) => state.map;
|
||||
export const selectMapProvider = (state: IState) => state.map.provider;
|
||||
export const selectMapRoute= (state: IState) => state.map.route;
|
||||
export const selectMapStickers = (state: IState) => state.map.stickers;
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,7 @@
|
|||
import { IState } from '$redux/store'
|
||||
|
||||
export const selectUser = (state: IState) => state.user;
|
||||
export const selectUserUser = (state: IState) => state.user.user;
|
||||
export const selectUserEditing = (state: IState) => state.user.editing;
|
||||
export const selectUserMode = (state: IState) => state.user.mode;
|
||||
export const selectUserActiveSticker = (state: IState) => state.user.activeSticker;
|
Loading…
Add table
Add a link
Reference in a new issue