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 {join} from 'path';
import {URL} from 'url'; 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 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'), preload: join(__dirname, '../../preload/dist/index.cjs'),
}, },
}); });
browserWindow.setMenu(null); browserWindow.setMenu(null);
@ -40,6 +44,11 @@ async function createWindow() {
await browserWindow.loadURL(pageUrl); await browserWindow.loadURL(pageUrl);
// session.fromPartition('default').setPermissionRequestHandler((webContents, permission, callback) => {
// console.log('requested permission', permission);
// })
return browserWindow; return browserWindow;
} }

View File

@ -6,11 +6,27 @@ import {URL} from 'url';
* *
* In development mode you need allow open `VITE_DEV_SERVER_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 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. * 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) => { contents.on('will-navigate', (event, url) => {
const {origin} = new URL(url); const {origin} = new URL(url);
if (ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) { if (ALLOWED_ORIGINS.has(origin)) {
return; return;
} }
@ -61,7 +77,7 @@ app.on('web-contents-created', (_, contents) => {
contents.session.setPermissionRequestHandler((webContents, permission, callback) => { contents.session.setPermissionRequestHandler((webContents, permission, callback) => {
const {origin} = new URL(webContents.getURL()); const {origin} = new URL(webContents.getURL());
const permissionGranted = !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission); const permissionGranted = !!ALLOWED_PERMISSIONS.includes(permission)
callback(permissionGranted); callback(permissionGranted);
if (!permissionGranted && import.meta.env.DEV) { if (!permissionGranted && import.meta.env.DEV) {
@ -106,7 +122,7 @@ app.on('web-contents-created', (_, contents) => {
*/ */
contents.on('will-attach-webview', (event, webPreferences, params) => { contents.on('will-attach-webview', (event, webPreferences, params) => {
const {origin} = new URL(params.src); const {origin} = new URL(params.src);
if (!ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) { if (!ALLOWED_ORIGINS.has(origin)) {
if (import.meta.env.DEV) { if (import.meta.env.DEV) {
console.warn(`A webview tried to attach ${params.src}, but was blocked.`); 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 Sidebar from "./components/Sidebar";
import TwoPanel from "./components/TwoPanel"; import TwoPanel from "./components/TwoPanel";
import Voice from "./components/Voice"; import Voice from "./components/Voice";
import ClientsListState from "./contexts/EphemeralState/ClientsListState";
import { SettingsContext } from "./contexts/EphemeralState/EphemeralState"; import { SettingsContext } from "./contexts/EphemeralState/EphemeralState";
import useHomeServer from "./contexts/PersistentState/useHomeServerNative"; import useHomeServer from "./contexts/PersistentState/useHomeServerNative";
import useChannel from "./hooks/useChannel"; import useChannel from "./hooks/useChannel";

View File

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

View File

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

View File

@ -1,4 +1,6 @@
import { useContext, useEffect } from 'react';
import TimeAgo from 'react-timeago'; import TimeAgo from 'react-timeago';
import { ClientsListContext } from '../contexts/EphemeralState/ClientsListState';
export interface IMessage { export interface IMessage {
uid: string; uid: string;
@ -20,6 +22,7 @@ export function Message({
message, message,
}: MessageProps) { }: MessageProps) {
const { clientName } = useContext(ClientsListContext);
return ( return (
<div style={{ <div style={{
@ -50,7 +53,7 @@ export function Message({
// height: '100%' // height: '100%'
// borderBottom: '1px solid white' // borderBottom: '1px solid white'
}}> }}>
{message.from} {clientName[message.from]}
</div> </div>
<div style={{ <div style={{
marginRight: rightMessagePagging, marginRight: rightMessagePagging,

View File

@ -1,14 +1,10 @@
import { connection } from './migrate'; import { connection } from './migrate';
export default function(sqlFile: any, ...args: any[]): Promise<any[] | null> {
const b64 = sqlFile.split('base64,')[1];
export default async function(a: any, ...opts: any[]): Promise<any[] | null> {
const b64 = a.split('base64,')[1];
const text = Buffer.from(b64, 'base64').toString(); const text = Buffer.from(b64, 'base64').toString();
try { return new Promise((resolve, reject) => {
return await new Promise((resolve, reject) => { connection.query(text, [...args], (err, results) => {
connection.query(text, [...opts], (err, results) => {
if(!err) return resolve(results); if(!err) return resolve(results);
console.error(err.errno, err.sqlMessage); console.error(err.errno, err.sqlMessage);
console.error('--- Query ---'); console.error('--- Query ---');
@ -16,8 +12,4 @@ export default async function(a: any, ...opts: any[]): Promise<any[] | null> {
reject(err); reject(err);
}); });
}); });
} catch(e) {
return null;
}
// console.log(...opts)
} }

View File

@ -1,7 +1,7 @@
SELECT SELECT
messages.t_sent, messages.t_sent,
clients.name as 'from', clients.uid as 'from',
messages.`text` as 'text', messages.`text` as 'text',
messages.channel_uid, messages.channel_uid,
messages.uid as 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 router from '../lib/router';
import query from '../db/query'; 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 _new from '../db/snippets/client/new.sql';
import _get from '../db/snippets/client/get.sql'; import _get from '../db/snippets/client/get.sql';
import rename from '../db/snippets/client/rename.sql'; import rename from '../db/snippets/client/rename.sql';
import database from '../lib/dbHelpers/database';
export default router({ export default router({
async 'new'(data: any) { async 'new'(data: any) {
@ -14,7 +15,7 @@ export default router({
data.username, data.username,
); );
if(response === null) return; if(response === null) return;
return reply({ return broadcast({
clientId: response[0][0].uid clientId: response[0][0].uid
}); });
}, },
@ -32,4 +33,9 @@ export default router({
const res = await query(rename, name, clientId); const res = await query(rename, name, clientId);
// silent failure O.O // 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 router from '../lib/router';
import newMessage from '../db/snippets/message/new.sql'; import newMessage from '../db/snippets/message/new.sql';
import recentMessages from '../db/snippets/message/recent.sql'; import recentMessages from '../db/snippets/message/recent.sql';
import getName from '../db/snippets/client/get.sql';
import { broadcast, reply } from '../lib/WebSocketServer'; import { broadcast, reply } from '../lib/WebSocketServer';
export default router({ export default router({
@ -20,10 +19,8 @@ export default router({
data.channel, data.channel,
); );
if(response === null) return; if(response === null) return;
// translate from to a real name
const nameRes = await query(getName, data.$clientId); data.from = data.$clientId;
if(nameRes === null) return;
data.from = nameRes[0].name;
return broadcast(data); return broadcast(data);
}, },
async recent(data: any) { async recent(data: any) {