wip session base page
This commit is contained in:
parent
4ffc0d9b3b
commit
4b3db7fed4
|
|
@ -12467,6 +12467,14 @@
|
|||
"version": "5.7.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
|
||||
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.4.tgz",
|
||||
"integrity": "sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -21860,12 +21868,9 @@
|
|||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0"
|
||||
}
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "3.0.0",
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@
|
|||
"react-typed": "^1.2.0",
|
||||
"reactstrap": "^8.6.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"uuid": "^3.4.0"
|
||||
"uuid": "^3.4.0",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -13,4 +13,5 @@
|
|||
@import './custom/partner_agreement_v1';
|
||||
@import './custom/audio_player';
|
||||
@import './custom/landing';
|
||||
@import './custom/buttons';
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
.btn-custom-outline {
|
||||
border-color: #d6d6d6;
|
||||
background-color: transparent;
|
||||
color: #8a8787; /* change text to match */
|
||||
border-width: 1px 2px 2px 1px; /* thicker on left & bottom */
|
||||
box-shadow: .5px .5px 0 #ffffffd7; /* shadow for depth */
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.btn-custom-outline:hover {
|
||||
background-color: #aca9a8;
|
||||
color: #494848;
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import React, { useEffect, useContext } from 'react'
|
||||
import { Alert, Col, Row, Button, Card, CardBody, Form, Modal, ModalHeader, ModalBody, ModalFooter, CardHeader } from 'reactstrap';
|
||||
import FalconCardHeader from '../common/FalconCardHeader';
|
||||
import useClientWebSocket from '../../hooks/useClientWebsocket'
|
||||
import NavbarTop from '../navbar/JKNavbarTop';
|
||||
import AppContext from '../../context/Context';
|
||||
import { getPageName } from '../../helpers/utils';
|
||||
|
||||
const JKSessionClient = () => {
|
||||
const { isConnected, messages, sendMessage } = useClientWebSocket('ws://localhost:8080');
|
||||
const { isFluid, isVertical, navbarStyle } = useContext(AppContext);
|
||||
const isKanban = getPageName('kanban');
|
||||
|
||||
useEffect(() => {
|
||||
if (isConnected) {
|
||||
sendMessage('FTUEGetStatus', { content: 'Hello, server!' });
|
||||
}
|
||||
}, [isConnected]);
|
||||
return (
|
||||
<Card>
|
||||
<FalconCardHeader title={`Session`} titleClass="font-weight-bold">
|
||||
<Button
|
||||
color="primary"
|
||||
className="me-2 mr-2 fs--1"
|
||||
>
|
||||
Leave Session
|
||||
</Button>
|
||||
</FalconCardHeader>
|
||||
<CardHeader className="bg-light border-bottom border-top py-2 border-3">
|
||||
<div className="d-flex flex-nowrap overflow-auto" style={{ gap: '0.5rem' }}>
|
||||
<Button className='btn-custom-outline' outline size="md">Settings</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Invite</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Volume</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Video</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Record</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Broadcast</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Open</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Chat</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Attach</Button>
|
||||
<Button className='btn-custom-outline' outline size="md">Resync</Button>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody className="pl-4" style={{ backgroundColor: '#edf2f9f5' }}>
|
||||
<div className='d-flex' style={{ gap: '1rem' }}>
|
||||
<div className='audioInputs'>
|
||||
<h5>Audio Inputs</h5>
|
||||
<div className='d-flex' style={{ gap: '0.5rem', borderRight: '1px #ddd solid', paddingRight: '1rem' }}>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>
|
||||
<div className='d-flex flex-column' style={{ height: '100%' }}>
|
||||
<div className='p-2'>Clive Leonard</div>
|
||||
<div className='p-2'>Image</div>
|
||||
<div className='p-2'>Instrument Icon</div>
|
||||
<div className='p-2'>Volume Slider</div>
|
||||
<div className='p-2'>Controls</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>audio input 2</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='sessionMix'>
|
||||
<h5>Session Mix</h5>
|
||||
<div className='d-flex' style={{ gap: '0.5rem' }}>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>audio input 1</div>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>audio input 2</div>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>audio input 1</div>
|
||||
<div className='shadow-sm' style={{ border: '1px #ddd solid', width: '100px', height: '600px', backgroundColor: 'white' }}>audio input 2</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='attachMedia'>
|
||||
<h5>Attach Media</h5>
|
||||
</div>
|
||||
</div>
|
||||
{/* <>
|
||||
<h1>WebSocket Client</h1>
|
||||
<p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
|
||||
<h2>Messages:</h2>
|
||||
<ul>
|
||||
{messages.map((msg, index) => (
|
||||
<li key={index}>{JSON.stringify(msg)}</li>
|
||||
))}
|
||||
</ul>
|
||||
</> */}
|
||||
</CardBody>
|
||||
</Card>
|
||||
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default JKSessionClient
|
||||
|
|
@ -5,8 +5,9 @@ import AppContext from '../../context/Context';
|
|||
import Logo from './Logo';
|
||||
import TopNavRightSideNavItem from './JKTopNavRightSideNavItem';
|
||||
import { navbarBreakPoint, topNavbarBreakpoint } from '../../config';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const JKNavbarTop = () => {
|
||||
const JKNavbarTop = ({ logoWidth}) => {
|
||||
const {
|
||||
showBurgerMenu,
|
||||
setShowBurgerMenu,
|
||||
|
|
@ -42,7 +43,7 @@ const JKNavbarTop = () => {
|
|||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<Logo at="navbar-top" id="topLogo" width={140} />
|
||||
<Logo at="navbar-top" id="topLogo" width={logoWidth} />
|
||||
{/* {isTopNav ? (
|
||||
<Collapse navbar isOpen={navbarCollapsed} className="scrollbar">
|
||||
<Nav navbar>
|
||||
|
|
@ -62,4 +63,12 @@ const JKNavbarTop = () => {
|
|||
);
|
||||
};
|
||||
|
||||
JKNavbarTop.propTypes = {
|
||||
logoWidth: PropTypes.number
|
||||
};
|
||||
|
||||
JKNavbarTop.defaultProps = {
|
||||
logoWidth: 140
|
||||
};
|
||||
|
||||
export default JKNavbarTop;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export default function useClientWebSocket(url) {
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [messages, setMessages] = useState([]);
|
||||
const ws = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
ws.current = new WebSocket(url);
|
||||
|
||||
ws.current.onopen = () => {
|
||||
console.log('Connected to WebSocket');
|
||||
setIsConnected(true);
|
||||
};
|
||||
|
||||
ws.current.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
setMessages(prev => [...prev, data]);
|
||||
};
|
||||
|
||||
ws.current.onclose = () => {
|
||||
console.log('WebSocket disconnected');
|
||||
setIsConnected(false);
|
||||
};
|
||||
|
||||
ws.current.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
};
|
||||
|
||||
return () => {
|
||||
if (ws.current) {
|
||||
ws.current.close();
|
||||
}
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
const sendMessage = (type, params = {}) => {
|
||||
if (ws.current && ws.current.readyState === WebSocket.OPEN) {
|
||||
ws.current.send(JSON.stringify({ type, params }));
|
||||
}
|
||||
};
|
||||
|
||||
return { isConnected, messages, sendMessage };
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types';
|
||||
import ClientRoutes from './JKClientRoutes';
|
||||
import UserAuth from '../context/UserAuth';
|
||||
import { BrowserQueryProvider } from '../context/BrowserQuery';
|
||||
import { AppDataProvider } from '../context/AppDataContext';
|
||||
import { AppRoutesProvider } from '../context/AppRoutesContext';
|
||||
|
||||
const JKClientLayout = ({ location }) => {
|
||||
|
||||
return (
|
||||
|
||||
<UserAuth path={location.pathname}>
|
||||
<AppRoutesProvider>
|
||||
<AppDataProvider>
|
||||
<BrowserQueryProvider>
|
||||
<ClientRoutes />
|
||||
</BrowserQueryProvider>
|
||||
</AppDataProvider>
|
||||
</AppRoutesProvider>
|
||||
</UserAuth>
|
||||
|
||||
)
|
||||
}
|
||||
JKClientLayout.propTypes = { location: PropTypes.object.isRequired };
|
||||
|
||||
export default JKClientLayout
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { Switch, Redirect, withRouter } from 'react-router-dom';
|
||||
import PrivateRoute from '../helpers/privateRoute';
|
||||
import AppContext from '../context/Context';
|
||||
import { getPageName } from '../helpers/utils';
|
||||
import PropTypes from 'prop-types';
|
||||
import NavbarTop from '../components/navbar/JKNavbarTop';
|
||||
import NavbarVertical from '../components/navbar/JKNavbarVertical';
|
||||
import Footer from '../components/footer/JKFooter';
|
||||
|
||||
// Import your page components here
|
||||
import JKSessionClient from '../components/client/JKSessionClient';
|
||||
|
||||
const JKClientRoutes = ({ match: { url } }) => {
|
||||
const { isFluid, isVertical, navbarStyle } = useContext(AppContext);
|
||||
const isKanban = getPageName('kanban');
|
||||
|
||||
return (
|
||||
<div className={isFluid || isKanban ? 'container-fluid' : 'container'}>
|
||||
{/* {isVertical && <NavbarVertical isKanban={isKanban} navbarStyle={navbarStyle} />} */}
|
||||
<div className="content">
|
||||
<NavbarTop logoWidth={240} />
|
||||
<Switch>
|
||||
<PrivateRoute exact path={`${url}/`} component={JKSessionClient} />
|
||||
{/*Redirect*/}
|
||||
<Redirect to="/errors/404" />
|
||||
</Switch>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
JKClientRoutes.propTypes = { match: PropTypes.object.isRequired };
|
||||
|
||||
export default withRouter(JKClientRoutes);
|
||||
|
|
@ -10,6 +10,7 @@ import BuildMeta from "./JKBuildMeta";
|
|||
import loadable from '@loadable/component';
|
||||
const AuthBasicLayout = loadable(() => import('./JKAuthBasicLayout'));
|
||||
const PublicLayout = loadable(() => import('./JKPublicLayout'));
|
||||
const ClientLayout = loadable(() => import('./JKClientLayout'));
|
||||
|
||||
const Layout = () => {
|
||||
useEffect(() => {
|
||||
|
|
@ -33,6 +34,7 @@ const Layout = () => {
|
|||
<Route path="/errors" component={ErrorLayout} />
|
||||
<Route path="/auth" component={AuthBasicLayout} />
|
||||
<Route path="/public" component={PublicLayout} />
|
||||
<Route path="/client" component={ClientLayout} />
|
||||
<Route component={DashboardLayout} />
|
||||
</Switch>
|
||||
<ToastContainer transition={Fade} closeButton={<CloseButton />} position={toast.POSITION.BOTTOM_RIGHT} />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,650 @@
|
|||
const WebSocket = require('ws');
|
||||
const http = require('http');
|
||||
|
||||
/**
|
||||
* Fake Client WebSocket Server
|
||||
* Simulates the JamKazam C++ backend client application
|
||||
* Handles all the internal system functions and provides mock responses
|
||||
*/
|
||||
|
||||
class FakeClientWebSocket {
|
||||
constructor(port = 8080) {
|
||||
this.port = port;
|
||||
this.server = null;
|
||||
this.wss = null;
|
||||
this.clients = new Set();
|
||||
|
||||
// Mock data storage
|
||||
this.mockData = {
|
||||
audioConfigurations: [
|
||||
{ id: 1, name: 'Default Audio Config', sampleRate: 44100, bufferSize: 256 },
|
||||
{ id: 2, name: 'Low Latency Config', sampleRate: 48000, bufferSize: 128 },
|
||||
{ id: 3, name: 'High Quality Config', sampleRate: 96000, bufferSize: 512 }
|
||||
],
|
||||
goodAudioConfigurations: [
|
||||
{ id: 1, name: 'Default Audio Config', sampleRate: 44100, bufferSize: 256 },
|
||||
{ id: 2, name: 'Low Latency Config', sampleRate: 48000, bufferSize: 128 }
|
||||
],
|
||||
sessionInfo: {
|
||||
sessionId: 'session_12345',
|
||||
sessionName: 'Mock Jam Session',
|
||||
participants: ['user1', 'user2', 'user3'],
|
||||
isActive: false,
|
||||
connectionStatus: 'disconnected'
|
||||
},
|
||||
userProfile: {
|
||||
username: 'MockUser',
|
||||
lastUsedProfile: 'Default Profile',
|
||||
macHash: 'mock_mac_hash_12345',
|
||||
clientId: 'client_67890',
|
||||
parentClientId: null,
|
||||
role: 'participant'
|
||||
},
|
||||
vstData: {
|
||||
loadedVsts: [],
|
||||
availableVsts: [
|
||||
{ id: 1, name: 'Reverb VST', path: '/mock/path/reverb.vst' },
|
||||
{ id: 2, name: 'Compressor VST', path: '/mock/path/compressor.vst' }
|
||||
],
|
||||
trackAssignments: [],
|
||||
searchPaths: ['/mock/vst/path1', '/mock/vst/path2'],
|
||||
midiDevices: ['Mock MIDI Device 1', 'Mock MIDI Device 2']
|
||||
},
|
||||
recordingData: {
|
||||
currentRecordingId: null,
|
||||
isRecording: false,
|
||||
localRecordingState: 'stopped',
|
||||
recordingPreference: 'local'
|
||||
},
|
||||
playbackData: {
|
||||
isPlaying: false,
|
||||
isPaused: false,
|
||||
currentPosition: 0,
|
||||
duration: 0,
|
||||
backingTrackFile: null,
|
||||
jamTrackDetails: null
|
||||
},
|
||||
systemInfo: {
|
||||
operatingMode: 'normal',
|
||||
os: 'macOS Monterey',
|
||||
detailedOS: 'macOS 12.6.1 (21G217)',
|
||||
version: '1.0.0',
|
||||
sampleRate: 44100,
|
||||
isAppInWritableVolume: true,
|
||||
isAudioStarted: false
|
||||
},
|
||||
networkData: {
|
||||
testScore: 85,
|
||||
expectedLatency: 25,
|
||||
connectionDetails: {
|
||||
ping: 15,
|
||||
jitter: 2,
|
||||
packetLoss: 0.1
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.setupMessageHandlers();
|
||||
}
|
||||
|
||||
start() {
|
||||
this.server = http.createServer();
|
||||
this.wss = new WebSocket.Server({ server: this.server });
|
||||
|
||||
this.wss.on('connection', (ws, req) => {
|
||||
console.log('Client connected from:', req.socket.remoteAddress);
|
||||
this.clients.add(ws);
|
||||
|
||||
ws.on('message', (message) => {
|
||||
try {
|
||||
const data = JSON.parse(message);
|
||||
this.handleMessage(ws, data);
|
||||
} catch (error) {
|
||||
console.error('Error parsing message:', error);
|
||||
this.sendError(ws, 'Invalid JSON format');
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
console.log('Client disconnected');
|
||||
this.clients.delete(ws);
|
||||
});
|
||||
|
||||
ws.on('error', (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
this.clients.delete(ws);
|
||||
});
|
||||
|
||||
// Send welcome message
|
||||
this.sendMessage(ws, {
|
||||
type: 'connection',
|
||||
status: 'connected',
|
||||
message: 'Connected to Fake JamKazam Client'
|
||||
});
|
||||
});
|
||||
|
||||
this.server.listen(this.port, () => {
|
||||
console.log(`Fake Client WebSocket server running on port ${this.port}`);
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.wss) {
|
||||
this.wss.close();
|
||||
}
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
}
|
||||
console.log('Fake Client WebSocket server stopped');
|
||||
}
|
||||
|
||||
sendMessage(ws, message) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
|
||||
sendError(ws, error) {
|
||||
this.sendMessage(ws, {
|
||||
type: 'error',
|
||||
error: error
|
||||
});
|
||||
}
|
||||
|
||||
broadcast(message) {
|
||||
this.clients.forEach(client => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify(message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupMessageHandlers() {
|
||||
this.messageHandlers = {
|
||||
// FTUE (First Time User Experience) Functions
|
||||
FTUEGetAllAudioConfigurations: () => ({
|
||||
type: 'FTUEGetAllAudioConfigurations',
|
||||
data: this.mockData.audioConfigurations
|
||||
}),
|
||||
|
||||
FTUEGetGoodAudioConfigurations: () => ({
|
||||
type: 'FTUEGetGoodAudioConfigurations',
|
||||
data: this.mockData.goodAudioConfigurations
|
||||
}),
|
||||
|
||||
FTUEGetChatInputVolume: () => ({
|
||||
type: 'FTUEGetChatInputVolume',
|
||||
data: { volume: 75 }
|
||||
}),
|
||||
|
||||
FTUEGetStatus: () => ({
|
||||
type: 'FTUEGetStatus',
|
||||
data: { status: 'completed', progress: 100 }
|
||||
}),
|
||||
|
||||
FTUEGetChannels: () => ({
|
||||
type: 'FTUEGetChannels',
|
||||
data: { inputChannels: 2, outputChannels: 2 }
|
||||
}),
|
||||
|
||||
FTUEGetExpectedLatency: () => ({
|
||||
type: 'FTUEGetExpectedLatency',
|
||||
data: { latency: this.mockData.networkData.expectedLatency }
|
||||
}),
|
||||
|
||||
// Session Management Functions
|
||||
SessionSetAlertCallback: () => ({
|
||||
type: 'SessionSetAlertCallback',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
SessionGetMacHash: () => ({
|
||||
type: 'SessionGetMacHash',
|
||||
data: { macHash: this.mockData.userProfile.macHash }
|
||||
}),
|
||||
|
||||
SessionGetAllControlState: () => ({
|
||||
type: 'SessionGetAllControlState',
|
||||
data: {
|
||||
volume: 80,
|
||||
mute: false,
|
||||
solo: false,
|
||||
effects: []
|
||||
}
|
||||
}),
|
||||
|
||||
SessionPageEnter: () => ({
|
||||
type: 'SessionPageEnter',
|
||||
data: { status: 'entered' }
|
||||
}),
|
||||
|
||||
SessionPageLeave: () => ({
|
||||
type: 'SessionPageLeave',
|
||||
data: { status: 'left' }
|
||||
}),
|
||||
|
||||
SessionRegisterCallback: () => ({
|
||||
type: 'SessionRegisterCallback',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
SessionSetConnectionStatusRefreshRate: (params) => ({
|
||||
type: 'SessionSetConnectionStatusRefreshRate',
|
||||
data: { refreshRate: params.rate || 1000 }
|
||||
}),
|
||||
|
||||
SessionSetUserName: (params) => {
|
||||
this.mockData.userProfile.username = params.username || 'MockUser';
|
||||
return {
|
||||
type: 'SessionSetUserName',
|
||||
data: { username: this.mockData.userProfile.username }
|
||||
};
|
||||
},
|
||||
|
||||
SessionSetTrackVolumeData: (params) => ({
|
||||
type: 'SessionSetTrackVolumeData',
|
||||
data: { trackId: params.trackId, volume: params.volume }
|
||||
}),
|
||||
|
||||
SessionAudioResync: () => ({
|
||||
type: 'SessionAudioResync',
|
||||
data: { status: 'resynced' }
|
||||
}),
|
||||
|
||||
// System Functions
|
||||
RegisterQuitCallback: () => ({
|
||||
type: 'RegisterQuitCallback',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
ResetPageCounters: () => ({
|
||||
type: 'ResetPageCounters',
|
||||
data: { status: 'counters_reset' }
|
||||
}),
|
||||
|
||||
getOperatingMode: () => ({
|
||||
type: 'getOperatingMode',
|
||||
data: { mode: this.mockData.systemInfo.operatingMode }
|
||||
}),
|
||||
|
||||
GetDetailedOS: () => ({
|
||||
type: 'GetDetailedOS',
|
||||
data: { os: this.mockData.systemInfo.detailedOS }
|
||||
}),
|
||||
|
||||
GetOSAsString: () => ({
|
||||
type: 'GetOSAsString',
|
||||
data: { os: this.mockData.systemInfo.os }
|
||||
}),
|
||||
|
||||
LastUsedProfileName: () => ({
|
||||
type: 'LastUsedProfileName',
|
||||
data: { profileName: this.mockData.userProfile.lastUsedProfile }
|
||||
}),
|
||||
|
||||
SetLatencyTestBlocked: (params) => ({
|
||||
type: 'SetLatencyTestBlocked',
|
||||
data: { blocked: params.blocked || false }
|
||||
}),
|
||||
|
||||
SetScoreWorkTimingInterval: (params) => ({
|
||||
type: 'SetScoreWorkTimingInterval',
|
||||
data: { interval: params.interval || 1000 }
|
||||
}),
|
||||
|
||||
IsAppInWritableVolume: () => ({
|
||||
type: 'IsAppInWritableVolume',
|
||||
data: { writable: this.mockData.systemInfo.isAppInWritableVolume }
|
||||
}),
|
||||
|
||||
RegisterSessionJoinLeaveRequestCallBack: () => ({
|
||||
type: 'RegisterSessionJoinLeaveRequestCallBack',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
OnLoggedIn: () => ({
|
||||
type: 'OnLoggedIn',
|
||||
data: { status: 'logged_in', user: this.mockData.userProfile.username }
|
||||
}),
|
||||
|
||||
RegisterRecordingManagerCallbacks: () => ({
|
||||
type: 'RegisterRecordingManagerCallbacks',
|
||||
data: { status: 'callbacks_registered' }
|
||||
}),
|
||||
|
||||
SetVURefreshRate: (params) => ({
|
||||
type: 'SetVURefreshRate',
|
||||
data: { refreshRate: params.rate || 30 }
|
||||
}),
|
||||
|
||||
RegisterGenericCallBack: () => ({
|
||||
type: 'RegisterGenericCallBack',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
ClientUpdateVersion: () => ({
|
||||
type: 'ClientUpdateVersion',
|
||||
data: { version: this.mockData.systemInfo.version }
|
||||
}),
|
||||
|
||||
P2PMessageReceived: (params) => ({
|
||||
type: 'P2PMessageReceived',
|
||||
data: { message: params.message, from: params.from }
|
||||
}),
|
||||
|
||||
RegisterVolChangeCallBack: () => ({
|
||||
type: 'RegisterVolChangeCallBack',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
setMetronomeOpenCallback: () => ({
|
||||
type: 'setMetronomeOpenCallback',
|
||||
data: { status: 'callback_registered' }
|
||||
}),
|
||||
|
||||
IsAudioStarted: () => ({
|
||||
type: 'IsAudioStarted',
|
||||
data: { started: this.mockData.systemInfo.isAudioStarted }
|
||||
}),
|
||||
|
||||
NetworkTestResult: () => ({
|
||||
type: 'NetworkTestResult',
|
||||
data: this.mockData.networkData
|
||||
}),
|
||||
|
||||
UpdateSessionInfo: (params) => {
|
||||
Object.assign(this.mockData.sessionInfo, params);
|
||||
return {
|
||||
type: 'UpdateSessionInfo',
|
||||
data: this.mockData.sessionInfo
|
||||
};
|
||||
},
|
||||
|
||||
getClientParentChildRole: () => ({
|
||||
type: 'getClientParentChildRole',
|
||||
data: { role: this.mockData.userProfile.role }
|
||||
}),
|
||||
|
||||
getParentClientId: () => ({
|
||||
type: 'getParentClientId',
|
||||
data: { parentClientId: this.mockData.userProfile.parentClientId }
|
||||
}),
|
||||
|
||||
ClientJoinedSession: (params) => ({
|
||||
type: 'ClientJoinedSession',
|
||||
data: { clientId: params.clientId, sessionId: params.sessionId }
|
||||
}),
|
||||
|
||||
ClientLeftSession: (params) => ({
|
||||
type: 'ClientLeftSession',
|
||||
data: { clientId: params.clientId, sessionId: params.sessionId }
|
||||
}),
|
||||
|
||||
JoinSession: (params) => {
|
||||
this.mockData.sessionInfo.isActive = true;
|
||||
this.mockData.sessionInfo.connectionStatus = 'connected';
|
||||
return {
|
||||
type: 'JoinSession',
|
||||
data: { sessionId: params.sessionId, status: 'joined' }
|
||||
};
|
||||
},
|
||||
|
||||
LeaveSession: () => {
|
||||
this.mockData.sessionInfo.isActive = false;
|
||||
this.mockData.sessionInfo.connectionStatus = 'disconnected';
|
||||
return {
|
||||
type: 'LeaveSession',
|
||||
data: { status: 'left' }
|
||||
};
|
||||
},
|
||||
|
||||
// VST Functions
|
||||
IsVstLoaded: (params) => ({
|
||||
type: 'IsVstLoaded',
|
||||
data: { loaded: this.mockData.vstData.loadedVsts.includes(params.vstId) }
|
||||
}),
|
||||
|
||||
hasVstAssignment: (params) => ({
|
||||
type: 'hasVstAssignment',
|
||||
data: { hasAssignment: this.mockData.vstData.trackAssignments.some(a => a.vstId === params.vstId) }
|
||||
}),
|
||||
|
||||
VSTListVsts: () => ({
|
||||
type: 'VSTListVsts',
|
||||
data: { vsts: this.mockData.vstData.availableVsts }
|
||||
}),
|
||||
|
||||
VSTListTrackAssignments: () => ({
|
||||
type: 'VSTListTrackAssignments',
|
||||
data: { assignments: this.mockData.vstData.trackAssignments }
|
||||
}),
|
||||
|
||||
VST_GetMidiDeviceList: () => ({
|
||||
type: 'VST_GetMidiDeviceList',
|
||||
data: { devices: this.mockData.vstData.midiDevices }
|
||||
}),
|
||||
|
||||
VSTListSearchPaths: () => ({
|
||||
type: 'VSTListSearchPaths',
|
||||
data: { paths: this.mockData.vstData.searchPaths }
|
||||
}),
|
||||
|
||||
getConnectionDetail: () => ({
|
||||
type: 'getConnectionDetail',
|
||||
data: this.mockData.networkData.connectionDetails
|
||||
}),
|
||||
|
||||
VSTLoad: (params) => {
|
||||
if (!this.mockData.vstData.loadedVsts.includes(params.vstId)) {
|
||||
this.mockData.vstData.loadedVsts.push(params.vstId);
|
||||
}
|
||||
return {
|
||||
type: 'VSTLoad',
|
||||
data: { vstId: params.vstId, status: 'loaded' }
|
||||
};
|
||||
},
|
||||
|
||||
GetNetworkTestScore: () => ({
|
||||
type: 'GetNetworkTestScore',
|
||||
data: { score: this.mockData.networkData.testScore }
|
||||
}),
|
||||
|
||||
setSessionMixerCategoryPlayoutState: (params) => ({
|
||||
type: 'setSessionMixerCategoryPlayoutState',
|
||||
data: { category: params.category, state: params.state }
|
||||
}),
|
||||
|
||||
// Recording Functions
|
||||
StartMediaRecording: () => {
|
||||
this.mockData.recordingData.isRecording = true;
|
||||
this.mockData.recordingData.currentRecordingId = 'rec_' + Date.now();
|
||||
return {
|
||||
type: 'StartMediaRecording',
|
||||
data: { recordingId: this.mockData.recordingData.currentRecordingId }
|
||||
};
|
||||
},
|
||||
|
||||
FrontStopRecording: () => {
|
||||
this.mockData.recordingData.isRecording = false;
|
||||
return {
|
||||
type: 'FrontStopRecording',
|
||||
data: { status: 'stopped' }
|
||||
};
|
||||
},
|
||||
|
||||
RegisterRecordingCallbacks: () => ({
|
||||
type: 'RegisterRecordingCallbacks',
|
||||
data: { status: 'callbacks_registered' }
|
||||
}),
|
||||
|
||||
GetAudioRecordingPreference: () => ({
|
||||
type: 'GetAudioRecordingPreference',
|
||||
data: { preference: this.mockData.recordingData.recordingPreference }
|
||||
}),
|
||||
|
||||
GetCurrentRecordingId: () => ({
|
||||
type: 'GetCurrentRecordingId',
|
||||
data: { recordingId: this.mockData.recordingData.currentRecordingId }
|
||||
}),
|
||||
|
||||
GetLocalRecordingState: () => ({
|
||||
type: 'GetLocalRecordingState',
|
||||
data: { state: this.mockData.recordingData.localRecordingState }
|
||||
}),
|
||||
|
||||
GetSampleRate: () => ({
|
||||
type: 'GetSampleRate',
|
||||
data: { sampleRate: this.mockData.systemInfo.sampleRate }
|
||||
}),
|
||||
|
||||
// Playback Functions
|
||||
ShowSelectBackingTrackDialog: () => ({
|
||||
type: 'ShowSelectBackingTrackDialog',
|
||||
data: { status: 'dialog_shown' }
|
||||
}),
|
||||
|
||||
SessionOpenBackingTrackFile: (params) => {
|
||||
this.mockData.playbackData.backingTrackFile = params.filePath;
|
||||
return {
|
||||
type: 'SessionOpenBackingTrackFile',
|
||||
data: { filePath: params.filePath, status: 'opened' }
|
||||
};
|
||||
},
|
||||
|
||||
SessionCurrrentPlayPosMs: () => ({
|
||||
type: 'SessionCurrrentPlayPosMs',
|
||||
data: { position: this.mockData.playbackData.currentPosition }
|
||||
}),
|
||||
|
||||
SessionGetTracksPlayDurationMs: () => ({
|
||||
type: 'SessionGetTracksPlayDurationMs',
|
||||
data: { duration: this.mockData.playbackData.duration }
|
||||
}),
|
||||
|
||||
isSessionTrackPlaying: () => ({
|
||||
type: 'isSessionTrackPlaying',
|
||||
data: { playing: this.mockData.playbackData.isPlaying }
|
||||
}),
|
||||
|
||||
SessionStartPlay: () => {
|
||||
this.mockData.playbackData.isPlaying = true;
|
||||
this.mockData.playbackData.isPaused = false;
|
||||
return {
|
||||
type: 'SessionStartPlay',
|
||||
data: { status: 'playing' }
|
||||
};
|
||||
},
|
||||
|
||||
SessionPausePlay: () => {
|
||||
this.mockData.playbackData.isPaused = true;
|
||||
return {
|
||||
type: 'SessionPausePlay',
|
||||
data: { status: 'paused' }
|
||||
};
|
||||
},
|
||||
|
||||
SessionStopPlay: () => {
|
||||
this.mockData.playbackData.isPlaying = false;
|
||||
this.mockData.playbackData.isPaused = false;
|
||||
this.mockData.playbackData.currentPosition = 0;
|
||||
return {
|
||||
type: 'SessionStopPlay',
|
||||
data: { status: 'stopped' }
|
||||
};
|
||||
},
|
||||
|
||||
SessionCloseBackingTrackFile: () => {
|
||||
this.mockData.playbackData.backingTrackFile = null;
|
||||
return {
|
||||
type: 'SessionCloseBackingTrackFile',
|
||||
data: { status: 'closed' }
|
||||
};
|
||||
},
|
||||
|
||||
JamTrackGetTrackDetail: (params) => ({
|
||||
type: 'JamTrackGetTrackDetail',
|
||||
data: {
|
||||
trackId: params.trackId,
|
||||
name: 'Mock Jam Track',
|
||||
duration: 180000,
|
||||
artist: 'Mock Artist'
|
||||
}
|
||||
}),
|
||||
|
||||
SessionCurrrentJamTrackPlayPosMs: () => ({
|
||||
type: 'SessionCurrrentJamTrackPlayPosMs',
|
||||
data: { position: this.mockData.playbackData.currentPosition }
|
||||
}),
|
||||
|
||||
SessionGetJamTracksPlayDurationMs: () => ({
|
||||
type: 'SessionGetJamTracksPlayDurationMs',
|
||||
data: { duration: this.mockData.playbackData.duration }
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
handleMessage(ws, data) {
|
||||
const { type, params = {} } = data;
|
||||
|
||||
console.log(`Received message: ${type}`, params);
|
||||
|
||||
if (this.messageHandlers[type]) {
|
||||
try {
|
||||
const response = this.messageHandlers[type](params);
|
||||
this.sendMessage(ws, response);
|
||||
} catch (error) {
|
||||
console.error(`Error handling ${type}:`, error);
|
||||
this.sendError(ws, `Error processing ${type}: ${error.message}`);
|
||||
}
|
||||
} else {
|
||||
console.warn(`Unknown message type: ${type}`);
|
||||
this.sendError(ws, `Unknown message type: ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate periodic updates
|
||||
startPeriodicUpdates() {
|
||||
// Simulate connection status updates
|
||||
setInterval(() => {
|
||||
this.broadcast({
|
||||
type: 'ConnectionStatusUpdate',
|
||||
data: {
|
||||
ping: Math.floor(Math.random() * 50) + 10,
|
||||
jitter: Math.floor(Math.random() * 10),
|
||||
packetLoss: Math.random() * 0.5
|
||||
}
|
||||
});
|
||||
}, 5000);
|
||||
|
||||
// Simulate playback position updates when playing
|
||||
setInterval(() => {
|
||||
if (this.mockData.playbackData.isPlaying && !this.mockData.playbackData.isPaused) {
|
||||
this.mockData.playbackData.currentPosition += 1000;
|
||||
this.broadcast({
|
||||
type: 'PlaybackPositionUpdate',
|
||||
data: { position: this.mockData.playbackData.currentPosition }
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Create and start the server
|
||||
const fakeClient = new FakeClientWebSocket(8080);
|
||||
fakeClient.start();
|
||||
fakeClient.startPeriodicUpdates();
|
||||
|
||||
// Handle graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\nShutting down Fake Client WebSocket server...');
|
||||
fakeClient.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
console.log('\nShutting down Fake Client WebSocket server...');
|
||||
fakeClient.stop();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
module.exports = FakeClientWebSocket;
|
||||
Loading…
Reference in New Issue