chore: more back

This commit is contained in:
cha0s 2020-12-08 07:07:06 -06:00
parent 9ffb5da0b5
commit 316b16670c
32 changed files with 471 additions and 59 deletions

View File

@ -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",

View File

@ -37,7 +37,7 @@ const App = () => {
/>
</Switch>
</Router>
{!isAnonymous && <Right />}
{<Right />}
</div>
)
};

View File

@ -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 = {};

View 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 = {};

View File

@ -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>
);

View 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 = {};

View 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%;
}

View 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 = {};

View 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 = {};

View 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 = {};

View 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,
};

View 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;
}

View File

@ -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>
);
}

View 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&apos;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 = {};

View 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;
}

View 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;

View 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);
}

View File

@ -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"