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 { v4 } from 'uuid';
import { registerRouter, router, send, unregisterRouter } from '../lib/api';
import type { IMessage} from './Message';
import { Message } from './Message';
const firstLineIndent = '10px';
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 {
function createMessage(from: string, text: string, t = 0): IMessage {
return {
text,
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 () => {
const [messages, setMessages] = useState<Message[]>(mockMessages);
const [messages, setMessages] = useState<IMessage[]>([]);
const [hist, setHist] = useState(false);
const textBoxRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const actions = router({
message(data: Message) {
message(data: IMessage) {
setMessages([...messages, data]);
},
recent(data: { messages: Message[] }) {
'message:recent'(data: { messages: IMessage[] }) {
setMessages([...data.messages, ...messages]);
},
});
@ -63,14 +38,14 @@ export default () => {
useEffect(() => {
if(!hist) {
console.log('sending recents request');
send('recent');
send('message:recent');
setHist(true);
}
}, [hist]);
const sendMessage = useCallback(() => {
if(textBoxRef.current === null) return;
send('message', createMessage('Val', textBoxRef.current.innerText));
send('message:message', createMessage('Val', textBoxRef.current.innerText));
textBoxRef.current.innerText = '';
}, []);
@ -105,46 +80,7 @@ export default () => {
width: '100%',
}}>
{messages.map(message => (
<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>
<Message message={message}></Message>
))}
</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!');
} else {
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}`);
const neededMigrations = migrations.filter(m => m.version > currentVersion);
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 text = Buffer.from(b64, 'base64').toString();
return await new Promise((resolve, reject) => {
connection.query(text, [...opts], (err, results) => {
if(!err) return resolve(results);
console.error(err);
try {
return await new Promise((resolve, reject) => {
connection.query(text, [...opts], (err, results) => {
if(!err) return resolve(results);
console.error(err.errno, err.sqlMessage);
console.error(err.sql);
reject(err);
});
});
});
} catch(e) {
return null;
}
// 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;
try {
console.log(action, data);
const _return = await (router(action, data) as unknown as Promise<any>);
console.log(_return);
if(_return) {
ws.send(JSON.stringify(_return));
}

View File

@ -1,19 +1,26 @@
export default function router(routes: any) {
for(const routeName in routes) {
const route = routes[routeName];
if(typeof route === 'object') {
for(const suffix in route) {
if('routes' in route) {
for(const suffix of route.routes) {
const combinedRouteName = routeName + ':' + suffix;
routes[combinedRouteName] = route[suffix];
routes[combinedRouteName] = (_: never, ...args: any[]) => route(suffix, args);
}
delete routes[routeName];
}
}
return function(route: any, data: any) {
const sendFn = function(route: any, data: any) {
console.log(routes);
if(route in routes) {
return routes[route](data);
} else {
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 { broadcast } from '../index';
import query from '../db/query';
import newMessage from '../db/snippets/message/new.sql';
import recentMessages from '../db/snippets/message/recent.sql';
import message from './message';
export default router({
up() {
console.log(Date.now());
},
message(data: any) {
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,
})),
},
};
},
message: message,
});

View File

@ -66,7 +66,7 @@ const setupServerPackageWatcher = () => {
if (!data) return;
const mayIgnore = stderrFilterPatterns.some((r) => r.test(data));
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 */