channels and router fix
parent
c81f19e700
commit
a212169022
|
|
@ -2,20 +2,10 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import TimeAgo from 'react-timeago';
|
import TimeAgo from 'react-timeago';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
|
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
|
||||||
|
import type { IMessage} from './Message';
|
||||||
|
import { Message } from './Message';
|
||||||
|
|
||||||
const firstLineIndent = '10px';
|
function createMessage(from: string, text: string, t = 0): IMessage {
|
||||||
const multiLineIndent = '16px';
|
|
||||||
const rightMessagePagging = '16px';
|
|
||||||
|
|
||||||
interface Message {
|
|
||||||
text: string;
|
|
||||||
from: string;
|
|
||||||
timestamp: number;
|
|
||||||
// nid: number;
|
|
||||||
uid: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createMessage(from: string, text: string, t = 0): Message {
|
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
from,
|
from,
|
||||||
|
|
@ -24,33 +14,18 @@ function createMessage(from: string, text: string, t = 0): Message {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockMessages: Message[] = [
|
|
||||||
// createMessage('Bob', 'Hey', 55),
|
|
||||||
// createMessage('Alice', 'Hello', 50),
|
|
||||||
// createMessage('Bob', 'What up', 45),
|
|
||||||
// createMessage('Alice', 'nm UUU', 40),
|
|
||||||
// createMessage('Bob', 'Hey', 35),
|
|
||||||
// createMessage('Alice', 'Hello', 30),
|
|
||||||
// createMessage('Bob', 'What up', 25),
|
|
||||||
// createMessage('Alice', 'nm UUU', 20),
|
|
||||||
// createMessage('Bob', 'Hey', 15),
|
|
||||||
// createMessage('Alice', 'Hello', 10),
|
|
||||||
// createMessage('Bob', 'What up', 5),
|
|
||||||
// createMessage('Alice', 'This is what a really long message could possibly look like, if a person decided to write a really long essay. This is what a really long message could possibly look like, if a person decided to write a really long essay. This is what a really long message could possibly look like, if a person decided to write a really long essay. This is what a really long message could possibly look like, if a person decided to write a really long essay.'),
|
|
||||||
];
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
const [messages, setMessages] = useState<Message[]>(mockMessages);
|
const [messages, setMessages] = useState<IMessage[]>([]);
|
||||||
const [hist, setHist] = useState(false);
|
const [hist, setHist] = useState(false);
|
||||||
|
|
||||||
const textBoxRef = useRef<HTMLDivElement>(null);
|
const textBoxRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const actions = router({
|
const actions = router({
|
||||||
message(data: Message) {
|
message(data: IMessage) {
|
||||||
setMessages([...messages, data]);
|
setMessages([...messages, data]);
|
||||||
},
|
},
|
||||||
recent(data: { messages: Message[] }) {
|
'message:recent'(data: { messages: IMessage[] }) {
|
||||||
setMessages([...data.messages, ...messages]);
|
setMessages([...data.messages, ...messages]);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -63,14 +38,14 @@ export default () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!hist) {
|
if(!hist) {
|
||||||
console.log('sending recents request');
|
console.log('sending recents request');
|
||||||
send('recent');
|
send('message:recent');
|
||||||
setHist(true);
|
setHist(true);
|
||||||
}
|
}
|
||||||
}, [hist]);
|
}, [hist]);
|
||||||
|
|
||||||
const sendMessage = useCallback(() => {
|
const sendMessage = useCallback(() => {
|
||||||
if(textBoxRef.current === null) return;
|
if(textBoxRef.current === null) return;
|
||||||
send('message', createMessage('Val', textBoxRef.current.innerText));
|
send('message:message', createMessage('Val', textBoxRef.current.innerText));
|
||||||
textBoxRef.current.innerText = '';
|
textBoxRef.current.innerText = '';
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -105,46 +80,7 @@ export default () => {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}>
|
}}>
|
||||||
{messages.map(message => (
|
{messages.map(message => (
|
||||||
<div key={message.uid} style={{
|
<Message message={message}></Message>
|
||||||
display: 'grid',
|
|
||||||
gridTemplateColumns: '128px 1fr',
|
|
||||||
width: '100%',
|
|
||||||
padding: '1px 0px',
|
|
||||||
}}>
|
|
||||||
<span style={{
|
|
||||||
fontStyle: 'italic',
|
|
||||||
color: '#596793',
|
|
||||||
textAlign: 'right',
|
|
||||||
userSelect: 'none',
|
|
||||||
marginRight: '16px',
|
|
||||||
}}>
|
|
||||||
<TimeAgo
|
|
||||||
date={message.timestamp}
|
|
||||||
formatter={(t, u) => u === 'second' ? 'Just Now' : ('' + t + u[0])}
|
|
||||||
></TimeAgo>
|
|
||||||
</span>
|
|
||||||
<span style={{
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
fontWeight: 'bold',
|
|
||||||
float: 'left',
|
|
||||||
paddingRight: firstLineIndent,
|
|
||||||
// marginRight: '16px',
|
|
||||||
// height: '100%'
|
|
||||||
// borderBottom: '1px solid white'
|
|
||||||
}}>
|
|
||||||
{message.from}
|
|
||||||
</div>
|
|
||||||
<div style={{
|
|
||||||
marginRight: rightMessagePagging,
|
|
||||||
paddingLeft: multiLineIndent,
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
position: 'relative',
|
|
||||||
}}>
|
|
||||||
{message.text}
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import TimeAgo from 'react-timeago';
|
||||||
|
|
||||||
|
export interface IMessage {
|
||||||
|
uid: string;
|
||||||
|
timestamp: number;
|
||||||
|
from: string;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageProps {
|
||||||
|
message: IMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstLineIndent = '10px';
|
||||||
|
const multiLineIndent = '16px';
|
||||||
|
const rightMessagePagging = '16px';
|
||||||
|
|
||||||
|
export function Message({
|
||||||
|
message,
|
||||||
|
}: MessageProps) {
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div key={message.uid} style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '128px 1fr',
|
||||||
|
width: '100%',
|
||||||
|
padding: '1px 0px',
|
||||||
|
}}>
|
||||||
|
<span style={{
|
||||||
|
fontStyle: 'italic',
|
||||||
|
color: '#596793',
|
||||||
|
textAlign: 'right',
|
||||||
|
userSelect: 'none',
|
||||||
|
marginRight: '16px',
|
||||||
|
}}>
|
||||||
|
<TimeAgo
|
||||||
|
date={message.timestamp}
|
||||||
|
formatter={(t, u) => u === 'second' ? 'Just Now' : ('' + t + u[0])}
|
||||||
|
></TimeAgo>
|
||||||
|
</span>
|
||||||
|
<span style={{
|
||||||
|
}}>
|
||||||
|
<div style={{
|
||||||
|
fontWeight: 'bold',
|
||||||
|
float: 'left',
|
||||||
|
paddingRight: firstLineIndent,
|
||||||
|
// marginRight: '16px',
|
||||||
|
// height: '100%'
|
||||||
|
// borderBottom: '1px solid white'
|
||||||
|
}}>
|
||||||
|
{message.from}
|
||||||
|
</div>
|
||||||
|
<div style={{
|
||||||
|
marginRight: rightMessagePagging,
|
||||||
|
paddingLeft: multiLineIndent,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
position: 'relative',
|
||||||
|
}}>
|
||||||
|
{message.text}
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
CREATE TABLE `channels` (`id` int(11) NOT NULL AUTO_INCREMENT, `name` TINYTEXT NOT NULL, `uid` TINYTEXT NOT NULL UNIQUE, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
|
||||||
|
INSERT INTO channels (name, uid) VALUES ('General', UUID());
|
||||||
|
|
||||||
|
ALTER TABLE `messages` ADD COLUMN `channel_uid` TINYTEXT NOT NULL AFTER `t_sent`;
|
||||||
|
|
||||||
|
UPDATE messages SET channel_uid = (SELECT uid FROM channels) where messages.channel_uid = '';
|
||||||
|
|
||||||
|
ALTER TABLE `messages` CHANGE `channel_uid` `channel_uid` varchar(36) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `t_sent`;
|
||||||
|
|
||||||
|
ALTER TABLE `channels` CHANGE `uid` `uid` varchar(36) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `name`;
|
||||||
|
|
||||||
|
ALTER TABLE messages ADD CONSTRAINT fk_channel_uid FOREIGN KEY (channel_uid) REFERENCES channels(uid);
|
||||||
|
|
@ -96,7 +96,7 @@ export async function update() {
|
||||||
console.log('database up to date!');
|
console.log('database up to date!');
|
||||||
} else {
|
} else {
|
||||||
const difference = expectedVersion - currentVersion;
|
const difference = expectedVersion - currentVersion;
|
||||||
process.stdout.write(`database ${difference} version${difference !== 1 ? 's' : ''} behind`);
|
console.log(`database ${difference} version${difference !== 1 ? 's' : ''} behind`);
|
||||||
// console.log(`${currentVersion} >>> ${expectedVersion}`);
|
// console.log(`${currentVersion} >>> ${expectedVersion}`);
|
||||||
const neededMigrations = migrations.filter(m => m.version > currentVersion);
|
const neededMigrations = migrations.filter(m => m.version > currentVersion);
|
||||||
let completedMigrations = 0;
|
let completedMigrations = 0;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,20 @@ import { connection } from './migrate';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default async function(a: any, ...opts: any[]): Promise<any[]> {
|
export default async function(a: any, ...opts: any[]): Promise<any[] | null> {
|
||||||
const b64 = a.split('base64,')[1];
|
const b64 = a.split('base64,')[1];
|
||||||
const text = Buffer.from(b64, 'base64').toString();
|
const text = Buffer.from(b64, 'base64').toString();
|
||||||
|
try {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
connection.query(text, [...opts], (err, results) => {
|
connection.query(text, [...opts], (err, results) => {
|
||||||
if(!err) return resolve(results);
|
if(!err) return resolve(results);
|
||||||
console.error(err);
|
console.error(err.errno, err.sqlMessage);
|
||||||
|
console.error(err.sql);
|
||||||
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} catch(e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// console.log(...opts)
|
// console.log(...opts)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
SELECT (name, uid) FROM channels;
|
||||||
|
|
@ -17,7 +17,9 @@ wss.on('connection', (ws) => {
|
||||||
}
|
}
|
||||||
const {action, data} = message;
|
const {action, data} = message;
|
||||||
try {
|
try {
|
||||||
|
console.log(action, data);
|
||||||
const _return = await (router(action, data) as unknown as Promise<any>);
|
const _return = await (router(action, data) as unknown as Promise<any>);
|
||||||
|
console.log(_return);
|
||||||
if(_return) {
|
if(_return) {
|
||||||
ws.send(JSON.stringify(_return));
|
ws.send(JSON.stringify(_return));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,26 @@
|
||||||
export default function router(routes: any) {
|
export default function router(routes: any) {
|
||||||
|
|
||||||
for(const routeName in routes) {
|
for(const routeName in routes) {
|
||||||
const route = routes[routeName];
|
const route = routes[routeName];
|
||||||
if(typeof route === 'object') {
|
if('routes' in route) {
|
||||||
for(const suffix in route) {
|
for(const suffix of route.routes) {
|
||||||
const combinedRouteName = routeName + ':' + suffix;
|
const combinedRouteName = routeName + ':' + suffix;
|
||||||
routes[combinedRouteName] = route[suffix];
|
routes[combinedRouteName] = (_: never, ...args: any[]) => route(suffix, args);
|
||||||
}
|
}
|
||||||
delete routes[routeName];
|
delete routes[routeName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return function(route: any, data: any) {
|
|
||||||
|
const sendFn = function(route: any, data: any) {
|
||||||
|
console.log(routes);
|
||||||
if(route in routes) {
|
if(route in routes) {
|
||||||
return routes[route](data);
|
return routes[route](data);
|
||||||
} else {
|
} else {
|
||||||
console.warn(`route <${route}> not found`);
|
console.warn(`route <${route}> not found`);
|
||||||
|
console.trace();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
sendFn.routes = Object.keys(routes);
|
||||||
|
return sendFn;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { broadcast } from '..';
|
||||||
|
import query from '../db/query';
|
||||||
|
import router from '../router';
|
||||||
|
import newMessage from '../db/snippets/message/new.sql';
|
||||||
|
import recentMessages from '../db/snippets/message/recent.sql';
|
||||||
|
|
||||||
|
export default router({
|
||||||
|
async message(data: any) {
|
||||||
|
const failed = null === await query(newMessage, data.text, data.from, data.uid, data.timestamp);
|
||||||
|
if(failed) return;
|
||||||
|
broadcast('message:message', data);
|
||||||
|
},
|
||||||
|
async recent() {
|
||||||
|
console.log('got recents request');
|
||||||
|
const messages = await query(recentMessages);
|
||||||
|
if(messages === null) return;
|
||||||
|
return {
|
||||||
|
action: 'message:recent',
|
||||||
|
data: {
|
||||||
|
messages: messages.map(v => ({
|
||||||
|
from: v.from,
|
||||||
|
uid: v.uid,
|
||||||
|
timestamp: v.t_sent * 1000,
|
||||||
|
text: v.text,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -1,30 +1,9 @@
|
||||||
import router from '../router';
|
import router from '../router';
|
||||||
import { broadcast } from '../index';
|
import message from './message';
|
||||||
import query from '../db/query';
|
|
||||||
import newMessage from '../db/snippets/message/new.sql';
|
|
||||||
import recentMessages from '../db/snippets/message/recent.sql';
|
|
||||||
|
|
||||||
export default router({
|
export default router({
|
||||||
up() {
|
up() {
|
||||||
console.log(Date.now());
|
console.log(Date.now());
|
||||||
},
|
},
|
||||||
message(data: any) {
|
message: message,
|
||||||
query(newMessage, data.text, data.from, data.uid, data.timestamp);
|
|
||||||
broadcast('message', data);
|
|
||||||
},
|
|
||||||
async recent() {
|
|
||||||
console.log('got recents request');
|
|
||||||
const messages = await query(recentMessages);
|
|
||||||
return {
|
|
||||||
action: 'recent',
|
|
||||||
data: {
|
|
||||||
messages: messages.map(v => ({
|
|
||||||
from: v.from,
|
|
||||||
uid: v.uid,
|
|
||||||
timestamp: v.t_sent * 1000,
|
|
||||||
text: v.text,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
@ -66,7 +66,7 @@ const setupServerPackageWatcher = () => {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
|
const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
|
||||||
if (mayIgnore) return;
|
if (mayIgnore) return;
|
||||||
logger.error(data, {timestamp: true});
|
data.split('\n').forEach(d => logger.error(d, {timestamp: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Stops the watch script when the application has been quit */
|
/** Stops the watch script when the application has been quit */
|
||||||
|
|
|
||||||
Reference in New Issue