Загрузка данных
import { AxiosResponse } from 'axios';
import axios, { axiosInstanceTus } from '@api/axios';
import type { ResponseStatusCodes } from '@api/models/responseStatusCodes';
import type { PartialWithRequired } from 'types/utilityTypes';
import type { Widget, WidgetPackedProps } from 'types/Widgets';
interface TerminalResponse {
headers: Record<string, never>;
statusCode: 'OK';
statusCodeValue: ResponseStatusCodes.OK;
}
export const widgetPropertiesController = {
create(data: WidgetPackedProps[]): Promise<AxiosResponse<TerminalResponse>> {
return axiosInstanceTus.post('/api/v1/widgetproperty/save-list', data);
},
read(data: Widget['id'][]): Promise<AxiosResponse<WidgetPackedProps[]>> {
return axiosInstanceTus.post('/api/v1/widgetproperty/get-list', data);
},
update(data: PartialWithRequired<WidgetPackedProps, 'widgetId'>): Promise<AxiosResponse<WidgetPackedProps[]>> {
return axiosInstanceTus.put('/api/v1/widgetproperty', data);
},
delete(data: Pick<Widget, 'id'>): Promise<AxiosResponse<undefined>> {
return axiosInstanceTus.delete('/api/v1/widgetproperty', { data });
},
};
import axios, { axiosInstanceTus } from '../axios';
import type { AxiosResponse } from 'axios';
import type { Widget } from 'types/Widgets';
import type { Workspace } from 'types/Workspace';
type WidgetCreateArgs = Pick<Widget, 'name' | 'workspaceId'>;
type WidgetCreateResponse = WidgetCreateArgs & Pick<Widget, 'id'>;
export const widgetsController = {
async create(data: WidgetCreateArgs[]): Promise<AxiosResponse<WidgetCreateResponse[]>> {
return axiosInstanceTus.post('/api/v1/widgets/save-list', data);
},
async update(data: WidgetCreateResponse): Promise<AxiosResponse<undefined>> {
return axiosInstanceTus.put('/api/v1/widgets', data);
},
async read(ids: Workspace['id'][]): Promise<AxiosResponse<Record<number, WidgetCreateResponse[]>>> {
return axiosInstanceTus.post('/api/v1/widgets/get-list', ids);
},
async delete(item: Widget): Promise<AxiosResponse<undefined>> {
return axiosInstanceTus.delete(`/api/v1/widgets/${(item as Widget).id}`);
},
};
import dayjs, { type Dayjs } from 'dayjs';
import { feedbackController } from '@api/controllers/feedbackController';
import { commonDateFormat } from '@configs/standartDateFormat';
import { getStore } from '@store/store';
import { AboutInstrumentTradingResultType } from 'types/AboutInstrument';
import { Candle } from 'types/Candles';
import { ResponseChatByPlacementId } from 'types/ResponseChatIdByPlacement';
import axios, {
axiosCurves,
axiosIndicative,
axiosInstanceFormalization,
axiosInstanceNews,
axiosInstanceNTBAnalytics,
axiosInstanceNTBTrading,
axiosInstanceNTPro,
axiosInstanceSPFI,
axiosInstanceTD,
axiosInstanceTMD,
axiosInstanceTus,
uninterceptedAxiosInstance,
} from './axios';
import { abbrevationsController } from './controllers/abbrevationsController';
import { candlesController } from './controllers/candles';
import { frontExceptionController } from './controllers/frontExceptionController';
import { newsSettingsController } from './controllers/newsController';
import { noTradeChatController } from './controllers/noTradeChatController';
import { tradeChatController } from './controllers/tradeChatController';
import { widgetPropertiesController } from './controllers/widgetProperties';
import { widgetsController } from './controllers/widgets';
import { workspaceController } from './controllers/workspace';
import type { ResponseSearchSmartType } from '@components/InputSearchCommand/type/type';
import type { Contract } from '@modules/contracts';
import type { BoardType, Counterparty } from '@widgets/AboutInstrument/types';
import type { QuotesNTProTypeServer } from '@widgets/QuotesNTPro/type';
import type { AxiosRequestConfig, AxiosResponse } from 'axios';
import type { IChatFolder, IChatMessages, ICreateChatFolder, IUpdateChatFolder } from 'types/Chats';
import type {
CurveValue,
IndicativeCurveFilters,
IndicativeCurveType,
IssCurveFilters,
IssCurveGroup,
IssCurveType,
ZeroCouponCurveLiveRequestParams,
ZeroCouponCurveLiveResponse,
} from 'types/Curves';
import type { CustomerDataType } from 'types/Customers';
import type { Firm } from 'types/Firm';
import type { FixingData } from 'types/FixingData';
import type { Instrument } from 'types/Instruments';
import type { INewsFeed, INewsItem } from 'types/News';
// import type { NSDCostType } from 'types/NSDCost';
import type { Payment } from 'types/Payment';
import type { QuotesType } from 'types/Quotes';
import type { GetBarsParams } from 'types/Requests/BarsTypes';
import type { SittingCB } from 'types/SittingCB';
import type { User, UserSettings } from 'types/User';
const api = {
getBars({ currencyPair, interval, date, count }: GetBarsParams) {
return axiosInstanceTD.get<Candle[]>(
`/api/v1/candles/list/offset?secId=${currencyPair}&date=${date}&interval=${interval}&count=${count}`,
);
},
getQuotes() {
return axios.post('api/v1/blotters/quotes', {
timestamp: -1,
keys: [
'AIX:MAIN:BAST',
'AIX:MAIN:Bond',
'AIX:MAIN:0000000000000',
'AIX:MAIN:aix123',
'AIX:AOTC:EQOTCa',
'AIX:MAIN:TickEQ1',
'AIX:MAIN:COVID19',
'AIX:MAIN:HSBK',
'AIX:MAIN:Forte_AIX',
'AIX:MAIN:Tatn_AIX',
'AIX:MAIN:170920201635',
'INT:IUSA:123987',
'INT:IUSA:HSBK_priv_IUSA',
'INT:IUSA:AAPL',
'INT:IUSA:aaa8888811122',
'INT:IUSA:aaa1',
'INT:IUSA:171107122020',
'KSE:TQBR:RAHT',
'KSE:EQBR:HSBKp',
'KSE:TQBR:KZTK',
'KSE:TQBR:KZAP',
'KSE:TQBR:HSBK',
'KSE:TQBR:hydro10',
'INT:ILSE:KAZ',
'INT:IOTC:EQOTCI',
'INT:ILSE:tick_test1201',
'INT:IOTC:test_IOTS1201',
'INT:ILSE:tick_009',
'INT:ILSE:13360812',
'INT:ILSE:Forte_LSE',
'INT:IUSA:Test666',
],
});
},
getMarketDepth(key: Contract['key']) {
return axiosInstanceTMD.post('/api/v1/blotters/marketdepths', {
key,
timestamp: -1,
});
},
workspaceController,
widgetsController,
widgetPropertiesController,
frontExceptionController,
candlesController,
newsSettingsController,
getChatCustomers(market?: string, spfiTradingPermission?: boolean) {
const { isIframeView } = getStore().getState().coreSlice;
if (isIframeView) {
return axiosInstanceFormalization.get<CustomerDataType[]>('/api/v1/data/cache/customers')
}
return axios.get<CustomerDataType[]>('/api/v1/data/cache/customers', { params: { market, spfiTradingPermission } });
},
/* Bookbuilder: получение customers для конкретного id размещения */
getChatCustomersByPlacement(placementId: string) {
return axiosInstanceFormalization.get<CustomerDataType[]>(`/api/v1/data/cache/customers/placement/${placementId}`);
},
getCounterparties() {
return axios.get<CustomerDataType[]>('/api/v1/data/customers');
},
getCounterpartiesById(id: number) {
return axios.get<Counterparty>(`/api/v1/data/customers/${id}`, {
full: true,
} as AxiosRequestConfig<{ full: boolean }>);
},
getInstrumentById(id: number): Promise<AxiosResponse<Instrument>> {
return axios.get(`/api/v1/data/instruments/${id}`);
},
getInstrumentList(): Promise<AxiosResponse<Instrument[]>> {
return axios.get('/api/v1/data/instruments?');
},
tradeChatController,
noTradeChatController,
getNewsList(dateRange: string[]) {
return axiosInstanceTD.get(`news/getList?from=${dateRange[0]}&till=${dateRange[1]}`);
},
getQuotesList() {
return axiosInstanceTD.get('quotes/');
},
getBoardByInstrumentId(id: number, floorCode: string) {
return axios.get<BoardType[]>(`/api/v1/data/boards?floorCode=${floorCode}&instrumentId=${id}`);
},
getDealsList(isTradeChat = false) {
return axios.get(`/api/v1/blotters/deals${isTradeChat ? '?isTradeChat=true' : ''}`);
},
hideTrade(talkId: number) {
return axios.put(`api/v1/blotters/deals/hide/${talkId}`);
},
getTermsDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/term');
},
getBoardsDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/board');
},
getInstrGroupDict() {
return axiosInstanceTD.get<{ dictionary: Record<string, string> }>('/api/v1/contracts/dictionary/instrGroupType');
},
getIssContracts() {
return axiosInstanceTD.get<Contract[]>('/api/v1/contracts');
},
getIndicativeContracts() {
return axiosIndicative.get<Contract[]>('/api/v1/contracts');
},
getNtProContracts() {
return axiosInstanceNTPro.get<Contract[]>('/api/v1/contracts');
},
getSapfirContracts() {
return axiosInstanceSPFI.get<Contract[]>('/api/v1/contracts');
},
getNtbAnalyticsContracts() {
return axiosInstanceNTBAnalytics.get<Contract[]>('/api/v1/contracts');
},
getNtbContracts() {
return axiosInstanceNTBTrading.get<Contract[]>('/api/v1/contracts');
},
getFixingsList(): Promise<AxiosResponse<FixingData[]>> {
return axiosInstanceTD.get('/api/fixings/');
},
getFixingById(key: string): Promise<AxiosResponse<FixingData[]>> {
return axiosInstanceTD.get(`/api/fixings?code=${key}`);
},
getQuotesByKeys(keys: string[], signal?: AbortSignal) {
return axiosInstanceTD.put<QuotesType[]>('/api/v1/quotes', keys, { signal });
},
getQuotesByIndicativeKeys(keys: string[], signal?: AbortSignal) {
return axiosIndicative.put<QuotesType[]>('/api/v1/quotes', keys, { signal });
},
getQuotesListWithParams({ skip, limit }: { skip: number; limit: number }) {
return axiosInstanceTD.get<QuotesType[]>('/api/v1/quotes/list', {
params: {
skip,
limit,
},
});
},
getTurnoversByDate(date: Date) {
const year = date.getFullYear();
const month = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1;
const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate();
const newDate = `${year}-${month}-${day}`;
return axiosInstanceTD.get(`/api/v1/turnovers?date=${newDate}`);
},
/** Кривые */
getCurveGroups() {
return axiosCurves.get<IssCurveGroup[]>('/api/v1/data/curves/groups');
},
getCurves() {
return axiosCurves.get<IssCurveType[]>('/api/v1/data/curves');
},
getCurvesFilter: (params: { groupCurveId?: number; id?: number; currencyCode?: string } = {}) =>
axiosCurves.get<IssCurveFilters>('/api/v1/data/curvesfilter', { params }),
getCurvePoints(groupId: number, curveId: number, date: Date | string) {
const newDate = date instanceof Date ? dayjs(date).format(commonDateFormat.backendDateFormat) : date;
return axiosCurves.get<CurveValue[]>(`/api/v1/data/curvepoints/${groupId}/${curveId}/${newDate}`);
},
getZeroCouponCurveLive: (params?: ZeroCouponCurveLiveRequestParams) =>
axiosInstanceTD.post<ZeroCouponCurveLiveResponse>('/api/v1/kbd/live', params),
/** Кривые индикативные котировок */
getIndicativeCurves: () => axiosIndicative.get<IndicativeCurveType[]>('/api/v1/data/curves'),
getIndicativeCurveFilter: (params: { groupCurveId?: number; curveId?: string; currencyCode?: string } = {}) =>
axiosIndicative.get<IndicativeCurveFilters>('/api/v1/data/curvesfilter', { params }),
getIndicativeCurvePoints(key: string, date: Dayjs) {
const formattedDate = date.format(commonDateFormat.backendDateFormat);
return axiosIndicative.get<CurveValue[]>(`/api/v1/data/curvepoints/${key}/${formattedDate}`);
},
getQuoteByIssKey(key: string) {
const issKeyParts = key.split(':');
return axiosInstanceTD.get<QuotesType>(
`/api/v1/quotes?secId=${issKeyParts[2]}&boardId=${issKeyParts[1]}&tradingSystem=${issKeyParts[0]}`,
);
},
getPaymentsByIsin(isin: string) {
return axiosInstanceTD.get<Payment[]>(`/api/v1/payments?isin=${isin}`);
},
getTradingResults(security: string, date: string | Dayjs, board: string) {
return axiosInstanceTD.get<AboutInstrumentTradingResultType[]>(
`api/trading-result?mode=instrument&security=${security}&till=${date}&from=${date}&board=${board}`,
);
},
getQuotesNTPro() {
return axiosInstanceNTPro.get<QuotesNTProTypeServer[]>('/quotes');
},
getIndexesBySecId(key: string) {
const issKeyParts = key.split(':');
return axios.get(`/api/v1/data/indexes/${issKeyParts[2]}`);
},
getUserPermissions(): Promise<AxiosResponse<string[]>> {
return axios.get('/api/v1/permission/user');
},
/** Получение основной информации по пользователю */
getCurrentUserInfo(): Promise<AxiosResponse<User>> {
return axios.get('api/v1/users/current');
},
updateMenuLocked(id: number, locked: boolean): Promise<AxiosResponse<User>> {
return axios.patch('api/v1/users/settings', {
user_id: id,
is_menu_locked: locked,
});
},
updateUser(user: User): Promise<AxiosResponse<User>> {
return axios.put('api/v1/users', user);
},
getCurrentUserSettings(id: number) {
return axios.get<UserSettings>(`/api/v1/notification-users/${id}`);
},
updateCurrentUserSettings(settigs: UserSettings) {
return axios.put('api/v1/notification-users', settigs);
},
getSearchSmart(search: string, signal: AbortSignal) {
return axiosInstanceTus.get<ResponseSearchSmartType>(`/api/v1/search-smart?search=${encodeURIComponent(search)}`, {
signal,
});
},
healthcheckTd(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceTD.get('/actuator/health-td');
},
healthcheckTb(): Promise<AxiosResponse<{ status: string }>> {
return axios.get('/actuator/health-tb');
},
healthcheckSPFI(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceSPFI.get('/actuator/health-spfi');
},
healthcheckForm(): Promise<AxiosResponse<{ status: string }>> {
return axiosInstanceFormalization.get('/actuator/health-form');
},
connectionCheckRequest(): Promise<AxiosResponse<{ status: string }>> {
return uninterceptedAxiosInstance.get('https://iss.moex.com/iss/sitenews/');
},
getFirms(): Promise<AxiosResponse<Firm[]>> {
return axios.get('/api/v1/data/cache/firms');
},
abbrevationsController,
feedbackController,
// Отключено в рамках TRADERADAR-12666 Отключение источника НРД
// getNSDCost() {
// return axiosInstanceTD.get<NSDCostType[]>('/api/v1/data/nsdCost');
// },
getChatFolders(): Promise<AxiosResponse<IChatFolder[]>> {
return axiosInstanceFormalization.get('/api/v1/folders');
},
createChatFolder(folderData: ICreateChatFolder): Promise<AxiosResponse<IChatFolder>> {
return axiosInstanceFormalization.post('/api/v1/folders', folderData);
},
deleteChatFolder(folderId: number): Promise<AxiosResponse> {
return axiosInstanceFormalization.delete(`/api/v1/folders/${folderId}`);
},
updateChatFolder(folderData: IUpdateChatFolder): Promise<AxiosResponse<IChatFolder>> {
return axiosInstanceFormalization.patch(`/api/v1/folders/${folderData.folderId}`, folderData);
},
updateFolderPosition(folderPosition: Record<string, number>): void {
axiosInstanceFormalization.post('/api/v1/folders/positions', folderPosition);
},
sendChatMessages(data: IChatMessages): Promise<AxiosResponse> {
return axiosInstanceFormalization.post('/api/v1/chat-messages', data);
},
getNewsItemById(newsId: string): Promise<AxiosResponse<INewsItem>> {
return axiosInstanceNews.get(`/api/v1/news/${newsId}`);
},
findNewsByText(text?: string): Promise<AxiosResponse<INewsItem[]>> {
const requestUrl = `/api/v1/news/search${text ? `?search=${encodeURIComponent(text)}` : ''}`;
return axiosInstanceNews.get(requestUrl);
},
getCombinedNews(searchParams?: string): Promise<AxiosResponse<INewsItem[]>> {
const requestUrl = searchParams ? `/api/v1/news/search?${searchParams}` : '/api/v1/news/search';
return axiosInstanceNews.get(requestUrl);
},
getNewsFeeds(): Promise<AxiosResponse<INewsFeed[]>> {
return axiosInstanceNews.get('/api/v1/newsfeeds');
},
getContractsIssInfo(): Promise<AxiosResponse<{ key: string }>> {
return axiosInstanceTD.get('/api/v1/contracts/info');
},
getSittingCB() {
return axiosInstanceTD.get<SittingCB>('/api/v1/data/sittingCB');
},
getChatIdByPlacement(placementId: string) {
return axiosInstanceFormalization.get<ResponseChatByPlacementId>(`/api/v1/chats/placement/${placementId}`);
},
};
export default api;
export type Api = typeof api;
import { AxiosResponse } from 'axios';
import chunk from 'lodash/chunk';
import { all, call, put, select } from 'typed-redux-saga';
import api from '@api/index';
import { TRADE_PERMISSION, WIDGET_PROPS_REQUEST_CHUNK_SIZE } from '@configs/appConfig';
import { permissionsSelector } from '@store/selectors/user';
import { metricsAction } from '@store/slices/core';
import { createValuesToPublicContext, PublicContextItem } from '@store/slices/publicContext';
import { addWidget } from '@store/slices/widgets';
import { addWorkspace } from '@store/slices/workspaces';
import { isRemovedWidget } from '@utils/isRemovedWidget';
import { robustRequest } from '@utils/robustRequest';
import { unpackWidgetProps } from '@utils/widgetPropsProceeders';
import { convertPercentToPx } from '@utils/widgetSizesNPositionProceeders';
import { MetricsAction } from 'types/MetricsActions';
import { WidgetPackedProps } from 'types/Widgets';
import { Workspace } from 'types/Workspace';
function* processProperties(workspacesIdList: number[]) {
const publicPropertiesResponse = yield* call(async () =>
robustRequest(() => api.widgetsController.read(workspacesIdList)),
);
const publicProperties = publicPropertiesResponse.data;
const result: PublicContextItem[] = [];
Object.values(publicProperties).forEach((elem) => {
elem.forEach((item) => {
const arrPublicProps = Object.values((item as PublicContextItem).publicProperties || []).map((obj) =>
JSON.parse(obj as unknown as string),
);
result.push({
...(item as unknown as PublicContextItem),
publicProperties: arrPublicProps,
} as PublicContextItem);
});
});
yield put(createValuesToPublicContext(result));
return Object.values(publicProperties)
.flat(1)
.map(({ id }) => id);
}
function processWidgetsData(response: AxiosResponse<WidgetPackedProps[]>[]) {
return response
.flatMap((chunkResult) => chunkResult.data)
.filter(({ properties }) => !isRemovedWidget(properties))
.map((i) => {
const unpackedWidget = unpackWidgetProps(convertPercentToPx(i));
if (!unpackedWidget.isExpand && unpackedWidget.beforeExpandParams) {
unpackedWidget.beforeExpandParams = null;
}
if (!unpackedWidget.widgetContentProps) {
unpackedWidget.widgetContentProps = {};
}
return unpackedWidget;
})
}
export function* fullWorkspacesUploadSaga() {
const permissions = yield* select(permissionsSelector);
const isCanTrade = permissions.includes(TRADE_PERMISSION);
const workspacesShortInfoResponse = yield* call(() => robustRequest(api.workspaceController.read));
yield put(metricsAction(MetricsAction.LOADING_SCREEN_TIME_WORKSPACES));
const workspaces: Workspace[] = workspacesShortInfoResponse.data;
workspaces
.sort((a, b) => a.position - b.position)
.map(({ saved, pinned, code, ...workspace }) => ({
saved: saved || false,
pinned: pinned || false,
code: code ?? null,
...workspace,
}));
if (workspaces.length > 0) {
let workspacesForStore = workspaces;
if (!isCanTrade) {
workspacesForStore = workspaces.filter(({ name }) => name !== 'Переговоры');
}
yield put(addWorkspace(workspacesForStore));
} else {
return;
}
const workspacesIdList = workspaces.map(({ id }) => id);
const widgetIdList = yield* processProperties(workspacesIdList);
// Получение подробной информации по виджетам
// Предположительно можно выполнить параллельно с запросом на проперти
const widgetIdChunks = chunk(widgetIdList, WIDGET_PROPS_REQUEST_CHUNK_SIZE);
const widgetsData = yield* all(
widgetIdChunks.map((v) => call(() => robustRequest(() => api.widgetPropertiesController.read(v)))),
);
const widgets = processWidgetsData(widgetsData);
if (widgets.length > 0) {
yield put(addWidget(widgets));
}
}
/* eslint-disable no-await-in-loop */
import { AxiosError } from 'axios';
const ATTEMPTS = 10;
/** Функция вызывающая передаваемую функцию несколько раз в случае исключения
* @param f - Функция которую необходимо выполнить
* @param axiosValidErrorCodes - массив значений кодов ошибок после которых не нужно кидать ошибку
*/
export async function robustRequest<T>(f: () => Promise<T>, axiosValidErrorCodes?: number[]): Promise<T> {
let cnt = ATTEMPTS - 1;
let timeoutMs = 100;
while (cnt > 0) {
try {
const result = await f();
return result;
} catch (error) {
if (axiosValidErrorCodes) {
const axiosError = error as AxiosError;
if (axiosError.status && axiosValidErrorCodes.includes(axiosError.status)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- прокидываем ошибку axios
throw new Error(error as any)
}
}
console.log(error)
}
// eslint-disable-next-line no-loop-func -- gg
await new Promise((r) => {
setTimeout(() => r(1), timeoutMs);
});
timeoutMs *= 2;
cnt -= 1;
}
throw new Error();
}
import { all, call, fork, join, put, select } from 'typed-redux-saga';
import { instrumentListsController } from '@api/controllers/instrumentLists';
import { profileController } from '@api/controllers/profileController';
import api from '@api/index';
import { getIsSpfiTrader } from '@api/utils/getIsSpfiTrader';
import { wsEndpointStompClient } from '@api/websokets/classes/WSEndpointStompClient';
import { systemNotificationController } from '@api/websokets/systemNotification/systemNotificationController';
import { initStorageData } from '@store/sagas/initStorageData';
// import { addNSDCost } from '@store/slices/cashedData';
import { desktopCriticalDataReceived, metricsAction } from '@store/slices/core';
import { setFirms } from '@store/slices/firms';
import { setIntrumentLists } from '@store/slices/instrumentLists';
import { setAvatarUrl, setPersonalData, setPrivacySettings } from '@store/slices/user';
import { getStore, RootState } from '@store/store';
import { MetricsAction } from 'types/MetricsActions';
import { contractsRequestSaga } from '../basic/contracts';
import { newsInitSaga } from '../basic/news';
import { chatsCacheSyncSaga } from '../chats/initChat';
import { customersAndChatsSaga } from '../customersAndChats';
import { initChatAvatars } from '../initAvatars';
import { initSurveys } from '../surveys/init';
import { requestAndPut } from '../utils';
import { fullWorkspacesUploadSaga } from '../workspaces';
/** В этот момент пропадает экран загрузки и пользователь видит терминал */
export function* backgroundDesktopReceiverSaga() {
yield* fork(async () => {
try {
const res = await profileController.getAvatar();
getStore().dispatch(setAvatarUrl(res.data ? URL.createObjectURL(res.data) : null));
} catch {
getStore().dispatch(setAvatarUrl(null));
}
});
yield* fork(newsInitSaga);
yield* fork(initChatAvatars);
yield* fork(initStorageData);
yield* fork(chatsCacheSyncSaga);
yield* fork(initSurveys);
}
export function* criticalDesktopReceiverSaga() {
if (yield* select((state: RootState) => state.coreSlice.desktopCriticalDataReceived)) {
return;
}
// Активируем соединение для большого количества виджетов
wsEndpointStompClient.activate();
// Активация контроллера системных уведомлений
const isSpfiTrader = yield* select(getIsSpfiTrader);
systemNotificationController.activate(isSpfiTrader);
const customersAndChatsFork = yield* fork(customersAndChatsSaga);
const workspacesFork = yield* fork(fullWorkspacesUploadSaga);
// eslint-disable-next-line func-names -- в сагах нормально
const contractsAndTradeFork = yield* fork(function* () {
yield call(contractsRequestSaga);
});
// eslint-disable-next-line func-names -- в сагах нормально
const massRequestsFork = yield* fork(function* () {
yield all([
// Нужно в виджете контакты, входящий звонок, список переговоров
requestAndPut(setFirms, api.getFirms, MetricsAction.LOADING_SCREEN_TIME_CATA_CACHE_FIRMS),
// Отключено в рамках TRADERADAR-12666 Отключение источника НРД
// requestAndPut(addNSDCost, api.getNSDCost, MetricsAction.LOADING_SCREEN_TIME_DATA_NSDCOST),
requestAndPut(
setIntrumentLists,
instrumentListsController.getInstrumentLists,
MetricsAction.LOADING_SCREEN_TIME_LISTINSTRUMENTS,
),
// TODO: убрать отсюда когда появится статус загрузки
requestAndPut(
setPersonalData,
profileController.getPersonalData,
MetricsAction.LOADING_SCREEN_TIME_USERS_PERSONAL_DATA,
),
// TODO: убрать отсюда когда появится статус загрузки
requestAndPut(
setPrivacySettings,
profileController.getPrivacySettings,
MetricsAction.LOADING_SCREEN_TIME_USERS_PRIVACY_SETTINGS,
),
]);
});
yield* join([customersAndChatsFork, workspacesFork, contractsAndTradeFork, massRequestsFork]);
yield put(metricsAction(MetricsAction.LOADING_SCREEN_TIME_END));
yield put(desktopCriticalDataReceived());
yield* call(backgroundDesktopReceiverSaga);
}
import { all, takeEvery, takeLeading } from 'typed-redux-saga';
import { requestLastReceivedMessages } from '@store/actions/chats';
import {
initMetrics,
initNotifications,
metricsAction,
metricTimer,
requestAppCriticalData,
requestDesktopCriticalData,
requestMobileCriticalData,
} from '@store/slices/core';
import { initLastMessagesSaga } from '../chats/initChat';
import { initMetricsSaga, sendMetricsSaga, sendTimerMetricAction } from '../metrics';
import { initNotificationsSaga } from '../notifications';
import { criticalDesktopReceiverSaga } from './desktop';
import { criticalMobileReceiverSaga } from './mobile';
import { criticalTerminalDataReceiverSaga } from './terminal';
export function* coreSagas() {
yield all([
takeLeading(initMetrics, initMetricsSaga),
takeEvery(metricsAction, sendMetricsSaga),
takeEvery(metricTimer, sendTimerMetricAction),
takeLeading(requestAppCriticalData, criticalTerminalDataReceiverSaga),
takeLeading(requestMobileCriticalData, criticalMobileReceiverSaga),
takeLeading(requestDesktopCriticalData, criticalDesktopReceiverSaga),
takeLeading(initNotifications, initNotificationsSaga),
takeLeading(requestLastReceivedMessages, initLastMessagesSaga),
]);
}
import { all } from 'redux-saga/effects';
import { watchCommission } from '@modules/MXT/shared/commission/model/saga';
import { watchLimitEstimation } from '@modules/MXT/shared/limitEstimation/model/saga';
import { watchPriceRange } from '@modules/MXT/shared/priceRange/model/saga';
import { mxtSagas } from '@store/sagas/mxt';
import { watchAddressDepositForm } from './addressDepositForm';
import { watchAddressDepositWithdrawalForm } from './addressDepositWithdrawalForm';
import { authSaga } from './auth';
import avatarsSagas from './avatars';
import { chatsSaga } from './chats';
import { coreSagas } from './core';
import { curvesSaga } from './curves';
import { customersSaga } from './customers';
import { watchDepositForm } from './depositForm';
import { watchDepositWithdrawalForm } from './depositWithdrawalForm';
import { notificationsSaga } from './notifications';
import { oidSaga } from './oids';
import { profileSaga } from './profile';
import { surveysSaga } from './surveys';
import { tradeJournalSaga } from './tradeJournal';
export default function* rootSaga() {
yield all([
coreSagas(),
avatarsSagas(),
profileSaga(),
chatsSaga(),
authSaga(),
notificationsSaga(),
customersSaga(),
watchAddressDepositForm(),
watchAddressDepositWithdrawalForm(),
watchDepositWithdrawalForm(),
watchDepositForm(),
watchCommission(),
watchLimitEstimation(),
watchPriceRange(),
tradeJournalSaga(),
oidSaga(),
curvesSaga(),
mxtSagas(),
surveysSaga(),
]);
}
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
// Нужно так для мока саг в тестах
import commissionReducer from '@modules/MXT/shared/commission/model/slice';
import limitEstimationReducer from '@modules/MXT/shared/limitEstimation/model/slice';
import priceRangeReducer from '@modules/MXT/shared/priceRange/model/slice';
import rootSaga from '@store/sagas';
import { chatSliceMiddleware } from './middlewares/chatSliceMiddleware';
import addressDepositForm from './slices/addressDepositForm';
import addressDepositWithdrawalForm from './slices/addressDepositWithdrawalForm';
import alertsSlice from './slices/alerts';
import auth from './slices/auth';
import cashedData from './slices/cashedData';
import chatDataByPlacementId from './slices/chatDataByPlacementId';
import chatSlice from './slices/chatSlice';
import coreSlice from './slices/core';
import curvesSlice from './slices/curves';
import depositForm from './slices/depositForm';
import depositWithdrawalForm from './slices/depositWithdrawalForm';
import firmsSlice from './slices/firms';
import instrumentLists from './slices/instrumentLists';
import issuersSlice from './slices/issuers';
import localisation from './slices/localisation';
import modalsSlice from './slices/modals';
import mxtSlice from './slices/mxt';
import newsSettings from './slices/newsSettings';
import newsSlice from './slices/newsSlice';
import ntbLogisticSlice from './slices/ntb/logistic';
import ntbOrdersAndDealsSlice from './slices/ntb/ordersAndDeals';
import ntbPositionsSlice from './slices/ntb/positions';
import orderbookSlice from './slices/orderbook';
import ordersJournalSlice from './slices/ordersJournalSlice';
import publicContext from './slices/publicContext';
import rcauth from './slices/rcauth';
import requestStatus from './slices/requestStatus';
import screenerSlice from './slices/screener';
import searchNews from './slices/searchNews';
import sidebarConfig from './slices/sidebarConfig';
import surveys from './slices/surveys';
import tourSlice from './slices/tour';
import tradeJournalSlice from './slices/tradeJournal';
import userSlice from './slices/user';
import widgets from './slices/widgets';
import workspaces from './slices/workspaces';
const sagaMiddleware = createSagaMiddleware();
const rootReducer = combineReducers({
coreSlice,
publicContext,
workspaces,
localisation,
widgets,
rcauth,
depositForm,
addressDepositForm,
addressDepositWithdrawalForm,
depositWithdrawalForm,
formCommission: commissionReducer,
formLimitEstimation: limitEstimationReducer,
formPriceRange: priceRangeReducer,
cashedData,
curvesSlice,
userSlice,
alertsSlice,
chatSlice,
firmsSlice,
sidebarConfig,
newsSlice,
newsSettings,
searchNews,
instrumentLists,
modalsSlice,
issuersSlice,
tourSlice,
ntbPositionsSlice,
ntbOrdersAndDealsSlice,
ntbLogisticSlice,
auth,
screenerSlice,
ordersJournalSlice,
tradeJournalSlice,
requestStatus,
orderbookSlice,
mxtSlice,
chatDataByPlacementId,
surveys,
});
export const setupStore = (preloadedState?: Partial<RootState>) => {
const store = configureStore({
reducer: rootReducer,
preloadedState,
middleware: (getDefaultMiddleware) => [
...getDefaultMiddleware({
serializableCheck: false,
}),
sagaMiddleware,
chatSliceMiddleware,
],
});
sagaMiddleware.run(rootSaga);
return store;
};
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;