1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

#23 added superpowers switch

This commit is contained in:
Fedor Katurov 2021-03-19 14:15:04 +07:00
parent e38090c755
commit 756840f173
16 changed files with 178 additions and 25 deletions

View file

@ -0,0 +1,27 @@
import React, { FC } from 'react';
import styles from './styles.module.scss';
import { Toggle } from '~/components/input/Toggle';
interface IProps {
active?: boolean;
onChange?: (val: boolean) => void;
}
const BorisSuperpowers: FC<IProps> = ({ active, onChange }) => (
<div className={styles.wrap}>
<div className={styles.toggle}>
<Toggle value={active} handler={onChange} />
</div>
<div className={styles.left}>
<div className={styles.title}>Суперспособности</div>
{active ? (
<div className={styles.subtitle}>Включи, чтобы видеть будущее</div>
) : (
<div className={styles.subtitle}>Ты видишь всё, что скрыто</div>
)}
</div>
</div>
);
export { BorisSuperpowers };

View file

@ -0,0 +1,19 @@
@import "~/styles/variables";
.wrap {
display: grid;
grid-template-columns: auto 1fr;
column-gap: $gap;
align-items: center;
}
.title {
font: $font_14_semibold;
color: white;
text-transform: uppercase;
}
.subtitle {
font: $font_12_regular;
color: transparentize(white, 0.5);
}

View file

@ -0,0 +1,16 @@
import React, { FC, memo } from 'react';
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
import { selectAuthIsTester, selectUser } from '~/redux/auth/selectors';
interface IProps {}
const Superpower: FC<IProps> = memo(({ children }) => {
const user = useShallowSelect(selectUser);
const is_tester = useShallowSelect(selectAuthIsTester);
if (!user || !is_tester) return null;
return <>{children}</>;
});
export { Superpower };

View file

@ -2,6 +2,7 @@ import React, { FC, useCallback, useEffect, useRef } from 'react';
import { IEditorComponentProps } from '~/redux/node/types';
import { usePopper } from 'react-popper';
import { Button } from '~/components/input/Button';
import { Superpower } from '~/components/boris/Superpower';
interface IProps extends IEditorComponentProps {}
@ -11,24 +12,22 @@ const EditorPublicSwitch: FC<IProps> = ({ data, setData }) => {
setData,
]);
if (process.env.REACT_APP_LAB_ENABLED !== '1') {
return null;
}
return (
<Button
color={data.is_promoted ? 'primary' : 'secondary'}
type="button"
iconLeft={data.is_promoted ? 'waves' : 'lab'}
size="giant"
label={
data.is_promoted
? 'Доступно всем на главной странице'
: 'Видно только сотрудникам в лаборатории'
}
onClick={onChange}
round
/>
<Superpower>
<Button
color={data.is_promoted ? 'primary' : 'secondary'}
type="button"
iconLeft={data.is_promoted ? 'waves' : 'lab'}
size="giant"
label={
data.is_promoted
? 'Доступно всем на главной странице'
: 'Видно только сотрудникам в лаборатории'
}
onClick={onChange}
round
/>
</Superpower>
);
};

View file

@ -0,0 +1,28 @@
import React, { FC, useCallback } from 'react';
import styles from './styles.module.scss';
import classNames from 'classnames';
interface IProps {
value?: boolean;
handler?: (val: boolean) => void;
}
const Toggle: FC<IProps> = ({ value, handler }) => {
const onClick = useCallback(() => {
if (!handler) {
return;
}
handler(!value);
}, [value, handler]);
return (
<button
type="button"
className={classNames(styles.toggle, { [styles.active]: value })}
onClick={onClick}
/>
);
};
export { Toggle };

View file

@ -0,0 +1,35 @@
@import "~/styles/variables.scss";
.toggle {
height: 24px;
width: 48px;
border-radius: 12px;
background-color: transparentize(white, 0.9);
display: flex;
border: none;
outline: none;
cursor: pointer;
position: relative;
&::after {
content: ' ';
position: absolute;
left: 3px;
top: 3px;
height: 18px;
width: 18px;
border-radius: 11px;
background-color: darken(white, 50%);
transform: translate(0, 0);
transition: transform 0.25s, color 0.25s, background-color;
}
&.active {
background-color: $wisegreen;
&::after {
transform: translate(24px, 0);
background-color: white;
}
}
}

View file

