Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 9728e9d

Browse files
authored
feat: expansion local storage (#1539)
* feat: expansion local storage * chore: helper function
1 parent 8b005ec commit 9728e9d

File tree

3 files changed

+186
-4
lines changed

3 files changed

+186
-4
lines changed

src/ui/common/hooks/services/useExpansionVisibilityService.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function useExpansionVisibilityService(
3131
* Checks if a delegation is a broadcasted expansion.
3232
* A broadcasted expansion must meet both criteria:
3333
* 1. Has VERIFIED status in the API data (delegation.state)
34-
* 2. Has INTERMEDIATE_PENDING_VERIFICATION status in localStorage (user broadcasted it)
34+
* 2. Has INTERMEDIATE_PENDING_BTC_CONFIRMATION status in localStorage (user broadcasted it)
3535
*/
3636
const isBroadcastedExpansion = useCallback(
3737
(delegation: DelegationV2): boolean => {
@@ -40,7 +40,7 @@ export function useExpansionVisibilityService(
4040
return false;
4141
}
4242

43-
// Then check if this delegation exists in localStorage with INTERMEDIATE_PENDING_VERIFICATION status
43+
// Then check if this delegation exists in localStorage with INTERMEDIATE_PENDING_BTC_CONFIRMATION status
4444
const storedDelegation = (expansionStorageDelegations ?? []).find(
4545
(stored) =>
4646
stored.stakingTxHashHex.toLowerCase() ===
@@ -49,7 +49,7 @@ export function useExpansionVisibilityService(
4949

5050
return (
5151
storedDelegation?.state ===
52-
DelegationV2StakingState.INTERMEDIATE_PENDING_VERIFICATION
52+
DelegationV2StakingState.INTERMEDIATE_PENDING_BTC_CONFIRMATION
5353
);
5454
},
5555
[expansionStorageDelegations],
@@ -77,7 +77,7 @@ export function useExpansionVisibilityService(
7777
* Returns delegations that should be visible in the Activity tab.
7878
* Applies the following rules:
7979
* 1. Exclude VERIFIED expansions that are not broadcasted (show in modal only)
80-
* 2. Include VERIFIED expansions that are broadcasted (INTERMEDIATE_PENDING_VERIFICATION)
80+
* 2. Include VERIFIED expansions that are broadcasted (INTERMEDIATE_PENDING_BTC_CONFIRMATION)
8181
* 3. Exclude original transactions that have broadcasted expansions
8282
* 4. Include all other regular transactions
8383
*/

src/ui/common/hooks/services/useStakingExpansionService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
DelegationV2,
1818
} from "@/ui/common/types/delegationsV2";
1919
import { retry } from "@/ui/common/utils";
20+
import { markExpansionAsBroadcasted } from "@/ui/common/utils/local_storage/expansionStorage";
2021
import { getTxHex } from "@/ui/common/utils/mempool_api";
2122
import { validateExpansionFormData } from "@/ui/common/utils/stakingExpansionValidation";
2223

@@ -388,6 +389,12 @@ export function useStakingExpansionService() {
388389
DelegationState.INTERMEDIATE_PENDING_BTC_CONFIRMATION,
389390
);
390391

392+
// Mark expansion as broadcasted in localStorage for visibility tracking
393+
markExpansionAsBroadcasted(
394+
delegation.stakingTxHashHex,
395+
publicKeyNoCoord,
396+
);
397+
391398
// Navigate to success
392399
goToStep(StakingExpansionStep.FEEDBACK_SUCCESS);
393400
setProcessing(false);
@@ -409,6 +416,7 @@ export function useStakingExpansionService() {
409416
reset,
410417
isUTXOsLoading,
411418
availableUTXOs,
419+
publicKeyNoCoord,
412420
],
413421
);
414422

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import {
2+
DelegationV2,
3+
DelegationV2StakingState,
4+
} from "@/ui/common/types/delegationsV2";
5+
6+
import { getExpansionsLocalStorageKey } from "./getExpansionsLocalStorageKey";
7+
8+
/**
9+
* Helper function to remove an item from a localStorage record.
10+
* This centralizes the localStorage record manipulation pattern used throughout the file.
11+
*
12+
* @param storageKey - The localStorage key
13+
* @param itemId - The ID of the item to remove from the record
14+
*/
15+
function removeFromLocalStorageRecord(
16+
storageKey: string,
17+
itemId: string,
18+
): void {
19+
try {
20+
const data = localStorage.getItem(storageKey);
21+
if (data) {
22+
const record = JSON.parse(data);
23+
if (record[itemId]) {
24+
delete record[itemId];
25+
localStorage.setItem(storageKey, JSON.stringify(record));
26+
}
27+
}
28+
} catch (error) {
29+
console.error(
30+
`Failed to remove item from localStorage key ${storageKey}:`,
31+
error,
32+
);
33+
}
34+
}
35+
36+
/**
37+
* Mark an expansion as broadcasted by updating its status to INTERMEDIATE_PENDING_BTC_CONFIRMATION.
38+
* This is called after a verified expansion is successfully signed and broadcasted to Bitcoin.
39+
*
40+
* @param expansionTxHashHex - The transaction hash of the expansion to mark as broadcasted
41+
* @param publicKeyNoCoord - The public key of the wallet
42+
*/
43+
export function markExpansionAsBroadcasted(
44+
expansionTxHashHex: string,
45+
publicKeyNoCoord: string | undefined,
46+
): void {
47+
if (!publicKeyNoCoord) {
48+
console.warn(
49+
"Cannot mark expansion as broadcasted: no public key provided",
50+
);
51+
return;
52+
}
53+
54+
const storageKey = getExpansionsLocalStorageKey(publicKeyNoCoord);
55+
const statusesKey = `${storageKey}_statuses`;
56+
57+
try {
58+
// Get existing statuses from localStorage
59+
const existingStatuses = localStorage.getItem(statusesKey);
60+
const statuses = existingStatuses ? JSON.parse(existingStatuses) : {};
61+
62+
// Update the status for this expansion
63+
statuses[expansionTxHashHex] =
64+
DelegationV2StakingState.INTERMEDIATE_PENDING_BTC_CONFIRMATION;
65+
66+
// Save back to localStorage
67+
localStorage.setItem(statusesKey, JSON.stringify(statuses));
68+
} catch (error) {
69+
console.error("Failed to mark expansion as broadcasted:", error);
70+
}
71+
}
72+
73+
/**
74+
* Get all broadcasted expansions from localStorage.
75+
* Broadcasted expansions are those with INTERMEDIATE_PENDING_BTC_CONFIRMATION status.
76+
*
77+
* @param publicKeyNoCoord - The public key of the wallet
78+
* @param expansions - The list of expansions to check against localStorage
79+
* @returns Array of expansions that have been broadcasted
80+
*/
81+
export function getBroadcastedExpansions(
82+
publicKeyNoCoord: string | undefined,
83+
expansions: DelegationV2[],
84+
): DelegationV2[] {
85+
if (!publicKeyNoCoord || !expansions || expansions.length === 0) {
86+
return [];
87+
}
88+
89+
const storageKey = getExpansionsLocalStorageKey(publicKeyNoCoord);
90+
const statusesKey = `${storageKey}_statuses`;
91+
92+
try {
93+
// Get statuses from localStorage
94+
const storedStatuses = localStorage.getItem(statusesKey);
95+
if (!storedStatuses) {
96+
return [];
97+
}
98+
99+
const statuses = JSON.parse(storedStatuses);
100+
101+
// Filter expansions to only include those marked as broadcasted
102+
return expansions.filter(
103+
(expansion) =>
104+
statuses[expansion.stakingTxHashHex] ===
105+
DelegationV2StakingState.INTERMEDIATE_PENDING_BTC_CONFIRMATION,
106+
);
107+
} catch (error) {
108+
console.error("Failed to get broadcasted expansions:", error);
109+
return [];
110+
}
111+
}
112+
113+
/**
114+
* Clean up a broadcasted expansion from localStorage when it becomes ACTIVE.
115+
* This is called when an expansion transitions from broadcasted to confirmed on-chain.
116+
*
117+
* @param expansionTxHashHex - The transaction hash of the expansion to clean up
118+
* @param publicKeyNoCoord - The public key of the wallet
119+
*/
120+
export function cleanupActiveExpansion(
121+
expansionTxHashHex: string,
122+
publicKeyNoCoord: string | undefined,
123+
): void {
124+
if (!publicKeyNoCoord) {
125+
return;
126+
}
127+
128+
const storageKey = getExpansionsLocalStorageKey(publicKeyNoCoord);
129+
const pendingKey = `${storageKey}_pending`;
130+
const statusesKey = `${storageKey}_statuses`;
131+
132+
// Remove from pending delegations
133+
removeFromLocalStorageRecord(pendingKey, expansionTxHashHex);
134+
135+
// Remove from statuses
136+
removeFromLocalStorageRecord(statusesKey, expansionTxHashHex);
137+
}
138+
139+
/**
140+
* Check if an expansion has been broadcasted.
141+
* This is a convenience function that checks if the expansion has
142+
* INTERMEDIATE_PENDING_BTC_CONFIRMATION status in localStorage.
143+
*
144+
* @param expansionTxHashHex - The transaction hash to check
145+
* @param publicKeyNoCoord - The public key of the wallet
146+
* @returns true if the expansion has been broadcasted, false otherwise
147+
*/
148+
export function isExpansionBroadcasted(
149+
expansionTxHashHex: string,
150+
publicKeyNoCoord: string | undefined,
151+
): boolean {
152+
if (!publicKeyNoCoord) {
153+
return false;
154+
}
155+
156+
const storageKey = getExpansionsLocalStorageKey(publicKeyNoCoord);
157+
const statusesKey = `${storageKey}_statuses`;
158+
159+
try {
160+
const statusesData = localStorage.getItem(statusesKey);
161+
if (!statusesData) {
162+
return false;
163+
}
164+
165+
const statuses = JSON.parse(statusesData);
166+
return (
167+
statuses[expansionTxHashHex] ===
168+
DelegationV2StakingState.INTERMEDIATE_PENDING_BTC_CONFIRMATION
169+
);
170+
} catch (error) {
171+
console.error("Failed to check if expansion is broadcasted:", error);
172+
return false;
173+
}
174+
}

0 commit comments

Comments
 (0)