11import React , { FunctionComponent , useEffect , useState } from "react" ;
22import { observer } from "mobx-react-lite" ;
33import {
4+ BackgroundTxStatus ,
45 GetIBCHistoriesMsg ,
56 GetSkipHistoriesMsg ,
67 GetSwapV2HistoriesMsg ,
8+ GetTxExecutionMsg ,
79 HideSwapV2HistoryMsg ,
810 IBCHistory ,
911 RemoveIBCHistoryMsg ,
@@ -12,12 +14,14 @@ import {
1214 SkipHistory ,
1315 SwapV2History ,
1416 SwapV2TxStatus ,
17+ TxExecution ,
1518} from "@keplr-wallet/background" ;
1619import { InExtensionMessageRequester } from "@keplr-wallet/router-extension" ;
1720import { BACKGROUND_PORT } from "@keplr-wallet/router" ;
1821import { useLayoutEffectOnce } from "../../../../hooks/use-effect-once" ;
1922import { Stack } from "../../../../components/stack" ;
2023import { Box } from "../../../../components/box" ;
24+ import { Button } from "../../../../components/button" ;
2125import { Gutter } from "../../../../components/gutter" ;
2226import { useTheme } from "styled-components" ;
2327import { ColorPalette } from "../../../../styles" ;
@@ -334,8 +338,8 @@ export const IbcHistoryView: FunctionComponent<{
334338 history = { history }
335339 removeHistory = { ( id ) => {
336340 const requester = new InExtensionMessageRequester ( ) ;
337- if ( history . requiresNextTransaction && ! history . trackDone ) {
338- // do not remove the history before the tracking is done
341+ if ( history . backgroundExecutionId && ! history . trackDone ) {
342+ // do not remove the history before the tracking is done (multi tx case)
339343 const msg = new HideSwapV2HistoryMsg ( id ) ;
340344 requester . sendMessage ( BACKGROUND_PORT , msg ) ;
341345 } else {
@@ -1193,7 +1197,12 @@ const SkipHistoryViewItem: FunctionComponent<{
11931197
11941198 return chainIds . map ( ( chainId , i ) => {
11951199 const chainInfo = chainStore . getChain ( chainId ) ;
1196- const completed = ! ! history . trackDone || i < history . routeIndex ;
1200+ // Only mark as completed based on routeIndex, not trackDone
1201+ const completed =
1202+ i < history . routeIndex ||
1203+ ( i === history . routeIndex &&
1204+ ! ! history . trackDone &&
1205+ ! history . trackError ) ;
11971206 const error = ! ! history . trackError && i >= failedRouteIndex ;
11981207
11991208 return (
@@ -1408,17 +1417,66 @@ const SwapV2HistoryViewItem: FunctionComponent<{
14081417} > = observer ( ( { history, removeHistory } ) => {
14091418 const { chainStore } = useStore ( ) ;
14101419
1420+ console . log ( "history" , history ) ;
1421+
14111422 const theme = useTheme ( ) ;
14121423 const intl = useIntl ( ) ;
14131424
1425+ const [ txExecution , setTxExecution ] = useState < TxExecution | undefined > (
1426+ undefined
1427+ ) ;
1428+
1429+ useEffect ( ( ) => {
1430+ if ( ! history . backgroundExecutionId ) {
1431+ setTxExecution ( undefined ) ;
1432+ return ;
1433+ }
1434+
1435+ const requester = new InExtensionMessageRequester ( ) ;
1436+ requester
1437+ . sendMessage (
1438+ BACKGROUND_PORT ,
1439+ new GetTxExecutionMsg ( history . backgroundExecutionId )
1440+ )
1441+ . then ( ( execution ) => {
1442+ setTxExecution ( execution ) ;
1443+ } )
1444+ . catch ( ( e ) => {
1445+ console . error ( "Failed to get tx execution:" , e ) ;
1446+ setTxExecution ( undefined ) ;
1447+ } ) ;
1448+ } , [ history . backgroundExecutionId ] ) ;
1449+
1450+ // 실행 가능한 트랜잭션이 있는지 확인 (멀티 TX 케이스)
1451+ const hasExecutableTx = ( ( ) => {
1452+ if ( ! txExecution || ! history . backgroundExecutionId ) {
1453+ return false ;
1454+ }
1455+ // pending 상태이면서 executableChainIds에 chainId가 포함된 트랜잭션이 있는 경우
1456+ return txExecution . txs . some (
1457+ ( tx ) =>
1458+ ( tx . status === BackgroundTxStatus . PENDING ||
1459+ tx . status === BackgroundTxStatus . BLOCKED ) &&
1460+ txExecution . executableChainIds . includes ( tx . chainId )
1461+ ) ;
1462+ } ) ( ) ;
1463+
14141464 const historyCompleted = ( ( ) => {
14151465 if ( ! history . trackDone ) {
14161466 return false ;
14171467 }
14181468
1419- // If there's a track error, check if swapRefundInfo exists (refund completed)
1469+ // If there's a track error, check if assetLocationInfo exists (refund completed)
14201470 if ( history . trackError ) {
1421- return ! ! history . swapRefundInfo ;
1471+ return ! ! history . assetLocationInfo ;
1472+ }
1473+
1474+ // SUCCESS 상태에서 assetLocationInfo가 없으면 destination에 도달한 것
1475+ if (
1476+ history . status === SwapV2TxStatus . SUCCESS &&
1477+ ! history . assetLocationInfo
1478+ ) {
1479+ return true ;
14221480 }
14231481
14241482 // Success or partial success with last route completed
@@ -1429,6 +1487,8 @@ const SwapV2HistoryViewItem: FunctionComponent<{
14291487 ) ;
14301488 } ) ( ) ;
14311489
1490+ console . log ( "historyCompleted" , historyCompleted , history ) ;
1491+
14321492 const failedRouteIndex = ( ( ) => {
14331493 // Failed if status is FAILED or if there's a trackError
14341494 if ( history . status === SwapV2TxStatus . FAILED || history . trackError ) {
@@ -1514,7 +1574,7 @@ const SwapV2HistoryViewItem: FunctionComponent<{
15141574 if ( failedRouteIndex >= 0 ) {
15151575 if (
15161576 history . status === SwapV2TxStatus . FAILED &&
1517- history . swapRefundInfo
1577+ history . assetLocationInfo
15181578 ) {
15191579 return intl . formatMessage ( {
15201580 id : "page.main.components.ibc-history-view.ibc-swap.item.refund.succeed" ,
@@ -1690,10 +1750,35 @@ const SwapV2HistoryViewItem: FunctionComponent<{
16901750 return route . chainId ;
16911751 } ) ;
16921752
1753+ // assetLocationInfo가 있으면 해당 체인까지는 asset이 릴리즈된 것이므로 성공으로 처리
1754+ const assetReleasedRouteIndex = ( ( ) => {
1755+ if ( history . assetLocationInfo ) {
1756+ const idx = chainIds . findIndex (
1757+ ( chainId ) => chainId === history . assetLocationInfo ! . chainId
1758+ ) ;
1759+ if ( idx >= 0 ) {
1760+ return idx ;
1761+ }
1762+ }
1763+ return - 1 ;
1764+ } ) ( ) ;
1765+
16931766 return chainIds . map ( ( chainId , i ) => {
16941767 const chainInfo = chainStore . getChain ( chainId ) ;
1695- const completed = ! ! history . trackDone || i < history . routeIndex ;
1696- const error = ! ! history . trackError && i >= failedRouteIndex ;
1768+ // Asset이 릴리즈된 체인까지는 성공으로 처리
1769+ const completed =
1770+ i < history . routeIndex ||
1771+ ( i === history . routeIndex &&
1772+ ! ! history . trackDone &&
1773+ ! history . trackError ) ||
1774+ ( assetReleasedRouteIndex >= 0 &&
1775+ i <= assetReleasedRouteIndex ) ;
1776+
1777+ // 에러는 assetReleasedRouteIndex보다 큰 인덱스에서만 표시
1778+ const error =
1779+ ! ! history . trackError &&
1780+ i >= failedRouteIndex &&
1781+ ( assetReleasedRouteIndex < 0 || i > assetReleasedRouteIndex ) ;
16971782
16981783 return (
16991784 // 일부분 순환하는 경우도 이론적으로 가능은 하기 때문에 chain id를 key로 사용하지 않음.
@@ -1703,6 +1788,13 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17031788 completed = { ! error && completed }
17041789 notCompletedBlink = { ( ( ) => {
17051790 if ( failedRoute ) {
1791+ // asset이 릴리즈된 체인까지는 blink하지 않음
1792+ if (
1793+ assetReleasedRouteIndex >= 0 &&
1794+ i <= assetReleasedRouteIndex
1795+ ) {
1796+ return false ;
1797+ }
17061798 return i === failedRouteIndex ;
17071799 }
17081800
@@ -1721,7 +1813,12 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17211813 return "right" ;
17221814 }
17231815
1724- return i === failedRouteIndex ? "left" : "hide" ;
1816+ // asset이 릴리즈된 체인이 있으면, 그 이후는 화살표 숨김
1817+ if ( assetReleasedRouteIndex >= 0 ) {
1818+ return i > assetReleasedRouteIndex ? "hide" : "right" ;
1819+ }
1820+
1821+ return i === failedRouteIndex ? "left" : "right" ;
17251822 } ) ( ) }
17261823 error = { error }
17271824 isLast = { chainIds . length - 1 === i }
@@ -1748,29 +1845,31 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17481845 history . status === SwapV2TxStatus . PARTIAL_SUCCESS ;
17491846
17501847 // status tracking이 오류로 끝난 경우
1751- // SwapV2에서는 swapRefundInfo를 사용하여 환불 정보 표시
1848+ // SwapV2에서는 assetLocationInfo를 사용하여 환불 정보 표시
17521849 if (
17531850 history . trackDone &&
17541851 ( history . trackError ||
17551852 history . status === SwapV2TxStatus . FAILED )
17561853 ) {
1757- if ( history . swapRefundInfo ) {
1758- if ( chainStore . hasChain ( history . swapRefundInfo . chainId ) ) {
1759- const swapRefundChain = chainStore . getChain (
1760- history . swapRefundInfo . chainId
1854+ if ( history . assetLocationInfo ) {
1855+ if (
1856+ chainStore . hasChain ( history . assetLocationInfo . chainId )
1857+ ) {
1858+ const assetLocationChain = chainStore . getChain (
1859+ history . assetLocationInfo . chainId
17611860 ) ;
17621861
17631862 return intl . formatMessage (
17641863 {
17651864 id : "page.main.components.ibc-history-view.skip-swap.failed.after-transfer.complete" ,
17661865 } ,
17671866 {
1768- chain : swapRefundChain . chainName ,
1769- assets : history . swapRefundInfo . amount
1867+ chain : assetLocationChain . chainName ,
1868+ assets : history . assetLocationInfo . amount
17701869 . map ( ( amount ) => {
17711870 return new CoinPretty (
17721871 chainStore
1773- . getChain ( history . swapRefundInfo ! . chainId )
1872+ . getChain ( history . assetLocationInfo ! . chainId )
17741873 . forceFindCurrency ( amount . denom ) ,
17751874 amount . amount
17761875 )
@@ -1796,6 +1895,27 @@ const SwapV2HistoryViewItem: FunctionComponent<{
17961895 </ Caption1 >
17971896 </ VerticalCollapseTransition >
17981897
1898+ { /* 추가 트랜잭션 실행이 필요한 경우 버튼 표시 */ }
1899+ < VerticalCollapseTransition collapsed = { ! hasExecutableTx } >
1900+ < Gutter size = "1rem" />
1901+ < Button
1902+ text = { intl . formatMessage ( {
1903+ id : "page.main.components.ibc-history-view.swap-v2.continue-transaction" ,
1904+ } ) }
1905+ size = "small"
1906+ color = "primary"
1907+ onClick = { ( ) => {
1908+ if ( ! txExecution ) {
1909+ return ;
1910+ }
1911+
1912+ // TODO: execution 데이터를 사용해서 다음 트랜잭션 실행 페이지로 이동
1913+ console . log ( "Execution data:" , txExecution ) ;
1914+ console . log ( "Pending txs:" , txExecution . txs ) ;
1915+ } }
1916+ />
1917+ </ VerticalCollapseTransition >
1918+
17991919 < VerticalCollapseTransition collapsed = { historyCompleted } >
18001920 < Gutter size = "1rem" />
18011921 < Box
0 commit comments