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
|
@ -13,6 +13,7 @@ const RouteSchema = new Schema(
|
||||||
owner: { type: Schema.Types.ObjectId, ref: 'User' },
|
owner: { type: Schema.Types.ObjectId, ref: 'User' },
|
||||||
distance: { type: Number, default: 0 },
|
distance: { type: Number, default: 0 },
|
||||||
is_public: { type: Boolean, default: false },
|
is_public: { type: Boolean, default: false },
|
||||||
|
is_starred: { type: Boolean, default: false },
|
||||||
is_deleted: { type: Boolean, default: false },
|
is_deleted: { type: Boolean, default: false },
|
||||||
created_at: { type: Date, default: Date.now() },
|
created_at: { type: Date, default: Date.now() },
|
||||||
updated_at: { type: Date, default: Date.now() },
|
updated_at: { type: Date, default: Date.now() },
|
||||||
|
|
|
@ -4,9 +4,11 @@ const get = require('./route/get');
|
||||||
const list = require('./route/list');
|
const list = require('./route/list');
|
||||||
const drop = require('./route/drop');
|
const drop = require('./route/drop');
|
||||||
const patch = require('./route/patch');
|
const patch = require('./route/patch');
|
||||||
|
const star = require('./route/star');
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post('/star', star);
|
||||||
router.post('/', post);
|
router.post('/', post);
|
||||||
router.get('/', get);
|
router.get('/', get);
|
||||||
router.patch('/', patch);
|
router.patch('/', patch);
|
||||||
|
|
|
@ -3,10 +3,12 @@ const { Route, User } = require('../../models');
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const {
|
const {
|
||||||
query: {
|
query: {
|
||||||
id, token, title, distance, author, step = 20, shift = 0,
|
id, token, title, distance, author, step = 20, shift = 0, starred,
|
||||||
}
|
}
|
||||||
} = req;
|
} = req;
|
||||||
|
|
||||||
|
const is_starred = parseInt(starred, 10) === 1;
|
||||||
|
|
||||||
const user = await User.findOne({ _id: id, token });
|
const user = await User.findOne({ _id: id, token });
|
||||||
|
|
||||||
let criteria = { is_deleted: false };
|
let criteria = { is_deleted: false };
|
||||||
|
@ -14,13 +16,21 @@ module.exports = async (req, res) => {
|
||||||
if (title) {
|
if (title) {
|
||||||
criteria = {
|
criteria = {
|
||||||
...criteria,
|
...criteria,
|
||||||
$or: [
|
$and: [
|
||||||
{ title: new RegExp(title.trim(), 'ig') },
|
{ title: new RegExp(title.trim(), 'ig') },
|
||||||
{ _id: new RegExp(title.trim(), 'ig') },
|
{ _id: new RegExp(title.trim(), 'ig') },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('is starred?', starred);
|
||||||
|
if (is_starred) {
|
||||||
|
criteria = {
|
||||||
|
...criteria,
|
||||||
|
is_starred: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!author || !user || (user._id !== author)) {
|
if (!author || !user || (user._id !== author)) {
|
||||||
criteria = {
|
criteria = {
|
||||||
...criteria,
|
...criteria,
|
||||||
|
@ -32,7 +42,7 @@ module.exports = async (req, res) => {
|
||||||
{
|
{
|
||||||
...criteria,
|
...criteria,
|
||||||
},
|
},
|
||||||
'_id title distance owner updated_at is_public is_deleted',
|
'_id title distance owner updated_at is_public is_deleted is_starred',
|
||||||
{
|
{
|
||||||
limit: 9000,
|
limit: 9000,
|
||||||
sort: { updated_at: -1 },
|
sort: { updated_at: -1 },
|
||||||
|
|
|
@ -17,7 +17,7 @@ module.exports = async (req, res) => {
|
||||||
if (!exists) return res.send({ success: false, mode: 'not_exists' });
|
if (!exists) return res.send({ success: false, mode: 'not_exists' });
|
||||||
if (exists && exists.owner._id !== id) return res.send({ success: false, mode: 'not_yours' });
|
if (exists && exists.owner._id !== id) return res.send({ success: false, mode: 'not_yours' });
|
||||||
|
|
||||||
exists.set({ title, is_public }).save();
|
await exists.set({ title, is_public }).save();
|
||||||
|
|
||||||
return res.send({ success: true, ...exists });
|
return res.send({ success: true, ...exists });
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,7 +27,7 @@ module.exports = async (req, res) => {
|
||||||
if (exists && !force) return res.send({ success: false, mode: 'overwriting' });
|
if (exists && !force) return res.send({ success: false, mode: 'overwriting' });
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
exists.set({
|
await exists.set({
|
||||||
title, route, stickers, logo, distance, updated_at: Date.now(), provider, is_public,
|
title, route, stickers, logo, distance, updated_at: Date.now(), provider, is_public,
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
|
|
19
backend/routes/route/star.js
Normal file
19
backend/routes/route/star.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
const { User, Route } = require('../../models');
|
||||||
|
|
||||||
|
module.exports = async (req, res) => {
|
||||||
|
const { body, body: { id, token, address } } = req;
|
||||||
|
|
||||||
|
const owner = await User.findOne({ _id: id, token }).populate('routes');
|
||||||
|
|
||||||
|
if (!owner || owner.role !== 'admin') return res.send({ success: false, reason: 'unauthorized' });
|
||||||
|
|
||||||
|
const is_starred = !!body.is_starred;
|
||||||
|
|
||||||
|
const exists = await Route.findOne({ _id: address }).populate('owner', '_id');
|
||||||
|
if (!exists) return res.send({ success: false, mode: 'not_exists' });
|
||||||
|
|
||||||
|
await exists.set({ is_starred }).save();
|
||||||
|
|
||||||
|
return res.send({ success: true, ...exists });
|
||||||
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
setDialogActive,
|
setDialogActive,
|
||||||
mapsLoadMore,
|
mapsLoadMore,
|
||||||
dropRoute,
|
dropRoute,
|
||||||
modifyRoute,
|
modifyRoute, toggleRouteStarred,
|
||||||
} from '$redux/user/actions';
|
} from '$redux/user/actions';
|
||||||
import { isMobile } from '$utils/window';
|
import { isMobile } from '$utils/window';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -21,12 +21,14 @@ import { TABS } from '$constants/dialogs';
|
||||||
import { Icon } from '$components/panels/Icon';
|
import { Icon } from '$components/panels/Icon';
|
||||||
import { pushPath } from '$utils/history';
|
import { pushPath } from '$utils/history';
|
||||||
import { IRootState, IRouteListItem } from '$redux/user/reducer';
|
import { IRootState, IRouteListItem } from '$redux/user/reducer';
|
||||||
|
import { ROLES } from "$constants/auth";
|
||||||
|
|
||||||
export interface IMapListDialogProps extends IRootState {
|
export interface IMapListDialogProps extends IRootState {
|
||||||
marks: { [x: number]: string },
|
marks: { [x: number]: string },
|
||||||
routes_sorted: Array<IRouteListItem>,
|
routes_sorted: Array<IRouteListItem>,
|
||||||
routes: IRootState['routes'],
|
routes: IRootState['routes'],
|
||||||
ready: IRootState['ready'],
|
ready: IRootState['ready'],
|
||||||
|
role: IRootState['user']['role'],
|
||||||
|
|
||||||
mapsLoadMore: typeof mapsLoadMore,
|
mapsLoadMore: typeof mapsLoadMore,
|
||||||
searchSetDistance: typeof searchSetDistance,
|
searchSetDistance: typeof searchSetDistance,
|
||||||
|
@ -35,6 +37,7 @@ export interface IMapListDialogProps extends IRootState {
|
||||||
setDialogActive: typeof setDialogActive,
|
setDialogActive: typeof setDialogActive,
|
||||||
dropRoute: typeof dropRoute,
|
dropRoute: typeof dropRoute,
|
||||||
modifyRoute: typeof modifyRoute,
|
modifyRoute: typeof modifyRoute,
|
||||||
|
toggleRouteStarred: typeof toggleRouteStarred,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMapListDialogState {
|
export interface IMapListDialogState {
|
||||||
|
@ -117,9 +120,12 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
||||||
this.stopEditing();
|
this.stopEditing();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
toggleStarred = (id: string) => this.props.toggleRouteStarred(id);
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
ready,
|
ready,
|
||||||
|
role,
|
||||||
routes: {
|
routes: {
|
||||||
list,
|
list,
|
||||||
loading,
|
loading,
|
||||||
|
@ -136,6 +142,8 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
||||||
|
|
||||||
const { editor_target, menu_target, is_editing, is_dropping } = this.state;
|
const { editor_target, menu_target, is_editing, is_dropping } = this.state;
|
||||||
|
|
||||||
|
console.log('role', this.props.role);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dialog-content">
|
<div className="dialog-content">
|
||||||
{ list.length === 0 && loading &&
|
{ list.length === 0 && loading &&
|
||||||
|
@ -156,7 +164,7 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
||||||
}
|
}
|
||||||
<div className="dialog-tabs">
|
<div className="dialog-tabs">
|
||||||
{
|
{
|
||||||
Object.keys(TABS).map(item => (
|
Object.keys(TABS).map(item => (role === ROLES.admin || item !== 'all') && (
|
||||||
<div
|
<div
|
||||||
className={classnames('dialog-tab', { active: tab === item })}
|
className={classnames('dialog-tab', { active: tab === item })}
|
||||||
onClick={() => this.props.searchSetTab(item)}
|
onClick={() => this.props.searchSetTab(item)}
|
||||||
|
@ -208,6 +216,7 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
||||||
distance={route.distance}
|
distance={route.distance}
|
||||||
_id={route._id}
|
_id={route._id}
|
||||||
is_public={route.is_public}
|
is_public={route.is_public}
|
||||||
|
is_starred={route.is_starred}
|
||||||
tab={tab}
|
tab={tab}
|
||||||
is_editing_mode={is_dropping ? 'drop' : 'edit'}
|
is_editing_mode={is_dropping ? 'drop' : 'edit'}
|
||||||
is_editing_target={editor_target === route._id}
|
is_editing_target={editor_target === route._id}
|
||||||
|
@ -220,7 +229,9 @@ class Component extends React.Component<IMapListDialogProps, IMapListDialogState
|
||||||
showDropCard={this.showDropCard}
|
showDropCard={this.showDropCard}
|
||||||
dropRoute={this.dropRoute}
|
dropRoute={this.dropRoute}
|
||||||
modifyRoute={this.modifyRoute}
|
modifyRoute={this.modifyRoute}
|
||||||
|
toggleStarred={this.toggleStarred}
|
||||||
key={route._id}
|
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) {
|
if (routes.filter.max >= 9999) {
|
||||||
return {
|
return {
|
||||||
routes, editing, marks: {}, ready: false,
|
routes, editing, marks: {}, ready: false, role,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return ({
|
return ({
|
||||||
|
role,
|
||||||
routes,
|
routes,
|
||||||
editing,
|
editing,
|
||||||
ready: true,
|
ready: true,
|
||||||
|
@ -260,6 +273,7 @@ const mapDispatchToProps = dispatch => bindActionCreators({
|
||||||
mapsLoadMore,
|
mapsLoadMore,
|
||||||
dropRoute,
|
dropRoute,
|
||||||
modifyRoute,
|
modifyRoute,
|
||||||
|
toggleRouteStarred,
|
||||||
}, dispatch);
|
}, dispatch);
|
||||||
|
|
||||||
export const MapListDialog = connect(mapStateToProps, mapDispatchToProps)(Component);
|
export const MapListDialog = connect(mapStateToProps, mapDispatchToProps)(Component);
|
||||||
|
|
|
@ -5,15 +5,20 @@ import { MapListDialog } from "$components/dialogs/MapListDialog";
|
||||||
import { Tooltip } from "$components/panels/Tooltip";
|
import { Tooltip } from "$components/panels/Tooltip";
|
||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { toggleRouteStarred } from "$redux/user/actions";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
_id: string,
|
|
||||||
tab: string,
|
tab: string,
|
||||||
|
|
||||||
|
_id: string,
|
||||||
title: string,
|
title: string,
|
||||||
distance: number,
|
distance: number,
|
||||||
is_public: boolean,
|
is_public: boolean,
|
||||||
|
is_admin: boolean,
|
||||||
|
is_starred: boolean,
|
||||||
|
|
||||||
openRoute: typeof MapListDialog.openRoute,
|
openRoute: typeof MapListDialog.openRoute,
|
||||||
|
toggleStarred: typeof MapListDialog.toggleStarred,
|
||||||
startEditing: typeof MapListDialog.startEditing,
|
startEditing: typeof MapListDialog.startEditing,
|
||||||
stopEditing: typeof MapListDialog.stopEditing,
|
stopEditing: typeof MapListDialog.stopEditing,
|
||||||
showMenu: typeof MapListDialog.showMenu,
|
showMenu: typeof MapListDialog.showMenu,
|
||||||
|
@ -22,11 +27,21 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RouteRowView = ({
|
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> => (
|
}: Props): ReactElement<Props, null> => (
|
||||||
<div
|
<div
|
||||||
className={classnames('route-row-view', { has_menu: (tab === 'mine') })}
|
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
|
<div
|
||||||
className="route-row"
|
className="route-row"
|
||||||
onClick={() => openRoute(_id)}
|
onClick={() => openRoute(_id)}
|
||||||
|
|
|
@ -12,7 +12,9 @@ interface Props {
|
||||||
title: string,
|
title: string,
|
||||||
distance: number,
|
distance: number,
|
||||||
is_public: boolean,
|
is_public: boolean,
|
||||||
|
is_starred: boolean,
|
||||||
|
|
||||||
|
is_admin: boolean,
|
||||||
is_editing_target: boolean,
|
is_editing_target: boolean,
|
||||||
is_menu_target: boolean,
|
is_menu_target: boolean,
|
||||||
|
|
||||||
|
@ -24,6 +26,7 @@ interface Props {
|
||||||
showDropCard: typeof MapListDialog.showDropCard,
|
showDropCard: typeof MapListDialog.showDropCard,
|
||||||
dropRoute: typeof MapListDialog.dropRoute,
|
dropRoute: typeof MapListDialog.dropRoute,
|
||||||
modifyRoute: typeof MapListDialog.modifyRoute,
|
modifyRoute: typeof MapListDialog.modifyRoute,
|
||||||
|
toggleStarred: typeof MapListDialog.toggleStarred,
|
||||||
|
|
||||||
is_editing_mode: 'edit' | 'drop',
|
is_editing_mode: 'edit' | 'drop',
|
||||||
}
|
}
|
||||||
|
@ -31,7 +34,7 @@ interface Props {
|
||||||
export const RouteRowWrapper = ({
|
export const RouteRowWrapper = ({
|
||||||
title, distance, _id, openRoute, tab, startEditing, showMenu,
|
title, distance, _id, openRoute, tab, startEditing, showMenu,
|
||||||
showDropCard, is_public, is_editing_target, is_menu_target, is_editing_mode,
|
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> => (
|
}: Props): ReactElement<Props, null> => (
|
||||||
<div
|
<div
|
||||||
className={classnames('route-row-wrapper', {
|
className={classnames('route-row-wrapper', {
|
||||||
|
@ -64,12 +67,15 @@ export const RouteRowWrapper = ({
|
||||||
title={title}
|
title={title}
|
||||||
distance={distance}
|
distance={distance}
|
||||||
is_public={is_public}
|
is_public={is_public}
|
||||||
|
is_starred={is_starred}
|
||||||
openRoute={openRoute}
|
openRoute={openRoute}
|
||||||
startEditing={startEditing}
|
startEditing={startEditing}
|
||||||
stopEditing={stopEditing}
|
stopEditing={stopEditing}
|
||||||
showMenu={showMenu}
|
showMenu={showMenu}
|
||||||
hideMenu={hideMenu}
|
hideMenu={hideMenu}
|
||||||
showDropCard={showDropCard}
|
showDropCard={showDropCard}
|
||||||
|
is_admin={is_admin}
|
||||||
|
toggleStarred={toggleStarred}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,4 +10,5 @@ export const API: { [x: string]: string } = {
|
||||||
|
|
||||||
DROP_ROUTE: `${CLIENT.API_ADDR}/route`,
|
DROP_ROUTE: `${CLIENT.API_ADDR}/route`,
|
||||||
MODIFY_ROUTE: `${CLIENT.API_ADDR}/route/modify`,
|
MODIFY_ROUTE: `${CLIENT.API_ADDR}/route/modify`,
|
||||||
|
SET_STARRED: `${CLIENT.API_ADDR}/route/star`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export interface IRoles {
|
export interface IRoles {
|
||||||
guest: string,
|
guest: string,
|
||||||
vk: string,
|
vk: string,
|
||||||
|
admin: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUser {
|
export interface IUser {
|
||||||
|
@ -24,6 +25,7 @@ export interface IUser {
|
||||||
export const ROLES: IRoles = {
|
export const ROLES: IRoles = {
|
||||||
guest: 'guest',
|
guest: 'guest',
|
||||||
vk: 'vk',
|
vk: 'vk',
|
||||||
|
admin: 'admin',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_USER: IUser = {
|
export const DEFAULT_USER: IUser = {
|
||||||
|
|
|
@ -7,6 +7,7 @@ export interface IDialogs {
|
||||||
export interface IMapTabs {
|
export interface IMapTabs {
|
||||||
mine: string,
|
mine: string,
|
||||||
all: string,
|
all: string,
|
||||||
|
starred: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DIALOGS: IDialogs = ({
|
export const DIALOGS: IDialogs = ({
|
||||||
|
@ -17,5 +18,6 @@ export const DIALOGS: IDialogs = ({
|
||||||
|
|
||||||
export const TABS: IMapTabs = ({
|
export const TABS: IMapTabs = ({
|
||||||
mine: 'Мои',
|
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 }) => ({
|
export const modifyRoute = (_id: string, { title, is_public }: { title: string, is_public: boolean }) => ({
|
||||||
type: ACTIONS.MODIFY_ROUTE, _id, title, is_public
|
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',
|
DROP_ROUTE: 'DROP_ROUTE',
|
||||||
MODIFY_ROUTE: 'MODIFY_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,
|
title: string,
|
||||||
distance: number,
|
distance: number,
|
||||||
is_public: boolean,
|
is_public: boolean,
|
||||||
|
is_starred: boolean,
|
||||||
updated_at: string,
|
updated_at: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +305,18 @@ const setIsRouting: ActionHandler<typeof ActionCreators.setIsRouting> = (state,
|
||||||
is_routing,
|
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 = ({
|
const HANDLERS = ({
|
||||||
[ACTIONS.SET_USER]: setUser,
|
[ACTIONS.SET_USER]: setUser,
|
||||||
[ACTIONS.SET_EDITING]: setEditing,
|
[ACTIONS.SET_EDITING]: setEditing,
|
||||||
|
@ -348,6 +361,8 @@ const HANDLERS = ({
|
||||||
|
|
||||||
[ACTIONS.SET_FEATURE]: setFeature,
|
[ACTIONS.SET_FEATURE]: setFeature,
|
||||||
[ACTIONS.SET_IS_ROUTING]: setIsRouting,
|
[ACTIONS.SET_IS_ROUTING]: setIsRouting,
|
||||||
|
|
||||||
|
[ACTIONS.SET_ROUTE_STARRED]: setRouteStarred,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const INITIAL_STATE: IRootReducer = {
|
export const INITIAL_STATE: IRootReducer = {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
checkUserToken, dropRoute,
|
checkUserToken, dropRoute,
|
||||||
getGuestToken, getRouteList,
|
getGuestToken, getRouteList,
|
||||||
getStoredMap, modifyRoute,
|
getStoredMap, modifyRoute,
|
||||||
postMap
|
postMap, sendRouteStarred
|
||||||
} from '$utils/api';
|
} from '$utils/api';
|
||||||
import {
|
import {
|
||||||
hideRenderer,
|
hideRenderer,
|
||||||
|
@ -32,7 +32,7 @@ import {
|
||||||
setProvider,
|
setProvider,
|
||||||
changeProvider,
|
changeProvider,
|
||||||
setSaveLoading,
|
setSaveLoading,
|
||||||
mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle,
|
mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle, setRouteStarred,
|
||||||
} from '$redux/user/actions';
|
} from '$redux/user/actions';
|
||||||
import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history';
|
import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history';
|
||||||
import { editor } from '$modules/Editor';
|
import { editor } from '$modules/Editor';
|
||||||
|
@ -51,7 +51,7 @@ import {
|
||||||
} from '$utils/renderer';
|
} from '$utils/renderer';
|
||||||
import { ILogos, LOGOS } from '$constants/logos';
|
import { ILogos, LOGOS } from '$constants/logos';
|
||||||
import { DEFAULT_PROVIDER } from '$constants/providers';
|
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 * as ActionCreators from '$redux/user/actions';
|
||||||
import { IRootState } from "$redux/user/reducer";
|
import { IRootState } from "$redux/user/reducer";
|
||||||
|
@ -501,7 +501,7 @@ function* searchGetRoutes() {
|
||||||
|
|
||||||
const { routes: { step, shift, filter: { title, distance, tab } } } = yield select(getState);
|
const { routes: { step, shift, filter: { title, distance, tab } } } = yield select(getState);
|
||||||
|
|
||||||
const result = yield call(getRouteList, {
|
return yield call(getRouteList, {
|
||||||
id,
|
id,
|
||||||
token,
|
token,
|
||||||
title,
|
title,
|
||||||
|
@ -509,10 +509,8 @@ function* searchGetRoutes() {
|
||||||
step,
|
step,
|
||||||
shift,
|
shift,
|
||||||
author: tab === 'mine' ? id : '',
|
author: tab === 'mine' ? id : '',
|
||||||
starred: tab === 'starred',
|
starred: tab === 'starred' ? 1 : 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* searchSetSagaWorker() {
|
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 });
|
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() {
|
export function* userSaga() {
|
||||||
yield takeLatest(REHYDRATE, authCheckSaga);
|
yield takeLatest(REHYDRATE, authCheckSaga);
|
||||||
yield takeEvery(ACTIONS.SET_MODE, setModeSaga);
|
yield takeEvery(ACTIONS.SET_MODE, setModeSaga);
|
||||||
|
@ -744,4 +753,5 @@ export function* userSaga() {
|
||||||
|
|
||||||
yield takeLatest(ACTIONS.DROP_ROUTE, dropRouteSaga);
|
yield takeLatest(ACTIONS.DROP_ROUTE, dropRouteSaga);
|
||||||
yield takeLatest(ACTIONS.MODIFY_ROUTE, modifyRouteSaga);
|
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)"/>
|
<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>
|
||||||
|
|
||||||
|
<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">
|
<g id="icon-cluster-1" stroke="none">
|
||||||
<rect x="0" y="0" width="32" height="32" fill="black" stroke="none" />
|
<rect x="0" y="0" width="32" height="32" fill="black" stroke="none" />
|
||||||
<circle cx="10" cy="21" fill="white" r="4" />
|
<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 {
|
&.is_menu_target {
|
||||||
.route-row {
|
.route-row, .route-row-fav {
|
||||||
transform: translateX(-120px);
|
transform: translateX(-120px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +222,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height 500ms;
|
transition: height 500ms;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
&.has_menu {
|
&.has_menu {
|
||||||
padding-right: 32px;
|
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 {
|
.route-row-edit-menu {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -29,7 +29,7 @@ interface IGetRouteList {
|
||||||
author: IRootState['routes']['filter']['author'],
|
author: IRootState['routes']['filter']['author'],
|
||||||
step: IRootState['routes']['step'],
|
step: IRootState['routes']['step'],
|
||||||
shift: IRootState['routes']['step'],
|
shift: IRootState['routes']['step'],
|
||||||
starred: IRootState['routes']['filter']['starred'],
|
starred: number,
|
||||||
id: IRootState['user']['id'],
|
id: IRootState['user']['id'],
|
||||||
token: IRootState['user']['token'],
|
token: IRootState['user']['token'],
|
||||||
}
|
}
|
||||||
|
@ -112,3 +112,13 @@ export const modifyRoute = (
|
||||||
): AxiosPromise<any> => (
|
): AxiosPromise<any> => (
|
||||||
axios.patch(API.DROP_ROUTE, { address, id, token, title, is_public })
|
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