App State Patch
src/shared/interfaces/supervisor-stream.interface.ts
Контракт payload-у, який Electron шле тімліду через канал UPDATE_APP_STATE. Самозаживляючий частковий апдейт — будь-яка секція опційна, будь-яке поле всередині секції опційне.
Структура
interface IAppStatePatch {
ladies?: SectionDelta<ILadyReact, number>;
favorites?: SectionDelta<IFavorite, string>;
tasks?: SectionDelta<IRunnerTask, string>;
dialogs?: IDialogsPatch;
streams?: IStreamsPatch;
sleepMode?: boolean;
}
interface SectionDelta<T, IdT> {
updated?: T[];
removed?: IdT[];
}Семантика секцій
| Секція | Тип | Семантика | id-поле |
|---|---|---|---|
ladies | SectionDelta | upsert-дельти ТЮ | ladyId_api (number) |
favorites | SectionDelta | upsert-дельти favorite-ів (через diff vs snapshot) | favoriteId (Mongo _id, string) |
tasks | SectionDelta | Заміна повного списку через updated: [...все] | id (string) |
dialogs | object | Ping події про активність у чаті / маїлі / email | — |
streams | object | Знімок поточного стану камер (snapshot-replace) | — |
sleepMode | boolean | Поточний стан Sleep Mode оператора (snapshot-replace) | — |
dialogs — пінги активності
interface IDialogsPatch {
chat?: Array<{ ladyId_api: number; manId_api: number }>; // вхідне або вихідне chat-повідомлення
mail?: Array<{ ladyId_api: number; manId_api: number }>; // вихідний лист
email?: Array<{ ladyId_api: number; manId_api: number }>; // вихідний email
}Без даних — тільки сигнал: «в цьому діалозі є зміни». Frontend якщо відображає той самий діалог — рефрешить його через існуючий механізм. Якщо ні — може показати індикатор/бейдж.
streams — стан камер
interface IStreamsPatch {
manStream: number[]; // manId_api що зараз з власним стрімом
manWatching: Array<{ ladyId_api: number; manId_api: number }>; // хто дивиться нашу ТЮ
}Snapshot-replace: коли приходить — frontend заміняє свій локальний стан стрімів повністю на те що в payload. Не дельти, а повний знімок.
sleepMode — стан паузи оператора
sleepMode?: boolean; // true — оператор у Sleep Mode, false — активнийSnapshot-replace: якщо поле є в payload — frontend замінює свій стан на це значення. Якщо нема — не чіпає. Initial snapshot завжди містить поточне значення.
Frontend reducer
Для всіх трьох CRUD-секцій (ladies, favorites, tasks):
| Операція | Дія на стороні supervisor frontend |
|---|---|
updated | Upsert by id — додати якщо нема, замінити якщо є |
removed | Видалити з store за id |
Семантика updated — «ось стан цієї сутності зараз». Якщо id раніше не було — додаємо, якщо був — замінюємо.
Для dialogs — frontend дивиться чи відкритий той діалог; якщо так — рефрешить дані з backend-у через існуючий ендпоінт. Якщо ні — може просто підсвітити в списку.
Для streams і sleepMode — frontend заміняє локальний state повністю на те що в payload.
Обгортка на wire
IAppStatePatch стискається через compressData (gzip + base64) і кладеться в IClientCommand.data:
interface IClientCommand {
messageId: string;
event: ClientCommand; // завжди 'updateAppState' для цього потоку
data: string; // compressed IAppStatePatch
favoritesHaveContacts?: boolean;
}favoritesHaveContacts — флаг для оптимізації на голдені. Виставляється true якщо в favorites.updated є хоч один fav з contactDetails?.status === 'Yes'. Голден читає прапор:
false→ проксі-ить compressed payload без розпаковкиtrue→ розпаковує, прокачує favorites через email-enrichment, пакує назад
Деталі — у favoritesHaveContacts флаг.
Чому одне UPDATE_APP_STATE event для всього
Раніше було два events — APP_STATE (повний snapshot на старті спостереження) і UPDATE_APP_STATE (дельти). Об’єднали в один → frontend має один reducer, initial snapshot — це просто {section: {updated: [...все]}}.
Прибрали асиметрію між форматами і одне місце входу.
Зв’язки
- Шле: SupervisorStreamService
- Канал: StackSocket (event
UPDATE_APP_STATE) - Споживає: supervisor frontend (через golden workspace-проксі)
- Enrichment favorites з контактами — на голдені, активується через
favoritesHaveContactsфлаг