make channels from array into object

This commit is contained in:
HF 2020-11-26 19:17:00 +01:00
parent ab910619f8
commit 8f24a34a1d
13 changed files with 151 additions and 91 deletions

View File

@ -90,7 +90,7 @@ export type Action =
minecraftname: string, minecraftname: string,
blockDm: boolean, blockDm: boolean,
canvases: Object, canvases: Object,
channels: Array, channels: Object,
blocked: Array, blocked: Array,
userlvl: number, userlvl: number,
} }

View File

@ -4,7 +4,7 @@
*/ */
import React, { import React, {
useRef, useEffect, useState, useLayoutEffect, useRef, useEffect,
} from 'react'; } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -23,7 +23,6 @@ const UserContextMenu = ({
close, close,
}) => { }) => {
const wrapperRef = useRef(null); const wrapperRef = useRef(null);
const [channelArray, setChannelArray] = useState([]);
useEffect(() => { useEffect(() => {
const handleClickOutside = (event) => { const handleClickOutside = (event) => {
@ -42,20 +41,6 @@ const UserContextMenu = ({
}; };
}, [wrapperRef]); }, [wrapperRef]);
useLayoutEffect(() => {
for (let i = 0; i < channels.length; i += 1) {
const chan = channels[i];
/*
* [cid, name, type, lastMessage]
*/
// eslint-disable-next-line eqeqeq
if (chan[0] == cid) {
setChannelArray(chan);
break;
}
}
}, [channels.length]);
return ( return (
<div <div
ref={wrapperRef} ref={wrapperRef}
@ -68,7 +53,7 @@ const UserContextMenu = ({
<div> <div>
Mute Mute
</div> </div>
{(channelArray[2] !== 0) {(channels[cid][1] !== 0)
&& ( && (
<div <div
role="button" role="button"

View File

@ -5,7 +5,7 @@
*/ */
import React, { import React, {
useRef, useState, useEffect, useCallback, useRef, useState, useEffect, useCallback, useLayoutEffect,
} from 'react'; } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { MdChat } from 'react-icons/md'; import { MdChat } from 'react-icons/md';
@ -44,8 +44,12 @@ const ChannelDropDown = ({
} }
}, []); }, []);
useEffect(() => { useLayoutEffect(() => {
if (show) { if (show) {
if (channels[chatChannel]) {
const chType = (channels[chatChannel][1] === 1) ? 1 : 0;
setType(chType);
}
document.addEventListener('mousedown', handleClickOutside); document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside); document.addEventListener('touchstart', handleClickOutside);
} else { } else {
@ -55,12 +59,10 @@ const ChannelDropDown = ({
}, [show]); }, [show]);
useEffect(() => { useEffect(() => {
for (let i = 0; i < channels.length; i += 1) { if (channels[chatChannel]) {
if (channels[i][0] === chatChannel) { setChatChannelName(channels[chatChannel][0]);
setChatChannelName(channels[i][1]);
}
} }
}, [chatChannel, channels]); }, [chatChannel]);
return ( return (
<div <div
@ -104,8 +106,8 @@ const ChannelDropDown = ({
className="channeldds" className="channeldds"
> >
{ {
channels.filter((ch) => { Object.keys(channels).filter((cid) => {
const chType = ch[2]; const chType = channels[cid][1];
if (type === 1 && chType === 1) { if (type === 1 && chType === 1) {
return true; return true;
} }
@ -113,24 +115,27 @@ const ChannelDropDown = ({
return true; return true;
} }
return false; return false;
}).map((ch) => ( }).map((cid) => {
<div const [name,, lastTs] = channels[cid];
onClick={() => setChannel(ch[0])} console.log(`name ${name} lastTC ${lastTs} compare to ${chatRead[cid]}`);
style={(ch[0] === chatChannel) ? { return (
fontWeight: 'bold', <div
fontSize: 17, onClick={() => setChannel(cid)}
} : null} className={
className={ `chn${
`chn${ (cid === chatChannel) ? ' selected' : ''
(ch[0] === chatChannel) ? ' selected' : '' }`
}${ }
(chatRead[ch[0]] < ch[3]) ? ' unread' : '' >
}` {
} (chatRead[cid] < lastTs) ? (
> <span className="chnunread"></span>
{ch[1]} ) : null
</div> }
)) {name}
</div>
);
})
} }
</div> </div>
</div> </div>

View File

@ -114,16 +114,11 @@ const Chat = ({
* set channel to first available one * set channel to first available one
*/ */
useEffect(() => { useEffect(() => {
let i = 0; if (!channels[chatChannel]) {
while (i < channels.length) { const cids = Object.keys(channels);
// eslint-disable-next-line eqeqeq if (cids.length) {
if (channels[i][0] == chatChannel) { setChannel(cids[0]);
break;
} }
i += 1;
}
if (i && i === channels.length) {
setChannel(channels[0][0]);
} }
}, [chatChannel, channels]); }, [chatChannel, channels]);

View File

@ -4,9 +4,10 @@
* *
* @flow * @flow
*/ */
import Sequelize from 'sequelize';
import logger from './logger'; import logger from './logger';
import { RegUser, Message } from '../data/models'; import { RegUser, Message, Channel } from '../data/models';
const MAX_BUFFER_TIME = 120000; const MAX_BUFFER_TIME = 120000;
@ -62,6 +63,13 @@ class ChatMessageBuffer {
uid, uid,
message, message,
}); });
Channel.update({
lastMessage: Sequelize.literal('CURRENT_TIMESTAMP'),
}, {
where: {
id: cid,
},
});
const messages = this.buffer.get(cid); const messages = this.buffer.get(cid);
if (messages) { if (messages) {
messages.push([ messages.push([

View File

@ -10,8 +10,7 @@ import { CHAT_CHANNELS, EVENT_USER_NAME, INFO_USER_NAME } from './constants';
export class ChatProvider { export class ChatProvider {
constructor() { constructor() {
this.defaultChannels = []; this.defaultChannels = {};
this.defaultChannelIds = [];
this.enChannelId = 0; this.enChannelId = 0;
this.intChannelId = 0; this.intChannelId = 0;
this.infoUserId = 1; this.infoUserId = 1;
@ -48,8 +47,6 @@ export class ChatProvider {
async initialize() { async initialize() {
// find or create default channels // find or create default channels
this.defaultChannels.length = 0;
this.defaultChannelIds.length = 0;
for (let i = 0; i < CHAT_CHANNELS.length; i += 1) { for (let i = 0; i < CHAT_CHANNELS.length; i += 1) {
const { name } = CHAT_CHANNELS[i]; const { name } = CHAT_CHANNELS[i];
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
@ -66,13 +63,11 @@ export class ChatProvider {
if (name === 'en') { if (name === 'en') {
this.enChannelId = id; this.enChannelId = id;
} }
this.defaultChannels.push([ this.defaultChannels[id] = [
id,
name, name,
type, type,
lastTs, lastTs,
]); ];
this.defaultChannelIds.push(id);
} }
// find or create default users // find or create default users
let name = INFO_USER_NAME; let name = INFO_USER_NAME;
@ -106,7 +101,7 @@ export class ChatProvider {
} }
userHasChannelAccess(user, cid, write = false) { userHasChannelAccess(user, cid, write = false) {
if (this.defaultChannelIds.includes(cid)) { if (this.defaultChannels[cid]) {
if (!write || user.regUser) { if (!write || user.regUser) {
return true; return true;
} }

View File

@ -31,10 +31,10 @@ export default async function getMe(user) {
delete userdata.mcVerified; delete userdata.mcVerified;
userdata.canvases = canvases; userdata.canvases = canvases;
userdata.channels = [ userdata.channels = {
...chatProvider.defaultChannels, ...chatProvider.defaultChannels,
...userdata.channels, ...userdata.channels,
]; };
return userdata; return userdata;
} }

View File

@ -28,7 +28,7 @@ class User {
// id should stay null if unregistered // id should stay null if unregistered
this.id = id; this.id = id;
this.ip = ip; this.ip = ip;
this.channels = []; this.channels = {};
this.channelIds = []; this.channelIds = [];
this.blocked = []; this.blocked = [];
this.ipSub = getIPv6Subnet(ip); this.ipSub = getIPv6Subnet(ip);
@ -70,12 +70,11 @@ class User {
name = (dmu1.id === this.id) ? dmu2.name : dmu1.name; name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
} }
this.channelIds.push(id); this.channelIds.push(id);
this.channels.push([ this.channels[id] = [
id,
name, name,
type, type,
lastTs, lastTs,
]); ];
} }
} }
if (reguser.blocked) { if (reguser.blocked) {

View File

@ -6,7 +6,21 @@ import type { Action } from '../actions/types';
export type ChatState = { export type ChatState = {
inputMessage: string, inputMessage: string,
// [[cid, name, type, lastMessage], [cid2, name2, type2, lastMessage2],...] /*
* {
* cid: [
* name,
* type,
* lastTs,
* ],
* cid2: [
* name,
* type,
* lastTs,
* ],
* ...
* }
*/
channels: Array, channels: Array,
// [[uId, userName], [userId2, userName2],...] // [[uId, userName], [userId2, userName2],...]
blocked: Array, blocked: Array,
@ -16,7 +30,7 @@ export type ChatState = {
const initialState: ChatState = { const initialState: ChatState = {
inputMessage: '', inputMessage: '',
channels: [], channels: {},
blocked: [], blocked: [],
messages: {}, messages: {},
}; };
@ -56,22 +70,19 @@ export default function chat(
case 'ADD_CHAT_CHANNEL': { case 'ADD_CHAT_CHANNEL': {
const { channel } = action; const { channel } = action;
const cid = channel[0];
const channels = state.channels
.filter((ch) => (ch[0] !== cid));
channels.push(channel);
return { return {
...state, ...state,
channels, channels: {
...state.channels,
...channel,
},
}; };
} }
case 'REMOVE_CHAT_CHANNEL': { case 'REMOVE_CHAT_CHANNEL': {
const { cid } = action; const { cid } = action;
const channels = state.channels.filter( const channels = { ...state.channels };
// eslint-disable-next-line eqeqeq delete channels[cid];
(chan) => (chan[0] != cid),
);
return { return {
...state, ...state,
channels, channels,
@ -106,7 +117,7 @@ export default function chat(
const { const {
name, text, country, channel, user, name, text, country, channel, user,
} = action; } = action;
if (!state.messages[channel]) { if (!state.messages[channel] || !state.channels[channel]) {
return state; return state;
} }
const messages = { const messages = {
@ -119,8 +130,19 @@ export default function chat(
if (messages[channel].length > MAX_CHAT_MESSAGES) { if (messages[channel].length > MAX_CHAT_MESSAGES) {
messages[channel].shift(); messages[channel].shift();
} }
/*
* update timestamp of last message
*/
const channelArray = [...state.channels[channel]];
channelArray[2] = Date.now();
return { return {
...state, ...state,
channels: {
...state.channels,
[channel]: channelArray,
},
messages, messages,
}; };
} }

View File

@ -109,9 +109,15 @@ export default function gui(
} }
case 'SET_CHAT_CHANNEL': { case 'SET_CHAT_CHANNEL': {
const { cid } = action;
return { return {
...state, ...state,
chatChannel: action.cid, chatChannel: cid,
chatRead: {
...state.chatRead,
cid: Date.now(),
}
}; };
} }
@ -139,6 +145,42 @@ export default function gui(
}; };
} }
case 'RECEIVE_ME': {
const { channels } = action;
const cids = Object.keys(channels);
const chatRead = {...state.chatRead};
for (let i = 0; i < cids.length; i += 1) {
const cid = cids[i];
chatRead[cid] = 0;
}
return {
...state,
chatRead,
};
}
case 'ADD_CHAT_CHANNEL': {
const [cid] = Object.keys(action.channel);
return {
...state,
chatRead: {
...state.chatRead,
[cid]: 0,
},
};
}
case 'REMOVE_CHAT_CHANNEL': {
const { cid } = action;
const chatRead = { ...state.chatRead };
delete chatRead[cid];
return {
...state,
chatRead,
};
}
case 'PLACE_PIXEL': { case 'PLACE_PIXEL': {
let { pixelsPlaced } = state; let { pixelsPlaced } = state;
pixelsPlaced += 1; pixelsPlaced += 1;

View File

@ -105,7 +105,6 @@ async function startDm(req: Request, res: Response) {
raw: true, raw: true,
}); });
const ChannelId = channel[0].id; const ChannelId = channel[0].id;
const { lastMessage } = channel[0];
const promises = [ const promises = [
UserChannel.findOrCreate({ UserChannel.findOrCreate({
@ -127,12 +126,13 @@ async function startDm(req: Request, res: Response) {
// TODO: inform websocket to add channelId to user // TODO: inform websocket to add channelId to user
res.json({ res.json({
channel: [ channel: {
ChannelId, [ChannelId]: [
userName, userName,
1, 1,
lastMessage, Date.now(),
], ],
},
}); });
} }

View File

@ -485,6 +485,15 @@ tr:nth-child(even) {
cursor: pointer; cursor: pointer;
} }
.chn.selected, .chnunread {
font-weight: bold;
font-size: 17px;
}
.chnunread {
color: red;
}
.usermessages { .usermessages {
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;

View File

@ -194,7 +194,7 @@ app.get('/', async (req, res) => {
// ip config // ip config
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// use this if models changed: // use this if models changed:
const promise = models.sync({ alter: { drop: true } }) const promise = models.sync({ alter: { drop: false } })
// const promise = models.sync() // const promise = models.sync()
.catch((err) => logger.error(err.stack)); .catch((err) => logger.error(err.stack));
promise.then(() => { promise.then(() => {