@ -21,6 +21,7 @@ import * as MODAL_ACTIONS from '~/redux/modal/actions';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { IState } from '~/redux/store';
import isBefore from 'date-fns/isBefore';
import { Superpower } from '~/components/boris/Superpower';
const mapStateToProps = (state: IState) => ({
user: pick(['username', 'is_user', 'photo', 'last_seen_boris'])(selectUser(state)),
@ -89,14 +90,14 @@ const HeaderUnconnected: FC<IProps> = memo(
ФЛОУ
</Link>
{is_user && process.env.REACT_APP_LAB_ENABLED === '1' && (
<Superpower>
<Link
className={classNames(styles.item, { [styles.is_active]: pathname === URLS.BASE })}
to={URLS.LAB}
>
ЛАБ
</Link>
)}
</Superpower>
<Link
className={classNames(styles.item, {

View file

@ -1,6 +1,6 @@
import React, { FC, useEffect } from 'react';
import React, { FC, useCallback, useEffect } from 'react';
import { selectNode, selectNodeComments } from '~/redux/node/selectors';
import { selectUser } from '~/redux/auth/selectors';
import { selectAuthIsTester, selectUser } from '~/redux/auth/selectors';
import { useDispatch } from 'react-redux';
import { NodeComments } from '~/components/node/NodeComments';
import styles from './styles.module.scss';
@ -15,11 +15,12 @@ import { Footer } from '~/components/main/Footer';
import { BorisStats } from '~/components/boris/BorisStats';
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
import { selectBorisStats } from '~/redux/boris/selectors';
import { authSetUser } from '~/redux/auth/actions';
import { authSetState, authSetUser } from '~/redux/auth/actions';
import { nodeLoadNode } from '~/redux/node/actions';
import { borisLoadStats } from '~/redux/boris/actions';
import { Container } from '~/containers/main/Container';
import StickyBox from 'react-sticky-box/dist/esnext';
import { BorisSuperpowers } from '~/components/boris/BorisSuperpowers';
type IProps = {};
@ -30,6 +31,7 @@ const BorisLayout: FC<IProps> = () => {
const user = useShallowSelect(selectUser);
const stats = useShallowSelect(selectBorisStats);
const comments = useShallowSelect(selectNodeComments);
const is_tester = useShallowSelect(selectAuthIsTester);
useEffect(() => {
const last_comment = comments[0];
@ -55,6 +57,13 @@ const BorisLayout: FC<IProps> = () => {
dispatch(borisLoadStats());
}, [dispatch]);
const setBetaTester = useCallback(
(is_tester: boolean) => {
dispatch(authSetState({ is_tester }));
},
[dispatch]
);
return (
<Container>
<div className={styles.wrap}>
@ -102,6 +111,10 @@ const BorisLayout: FC<IProps> = () => {
<p className="grey">//&nbsp;Такова&nbsp;жизнь.</p>
</div>
<div>
{user.is_user && <BorisSuperpowers active={is_tester} onChange={setBetaTester} />}
</div>
<div className={styles.stats__wrap}>
<BorisStats stats={stats} />
</div>

View file

@ -20,6 +20,11 @@ export const authSetToken = (token: IAuthState['token']) => ({
token,
});
export const authSetState = (payload: Partial<IAuthState>) => ({
type: AUTH_USER_ACTIONS.SET_STATE,
payload,
});
export const gotAuthPostMessage = ({ token }: { token: string }) => ({
type: AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE,
token,

View file

@ -1,6 +1,5 @@
import { api, cleanResult, errorMiddleware, resultMiddleware } from '~/utils/api';
import { api, cleanResult } from '~/utils/api';
import { API } from '~/constants/api';
import { IResultWithStatus } from '~/redux/types';
import {
ApiAttachSocialRequest,
ApiAttachSocialResult,

View file

@ -3,6 +3,7 @@ import { IToken, IUser } from '~/redux/auth/types';
export const AUTH_USER_ACTIONS = {
SEND_LOGIN_REQUEST: 'SEND_LOGIN_REQUEST',
SET_LOGIN_ERROR: 'SET_LOGIN_ERROR',
SET_STATE: 'SET_STATE',
SET_USER: 'SET_USER',
SET_TOKEN: 'SET_TOKEN',

View file

@ -25,6 +25,11 @@ const setUser: ActionHandler<typeof ActionCreators.authSetUser> = (state, { prof
},
});
const setState: ActionHandler<typeof ActionCreators.authSetState> = (state, { payload }) => ({
...state,
...payload,
});
const setToken: ActionHandler<typeof ActionCreators.authSetToken> = (state, { token }) => ({
...state,
token,
@ -104,6 +109,7 @@ const setRegisterSocialErrors: ActionHandler<typeof ActionCreators.authSetRegist
export const AUTH_USER_HANDLERS = {
[AUTH_USER_ACTIONS.SET_LOGIN_ERROR]: setLoginError,
[AUTH_USER_ACTIONS.SET_USER]: setUser,
[AUTH_USER_ACTIONS.SET_STATE]: setState,
[AUTH_USER_ACTIONS.SET_TOKEN]: setToken,
[AUTH_USER_ACTIONS.SET_PROFILE]: setProfile,
[AUTH_USER_ACTIONS.SET_UPDATES]: setUpdates,

View file

@ -10,6 +10,7 @@ const HANDLERS = {
const INITIAL_STATE: IAuthState = {
token: '',
user: { ...EMPTY_USER },
is_tester: false,
updates: {
last: '',

View file

@ -2,6 +2,7 @@ import { IState } from '~/redux/store';
export const selectAuth = (state: IState) => state.auth;
export const selectUser = (state: IState) => state.auth.user;
export const selectAuthIsTester = (state: IState) => state.auth.is_tester;
export const selectToken = (state: IState) => state.auth.token;
export const selectAuthLogin = (state: IState) => state.auth.login;
export const selectAuthProfile = (state: IState) => state.auth.profile;

View file

@ -37,6 +37,8 @@ export type IAuthState = Readonly<{
user: IUser;
token: string;
is_tester: boolean;
updates: {
last: string;
notifications: INotification[];

View file

@ -46,7 +46,7 @@ import { assocPath } from 'ramda';
const authPersistConfig: PersistConfig = {
key: 'auth',
whitelist: ['token', 'user', 'updates'],
whitelist: ['token', 'user', 'updates', 'is_tester'],
storage,
};