good shet eh?

main
Valerie 2022-07-30 21:27:56 -04:00
parent 277d92d97a
commit fd180cca7a
16 changed files with 157 additions and 57 deletions

View File

@ -12,6 +12,8 @@ async function createWindow() {
}, },
}); });
browserWindow.setMenu(null);
/** /**
* If you install `show: true` then it can cause issues when trying to close the window. * If you install `show: true` then it can cause issues when trying to close the window.
* Use `show: false` and listener events `ready-to-show` to fix these issues. * Use `show: false` and listener events `ready-to-show` to fix these issues.

View File

@ -40,9 +40,9 @@ export default function Sidebar(props: {
useEffect(() => { useEffect(() => {
if(screenRef === null) return; if(screenRef === null) return;
screenRef.addEventListener('touchstart', pointerDown); screenRef.addEventListener('touchstart', pointerDown, { passive: true });
screenRef.addEventListener('touchend', pointerUp); screenRef.addEventListener('touchend', pointerUp, { passive: true });
screenRef.addEventListener('touchmove', pointerMove); screenRef.addEventListener('touchmove', pointerMove, { passive: true });
// screenRef.addEventListener('pointercancel', pointerUp); // screenRef.addEventListener('pointercancel', pointerUp);
return () => { return () => {
screenRef.removeEventListener('touchstart', pointerDown); screenRef.removeEventListener('touchstart', pointerDown);

View File

@ -0,0 +1,30 @@
import { useEffect, useState } from "react";
export default function useHover<T extends HTMLElement>(): [
(t: T) => void,
boolean
] {
const [value, setValue] = useState(false);
const [ref, setRef] = useState<T | null>(null);
const handleMouseOver = () => setValue(true);
const handleMouseOut = () => setValue(false);
useEffect(
() => {
const node = ref;
if (node) {
node.addEventListener("mouseover", handleMouseOver);
node.addEventListener("mouseout", handleMouseOut);
return () => {
node.removeEventListener("mouseover", handleMouseOver);
node.removeEventListener("mouseout", handleMouseOut);
};
}
},
[ref] // Recall only if ref changes
);
return [setRef, value];
}

View File

@ -8,7 +8,7 @@ export function connectApi(url: string) {
const connect = async () => { const connect = async () => {
try { try {
connectionAttempts ++; connectionAttempts ++;
console.log('connecting to', url); // console.log('connecting to', url);
socket = new WebSocket(url); socket = new WebSocket(url);
} catch (e) { } catch (e) {
if(destroy) return; if(destroy) return;
@ -30,14 +30,12 @@ export function connectApi(url: string) {
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.debug('[IN]', action, data);
const routeFound = routers const routeFound = routers
.map(router => router(action, data)) .map(router => router(action, data))
.reduce((a, b) => a + b, 0); .reduce((a, b) => a + b, 0);
if(routeFound === 0) { if(routeFound === 0) {
console.warn(`route <${action}> not found`); console.warn(`route <${action}> not found`);
} else {
console.log(`routed to ${routeFound} elements`);
} }
}); });

View File

@ -1,6 +1,6 @@
import * as preload from '#preload'; import * as preload from '#preload';
console.log('#preload', preload); // console.log('#preload', preload);
const functions: any = (function() { const functions: any = (function() {
const electron = !!preload.versions; const electron = !!preload.versions;
@ -29,7 +29,7 @@ const functions: any = (function() {
})(); })();
console.log('native functions loaded', functions); // console.log('native functions loaded', functions);
export const getClientId = functions.getClientId; export const getClientId = functions.getClientId;
export const setClientId = functions.setClientId; export const setClientId = functions.setClientId;

View File

@ -1,10 +1,12 @@
import { useContext, useEffect } from 'react'; import { useContext, useEffect } from 'react';
import { ServerConnectionContext } from '../components/ServerConnection'; import { ServerConnectionContext } from '../components/ServerConnection';
import useSessionToken from '../hooks/useSessionToken';
import { Router, router, RouterObject } from './api'; import { Router, router, RouterObject } from './api';
export function useApi(actions: Router | RouterObject, deps: any[]) { export function useApi(actions: Router | RouterObject, deps: any[]) {
const connection = useContext(ServerConnectionContext); const connection = useContext(ServerConnectionContext);
const _router = typeof actions === 'object' ? router(actions) : actions; const _router = typeof actions === 'object' ? router(actions) : actions;
const { sessionToken } = useSessionToken();
useEffect(() => { useEffect(() => {
connection.registerRouter(_router); connection.registerRouter(_router);
@ -14,6 +16,15 @@ export function useApi(actions: Router | RouterObject, deps: any[]) {
}, deps); }, deps);
return { return {
send: connection.send, send(action: string, data: Object = {}) {
if('sessionToken' in data) {
console.warn('sessionToken already present in action. this is deprecated.')
console.trace();
}
connection.send(action, {
...(data ?? {}),
sessionToken
});
}
}; };
} }

View File

@ -0,0 +1,68 @@
import { CgHashtag } from "react-icons/cg";
import useChannel from "../hooks/useChannel";
import useHover from "../hooks/useHover";
interface ChannelProps {
unread: number;
uid: string;
name: string;
}
export default function Channel(props: ChannelProps) {
const { channel, setChannel } = useChannel();
const { unread, uid, name } = props;
const [ref, hover] = useHover<HTMLDivElement>();
const selected = channel === uid;
return (
<div
style={{
margin: '2px 2px',
display: 'grid',
gridTemplateColumns: 'min-content 1fr',
color: selected ? 'cyan' : 'inherit',
cursor: 'pointer',
background: selected ? 'var(--current-line)' :
hover ? 'rgba(255, 255, 255, 0.1)' :
'inherit',
borderRadius: '8px',
// placeItems: 'left center',
// border: '1px solid white'
}}
onClick={() => {
setChannel(uid);
}}
ref={ref}
>
<CgHashtag color={
selected ? 'var(--foreground)' :
hover ? 'var(--comment)' :
'var(--current-line)'
} size={24} style={{
fontWeight: 'bold',
margin: '4px',
}}></CgHashtag>
<div style={{
lineHeight: '32px',
color: selected ? 'var(--foreground)' :
hover ? 'var(--comment)' :
'var(--current-line)'
}}>
{name.toLowerCase().replaceAll(' ', '-').trim()}
</div>
{/* {(unread > 0) && (
<span style={{ paddingRight: '8px' }}>({unread})</span>
)}
<span style={{
fontWeight: (unread ?? 0) > 0 ? 'bold' : '300',
}}>
{name}
</span>
<a style={{
color: 'rgba(0, 100, 200, 1)',
marginLeft: '8px',
fontSize: '10px',
}} href="#" onClick={() => {}}>Delete</a> */}
</div>
)
}

