1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 21:06:42 +07:00

removed redux completely

This commit is contained in:
Fedor Katurov 2022-01-09 19:03:01 +07:00
parent 26e6d8d41b
commit a4bb07e9cf
323 changed files with 2464 additions and 3348 deletions

View file

@ -1,5 +1,5 @@
import React, { FC, memo } from "react";
import styles from "./styles.module.scss";
import React, { FC, memo } from 'react';
import styles from './styles.module.scss';
interface IProps {}

View file

@ -1,4 +1,4 @@
import * as React from "react";
import * as React from 'react';
interface IGodRaysProps {
raised?: boolean;

View file

@ -1,152 +0,0 @@
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { push as historyPush } from 'connected-react-router';
import { Link } from 'react-router-dom';
import { Logo } from '~/components/main/Logo';
import { Filler } from '~/components/containers/Filler';
import { selectAuthUpdates, selectUser } from '~/redux/auth/selectors';
import { path, pick } from 'ramda';
import { UserButton } from '../UserButton';
import { Notifications } from '../Notifications';
import { URLS } from '~/constants/urls';
import classNames from 'classnames';
import styles from './styles.module.scss';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { IState } from '~/redux/store';
import isBefore from 'date-fns/isBefore';
import { Authorized } from '~/components/containers/Authorized';
import { Button } from '~/components/input/Button';
import { useFlowStore } from '~/store/flow/useFlowStore';
import { observer } from 'mobx-react';
import { useShowModal } from '~/hooks/modal/useShowModal';
import { Dialog } from '~/constants/modal';
import { useGetLabStats } from '~/hooks/lab/useGetLabStats';
const mapStateToProps = (state: IState) => ({
user: pick(['username', 'is_user', 'photo', 'last_seen_boris'])(selectUser(state)),
updates: pick(['boris_commented_at'])(selectAuthUpdates(state)),
pathname: path(['router', 'location', 'pathname'], state),
});
const mapDispatchToProps = {
push: historyPush,
authLogout: AUTH_ACTIONS.authLogout,
authOpenProfile: AUTH_ACTIONS.authOpenProfile,
};
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
const HeaderUnconnected: FC<IProps> = observer(
({
user,
user: { is_user, last_seen_boris },
pathname,
updates: { boris_commented_at },
authLogout,
authOpenProfile,
}) => {
const [is_scrolled, setIsScrolled] = useState(false);
const { updated: flowUpdates } = useFlowStore();
const onLogin = useShowModal(Dialog.Login);
const labStats = useGetLabStats();
const onScroll = useCallback(() => {
const active = window.scrollY > 32;
if (active !== is_scrolled) setIsScrolled(active);
}, [is_scrolled, setIsScrolled]);
useEffect(() => {
onScroll();
}, [onScroll]);
useEffect(() => {
window.addEventListener('scroll', onScroll);
return () => window.removeEventListener('scroll', onScroll);
}, [onScroll]);
const hasBorisUpdates = useMemo(
() =>
is_user &&
boris_commented_at &&
(!last_seen_boris || isBefore(new Date(last_seen_boris), new Date(boris_commented_at))),
[boris_commented_at, is_user, last_seen_boris]
);
const hasLabUpdates = useMemo(() => labStats.updates.length > 0, [labStats.updates]);
const hasFlowUpdates = useMemo(() => flowUpdates.length > 0, [flowUpdates]);
return (
<div className={classNames(styles.wrap, { [styles.is_scrolled]: is_scrolled })}>
<div className={styles.container}>
<div className={classNames(styles.logo_wrapper, { [styles.logged_in]: is_user })}>
<Logo />
</div>
<Filler className={styles.filler} />
<div className={styles.plugs}>
<Authorized>
<Link
className={classNames(styles.item, {
[styles.is_active]: pathname === URLS.BASE,
[styles.has_dot]: hasFlowUpdates,
})}
to={URLS.BASE}
>
ФЛОУ
</Link>
<Link
className={classNames(styles.item, styles.lab, {
[styles.is_active]: pathname === URLS.LAB,
[styles.has_dot]: hasLabUpdates,
})}
to={URLS.LAB}
>
ЛАБ
</Link>
<Link
className={classNames(styles.item, styles.boris, {
[styles.is_active]: pathname === URLS.BORIS,
[styles.has_dot]: hasBorisUpdates,
})}
to={URLS.BORIS}
>
БОРИС
</Link>
</Authorized>
{is_user && (
<div className={classNames(styles.item, styles.notifications)}>
<Notifications />
</div>
)}
</div>
{is_user && (
<UserButton user={user} onLogout={authLogout} authOpenProfile={authOpenProfile} />
)}
{!is_user && (
<Button
className={styles.user_button}
onClick={() => onLogin({})}
round
color="secondary"
>
ВДОХ
</Button>
)}
</div>
</div>
);
}
);
const Header = connect(mapStateToProps, mapDispatchToProps)(HeaderUnconnected);
export { Header };

View file

@ -1,152 +0,0 @@
@import "src/styles/variables";
.wrap {
height: $header_height;
z-index: 25;
position: sticky;
top: 0;
width: 100%;
display: flex;
align-items: stretch;
justify-content: center;
box-sizing: border-box;
transition: background-color 0.5s;
@include desktop {
height: 64px;
padding: 0;
}
&.is_scrolled {
@include blur();
}
}
.container {
@include container;
display: flex;
align-items: center;
justify-content: flex-end;
font-weight: 500;
box-sizing: border-box;
@include tablet {
padding: 0 $gap;
}
}
.spacer {
flex: 1;
}
.plugs {
display: flex;
user-select: none;
text-transform: uppercase;
align-items: center;
@include tablet {
flex: 1;
justify-content: flex-start;
}
}
.profile {
padding: 5px 10px;
box-shadow: white 0 0 0 1px;
border-radius: 10px;
}
.user_button {
flex: 0;
cursor: pointer;
}
.item {
font: $font_16_medium;
display: flex;
align-items: center;
position: relative;
padding: $gap $gap * 2;
cursor: pointer;
transition: color 0.25s;
text-decoration: none;
color: white;
white-space: nowrap;
&:hover {
color: $red;
}
&::before {
content: ' ';
position: absolute;
bottom: 0;
height: 3px;
width: 50%;
right: 50%;
background: white;
transform: translate(50%, 0) scaleX(0);
opacity: 0;
border-radius: 3px;
transition: transform 0.5s, opacity 0.25s;
}
&::after {
content: ' ';
position: absolute;
width: 6px;
height: 6px;
border-radius: 4px;
background: lighten($red, 10%);
right: 12px;
top: 6px;
transition: opacity 0.5s;
opacity: 0;
}
&.has_dot {
&::after {
opacity: 1;
}
}
&.lab::after {
background: lighten($blue, 10%);
}
&.boris::after {
background: lighten($wisegreen, 10%);
}
@include tablet {
padding: 0 $gap * 2 0 0;
&.notifications {
flex: 1;
justify-content: flex-end;
align-items: center;
margin-right: $gap;
}
&::after {
right: 0;
}
}
}
.filler {
@include tablet {
display: none;
}
}
.logo_wrapper {
@include tablet {
&.logged_in {
display: none;
}
}
}

View file

@ -1,84 +0,0 @@
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { Icon } from '~/components/input/Icon';
import styles from './styles.module.scss';
import { connect } from 'react-redux';
import { selectAuthUpdates, selectAuthUser } from '~/redux/auth/selectors';
import { pick } from 'ramda';
import classNames from 'classnames';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { NotificationBubble } from '../../notifications/NotificationBubble';
import { IMessageNotification, INotification } from '~/redux/types';
const mapStateToProps = state => ({
user: pick(['last_seen_messages'], selectAuthUser(state)),
updates: selectAuthUpdates(state),
});
const mapDispatchToProps = {
authSetLastSeenMessages: AUTH_ACTIONS.authSetLastSeenMessages,
authOpenProfile: AUTH_ACTIONS.authOpenProfile,
};
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
const NotificationsUnconnected: FC<IProps> = ({
updates: { last, notifications },
user: { last_seen_messages },
authSetLastSeenMessages,
authOpenProfile,
}) => {
const [visible, setVisible] = useState(false);
const has_new = useMemo(
() =>
notifications.length &&
last &&
Date.parse(last) &&
(!last_seen_messages ||
(Date.parse(last_seen_messages) && Date.parse(last) > Date.parse(last_seen_messages))),
[last, last_seen_messages, notifications]
);
const onNotificationClick = useCallback(
(notification: INotification) => {
switch (notification.type) {
case 'message':
if (!(notification as IMessageNotification)?.content?.from?.username) {
return;
}
return authOpenProfile((notification as IMessageNotification).content.from!.username);
default:
return;
}
},
[authOpenProfile]
);
const showList = useCallback(() => setVisible(true), [setVisible]);
const hideList = useCallback(() => setVisible(false), [setVisible]);
useEffect(() => {
if (!visible || !has_new || !last) return;
authSetLastSeenMessages(last);
}, [visible, last, has_new, authSetLastSeenMessages]);
return (
<div
className={classNames(styles.wrap, {
[styles.is_new]: has_new,
[styles.active]: notifications.length > 0,
})}
>
<div className={styles.icon} onFocus={showList} onBlur={hideList} tabIndex={-1}>
{has_new ? <Icon icon="bell_ring" size={24} /> : <Icon icon="bell" size={24} />}
</div>
{visible && (
<NotificationBubble notifications={notifications} onClick={onNotificationClick} />
)}
</div>
);
};
const Notifications = connect(mapStateToProps, mapDispatchToProps)(NotificationsUnconnected);
export { Notifications };

View file

@ -1,51 +0,0 @@
@import "src/styles/variables";
@keyframes ring {
0% {
transform: rotate(-10deg);
}
20% {
transform: rotate(10deg);
}
40% {
transform: rotate(-10deg);
}
100% {
transform: rotate(0);
}
}
.wrap {
fill: white;
position: relative;
outline: none;
color: white;
word-wrap: initial;
white-space: initial;
&.active {
.icon {
opacity: 1;
}
}
&.is_new {
.icon {
animation: ring 1s infinite alternate;
opacity: 1;
svg {
fill: $red;
}
}
}
}
.icon {
outline: none;
cursor: pointer;
opacity: 0.5;
}

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback, useEffect, useState } from "react";
import classNames from "classnames";
import styles from "./styles.module.scss";
import { Group } from "~/components/containers/Group";
import React, { FC, useCallback, useEffect, useState } from 'react';
import classNames from 'classnames';
import styles from './styles.module.scss';
import { Group } from '~/components/containers/Group';
interface IProps {}

