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 {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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.`);
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
}
|
}
|
||||||
|
|
@ -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 { 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';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 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()
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Reference in New Issue