Backend: Update user-delete
This commit is contained in:
parent
b67ae3166d
commit
931b7e0b66
@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
- Moved advanced user settings to 'Advanced' tab
|
||||
- Moved advanced guild settings to 'Advanced' tab
|
||||
|
||||
### Fixed
|
||||
- Emails, and passwords salts/hashes are now forgotten when deleting user is deleted
|
||||
|
||||
## [Winter 0.4.0-alpha] - 2022/12/17
|
||||
|
||||
|
@ -22,17 +22,6 @@ router.get('/count', async (req, res) => {
|
||||
res.json(count);
|
||||
});
|
||||
|
||||
router.delete('/:id', updateUser, validateUser, async (req, res) => {
|
||||
const user = res.locals.user;
|
||||
user.username = `deleted-user-${generateInvite(6)}`;
|
||||
user.discriminator = 0;
|
||||
delete user.salt;
|
||||
delete user.hash;
|
||||
await user.save();
|
||||
|
||||
res.status(201).json({ message: 'Modified' });
|
||||
});
|
||||
|
||||
router.get('/check-username', async (req, res) => {
|
||||
const username = req.query.value?.toString().toLowerCase();
|
||||
const exists = await User.exists({
|
||||
|
@ -2,7 +2,7 @@ import { WSEvent } from './ws-event';
|
||||
import { Socket } from 'socket.io';
|
||||
import { WebSocket } from '../websocket';
|
||||
import generateInvite from '../../data/utils/generate-invite';
|
||||
import { WS } from '@acrd/types';
|
||||
import { Entity, UserTypes, WS } from '@acrd/types';
|
||||
|
||||
export default class implements WSEvent<'USER_DELETE'> {
|
||||
public on = 'USER_DELETE' as const;
|
||||
@ -15,6 +15,9 @@ export default class implements WSEvent<'USER_DELETE'> {
|
||||
discriminator: 0,
|
||||
locked: true,
|
||||
username: `Deleted User ${generateInvite(6)}`,
|
||||
email: generateInvite(16),
|
||||
salt: null,
|
||||
hash: null,
|
||||
};
|
||||
await user.updateOne(partialUser);
|
||||
|
||||
|
@ -21,9 +21,9 @@ const CreateInvite: React.FunctionComponent = () => {
|
||||
}, [isOpen]);
|
||||
|
||||
const copyCode = () => window.navigator.clipboard.writeText(activeInvite!.id);
|
||||
|
||||
|
||||
return (activeInvite) ? (
|
||||
<Modal typeName={'CreateInvite'} className="p-5">
|
||||
<Modal typeName={'CreateInvite'} className="p-5" >
|
||||
<header className="mb-3">
|
||||
<h1 className="font-bold inline uppercase">Invite Friends to {activeGuild?.name}</h1>
|
||||
</header>
|
||||
@ -43,5 +43,5 @@ const CreateInvite: React.FunctionComponent = () => {
|
||||
</Modal>
|
||||
) : null;
|
||||
}
|
||||
|
||||
|
||||
export default CreateInvite;
|
@ -1,3 +1,12 @@
|
||||
.invite:nth-child(even) {
|
||||
th,
|
||||
td {
|
||||
padding-left: 7.5px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
tr:nth-child(even) {
|
||||
background-color: var(--bg-secondary);
|
||||
}
|
@ -3,7 +3,7 @@ import { useParams } from 'react-router';
|
||||
import { fetchGuildInvites, getGuild, getGuildInvites, getGuildUsers } from '../../../store/guilds';
|
||||
import { deleteInvite } from '../../../store/invites';
|
||||
import { openSaveChanges } from '../../../store/ui';
|
||||
import Username from '../../user/username';
|
||||
import FoundUsername from '../../user/username';
|
||||
import CircleButton from '../../utils/buttons/circle-button';
|
||||
import './guild-settings-invites.scoped.css';
|
||||
|
||||
@ -17,29 +17,33 @@ const GuildSettingsInvites: React.FunctionComponent = () => {
|
||||
dispatch(fetchGuildInvites(guildId));
|
||||
|
||||
const Invites = () => (
|
||||
<div className="mt-2">
|
||||
{invites.filter(x => x).map(i => (
|
||||
<div className="flex align-center justify-between invite w-full p-2">
|
||||
<code className='font-bold pt-2'>{i.id}</code>
|
||||
<span className="ml-4 secondary">
|
||||
<span className='ml-4'>Used <code>{i.uses}</code> times</span>
|
||||
</span>
|
||||
<span className='ml-4'>Created by
|
||||
<Username
|
||||
<table className="mt-2">
|
||||
<tr>
|
||||
<th>Code</th>
|
||||
<th>Used</th>
|
||||
<th>Creator</th>
|
||||
</tr>
|
||||
{invites.map(i => (
|
||||
<tr key={i.code} className="invite">
|
||||
<td><code className='font-bold primary'>{i.id}</code></td>
|
||||
<td><code className='tertiary'>{i.uses}</code> times</td>
|
||||
<td>
|
||||
<FoundUsername
|
||||
size='sm'
|
||||
className='h-full'
|
||||
user={guildUsers.find(gu => gu.id == i.inviterId)}
|
||||
guild={guild} />
|
||||
</span>
|
||||
<span className="justify-end">
|
||||
</td>
|
||||
<td className='w-0'>
|
||||
<CircleButton
|
||||
type="button"
|
||||
style={{ borderColor: 'var(--danger)', color: 'var(--danger)' }}
|
||||
onClick={() => dispatch(deleteInvite(i.id))}>X</CircleButton>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{!invites.length && <span>No invites created.</span>}
|
||||
</div>
|
||||
</table>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -7,7 +7,8 @@ import Category from '../../utils/category';
|
||||
import SaveChanges from '../../utils/save-changes';
|
||||
import Input from '../../inputs/input';
|
||||
import ChannelSelect from '../../inputs/channel-select';
|
||||
|
||||
import CircleButton from '../../utils/buttons/circle-button';
|
||||
|
||||
const GuildSettingsOverview: React.FunctionComponent = () => {
|
||||
const dispatch = useDispatch();
|
||||
const guild = useSelector((s: Store.AppState) => s.ui.activeGuild)!;
|
||||
@ -22,7 +23,7 @@ const GuildSettingsOverview: React.FunctionComponent = () => {
|
||||
const confirmation = window.confirm('Are you sure you want to delete this guild?');
|
||||
if (confirmation) dispatch(deleteGuild(guild.id));
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<form
|
||||
onChange={() => dispatch(openSaveChanges(true))}
|
||||
@ -30,7 +31,7 @@ const GuildSettingsOverview: React.FunctionComponent = () => {
|
||||
<header>
|
||||
<h1 className="text-xl font-bold inline">Guild Overview</h1>
|
||||
</header>
|
||||
|
||||
|
||||
<section className="w-1/2">
|
||||
<Input
|
||||
label="Name"
|
||||
@ -64,18 +65,18 @@ const GuildSettingsOverview: React.FunctionComponent = () => {
|
||||
title="Advanced Settings" />
|
||||
|
||||
<section>
|
||||
<NormalButton
|
||||
type="button"
|
||||
<CircleButton
|
||||
onClick={onDelete}
|
||||
className="bg-danger">Delete</NormalButton>
|
||||
style={{ color: 'var(--danger)', borderColor: 'var(--danger)' }}
|
||||
className="border-danger red m-2">Delete</CircleButton>
|
||||
</section>
|
||||
|
||||
<SaveChanges
|
||||
setValue={setValue}
|
||||
onSave={onSave}
|
||||
obj={guild} />
|
||||
</form>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default GuildSettingsOverview;
|
5
frontend/src/components/modals/modal.scoped.css
Normal file
5
frontend/src/components/modals/modal.scoped.css
Normal file
@ -0,0 +1,5 @@
|
||||
.modal {
|
||||
position: relative;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
@ -3,6 +3,7 @@ import { useSnackbar } from 'notistack';
|
||||
import ReactModal from 'react-modal'
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { closeModal } from '../../store/ui';
|
||||
import './modal.scoped.css';
|
||||
|
||||
export interface ModalProps {
|
||||
typeName: string;
|
||||
@ -18,7 +19,7 @@ const sizeClass = {
|
||||
'xl': 'rounded-lg 2xl:w-1/2 2xl:inset-x-1/4 2xl:top-1/4 md:w-1/3 md:inset-x-1/3 md:top-20',
|
||||
'full': 'h-full w-full',
|
||||
};
|
||||
|
||||
|
||||
const Modal: React.FunctionComponent<ModalProps> = ({ className, typeName, size, children }) => {
|
||||
const dispatch = useDispatch();
|
||||
const openModal = useSelector((s: Store.AppState) => s.ui.openModal);
|
||||
@ -27,7 +28,7 @@ const Modal: React.FunctionComponent<ModalProps> = ({ className, typeName, size,
|
||||
return (
|
||||
<ReactModal
|
||||
className={classNames(
|
||||
`bg-bg-primary overflow-auto fixed outline-none`,
|
||||
`modal bg-bg-primary overflow-auto fixed outline-none`,
|
||||
className,
|
||||
sizeClass[size ?? 'sm'],
|
||||
)}
|
||||
@ -39,5 +40,5 @@ const Modal: React.FunctionComponent<ModalProps> = ({ className, typeName, size,
|
||||
}}>{children}</ReactModal>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default Modal;
|
@ -7,7 +7,7 @@ import { FunctionComponent, useState } from 'react';
|
||||
import { useSelector, useStore } from 'react-redux';
|
||||
import { getGuild } from '../../store/guilds';
|
||||
import SidebarIcon from '../navigation/sidebar/sidebar-icon';
|
||||
import Username from '../user/username';
|
||||
import FoundUsername from '../user/username';
|
||||
import Category from '../utils/category';
|
||||
import NavTabs from '../utils/nav-tabs';
|
||||
import Modal from './modal';
|
||||
@ -128,7 +128,7 @@ const UserProfile: FunctionComponent = () => {
|
||||
size="md">
|
||||
<header className="bg-bg-tertiary">
|
||||
<div className="p-5">
|
||||
<Username size="lg" user={user} />
|
||||
<FoundUsername size="lg" user={user} />
|
||||
<UserBadges />
|
||||
</div>
|
||||
<hr className="border-bg-primary" />
|
||||
|
@ -0,0 +1,57 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Category from '../../utils/category';
|
||||
import CircleButton from '../../utils/buttons/circle-button';
|
||||
import Toggle from '../../inputs/toggle';
|
||||
import { deleteSelf } from '../../../store/users';
|
||||
import { toggleDevMode } from '../../../store/config';
|
||||
|
||||
const UserSettingsAdvanced: React.FunctionComponent = () => {
|
||||
const dispatch = useDispatch();
|
||||
const selfUser = useSelector((s: Store.AppState) => s.auth.user);
|
||||
const devMode = useSelector((s: Store.AppState) => s.config.devMode);
|
||||
|
||||
const requestDelete = () => {
|
||||
const confirmation = window.prompt(
|
||||
`WARNING: You are about to delete account '${selfUser.username}'\n` +
|
||||
`Type \'accord\' to confirm deletion.`
|
||||
);
|
||||
if (confirmation == 'accord')
|
||||
dispatch(deleteSelf());
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col pt-14 px-10 pb-20 h-full mt-1">
|
||||
<header>
|
||||
<h1 className="text-xl font-bold inline">Advanced Settings</h1>
|
||||
</header>
|
||||
|
||||
<Category
|
||||
className="py-2 mt-5"
|
||||
title="Advanced Settings" />
|
||||
|
||||
<section>
|
||||
<div className="w-1/2 pb-5">
|
||||
<label htmlFor="devMode">Dev Mode</label>
|
||||
<Toggle
|
||||
onChange={(e) => e.stopPropagation()}
|
||||
onClick={() => dispatch(toggleDevMode())}
|
||||
checked={devMode}
|
||||
className="float-right"
|
||||
id="devMode" />
|
||||
</div>
|
||||
|
||||
<Category
|
||||
className="py-2 mt-5"
|
||||
title="Delete Account" />
|
||||
|
||||
<CircleButton
|
||||
id="deleteUserButton"
|
||||
onClick={requestDelete}
|
||||
style={{ color: 'var(--danger)', borderColor: 'var(--danger)' }}
|
||||
className="border-danger red m-2">Delete</CircleButton>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserSettingsAdvanced;
|
@ -1,12 +1,8 @@
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { toggleDevMode } from '../../../store/config';
|
||||
import { openSaveChanges } from '../../../store/ui';
|
||||
import { updateSelf, deleteSelf, uploadUserAvatar } from '../../../store/users';
|
||||
import NormalButton from '../../utils/buttons/normal-button';
|
||||
import Category from '../../utils/category';
|
||||
import { updateSelf, uploadUserAvatar } from '../../../store/users';
|
||||
import Input from '../../inputs/input';
|
||||
import Toggle from '../../inputs/toggle';
|
||||
import SaveChanges from '../../utils/save-changes';
|
||||
import FileInput from '../../inputs/file-input';
|
||||
|
||||
@ -14,16 +10,11 @@ const UserSettingsOverview: React.FunctionComponent = () => {
|
||||
const dispatch = useDispatch();
|
||||
const user = useSelector((s: Store.AppState) => s.auth.user)!;
|
||||
const { register, handleSubmit, setValue } = useForm();
|
||||
const devMode = useSelector((s: Store.AppState) => s.config.devMode);
|
||||
|
||||
const onSave = (e) => {
|
||||
const onUpdate = (payload) => dispatch(updateSelf(payload));
|
||||
handleSubmit(onUpdate)(e);
|
||||
};
|
||||
const onDelete = () => {
|
||||
const confirmation = window.confirm('Are you sure you want to delete your user?');
|
||||
if (confirmation) dispatch(deleteSelf());
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col pt-14 px-10 pb-20 h-full mt-1">
|
||||
@ -59,28 +50,6 @@ const UserSettingsOverview: React.FunctionComponent = () => {
|
||||
onSave={onSave}
|
||||
obj={user} />
|
||||
</form>
|
||||
|
||||
<Category
|
||||
className="py-2 mt-5"
|
||||
title="Advanced Settings" />
|
||||
|
||||
<section>
|
||||
<div className="w-1/2 pb-5">
|
||||
<label htmlFor="devMode">Dev Mode</label>
|
||||
<Toggle
|
||||
onChange={(e) => e.stopPropagation()}
|
||||
onClick={() => dispatch(toggleDevMode())}
|
||||
checked={devMode}
|
||||
className="float-right"
|
||||
id="devMode" />
|
||||
</div>
|
||||
|
||||
<NormalButton
|
||||
id="deleteUserButton"
|
||||
role="button"
|
||||
onClick={handleSubmit(onDelete)}
|
||||
className="bg-danger">Delete</NormalButton>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import EscButton from '../../utils/buttons/esc-button';
|
||||
import Category from '../../utils/category';
|
||||
import NavTabs from '../../utils/nav-tabs';
|
||||
import Modal from '../modal';
|
||||
import UserSettingsAdvanced from './user-settings-advanced';
|
||||
import UserSettingsOverview from './user-settings-overview';
|
||||
import UserSettingsSecurity from './user-settings-security';
|
||||
import UserSettingsThemes from './user-settings-themes';
|
||||
@ -32,6 +33,7 @@ const UserSettings: React.FunctionComponent = () => {
|
||||
{ name: 'Overview', id: 'overview' },
|
||||
{ name: 'Security', id: 'security' },
|
||||
{ name: <>Themes <NewBadge /></>, id: 'themes' },
|
||||
{ name: 'Advanced', id: 'advanced' },
|
||||
]} />
|
||||
<div className="rounded-sm bg-bg-modifier-accent h-px w-42 my-2 mx-2.5 " />
|
||||
|
||||
@ -45,6 +47,7 @@ const UserSettings: React.FunctionComponent = () => {
|
||||
{tab === 'overview' && <UserSettingsOverview />}
|
||||
{tab === 'themes' && <UserSettingsThemes />}
|
||||
{tab === 'security' && <UserSettingsSecurity />}
|
||||
{tab === 'advanced' && <UserSettingsAdvanced />}
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 h-full">
|
||||
|
@ -7,10 +7,10 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Link } from 'react-router-dom';
|
||||
import usePerms from '../../../hooks/use-perms';
|
||||
import { getChannelUsers, joinVoiceChannel } from '../../../store/channels';
|
||||
import { getGuild, getGuildChannels } from '../../../store/guilds';
|
||||
import { getGuildChannels } from '../../../store/guilds';
|
||||
import { actions as ui } from '../../../store/ui';
|
||||
import ChannelMenu from '../../ctx-menus/channel-menu';
|
||||
import Username from '../../user/username';
|
||||
import FoundUsername from '../../user/username';
|
||||
import React from 'react';
|
||||
import { Entity } from '@acrd/types';
|
||||
|
||||
@ -41,7 +41,7 @@ const ChannelTabs: React.FunctionComponent = () => {
|
||||
return <div className="p-2 pl-3">{users.map(u =>
|
||||
<ContextMenuTrigger key={u.id} id={u.id}>
|
||||
<div className="mb-1">
|
||||
<Username user={u} size="sm" guild={activeGuild} />
|
||||
<FoundUsername user={u} size="sm" guild={activeGuild} />
|
||||
</div>
|
||||
</ContextMenuTrigger>
|
||||
)}</div>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Username from '../../user/username';
|
||||
import FoundUsername from '../../user/username';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCog, faPhoneSlash, faSignal } from '@fortawesome/free-solid-svg-icons';
|
||||
@ -51,7 +51,7 @@ const SidebarFooter: React.FunctionComponent = () => {
|
||||
<VoiceFooter />
|
||||
<div className="relative flex items-center py-2">
|
||||
<div className="select-all">
|
||||
<Username user={user} />
|
||||
<FoundUsername user={user} />
|
||||
</div>
|
||||
<FontAwesomeIcon
|
||||
id="userSettingsButton"
|
||||
|
@ -10,7 +10,7 @@ import { fetchInvite, getInvite } from '../../store/invites';
|
||||
import { joinGuild } from '../../store/members';
|
||||
import { getTag, getUser } from '../../store/users';
|
||||
import SidebarIcon from '../navigation/sidebar/sidebar-icon';
|
||||
import Username from '../user/username';
|
||||
import FoundUsername from '../user/username';
|
||||
import NormalButton from '../utils/buttons/normal-button';
|
||||
import FullParticles from '../utils/full-particles';
|
||||
import PageWrapper from './page-wrapper';
|
||||
@ -76,7 +76,7 @@ const InvitePage: React.FunctionComponent<InvitePageProps> = () => {
|
||||
</span>
|
||||
<span className='text-center'>
|
||||
<div className="heading font-bold">Owned By</div>
|
||||
<Username user={ownerUser} />
|
||||
<FoundUsername user={ownerUser} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Category from '../utils/category';
|
||||
import Username from './username';
|
||||
import FoundUsername from './username';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ContextMenuTrigger } from 'react-contextmenu';
|
||||
import GuildMemberMenu from '../ctx-menus/guild-member/guild-member-menu';
|
||||
@ -38,7 +38,7 @@ const MemberList: React.FunctionComponent = () => {
|
||||
id={u.id}
|
||||
key={u.id}>
|
||||
<div className="m-2">
|
||||
<Username guild={guild} user={u} />
|
||||
<FoundUsername guild={guild} user={u} />
|
||||
</div>
|
||||
<GuildMemberMenu user={u} />
|
||||
</ContextMenuTrigger>
|
||||
|
@ -13,13 +13,23 @@ export interface UsernameProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Username: React.FunctionComponent<UsernameProps> = ({ guild, user, className, size = 'md' }) => {
|
||||
const highestRole = useSelector(getMemberHighestRole(guild?.id, user?.id));
|
||||
export const Username: React.FunctionComponent<UsernameProps> = (props) =>
|
||||
(props.user)
|
||||
? FoundUsername(props)
|
||||
: FoundUsername({
|
||||
...props,
|
||||
user: {
|
||||
id: '0',
|
||||
username: 'Unknown User',
|
||||
discriminator: 0,
|
||||
} as Partial<Entity.User>,
|
||||
} as any);
|
||||
|
||||
const FoundUsername: React.FunctionComponent<UsernameProps> = ({ guild, user, className, size = 'md' }) => {
|
||||
const highestRole = useSelector(getMemberHighestRole(guild?.id, user.id));
|
||||
|
||||
const userOwnsGuild = (guild?.ownerId === user.id);
|
||||
const discrim = user.discriminator
|
||||
.toString()
|
||||
.padStart(4, '0');
|
||||
const discrim = user.discriminator.toString().padStart(4, '0');
|
||||
const isOnline = (user.status === 'ONLINE');
|
||||
|
||||
const UserPresence = () => {
|
||||
|
@ -9,5 +9,5 @@ const CircleButton: React.FunctionComponent<any> = (props) => {
|
||||
props.className)}>{props.children}</button>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default CircleButton;
|
@ -29,17 +29,16 @@ const WSListener: React.FunctionComponent = () => {
|
||||
|
||||
const state = () => store.getState() as Store.AppState;
|
||||
|
||||
// TODO: make alphabetical order
|
||||
useEffect(() => {
|
||||
if (hasListened) return;
|
||||
if (hasListened) return;
|
||||
|
||||
const handleDialog = (dialog: Dialog) =>
|
||||
const handleDialog = (dialog: Dialog) =>
|
||||
enqueueSnackbar(`${dialog.content}.`, {
|
||||
anchorOrigin: { vertical: 'bottom', horizontal: 'left' },
|
||||
variant: dialog.variant,
|
||||
autoHideDuration: 5000,
|
||||
});
|
||||
|
||||
|
||||
ws.on('error', (error: any) => handleDialog({
|
||||
variant: 'error',
|
||||
content: error.data?.message ?? error.message,
|
||||
@ -56,9 +55,9 @@ const WSListener: React.FunctionComponent = () => {
|
||||
ws.on('CHANNEL_CREATE', (args) => {
|
||||
// if we created it, we want to navigate there
|
||||
// we'd expect the user to exist, as they should be logged in to receive ws events
|
||||
const { auth, ui } = state();
|
||||
const { auth, ui } = state();
|
||||
const selfCreated = args.creatorId === auth.user!.id;
|
||||
|
||||
|
||||
// we cannot go to the channel if not in store
|
||||
dispatch(channels.created(args));
|
||||
|
||||
@ -105,7 +104,7 @@ const WSListener: React.FunctionComponent = () => {
|
||||
ws.on('GUILD_MEMBER_ADD', (args) => {
|
||||
// we not getting other users when joining guild
|
||||
dispatch(users.fetched([args.user]));
|
||||
dispatch(members.added(args));
|
||||
dispatch(members.added(args));
|
||||
});
|
||||
ws.on('GUILD_MEMBER_UPDATE', (args) => dispatch(members.updated(args)));
|
||||
// user may be in mutual guilds, and therefore not removed from global user cache
|
||||
@ -125,7 +124,7 @@ const WSListener: React.FunctionComponent = () => {
|
||||
if (isBlocked) return;
|
||||
|
||||
dispatch(messages.created(args));
|
||||
|
||||
|
||||
const { channelId } = args.message;
|
||||
const { activeChannel } = state().ui;
|
||||
if (activeChannel && activeChannel.id !== channelId)
|
||||
@ -151,7 +150,7 @@ const WSListener: React.FunctionComponent = () => {
|
||||
history.push('/');
|
||||
dispatch(logoutUser());
|
||||
});
|
||||
ws.on('USER_UPDATE', (args) => {
|
||||
ws.on('USER_UPDATE', (args) => {
|
||||
dispatch(auth.updatedUser(args));
|
||||
dispatch(users.updated(args));
|
||||
});
|
||||
@ -170,8 +169,8 @@ const WSListener: React.FunctionComponent = () => {
|
||||
|
||||
dispatch(meta.listenedToWS());
|
||||
}, [hasListened]);
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export default WSListener;
|
@ -74,4 +74,13 @@ nav[role='menu'] {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.text-center-v {
|
||||
line-height: 225%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user