View file

@ -1,28 +1,25 @@
import React, { FC, useCallback } from "react";
import { Group } from "~/components/containers/Group";
import styles from "./styles.module.scss";
import { getURL } from "~/utils/dom";
import { Icon } from "~/components/input/Icon";
import { IUser } from "~/redux/auth/types";
import { PRESETS } from "~/constants/urls";
import { authOpenProfile } from "~/redux/auth/actions";
import React, { FC, useCallback } from 'react';
import { Group } from '~/components/containers/Group';
import styles from './styles.module.scss';
import { getURL } from '~/utils/dom';
import { Icon } from '~/components/input/Icon';
import { IUser } from '~/types/auth';
import { PRESETS } from '~/constants/urls';
interface IProps {
user: Partial<IUser>;
onLogout: () => void;
authOpenProfile: typeof authOpenProfile;
authOpenProfile: () => void;
}
const UserButton: FC<IProps> = ({ user: { username, photo }, authOpenProfile, onLogout }) => {
const onProfileOpen = useCallback(() => {
if (!username) return;
authOpenProfile(username);
}, [authOpenProfile, username]);
authOpenProfile();
}, [authOpenProfile]);
const onSettingsOpen = useCallback(() => {
if (!username) return;
authOpenProfile(username);
}, [authOpenProfile, username]);
authOpenProfile();
}, [authOpenProfile]);
return (
<div className={styles.wrap}>