subscribe to all available categories, instead of subscribing to them when you click on them if that makes sense

This commit is contained in:
hippoz 2021-02-09 20:09:36 +02:00
parent 57e72d24c7
commit d35198e88e
Signed by: hippoz
GPG key ID: 7C52899193467641
9 changed files with 83 additions and 81 deletions

View file

@ -3,9 +3,12 @@ import gatewayConnection from '../../Gateway/globalGatewayConnection';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
export default function CategoryButton({ category }) { export default function CategoryButton({ category, selected }) {
const history = useHistory(); const history = useHistory();
let buttonClasses = 'button category-button';
if (selected) buttonClasses += ' pressed';
const handleClick = () => { const handleClick = () => {
if (gatewayConnection.isConnected) { if (gatewayConnection.isConnected) {
history.push(`/categories/${category._id}`); history.push(`/categories/${category._id}`);
@ -13,7 +16,7 @@ export default function CategoryButton({ category }) {
}; };
return ( return (
<button className="button category-button" onClick={ handleClick }> <button className={ buttonClasses } onClick={ handleClick }>
<CategoryProfile category={ category } size="32" /> <CategoryProfile category={ category } size="32" />
</button> </button>
); );

View file

@ -3,13 +3,14 @@ import CategoryButton from './CategoryButton';
import APIRequest from '../../APIRequest'; import APIRequest from '../../APIRequest';
import { couldNotReach } from '../../Errors'; import { couldNotReach } from '../../Errors';
import { useDispatch } from 'react-redux' import { connect, useDispatch } from 'react-redux'
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import Logger from '../../Logger'; import Logger from '../../Logger';
import gatewayConnection from '../../Gateway/globalGatewayConnection';
const { log: loaderLog } = Logger([ 'CategoryList', 'Loader' ]); const { log: loaderLog } = Logger([ 'CategoryList', 'Loader' ]);
export default function CategoryList() { function CategoryList({ selectedCategoryId }) {
const [ categoryList, setCategoryList ] = useState(); const [ categoryList, setCategoryList ] = useState();
const [ error, setError ] = useState(); const [ error, setError ] = useState();
@ -24,6 +25,9 @@ export default function CategoryList() {
loaderLog('Got category list from server, dispatching...'); loaderLog('Got category list from server, dispatching...');
setCategoryList(json.categories || []); setCategoryList(json.categories || []);
dispatch({ type: 'categories/updatecategorylist', categories: json.categories }); dispatch({ type: 'categories/updatecategorylist', categories: json.categories });
loaderLog('Subscribing to all categories...');
// TODO: IMPORTANT: Subscribing to a lot of channels puts strain on the server
gatewayConnection.subscribeToCategoryChats(json.categories.map(category => category._id));
}) })
.catch(() => { .catch(() => {
setError(true); setError(true);
@ -49,10 +53,16 @@ export default function CategoryList() {
} else { } else {
return ( return (
<div className="category-list"> <div className="category-list">
{categoryList.map((category) => { categoryList.map((category) => ( <CategoryButton key={ category._id } category={ category } selected={ (category._id === selectedCategoryId) } /> )) }
<CategoryButton key={ category._id } category={ category } />
)}
</div> </div>
); );
} }
} }
const stateToProps = (state) => {
return {
selectedCategoryId: state?.selectedCategoryId
};
};
export default connect(stateToProps)(CategoryList);

View file

@ -1,24 +1,7 @@
import defaultProfile from '../../Images/defaultprofile_256px-256px.png' import ProfileLink from '../UI/ProfileLink'
export default function CategoryProfile({ category, size }) { export default function CategoryProfile({ category, size }) {
let picture;
if (!size) size = 32;
// TODO: Make a debug error message for then the size does not exist
const pictureClass = `profile-picture profile-picture-${size}`;
if (category.picture) {
// Not actually implemented on the server and can be unsafe, this is just futureproofing
picture = <img className={ pictureClass } src={ category.picture } alt="Profile"></img>
} else {
picture = <img className={ pictureClass } src={ defaultProfile } alt="Profile"></img>
}
return ( return (
<div className="profile-link"> <ProfileLink object={ category } size={ size } type="category" />
{ picture } );
<span className="profile-username">{ category.title }</span>
</div>
)
} }

View file

@ -7,7 +7,7 @@ import { useParams } from 'react-router-dom';
import { connect, useDispatch } from 'react-redux'; import { connect, useDispatch } from 'react-redux';
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
function CategoryView({ categories, messages, isHandlingRoute=true, subscribedToCategories, gateway: { isConnected: isGatewayConnected } }) { function CategoryView({ categories, messages }) {
const { id } = useParams(); const { id } = useParams();
const [ textInput, setTextInput ] = useState(''); const [ textInput, setTextInput ] = useState('');
const [ category, setCategory ] = useState(); const [ category, setCategory ] = useState();
@ -32,29 +32,24 @@ function CategoryView({ categories, messages, isHandlingRoute=true, subscribedTo
} }
} }
useEffect(() => {
if (isHandlingRoute) {
if (!id || id === '') return;
if (!isGatewayConnected ) return;
if (subscribedToCategories.includes(id)) return;
dispatch({ type: 'categories/selectcategory', categoryId: id });
gatewayConnection.subscribeToCategoryChat(id);
}
}, [dispatch, id, isHandlingRoute, subscribedToCategories, isGatewayConnected]);
useEffect(() => { useEffect(() => {
if (!categories) return; if (!categories) return;
setCategory(categories.find(x => x._id === id)); setCategory(categories.find(x => x._id === id));
}, [ categories, id ]); }, [ categories, id ]);
useEffect(() => {
if (!category) return;
dispatch({ type: 'categories/selectcategory', categoryId: category._id });
}, [ category, dispatch ]);
if (category) { if (category) {
let messagesView = messages.map(m => <Message key={ m._id } message={ m } />); let messagesView = messages.map(m => <Message key={ m._id } message={ m } />);
if (messagesView === undefined || messagesView.length <= 0) { if (messagesView === undefined || messagesView.length <= 0) {
messagesView = ( messagesView = (
<div class='no-messages-warning'> <div className='no-messages-warning'>
A bit empty in here... A bit empty in here...
</div> </div>
); );
@ -98,8 +93,6 @@ const stateToProps = (state, ownProps) => {
return { return {
categories: state?.categories, categories: state?.categories,
messages: state?.messages[categoryId] || [], messages: state?.messages[categoryId] || [],
subscribedToCategories: state?.subscribedToCategories || [],
gateway: state?.gateway
}; };
}; };

View file

@ -0,0 +1,30 @@
import defaultProfile from '../../Images/defaultprofile_256px-256px.png'
export default function ProfileLink({ object, size, type }) {
let picture;
if (!size) size = 32;
if (size !== 0) {
// TODO: Make a debug error message for then the size does not exist
const pictureClass = `profile-picture profile-picture-${size}`;
if (object.picture) {
// Not actually implemented on the server and can be unsafe, this is just futureproofing
picture = <img className={ pictureClass } src={ object.picture } alt="Profile"></img>
} else {
picture = <img className={ pictureClass } src={ defaultProfile } alt="Profile"></img>
}
} else {
picture = null;
}
const classes = type === 'user' ? 'profile-link user-profile-link' : 'profile-link';
const title = type === 'category' ? object.title : object.username;
return (
<div className={ classes }>
{ picture }
<span className="profile-username">{ title }</span>
</div>
)
}

View file

@ -1,27 +1,7 @@
import defaultProfile from '../../Images/defaultprofile_256px-256px.png' import ProfileLink from '../UI/ProfileLink'
export default function UserProfileLink({ user, size }) {
let picture;
if (!size) size = 32;
if (size !== 0) {
// TODO: Make a debug error message for then the size does not exist
const pictureClass = `profile-picture profile-picture-${size}`;
if (user.picture) {
// Not actually implemented on the server and can be unsafe, this is just futureproofing
picture = <img className={ pictureClass } src={ user.picture } alt="Profile"></img>
} else {
picture = <img className={ pictureClass } src={ defaultProfile } alt="Profile"></img>
}
} else {
picture = null;
}
export default function CategoryProfile({ user, size }) {
return ( return (
<div className="profile-link user-profile-link"> <ProfileLink object={ user } size={ size } type="user" />
{ picture } );
<span className="profile-username">{ user.username }</span>
</div>
)
} }

View file

@ -74,14 +74,12 @@ GatewayConnection.prototype.sendMessage = function(categoryId, content) {
}); });
}; };
GatewayConnection.prototype.subscribeToCategoryChat = function(categoryId) { GatewayConnection.prototype.subscribeToCategoryChats = function(categoryIds) {
if (!this.isConnected) return; if (!this.isConnected) return;
const request = [categoryId]; gatewayLog('Subscribing to channel(s)', categoryIds);
gatewayLog('Subscribing to channel(s)', request); this.socket.emit('subscribe', categoryIds);
this.socket.emit('subscribe', request);
}; };
export default GatewayConnection; export default GatewayConnection;

View file

@ -1,7 +1,7 @@
$nord0darker: #22262e; $nord0darker: #22262e;
$nord1darker: #282b36; $nord1darker: #282b36;
$nord2darker: #333947; $nord2darker: #333947;
$nord3darker: #40495a;
$nord4darker: #9da2ad; $nord4darker: #9da2ad;
$nord0: #2E3440; $nord0: #2E3440;
@ -32,6 +32,7 @@ $nord15: #B48EAD;
--accent-color-dark: #{$nord1darker}; --accent-color-dark: #{$nord1darker};
--accent-color-light: #{$nord2darker}; --accent-color-light: #{$nord2darker};
--accent-color-very-light: #{$nord3darker};
--category-top-bar-color: var(--accent-color-light); --category-top-bar-color: var(--accent-color-light);
--category-bottom-text-bar-color: var(--accent-color-light); --category-bottom-text-bar-color: var(--accent-color-light);
@ -171,11 +172,11 @@ body {
} }
&.pressed { &.pressed {
background-color: var(--accent-color-light); background-color: var(--accent-color-very-light);
} }
} }
.button:hover { .button:hover:not(.pressed) {
background-color: var(--accent-color-light); background-color: var(--accent-color-light);
} }
@ -274,6 +275,10 @@ body {
color: var(--darker-text-color); color: var(--darker-text-color);
} }
.unread-indicator {
float: right;
}
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
.button.category-button { .button.category-button {
min-width: 100px; min-width: 100px;

View file

@ -2,11 +2,14 @@ import { createStore } from 'redux';
const intitialState = { const intitialState = {
user: null, user: null,
categories: null, categories: null,
gateway: { isConnected: false }, gateway: { isConnected: false },
messages: {}, messages: {},
subscribedToCategories: [], categoryPresenceClientList: {},
categoryPresenceClientList: {}
selectedCategoryId: undefined
}; };
const reducer = (state = intitialState, payload) => { const reducer = (state = intitialState, payload) => {
@ -50,10 +53,7 @@ const reducer = (state = intitialState, payload) => {
case 'categories/selectcategory': { case 'categories/selectcategory': {
return { return {
...state, ...state,
subscribedToCategories: [ selectedCategoryId: payload.categoryId
...state.subscribedToCategories || [],
payload.categoryId
]
}; };
} }