View File

@ -9,20 +9,13 @@ import useClientId from '../hooks/useClientId';
import useHomeServer from '../contexts/PersistentState/useHomeServerNative'; import useHomeServer from '../contexts/PersistentState/useHomeServerNative';
import Logout from '../components/Logout'; import Logout from '../components/Logout';
import { CgHashtag } from 'react-icons/cg'; import { CgHashtag } from 'react-icons/cg';
import Channel from './Channel';
interface IChannel { interface IChannel {
uid: string; uid: string;
name: string; name: string;
} }
function Hashmark() {
return <CgHashtag style={{
fontWeight: 'bold',
marginRight: '8px',
marginLeft: '8px',
}}>#</CgHashtag>;
}
interface IUnreads { interface IUnreads {
[uid: string]: number [uid: string]: number
} }
@ -81,7 +74,7 @@ export default function Channels() {
useEffect(() => { useEffect(() => {
if(clientId === null) return; if(clientId === null) return;
send('client:get', clientId); send('client:get', { clientId });
}, [clientId]); }, [clientId]);
const textbox = useRef<HTMLInputElement>(null); const textbox = useRef<HTMLInputElement>(null);
@ -99,30 +92,14 @@ export default function Channels() {
}}> }}>
<br></br> <br></br>
{channels.map(c => ( {channels.map(c => (
<div key={c.uid} style={{ <Channel
margin: '8px 0px', key={c.uid}
color: channel === c.uid ? 'cyan' : 'inherit', uid={c.uid}
cursor: 'pointer', unread={unreads[c.uid] ?? 0}
}} onClick={() => { name={c.name}
setChannel(c.uid); ></Channel>
}}>
<Hashmark></Hashmark>
{(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>
))} ))}
<Hashmark></Hashmark><input {/* <input
ref={textbox} ref={textbox}
style={{ style={{
background: '#343746', background: '#343746',
@ -146,7 +123,7 @@ export default function Channels() {
// lineHeight: '20px' // lineHeight: '20px'
}}>ADD</button> }}>ADD</button>
<NameTextbox></NameTextbox><br></br> <NameTextbox></NameTextbox><br></br>
<Logout></Logout><br></br> <Logout></Logout><br></br> */}
{/* <LoginQR></LoginQR> */} {/* <LoginQR></LoginQR> */}
{/* <Totp></Totp> */} {/* <Totp></Totp> */}
</div> </div>

View File

@ -44,7 +44,7 @@ export default () => {
}, [messages]); }, [messages]);
useEffect(() => { useEffect(() => {
send('message:recent', { channel, sessionToken }); send('message:recent', { channel });
}, [channel, sessionToken]); }, [channel, sessionToken]);
const sendMessage = useCallback(() => { const sendMessage = useCallback(() => {
@ -54,11 +54,11 @@ export default () => {
if(sessionToken === null) return; if(sessionToken === null) return;
send( send(
'message:message', 'message:message',
{ ...createMessage( createMessage(
clientId, clientId,
textBoxRef.current.innerText, textBoxRef.current.innerText,
channel, channel,
), sessionToken }, )
); );
textBoxRef.current.innerText = ''; textBoxRef.current.innerText = '';
}, [channel, sessionToken]); }, [channel, sessionToken]);
@ -143,9 +143,11 @@ export default () => {
cursor: 'pointer', cursor: 'pointer',
display: 'grid', display: 'grid',
placeItems: 'center center', placeItems: 'center center',
fontSize: '32px', paddingLeft: '4px',
// paddingTop: '2px',
boxSizing: 'border-box',
}}> }}>
<MdSend></MdSend> <MdSend size={24}></MdSend>
</div> </div>
</div> </div>
</div> </div>

