some more basic voice updates

main
Valerie 2022-08-08 03:15:29 -04:00
parent 46d3f00280
commit 1aead4cc6b
18 changed files with 174 additions and 73 deletions

View File

@ -1,4 +1,7 @@
import {BrowserWindow} from 'electron';
import {
BrowserWindow,
session
} from 'electron';
import {join} from 'path';
import {URL} from 'url';
@ -10,6 +13,7 @@ async function createWindow() {
webviewTag: false, // The webview tag is not recommended. Consider alternatives like iframe or Electron's BrowserView. https://www.electronjs.org/docs/latest/api/webview-tag#warning
preload: join(__dirname, '../../preload/dist/index.cjs'),
},
});
browserWindow.setMenu(null);
@ -40,6 +44,11 @@ async function createWindow() {
await browserWindow.loadURL(pageUrl);
// session.fromPartition('default').setPermissionRequestHandler((webContents, permission, callback) => {
// console.log('requested permission', permission);
// })
return browserWindow;
}

View File

@ -6,11 +6,27 @@ import {URL} from 'url';
*
* In development mode you need allow open `VITE_DEV_SERVER_URL`
*/
const ALLOWED_ORIGINS_AND_PERMISSIONS = new Map<string, Set<'clipboard-read' | 'media' | 'display-capture' | 'mediaKeySystem' | 'geolocation' | 'notifications' | 'midi' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' | 'unknown'>>(
type PermissionScope =
'clipboard-read'
| 'media'
| 'display-capture'
| 'mediaKeySystem'
| 'geolocation'
| 'notifications'
| 'midi'
| 'midiSysex'
| 'pointerLock'
| 'fullscreen'
| 'openExternal'
| 'unknown';
const ALLOWED_ORIGINS = new Set<string>(
import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL
? [[new URL(import.meta.env.VITE_DEV_SERVER_URL).origin, new Set]]
? [new URL(import.meta.env.VITE_DEV_SERVER_URL).origin]
: [],
);
const ALLOWED_PERMISSIONS: PermissionScope[] = [
'media'
]
/**
* List of origins that you allow open IN BROWSER.
@ -39,7 +55,7 @@ app.on('web-contents-created', (_, contents) => {
*/
contents.on('will-navigate', (event, url) => {
const {origin} = new URL(url);
if (ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
if (ALLOWED_ORIGINS.has(origin)) {
return;
}
@ -61,7 +77,7 @@ app.on('web-contents-created', (_, contents) => {
contents.session.setPermissionRequestHandler((webContents, permission, callback) => {
const {origin} = new URL(webContents.getURL());
const permissionGranted = !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission);
const permissionGranted = !!ALLOWED_PERMISSIONS.includes(permission)
callback(permissionGranted);
if (!permissionGranted && import.meta.env.DEV) {
@ -106,7 +122,7 @@ app.on('web-contents-created', (_, contents) => {
*/
contents.on('will-attach-webview', (event, webPreferences, params) => {
const {origin} = new URL(params.src);
if (!ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) {
if (!ALLOWED_ORIGINS.has(origin)) {
if (import.meta.env.DEV) {
console.warn(`A webview tried to attach ${params.src}, but was blocked.`);

View File

@ -3,6 +3,7 @@ import ServerConnection from "./components/ServerConnection";
import Sidebar from "./components/Sidebar";
import TwoPanel from "./components/TwoPanel";
import Voice from "./components/Voice";
import ClientsListState from "./contexts/EphemeralState/ClientsListState";
import { SettingsContext } from "./contexts/EphemeralState/EphemeralState";
import useHomeServer from "./contexts/PersistentState/useHomeServerNative";
import useChannel from "./hooks/useChannel";

View File

@ -1,4 +1,5 @@
import { createContext, PropsWithChildren, ReactNode, useEffect, useMemo } from "react";
import ClientsListState from "../contexts/EphemeralState/ClientsListState";
import { connectApi } from "../lib/api";
interface ServerConnectionProps {
@ -28,9 +29,11 @@ export default function ServerConnection(props: ServerConnectionProps) {
if(!serverConnection) return;
serverConnection.destroy();
}
}, [])
}, []);
return <ServerConnectionContext.Provider value={serverConnection}>
{props.children}
<ClientsListState>
{props.children}
</ClientsListState>
</ServerConnectionContext.Provider>
}

View File

@ -0,0 +1,47 @@
import { createContext, useEffect, useMemo, useState } from "react";
import { useApi } from "/@/lib/useApi";
export const ClientsListContext = createContext<{
clientName: { [clientId: string]: string }
}>({
clientName: {}
});
// export function useClientList() {
// const
// }
export default function ClientsListState(props: any) {
const [clients, setClients] = useState<{
[id: string]: string
}>({});
const { send } = useApi({
'clients:list'(data: any) {
const obj: any = {};
for(const client of data.clients) {
obj[client.clientId] = client.displayName;
}
setClients(obj);
}
});
useEffect(() => {
console.log(clients);
}, [clients]);
useEffect(() => {
send('clients:list')
}, []);
const value = useMemo(() => ({
clientName: clients
}), [clients]);
return <ClientsListContext.Provider value={value}>
{props.children}
</ClientsListContext.Provider>
}

View File

@ -1,6 +1,7 @@
import { createContext, useState, useMemo, useEffect } from "react";
import UserMediaState from "./UserMediaState";
import PeerState from "./PeerState";
import ClientsListState from "./ClientsListState";
export type ChannelType = 'text' | 'voice';

View File

@ -7,6 +7,7 @@ import useHover from "../hooks/useHover";
import { useApi } from "../lib/useApi";
import { useContext, useEffect, useState } from "react";
import { VoiceChannelContext } from "../contexts/EphemeralState/VoiceChannelState";
import { ClientsListContext } from "../contexts/EphemeralState/ClientsListState";
interface ChannelProps {
unread: number;
@ -16,6 +17,7 @@ interface ChannelProps {
}
export default function Channel(props: ChannelProps) {
const { clientName } = useContext(ClientsListContext);
const { channel, setChannel } = useChannel();
const { unread, uid, name, type } = props;
const [ref, hover] = useHover<HTMLDivElement>();
@ -126,7 +128,7 @@ export default function Channel(props: ChannelProps) {
<br></br>
{participants.map(participant => (
<div key={participant.clientId}>
{participant.clientId}
{clientName[participant.clientId]}
</div>
))}
</div>

View File

@ -1,4 +1,6 @@
import { useContext, useEffect } from 'react';
import TimeAgo from 'react-timeago';
import { ClientsListContext } from '../contexts/EphemeralState/ClientsListState';
export interface IMessage {
uid: string;
@ -19,48 +21,49 @@ const rightMessagePagging = '16px';
export function Message({
message,
}: MessageProps) {
const { clientName } = useContext(ClientsListContext);
return (
<div style={{
display: 'grid',
gridTemplateColumns: '4em 1fr',
width: '100%',
padding: '1px 0px',
<div style={{
display: 'grid',
gridTemplateColumns: '4em 1fr',
width: '100%',
padding: '1px 0px',
}}>
<span style={{
fontStyle: 'italic',
color: 'var(--neutral-6)',
textAlign: 'right',
userSelect: 'none',
marginRight: '16px',
}}>
<span style={{
fontStyle: 'italic',
color: 'var(--neutral-6)',
textAlign: 'right',
userSelect: 'none',
marginRight: '16px',
<TimeAgo
date={message.timestamp}
formatter={(t, u) => u === 'second' ? 'Now' : ('' + t + u[0])}
></TimeAgo>
</span>
<span style={{
}}>
<div style={{
fontWeight: '500',
float: 'left',
paddingRight: firstLineIndent,
// marginRight: '16px',
// height: '100%'
// borderBottom: '1px solid white'
}}>
<TimeAgo
date={message.timestamp}
formatter={(t, u) => u === 'second' ? 'Now' : ('' + t + u[0])}
></TimeAgo>
</span>
<span style={{
{clientName[message.from]}
</div>
<div style={{
marginRight: rightMessagePagging,
paddingLeft: multiLineIndent,
boxSizing: 'border-box',
position: 'relative',
}}>
<div style={{
fontWeight: '500',
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.text}
</div>
</span>
</div>
);
}

View File

@ -1,23 +1,15 @@
import { connection } from './migrate';
export default async function(a: any, ...opts: any[]): Promise<any[] | null> {
const b64 = a.split('base64,')[1];
export default function(sqlFile: any, ...args: any[]): Promise<any[] | null> {
const b64 = sqlFile.split('base64,')[1];
const text = Buffer.from(b64, 'base64').toString();
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('--- Query ---');
console.error(err.sql);
reject(err);
});
return new Promise((resolve, reject) => {
connection.query(text, [...args], (err, results) => {
if(!err) return resolve(results);
console.error(err.errno, err.sqlMessage);
console.error('--- Query ---');
console.error(err.sql);
reject(err);
});
} catch(e) {
return null;
}
// console.log(...opts)
});
}

View File

@ -1,7 +1,7 @@
SELECT
messages.t_sent,
clients.name as 'from',
clients.uid as 'from',
messages.`text` as 'text',
messages.channel_uid,
messages.uid as uid

View File

@ -0,0 +1,13 @@
import getAllDisplayNames from './get/all/displayNames';
const database = {
get: {
all: {
displayNames: getAllDisplayNames
}
}
};
export default database;

View File

@ -0,0 +1,5 @@
SELECT
uid as clientId,
name as displayName
FROM clients
WHERE totp IS NOT NULL;

View File

@ -0,0 +1,6 @@
import sql from './displayNames.sql';
import query from '/@/db/query';
export default function() {
return query(sql)
}

View File

@ -1,10 +1,11 @@
import router from '../lib/router';
import query from '../db/query';
import { reply } from '../lib/WebSocketServer';
import { broadcast, reply } from '../lib/WebSocketServer';
import _new from '../db/snippets/client/new.sql';
import _get from '../db/snippets/client/get.sql';
import rename from '../db/snippets/client/rename.sql';
import database from '../lib/dbHelpers/database';
export default router({
async 'new'(data: any) {
@ -14,7 +15,7 @@ export default router({
data.username,
);
if(response === null) return;
return reply({
return broadcast({
clientId: response[0][0].uid
});
},
@ -32,4 +33,9 @@ export default router({
const res = await query(rename, name, clientId);
// silent failure O.O
},
async list() {
return reply({
clients: await database.get.all.displayNames()
});
}
});

View File

@ -2,7 +2,6 @@ import query from '../db/query';
import router from '../lib/router';
import newMessage from '../db/snippets/message/new.sql';
import recentMessages from '../db/snippets/message/recent.sql';
import getName from '../db/snippets/client/get.sql';
import { broadcast, reply } from '../lib/WebSocketServer';
export default router({
@ -20,10 +19,8 @@ export default router({
data.channel,
);
if(response === null) return;
// translate from to a real name
const nameRes = await query(getName, data.$clientId);
if(nameRes === null) return;
data.from = nameRes[0].name;
data.from = data.$clientId;
return broadcast(data);
},
async recent(data: any) {