import { createReducer, on } from '@ngrx/store'; import { initCLNState } from './cln.state'; import { addInvoice, addPeer, removeChannel, removePeer, resetCLStore, setBalance, setChannels, setChildNodeSettingsCL, setFeeRates, setFees, setForwardingHistory, setInfo, setInvoices, setLocalRemoteBalance, setOffers, addOffer, setPayments, setPeers, setUTXOs, updateCLAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark, setPageSettings } from './cln.actions'; import { Channel, OfferBookmark } from '../../shared/models/clnModels'; import { CLNForwardingEventsStatusEnum, CLN_DEFAULT_PAGE_SETTINGS } from '../../shared/services/consts-enums-functions'; import { PageSettingsCLN } from '../../shared/models/pageSettings'; export const CLNReducer = createReducer(initCLNState, on(updateCLAPICallStatus, (state, { payload }) => { const updatedApisCallStatus = JSON.parse(JSON.stringify(state.apisCallStatus)); if (payload.action) { updatedApisCallStatus[payload.action] = { status: payload.status, statusCode: payload.statusCode, message: payload.message, URL: payload.URL, filePath: payload.filePath }; } return { ...state, apisCallStatus: updatedApisCallStatus }; }), on(setChildNodeSettingsCL, (state, { payload }) => ({ ...state, nodeSettings: payload })), on(resetCLStore, (state, { payload }) => ({ ...initCLNState, nodeSettings: payload })), on(setInfo, (state, { payload }) => ({ ...state, information: payload })), on(setFees, (state, { payload }) => ({ ...state, fees: payload })), on(setFeeRates, (state, { payload }) => { if (payload.perkb) { return { ...state, feeRatesPerKB: payload }; } else if (payload.perkw) { return { ...state, feeRatesPerKW: payload }; } else { return { ...state }; } }), on(setBalance, (state, { payload }) => ({ ...state, balance: payload })), on(setLocalRemoteBalance, (state, { payload }) => ({ ...state, localRemoteBalance: payload })), on(setPeers, (state, { payload }) => ({ ...state, peers: payload })), on(addPeer, (state, { payload }) => ({ ...state, peers: [...state.peers, payload] })), on(removePeer, (state, { payload }) => { const modifiedPeers = [...state.peers]; const removePeerIdx = state.peers.findIndex((peer) => peer.id === payload.id); if (removePeerIdx > -1) { modifiedPeers.splice(removePeerIdx, 1); } return { ...state, peers: modifiedPeers }; }), on(setChannels, (state, { payload }) => ({ ...state, activeChannels: payload.activeChannels, pendingChannels: payload.pendingChannels, inactiveChannels: payload.inactiveChannels })), on(removeChannel, (state, { payload }) => { const modifiedPeers = [...state.peers]; modifiedPeers.forEach((peer) => { if (peer.id === payload.id) { peer.connected = false; delete peer.netaddr; } }); return { ...state, peers: modifiedPeers }; }), on(setPayments, (state, { payload }) => ({ ...state, payments: payload })), on(setForwardingHistory, (state, { payload }) => { const storedChannels = [...state.activeChannels, ...state.pendingChannels, ...state.inactiveChannels]; const forwardsWithAlias = mapAliases(payload.listForwards, storedChannels); payload.listForwards = forwardsWithAlias; switch (payload.status) { case CLNForwardingEventsStatusEnum.SETTLED: const modifiedFeeWithTxCount = state.fees; modifiedFeeWithTxCount.totalTxCount = payload.totalForwards || 0; return { ...state, fees: modifiedFeeWithTxCount, forwardingHistory: payload }; case CLNForwardingEventsStatusEnum.FAILED: return { ...state, failedForwardingHistory: payload }; case CLNForwardingEventsStatusEnum.LOCAL_FAILED: return { ...state, localFailedForwardingHistory: payload }; default: return { ...state }; } }), on(addInvoice, (state, { payload }) => { const newInvoices = state.invoices; newInvoices.invoices?.unshift(payload); return { ...state, invoices: newInvoices }; }), on(setInvoices, (state, { payload }) => ({ ...state, invoices: payload })), on(updateInvoice, (state, { payload }) => { const modifiedInvoices = state.invoices; modifiedInvoices.invoices = modifiedInvoices.invoices?.map((invoice) => ((invoice.label === payload.label) ? payload : invoice)); return { ...state, invoices: modifiedInvoices }; }), on(setUTXOs, (state, { payload }) => ({ ...state, utxos: payload })), on(setOffers, (state, { payload }) => ({ ...state, offers: payload })), on(addOffer, (state, { payload }) => { const newOffers = state.offers; newOffers?.unshift(payload); return { ...state, offers: newOffers }; }), on(updateOffer, (state, { payload }) => { const modifiedOffers = [...state.offers]; const updateOfferIdx = state.offers.findIndex((offer) => offer.offer_id === payload.offer.offer_id); if (updateOfferIdx > -1) { modifiedOffers.splice(updateOfferIdx, 1, payload.offer); } return { ...state, offers: modifiedOffers }; }), on(setOfferBookmarks, (state, { payload }) => ({ ...state, offersBookmarks: payload })), on(addUpdateOfferBookmark, (state, { payload }) => { const newOfferBMs: OfferBookmark[] = [...state.offersBookmarks]; const offerBMExistsIdx = newOfferBMs.findIndex((offer: OfferBookmark) => offer.bolt12 === payload.bolt12); if (offerBMExistsIdx < 0) { newOfferBMs?.unshift(payload); } else { const updatedOffer = { ...newOfferBMs[offerBMExistsIdx] }; updatedOffer.title = payload.title; updatedOffer.amountMSat = payload.amountMSat; updatedOffer.lastUpdatedAt = payload.lastUpdatedAt; updatedOffer.description = payload.description; updatedOffer.vendor = payload.vendor; newOfferBMs.splice(offerBMExistsIdx, 1, updatedOffer); } return { ...state, offersBookmarks: newOfferBMs }; }), on(removeOfferBookmark, (state, { payload }) => { const modifiedOfferBookmarks = [...state.offersBookmarks]; const removeOfferBookmarkIdx = state.offersBookmarks.findIndex((ob) => ob.bolt12 === payload.bolt12); if (removeOfferBookmarkIdx > -1) { modifiedOfferBookmarks.splice(removeOfferBookmarkIdx, 1); } return { ...state, offersBookmarks: modifiedOfferBookmarks }; }), on(setPageSettings, (state, { payload }) => { const newPageSettings: PageSettingsCLN[] = []; CLN_DEFAULT_PAGE_SETTINGS.forEach((defaultPage) => { const pageSetting = payload.find((p) => p.pageId === defaultPage.pageId) || null; if (pageSetting) { const tablesSettings = JSON.parse(JSON.stringify(pageSetting.tables)); pageSetting.tables = []; // To maintain settings order defaultPage.tables.forEach((defaultTable) => { const tableSetting = tablesSettings.find((t) => t.tableId === defaultTable.tableId) || null; if (tableSetting) { pageSetting.tables.push(tableSetting); } else { pageSetting.tables.push(JSON.parse(JSON.stringify(defaultTable))); } }); newPageSettings.push(pageSetting); } else { newPageSettings.push(JSON.parse(JSON.stringify(defaultPage))); } }); return { ...state, pageSettings: newPageSettings }; }) ); const mapAliases = (payload: any, storedChannels: Channel[]) => { if (payload && payload.length > 0) { payload.forEach((fhEvent, i) => { if (storedChannels && storedChannels.length > 0) { for (let idx = 0; idx < storedChannels.length; idx++) { if (storedChannels[idx].short_channel_id && storedChannels[idx].short_channel_id === fhEvent.in_channel) { fhEvent.in_channel_alias = storedChannels[idx].alias ? storedChannels[idx].alias : fhEvent.in_channel; if (fhEvent.out_channel_alias) { return; } } if (storedChannels[idx].short_channel_id && storedChannels[idx].short_channel_id?.toString() === fhEvent.out_channel) { fhEvent.out_channel_alias = storedChannels[idx].alias ? storedChannels[idx].alias : fhEvent.out_channel; if (fhEvent.in_channel_alias) { return; } } if (idx === storedChannels.length - 1) { if (!fhEvent.in_channel_alias) { fhEvent.in_channel_alias = fhEvent.in_channel ? fhEvent.in_channel : '-'; } if (!fhEvent.out_channel_alias) { fhEvent.out_channel_alias = fhEvent.out_channel ? fhEvent.out_channel : '-'; } } } } else { fhEvent.in_channel_alias = fhEvent.in_channel ? fhEvent.in_channel : '-'; fhEvent.out_channel_alias = fhEvent.out_channel ? fhEvent.out_channel : '-'; } }); } else { payload = []; } return payload; };