good shet eh?
parent
277d92d97a
commit
fd180cca7a
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
}
|
||||||
|
|
@ -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`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_USER=corner
|
DB_USER=corner
|
||||||
DB_PASSWORD=corner
|
DB_PASSWORD=corner
|
||||||
DB_DB=corner
|
DB_DB=corner
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
SELECT name FROM clients WHERE uid=?
|
SELECT name, username, uid FROM clients WHERE uid=?
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
Reference in New Issue