From 618c2e32756da516b238599d964d57c486a72abf Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 11 Nov 2019 16:01:21 +0700 Subject: [PATCH] added initial profile dialog --- src/components/main/UserButton/index.tsx | 4 +- .../node/CommentContent/styles.scss | 2 +- .../ParagraphPlaceholder/index.tsx | 26 ++++----- .../placeholders/Placeholder/index.tsx | 9 +-- src/constants/api.ts | 3 +- src/constants/urls.ts | 1 + src/containers/App.tsx | 4 +- .../dialogs/ProfileDialog/index.tsx | 28 +++++++++ .../dialogs/ProfileDialog/styles.scss | 0 src/containers/node/BorisLayout/index.tsx | 7 ++- src/containers/node/BorisLayout/styles.scss | 18 +++--- src/containers/profile/ProfileInfo/index.tsx | 27 +++++++++ .../profile/ProfileInfo/styles.scss | 28 +++++++++ .../profile/ProfileLayout/index.tsx | 58 +++++++++++++++++++ .../profile/ProfileLayout/styles.scss | 27 +++++++++ src/redux/auth/actions.ts | 14 ++++- src/redux/auth/api.ts | 9 +++ src/redux/auth/constants.ts | 4 +- src/redux/auth/handlers.ts | 17 ++++-- src/redux/auth/reducer.ts | 6 ++ src/redux/auth/sagas.ts | 33 ++++++++--- src/redux/auth/selectors.ts | 1 + src/redux/auth/types.ts | 5 ++ src/redux/modal/constants.ts | 3 + src/redux/store.ts | 17 ++++-- src/styles/global.scss | 17 +++--- src/styles/variables.scss | 1 + src/utils/dom.ts | 4 ++ 28 files changed, 315 insertions(+), 58 deletions(-) create mode 100644 src/containers/dialogs/ProfileDialog/index.tsx create mode 100644 src/containers/dialogs/ProfileDialog/styles.scss create mode 100644 src/containers/profile/ProfileInfo/index.tsx create mode 100644 src/containers/profile/ProfileInfo/styles.scss create mode 100644 src/containers/profile/ProfileLayout/index.tsx create mode 100644 src/containers/profile/ProfileLayout/styles.scss diff --git a/src/components/main/UserButton/index.tsx b/src/components/main/UserButton/index.tsx index 6bf70b84..4cffe38d 100644 --- a/src/components/main/UserButton/index.tsx +++ b/src/components/main/UserButton/index.tsx @@ -5,6 +5,7 @@ import { getURL } from '~/utils/dom'; import { Icon } from '~/components/input/Icon'; import { IUser } from '~/redux/auth/types'; import { PRESETS } from '~/constants/urls'; +import { Link } from 'react-router-dom'; interface IProps { user: Partial; @@ -14,7 +15,8 @@ interface IProps { const UserButton: FC = ({ user: { username, photo }, onLogout }) => (
-
{username}
+ {username} +
( +const ParagraphPlaceholder = ({}) => (
- - - - - - + + + + + +
- - - - - - + + + + + +
); diff --git a/src/components/placeholders/Placeholder/index.tsx b/src/components/placeholders/Placeholder/index.tsx index f6f55505..b29d12bb 100644 --- a/src/components/placeholders/Placeholder/index.tsx +++ b/src/components/placeholders/Placeholder/index.tsx @@ -2,16 +2,13 @@ import React, { FC } from 'react'; import * as styles from './styles.scss'; interface IProps { - width?: number; + width?: string; height?: number; color?: string; } -const Placeholder: FC = ({ width = 120, height, color }) => ( -
+const Placeholder: FC = ({ width = '120px', height, color }) => ( +
); export { Placeholder }; diff --git a/src/constants/api.ts b/src/constants/api.ts index de72ef85..653fb08c 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -5,8 +5,9 @@ export const API = { USER: { LOGIN: '/auth/login', VKONTAKTE_LOGIN: `${process.env.API_HOST}/auth/vkontakte`, - ME: '/auth/', // + ME: '/auth/', UPLOAD: (target, type) => `/upload/${target}/${type}`, + PROFILE: (username: string) => `/user/${username}`, }, NODE: { SAVE: '/node/', diff --git a/src/constants/urls.ts b/src/constants/urls.ts index fec425c2..da71f8de 100644 --- a/src/constants/urls.ts +++ b/src/constants/urls.ts @@ -13,6 +13,7 @@ export const URLS = { BACKEND_DOWN: '/oopsie', }, NODE_URL: (id: number | string) => `/post${id}`, + PROFILE: (username: string) => `/~${username}`, }; export const PRESETS = { diff --git a/src/containers/App.tsx b/src/containers/App.tsx index 4bff1099..bcd0a5ba 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect } from 'react'; +import React, { FC } from 'react'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; import { ConnectedRouter } from 'connected-react-router'; @@ -8,7 +8,6 @@ import { FlowLayout } from '~/containers/flow/FlowLayout'; import { MainLayout } from '~/containers/main/MainLayout'; import { ImageExample } from '~/containers/examples/ImageExample'; import { EditorExample } from '~/containers/examples/EditorExample'; -import { HorizontalExample } from '~/containers/examples/HorizontalExample'; import { Sprites } from '~/sprites/Sprites'; import { URLS } from '~/constants/urls'; import { Modal } from '~/containers/dialogs/Modal'; @@ -19,6 +18,7 @@ import { NodeLayout } from './node/NodeLayout'; import { BottomContainer } from '~/containers/main/BottomContainer'; import { BorisLayout } from './node/BorisLayout'; import { ErrorNotFound } from './pages/ErrorNotFound'; +import { ProfileLayout } from './profile/ProfileLayout'; const mapStateToProps = state => ({ modal: selectModal(state), diff --git a/src/containers/dialogs/ProfileDialog/index.tsx b/src/containers/dialogs/ProfileDialog/index.tsx new file mode 100644 index 00000000..0521e09c --- /dev/null +++ b/src/containers/dialogs/ProfileDialog/index.tsx @@ -0,0 +1,28 @@ +import React, { FC } from 'react'; +import { BetterScrollDialog } from '../BetterScrollDialog'; +import styles from './styles.scss'; +import { ProfileInfo } from '~/containers/profile/ProfileInfo'; +import { IDialogProps } from '~/redux/types'; +import { connect } from 'react-redux'; +import { selectAuthProfile } from '~/redux/auth/selectors'; + +const mapStateToProps = selectAuthProfile; +const mapDispatchToProps = {}; + +type IProps = IDialogProps & ReturnType & {}; + +const ProfileDialogUnconnected: FC = ({ onRequestClose, is_loading, user }) => ( + } + onClose={onRequestClose} + > +
+ +); + +const ProfileDialog = connect( + mapStateToProps, + mapDispatchToProps +)(ProfileDialogUnconnected); + +export { ProfileDialog }; diff --git a/src/containers/dialogs/ProfileDialog/styles.scss b/src/containers/dialogs/ProfileDialog/styles.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/containers/node/BorisLayout/index.tsx b/src/containers/node/BorisLayout/index.tsx index 0f811e77..94f1e09b 100644 --- a/src/containers/node/BorisLayout/index.tsx +++ b/src/containers/node/BorisLayout/index.tsx @@ -9,6 +9,7 @@ import styles from './styles.scss'; import { CommentForm } from '~/components/node/CommentForm'; import { Group } from '~/components/containers/Group'; import boris from '~/sprites/boris_robot.svg'; +import { NodeNoComments } from '~/components/node/NodeNoComments'; const mapStateToProps = state => ({ node: selectNode(state), @@ -78,7 +79,11 @@ const BorisLayoutUnconnected: FC = ({ {is_user && } - + {is_loading_comments ? ( + + ) : ( + + )}
diff --git a/src/containers/node/BorisLayout/styles.scss b/src/containers/node/BorisLayout/styles.scss index 6fb108c9..734d1dfb 100644 --- a/src/containers/node/BorisLayout/styles.scss +++ b/src/containers/node/BorisLayout/styles.scss @@ -11,6 +11,7 @@ padding: $gap; background: $content_bg; border-radius: $radius; + flex: 0 1 $limited_width; } .column { @@ -70,12 +71,13 @@ align-items: flex-start; justify-content: center; flex-direction: row; - width: 80%; - margin: auto; + flex: 0 1 $limited_width; + width: 100%; + // margin: auto; - @include tablet { - width: 100%; - } + // @include tablet { + // width: 100%; + // } } .image { @@ -101,9 +103,10 @@ .caption { position: absolute; - left: 0; + left: 50%; bottom: 0; - width: 50%; + width: 100%; + max-width: $limited_width; height: 100%; display: flex; align-items: flex-start; @@ -114,6 +117,7 @@ flex-direction: column; padding-bottom: $gap * 2; padding: 0 10% $gap * 2; + transform: translate(-50%, 0); @include tablet { align-items: flex-start; diff --git a/src/containers/profile/ProfileInfo/index.tsx b/src/containers/profile/ProfileInfo/index.tsx new file mode 100644 index 00000000..c3b950b4 --- /dev/null +++ b/src/containers/profile/ProfileInfo/index.tsx @@ -0,0 +1,27 @@ +import React, { FC } from 'react'; +import { IUser } from '~/redux/auth/types'; +import styles from './styles.scss'; +import { Grid } from '~/components/containers/Grid'; +import { Group } from '~/components/containers/Group'; +import { Placeholder } from '~/components/placeholders/Placeholder'; + +interface IProps { + user?: IUser; + is_loading?: boolean; +} + +const ProfileInfo: FC = ({ user, is_loading = false }) => ( + +
+ + +
{is_loading ? : 'User Name'}
+ +
+ {is_loading ? : 'Some description here'} +
+
+ +); + +export { ProfileInfo }; diff --git a/src/containers/profile/ProfileInfo/styles.scss b/src/containers/profile/ProfileInfo/styles.scss new file mode 100644 index 00000000..5caf2ec9 --- /dev/null +++ b/src/containers/profile/ProfileInfo/styles.scss @@ -0,0 +1,28 @@ +.wrap { + justify-content: flex-start; + align-items: flex-start !important; + // min-height: 64px; + padding: $gap; + box-sizing: border-box; +} + +.avatar { + @include outer_shadow(); + + border-radius: $radius; + width: 140px; + height: 140px; + background: $content_bg; + position: absolute; + top: -60px; + left: $gap; +} + +.field { + padding-left: 140px; + flex: 1; +} + +.name { + font: $font_24_bold; +} diff --git a/src/containers/profile/ProfileLayout/index.tsx b/src/containers/profile/ProfileLayout/index.tsx new file mode 100644 index 00000000..09535d54 --- /dev/null +++ b/src/containers/profile/ProfileLayout/index.tsx @@ -0,0 +1,58 @@ +import React, { FC, useEffect, useState } from 'react'; +import { useRouteMatch, withRouter, RouteComponentProps } from 'react-router'; +import styles from './styles.scss'; +import { NodeNoComments } from '~/components/node/NodeNoComments'; +import { Grid } from '~/components/containers/Grid'; +import { CommentForm } from '~/components/node/CommentForm'; +import { ProfileInfo } from '../ProfileInfo'; +import * as NODE_ACTIONS from '~/redux/node/actions'; +import { connect } from 'react-redux'; +import { IUser } from '~/redux/auth/types'; +import { Group } from '~/components/containers/Group'; + +const mapStateToProps = () => ({}); +const mapDispatchToProps = { + nodeSetCoverImage: NODE_ACTIONS.nodeSetCoverImage, +}; + +type IProps = RouteComponentProps & typeof mapDispatchToProps & {}; + +const ProfileLayoutUnconnected: FC = ({ history, nodeSetCoverImage }) => { + const { + params: { username }, + } = useRouteMatch<{ username: string }>(); + const [user, setUser] = useState(null); + + useEffect(() => { + if (user) setUser(null); + }, [username]); + + useEffect(() => { + if (user && user.id && user.cover) { + nodeSetCoverImage(user.cover); + return () => nodeSetCoverImage(null); + } + }, [user]); + + return ( + +
+ +
+ + +
+ + +
+
+
+ ); +}; + +const ProfileLayout = connect( + mapStateToProps, + mapDispatchToProps +)(withRouter(ProfileLayoutUnconnected)); + +export { ProfileLayout }; diff --git a/src/containers/profile/ProfileLayout/styles.scss b/src/containers/profile/ProfileLayout/styles.scss new file mode 100644 index 00000000..072539f9 --- /dev/null +++ b/src/containers/profile/ProfileLayout/styles.scss @@ -0,0 +1,27 @@ +.wrap { + display: flex; + align-items: flex-start !important; + justify-content: flex-start !important; + flex-direction: row; + flex: 1; +} + +.content { + flex: 3; + // flex: 0 1 $limited_width; + // padding: $gap; + box-sizing: border-box; +} + +.column { + flex: 1; +} + +.info { +} + +.comments { + background: $node_bg; + border-radius: $radius; + padding: $gap; +} diff --git a/src/redux/auth/actions.ts b/src/redux/auth/actions.ts index c09619ae..a40bcc6d 100644 --- a/src/redux/auth/actions.ts +++ b/src/redux/auth/actions.ts @@ -19,8 +19,8 @@ export const authSetToken = (token: IAuthState['token']) => ({ token, }); -export const gotPostMessage = ({ token }: { token: string }) => ({ - type: AUTH_USER_ACTIONS.GOT_POST_MESSAGE, +export const gotAuthPostMessage = ({ token }: { token: string }) => ({ + type: AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE, token, }); @@ -32,3 +32,13 @@ export const authSetUser = (profile: Partial) => ({ export const authLogout = () => ({ type: AUTH_USER_ACTIONS.LOGOUT, }); + +export const authOpenProfile = (username: string) => ({ + type: AUTH_USER_ACTIONS.OPEN_PROFILE, + username, +}); + +export const authSetProfile = (profile: Partial) => ({ + type: AUTH_USER_ACTIONS.SET_PROFILE, + profile, +}); diff --git a/src/redux/auth/api.ts b/src/redux/auth/api.ts index fcb8fce1..6035c14f 100644 --- a/src/redux/auth/api.ts +++ b/src/redux/auth/api.ts @@ -22,3 +22,12 @@ export const apiAuthGetUser = ({ access }): Promise> => + api + .get(API.USER.PROFILE(username), configWithToken(access)) + .then(resultMiddleware) + .catch(errorMiddleware); diff --git a/src/redux/auth/constants.ts b/src/redux/auth/constants.ts index e8ca97a1..d86a2720 100644 --- a/src/redux/auth/constants.ts +++ b/src/redux/auth/constants.ts @@ -8,7 +8,9 @@ export const AUTH_USER_ACTIONS = { LOGOUT: 'LOGOUT', - GOT_POST_MESSAGE: 'GOT_POST_MESSAGE', + GOT_AUTH_POST_MESSAGE: 'GOT_POST_MESSAGE', + OPEN_PROFILE: 'OPEN_PROFILE', + SET_PROFILE: 'SET_PROFILE', }; export const USER_ERRORS = { diff --git a/src/redux/auth/handlers.ts b/src/redux/auth/handlers.ts index 2044fc54..0c46fb9a 100644 --- a/src/redux/auth/handlers.ts +++ b/src/redux/auth/handlers.ts @@ -14,23 +14,32 @@ const setLoginError: ActionHandler = ( login: { ...state.login, error, - } + }, }); const setUser: ActionHandler = (state, { profile }) => ({ ...state, user: { ...state.user, - ...profile - } + ...profile, + }, }); const setToken: ActionHandler = (state, { token }) => ({ - ...state, token, + ...state, + token, }); +const setProfile: ActionHandler = (state, { profile }) => ({ + ...state, + profile: { + ...state.profile, + ...profile, + }, +}); export const AUTH_USER_HANDLERS = { [AUTH_USER_ACTIONS.SET_LOGIN_ERROR]: setLoginError, [AUTH_USER_ACTIONS.SET_USER]: setUser, [AUTH_USER_ACTIONS.SET_TOKEN]: setToken, + [AUTH_USER_ACTIONS.SET_PROFILE]: setProfile, }; diff --git a/src/redux/auth/reducer.ts b/src/redux/auth/reducer.ts index 73c065b4..49fcc49c 100644 --- a/src/redux/auth/reducer.ts +++ b/src/redux/auth/reducer.ts @@ -10,10 +10,16 @@ const HANDLERS = { const INITIAL_STATE: IAuthState = { token: null, user: { ...EMPTY_USER }, + login: { error: null, is_loading: false, }, + + profile: { + is_loading: true, + user: null, + }, }; export default createReducer(INITIAL_STATE, HANDLERS); diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts index 140d940f..c81b639b 100644 --- a/src/redux/auth/sagas.ts +++ b/src/redux/auth/sagas.ts @@ -1,19 +1,20 @@ -import { call, put, takeLatest, select } from 'redux-saga/effects'; +import { call, put, takeLatest, select, delay } from 'redux-saga/effects'; import { AUTH_USER_ACTIONS, EMPTY_USER, USER_ERRORS } from '~/redux/auth/constants'; import { authSetToken, userSetLoginError, authSetUser, userSendLoginRequest, - gotPostMessage, + gotAuthPostMessage, + authOpenProfile, + authSetProfile, } from '~/redux/auth/actions'; -import { apiUserLogin, apiAuthGetUser } from '~/redux/auth/api'; -import { modalSetShown } from '~/redux/modal/actions'; +import { apiUserLogin, apiAuthGetUser, apiAuthGetUserProfile } from '~/redux/auth/api'; +import { modalSetShown, modalShowDialog } from '~/redux/modal/actions'; import { selectToken } from './selectors'; import { IResultWithStatus } from '../types'; import { IUser } from './types'; import { REHYDRATE, RehydrateAction } from 'redux-persist'; -import path from 'ramda/es/path'; import { selectModal } from '../modal/selectors'; import { IModalState } from '../modal/reducer'; import { DIALOGS } from '../modal/constants'; @@ -76,7 +77,7 @@ function* checkUserSaga({ key }: RehydrateAction) { yield call(refreshUser); } -function* gotPostMessageSaga({ token }: ReturnType) { +function* gotPostMessageSaga({ token }: ReturnType) { yield put(authSetToken(token)); yield call(refreshUser); @@ -90,11 +91,29 @@ function* logoutSaga() { yield put(authSetUser({ ...EMPTY_USER })); } +function* openProfile({ username }: ReturnType) { + yield put(modalShowDialog(DIALOGS.PROFILE)); + yield put(authSetProfile({ is_loading: true })); + + const { + error, + data: { user }, + } = yield call(reqWrapper, apiAuthGetUserProfile, { username }); + + if (error || !user) { + return yield put(modalSetShown(false)); + } + + yield put(authSetProfile({ is_loading: false })); + console.log({ username, user }); +} + function* authSaga() { yield takeLatest(REHYDRATE, checkUserSaga); yield takeLatest(AUTH_USER_ACTIONS.LOGOUT, logoutSaga); yield takeLatest(AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga); - yield takeLatest(AUTH_USER_ACTIONS.GOT_POST_MESSAGE, gotPostMessageSaga); + yield takeLatest(AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE, gotPostMessageSaga); + yield takeLatest(AUTH_USER_ACTIONS.OPEN_PROFILE, openProfile); } export default authSaga; diff --git a/src/redux/auth/selectors.ts b/src/redux/auth/selectors.ts index e1f8ad29..df584d7b 100644 --- a/src/redux/auth/selectors.ts +++ b/src/redux/auth/selectors.ts @@ -4,3 +4,4 @@ export const selectAuth = (state: IState): IState['auth'] => state.auth; export const selectUser = (state: IState): IState['auth']['user'] => state.auth.user; export const selectToken = (state: IState): IState['auth']['token'] => state.auth.token; export const selectAuthLogin = (state: IState): IState['auth']['login'] => state.auth.login; +export const selectAuthProfile = (state: IState): IState['auth']['profile'] => state.auth.profile; diff --git a/src/redux/auth/types.ts b/src/redux/auth/types.ts index 7b1901f3..423637ec 100644 --- a/src/redux/auth/types.ts +++ b/src/redux/auth/types.ts @@ -26,4 +26,9 @@ export type IAuthState = Readonly<{ error: string; is_loading: boolean; }; + + profile: { + is_loading: boolean; + user: IUser; + }; }>; diff --git a/src/redux/modal/constants.ts b/src/redux/modal/constants.ts index 4e5977c2..b611f962 100644 --- a/src/redux/modal/constants.ts +++ b/src/redux/modal/constants.ts @@ -7,6 +7,7 @@ import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo'; import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio'; import { NODE_TYPES } from '../node/constants'; import { TestDialog } from '~/containers/dialogs/TestDialog'; +import { ProfileDialog } from '~/containers/dialogs/ProfileDialog'; export const MODAL_ACTIONS = { SET_SHOWN: 'MODAL.SET_SHOWN', @@ -21,6 +22,7 @@ export const DIALOGS = { EDITOR_AUDIO: 'EDITOR_AUDIO', LOGIN: 'LOGIN', LOADING: 'LOADING', + PROFILE: 'PROFILE', TEST: 'TEST', }; @@ -32,6 +34,7 @@ export const DIALOG_CONTENT = { [DIALOGS.LOGIN]: LoginDialog, [DIALOGS.LOADING]: LoadingDialog, [DIALOGS.TEST]: TestDialog, + [DIALOGS.PROFILE]: ProfileDialog, }; export const NODE_EDITOR_DIALOGS = { diff --git a/src/redux/store.ts b/src/redux/store.ts index 379d47ee..076721d9 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -25,7 +25,7 @@ import playerSaga from '~/redux/player/sagas'; import { IAuthState } from '~/redux/auth/types'; import modalReducer, { IModalState } from '~/redux/modal/reducer'; -import { gotPostMessage } from './auth/actions'; +import { gotAuthPostMessage, authOpenProfile } from './auth/actions'; const authPersistConfig: PersistConfig = { key: 'auth', @@ -46,6 +46,13 @@ export interface IState { export const sagaMiddleware = createSagaMiddleware(); export const history = createBrowserHistory(); +history.listen(({ pathname }) => { + if (pathname.match(/~([\wа-яА-Я]+)/)) { + const [, username] = pathname.match(/~([\wа-яА-Я]+)/); + window.postMessage({ type: 'username', username }, '*'); + } +}); + const composeEnhancers = typeof window === 'object' && (window).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? (window).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) @@ -72,9 +79,11 @@ export function configureStore(): { store: Store; persistor: Persistor } sagaMiddleware.run(playerSaga); window.addEventListener('message', message => { - if (!message || !message.data || message.data.type !== 'oauth_login' || !message.data.token) - return; - store.dispatch(gotPostMessage({ token: message.data.token })); + if (message && message.data && message.data.type === 'oauth_login' && message.data.token) + return store.dispatch(gotAuthPostMessage({ token: message.data.token })); + + if (message && message.data && message.data.type === 'username' && message.data.username) + return store.dispatch(authOpenProfile(message.data.username)); }); const persistor = persistStore(store); diff --git a/src/styles/global.scss b/src/styles/global.scss index f9845fa3..fdbcedd5 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -87,6 +87,15 @@ body { font: $font_24_bold; } +:global(.username) { + background: transparentize($color: #000000, $amount: 0.8); + padding: 2px 4px; + border-radius: 4px; + cursor: pointer; + color: $wisegreen; + font-weight: bold; +} + ::-webkit-scrollbar { width: 18px; height: 18px; @@ -117,14 +126,6 @@ body { border-radius: 50px; } -// ::-webkit-scrollbar-track:hover { -// background: #666666; -// } -// -// ::-webkit-scrollbar-track:active { -// background: #333333; -// } - ::-webkit-scrollbar-corner { background: transparent; } diff --git a/src/styles/variables.scss b/src/styles/variables.scss index 34a970a2..c5b76470 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -16,6 +16,7 @@ $dialog_radius: $radius * 2; $input_height: 36px; $info_height: 24px; +$limited_width: 940px; $panel_size: 64px; $node_title_height: $panel_size; diff --git a/src/utils/dom.ts b/src/utils/dom.ts index af696535..321d40de 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -79,6 +79,10 @@ export const formatText = (text: string): string => .replace(/(\n{2,})/gi, '\n') .replace(//g, '>') + .replace( + /~([\wа-яА-Я\-]+)/giu, + '~$1' + ) .replace(/:\/\//gim, ':|--|') .replace(/(\/\/[^\n]+)/gim, '$1') .replace(/(\/\*[\s\S]*?\*\/)/gim, '$1')