chore: more back
This commit is contained in:
parent
9ffb5da0b5
commit
316b16670c
|
@ -33,6 +33,7 @@
|
|||
"deepmerge": "^4.2.2",
|
||||
"dotenv": "8.2.0",
|
||||
"history": "^4.7.2",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react": "^17.0.1",
|
||||
"react-hot-loader": "4.13.0",
|
||||
"react-redux": "^7.2.2",
|
||||
|
|
|
@ -37,7 +37,7 @@ const App = () => {
|
|||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
{!isAnonymous && <Right />}
|
||||
{<Right />}
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import FriendsActive from './active';
|
||||
import FriendsAdd from './add';
|
||||
import FriendsFavorites from './favorites';
|
||||
import FriendsPendingIncoming from './pending/incoming';
|
||||
import FriendsPendingOutgoing from './pending/outgoing';
|
||||
|
||||
export default function ChatLeftFriends() {
|
||||
return (
|
||||
<div
|
||||
className="friends"
|
||||
>
|
||||
<FriendsAdd />
|
||||
<FriendsActive />
|
||||
<FriendsFavorites />
|
||||
<FriendsPendingIncoming />
|
||||
<FriendsPendingOutgoing />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftFriends.propTypes = {};
|
25
app/src/react/components/left/friends/index.jsx
Normal file
25
app/src/react/components/left/friends/index.jsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Active from './active';
|
||||
import Add from './add';
|
||||
import Favorites from './favorites';
|
||||
import PendingIncoming from './pending/incoming';
|
||||
import PendingOutgoing from './pending/outgoing';
|
||||
|
||||
export default function ChatLeftFriends() {
|
||||
return (
|
||||
<div
|
||||
className="friends"
|
||||
>
|
||||
<Add />
|
||||
<Active />
|
||||
<Favorites />
|
||||
<PendingIncoming />
|
||||
<PendingOutgoing />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftFriends.propTypes = {};
|
|
@ -19,8 +19,9 @@ import {
|
|||
|
||||
import Bar from 'components/bar';
|
||||
import Branding from 'components/branding';
|
||||
import Friends from 'components/friends';
|
||||
// import ChatLeftRooms from './chat--leftRooms';
|
||||
|
||||
import Friends from './friends';
|
||||
import Rooms from './rooms';
|
||||
|
||||
export default function ChatLeft() {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -57,7 +58,7 @@ export default function ChatLeft() {
|
|||
isHorizontal
|
||||
onActive={(active) => dispatch(setLeftActiveIndex(active))}
|
||||
/>
|
||||
{/* {0 === active && <ChatLeftRooms />} */}
|
||||
{0 === active && <Rooms />}
|
||||
{1 === active && <Friends />}
|
||||
</div>
|
||||
);
|
||||
|
|
54
app/src/react/components/left/rooms/add/index.jsx
Normal file
54
app/src/react/components/left/rooms/add/index.jsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import './index.scss';
|
||||
|
||||
import {push} from 'connected-react-router';
|
||||
import React, {useRef, useState} from 'react';
|
||||
import {useDispatch} from 'react-redux';
|
||||
|
||||
import {validateChannel} from '@reddichat/core/client';
|
||||
|
||||
export default function ChatLeftRoomsAdd() {
|
||||
const dispatch = useDispatch();
|
||||
const [text, setText] = useState('');
|
||||
const $form = useRef(null);
|
||||
return (
|
||||
<div
|
||||
className="chat--leftRoomsAdd"
|
||||
>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
if (validateChannel({type: 'r', name: text})) {
|
||||
dispatch(push(`/chat/r/${text}`));
|
||||
}
|
||||
setText('');
|
||||
}}
|
||||
ref={$form}
|
||||
>
|
||||
<label className="channels__join">
|
||||
Start chatting at
|
||||
<span className="channels__joinTextWrapper">
|
||||
<input
|
||||
className="channels__joinText"
|
||||
onChange={(event) => {
|
||||
setText(event.target.value);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if ('Enter' === event.key && !event.shiftKey) {
|
||||
if ($form.current) {
|
||||
$form.current.dispatchEvent(new Event('submit', {cancelable: true}));
|
||||
}
|
||||
event.preventDefault();
|
||||
}
|
||||
}}
|
||||
placeholder="reddichat"
|
||||
type="text"
|
||||
value={text}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftRoomsAdd.propTypes = {};
|
30
app/src/react/components/left/rooms/add/index.scss
Normal file
30
app/src/react/components/left/rooms/add/index.scss
Normal file
|
@ -0,0 +1,30 @@
|
|||
@import 'scss/colors.scss';
|
||||
|
||||
.channels__join[class] {
|
||||
align-items: center;
|
||||
background-color: #272727;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.channels__joinTextWrapper {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.channels__joinTextWrapper::before {
|
||||
content: '/r/';
|
||||
font-size: 0.8em;
|
||||
padding: 0 0.25em 0 0.5em;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.channels__joinText {
|
||||
flex-grow: 1;
|
||||
padding-left: 0.25em;
|
||||
text-transform: lowercase;
|
||||
width: 0%;
|
||||
}
|
37
app/src/react/components/left/rooms/favorites/index.jsx
Normal file
37
app/src/react/components/left/rooms/favorites/index.jsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {
|
||||
favoriteChannelsSelector,
|
||||
submitRemoveFavorite,
|
||||
} from '@reddichat/user/client';
|
||||
|
||||
import Channels from 'components/channels';
|
||||
|
||||
export default function ChatLeftRoomsFavorites() {
|
||||
const dispatch = useDispatch();
|
||||
const favorites = useSelector(favoriteChannelsSelector);
|
||||
|
||||
const channels = favorites
|
||||
.map((channel) => ({
|
||||
name: channel,
|
||||
actions: [
|
||||
['💔', 'Unfavorite', () => dispatch(submitRemoveFavorite(`/r/${channel}`))],
|
||||
],
|
||||
}));
|
||||
return (
|
||||
<div
|
||||
className="chat--leftRoomsFavorites"
|
||||
>
|
||||
<Channels
|
||||
channels={channels}
|
||||
type="/r/"
|
||||
title="Favorites"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftRoomsFavorites.propTypes = {};
|
21
app/src/react/components/left/rooms/index.jsx
Normal file
21
app/src/react/components/left/rooms/index.jsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Add from './add';
|
||||
import Favorites from './favorites';
|
||||
import Recent from './recent';
|
||||
|
||||
export default function ChatLeftRooms() {
|
||||
return (
|
||||
<div
|
||||
className="chat--leftRooms"
|
||||
>
|
||||
<Add />
|
||||
<Favorites />
|
||||
<Recent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftRooms.propTypes = {};
|
0
app/src/react/components/left/rooms/index.scss
Normal file
0
app/src/react/components/left/rooms/index.scss
Normal file
41
app/src/react/components/left/rooms/recent/index.jsx
Normal file
41
app/src/react/components/left/rooms/recent/index.jsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {
|
||||
favoriteChannelsSelector,
|
||||
recentSelector,
|
||||
removeRecent,
|
||||
submitAddFavorite,
|
||||
} from '@reddichat/user/client';
|
||||
|
||||
import Channels from 'components/channels';
|
||||
|
||||
export default function ChatLeftRoomsRecent() {
|
||||
const dispatch = useDispatch();
|
||||
const favorites = useSelector(favoriteChannelsSelector);
|
||||
const recent = useSelector(recentSelector)
|
||||
.filter((channel) => -1 === favorites.indexOf(channel));
|
||||
const channels = recent
|
||||
.map((channel) => ({
|
||||
name: channel,
|
||||
actions: [
|
||||
['❤️', 'Favorite', () => dispatch(submitAddFavorite(`/r/${channel}`))],
|
||||
['×', 'Close', () => dispatch(removeRecent(channel))],
|
||||
],
|
||||
}));
|
||||
return (
|
||||
<div
|
||||
className="chat--leftRoomsRecent"
|
||||
>
|
||||
<Channels
|
||||
channels={channels}
|
||||
type="/r/"
|
||||
title="Recent"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatLeftRoomsRecent.propTypes = {};
|
44
app/src/react/components/right/blocked/index.jsx
Normal file
44
app/src/react/components/right/blocked/index.jsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import './index.scss';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {
|
||||
submitUnblock,
|
||||
usernamesSelector,
|
||||
} from '@reddichat/user/client';
|
||||
|
||||
import Channel from 'components/channel';
|
||||
|
||||
export default function ChatRightBlocked(props) {
|
||||
const {ids} = props;
|
||||
const dispatch = useDispatch();
|
||||
const usernames = useSelector(usernamesSelector);
|
||||
return (
|
||||
<div
|
||||
className="chat--rightBlocked"
|
||||
>
|
||||
<h2 className="blocked__chatsTitle">Blocked</h2>
|
||||
<ul className="blocked__chatsList">
|
||||
{ids.map((id) => {
|
||||
const blockedActions = [
|
||||
['😶', 'Unblock', () => dispatch(submitUnblock(id))],
|
||||
];
|
||||
return (
|
||||
<Channel
|
||||
key={id}
|
||||
actions={blockedActions}
|
||||
name={usernames[id] || '?'}
|
||||
type="/u/"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatRightBlocked.propTypes = {
|
||||
ids: PropTypes.arrayOf(PropTypes.number).isRequired,
|
||||
};
|
10
app/src/react/components/right/blocked/index.scss
Normal file
10
app/src/react/components/right/blocked/index.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
@import 'scss/colors.scss';
|
||||
|
||||
.blocked__chatsTitle {
|
||||
color: $color-muted;
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
padding: 2.5em 1em 0.625em 1.375em;
|
||||
text-transform: uppercase;
|
||||
}
|
|
@ -2,41 +2,42 @@ import './index.scss';
|
|||
|
||||
import classnames from 'classnames';
|
||||
import React from 'react';
|
||||
// import {useDispatch, useSelector} from 'react-redux';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
// import {
|
||||
// rightActiveIndexSelector,
|
||||
// rightIsOpenSelector,
|
||||
// setRightActiveIndex,
|
||||
// } from '~/common/state/app';
|
||||
// import {channelUsersSelector} from '~/common/state/chat';
|
||||
// import {
|
||||
// blockedSelector,
|
||||
// } from '~/common/state/user';
|
||||
import {
|
||||
rightActiveIndexSelector,
|
||||
rightIsOpenSelector,
|
||||
setRightActiveIndex,
|
||||
} from '@reddichat/app/client';
|
||||
import {channelUsersSelector} from '@reddichat/chat/client';
|
||||
import {
|
||||
blockedSelector,
|
||||
} from '@reddichat/user/client';
|
||||
|
||||
// import useChannel from '~/client/hooks/useChannel';
|
||||
import useChannel from 'hooks/useChannel';
|
||||
|
||||
// import Bar from './bar';
|
||||
// import ChatRightBlocked from './chat--rightBlocked';
|
||||
// import ChatRightUsers from './chat--rightUsers';
|
||||
import Bar from 'components/bar';
|
||||
|
||||
import Blocked from './blocked';
|
||||
import Users from './users';
|
||||
|
||||
export default function ChatRight() {
|
||||
// const dispatch = useDispatch();
|
||||
// const activeIndex = useSelector(rightActiveIndexSelector);
|
||||
// const channel = useChannel();
|
||||
// const blockedIds = useSelector(blockedSelector);
|
||||
// const channelUsers = useSelector((state) => channelUsersSelector(state, channel));
|
||||
// const isOpen = useSelector(rightIsOpenSelector);
|
||||
const showsAsOpen = true;
|
||||
// const rightButtons = []
|
||||
// .concat(channelUsers.length > 0 ? [{icon: '🙃', label: 'Present'}] : [])
|
||||
// .concat(blockedIds.length > 0 ? [{icon: '☢️', label: 'Blocked'}] : []);
|
||||
const dispatch = useDispatch();
|
||||
const activeIndex = useSelector(rightActiveIndexSelector);
|
||||
const channel = useChannel();
|
||||
const blockedIds = useSelector(blockedSelector);
|
||||
const channelUsers = useSelector((state) => channelUsersSelector(state, channel));
|
||||
const isOpen = useSelector(rightIsOpenSelector);
|
||||
const showsAsOpen = isOpen;
|
||||
const rightButtons = []
|
||||
.concat(channelUsers.length > 0 ? [{icon: '🙃', label: 'Present'}] : [])
|
||||
.concat(blockedIds.length > 0 ? [{icon: '☢️', label: 'Blocked'}] : []);
|
||||
return (
|
||||
<div
|
||||
className={classnames('right', 'flexed', showsAsOpen ? 'open' : 'closed')}
|
||||
>
|
||||
{/* <Bar
|
||||
<Bar
|
||||
active={activeIndex}
|
||||
buttons={rightButtons}
|
||||
className="bar--right"
|
||||
|
@ -46,10 +47,10 @@ export default function ChatRight() {
|
|||
<div className="right__links">
|
||||
<Link className="right__link" to="/about">About</Link>
|
||||
<span className="right__linksBullet">•</span>
|
||||
<Link className="right__link" to="/logout">Log out</Link>
|
||||
<Link className="right__link" to="/auth/logout">Log out</Link>
|
||||
</div>
|
||||
{0 === activeIndex && channelUsers.length > 0 && <ChatRightUsers />}
|
||||
{1 === activeIndex && blockedIds.length > 0 && <ChatRightBlocked ids={blockedIds} />} */}
|
||||
{0 === activeIndex && channelUsers.length > 0 && <Users />}
|
||||
{1 === activeIndex && blockedIds.length > 0 && <Blocked ids={blockedIds} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
121
app/src/react/components/right/users/index.jsx
Normal file
121
app/src/react/components/right/users/index.jsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import {useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import {channelIsAnonymous} from '@reddichat/core/client';
|
||||
import {channelUsersSelector} from '@reddichat/chat/client';
|
||||
import {
|
||||
blockedSelector,
|
||||
favoriteUsersSelector,
|
||||
friendshipIdIndex,
|
||||
friendshipSelector,
|
||||
idSelector,
|
||||
redditUsernameSelector,
|
||||
submitAddFavorite,
|
||||
submitAddFriend,
|
||||
submitBlock,
|
||||
submitConfirmFriend,
|
||||
submitRemoveFavorite,
|
||||
submitRemoveFriend,
|
||||
submitUnblock,
|
||||
usernamesSelector,
|
||||
} from '@reddichat/user/client';
|
||||
|
||||
import useChannel from 'hooks/useChannel';
|
||||
|
||||
import Channel from 'components/channel';
|
||||
|
||||
export default function ChatRightUsers() {
|
||||
const channel = useChannel();
|
||||
const dispatch = useDispatch();
|
||||
const blocked = useSelector(blockedSelector);
|
||||
const favoriteUsers = useSelector(favoriteUsersSelector);
|
||||
const friendship = useSelector(friendshipSelector);
|
||||
const userId = useSelector(idSelector);
|
||||
const ids = useSelector((state) => channelUsersSelector(state, channel));
|
||||
const redditUsername = useSelector(redditUsernameSelector);
|
||||
const usernames = useSelector(usernamesSelector);
|
||||
const list = ids
|
||||
.map((id) => [id, usernames[id]])
|
||||
.sort((l, r) => (l[1] < r[1] ? -1 : 1));
|
||||
return (
|
||||
<div
|
||||
className="chat--rightUsers"
|
||||
>
|
||||
<h2 className="users__chatsTitle">Who's here</h2>
|
||||
<ul className="users__chatsList">
|
||||
{list.map(([id, username]) => {
|
||||
const hasBlocked = -1 !== blocked.indexOf(id);
|
||||
const isAnonymous = channelIsAnonymous(channel);
|
||||
const isSelf = redditUsername === username;
|
||||
const index = friendshipIdIndex(friendship, id);
|
||||
const hasFriendship = -1 !== index;
|
||||
const userActions = [];
|
||||
if (!isAnonymous && !isSelf) {
|
||||
if (hasFriendship) {
|
||||
if ('active' === friendship[index].status) {
|
||||
if (-1 === favoriteUsers.indexOf(username)) {
|
||||
userActions.push([
|
||||
'🚫',
|
||||
'Unfriend',
|
||||
() => dispatch(submitRemoveFriend(id)),
|
||||
]);
|
||||
userActions.push([
|
||||
'❤️',
|
||||
'Favorite',
|
||||
() => dispatch(submitAddFavorite(`/u/${username}`)),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
userActions.push([
|
||||
'💔',
|
||||
'Unfavorite',
|
||||
() => dispatch(submitRemoveFavorite(`/u/${username}`)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
else if (friendship[index].adderId !== userId) {
|
||||
userActions.push([
|
||||
'👎',
|
||||
'Deny friend request',
|
||||
() => dispatch(submitRemoveFriend(id)),
|
||||
]);
|
||||
const {adderId} = friendship[index];
|
||||
userActions.push([
|
||||
'👍',
|
||||
'Confirm friend request',
|
||||
() => dispatch(submitConfirmFriend(adderId)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
else if (!hasBlocked) {
|
||||
userActions.push(['➕', 'Add friend', () => dispatch(submitAddFriend(username))]);
|
||||
}
|
||||
if (hasBlocked) {
|
||||
userActions.push(['😶', 'Unblock', () => dispatch(submitUnblock(id))]);
|
||||
}
|
||||
else if (-1 === favoriteUsers.indexOf(username)) {
|
||||
userActions.push(['☢️', 'Block', () => dispatch(submitBlock(id))]);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Channel
|
||||
key={id}
|
||||
actions={userActions}
|
||||
href={(
|
||||
(hasFriendship && userId !== id)
|
||||
? `/chat/u/${username}`
|
||||
: null
|
||||
)}
|
||||
name={username || '?'}
|
||||
type="/u/"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
ChatRightUsers.propTypes = {};
|
11
app/src/react/components/right/users/index.scss
Normal file
11
app/src/react/components/right/users/index.scss
Normal file
|
@ -0,0 +1,11 @@
|
|||
@import 'scss/colors.scss';
|
||||
|
||||
.users__chatsTitle {
|
||||
color: $color-muted;
|
||||
font-family: var(--thick-title-font-family);
|
||||
font-size: 0.7em;
|
||||
font-weight: bold;
|
||||
padding: 2.5em 1em 0.625em 1.375em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
32
app/src/react/hooks/useBreakpoints.js
Normal file
32
app/src/react/hooks/useBreakpoints.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import {useState, useEffect} from 'react';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
const points = {
|
||||
nano: 0,
|
||||
tiny: 320,
|
||||
tablet: 720,
|
||||
desktop: 1280,
|
||||
hd: 1920,
|
||||
uhd: 3840,
|
||||
};
|
||||
|
||||
const satisfiedPoints = (width) => {
|
||||
const keys = Object.keys(points);
|
||||
const satisfied = {};
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
satisfied[key] = width >= points[key];
|
||||
}
|
||||
return satisfied;
|
||||
};
|
||||
|
||||
const useBreakpoints = () => {
|
||||
const [satisfied, setSatisfied] = useState(() => satisfiedPoints(window.innerWidth));
|
||||
useEffect(() => {
|
||||
const onResize = throttle(() => setSatisfied(satisfiedPoints(window.innerWidth)), 100);
|
||||
window.addEventListener('resize', onResize);
|
||||
return () => window.removeEventListener('resize', onResize);
|
||||
}, []);
|
||||
return satisfied;
|
||||
};
|
||||
export default useBreakpoints;
|
8
app/src/react/hooks/useChannel.js
Normal file
8
app/src/react/hooks/useChannel.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import {useLocation} from 'react-router-dom';
|
||||
|
||||
import {parseChatChannel} from '@reddichat/core/client';
|
||||
|
||||
export default function useChannel() {
|
||||
const {pathname} = useLocation();
|
||||
return parseChatChannel(pathname);
|
||||
}
|
|
@ -968,8 +968,8 @@
|
|||
|
||||
"@latus/react@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://npm.i12e.cha0s.io/@latus%2freact/-/react-1.0.0.tgz#e68c8cd7015725c17a2aa18aebff52b39bbecca5"
|
||||
integrity sha512-ywXWx21xcJvVfp67Mj6rIaw4p9wepDDmRF0zzhyaJbhAu3weJ5oK9yd3IwWEYONtc/lzblak+3/fEaK+GFM01Q==
|
||||
resolved "https://npm.i12e.cha0s.io/@latus%2freact/-/react-1.0.0.tgz#965c0ca868f4fd83195e26f99bfd82d03ec8f361"
|
||||
integrity sha512-ffWESmTinN2vxfxkpznysF+uU1rWSbB3AtvS2XWLzPg/X3A5yRql4J2602SHIy/xXhLWzHueDZ5pzxDiGe0S8g==
|
||||
dependencies:
|
||||
"@neutrinojs/react" "^9.4.0"
|
||||
debug "4.3.1"
|
||||
|
|
Loading…
Reference in New Issue
Block a user