parent
916c8ac3c8
commit
2e89be3b84
|
|
@ -32,8 +32,13 @@ const connect = async () => {
|
||||||
socket.addEventListener('message', (event) => {
|
socket.addEventListener('message', (event) => {
|
||||||
const {action, data} = JSON.parse(event.data);
|
const {action, data} = JSON.parse(event.data);
|
||||||
console.log('[IN]', action, data);
|
console.log('[IN]', action, data);
|
||||||
for(const router of routers) {
|
const routeFound = routers
|
||||||
router(action, data);
|
.map(router => router(action, data))
|
||||||
|
.reduce((a, b) => a + b, 0);
|
||||||
|
if(routeFound === 0) {
|
||||||
|
console.warn(`route <${action}> not found`);
|
||||||
|
} else {
|
||||||
|
console.log(`routed to ${routeFound} elements`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -63,21 +68,12 @@ export async function send(action: string, data?: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function router(routes: any) {
|
export function router(routes: any) {
|
||||||
for(const routeName in routes) {
|
|
||||||
const route = routes[routeName];
|
|
||||||
if(typeof route === 'object') {
|
|
||||||
for(const suffix in route) {
|
|
||||||
const combinedRouteName = routeName + ':' + suffix;
|
|
||||||
routes[combinedRouteName] = route[suffix];
|
|
||||||
}
|
|
||||||
delete routes[routeName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return function(route: string, data: any) {
|
return function(route: string, data: any) {
|
||||||
if(route in routes) {
|
if(route in routes) {
|
||||||
routes[route](data);
|
routes[route](data);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.warn(`route <${route}> not found`);
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { registerRouter, router, send, unregisterRouter } from './api';
|
||||||
|
|
||||||
|
export function useAPI(actions: Function | object, deps: any[]) {
|
||||||
|
const _router = typeof actions === 'object' ? router(actions) : actions;
|
||||||
|
useEffect(() => {
|
||||||
|
registerRouter(_router);
|
||||||
|
return () => {
|
||||||
|
unregisterRouter(_router);
|
||||||
|
};
|
||||||
|
}, deps);
|
||||||
|
|
||||||
|
return {
|
||||||
|
send: send,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,7 @@
|
||||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
|
|
||||||
import { channelContext } from './App';
|
import { channelContext } from './App';
|
||||||
|
import { useAPI } from '../lib/useRouter';
|
||||||
function useRouter(actions: Function | object, deps: any[]) {
|
import type { IMessage } from './Message';
|
||||||
const _router = typeof actions === 'object' ? router(actions) : actions;
|
|
||||||
useEffect(() => {
|
|
||||||
registerRouter(_router);
|
|
||||||
return () => {
|
|
||||||
unregisterRouter(_router);
|
|
||||||
};
|
|
||||||
}, deps);
|
|
||||||
|
|
||||||
return {
|
|
||||||
send: send,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IChannel {
|
interface IChannel {
|
||||||
uid: string;
|
uid: string;
|
||||||
|
|
@ -29,12 +16,19 @@ function Hashmark() {
|
||||||
}}>#</span>;
|
}}>#</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IUnreads {
|
||||||
|
[uid: string]: number
|
||||||
|
}
|
||||||
|
|
||||||
export default function Channels() {
|
export default function Channels() {
|
||||||
|
|
||||||
const [channels, setChannels] = useState<IChannel[]>([]);
|
const [channels, setChannels] = useState<IChannel[]>([]);
|
||||||
const {channel, setChannel} = useContext(channelContext);
|
const {channel, setChannel} = useContext(channelContext);
|
||||||
|
|
||||||
const { send } = useRouter({
|
const [unreads, setUnreads] = useState<IUnreads>({});
|
||||||
|
|
||||||
|
|
||||||
|
const { send } = useAPI({
|
||||||
'channels:list'(data: IChannel[]) {
|
'channels:list'(data: IChannel[]) {
|
||||||
// console.log(data)
|
// console.log(data)
|
||||||
setChannels(data);
|
setChannels(data);
|
||||||
|
|
@ -42,7 +36,18 @@ export default function Channels() {
|
||||||
'channel:add'(channel: IChannel) {
|
'channel:add'(channel: IChannel) {
|
||||||
setChannels([...channels, channel]);
|
setChannels([...channels, channel]);
|
||||||
},
|
},
|
||||||
}, [channels]);
|
'message:message'(message: IMessage) {
|
||||||
|
if(channel === message.channel) return;
|
||||||
|
setUnreads({
|
||||||
|
...unreads,
|
||||||
|
[message.channel]: (unreads[message.channel] ?? 0) + 1,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, [channels, unreads]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('unreads', unreads);
|
||||||
|
}, [unreads]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(channels.length === 0) {
|
if(channels.length === 0) {
|
||||||
|
|
@ -56,6 +61,14 @@ export default function Channels() {
|
||||||
setChannel(channels[0].uid);
|
setChannel(channels[0].uid);
|
||||||
}, [channel, channels]);
|
}, [channel, channels]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(!channel) return;
|
||||||
|
setUnreads({
|
||||||
|
...unreads,
|
||||||
|
[channel]: 0,
|
||||||
|
});
|
||||||
|
}, [channel]);
|
||||||
|
|
||||||
const textbox = useRef<HTMLInputElement>(null);
|
const textbox = useRef<HTMLInputElement>(null);
|
||||||
const add = useCallback(() => {
|
const add = useCallback(() => {
|
||||||
if(textbox.current === null) return;
|
if(textbox.current === null) return;
|
||||||
|
|
@ -75,8 +88,20 @@ export default function Channels() {
|
||||||
}} onClick={() => {
|
}} onClick={() => {
|
||||||
setChannel(c.uid);
|
setChannel(c.uid);
|
||||||
}}>
|
}}>
|
||||||
<Hashmark></Hashmark>{c.name}
|
<Hashmark></Hashmark>
|
||||||
<a style={{ color: 'rgba(0, 100, 200, 1)', marginLeft: '8px', fontSize: '10px' }} href="#" onClick={() => {}}>Delete</a>
|
{(c.uid in unreads) && (unreads[c.uid] > 0) && (
|
||||||
|
<span style={{ paddingRight: '8px' }}>({unreads[c.uid]})</span>
|
||||||
|
)}
|
||||||
|
<span style={{
|
||||||
|
fontWeight: (unreads[c.uid] ?? 0) > 0 ? 'bold' : '300',
|
||||||
|
}}>
|
||||||
|
{c.name}
|
||||||
|
</span>
|
||||||
|
<a style={{
|
||||||
|
color: 'rgba(0, 100, 200, 1)',
|
||||||
|
marginLeft: '8px',
|
||||||
|
fontSize: '10px',
|
||||||
|
}} href="#" onClick={() => {}}>Delete</a>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Hashmark></Hashmark><input
|
<Hashmark></Hashmark><input
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import TimeAgo from 'react-timeago';
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
|
import { useAPI } from '../lib/useRouter';
|
||||||
import { channelContext } from './App';
|
import { channelContext } from './App';
|
||||||
import type { IMessage} from './Message';
|
import type { IMessage} from './Message';
|
||||||
import { Message } from './Message';
|
import { Message } from './Message';
|
||||||
|
|
||||||
function createMessage(from: string, text: string, channel: string, t = 0): IMessage {
|
function createMessage(from: string, text: string,
|
||||||
|
channel: string, t = 0): IMessage {
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
from,
|
from,
|
||||||
|
|
@ -23,19 +23,15 @@ export default () => {
|
||||||
const textBoxRef = useRef<HTMLDivElement>(null);
|
const textBoxRef = useRef<HTMLDivElement>(null);
|
||||||
const { channel, setChannel } = useContext(channelContext);
|
const { channel, setChannel } = useContext(channelContext);
|
||||||
|
|
||||||
useEffect(() => {
|
const { send } = useAPI({
|
||||||
const actions = router({
|
'message:message'(data: IMessage) {
|
||||||
'message:message'(data: IMessage) {
|
if(data.channel !== channel) return;
|
||||||
setMessages([...messages, data]);
|
|
||||||
},
|
setMessages([...messages, data]);
|
||||||
'message:recent'(data: { messages: IMessage[] }) {
|
},
|
||||||
setMessages(data.messages);
|
'message:recent'(data: { messages: IMessage[] }) {
|
||||||
},
|
setMessages(data.messages);
|
||||||
});
|
},
|
||||||
registerRouter(actions);
|
|
||||||
return () => {
|
|
||||||
unregisterRouter(actions);
|
|
||||||
};
|
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -46,12 +42,18 @@ export default () => {
|
||||||
const sendMessage = useCallback(() => {
|
const sendMessage = useCallback(() => {
|
||||||
if(textBoxRef.current === null) return;
|
if(textBoxRef.current === null) return;
|
||||||
if(channel === null) return;
|
if(channel === null) return;
|
||||||
send('message:message', createMessage('Val', textBoxRef.current.innerText, channel));
|
send(
|
||||||
|
'message:message',
|
||||||
|
createMessage(
|
||||||
|
'Val',
|
||||||
|
textBoxRef.current.innerText,
|
||||||
|
channel,
|
||||||
|
),
|
||||||
|
);
|
||||||
textBoxRef.current.innerText = '';
|
textBoxRef.current.innerText = '';
|
||||||
}, [channel]);
|
}, [channel]);
|
||||||
|
|
||||||
const keyDown = useCallback((evt: any) => {
|
const keyDown = useCallback((evt: any) => {
|
||||||
console.log(evt);
|
|
||||||
if(evt.key === 'Enter') {
|
if(evt.key === 'Enter') {
|
||||||
sendMessage();
|
sendMessage();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ export default router({
|
||||||
messages: messages.map(v => ({
|
messages: messages.map(v => ({
|
||||||
from: v.from,
|
from: v.from,
|
||||||
uid: v.uid,
|
uid: v.uid,
|
||||||
timestamp: v.t_sent * 1000,
|
timestamp: v.t_sent,
|
||||||
text: v.text,
|
text: v.text,
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Reference in New Issue