From 52fb7bf9fc89ed8993bf786d13a1929aaf74fbdf Mon Sep 17 00:00:00 2001 From: Eugene Panteleymonchuk Date: Fri, 22 Mar 2024 16:37:38 +0200 Subject: [PATCH] feat: onSlotFinalized include all previous slots. Signed-off-by: Eugene Panteleymonchuk --- .../visualizer-vivagraph/hooks/useFeed.ts | 63 +++++++++++++++++-- .../visualizer-vivagraph/store/tangle.ts | 40 ++++++++++++ 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/client/src/features/visualizer-vivagraph/hooks/useFeed.ts b/client/src/features/visualizer-vivagraph/hooks/useFeed.ts index daf743306..a2ca6492e 100644 --- a/client/src/features/visualizer-vivagraph/hooks/useFeed.ts +++ b/client/src/features/visualizer-vivagraph/hooks/useFeed.ts @@ -1,5 +1,5 @@ import { useEffect, useRef, useState, useContext } from "react"; -import { type BlockMetadataResponse } from "@iota/sdk-wasm-nova/web"; +import { type BlockMetadataResponse, Utils, BlockState, SlotIndex } from "@iota/sdk-wasm-nova/web"; import { ServiceFactory } from "~factories/serviceFactory"; import { IFeedBlockData } from "~/models/api/nova/feed/IFeedBlockData"; import Viva from "vivagraphjs"; @@ -35,6 +35,9 @@ export const useFeed = (network: string) => { const deleteBlockIdToMetadata = useTangleStore((state) => state.deleteBlockIdToMetadata); const updateBlockIdToMetadata = useTangleStore((state) => state.updateBlockIdToMetadata); const search = useTangleStore((state) => state.search); + const addToConfirmedBlocksBySlot = useTangleStore((state) => state.addToConfirmedBlocksBySlot); + const removeConfirmedBlocksSlot = useTangleStore((state) => state.removeConfirmedBlocksSlot); + const confirmedBlocksBySlot = useTangleStore((state) => state.confirmedBlocksBySlot); const themeMode = useGetThemeMode(); const graphContext = useContext(GraphContext); @@ -209,8 +212,9 @@ export const useFeed = (network: string) => { const blockMetadata = getBlockIdToMetadata(blockId); if (!blockMetadata) { - const color = getBlockColorByState(themeMode, "pending"); - createBlock(blockId, { ...newBlock, color: color }, now); + const initState = "pending"; + const color = getBlockColorByState(themeMode, initState); + createBlock(blockId, { ...newBlock, color: color, state: initState }, now); const parentIds = getBlockParents(newBlock); const existingBlockIds = getBlockMetadataKeys(); @@ -232,12 +236,61 @@ export const useFeed = (network: string) => { const selectedColor = getBlockColorByState(themeMode, metadataUpdate.blockState); updateBlockIdToMetadata(metadataUpdate.blockId, { color: selectedColor }); - updateBlockColor(metadataUpdate.blockId, selectedColor); + + const blockMetadata = getBlockIdToMetadata(metadataUpdate.blockId); + if (blockMetadata) { + const previousBlockState = blockMetadata.state; + const wasConfirmedBeforeAccepted = previousBlockState === "accepted" && metadataUpdate.blockState === "confirmed"; + + if (!wasConfirmedBeforeAccepted) { + updateBlockIdToMetadata(metadataUpdate.blockId, { + state: metadataUpdate.blockState, + }); + } + + updateBlockColor(metadataUpdate.blockId, selectedColor); + const acceptedStates: BlockState[] = ["confirmed", "accepted"]; + if (acceptedStates.includes(metadataUpdate.blockState)) { + const slot = Utils.computeSlotIndex(metadataUpdate.blockId); + addToConfirmedBlocksBySlot(metadataUpdate.blockId, slot); + } + } + } + } + + function onSlotFinalized(slotFinalized: SlotIndex): void { + const slotsBefore = Array.from(confirmedBlocksBySlot.keys()); + + const slots = [...slotsBefore, slotFinalized]; + + const blocks = []; + for (const slot of slots) { + const blockIds = confirmedBlocksBySlot.get(slot); + if (blockIds) { + blocks.push(...blockIds); + } + } + + if (blocks?.length) { + blocks.forEach((blockId) => { + const selectedColor = getBlockColorByState(themeMode, "finalized"); + if (selectedColor) { + updateBlockIdToMetadata(blockId, { + state: "finalized", + color: selectedColor, + }); + updateBlockColor(blockId, selectedColor); + } + }); + } + + for (const slot of slots) { + removeConfirmedBlocksSlot(slot); } } const feedSubscriptionStart = () => { - feedService.subscribeBlocks(onNewBlock, onBlockMetadataUpdate, () => {}); + feedService.subscribeBlocks(onNewBlock, onBlockMetadataUpdate, onSlotFinalized); }; useEffect(() => { diff --git a/client/src/features/visualizer-vivagraph/store/tangle.ts b/client/src/features/visualizer-vivagraph/store/tangle.ts index 8d35fdb5d..e387a0cd8 100644 --- a/client/src/features/visualizer-vivagraph/store/tangle.ts +++ b/client/src/features/visualizer-vivagraph/store/tangle.ts @@ -1,9 +1,11 @@ import { create } from "zustand"; import { devtools } from "zustand/middleware"; import { IFeedBlockData } from "~models/api/nova/feed/IFeedBlockData"; +import { BlockId, BlockState, SlotIndex } from "@iota/sdk-wasm-nova/web"; export interface VivagraphParams { color: string; + state: BlockState; } interface TangleState { @@ -24,6 +26,13 @@ interface TangleState { search: string; setSearch: (search: string) => void; + + resetTangleStore: () => void; + + // Confirmed/accepted blocks by slot + confirmedBlocksBySlot: Map; + addToConfirmedBlocksBySlot: (blockId: BlockId, slot: SlotIndex) => void; + removeConfirmedBlocksSlot: (slot: SlotIndex) => void; } const INITIAL_STATE = { @@ -31,6 +40,7 @@ const INITIAL_STATE = { visibleBlocks: [], selectedNode: null, search: "", + confirmedBlocksBySlot: new Map(), }; export const useTangleStore = create()( @@ -85,5 +95,35 @@ export const useTangleStore = create()( getBlockMetadataValues: () => { return Array.from(get().blockIdToMetadata.values()); }, + + addToConfirmedBlocksBySlot: (blockId, slot) => { + set((state) => { + state.confirmedBlocksBySlot.has(slot) + ? state.confirmedBlocksBySlot.get(slot)?.push(blockId) + : state.confirmedBlocksBySlot.set(slot, [blockId]); + return { + ...state, + confirmedBlocksBySlot: state.confirmedBlocksBySlot, + }; + }); + }, + + removeConfirmedBlocksSlot: (slot) => { + set((state) => { + state.confirmedBlocksBySlot.delete(slot); + + // Cleanup all slots that are lower than the current slot + for (const existingSlot of state.confirmedBlocksBySlot.keys()) { + if (existingSlot < slot) { + state.confirmedBlocksBySlot.delete(existingSlot); + } + } + + return { + ...state, + confirmedBlocksBySlot: state.confirmedBlocksBySlot, + }; + }); + }, })), );