View File

@ -15,8 +15,8 @@ export default function NameTextbox() {
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null); const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);
const { send } = useApi({ const { send } = useApi({
'client:get'(_name: string) { 'client:get'(data: any) {
setName(_name); setName(data.name);
}, },
}, [name, clientId]); }, [name, clientId]);
@ -33,7 +33,7 @@ export default function NameTextbox() {
useEffect(() => { useEffect(() => {
if(clientId === null) return; if(clientId === null) return;
if(inputElement === null) return; if(inputElement === null) return;
send('client:get', clientId); send('client:get', { clientId });
}, [inputElement, clientId]); }, [inputElement, clientId]);
return <input return <input

View File

@ -46,6 +46,12 @@ const migrationConnection = createConnection({
multipleStatements: true, multipleStatements: true,
}); });
function keepAlive() {
connection.ping();
migrationConnection.ping();
}
setInterval(keepAlive, 60000); // ping to DB every minute
const connected: Promise<null> = new Promise((res, rej) => { const connected: Promise<null> = new Promise((res, rej) => {
migrationConnection.connect((err) => { migrationConnection.connect((err) => {
if(err === null) { if(err === null) {

View File

@ -1 +1 @@
SELECT name FROM clients WHERE uid=? SELECT name, username, uid FROM clients WHERE uid=?

View File

@ -25,6 +25,9 @@ export function expose(router: Function, port: number) {
if(auth === null) return; if(auth === null) return;
data.$clientId = auth; data.$clientId = auth;
} }
if(typeof data !== 'object') {
throw new Error('action ' + action + ' payload not an object');
}
console.log('[IN]', action, data); console.log('[IN]', action, data);
const _return = await (router(action, data) as unknown as Promise<any>); const _return = await (router(action, data) as unknown as Promise<any>);
if(_return) { if(_return) {

View File

@ -16,10 +16,14 @@ export default router({
if(response === null) return; if(response === null) return;
return reply(response[0][0].uid); return reply(response[0][0].uid);
}, },
async 'get'(uid: string) { async 'get'(data: any) {
const response = await query(_get, uid); const response = await query(_get, data.clientId);
if(response === null) return; if(response === null) return;
return reply(response[0].name); return reply({
name: response[0].name,
clientId: response[0].uid,
username: response[0].username
});
}, },
async 'rename'(data: any) { async 'rename'(data: any) {
const { clientId, name } = data; const { clientId, name } = data;

View File

@ -15,7 +15,6 @@ export default router({
}); });
export async function validateSessionToken(token: string) { export async function validateSessionToken(token: string) {
console.log('ASDASDASDASD')
const res = await query(_get, token); const res = await query(_get, token);
if(res === null) return null; if(res === null) return null;
if(res.length === 1 && res[0].expires > Date.now()) if(res.length === 1 && res[0].expires > Date.now())