From 8f60a5efd6abb69afd75a47a162411f70e958e84 Mon Sep 17 00:00:00 2001 From: muerwre Date: Fri, 29 Mar 2019 11:31:04 +0700 Subject: [PATCH] complete setting and editing description --- backend/models/Route.js | 36 +++++++------- backend/routes/route/post.js | 19 +++++--- package-lock.json | 2 +- src/components/dialogs/SaveDialog.tsx | 4 +- src/components/dialogs/TitleDialog.tsx | 65 ++++++++++++++------------ src/redux/user/actions.ts | 23 ++++++--- src/redux/user/reducer.ts | 6 ++- src/redux/user/sagas.ts | 19 ++++---- src/styles/panel.less | 33 +++++++------ src/utils/format.ts | 2 + 10 files changed, 118 insertions(+), 91 deletions(-) diff --git a/backend/models/Route.js b/backend/models/Route.js index 32c3696..be39b3e 100644 --- a/backend/models/Route.js +++ b/backend/models/Route.js @@ -2,24 +2,22 @@ const mongoose = require('mongoose'); const { Schema } = mongoose; -const RouteSchema = new Schema( - { - _id: { type: String, required: true }, - title: { type: String, default: '' }, - // address: { type: String, required: true }, - version: { type: Number, default: 2 }, - route: { type: Array, default: [] }, - stickers: { type: Array, default: [] }, - owner: { type: Schema.Types.ObjectId, ref: 'User' }, - distance: { type: Number, default: 0 }, - is_public: { type: Boolean, default: false }, - is_starred: { type: Boolean, default: false }, - is_deleted: { type: Boolean, default: false }, - created_at: { type: Date, default: Date.now() }, - updated_at: { type: Date, default: Date.now() }, - logo: { type: String, default: 'DEFAULT' }, - provider: { type: String, default: 'DEFAULT' }, - }, -); +const RouteSchema = new Schema({ + _id: { type: String, required: true }, + title: { type: String, default: '' }, + version: { type: Number, default: 2 }, + route: { type: Array, default: [] }, + stickers: { type: Array, default: [] }, + owner: { type: Schema.Types.ObjectId, ref: 'User' }, + distance: { type: Number, default: 0 }, + is_public: { type: Boolean, default: false }, + is_starred: { type: Boolean, default: false }, + is_deleted: { type: Boolean, default: false }, + created_at: { type: Date, default: Date.now() }, + updated_at: { type: Date, default: Date.now() }, + logo: { type: String, default: 'DEFAULT' }, + provider: { type: String, default: 'DEFAULT' }, + description: { type: String, default: '' }, +}); module.exports.RouteSchema = RouteSchema; diff --git a/backend/routes/route/post.js b/backend/routes/route/post.js index f2ac8af..e1251f4 100644 --- a/backend/routes/route/post.js +++ b/backend/routes/route/post.js @@ -1,14 +1,21 @@ const { User, Route } = require('../../models'); -const { parseRoute, parseStickers, parseString, parseNumber, parseAddress } = require('../../utils/parse'); +const { + parseRoute, parseStickers, parseString, parseNumber, parseAddress +} = require('../../utils/parse'); module.exports = async (req, res) => { const { body, body: { id, token, force } } = req; const owner = await User.findOne({ _id: id, token }).populate('routes'); - if (!owner) return res.send({ success: false, reason: 'unauthorized', id, token }); + if (!owner) { + return res.send({ + success: false, reason: 'unauthorized', id, token + }); + } const title = parseString(body.title, 64); + const description = parseString(body.description, 256); const address = parseAddress(body.address, 32); const route = parseRoute(body.route); const stickers = parseStickers(body.stickers); @@ -28,23 +35,23 @@ module.exports = async (req, res) => { if (exists) { 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, description, }).save(); return res.send({ - success: true, title, address, route, stickers, mode: 'overwrited', is_public, + success: true, title, address, route, stickers, mode: 'overwrited', is_public, description, }); } const created = await Route.create({ - _id: address, title, route, stickers, owner, logo, distance, provider, is_public, + _id: address, title, route, stickers, owner, logo, distance, provider, is_public, description, }); await owner.routes.push(created); await owner.save(); return res.send({ - success: true, title, address, route, stickers, provider, is_public, + success: true, title, address, route, stickers, provider, is_public, description, }); }; diff --git a/package-lock.json b/package-lock.json index 6d5716b..c0a0544 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10750,7 +10750,7 @@ } }, "react-expandable-textarea": { - "version": "github:muerwre/react-expandable-textarea#9a3b826abd5203c5d6adf77cf4bfdd10131de417", + "version": "github:muerwre/react-expandable-textarea#0cbcbbd875439090a2d48e711da241f2a83dd6b2", "from": "github:muerwre/react-expandable-textarea", "requires": { "classnames": "^2.2.6" diff --git a/src/components/dialogs/SaveDialog.tsx b/src/components/dialogs/SaveDialog.tsx index 5ce7e31..aa1f521 100644 --- a/src/components/dialogs/SaveDialog.tsx +++ b/src/components/dialogs/SaveDialog.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { copyToClipboard, getUrlData } from '$utils/history'; -import { toTranslit } from '$utils/format'; +import { toTranslit, parseDesc } from '$utils/format'; import { TIPS } from '$constants/tips'; import { MODES } from '$constants/modes'; import { Icon } from '$components/panels/Icon'; @@ -116,7 +116,7 @@ export class SaveDialog extends React.Component { minRows={2} maxRows={5} placeholder="Описание маршрута" - value={description} + value={parseDesc(description)} onChange={this.setDescription} /> diff --git a/src/components/dialogs/TitleDialog.tsx b/src/components/dialogs/TitleDialog.tsx index 018d211..3537c11 100644 --- a/src/components/dialogs/TitleDialog.tsx +++ b/src/components/dialogs/TitleDialog.tsx @@ -5,10 +5,13 @@ import { connect } from 'react-redux'; import classnames from 'classnames'; import { getStyle } from "$utils/dom"; import { nearestInt } from "$utils/geom"; +import { IRootState } from "$redux/user/reducer"; +import { parseDesc } from "$utils/format"; interface ITitleDialogProps { - editing: boolean, - title?: string, + editing: IRootState['editing'], + title?: IRootState['title'], + description?: IRootState['description'], minLines?: number, maxLines?: number, } @@ -30,10 +33,14 @@ export class Component extends React.PureComponent this.setState({ raised: false }); componentDidMount() { - this.getMaxHeight(); + this.setMaxHeight(); } - getMaxHeight = (): number => { + componentDidUpdate() { + this.setMaxHeight(); + } + + setMaxHeight = (): number => { if (!this.ref_sizer || !this.ref_title || !this.ref_text) return 0; const { height: sizer_height } = this.ref_sizer.getBoundingClientRect(); @@ -52,7 +59,7 @@ export class Component extends React.PureComponent - { - title && -
{ this.ref_title = el; }} - > -

{title}

-
- } - { -
{ this.ref_overflow = el; }} - > -
{ this.ref_text = el; }} - > +
{ this.ref_title = el; }} + > +

{title}

+
-
+
height })} + style={{ + height: (raised ? height_raised : height), + marginBottom: height === 0 ? 0 : 15, + }} + ref={el => { this.ref_overflow = el; }} + > +
{ this.ref_text = el; }} + > + { + parseDesc(description) + }
- } +
@@ -110,7 +115,7 @@ export class Component extends React.PureComponent ({ editing, title }); +const mapStateToProps = ({ user: { editing, title, description } }) => ({ editing, title, description }); const mapDispatchToProps = dispatch => bindActionCreators({ }, dispatch); export const TitleDialog = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/src/redux/user/actions.ts b/src/redux/user/actions.ts index 88417d3..ae42b87 100644 --- a/src/redux/user/actions.ts +++ b/src/redux/user/actions.ts @@ -5,10 +5,10 @@ import { IRootState } from "$redux/user/reducer"; export const setUser = (user: IUser) => ({ type: ACTIONS.SET_USER, user }); export const userLogout = () => ({ type: ACTIONS.USER_LOGOUT }); -export const setEditing = editing => ({ type: ACTIONS.SET_EDITING, editing }); -export const setMode = mode => ({ type: ACTIONS.SET_MODE, mode }); -export const setDistance = distance => ({ type: ACTIONS.SET_DISTANCE, distance }); -export const setChanged = changed => ({ type: ACTIONS.SET_CHANGED, changed }); +export const setEditing = (editing: IRootState['editing']) => ({ type: ACTIONS.SET_EDITING, editing }); +export const setMode = (mode: IRootState['mode']) => ({ type: ACTIONS.SET_MODE, mode }); +export const setDistance = (distance: IRootState['distance']) => ({ type: ACTIONS.SET_DISTANCE, distance }); +export const setChanged = (changed: IRootState['changed']) => ({ type: ACTIONS.SET_CHANGED, changed }); export const setRouterPoints = routerPoints => ({ type: ACTIONS.SET_ROUTER_POINTS, routerPoints }); export const setActiveSticker = activeSticker => ({ type: ACTIONS.SET_ACTIVE_STICKER, activeSticker }); export const setLogo = logo => ({ type: ACTIONS.SET_LOGO, logo }); @@ -44,9 +44,18 @@ export const sendSaveRequest = (payload: { export const resetSaveDialog = () => ({ type: ACTIONS.RESET_SAVE_DIALOG }); -export const setSaveLoading = save_loading => ({ type: ACTIONS.SET_SAVE_LOADING, save_loading }); -export const setSaveSuccess = payload => ({ type: ACTIONS.SET_SAVE_SUCCESS, ...payload }); -export const setSaveError = save_error => ({ type: ACTIONS.SET_SAVE_ERROR, save_error }); +export const setSaveLoading = (save_loading: IRootState['save_loading']) => ({ type: ACTIONS.SET_SAVE_LOADING, save_loading }); + +export const setSaveSuccess = (payload: { + address: IRootState['address'], + title: IRootState['address'], + is_public: IRootState['is_public'], + description: IRootState['description'], + + save_error: string, +}) => ({ type: ACTIONS.SET_SAVE_SUCCESS, ...payload }); + +export const setSaveError = (save_error: IRootState['save_error']) => ({ type: ACTIONS.SET_SAVE_ERROR, save_error }); export const setSaveOverwrite = () => ({ type: ACTIONS.SET_SAVE_OVERWRITE }); export const hideRenderer = () => ({ type: ACTIONS.HIDE_RENDERER }); diff --git a/src/redux/user/reducer.ts b/src/redux/user/reducer.ts index 389e29d..00d2aaa 100644 --- a/src/redux/user/reducer.ts +++ b/src/redux/user/reducer.ts @@ -179,7 +179,11 @@ const setSaveOverwrite: ActionHandler = }); const setSaveSuccess: ActionHandler = (state, { save_error }) => ({ - ...state, save_overwriting: false, save_finished: true, save_processing: false, save_error + ...state, + save_overwriting: false, + save_finished: true, + save_processing: false, + save_error, }); const resetSaveDialog: ActionHandler = (state) => ({ diff --git a/src/redux/user/sagas.ts b/src/redux/user/sagas.ts index 51b1156..b608a43 100644 --- a/src/redux/user/sagas.ts +++ b/src/redux/user/sagas.ts @@ -32,7 +32,7 @@ import { setProvider, changeProvider, setSaveLoading, - mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle, setRouteStarred, + mapsSetShift, searchChangeDistance, clearAll, setFeature, searchSetTitle, setRouteStarred, setDescription, } from '$redux/user/actions'; import { getUrlData, parseQuery, pushLoaderState, pushNetworkInitError, pushPath, replacePath } from '$utils/history'; import { editor } from '$modules/Editor'; @@ -333,17 +333,15 @@ function* sendSaveRequestSaga({ if (timeout || !result || !result.success || !result.address) return yield put(setSaveError(TIPS.SAVE_TIMED_OUT)); return yield put(setSaveSuccess({ - address: result.address, save_error: TIPS.SAVE_SUCCESS, title, is_public: result.is_public + address: result.address, + title: result.title, + is_public: result.is_public, + description: result.description, + + save_error: TIPS.SAVE_SUCCESS, })); } -// function* refreshUserData() { -// const user = yield select(getUser); -// const data = yield call(checkUserToken, user); -// -// return yield put(setUser(data)); -// } - function* getRenderData() { yield put(setRenderer({ info: 'Загрузка тайлов', progress: 0.1 })); @@ -570,7 +568,7 @@ function* searchSetTabSaga() { } function* setSaveSuccessSaga({ - address, title, is_public + address, title, is_public, description, }: ReturnType) { const { id } = yield select(getUser); const { dialog_active } = yield select(getState); @@ -580,6 +578,7 @@ function* setSaveSuccessSaga({ yield put(setTitle(title)); yield put(setAddress(address)); yield put(setPublic(is_public)); + yield put(setDescription(description)); yield put(setChanged(false)); yield editor.owner = { id }; diff --git a/src/styles/panel.less b/src/styles/panel.less index 4c24f79..ad555a3 100644 --- a/src/styles/panel.less +++ b/src/styles/panel.less @@ -685,24 +685,27 @@ > div { margin: 10px; + white-space: pre-line; } - ::after { - content: ' '; - width: 100%; - height: 40px; - background: linear-gradient(fade(@dialog_background, 0), @dialog_background); - position: absolute; - bottom: 0; - left: 0; - transition: opacity 250ms; - pointer-events: none; - touch-action: none; - } - - &:hover { + &.has_shade { ::after { - opacity: 0; + content: ' '; + width: 100%; + height: 40px; + background: linear-gradient(fade(@dialog_background, 0), @dialog_background); + position: absolute; + bottom: 0; + left: 0; + transition: opacity 250ms; + pointer-events: none; + touch-action: none; + } + + &:hover { + ::after { + opacity: 0; + } } } } diff --git a/src/utils/format.ts b/src/utils/format.ts index f0c55a5..5710576 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -13,3 +13,5 @@ export const toHours = (info: number): string => { export const toTranslit = (string: string): string => ( removeGarbage(ru.reduce((text, el, i) => (text.replace(new RegExp(ru[i], 'g'), en[i])), (String(string) || ''))) ); + +export const parseDesc = (text: string): string => text.replace(/(\n{2,})/ig, "\n\n");