/* eslint-disable no-promise-executor-return */ import dayjs from 'dayjs'; import _ from 'lodash'; import { useEffect, useRef, useState, } from 'react'; import { useRecoilValue, useSetRecoilState, } from 'recoil'; import { t } from 'ttag'; import { beatEventCountState, rhythmEventCountState, specialEventTypesState, updatedAfibAvgHrState, updatedDailyAfibHrSummariesState, } from '.'; import { clearApolloCache } from '../../../../../Apollo/apolloClient'; import fetchHolterBeatEventsCount from '../../../../../Apollo/Functions/fetchHolterBeatEventsCount'; import fetchHolterEventIndex from '../../../../../Apollo/Functions/fetchHolterEventIndex'; import fetchHolterEvents from '../../../../../Apollo/Functions/fetchHolterEvents'; import fetchHolterEventsDailyCount from '../../../../../Apollo/Functions/fetchHolterEventsDailyCount'; import fetchHolterRhythmEventsCount from '../../../../../Apollo/Functions/fetchHolterRhythmEventsCount'; import handleCheckSavingAiProcess from '../../../../../Apollo/Functions/handleCheckSavingAiProcess'; import { EMITTER_CONSTANTS } from '../../../../../ConstantsV2'; import { AI_COMMAND, canShowCountBeatEvents, canShowDurationEvents, ECG_EVENT_MENU_OPTIONS, EVENT_COUNT_CONVERT, ID_BOOKMARK_TAB, KEY_CANCEL, KEY_RECOIL, STRIP_EVENT_THUMBNAIL_INFO, TYPE_RHYTHM_EVENT_ENUM, typesPersistEventV2, } from '../../../../../ConstantsV2/aiConstants'; import { convertToDayJS } from '../../../../../UtilsV2/aiUtils'; import { useEmitter, useGetRecoilValue } from '../../../../../UtilsV2/customHooks'; import emitter from '../../../../../UtilsV2/eventEmitter'; import { toastrError } from '../../../../../UtilsV2/toastNotification'; import { activeTabState, beatChangesState, dailyCountState, ecgDataMapState, eventChangesState, eventFilterState, eventNewsState, eventOptionsState, eventOthersState, groupEventChangesState, isActiveTabState, isNotReadyAfterSaveState, isRenderViewerEcgState, originalDailySummariesState, pageIndexState, profileAfibAvgHrState, reloadEcgViewerState, reloadHrChartState, reportInfoState, selectedDateValueState, selectedStripState, } from '../../Recoil'; import { logError } from '../../handler'; import { generateFilterSpecialEventType, isAppliedFilterEvent, } from '../handler'; import { clearCachesBeatHourly } from '../../../../../Store/caches'; import { removeSavingData } from '../../BeatHR/helper'; import { beatOptionsState } from '../../BeatHR/recoil'; const RhythmEventsRecoilDataEffect = (props) => { const keyRecoil = KEY_RECOIL.TAB_2; const activeButton = useRecoilValue(activeTabState(keyRecoil)); const getActiveButton = useGetRecoilValue(activeTabState(keyRecoil)); const getSelectedStrip = useGetRecoilValue(selectedStripState(keyRecoil)); const getPageIndex = useGetRecoilValue(pageIndexState(keyRecoil)); const setPageIndex = useSetRecoilState(pageIndexState(keyRecoil)); const getSelectedDateValue = useGetRecoilValue(selectedDateValueState(keyRecoil)); const getSpecialEventTypes = useGetRecoilValue(specialEventTypesState); const getEventFilter = useGetRecoilValue(eventFilterState(keyRecoil)); const setDailyCount = useSetRecoilState(dailyCountState(keyRecoil)); const setUpdatedAfibAvgHr = useSetRecoilState(updatedAfibAvgHrState); const setUpdatedDailyAfibHrSummaries = useSetRecoilState(updatedDailyAfibHrSummariesState); const setEventChanges = useSetRecoilState(eventChangesState(keyRecoil)); const setBeatChanges = useSetRecoilState(beatChangesState(keyRecoil)); const setReloadEcgViewer = useSetRecoilState(reloadEcgViewerState(keyRecoil)); const setReloadHrChart = useSetRecoilState(reloadHrChartState(keyRecoil)); const setGroupEventChanges = useSetRecoilState(groupEventChangesState(keyRecoil)); const isActiveTab = useRecoilValue(isActiveTabState(keyRecoil)); const setIsNotReadyAfterSaveTab1 = useSetRecoilState(isNotReadyAfterSaveState(KEY_RECOIL.TAB_1)); const setIsNotReadyAfterSaveTab2 = useSetRecoilState(isNotReadyAfterSaveState(KEY_RECOIL.TAB_2)); const setIsNotReadyAfterSaveTab3 = useSetRecoilState(isNotReadyAfterSaveState(KEY_RECOIL.TAB_3)); const getHolterRhythmEventsCount = useGetRecoilValue(rhythmEventCountState); const getHolterBeatEventsCount = useGetRecoilValue(beatEventCountState); const { studyId, profileId, timezoneOffset } = useRecoilValue(reportInfoState); const setEcgDataMap = useSetRecoilState(ecgDataMapState); const getEcgDataMap = useGetRecoilValue(ecgDataMapState); const setOriginalDailySummaries = useSetRecoilState(originalDailySummariesState); const setProfileAfibAvgHr = useSetRecoilState(profileAfibAvgHrState); const setEventOthers = useSetRecoilState(eventOthersState(keyRecoil)); const setEventNews = useSetRecoilState(eventNewsState(keyRecoil)); const setEventOptions = useSetRecoilState(eventOptionsState(keyRecoil)); const setIsRenderViewerEcg = useSetRecoilState(isRenderViewerEcgState); const setHolterRhythmEventsCount = useSetRecoilState(rhythmEventCountState); const setHolterBeatEventsCount = useSetRecoilState(beatEventCountState); const setSelectedStrip = useSetRecoilState(selectedStripState(keyRecoil)); const [doneReset, setDoneReset] = useState(false); const setBeatOptions = useSetRecoilState(beatOptionsState(keyRecoil)); const commandPendingQueue = useRef([]); const updateDataMessageQueue = useRef([]); const promiseUpdateData = useRef([]); const promiseUpdateDataType = useRef([]); const isExcutedRef = useRef(false); const handleFetchStripEvents = async ({ page, additionalFilter = {} }) => { try { const type = getActiveButton(); const validatePage = page <= 0 ? 0 : page; const filterHolterEvents = { studyId, profileId, types: [type], skip: validatePage * STRIP_EVENT_THUMBNAIL_INFO.stripDisplayLimit, ...additionalFilter, }; const promises = [fetchHolterEvents( filterHolterEvents, STRIP_EVENT_THUMBNAIL_INFO.stripDisplayLimit, true, KEY_CANCEL.API_HOLTER_AI, )]; if (!_.isEmpty(additionalFilter)) { const holterEventsCountFilter = { studyId, profileId, types: [type], ...additionalFilter, }; promises.push( _.find(ECG_EVENT_MENU_OPTIONS, (x) => x.value === type).type === 'beat' ? fetchHolterBeatEventsCount(holterEventsCountFilter, true, KEY_CANCEL.API_HOLTER_AI) : fetchHolterRhythmEventsCount(holterEventsCountFilter, true, KEY_CANCEL.API_HOLTER_AI), ); } const [{ events }, eventsCount] = await Promise.all(promises); if (!_.isEmpty(additionalFilter)) { return { events, eventsCount }; } return { events, eventsCount: _.find(ECG_EVENT_MENU_OPTIONS, (x) => x.value === type).type === 'beat' ? getHolterBeatEventsCount() : getHolterRhythmEventsCount() }; } catch (error) { return { events: undefined, eventsCount: 0 }; } }; const handleReloadEventHrChartV2 = async () => { try { const type = activeButton; const selectedDateValue = getSelectedDateValue(); const startSearchDate = selectedDateValue; const stopSearchDate = selectedDateValue ? dayjs(selectedDateValue).add(1, 'days').toISOString() : null; const promiseArr = [ selectedDateValue ? fetchHolterEvents({ studyId, profileId, types: typesPersistEventV2, startSearchDate, stopSearchDate, }, 0, true, KEY_CANCEL.API_HOLTER_AI) : undefined, ]; await Promise.allSettled(promiseArr); if (!_.includes(promiseUpdateDataType.current, 'setReloadHrChart')) { promiseUpdateDataType.current.push('setReloadHrChart'); promiseUpdateData.current.push(() => setReloadHrChart((prev) => prev + _.round(Math.random() * 100))); } } catch (error) { logError('Failed to fetch reload data: ', error); toastrError(error.message, t`Error`); } }; const handleReloadEventDataV2 = async (needUpdateAfibArtifact) => { try { if (isActiveTab) { setIsRenderViewerEcg(false); } const type = activeButton; const selectedDateValue = getSelectedDateValue(); const startSearchDate = selectedDateValue; const stopSearchDate = selectedDateValue ? dayjs(selectedDateValue).add(1, 'days').toISOString() : null; const additionalFilter = generateFilterSpecialEventType(type, getSpecialEventTypes()); const eventFilter = getEventFilter(); if (isAppliedFilterEvent(eventFilter, type)) { _.assign(additionalFilter, { ...(eventFilter.isHideReviewed && { isReviewed: false }), ...(eventFilter.isCapture && { isCaptured: true }), ...(eventFilter.sortOrder && (canShowCountBeatEvents.includes(type) || canShowDurationEvents.includes(type)) && { sortBy: eventFilter.sortBy || 'countBeats', sortOrder: eventFilter.sortOrder, }), }); } const promiseArr = [ selectedDateValue ? fetchHolterEvents({ studyId, profileId, types: typesPersistEventV2, startSearchDate, stopSearchDate, }, 0, true, KEY_CANCEL.API_HOLTER_AI) : undefined, fetchHolterEventsDailyCount({ studyId, profileId, types: [type], ...additionalFilter, }, null, false), ]; const selectedStrip = getSelectedStrip(); if (selectedStrip?.idEvent) { const filterHolterEventIndex = { studyId, profileId, types: [type], includedEventId: selectedStrip.idEvent, ...additionalFilter, }; promiseArr.push(fetchHolterEventIndex(filterHolterEventIndex, false, KEY_CANCEL.API_HOLTER_AI)); } else { promiseArr.push(new Promise((resolve) => resolve(undefined))); } const [resultHolterEventsPersistAllDay, resultDailyCount, resultEventIndex] = await Promise.allSettled(promiseArr); const dailyCount = resultDailyCount?.status === 'rejected' ? undefined : resultDailyCount.value; if (resultEventIndex?.status === 'fulfilled') { const eventIndex = resultEventIndex.value; if (!_.isNil(eventIndex) && eventIndex >= 0) { const page = Math.floor(eventIndex / STRIP_EVENT_THUMBNAIL_INFO.stripDisplayLimit); await handleFetchStripEvents({ page, additionalFilter }); promiseUpdateData.current.push(() => setPageIndex({ index: page < 0 ? 0 : page })); } else { const pageIndex = getPageIndex(); const { events, eventsCount } = await handleFetchStripEvents({ page: pageIndex.index, additionalFilter }); if (_.isEmpty(events)) { const totalEvent = eventsCount[EVENT_COUNT_CONVERT[getActiveButton()]]?.count || 0; const pageTemp = Math.floor(totalEvent / STRIP_EVENT_THUMBNAIL_INFO.stripDisplayLimit); const page = (totalEvent % STRIP_EVENT_THUMBNAIL_INFO.stripDisplayLimit) === 0 ? pageTemp - 1 : pageTemp; promiseUpdateData.current.push(() => setPageIndex({ index: page < 0 ? 0 : page })); } else { promiseUpdateData.current.push(() => setPageIndex({ index: getPageIndex().index })); } } } if (needUpdateAfibArtifact) { handleReloadEventHrChartV2(); } if (!_.isEmpty(dailyCount)) { promiseUpdateData.current.push(() => setDailyCount(dailyCount)); } if (!_.includes(promiseUpdateDataType.current, 'setReloadHrChart')) { promiseUpdateDataType.current.push('setReloadHrChart'); promiseUpdateData.current.push(() => setReloadHrChart((prev) => prev + _.round(Math.random() * 100))); } } catch (error) { logError('Failed to fetch reload data: ', error); toastrError(error.message, t`Error`); } }; const handleReloadEvent = async (msg) => { try { if (isActiveTab) { setIsRenderViewerEcg(false); } const { newEvents, deletedEvents, updatedEvents, deletedEventTypes, } = msg; const selectedDateValue = getSelectedDateValue(); const selectedDateString = selectedDateValue ? convertToDayJS(selectedDateValue, timezoneOffset).format('DD-MM-YYYY') : null; const types = [TYPE_RHYTHM_EVENT_ENUM.AFIB, TYPE_RHYTHM_EVENT_ENUM.ARTIFACT]; let needUpdate; let needUpdateAfibArtifact; //* Trường hợp sửa artifact hoặc afib xong qua tab event khác save thì hr chart ko update if (deletedEventTypes?.length) { deletedEventTypes.forEach((type) => { if (type === activeButton || type === TYPE_RHYTHM_EVENT_ENUM.TACHY) { needUpdate = true; } else if (types.includes(type)) { needUpdateAfibArtifact = true; } }); } if (deletedEvents?.length) { deletedEvents.forEach((deletedEvent) => { if (deletedEvent.type === activeButton || (deletedEvent.type === TYPE_RHYTHM_EVENT_ENUM.TACHY)) { needUpdate = true; } else { const isSameDate = selectedDateString === convertToDayJS(deletedEvent.start, timezoneOffset).format('DD-MM-YYYY'); if (types.includes(deletedEvent.type) && isSameDate) { needUpdateAfibArtifact = true; } } }); } if (newEvents?.length) { newEvents.forEach((newEvent) => { if (newEvent.type === activeButton) { needUpdate = true; } else { const isSameDate = selectedDateString === convertToDayJS(newEvent.start, timezoneOffset).format('DD-MM-YYYY'); if (types.includes(newEvent.type) && isSameDate) { needUpdateAfibArtifact = true; } } }); } if (updatedEvents?.length) { updatedEvents.forEach((updatedEvent) => { if (updatedEvent.type === activeButton) { needUpdate = true; } else { const isSameDate = selectedDateString === convertToDayJS(updatedEvent.start, timezoneOffset).format('DD-MM-YYYY'); if (types.includes(updatedEvent.type) && isSameDate) { needUpdateAfibArtifact = true; } } }); } if (needUpdate) { await handleReloadEventDataV2(needUpdateAfibArtifact); } else if (needUpdateAfibArtifact || activeButton === TYPE_RHYTHM_EVENT_ENUM.AFIB) { await handleReloadEventHrChartV2(); } else if (activeButton === ID_BOOKMARK_TAB) { if (!_.includes(promiseUpdateDataType.current, 'setReloadHrChart')) { promiseUpdateDataType.current.push('setReloadHrChart'); promiseUpdateData.current.push(() => setReloadHrChart((prev) => prev + _.round(Math.random() * 100))); } } console.log('[rhythmEventsRecoilDataEffect]-RELOAD-EVENT-RHYTHMEVENT', needUpdate, activeButton, needUpdateAfibArtifact); if (!_.includes(promiseUpdateDataType.current, 'setReloadEcgViewer')) { promiseUpdateDataType.current.push('setReloadEcgViewer'); promiseUpdateData.current.push(() => setReloadEcgViewer((prev) => prev + _.round(Math.random() * 100))); } promiseUpdateData.current.push(() => { setGroupEventChanges((prev) => { if (prev.length === 0) { return prev; } return []; }); let eventChanges = []; let eventNews = []; let eventOthers = []; setEventChanges((prev) => { let count = 0; const filterResult = prev.filter((x) => { if (x.isSaving) { count += 1; return false; } return true; }); if (count === 0) { return prev; } eventChanges.push(...filterResult); return filterResult; }); setEventOthers((prev) => { let count = 0; const filterResult = prev.filter((x) => { if (x.isSaving) { count += 1; return false; } return true; }); if (count === 0) { return prev; } eventOthers.push(...filterResult); return filterResult; }); setEventNews((prev) => { let count = 0; const filterResult = prev.filter((x) => { if (x.isSaving) { count += 1; return false; } return true; }); if (count === 0) { return prev; } eventNews.push(...filterResult); return filterResult; }); // Perform grouping action applicable to undo/redo setEventOptions({ eventNewsState: eventNews, eventChangesState: eventChanges, eventOthersState: eventOthers, }); eventChanges = []; eventNews = []; eventOthers = []; const selectedStrip = getSelectedStrip(); if (selectedStrip?.isNew) { setSelectedStrip(null); } }); } catch (error) { logError('Error: ', error); } return true; }; const handleUpdateBeats = (msg) => { const prevEcgDataMap = getEcgDataMap(); const cloneEcgDataMap = { ...prevEcgDataMap }; if ((msg.summaries?.length || msg.beatsUpdated?.summaries?.length) && !_.isEmpty(prevEcgDataMap?.data)) { _.forEach(msg.summaries || msg.beatsUpdated?.summaries, (summary) => { const foundData = _.find(cloneEcgDataMap.data, (x) => x.id === summary.id); if (foundData) { _.assign(foundData, { avgHrs: summary?.avgHrs, maxHrs: summary?.maxHrs, minHrs: summary?.minHrs, beatFinalPath: summary?.beatFinalPath, latestBeatPrefix: summary?.latestBeatPrefix, }); } }); const summaryIds = _.map(msg.summaries || msg.beatsUpdated?.summaries, 'id'); const socketUpdateEcgDataMap = { ...cloneEcgDataMap, data: _.filter(cloneEcgDataMap.data, (item) => summaryIds.includes(item.id)), }; if (isActiveTab) { emitter.emit(EMITTER_CONSTANTS.UPDATE_SOCKET_BEAT_DATA_MAP, socketUpdateEcgDataMap); } } return cloneEcgDataMap; }; const handleReloadBeat = async (msg) => { try { const cloneEcgDataMap = handleUpdateBeats(msg); promiseUpdateData.current.push(() => { let beatsChanges = []; setBeatChanges((prev) => { if (_.isEmpty(prev)) return prev; const beats = removeSavingData(prev); beatsChanges = beats; return beats; }); setBeatOptions((prev) => ({ ...prev, beatChangesState: beatsChanges })); setEcgDataMap((prev) => { if (_.isEqual(prev, cloneEcgDataMap)) { return prev; } return cloneEcgDataMap; }); setReloadEcgViewer((prev) => prev + _.round(Math.random() * 100)); }); } catch (error) { logError('Failed to reload beat', error); } return true; }; const fetchEventsCount = async () => { try { const eventsCountFilter = { studyId, profileId, }; const promises = [ fetchHolterBeatEventsCount(eventsCountFilter, false, null, false), fetchHolterRhythmEventsCount(eventsCountFilter, false, null, false), ]; const [holterBeatEventsCount, holterRhythmEventsCount] = await Promise.all(promises); setHolterBeatEventsCount(holterBeatEventsCount); setHolterRhythmEventsCount(holterRhythmEventsCount); } catch (error) { logError('Failed to fetch holter rhythm events count: ', error); } }; const updateEventOutOfPending = _.debounce(async () => { await clearApolloCache(); setIsRenderViewerEcg(false); setTimeout(() => { setIsRenderViewerEcg(true); }, 500); }, 2000); const batchUpdateData = async () => { try { await clearApolloCache(); await clearCachesBeatHourly(); if (isActiveTab || props.activeTab !== '1') { setIsRenderViewerEcg(false); } const promise = []; updateDataMessageQueue.current.forEach((x) => { if (x.type === AI_COMMAND.EVENT) { promise.push(handleReloadEvent(x.msg)); promise.push(fetchEventsCount()); } }); const beatPromises = []; updateDataMessageQueue.current.forEach((x) => { if (x.type === AI_COMMAND.BEAT) { beatPromises.push(handleReloadBeat(x.msg)); } }); await Promise.all(beatPromises); // Wait for all beat promises to resolve console.log('[rhythmEventsRecoilDataEffect]-COMMANDEXECUTED-1', { promise, updateDataMessageQueue: updateDataMessageQueue.current, promiseUpdateData: promiseUpdateData.current, }); await Promise.all(promise); promiseUpdateData.current.forEach((update) => update()); updateDataMessageQueue.current.length = 0; } catch (error) { logError('Error: ', error); } finally { console.log('[rhythmEventsRecoilDataEffect]-COMMANDEXECUTED-2', { updateDataMessageQueue: updateDataMessageQueue.current, promiseUpdateData: promiseUpdateData.current, }); updateDataMessageQueue.current.length = 0; promiseUpdateData.current.length = 0; commandPendingQueue.current.length = 0; promiseUpdateDataType.current.length = 0; emitter.emit(EMITTER_CONSTANTS.RELOAD_EVENT_STRIP); if (isActiveTab) { setDoneReset(true); } } }; useEffect(() => { if (doneReset) { setTimeout(() => { emitter.emit(EMITTER_CONSTANTS.AI_LOADING, { isLoading: false, tab: 'rhythmEvents', isExcuted: isExcutedRef.current, }); setDoneReset(false); if (isExcutedRef.current) { isExcutedRef.current = false; } }, 2000); } }, [doneReset]); useEmitter(EMITTER_CONSTANTS.EVENTSUPDATED_EVENT, (msg) => { const foundMsg = updateDataMessageQueue.current.find((x) => x.type === AI_COMMAND.EVENT); if (foundMsg) { if (!_.isEmpty(msg)) { Object.keys(msg).forEach((key) => { if (foundMsg.msg[key]) { foundMsg.msg[key] = foundMsg.msg[key].concat(msg[key]); } else { foundMsg.msg[key] = msg[key]; } }); } } else { updateDataMessageQueue.current.push({ type: AI_COMMAND.EVENT, msg: _.cloneDeep(msg), }); } if (commandPendingQueue.current.length === 0) { // update out of pending command updateEventOutOfPending(); } }, [studyId, profileId]); useEmitter(EMITTER_CONSTANTS.BEATSUPDATED_EVENT, async (msg) => { const foundMsg = updateDataMessageQueue.current.find((x) => x.type === AI_COMMAND.BEAT); if (foundMsg) { if (!_.isEmpty(msg)) { _.assign(foundMsg, { msg }); } } else { updateDataMessageQueue.current.push({ type: AI_COMMAND.BEAT, msg, }); } }, []); useEmitter(EMITTER_CONSTANTS.DAILY_SUMMARY_UPDATED, (msg) => { setOriginalDailySummaries((prev) => { if (msg) { const cloneDailySummaries = _.cloneDeep(prev); _.forEach(msg, (dailySummary) => { const foundDailySummary = _.find(cloneDailySummaries, (x) => x.date === dailySummary.date); if (foundDailySummary) { _.assign(foundDailySummary, dailySummary); } }); return cloneDailySummaries; } return prev; }); }, []); useEmitter(EMITTER_CONSTANTS.AFIB_HR_SUMMARY_UPDATED, (msg) => { const { afibAvgHr } = msg; setProfileAfibAvgHr(afibAvgHr); setUpdatedAfibAvgHr(undefined); setUpdatedDailyAfibHrSummaries([]); }, []); useEmitter(EMITTER_CONSTANTS.AI_PROCESS_SAVED, async (msg) => { await batchUpdateData(); setIsNotReadyAfterSaveTab2(false); }, [activeButton, studyId, profileId, isActiveTab, props.activeTab]); useEmitter(EMITTER_CONSTANTS.COMMAND_PENDING, (msg) => { //* Khi 1 user save, BE gửi command pending về tuỳ theo số lượng api gọi lúc save, push command vào mảng và khi // có socket command execute thì remove => khi mảng về 0 thì get hết data nhận đc trong mảng updateDataMessage => update UI if (msg.messageId !== profileId) return; emitter.emit(EMITTER_CONSTANTS.AI_LOADING, { isLoading: true, tab: 'rhythmEvents' }); commandPendingQueue.current.push(msg.command); }, [profileId]); useEmitter(EMITTER_CONSTANTS.COMMAND_EXECUTED, async (msg) => { // msg.command: update-events, update-beats, updateHolterHrDailySummary // updateHolterProfile, generate-report-comments if (msg.messageId !== profileId) return; if (msg.command === 'updateHolterProfile' || msg.command === 'generate-report-comments') return; console.log('[rhythmEventsRecoilDataEffect]-COMMANDEXECUTED-BEFORE', { msg, commandPendingQueue: commandPendingQueue.current, updateDataMessageQueue: updateDataMessageQueue.current, }); const foundIndexCommand = commandPendingQueue.current.findIndex((command) => command === msg.command); if (foundIndexCommand !== -1) { commandPendingQueue.current.splice(foundIndexCommand, 1); } //* Finish all command if (commandPendingQueue.current.length === 0) { isExcutedRef.current = true; if (isActiveTab) { if (updateDataMessageQueue.current.length !== 0) { //* Announce this tab have data to update emitter.emit(EMITTER_CONSTANTS.AI_UPDATE_TAB, '2'); const { beats, events, strips } = await handleCheckSavingAiProcess({ studyId, profileId, }); if (!beats) { setIsNotReadyAfterSaveTab1(true); } if (!strips) { setIsNotReadyAfterSaveTab3(true); } await batchUpdateData(); } else { setDoneReset(true); } } else { await batchUpdateData(); } } }, [activeButton, isActiveTab, studyId, profileId, props.activeTab]); return null; }; export default RhythmEventsRecoilDataEffect;
Preview:
downloadDownload PNG
downloadDownload JPEG
downloadDownload SVG
Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!
Click to optimize width for Twitter