diff --git a/src/actions/types.js b/src/actions/types.js
index 1c4bf4d..3769a4c 100644
--- a/src/actions/types.js
+++ b/src/actions/types.js
@@ -90,7 +90,7 @@ export type Action =
minecraftname: string,
blockDm: boolean,
canvases: Object,
- channels: Array,
+ channels: Object,
blocked: Array,
userlvl: number,
}
diff --git a/src/components/ChannelContextMenu.jsx b/src/components/ChannelContextMenu.jsx
index d2e1b81..a12ea3e 100644
--- a/src/components/ChannelContextMenu.jsx
+++ b/src/components/ChannelContextMenu.jsx
@@ -4,7 +4,7 @@
*/
import React, {
- useRef, useEffect, useState, useLayoutEffect,
+ useRef, useEffect,
} from 'react';
import { connect } from 'react-redux';
@@ -23,7 +23,6 @@ const UserContextMenu = ({
close,
}) => {
const wrapperRef = useRef(null);
- const [channelArray, setChannelArray] = useState([]);
useEffect(() => {
const handleClickOutside = (event) => {
@@ -42,20 +41,6 @@ const UserContextMenu = ({
};
}, [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 (
✔✘ Mute
- {(channelArray[2] !== 0)
+ {(channels[cid][1] !== 0)
&& (
{
+ useLayoutEffect(() => {
if (show) {
+ if (channels[chatChannel]) {
+ const chType = (channels[chatChannel][1] === 1) ? 1 : 0;
+ setType(chType);
+ }
document.addEventListener('mousedown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside);
} else {
@@ -55,12 +59,10 @@ const ChannelDropDown = ({
}, [show]);
useEffect(() => {
- for (let i = 0; i < channels.length; i += 1) {
- if (channels[i][0] === chatChannel) {
- setChatChannelName(channels[i][1]);
- }
+ if (channels[chatChannel]) {
+ setChatChannelName(channels[chatChannel][0]);
}
- }, [chatChannel, channels]);
+ }, [chatChannel]);
return (
{
- channels.filter((ch) => {
- const chType = ch[2];
+ Object.keys(channels).filter((cid) => {
+ const chType = channels[cid][1];
if (type === 1 && chType === 1) {
return true;
}
@@ -113,24 +115,27 @@ const ChannelDropDown = ({
return true;
}
return false;
- }).map((ch) => (
-
setChannel(ch[0])}
- style={(ch[0] === chatChannel) ? {
- fontWeight: 'bold',
- fontSize: 17,
- } : null}
- className={
- `chn${
- (ch[0] === chatChannel) ? ' selected' : ''
- }${
- (chatRead[ch[0]] < ch[3]) ? ' unread' : ''
- }`
- }
- >
- {ch[1]}
-
- ))
+ }).map((cid) => {
+ const [name,, lastTs] = channels[cid];
+ console.log(`name ${name} lastTC ${lastTs} compare to ${chatRead[cid]}`);
+ return (
+
setChannel(cid)}
+ className={
+ `chn${
+ (cid === chatChannel) ? ' selected' : ''
+ }`
+ }
+ >
+ {
+ (chatRead[cid] < lastTs) ? (
+ ※
+ ) : null
+ }
+ {name}
+
+ );
+ })
}
diff --git a/src/components/Chat.jsx b/src/components/Chat.jsx
index 607c4dc..ad4c07e 100644
--- a/src/components/Chat.jsx
+++ b/src/components/Chat.jsx
@@ -114,16 +114,11 @@ const Chat = ({
* set channel to first available one
*/
useEffect(() => {
- let i = 0;
- while (i < channels.length) {
- // eslint-disable-next-line eqeqeq
- if (channels[i][0] == chatChannel) {
- break;
+ if (!channels[chatChannel]) {
+ const cids = Object.keys(channels);
+ if (cids.length) {
+ setChannel(cids[0]);
}
- i += 1;
- }
- if (i && i === channels.length) {
- setChannel(channels[0][0]);
}
}, [chatChannel, channels]);
diff --git a/src/core/ChatMessageBuffer.js b/src/core/ChatMessageBuffer.js
index 9e988ff..50fe2f8 100644
--- a/src/core/ChatMessageBuffer.js
+++ b/src/core/ChatMessageBuffer.js
@@ -4,9 +4,10 @@
*
* @flow
*/
+import Sequelize from 'sequelize';
import logger from './logger';
-import { RegUser, Message } from '../data/models';
+import { RegUser, Message, Channel } from '../data/models';
const MAX_BUFFER_TIME = 120000;
@@ -62,6 +63,13 @@ class ChatMessageBuffer {
uid,
message,
});
+ Channel.update({
+ lastMessage: Sequelize.literal('CURRENT_TIMESTAMP'),
+ }, {
+ where: {
+ id: cid,
+ },
+ });
const messages = this.buffer.get(cid);
if (messages) {
messages.push([
diff --git a/src/core/ChatProvider.js b/src/core/ChatProvider.js
index 52a303d..1f26e98 100644
--- a/src/core/ChatProvider.js
+++ b/src/core/ChatProvider.js
@@ -10,8 +10,7 @@ import { CHAT_CHANNELS, EVENT_USER_NAME, INFO_USER_NAME } from './constants';
export class ChatProvider {
constructor() {
- this.defaultChannels = [];
- this.defaultChannelIds = [];
+ this.defaultChannels = {};
this.enChannelId = 0;
this.intChannelId = 0;
this.infoUserId = 1;
@@ -48,8 +47,6 @@ export class ChatProvider {
async initialize() {
// find or create default channels
- this.defaultChannels.length = 0;
- this.defaultChannelIds.length = 0;
for (let i = 0; i < CHAT_CHANNELS.length; i += 1) {
const { name } = CHAT_CHANNELS[i];
// eslint-disable-next-line no-await-in-loop
@@ -66,13 +63,11 @@ export class ChatProvider {
if (name === 'en') {
this.enChannelId = id;
}
- this.defaultChannels.push([
- id,
+ this.defaultChannels[id] = [
name,
type,
lastTs,
- ]);
- this.defaultChannelIds.push(id);
+ ];
}
// find or create default users
let name = INFO_USER_NAME;
@@ -106,7 +101,7 @@ export class ChatProvider {
}
userHasChannelAccess(user, cid, write = false) {
- if (this.defaultChannelIds.includes(cid)) {
+ if (this.defaultChannels[cid]) {
if (!write || user.regUser) {
return true;
}
diff --git a/src/core/me.js b/src/core/me.js
index 4f1dbf7..2a3ae05 100644
--- a/src/core/me.js
+++ b/src/core/me.js
@@ -31,10 +31,10 @@ export default async function getMe(user) {
delete userdata.mcVerified;
userdata.canvases = canvases;
- userdata.channels = [
+ userdata.channels = {
...chatProvider.defaultChannels,
...userdata.channels,
- ];
+ };
return userdata;
}
diff --git a/src/data/models/User.js b/src/data/models/User.js
index d482be1..8b6ee1b 100644
--- a/src/data/models/User.js
+++ b/src/data/models/User.js
@@ -28,7 +28,7 @@ class User {
// id should stay null if unregistered
this.id = id;
this.ip = ip;
- this.channels = [];
+ this.channels = {};
this.channelIds = [];
this.blocked = [];
this.ipSub = getIPv6Subnet(ip);
@@ -70,12 +70,11 @@ class User {
name = (dmu1.id === this.id) ? dmu2.name : dmu1.name;
}
this.channelIds.push(id);
- this.channels.push([
- id,
+ this.channels[id] = [
name,
type,
lastTs,
- ]);
+ ];
}
}
if (reguser.blocked) {
diff --git a/src/reducers/chat.js b/src/reducers/chat.js
index 19262db..abb7bb6 100644
--- a/src/reducers/chat.js
+++ b/src/reducers/chat.js
@@ -6,7 +6,21 @@ import type { Action } from '../actions/types';
export type ChatState = {
inputMessage: string,
- // [[cid, name, type, lastMessage], [cid2, name2, type2, lastMessage2],...]
+ /*
+ * {
+ * cid: [
+ * name,
+ * type,
+ * lastTs,
+ * ],
+ * cid2: [
+ * name,
+ * type,
+ * lastTs,
+ * ],
+ * ...
+ * }
+ */
channels: Array,
// [[uId, userName], [userId2, userName2],...]
blocked: Array,
@@ -16,7 +30,7 @@ export type ChatState = {
const initialState: ChatState = {
inputMessage: '',
- channels: [],
+ channels: {},
blocked: [],
messages: {},
};
@@ -56,22 +70,19 @@ export default function chat(
case 'ADD_CHAT_CHANNEL': {
const { channel } = action;
- const cid = channel[0];
- const channels = state.channels
- .filter((ch) => (ch[0] !== cid));
- channels.push(channel);
return {
...state,
- channels,
+ channels: {
+ ...state.channels,
+ ...channel,
+ },
};
}
case 'REMOVE_CHAT_CHANNEL': {
const { cid } = action;
- const channels = state.channels.filter(
- // eslint-disable-next-line eqeqeq
- (chan) => (chan[0] != cid),
- );
+ const channels = { ...state.channels };
+ delete channels[cid];
return {
...state,
channels,
@@ -106,7 +117,7 @@ export default function chat(
const {
name, text, country, channel, user,
} = action;
- if (!state.messages[channel]) {
+ if (!state.messages[channel] || !state.channels[channel]) {
return state;
}
const messages = {
@@ -119,8 +130,19 @@ export default function chat(
if (messages[channel].length > MAX_CHAT_MESSAGES) {
messages[channel].shift();
}
+
+ /*
+ * update timestamp of last message
+ */
+ const channelArray = [...state.channels[channel]];
+ channelArray[2] = Date.now();
+
return {
...state,
+ channels: {
+ ...state.channels,
+ [channel]: channelArray,
+ },
messages,
};
}
diff --git a/src/reducers/gui.js b/src/reducers/gui.js
index cf80355..7294793 100644
--- a/src/reducers/gui.js
+++ b/src/reducers/gui.js
@@ -109,9 +109,15 @@ export default function gui(
}
case 'SET_CHAT_CHANNEL': {
+ const { cid } = action;
+
return {
...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': {
let { pixelsPlaced } = state;
pixelsPlaced += 1;
diff --git a/src/routes/api/startdm.js b/src/routes/api/startdm.js
index 7ddb2e8..a053b68 100644
--- a/src/routes/api/startdm.js
+++ b/src/routes/api/startdm.js
@@ -105,7 +105,6 @@ async function startDm(req: Request, res: Response) {
raw: true,
});
const ChannelId = channel[0].id;
- const { lastMessage } = channel[0];
const promises = [
UserChannel.findOrCreate({
@@ -127,12 +126,13 @@ async function startDm(req: Request, res: Response) {
// TODO: inform websocket to add channelId to user
res.json({
- channel: [
- ChannelId,
- userName,
- 1,
- lastMessage,
- ],
+ channel: {
+ [ChannelId]: [
+ userName,
+ 1,
+ Date.now(),
+ ],
+ },
});
}
diff --git a/src/styles/default.css b/src/styles/default.css
index 1a29016..31aeaf1 100644
--- a/src/styles/default.css
+++ b/src/styles/default.css
@@ -485,6 +485,15 @@ tr:nth-child(even) {
cursor: pointer;
}
+.chn.selected, .chnunread {
+ font-weight: bold;
+ font-size: 17px;
+}
+
+.chnunread {
+ color: red;
+}
+
.usermessages {
font-size: 14px;
font-weight: 500;
diff --git a/src/web.js b/src/web.js
index 9baaec0..5b18aad 100644
--- a/src/web.js
+++ b/src/web.js
@@ -194,7 +194,7 @@ app.get('/', async (req, res) => {
// ip config
// -----------------------------------------------------------------------------
// use this if models changed:
-const promise = models.sync({ alter: { drop: true } })
+const promise = models.sync({ alter: { drop: false } })
// const promise = models.sync()
.catch((err) => logger.error(err.stack));
promise.then(() => {