channels and router fix

cordova
Valerie 2022-07-21 15:22:00 -04:00
parent c81f19e700
commit a212169022
11 changed files with 149 additions and 108 deletions

View File

@ -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>

View File

@ -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>
</>
);
}

View File

@ -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);

View File

@ -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;

View File

@ -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();
return await new Promise((resolve, reject) => { try {
connection.query(text, [...opts], (err, results) => { return await new Promise((resolve, reject) => {
if(!err) return resolve(results); connection.query(text, [...opts], (err, results) => {
console.error(err); if(!err) return resolve(results);
console.error(err.errno, err.sqlMessage);
console.error(err.sql);
reject(err);
});
}); });
}); } catch(e) {
return null;
}
// console.log(...opts) // console.log(...opts)
} }

View File

@ -0,0 +1 @@
SELECT (name, uid) FROM channels;

View File

@ -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));
} }

View File

@ -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;
} }

View File

@ -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,
})),
},
};
},
});

View File

@ -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,
})),
},
};
},
}); });

View File

@ -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 */