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.
* 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(() => {
if(screenRef === null) return;
screenRef.addEventListener('touchstart', pointerDown);
screenRef.addEventListener('touchend', pointerUp);
screenRef.addEventListener('touchmove', pointerMove);
screenRef.addEventListener('touchstart', pointerDown, { passive: true });
screenRef.addEventListener('touchend', pointerUp, { passive: true });
screenRef.addEventListener('touchmove', pointerMove, { passive: true });
// screenRef.addEventListener('pointercancel', pointerUp);
return () => {
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 () => {
try {
connectionAttempts ++;
console.log('connecting to', url);
// console.log('connecting to', url);
socket = new WebSocket(url);
} catch (e) {
if(destroy) return;
@ -30,14 +30,12 @@ export function connectApi(url: string) {
socket.addEventListener('message', (event) => {
const {action, data} = JSON.parse(event.data);
console.log('[IN]', action, data);
// console.debug('[IN]', action, data);
const routeFound = routers
.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`);
}
});

View File

@ -1,6 +1,6 @@
import * as preload from '#preload';
console.log('#preload', preload);
// console.log('#preload', preload);
const functions: any = (function() {
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 setClientId = functions.setClientId;

View File

@ -1,10 +1,12 @@
import { useContext, useEffect } from 'react';
import { ServerConnectionContext } from '../components/ServerConnection';
import useSessionToken from '../hooks/useSessionToken';
import { Router, router, RouterObject } from './api';
export function useApi(actions: Router | RouterObject, deps: any[]) {
const connection = useContext(ServerConnectionContext);
const _router = typeof actions === 'object' ? router(actions) : actions;
const { sessionToken } = useSessionToken();
useEffect(() => {
connection.registerRouter(_router);
@ -14,6 +16,15 @@ export function useApi(actions: Router | RouterObject, deps: any[]) {
}, deps);
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 Logout from '../components/Logout';
import { CgHashtag } from 'react-icons/cg';
import Channel from './Channel';
interface IChannel {
uid: string;
name: string;
}
function Hashmark() {
return <CgHashtag style={{
fontWeight: 'bold',
marginRight: '8px',
marginLeft: '8px',
}}>#</CgHashtag>;
}
interface IUnreads {
[uid: string]: number
}
@ -81,7 +74,7 @@ export default function Channels() {
useEffect(() => {
if(clientId === null) return;
send('client:get', clientId);
send('client:get', { clientId });
}, [clientId]);
const textbox = useRef<HTMLInputElement>(null);
@ -99,30 +92,14 @@ export default function Channels() {
}}>
<br></br>
{channels.map(c => (
<div key={c.uid} style={{
margin: '8px 0px',
color: channel === c.uid ? 'cyan' : 'inherit',
cursor: 'pointer',
}} onClick={() => {
setChannel(c.uid);
}}>
<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>
<Channel
key={c.uid}
uid={c.uid}
unread={unreads[c.uid] ?? 0}
name={c.name}
></Channel>
))}
<Hashmark></Hashmark><input
{/* <input
ref={textbox}
style={{
background: '#343746',
@ -146,7 +123,7 @@ export default function Channels() {
// lineHeight: '20px'
}}>ADD</button>
<NameTextbox></NameTextbox><br></br>
<Logout></Logout><br></br>
<Logout></Logout><br></br> */}
{/* <LoginQR></LoginQR> */}
{/* <Totp></Totp> */}
</div>

View File

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

View File

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

View File

@ -1,4 +1,4 @@
DB_HOST=localhost
DB_USER=corner
DB_PASSWORD=corner
DB_DB=corner
DB_DB=corner

View File

@ -46,6 +46,12 @@ const migrationConnection = createConnection({
multipleStatements: true,
});
function keepAlive() {
connection.ping();
migrationConnection.ping();
}
setInterval(keepAlive, 60000); // ping to DB every minute
const connected: Promise<null> = new Promise((res, rej) => {
migrationConnection.connect((err) => {
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;
data.$clientId = auth;
}
if(typeof data !== 'object') {
throw new Error('action ' + action + ' payload not an object');
}
console.log('[IN]', action, data);
const _return = await (router(action, data) as unknown as Promise<any>);
if(_return) {

View File

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

View File

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