some more basic voice updates
parent
46d3f00280
commit
1aead4cc6b
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.`);
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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>
|
||||
}
|
||||
|
|
@ -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';
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
import getAllDisplayNames from './get/all/displayNames';
|
||||
|
||||
|
||||
const database = {
|
||||
get: {
|
||||
all: {
|
||||
displayNames: getAllDisplayNames
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default database;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
SELECT
|
||||
uid as clientId,
|
||||
name as displayName
|
||||
FROM clients
|
||||
WHERE totp IS NOT NULL;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import sql from './displayNames.sql';
|
||||
import query from '/@/db/query';
|
||||
|
||||
export default function() {
|
||||
return query(sql)
|
||||
}
|
||||
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
Reference in New Issue