1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 12:56:41 +07:00

actually getting event about adding account

This commit is contained in:
Fedor Katurov 2020-07-27 14:17:32 +07:00
parent 5396cf7611
commit e94d776ee5
12 changed files with 252 additions and 136 deletions

View file

@ -22,9 +22,9 @@ const UserButton: FC<IProps> = ({ user: { username, photo }, authOpenProfile, on
authOpenProfile(username, 'settings');
}, [authOpenProfile, username]);
// const onMessagesOpen = useCallback(() => {
// authOpenProfile(username, 'messages');
// }, [authOpenProfile, username]);
const onAccountsOpen = useCallback(() => {
authOpenProfile(username, 'accounts');
}, [authOpenProfile, username]);
return (
<div className={styles.wrap}>
@ -42,6 +42,7 @@ const UserButton: FC<IProps> = ({ user: { username, photo }, authOpenProfile, on
<div className={styles.menu}>
<div onClick={onProfileOpen}>Профиль</div>
<div onClick={onSettingsOpen}>Настройки</div>
<div onClick={onAccountsOpen}>Аккаунты</div>
<div onClick={onLogout}>Выдох</div>
</div>
</div>

View file

@ -0,0 +1,120 @@
import React, { FC, Fragment, useCallback, useEffect } from 'react';
import { ISocialProvider } from '~/redux/auth/types';
import styles from './styles.scss';
import { Placeholder } from '~/components/placeholders/Placeholder';
import { Icon } from '~/components/input/Icon';
import { Button } from '~/components/input/Button';
import { Group } from '~/components/containers/Group';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { selectAuthProfile } from '~/redux/auth/selectors';
import { IState } from '~/redux/store';
import { connect } from 'react-redux';
import { API } from '~/constants/api';
const mapStateToProps = (state: IState) => selectAuthProfile(state).socials;
const mapDispatchToProps = {
authGetSocials: AUTH_ACTIONS.authGetSocials,
authDropSocial: AUTH_ACTIONS.authDropSocial,
};
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
const SOCIAL_ICONS: Record<ISocialProvider, string> = {
vkontakte: 'vk',
google: 'google',
};
const ProfileAccountsUnconnected: FC<IProps> = ({
authGetSocials,
authDropSocial,
accounts,
is_loading,
}) => {
const onMessage = useCallback(event => {
if (event?.data?.type !== 'oauth_attach' || !event?.data?.payload?.token) return;
const token = event?.data?.payload?.token;
console.log('GOT TOKEN!!!', token);
}, []);
const openOauthWindow = useCallback(
(provider: ISocialProvider) => () => {
console.log(API.USER.OAUTH_WINDOW(provider));
window.open(API.USER.OAUTH_WINDOW(provider), '', 'width=600,height=400');
},
[]
);
useEffect(() => {
authGetSocials();
}, [authGetSocials]);
useEffect(() => {
window.addEventListener('message', onMessage);
return () => window.removeEventListener('message', onMessage);
}, [onMessage]);
return (
<Group className={styles.wrap}>
<Group className={styles.info}>
<p>
Ты можешь входить в Убежище, используя аккаунты на других сайтах вместо ввода логина и
пароля.
</p>
<p>
Мы честно украдём и будем хранить твои имя, фото и адрес на этом сайте, но никому о них не
расскажем.
</p>
</Group>
{is_loading && (
<div className={styles.loader}>
{[...new Array(accounts.length || 1)].map((_, i) => (
<Fragment key={i}>
<Placeholder width="50%" />
<Placeholder width="auto" />
</Fragment>
))}
</div>
)}
{!is_loading && accounts.length > 0 && (
<div className={styles.list}>
{!is_loading &&
accounts.map(it => (
<div className={styles.account} key={`${it.provider}-${it.id}`}>
<div
className={styles.account__photo}
style={{ backgroundImage: it.photo ? `url(${it.photo})` : 'none' }}
>
<div className={styles.account__provider}>
<Icon icon={SOCIAL_ICONS[it.provider]} size={12} />
</div>
</div>
<div className={styles.account__name}>{it.name}</div>
<div className={styles.account__drop}>
<Icon icon="close" size={22} onClick={() => authDropSocial(it.provider, it.id)} />
</div>
</div>
))}
</div>
)}
<Group horizontal className={styles.buttons}>
<Button size="small" iconLeft="vk" color="gray" onClick={openOauthWindow('vkontakte')}>
Вконтакте
</Button>
<Button size="small" iconLeft="google" color="gray" onClick={openOauthWindow('google')}>
Google
</Button>
</Group>
</Group>
);
};
const ProfileAccounts = connect(mapStateToProps, mapDispatchToProps)(ProfileAccountsUnconnected);
export { ProfileAccounts };

View file

@ -0,0 +1,80 @@
.wrap {
padding: $gap;
}
.list {
padding: $gap;
box-shadow: inset transparentize(white, 0.9) 0 0 0 2px;
border-radius: $radius;
}
.buttons {
background: transparentize(black, 0.8);
border-radius: $radius;
padding: $gap / 2;
display: flex;
align-items: center;
justify-content: flex-end;
}
.add {
//background-color: $content_bg !important;
}
.loader {
display: grid;
grid-row-gap: $gap;
grid-column-gap: $gap * 4;
grid-template-columns: 1fr 32px;
& > div {
height: 48px;
width: auto;
}
}
.account {
display: grid;
grid-template-columns: 20px auto 20px;
grid-column-gap: $gap * 1.5;
align-items: center;
&__photo {
width: 28px;
height: 28px;
background: 50% 50% no-repeat;
background-size: cover;
border-radius: 2px;
position: relative;
}
&__provider {
position: absolute;
right: -2px;
bottom: -8px;
background: $content_bg;
}
&__name {
font: $font_16_semibold;
padding-left: $gap / 2;
}
&__drop {
cursor: pointer;
opacity: 0.5;
transition: opacity 0.25s;
fill: $red;
display: flex;
align-items: center;
&:hover {
opacity: 1;
}
}
}
.info {
padding: $gap;
font: $font_14_regular;
}

View file

@ -10,7 +10,7 @@ import { InputText } from '~/components/input/InputText';
import reject from 'ramda/es/reject';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { ERROR_LITERAL } from '~/constants/errors';
import { ProfileSettingsSocials } from '~/components/profile/ProfileSettingsSocials';
import { ProfileAccounts } from '~/components/profile/ProfileAccounts';
const mapStateToProps = state => ({
user: selectAuthUser(state),
@ -20,8 +20,6 @@ const mapStateToProps = state => ({
const mapDispatchToProps = {
authPatchUser: AUTH_ACTIONS.authPatchUser,
authSetProfile: AUTH_ACTIONS.authSetProfile,
authGetSocials: AUTH_ACTIONS.authGetSocials,
authDropSocial: AUTH_ACTIONS.authDropSocial,
};
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
@ -31,8 +29,6 @@ const ProfileSettingsUnconnected: FC<IProps> = ({
profile: { patch_errors, socials },
authPatchUser,
authSetProfile,
authGetSocials,
authDropSocial,
}) => {
const [password, setPassword] = useState('');
const [new_password, setNewPassword] = useState('');
@ -74,26 +70,21 @@ const ProfileSettingsUnconnected: FC<IProps> = ({
return (
<form className={styles.wrap} onSubmit={onSubmit}>
<Group>
<InputText
value={data.fullname}
handler={setFullname}
title="Полное имя"
error={patch_errors.fullname && ERROR_LITERAL[patch_errors.fullname]}
/>
<Group>
<InputText
value={data.fullname}
handler={setFullname}
title="Полное имя"
error={patch_errors.fullname && ERROR_LITERAL[patch_errors.fullname]}
/>
<Textarea value={data.description} handler={setDescription} title="Описание" />
<Textarea value={data.description} handler={setDescription} title="Описание" />
<div className={styles.small}>
Описание будет видно на странице профиля. Здесь работают те же правила оформления, что и в
комментариях.
</div>
<ProfileSettingsSocials
accounts={socials.accounts}
is_loading={socials.is_loading}
authGetSocials={authGetSocials}
authDropSocial={authDropSocial}
/>
<div className={styles.small}>
Описание будет видно на странице профиля. Здесь работают те же правила оформления, что и
в комментариях.
</div>
</Group>
<Group className={styles.pad}>
<InputText
@ -133,6 +124,8 @@ const ProfileSettingsUnconnected: FC<IProps> = ({
</div>
</Group>
<Filler />
<Group horizontal>
<Filler />
<Button title="Сохранить" type="submit" />

View file

@ -12,3 +12,6 @@
font: $font_12_regular;
padding: 0 $gap $gap;
}
.grid {
}

View file

@ -1,63 +0,0 @@
import React, { FC, useEffect, Fragment } from 'react';
import * as AUTH_ACTIONS from '~/redux/auth/actions';
import { IAuthState, ISocialProvider } from '~/redux/auth/types';
import styles from './styles.scss';
import { Placeholder } from '~/components/placeholders/Placeholder';
import { Icon } from '~/components/input/Icon';
interface IProps {
accounts: IAuthState['profile']['socials']['accounts'];
is_loading: boolean;
authGetSocials: typeof AUTH_ACTIONS.authGetSocials;
authDropSocial: typeof AUTH_ACTIONS.authDropSocial;
}
const SOCIAL_ICONS: Record<ISocialProvider, string> = {
vkontakte: 'vk',
google: 'google',
};
const ProfileSettingsSocials: FC<IProps> = ({
authGetSocials,
authDropSocial,
accounts,
is_loading,
}) => {
useEffect(() => {
authGetSocials();
}, [authGetSocials]);
if (!accounts.length) return null;
return (
<div className={styles.wrap}>
{is_loading && (
<div className={styles.loader}>
{[...new Array(accounts.length || 1)].map((_, i) => (
<Fragment key={i}>
<Placeholder width="50%" />
<Placeholder width="auto" />
</Fragment>
))}
</div>
)}
{!is_loading &&
accounts.map(it => (
<div className={styles.account}>
<div className={styles.account__provider}>
<Icon icon={SOCIAL_ICONS[it.provider]} />
</div>
<div className={styles.account__name}>{it.name}</div>
<div className={styles.account__drop}>
<Icon icon="close" size={22} onClick={() => authDropSocial(it.provider, it.id)} />
</div>
</div>
))}
</div>
);
};
export { ProfileSettingsSocials };

View file

@ -1,36 +0,0 @@
.wrap {
padding: $gap,
}
.loader {
display: grid;
grid-row-gap: $gap;
grid-column-gap: $gap * 4;
grid-template-columns: 1fr 32px;
& > div {
height: 22px;
width: auto;
}
}
.account {
display: grid;
grid-template-columns: 20px auto 20px;
grid-column-gap: $gap;
&__name {
font: $font_16_semibold;
}
&__drop {
cursor: pointer;
opacity: 0.5;
transition: opacity 0.25s;
fill: $red;
&:hover {
opacity: 1;
}
}
}