feat : stop message sync with 2nd screen also
This commit is contained in:
parent
7ebce2dbcd
commit
1cfe2fd970
@ -1,4 +1,3 @@
|
||||
// main.js
|
||||
const { app, BrowserWindow, screen, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
@ -23,7 +22,7 @@ function createWindows() {
|
||||
},
|
||||
});
|
||||
|
||||
mainWindow.loadURL('http://10.74.231.124:4200/login');
|
||||
mainWindow.loadURL('http://10.150.40.124:4200/login');
|
||||
|
||||
screenWindow = new BrowserWindow({
|
||||
x: secondary.bounds.x,
|
||||
@ -38,7 +37,7 @@ function createWindows() {
|
||||
},
|
||||
});
|
||||
|
||||
screenWindow.loadURL('http://10.74.231.124:4200/shared-display');
|
||||
screenWindow.loadURL('http://10.150.40.124:4200/shared-display');
|
||||
|
||||
// Handle opening second screen and send initial data
|
||||
ipcMain.on('open-second-screen', () => {
|
||||
@ -58,6 +57,13 @@ function createWindows() {
|
||||
}
|
||||
});
|
||||
|
||||
// Add this new handler
|
||||
ipcMain.on('sync-stop-message', (event, message) => {
|
||||
if (screenWindow && screenWindow.webContents) {
|
||||
screenWindow.webContents.send('update-stop-message', message);
|
||||
}
|
||||
});
|
||||
|
||||
// Handle BTID request
|
||||
ipcMain.handle('get-btid', () => {
|
||||
try {
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// preload.js
|
||||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
openSecondScreen: () => ipcRenderer.send('open-second-screen'),
|
||||
closeSecondScreen: () => ipcRenderer.send('close-second-screen'),
|
||||
getBtid: () => ipcRenderer.invoke('get-btid'),
|
||||
// New: Send data to main process
|
||||
syncSharedData: (data) => ipcRenderer.send('sync-shared-data', data),
|
||||
// New: Receive data in second window
|
||||
onUpdateSharedData: (callback) => ipcRenderer.on('update-shared-data', (event, data) => callback(data)),
|
||||
// Add these new lines
|
||||
syncStopMessage: (message) => ipcRenderer.send('sync-stop-message', message),
|
||||
onUpdateStopMessage: (callback) => ipcRenderer.on('update-stop-message', (event, message) => callback(message)),
|
||||
});
|
||||
@ -668,3 +668,28 @@
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Style for the scrolling stop message */
|
||||
.stop-message {
|
||||
background-color: red;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 2px 5px;
|
||||
margin: 0 10px;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
max-width: 50%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stop-message marquee {
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
/* Existing styles for other elements */
|
||||
.text-light {
|
||||
color: white !important;
|
||||
}
|
||||
/* Add other existing styles as needed */
|
||||
@ -3,19 +3,20 @@
|
||||
<div class="text-light small ps-3">
|
||||
<span class="date-time">{{ dateTime }}</span>
|
||||
</div>
|
||||
<div class="flex-grow-1 text-center">
|
||||
<div *ngIf="stopMessage" class="stop-message">
|
||||
<marquee behavior="scroll" direction="left" scrollamount="5">{{ stopMessage }}</marquee>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="d-flex align-items-center text-light small justify-content-end flex-grow-1"
|
||||
>
|
||||
|
||||
<span class="btno-label btno-space">
|
||||
<b>Operator:</b> {{ userName }}
|
||||
</span>
|
||||
|
||||
<span class="btno-label me-3">
|
||||
<b>B.T.No: </b> {{ btid }}
|
||||
</span>
|
||||
|
||||
|
||||
<div
|
||||
class="status_box text-center"
|
||||
[ngClass]="liveStatusOk ? 'live-green' : 'live-red'"
|
||||
@ -106,7 +107,6 @@
|
||||
>
|
||||
Race No. | {{ selectedRace }}
|
||||
</button>
|
||||
|
||||
<div class="d-flex flex-column text-end">
|
||||
<div
|
||||
class="nav-button w-100 text-end pe-3 py-2 border-top"
|
||||
@ -217,18 +217,10 @@
|
||||
</div>
|
||||
|
||||
<!-- View Log Modal -->
|
||||
<!-- <div class="viewlog-modal-overlay" *ngIf="showLogModal" (click)="closeModals()">
|
||||
<div class="viewlog-modal-box" (click)="$event.stopPropagation()">
|
||||
<h5>VIEW-LOG</h5>
|
||||
<button class="cancel-btn" (click)="closeModals()">CANCEL</button>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="viewlog-modal-overlay" *ngIf="showLogModal" (click)="closeModals()">
|
||||
<div class="viewlog-modal-box" (click)="$event.stopPropagation()">
|
||||
|
||||
<!-- Modal Header -->
|
||||
<h3 class="modal-title">VIEW-LOG</h3>
|
||||
|
||||
<!-- Scrollable Table Area -->
|
||||
<div class="table-container">
|
||||
<table class="view-log-table">
|
||||
@ -240,7 +232,7 @@
|
||||
<th>Tickets</th>
|
||||
<th>Count</th>
|
||||
<th>Amount</th>
|
||||
<th>Barcode</th> <!-- ✅ New Column -->
|
||||
<th>Barcode</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -256,7 +248,6 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- CANCEL Button at Bottom -->
|
||||
<div class="modal-footer">
|
||||
<button class="cancel-btn" (click)="closeModals()">CANCEL</button>
|
||||
@ -264,48 +255,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div
|
||||
class="viewlog-modal-overlay"
|
||||
*ngIf="showLogModal"
|
||||
(click)="closeModals()"
|
||||
>
|
||||
<div class="viewlog-modal-box" (click)="$event.stopPropagation()">
|
||||
<h5 class="modal-title text-center">VIEW-LOG</h5>
|
||||
|
||||
<div class="modal-body" style="max-height: 400px; overflow-y: auto;">
|
||||
<ng-container *ngIf="formattedTicketLogs.length > 0; else noLogs">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="text-align: left;">
|
||||
<th>Pool</th>
|
||||
<th>Numbers</th>
|
||||
<th>×Count</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let log of formattedTicketLogs">
|
||||
<td>{{ log.label }}</td>
|
||||
<td>{{ log.numbers.join(',') }}</td>
|
||||
<td>*{{ log.count }}</td>
|
||||
<td>₹ {{ log.amount }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</ng-container>
|
||||
|
||||
<ng-template #noLogs>
|
||||
<p>No tickets found.</p>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer text-center mt-3">
|
||||
<button class="cancel-btn" (click)="closeModals()">CANCEL</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
|
||||
<!-- Venue Modal -->
|
||||
<div class="modal-overlay" *ngIf="showVenueModal" (click)="closeModals()">
|
||||
<div class="modal-box" (click)="$event.stopPropagation()">
|
||||
|
||||
@ -44,7 +44,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
|
||||
selectedRaceId: number = 0;
|
||||
enabledHorseNumbers: number[] = [];
|
||||
multiLegBaseRaceIdx: number = 0;
|
||||
multiLegBaseRaceIdx = 0;
|
||||
currentPool: string | null = null;
|
||||
|
||||
private prevEnabledKey = '';
|
||||
@ -75,6 +75,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
private currentDate: string = '';
|
||||
private eventSource: EventSource | undefined;
|
||||
|
||||
stopMessage: string = '';
|
||||
private stopMessageTimeout: number | null = null;
|
||||
|
||||
formattedTicketLogs: {
|
||||
pool: string;
|
||||
horses: string;
|
||||
@ -108,7 +111,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
// === Load structuredRaceCard from rpinfo if available (prefer structured) ===
|
||||
// Load structuredRaceCard from rpinfo if available (prefer structured)
|
||||
const rpCached = localStorage.getItem('rpinfo');
|
||||
if (rpCached) {
|
||||
try {
|
||||
@ -120,7 +123,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
this.raceCardData = {};
|
||||
}
|
||||
} else {
|
||||
// fallback to older raceCardData key if present (non-structured)
|
||||
// Fallback to older raceCardData key if present (non-structured)
|
||||
const fallback = localStorage.getItem('raceCardData');
|
||||
if (fallback) {
|
||||
try {
|
||||
@ -155,10 +158,51 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
const statuses$ = this.stopbetService.getStopbetStatuses();
|
||||
if (statuses$ && typeof statuses$.subscribe === 'function') {
|
||||
this.stopbetSubscription = statuses$.subscribe((statuses: Map<number, string>) => {
|
||||
this.stopbetStatuses.clear();
|
||||
statuses.forEach((value, key) => {
|
||||
this.stopbetStatuses.set(key, value);
|
||||
const newStatuses = new Map(statuses);
|
||||
const newStops: number[] = [];
|
||||
|
||||
// Detect newly stopped races
|
||||
newStatuses.forEach((value, key) => {
|
||||
const prev = this.stopbetStatuses.get(key);
|
||||
if (prev === 'N' || prev === undefined) {
|
||||
if (value === 'Y' || value === 'S') {
|
||||
newStops.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.stopbetStatuses = newStatuses;
|
||||
|
||||
// If there are new stops, display the message for 30 seconds
|
||||
if (newStops.length > 0) {
|
||||
newStops.sort((a, b) => a - b);
|
||||
this.stopMessage = newStops.map(r => `Race ${r} Stopped`).join(' - ');
|
||||
// Sync new stop message to electron main if available
|
||||
if ((window as any).electronAPI) {
|
||||
try {
|
||||
(window as any).electronAPI.syncStopMessage(this.stopMessage);
|
||||
} catch (e) {
|
||||
console.warn('[NAVBAR] electronAPI.syncStopMessage failed:', e);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stopMessageTimeout) {
|
||||
clearTimeout(this.stopMessageTimeout);
|
||||
}
|
||||
this.stopMessageTimeout = window.setTimeout(() => {
|
||||
this.stopMessage = '';
|
||||
this.stopMessageTimeout = null;
|
||||
// Sync cleared message to electron main if available
|
||||
if ((window as any).electronAPI) {
|
||||
try {
|
||||
(window as any).electronAPI.syncStopMessage(this.stopMessage);
|
||||
} catch (e) {
|
||||
console.warn('[NAVBAR] electronAPI.syncStopMessage (clear) failed:', e);
|
||||
}
|
||||
}
|
||||
}, 50000);
|
||||
}
|
||||
|
||||
console.log('[NAVBAR] Updated stopbetStatuses:', Object.fromEntries(this.stopbetStatuses));
|
||||
this.checkAndSwitchIfStopped();
|
||||
this.updateEnabledHorseNumbers();
|
||||
@ -432,7 +476,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
openVenueModal() {
|
||||
// Prefer structuredRaceCard loaded at init (rpinfo). If empty, try legacy key.
|
||||
if (!this.raceCardData || Object.keys(this.raceCardData).length === 0) {
|
||||
const cachedData = localStorage.getItem('raceCardData');
|
||||
if (cachedData) {
|
||||
@ -447,7 +490,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
// Use lowercase 'venue' as structuredRaceCard uses .venue
|
||||
this.selectedVenue = this.raceCardData?.venue ?? this.raceCardData?.Venue ?? 'Select Venue';
|
||||
this.updateEnabledHorseNumbers();
|
||||
this.showVenueModal = true;
|
||||
@ -500,7 +542,6 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
selectRace(race: number) {
|
||||
// Adjust race if the attempted one is stopped
|
||||
const adjustedRace = this.isOpen(race) ? race : this.getOpenRaceStartingFrom(race);
|
||||
|
||||
this.selectedRace = adjustedRace;
|
||||
@ -513,11 +554,9 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
value: this.selectedRace,
|
||||
});
|
||||
|
||||
// Use this.raceCardData (structured) rather than re-parsing localStorage
|
||||
const raceList = this.raceCardData?.raceVenueRaces?.races ?? [];
|
||||
const selectedRaceEntry = raceList[this.selectedRace - 1] ?? [];
|
||||
|
||||
// Determine runnerCount defensively based on possible shapes
|
||||
let runnerCount = 12;
|
||||
if (Array.isArray(selectedRaceEntry)) {
|
||||
runnerCount = selectedRaceEntry.length || 12;
|
||||
@ -573,22 +612,18 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
displayBarcode = '';
|
||||
|
||||
if (rawLabel) {
|
||||
// 1️⃣ Extract pool (first word)
|
||||
const parts = rawLabel.split(/\s+/);
|
||||
pool = parts[0];
|
||||
|
||||
// 2️⃣ Extract ticket count (*n) & price if exists
|
||||
const countMatch = rawLabel.match(/\*(\d+)(?:\s+(Rs\s*\d+))?/);
|
||||
if (countMatch) {
|
||||
ticketCountLabel = `*${countMatch[1]}`;
|
||||
price = countMatch[2] || '';
|
||||
}
|
||||
|
||||
// 3️⃣ Extract horses part (between pool name & ticket count)
|
||||
const horsesPartMatch = rawLabel.match(/^\w+\s+(.+?)\s+\*\d+/);
|
||||
if (horsesPartMatch) {
|
||||
horses = horsesPartMatch[1].trim();
|
||||
// Special pools split into races
|
||||
if (['MJP', 'JKP', 'TRE'].includes(pool)) {
|
||||
horsesArray = horses.split('/').map((r) => r.trim().split(',').map((h) => h.trim()));
|
||||
} else {
|
||||
@ -627,7 +662,7 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
logout(): void {
|
||||
const name = localStorage.getItem('userName') || 'Unknown User';
|
||||
const employeeId = localStorage.getItem('employeeId') || '000000';
|
||||
const stopbetData = localStorage.getItem('stopbetStatuses'); // Save stopbetStatuses
|
||||
const stopbetData = localStorage.getItem('stopbetStatuses');
|
||||
|
||||
const printData = {
|
||||
name,
|
||||
@ -675,9 +710,11 @@ export class NavbarComponent implements OnInit, OnDestroy {
|
||||
this.eventSource.close();
|
||||
} catch {}
|
||||
}
|
||||
if (this.stopMessageTimeout) {
|
||||
clearTimeout(this.stopMessageTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
// Add trackByHorse for use in *ngFor
|
||||
trackByHorse(index: number, item: number): number {
|
||||
return item;
|
||||
}
|
||||
|
||||
@ -98,14 +98,14 @@ div[style*="background-color: black"] .custom-cell {
|
||||
/* === Footer Always at Bottom === */
|
||||
.footer {
|
||||
height: 10vh;
|
||||
background-color: #1c2c46a8;
|
||||
color: white;
|
||||
background-color: #fafbfca2;
|
||||
color: rgb(126, 0, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 15%;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
font-size: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
@ -160,3 +160,21 @@ div[style*="background-color: black"] .custom-cell {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add these new styles for scrolling */
|
||||
.live-data-text.scrolling {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
animation: scroll 20s linear infinite; /* Adjust duration for speed */
|
||||
width: 55%; /* Ensure it takes full width */
|
||||
}
|
||||
|
||||
@keyframes scroll {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
@ -44,9 +44,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer" [ngStyle]="{ 'background-color': isConnected ? '#d1ffd1' : '#ffcccc' }">
|
||||
<div class="live-data-text">
|
||||
Live Data: {{ message || 'Disconnected' }}
|
||||
<!-- Updated footer: Conditional message, background, and scrolling class -->
|
||||
<footer class="footer" >
|
||||
<div class="live-data-text" [class.scrolling]="!!stopMessage">
|
||||
{{ stopMessage ? ('Races Stopped: ' + stopMessage) : (message ? ('Live Data: ' + message) : 'Hello everyone!') }}
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
@ -1,4 +1,3 @@
|
||||
// shared-table.component.ts
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SelectionData } from '../selection.service/selection.service';
|
||||
@ -19,18 +18,28 @@ export class SharedTableComponent implements OnInit {
|
||||
|
||||
message = '';
|
||||
isConnected = false;
|
||||
// Add this new property
|
||||
stopMessage: string = '';
|
||||
|
||||
constructor(private websocketService: WebsocketService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.websocketService.message$.subscribe((msg) => {
|
||||
this.message = msg;
|
||||
console.log('[SHARED TABLE] WebSocket message:', msg);
|
||||
});
|
||||
// this.websocketService.message$.subscribe((msg) => {
|
||||
// this.message = msg;
|
||||
// console.log('[SHARED TABLE] WebSocket message:', msg);
|
||||
// });
|
||||
|
||||
this.websocketService.isConnected$.subscribe((status) => {
|
||||
this.isConnected = status;
|
||||
console.log('[SHARED TABLE] WebSocket connection status:', status);
|
||||
// this.websocketService.isConnected$.subscribe((status) => {
|
||||
// this.isConnected = status;
|
||||
// console.log('[SHARED TABLE] WebSocket connection status:', status);
|
||||
// });
|
||||
|
||||
// Add this: Listen for stop message updates
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.onUpdateStopMessage((message: string) => {
|
||||
this.stopMessage = message;
|
||||
console.log('[SHARED TABLE] Received stop message:', message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
4
btc-UI/src/electron.d.ts
vendored
4
btc-UI/src/electron.d.ts
vendored
@ -1,10 +1,12 @@
|
||||
// src/electron.d.ts
|
||||
interface ElectronAPI {
|
||||
openSecondScreen: () => void;
|
||||
closeSecondScreen: () => void;
|
||||
getBtid: () => Promise<string | null>;
|
||||
syncSharedData: (data: SharedData) => void;
|
||||
onUpdateSharedData: (callback: (data: SharedData) => void) => void;
|
||||
// Add these new lines
|
||||
syncStopMessage: (message: string) => void;
|
||||
onUpdateStopMessage: (callback: (message: string) => void) => void;
|
||||
}
|
||||
|
||||
interface SharedData {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user