mirror of
https://github.com/muerwre/orchidmap-front.git
synced 2025-04-25 11:06:40 +07:00
starred content
This commit is contained in:
parent
a920217959
commit
baa41f707d
19 changed files with 159 additions and 22 deletions
|
@ -10,7 +10,7 @@ import {
|
|||
setDialogActive,
|
||||
mapsLoadMore,
|
||||
dropRoute,
|
||||
modifyRoute,
|
||||
modifyRoute, toggleRouteStarred,
|
||||
} from '$redux/user/actions';
|
||||
import { isMobile } from '$utils/window';
|
||||
import classnames from 'classnames';
|
||||
|
@ -21,12 +21,14 @@ import { TABS } from '$constants/dialogs';
|
|||
import { Icon } from '$components/panels/Icon';
|
||||
import { pushPath } from '$utils/history';
|
||||
import { IRootState, IRouteListItem } from '$redux/user/reducer';
|
||||
import { ROLES } from "$constants/auth";
|
||||
|
||||
export interface IMapListDialogProps extends IRootState {
|
||||
marks: { [x: number]: string },
|
||||
routes_sorted: Array<IRouteListItem>,
|
||||
routes: IRootState['routes'],
|
||||
ready: IRootState['ready'],
|
||||
role: IRootState['user']['role'],
|
||||
|
||||
mapsLoadMore: typeof mapsLoadMore,
|
||||
searchSetDistance: typeof searchSetDistance,
|
||||
|
@ -35,6 +37,7 @@ export interface IMapListDialogProps extends IRootState {
|
|||
setDialogActive: typeof setDialogActive,
|
||||
dropRoute: typeof dropRoute,
|
||||
modifyRoute: typeof modifyRoute,
|
||||
toggleRouteStarred: typeof toggleRouteStarred,
|
||||
}
|
||||
|
||||
export interface IMapListDialogState {
|
||||
|
@ -117,9 +120,12 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
this.stopEditing();
|
||||
};
|
||||
|
||||
toggleStarred = (id: string) => this.props.toggleRouteStarred(id);
|
||||
|
||||
render() {
|
||||
const {
|
||||
ready,
|
||||
role,
|
||||
routes: {
|
||||
list,
|
||||
loading,
|
||||
|
@ -136,6 +142,8 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
|
||||
const { editor_target, menu_target, is_editing, is_dropping } = this.state;
|
||||
|
||||
console.log('role', this.props.role);
|
||||
|
||||
return (
|
||||
<div className="dialog-content">
|
||||
{ list.length === 0 && loading &&
|
||||
|
@ -156,7 +164,7 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
}
|
||||
<div className="dialog-tabs">
|
||||
{
|
||||
Object.keys(TABS).map(item => (
|
||||
Object.keys(TABS).map(item => (role === ROLES.admin || item !== 'all') && (
|
||||
<div
|
||||
className={classnames('dialog-tab', { active: tab === item })}
|
||||
onClick={() => this.props.searchSetTab(item)}
|
||||
|
@ -208,6 +216,7 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
distance={route.distance}
|
||||
_id={route._id}
|
||||
is_public={route.is_public}
|
||||
is_starred={route.is_starred}
|
||||
tab={tab}
|
||||
is_editing_mode={is_dropping ? 'drop' : 'edit'}
|
||||
is_editing_target={editor_target === route._id}
|
||||
|
@ -220,7 +229,9 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
showDropCard={this.showDropCard}
|
||||
dropRoute={this.dropRoute}
|
||||
modifyRoute={this.modifyRoute}
|
||||
toggleStarred={this.toggleStarred}
|
||||
key={route._id}
|
||||
is_admin={role === ROLES.admin}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
@ -233,13 +244,15 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
|||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({ user: { editing, routes } }) => {
|
||||
const mapStateToProps = ({ user: { editing, routes, user: { role } } }: { user: IRootState }) => {
|
||||
if (routes.filter.max >= 9999) {
|
||||
return {
|
||||
routes, editing, marks: {}, ready: false,
|
||||
routes, editing, marks: {}, ready: false, role,
|
||||
};
|
||||
}
|
||||
|
||||
return ({
|
||||
role,
|
||||
routes,
|
||||
editing,
|
||||
ready: true,
|
||||
|
@ -260,6 +273,7 @@ const mapDispatchToProps = dispatch => bindActionCreators({
|
|||
mapsLoadMore,
|
||||
dropRoute,
|
||||
modifyRoute,
|
||||
toggleRouteStarred,
|
||||
}, dispatch);
|
||||
|
||||
export const MapListDialog = connect(mapStateToProps, mapDispatchToProps)(Component);
|
||||
|
|
|
@ -5,15 +5,20 @@ import { MapListDialog } from "$components/dialogs/MapListDialog";
|
|||
import { Tooltip } from "$components/panels/Tooltip";
|
||||
import { ReactElement } from "react";
|
||||
import classnames from 'classnames';
|
||||
import { toggleRouteStarred } from "$redux/user/actions";
|
||||
|
||||
interface Props {
|
||||
_id: string,
|
||||
tab: string,
|
||||
|
||||
_id: string,
|
||||
title: string,
|
||||
distance: number,
|
||||
is_public: boolean,
|
||||
is_admin: boolean,
|
||||
is_starred: boolean,
|
||||
|
||||
openRoute: typeof MapListDialog.openRoute,
|
||||
toggleStarred: typeof MapListDialog.toggleStarred,
|
||||
startEditing: typeof MapListDialog.startEditing,
|
||||
stopEditing: typeof MapListDialog.stopEditing,
|
||||
showMenu: typeof MapListDialog.showMenu,
|
||||
|
@ -22,11 +27,21 @@ interface Props {
|
|||
}
|
||||
|
||||
export const RouteRowView = ({
|
||||
title, distance, _id, openRoute, tab, startEditing, showMenu, showDropCard, hideMenu,
|
||||
title, distance, _id, openRoute, tab, startEditing, showMenu, showDropCard, hideMenu, is_admin, is_starred, toggleStarred,
|
||||
}: Props): ReactElement<Props, null> => (
|
||||
<div
|
||||
className={classnames('route-row-view', { has_menu: (tab === 'mine') })}
|
||||
>
|
||||
{
|
||||
(tab === 'all' || tab === 'starred') && is_admin &&
|
||||
<div className="route-row-fav" onClick={toggleStarred.bind(null, _id)}>
|
||||
{
|
||||
is_starred
|
||||
? <Icon icon="icon-star-fill" size={24}/>
|
||||
: <Icon icon="icon-star-blank" size={24}/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div
|
||||
className="route-row"
|
||||
onClick={() => openRoute(_id)}
|
||||
|
|
|
@ -12,7 +12,9 @@ interface Props {
|
|||
title: string,
|
||||
distance: number,
|
||||
is_public: boolean,
|
||||
is_starred: boolean,
|
||||
|
||||
is_admin: boolean,
|
||||
is_editing_target: boolean,
|
||||
is_menu_target: boolean,
|
||||
|
||||
|
@ -24,6 +26,7 @@ interface Props {
|
|||
showDropCard: typeof MapListDialog.showDropCard,
|
||||
dropRoute: typeof MapListDialog.dropRoute,
|
||||
modifyRoute: typeof MapListDialog.modifyRoute,
|
||||
toggleStarred: typeof MapListDialog.toggleStarred,
|
||||
|
||||
is_editing_mode: 'edit' | 'drop',
|
||||
}
|
||||
|
@ -31,7 +34,7 @@ interface Props {
|
|||
export const RouteRowWrapper = ({
|
||||
title, distance, _id, openRoute, tab, startEditing, showMenu,
|
||||
showDropCard, is_public, is_editing_target, is_menu_target, is_editing_mode,
|
||||
dropRoute, stopEditing, modifyRoute, hideMenu,
|
||||
dropRoute, stopEditing, modifyRoute, hideMenu, is_admin, is_starred, toggleStarred,
|
||||
}: Props): ReactElement<Props, null> => (
|
||||
<div
|
||||
className={classnames('route-row-wrapper', {
|
||||
|
@ -64,12 +67,15 @@ export const RouteRowWrapper = ({
|
|||
title={title}
|
||||
distance={distance}
|
||||
is_public={is_public}
|
||||
is_starred={is_starred}
|
||||
openRoute={openRoute}
|
||||
startEditing={startEditing}
|
||||
stopEditing={stopEditing}
|
||||
showMenu={showMenu}
|
||||
hideMenu={hideMenu}
|
||||
showDropCard={showDropCard}
|
||||
is_admin={is_admin}
|
||||
toggleStarred={toggleStarred}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -10,4 +10,5 @@ export const API: { [x: string]: string } = {
|
|||
|
||||
DROP_ROUTE: `${CLIENT.API_ADDR}/route`,
|
||||
MODIFY_ROUTE: `${CLIENT.API_ADDR}/route/modify`,
|
||||
SET_STARRED: `${CLIENT.API_ADDR}/route/star`,
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export interface IRoles {
|
||||
guest: string,
|
||||
vk: string,
|
||||
admin: string,
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
|
@ -24,6 +25,7 @@ export interface IUser {
|
|||
export const ROLES: IRoles = {
|
||||
guest: 'guest',
|
||||
vk: 'vk',
|
||||
admin: 'admin',
|
||||
};
|
||||
|
||||
export const DEFAULT_USER: IUser = {
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface IDialogs {
|
|||
export interface IMapTabs {
|
||||
mine: string,
|
||||
all: string,
|
||||
starred: string,
|
||||
}
|
||||
|
||||
export const DIALOGS: IDialogs = ({
|
||||
|
@ -17,5 +18,6 @@ export const DIALOGS: IDialogs = ({
|
|||
|
||||
export const TABS: IMapTabs = ({
|
||||
mine: 'Мои',
|
||||
all: 'Общие',
|
||||
starred: 'Общие',
|
||||
all: 'ВСЕ',
|
||||
});
|
||||
|
|
|
@ -76,3 +76,5 @@ export const dropRoute = (_id: string) => ({ type: ACTIONS.DROP_ROUTE, _id });
|
|||
export const modifyRoute = (_id: string, { title, is_public }: { title: string, is_public: boolean }) => ({
|
||||
type: ACTIONS.MODIFY_ROUTE, _id, title, is_public
|
||||
});
|
||||
export const toggleRouteStarred = (_id: string) => ({ type: ACTIONS.TOGGLE_ROUTE_STARRED, _id });
|
||||
export const setRouteStarred = (_id: string, is_starred: boolean) => ({ type: ACTIONS.SET_ROUTE_STARRED, _id, is_starred });
|
||||
|
|
|
@ -81,4 +81,6 @@ export const ACTIONS: IActions = {
|
|||
|
||||
DROP_ROUTE: 'DROP_ROUTE',
|
||||
MODIFY_ROUTE: 'MODIFY_ROUTE',
|
||||
SET_ROUTE_STARRED: 'SET_ROUTE_STARRED',
|
||||
TOGGLE_ROUTE_STARRED: 'TOGGLE_ROUTE_STARRED',
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface IRouteListItem {
|
|||
title: string,
|
||||
distance: number,
|
||||
is_public: boolean,
|
||||
is_starred: boolean,
|
||||
updated_at: string,
|
||||
}
|
||||
|
||||
|
@ -304,6 +305,18 @@ const setIsRouting: ActionHandler<typeof ActionCreators.setIsRouting> = (state,
|
|||
is_routing,
|
||||
});
|
||||
|
||||
const setRouteStarred: ActionHandler<typeof ActionCreators.setRouteStarred> = (state, { _id, is_starred }) => ({
|
||||
...state,
|
||||
routes: {
|
||||
...state.routes,
|
||||
list: (
|
||||
state.routes.list
|
||||
.map(el => el._id === _id ? { ...el, is_starred } : el)
|
||||
.filter(el => state.routes.filter.tab !== 'starred' || el.is_starred)
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
const HANDLERS = ({
|
||||
[ACTIONS.SET_USER]: setUser,
|
||||
[ACTIONS.SET_EDITING]: setEditing,
|
||||
|
@ -348,6 +361,8 @@ const HANDLERS = ({
|
|||
|
||||
[ACTIONS.SET_FEATURE]: setFeature,
|
||||
[ACTIONS.SET_IS_ROUTING]: setIsRouting,
|
||||
|
||||
[ACTIONS.SET_ROUTE_STARRED]: setRouteStarred,
|
||||
});
|
||||
|
||||
export const INITIAL_STATE: IRootReducer = {
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
checkUserToken, dropRoute,
|
||||
getGuestToken, getRouteList,
|
||||
getStoredMap, modifyRoute,
|
||||
postMap
|
||||
postMap, sendRouteStarred
|
||||
} from '$utils/api';
|
||||
import {
|
||||
hideRenderer,
|
||||
|
@ -32,7 +32,7 @@ import {
|
|||
setProvider,
|
||||
changeProvider,
|
||||
setSaveLoading,
|
||||
mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle,
|
||||
mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle, setRouteStarred,
|
||||
} from '$redux/user/actions';
|
||||
import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history';
|
||||
import { editor } from '$modules/Editor';
|
||||
|
@ -51,7 +51,7 @@ import {
|
|||
} from '$utils/renderer';
|
||||
import { ILogos, LOGOS } from '$constants/logos';
|
||||
import { DEFAULT_PROVIDER } from '$constants/providers';
|
||||
import { DIALOGS } from '$constants/dialogs';
|
||||
import { DIALOGS, TABS } from '$constants/dialogs';
|
||||
|
||||
import * as ActionCreators from '$redux/user/actions';
|
||||
import { IRootState } from "$redux/user/reducer";
|
||||
|
@ -501,7 +501,7 @@ function* searchGetRoutes() {
|
|||
|
||||
const { routes: { step, shift, filter: { title, distance, tab } } } = yield select(getState);
|
||||
|
||||
const result = yield call(getRouteList, {
|
||||
return yield call(getRouteList, {
|
||||
id,
|
||||
token,
|
||||
title,
|
||||
|
@ -509,10 +509,8 @@ function* searchGetRoutes() {
|
|||
step,
|
||||
shift,
|
||||
author: tab === 'mine' ? id : '',
|
||||
starred: tab === 'starred',
|
||||
starred: tab === 'starred' ? 1 : 0,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function* searchSetSagaWorker() {
|
||||
|
@ -697,6 +695,17 @@ function* modifyRouteSaga({ _id, title, is_public }: ReturnType<typeof ActionCre
|
|||
return yield call(modifyRoute, { address: _id, id, token, title, is_public });
|
||||
}
|
||||
|
||||
function* toggleRouteStarredSaga({ _id }: ReturnType<typeof ActionCreators.toggleRouteStarred>) {
|
||||
const { routes: { list } } = yield select(getState);
|
||||
const route = list.find(el => el._id === _id);
|
||||
const { id, token } = yield select(getUser);
|
||||
|
||||
yield put(setRouteStarred(_id, !route.is_starred));
|
||||
const result = yield sendRouteStarred({ id, token, _id, is_starred: !route.is_starred });
|
||||
|
||||
if (!result) return yield put(setRouteStarred(_id, route.is_starred));
|
||||
}
|
||||
|
||||
export function* userSaga() {
|
||||
yield takeLatest(REHYDRATE, authCheckSaga);
|
||||
yield takeEvery(ACTIONS.SET_MODE, setModeSaga);
|
||||
|
@ -744,4 +753,5 @@ export function* userSaga() {
|
|||
|
||||
yield takeLatest(ACTIONS.DROP_ROUTE, dropRouteSaga);
|
||||
yield takeLatest(ACTIONS.MODIFY_ROUTE, modifyRouteSaga);
|
||||
yield takeLatest(ACTIONS.TOGGLE_ROUTE_STARRED, toggleRouteStarredSaga);
|
||||
}
|
||||
|
|
|
@ -384,6 +384,16 @@
|
|||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" fill="white" stroke="none" stroke-width="0" transform="translate(4 4)"/>
|
||||
</g>
|
||||
|
||||
<g id="icon-star-blank" stroke="none">
|
||||
<path stroke="none" fill="black"/>
|
||||
<path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z" fill="white" stroke="none" stroke-width="0" transform="translate(4 4)"/>
|
||||
</g>
|
||||
|
||||
<g id="icon-star-fill" stroke="none">
|
||||
<path stroke="none" fill="black"/>
|
||||
<path d="m12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27z" fill="white" stroke="none" stroke-width="0" transform="translate(4 4)"/>
|
||||
</g>
|
||||
|
||||
<g id="icon-cluster-1" stroke="none">
|
||||
<rect x="0" y="0" width="32" height="32" fill="black" stroke="none" />
|
||||
<circle cx="10" cy="21" fill="white" r="4" />
|
||||
|
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
|
@ -196,7 +196,7 @@
|
|||
}
|
||||
|
||||
&.is_menu_target {
|
||||
.route-row {
|
||||
.route-row, .route-row-fav {
|
||||
transform: translateX(-120px);
|
||||
}
|
||||
|
||||
|
@ -222,6 +222,7 @@
|
|||
overflow: hidden;
|
||||
transition: height 500ms;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
&.has_menu {
|
||||
padding-right: 32px;
|
||||
|
@ -285,6 +286,21 @@
|
|||
}
|
||||
}
|
||||
|
||||
.route-row-fav {
|
||||
width: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
fill: fade(white, 30%);
|
||||
background: fade(white, 5%);
|
||||
cursor: pointer;
|
||||
transition: background 250ms, transform 500ms;
|
||||
|
||||
&:hover {
|
||||
background: fade(white, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.route-row-edit-menu {
|
||||
width: 0;
|
||||
height: 100%;
|
||||
|
|
|
@ -29,7 +29,7 @@ interface IGetRouteList {
|
|||
author: IRootState['routes']['filter']['author'],
|
||||
step: IRootState['routes']['step'],
|
||||
shift: IRootState['routes']['step'],
|
||||
starred: IRootState['routes']['filter']['starred'],
|
||||
starred: number,
|
||||
id: IRootState['user']['id'],
|
||||
token: IRootState['user']['token'],
|
||||
}
|
||||
|
@ -112,3 +112,13 @@ export const modifyRoute = (
|
|||
): AxiosPromise<any> => (
|
||||
axios.patch(API.DROP_ROUTE, { address, id, token, title, is_public })
|
||||
);
|
||||
|
||||
export const sendRouteStarred = (
|
||||
{ id, token, _id, is_starred }:
|
||||
{ id: string, token: string, _id: string, is_starred: boolean }
|
||||
): Promise<boolean> => (
|
||||
axios.post(API.SET_STARRED, { id, token, address: _id, is_starred })
|
||||
.then(() => true)
|
||||
.catch(() => true)
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue