From af8d2704608a1c68460d71abb9c29050ce9845e9 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Wed, 8 Jan 2020 14:25:46 +0700 Subject: [PATCH] cleaned out user reducer --- src/components/StickerDesc.tsx | 4 +- src/components/dialogs/SaveDialog.tsx | 137 ++++++++++++-------- src/components/dialogs/TitleDialog.tsx | 88 ++++++++----- src/components/panels/EditorDialog.tsx | 3 - src/components/panels/TopRightPanel.tsx | 123 +++++++++--------- src/components/panels/UserPanel.tsx | 114 +++++++---------- src/components/renderer/Renderer.tsx | 3 +- src/redux/map/types.ts | 35 +++-- src/redux/user/actions.ts | 17 +-- src/redux/user/index.ts | 49 +++---- src/utils/api.ts | 5 +- src/utils/format.ts | 162 +++++++++++++++++++++++- src/utils/renderer.ts | 4 +- 13 files changed, 462 insertions(+), 282 deletions(-) diff --git a/src/components/StickerDesc.tsx b/src/components/StickerDesc.tsx index d2e90db..44c5ee4 100644 --- a/src/components/StickerDesc.tsx +++ b/src/components/StickerDesc.tsx @@ -10,7 +10,7 @@ type State = { text: String; } -export class StickerDesc extends React.PureComponent { +class StickerDesc extends React.PureComponent { state = { text: this.props.value, }; @@ -56,3 +56,5 @@ export class StickerDesc extends React.PureComponent { ) } } + +export { StickerDesc }; \ No newline at end of file diff --git a/src/components/dialogs/SaveDialog.tsx b/src/components/dialogs/SaveDialog.tsx index 7f66b14..4fbd879 100644 --- a/src/components/dialogs/SaveDialog.tsx +++ b/src/components/dialogs/SaveDialog.tsx @@ -7,32 +7,32 @@ import { Icon } from '$components/panels/Icon'; import { Switch } from '$components/Switch'; import classnames from 'classnames'; -import { sendSaveRequest, setMode } from "$redux/user/actions"; import ExpandableTextarea from 'react-expandable-textarea'; +import { connect } from 'react-redux'; +import { selectMap } from '$redux/map/selectors'; +import { selectUser } from '$redux/user/selectors'; +import * as USER_ACTIONS from '$redux/user/actions'; -interface Props { - address: string, - title: string, - is_public: boolean, +const mapStateToProps = state => ({ + map: selectMap(state), + user: selectUser(state), +}); - width: number, - setMode: typeof setMode, - sendSaveRequest: typeof sendSaveRequest, - save_error: string, +const mapDispatchToProps = { + setMode: USER_ACTIONS.setMode, + sendSaveRequest: USER_ACTIONS.sendSaveRequest, +}; - save_loading: boolean, - save_finished: boolean, - save_overwriting: boolean, -} +type Props = ReturnType & typeof mapDispatchToProps & { width: number }; interface State { - address: string, - title: string, - is_public: boolean, - description: string, + address: string; + title: string; + is_public: boolean; + description: string; } -export class SaveDialog extends React.Component { +class SaveDialogUnconnected extends React.Component { constructor(props) { super(props); @@ -48,21 +48,30 @@ export class SaveDialog extends React.Component { const { path } = getUrlData(); const { title, address } = this.state; - return toTranslit(address.trim()) || toTranslit(title.trim().toLowerCase()).substr(0, 32) || toTranslit(path.trim()).substr(0, 32); + return ( + toTranslit(address.trim()) || + toTranslit(title.trim().toLowerCase()).substr(0, 32) || + toTranslit(path.trim()).substr(0, 32) + ); }; - setTitle = ({ target: { value } }) => this.setState({ title: ((value && value.substr(0, 64)) || '') }); - setAddress = ({ target: { value } }) => this.setState({ address: (value && value.substr(0, 32) || '') }); - setDescription = ({ target: { value } }) => this.setState({ description: (value && value.substr(0, 256) || '') }); - - + setTitle = ({ target: { value } }) => + this.setState({ title: (value && value.substr(0, 64)) || '' }); + setAddress = ({ target: { value } }) => + this.setState({ address: (value && value.substr(0, 32)) || '' }); + setDescription = ({ target: { value } }) => + this.setState({ description: (value && value.substr(0, 256)) || '' }); sendSaveRequest = (e, force = false) => { const { title, is_public, description } = this.state; const address = this.getAddress(); this.props.sendSaveRequest({ - title, address, force, is_public, description, + title, + address, + force, + is_public, + description, }); }; forceSaveRequest = e => this.sendSaveRequest(e, true); @@ -81,7 +90,10 @@ export class SaveDialog extends React.Component { render() { const { title, is_public, description } = this.state; - const { save_error, save_finished, save_overwriting, width, save_loading } = this.props; + const { + user: { save_error, save_finished, save_overwriting, save_loading }, + width, + } = this.props; const { host, protocol } = getUrlData(); return ( @@ -92,13 +104,21 @@ export class SaveDialog extends React.Component {
Название
- +
- + { onChange={this.setDescription} />
-
- { save_error || TIPS.SAVE_INFO } -
+
{save_error || TIPS.SAVE_INFO}
-
+
- { - is_public - ? ' В каталоге карт' - : ' Только по ссылке' - } + {is_public ? ' В каталоге карт' : ' Только по ссылке'}
- { !save_finished && -
Отмена
- } - { - !save_finished && !save_overwriting && -
Сохранить
- } - { - save_overwriting && -
Перезаписать
- } - { save_finished && -
Скопировать
- } - { save_finished && -
Отлично!
- } + {!save_finished && ( +
+ Отмена +
+ )} + {!save_finished && !save_overwriting && ( +
+ Сохранить +
+ )} + {save_overwriting && ( +
+ Перезаписать +
+ )} + {save_finished && ( +
+ Скопировать +
+ )} + {save_finished && ( +
+ Отлично! +
+ )}
@@ -159,3 +184,7 @@ export class SaveDialog extends React.Component { ); } } + +const SaveDialog = connect(mapStateToProps, mapDispatchToProps)(SaveDialogUnconnected); + +export { SaveDialog }; diff --git a/src/components/dialogs/TitleDialog.tsx b/src/components/dialogs/TitleDialog.tsx index ae8d990..e1fe86c 100644 --- a/src/components/dialogs/TitleDialog.tsx +++ b/src/components/dialogs/TitleDialog.tsx @@ -1,28 +1,30 @@ import React from 'react'; -import { bindActionCreators } from "redux"; import { connect } from 'react-redux'; import classnames from 'classnames'; -import { getStyle } from "$utils/dom"; -import { nearestInt } from "$utils/geom"; -import { IRootState } from "$redux/user"; -import { parseDesc } from "$utils/format"; +import { getStyle } from '$utils/dom'; +import { nearestInt } from '$utils/geom'; +import { parseDesc } from '$utils/format'; +import { selectUser } from '$redux/user/selectors'; +import { selectMap } from '$redux/map/selectors'; -interface ITitleDialogProps { - editing: IRootState['editing'], - title?: IRootState['title'], - description?: IRootState['description'], - minLines?: number, - maxLines?: number, -} +const mapStateToProps = state => ({ + user: selectUser(state), + map: selectMap(state), +}); -interface ITitleDialogState { +type Props = ReturnType & { + minLines?: number; + maxLines?: number; +}; + +interface State { raised: boolean; height: number; height_raised: number; } -export class Component extends React.PureComponent { +export class TitleDialogUnconnected extends React.PureComponent { state = { raised: false, height: 0, @@ -53,8 +55,9 @@ export class Component extends React.PureComponent -
{ this.ref_sizer = el; }}> +
{ + this.ref_sizer = el; + }} + >
{ this.ref_title = el; }} + ref={el => { + this.ref_title = el; + }} >

{title}

height })} + className={classnames('title-dialog-pane title-dialog-text', { + has_shade: height_raised > height, + })} style={{ - height: (raised ? height_raised : height), + height: raised ? height_raised : height, marginBottom: height === 0 ? 0 : 15, }} - ref={el => { this.ref_overflow = el; }} + ref={el => { + this.ref_overflow = el; + }} >
{ this.ref_text = el; }} + ref={el => { + this.ref_text = el; + }} > - { - parseDesc(description) - } + {parseDesc(description)}
- ) + ); } ref_sizer; @@ -118,7 +139,6 @@ export class Component extends React.PureComponent ({ editing, title, description }); -const mapDispatchToProps = dispatch => bindActionCreators({ }, dispatch); +const TitleDialog = connect(mapStateToProps)(TitleDialogUnconnected); -export const TitleDialog = connect(mapStateToProps, mapDispatchToProps)(Component); +export { TitleDialog }; diff --git a/src/components/panels/EditorDialog.tsx b/src/components/panels/EditorDialog.tsx index 6edb3c7..d3f50a5 100644 --- a/src/components/panels/EditorDialog.tsx +++ b/src/components/panels/EditorDialog.tsx @@ -8,13 +8,10 @@ import { LogoDialog } from '$components/dialogs/LogoDialog'; import { SaveDialog } from '$components/dialogs/SaveDialog'; import { CancelDialog } from '$components/dialogs/CancelDialog'; -import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import * as USER_ACTIONS from '$redux/user/actions'; import { ProviderDialog } from '$components/dialogs/ProviderDialog'; import { ShotPrefetchDialog } from '$components/dialogs/ShotPrefetchDialog'; -import * as MAP_ACTIONS from '$redux/map/actions'; import { selectUserMode } from '$redux/user/selectors'; const mapStateToProps = state => ({ mode: selectUserMode(state) }); diff --git a/src/components/panels/TopRightPanel.tsx b/src/components/panels/TopRightPanel.tsx index 246dc0e..c06c7e8 100644 --- a/src/components/panels/TopRightPanel.tsx +++ b/src/components/panels/TopRightPanel.tsx @@ -1,71 +1,72 @@ // flow -import React from 'react'; +import React, { useCallback } from 'react'; import { Icon } from '$components/panels/Icon'; import { PROVIDERS } from '$constants/providers'; import { LOGOS } from '$constants/logos'; -import { setMode } from '$redux/user/actions'; +import * as USER_ACTIONS from '$redux/user/actions'; import { connect } from 'react-redux'; import { MODES } from '$constants/modes'; -import { IRootState } from "$redux/user"; +import { IRootState } from '$redux/user'; -import { Tooltip } from "$components/panels/Tooltip"; +import { Tooltip } from '$components/panels/Tooltip'; +import { selectMap } from '$redux/map/selectors'; +import { selectUser } from '$redux/user/selectors'; -interface Props extends IRootState { - startProviderMode: () => void, - startLogoMode: () => void, - clearMode: () => void, -} - -const Component = ({ - provider, logo, startProviderMode, startLogoMode, clearMode, editing, markers_shown, -}: Props) => ( -
- { - editing && !markers_shown && -
- - Приблизьте, чтобы редактировать кривую -
- } -
- Стиль карты - -
- {(provider && PROVIDERS[provider] && PROVIDERS[provider].name) || '...'} -
- -
- Логотип - -
- {(logo && LOGOS[logo] && LOGOS[logo][0]) || '...'} -
-
-); - -function mapStateToProps(state) { - const { - map: { - provider, - logo, - }, - user: { - markers_shown, editing - }, - } = state; - - return { - provider, logo, markers_shown, editing - }; -} - -const mapDispatchToProps = dispatch => ({ - startProviderMode: () => dispatch(setMode(MODES.PROVIDER)), - startLogoMode: () => dispatch(setMode(MODES.LOGO)), - clearMode: () => dispatch(setMode(MODES.NONE)), +const mapStateToProps = state => ({ + map: selectMap(state), + user: selectUser(state), }); -export const TopRightPanel = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +const mapDispatchToProps = { + setMode: USER_ACTIONS.setMode, +}; + +type Props = ReturnType & typeof mapDispatchToProps & {}; + +const TopRightPanelUnconnected = ({ + map: { provider, logo }, + user: { markers_shown, editing }, + setMode, +}: Props) => { + const startProviderMode = useCallback(() => setMode(MODES.PROVIDER), [setMode]); + const startLogoMode = useCallback(() => setMode(MODES.LOGO), [setMode]); + const clearMode = useCallback(() => setMode(MODES.NONE), [setMode]); + + return ( +
+ {editing && !markers_shown && ( +
+ + Приблизьте, чтобы редактировать кривую +
+ )} +
+ Стиль карты + +
+ {(provider && PROVIDERS[provider] && PROVIDERS[provider].name) || '...'} +
+ +
+ Логотип + +
+ {(logo && LOGOS[logo] && LOGOS[logo][0]) || '...'} +
+
+ ); +}; + +const TopRightPanel = connect(mapStateToProps, mapDispatchToProps)(TopRightPanelUnconnected); + +export { TopRightPanel }; diff --git a/src/components/panels/UserPanel.tsx b/src/components/panels/UserPanel.tsx index 8c494af..2bca218 100644 --- a/src/components/panels/UserPanel.tsx +++ b/src/components/panels/UserPanel.tsx @@ -1,9 +1,9 @@ -import React from "react"; +import React, { PureComponent } from 'react'; -import { GuestButton } from "$components/user/GuestButton"; -import { DEFAULT_USER, IUser, ROLES } from "$constants/auth"; -import { UserButton } from "$components/user/UserButton"; -import { UserMenu } from "$components/user/UserMenu"; +import { GuestButton } from '$components/user/GuestButton'; +import { DEFAULT_USER, IUser, ROLES } from '$constants/auth'; +import { UserButton } from '$components/user/UserButton'; +import { UserMenu } from '$components/user/UserMenu'; import { setUser, userLogout, @@ -12,18 +12,17 @@ import { gotVkUser, setDialogActive, openMapDialog, - getGPXTrack -} from "$redux/user/actions"; -import { bindActionCreators } from "redux"; -import { connect } from "react-redux"; -import { Icon } from "$components/panels/Icon"; + getGPXTrack, +} from '$redux/user/actions'; +import { connect } from 'react-redux'; +import { Icon } from '$components/panels/Icon'; -import classnames from "classnames"; -import { CLIENT } from "$config/frontend"; -import { DIALOGS, TABS } from "$constants/dialogs"; -import { IRootState } from "$redux/user"; -import { Tooltip } from "$components/panels/Tooltip"; -import { TitleDialog } from "$components/dialogs/TitleDialog"; +import classnames from 'classnames'; +import { CLIENT } from '$config/frontend'; +import { DIALOGS, TABS } from '$constants/dialogs'; +import { IRootState } from '$redux/user'; +import { Tooltip } from '$components/panels/Tooltip'; +import { TitleDialog } from '$components/dialogs/TitleDialog'; interface Props extends IRootState { userLogout: typeof userLogout; @@ -39,34 +38,26 @@ interface State { menuOpened: boolean; } -export class Component extends React.PureComponent { +export class UserPanelUnconnected extends PureComponent { state = { - menuOpened: false + menuOpened: false, }; componentDidMount() { - window.addEventListener("message", e => { + window.addEventListener('message', e => { const { data } = e; if ( !data || !data.type || - data.type !== "oauth_login" || + data.type !== 'oauth_login' || !data.user || !data.user.id || !data.user.token ) return; - const { - id, - token, - role = "vk", - name = "", - ip = "", - photo = "", - agent = "" - } = data.user; + const { id, token, role = 'vk', name = '', ip = '', photo = '', agent = '' } = data.user; const user = { ...DEFAULT_USER, @@ -77,8 +68,8 @@ export class Component extends React.PureComponent { name, ip, agent, - photo - } + photo, + }, }; this.setState({ menuOpened: false }); @@ -105,7 +96,7 @@ export class Component extends React.PureComponent { window.open( `https://oauth.vk.com/authorize?client_id=5987644&scope=&redirect_uri=${CLIENT.API_ADDR}/api/auth/vk`, - "socialPopupWindow", + 'socialPopupWindow', `location=no,width=700,height=370,scrollbars=no,top=${top},left=${left},resizable=no` ); }; @@ -113,13 +104,13 @@ export class Component extends React.PureComponent { render() { const { props: { user, dialog, dialog_active, is_empty }, - state: { menuOpened } + state: { menuOpened }, } = this; return (
- +
{!user || user.role === ROLES.guest ? ( @@ -127,7 +118,7 @@ export class Component extends React.PureComponent { ) : ( )} - {user && user.role && user.role !== "guest" && menuOpened && ( + {user && user.role && user.role !== 'guest' && menuOpened && ( {
@@ -165,10 +153,7 @@ export class Component extends React.PureComponent {
- @@ -181,25 +166,24 @@ export class Component extends React.PureComponent { } } -const mapStateToProps = ({ - user: { dialog, dialog_active, user, is_empty } -}) => ({ dialog, dialog_active, user, is_empty }); -const mapDispatchToProps = dispatch => - bindActionCreators( - { - setUser, - userLogout, - takeAShot, - setDialog, - gotVkUser, - setDialogActive, - openMapDialog, - getGPXTrack - }, - dispatch - ); +const mapStateToProps = ({ user: { dialog, dialog_active, user, is_empty } }) => ({ + dialog, + dialog_active, + user, + is_empty, +}); -export const UserPanel = connect( - mapStateToProps, - mapDispatchToProps -)(Component); +const mapDispatchToProps = { + setUser, + userLogout, + takeAShot, + setDialog, + gotVkUser, + setDialogActive, + openMapDialog, + getGPXTrack, +}; + +const UserPanel = connect(mapStateToProps, mapDispatchToProps)(UserPanelUnconnected); + +export { UserPanel }; diff --git a/src/components/renderer/Renderer.tsx b/src/components/renderer/Renderer.tsx index c3f7a5a..60166c8 100644 --- a/src/components/renderer/Renderer.tsx +++ b/src/components/renderer/Renderer.tsx @@ -8,10 +8,11 @@ import 'croppr/dist/croppr.css'; import { LOGOS } from '$constants/logos'; import { RendererPanel } from '$components/panels/RendererPanel'; import { IRootState } from "$redux/user"; +import { IRoute } from '$redux/map/types'; type Props = { data: IRootState['renderer']['data'], - logo: IRootState['logo'], + logo: IRoute['logo'], hideRenderer: typeof hideRenderer, cropAShot: typeof cropAShot, }; diff --git a/src/redux/map/types.ts b/src/redux/map/types.ts index d17a512..e36f37b 100644 --- a/src/redux/map/types.ts +++ b/src/redux/map/types.ts @@ -1,17 +1,32 @@ -import { LatLng } from "leaflet"; +import { LatLng } from 'leaflet'; +import { IRoutePoint } from '$utils/gpx'; export type ILatLng = { - lat: number, - lng: number, -} + lat: number; + lng: number; +}; export type IMapRoute = ILatLng[]; - export interface IStickerDump { - latlng: ILatLng, - set: string, - sticker: string, - angle?: number, - text?: string, + latlng: ILatLng; + set: string; + sticker: string; + angle?: number; + text?: string; +} + +export interface IRoute { + version: number; + title: string; + owner: number; + address: string; + route: IRoutePoint[]; + stickers: IStickerDump[]; + provider: string; + is_public: boolean; + is_published: boolean; + description: string; + logo: string; + distance: number; } diff --git a/src/redux/user/actions.ts b/src/redux/user/actions.ts index f19e18a..14dc609 100644 --- a/src/redux/user/actions.ts +++ b/src/redux/user/actions.ts @@ -1,6 +1,7 @@ import { USER_ACTIONS } from '$redux/user/constants'; import { IUser } from "$constants/auth"; import { IRootState } from "$redux/user"; +import { IRoute } from '$redux/map/types'; export const setUser = (user: IUser) => ({ type: USER_ACTIONS.SET_USER, user }); export const userLogout = () => ({ type: USER_ACTIONS.USER_LOGOUT }); @@ -32,10 +33,10 @@ export const clearAll = () => ({ type: USER_ACTIONS.CLEAR_ALL }); export const clearCancel = () => ({ type: USER_ACTIONS.CLEAR_CANCEL }); export const sendSaveRequest = (payload: { - title: IRootState['title'], - address: IRootState['address'], - is_public: IRootState['is_public'], - description: IRootState['description'], + title: IRoute['title'], + address: IRoute['address'], + is_public: IRoute['is_public'], + description: IRoute['description'], force: boolean, }) => ({ type: USER_ACTIONS.SEND_SAVE_REQUEST, @@ -47,10 +48,10 @@ export const resetSaveDialog = () => ({ type: USER_ACTIONS.RESET_SAVE_DIALOG }); export const setSaveLoading = (save_loading: IRootState['save_loading']) => ({ type: USER_ACTIONS.SET_SAVE_LOADING, save_loading }); export const setSaveSuccess = (payload: { - address: IRootState['address'], - title: IRootState['address'], - is_public: IRootState['is_public'], - description: IRootState['description'], + address: IRoute['address'], + title: IRoute['address'], + is_public: IRoute['is_public'], + description: IRoute['description'], save_error: string, }) => ({ type: USER_ACTIONS.SET_SAVE_SUCCESS, ...payload }); diff --git a/src/redux/user/index.ts b/src/redux/user/index.ts index 8017093..15d1e12 100644 --- a/src/redux/user/index.ts +++ b/src/redux/user/index.ts @@ -1,29 +1,10 @@ import { createReducer } from 'reduxsauce'; import { DEFAULT_USER, IUser } from '$constants/auth'; import { MODES } from '$constants/modes'; -import { DEFAULT_LOGO, LOGOS } from '$constants/logos'; -import { DEFAULT_PROVIDER, PROVIDERS } from '$constants/providers'; -import { DIALOGS, IDialogs, TABS } from '$constants/dialogs'; +import { DIALOGS, IDialogs } from '$constants/dialogs'; import { IStickers } from "$constants/stickers"; -import { IRoutePoint } from '$utils/gpx'; -import { IStickerDump } from '$redux/map/types'; import { USER_HANDLERS } from './handlers'; -export interface IRoute { - version: number, - title: IRootState['title'], - owner: number, - address: IRootState['address'], - route: IRoutePoint[], - stickers: IStickerDump[], - provider: IRootState['provider'], - is_public: IRootState['is_public'], - is_published: IRootState['is_published'], - description: IRootState['description'], - logo: IRootState['logo'], - distance: IRootState['distance'] -} - export interface IRouteListItem { address: string, title: string, @@ -38,22 +19,22 @@ export interface IRootReducer { user: IUser, editing: boolean, mode: typeof MODES[keyof typeof MODES], - logo: keyof typeof LOGOS, + // logo: keyof typeof LOGOS, routerPoints: number, distance: number, - description: string, + // description: string, estimated: number, speed: number, activeSticker: { set?: keyof IStickers, sticker?: string }, - title: string, - address: string, - address_origin: string, + // title: string, + // address: string, + // address_origin: string, changed: boolean, - provider: keyof typeof PROVIDERS, + // provider: keyof typeof PROVIDERS, markers_shown: boolean, is_published: boolean, - is_public: boolean, + // is_public: boolean, is_empty: boolean, is_routing: boolean, @@ -104,24 +85,24 @@ export const INITIAL_STATE: IRootReducer = { user: { ...DEFAULT_USER }, mode: MODES.NONE, - logo: DEFAULT_LOGO, + // logo: DEFAULT_LOGO, routerPoints: 0, distance: 0, - description: '', + // description: '', estimated: 0, speed: 15, activeSticker: { set: null, sticker: null }, - title: '', - address: '', - address_origin: '', - provider: DEFAULT_PROVIDER, + // title: '', + // address: '', + // address_origin: '', + // provider: DEFAULT_PROVIDER, markers_shown: true, changed: false, editing: false, is_published: false, - is_public: false, + // is_public: false, is_empty: true, is_routing: false, diff --git a/src/utils/api.ts b/src/utils/api.ts index cf9142b..47a3f8b 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,6 +1,6 @@ import axios from 'axios/index'; import { API } from '$constants/api'; -import { IRootState, IRouteListItem, IRoute } from '$redux/user'; +import { IRootState, IRouteListItem } from '$redux/user'; import { IUser } from '$constants/auth'; import { CLIENT } from '$config/frontend'; import { LatLngLiteral } from 'leaflet'; @@ -10,6 +10,7 @@ import { IResultWithStatus, configWithToken, } from './middleware'; +import { IRoute } from '$redux/map/types'; const arrayToObject = (array: any[], key: string): {} => array.reduce((obj, el) => ({ ...obj, [el[key]]: el }), {}); @@ -63,7 +64,7 @@ export const getGuestToken = (): Promise text.replace(/[^A-Za-z\-_0-9]/ig, '_').replace(/_{2,}/ig, '_'); +export const removeGarbage = (text: string): string => + text.replace(/[^A-Za-z\-_0-9]/gi, '_').replace(/_{2,}/gi, '_'); export const toHours = (info: number): string => { const hrs = parseInt(String(info), 10); @@ -10,10 +157,11 @@ export const toHours = (info: number): string => { return `${hrs}:${lmin}`; }; -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 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"); +export const parseDesc = (text: string = ''): string => text.replace(/(\n{2,})/gi, '\n\n'); // export const colorizeTitle = (text: string): string => text.replace(/(\[[^\]^]+\])/, ``) diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index f5ecbdf..e5fff74 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -3,7 +3,7 @@ import { COLORS, CLIENT } from '$config/frontend'; import saveAs from 'file-saver'; import { replaceProviderUrl } from '$constants/providers'; import { STICKERS } from '$constants/stickers'; -import { ILatLng } from '$redux/map/types'; +import { ILatLng, IRoute } from '$redux/map/types'; import { IStickerDump } from '$redux/map/types'; import { IRootState } from '$redux/user'; import { @@ -469,5 +469,5 @@ export const composeStickers = async ({ ); }; -export const downloadCanvas = (canvas: HTMLCanvasElement, title: IRootState['title']): void => +export const downloadCanvas = (canvas: HTMLCanvasElement, title: IRoute['title']): void => canvas.toBlob(blob => saveAs(blob, title));