forked from Prompt-Hash-Stellar/prompt-hash
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransactionProvider.tsx
More file actions
83 lines (71 loc) · 2.62 KB
/
Copy pathTransactionProvider.tsx
File metadata and controls
83 lines (71 loc) · 2.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import React, { createContext, useContext, useState, useCallback, useEffect, ReactNode } from "react";
import { StatusBanner } from "./StatusBanner";
import "./TransactionProvider.css";
export type TransactionStatus = "idle" | "pending" | "success" | "error";
export interface TransactionState {
id: string;
status: TransactionStatus;
message: string;
retryAction?: () => void;
}
interface TransactionContextType {
transactions: TransactionState[];
addTransaction: (_tx: TransactionState) => void;
updateTransaction: (_id: string, _updates: Partial<TransactionState>) => void;
removeTransaction: (_id: string) => void;
}
const TransactionContext = createContext<TransactionContextType | undefined>(undefined);
export const TransactionProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [transactions, setTransactions] = useState<TransactionState[]>([]);
const addTransaction = useCallback((tx: TransactionState) => {
setTransactions((prev) => [...prev, tx]);
}, []);
const updateTransaction = useCallback((id: string, updates: Partial<TransactionState>) => {
setTransactions((prev) =>
prev.map((tx) => {
if (tx.id === id) {
const updated = { ...tx, ...updates };
return updated;
}
return tx;
})
);
}, []);
const removeTransaction = useCallback((id: string) => {
setTransactions((prev) => prev.filter((tx) => tx.id !== id));
}, []);
// Programmatic accessibility focus check
useEffect(() => {
if (transactions.some((tx) => tx.status === "error")) {
// Allow DOM to finish rendering the Retry button before querying it
setTimeout(() => {
const retryBtn = document.querySelector(".retry-btn") as HTMLButtonElement | null;
if (retryBtn) retryBtn.focus();
}, 0);
}
}, [transactions]);
return (
<TransactionContext.Provider value={{ transactions, addTransaction, updateTransaction, removeTransaction }}>
{children}
<div className="notification-container">
{transactions.map((tx) => (
<div key={tx.id} className={`notification ${tx.status} slide-in`}>
<StatusBanner
status={tx.status}
message={tx.message}
onRetry={tx.retryAction}
onDismiss={() => removeTransaction(tx.id)}
/>
</div>
))}
</div>
</TransactionContext.Provider>
);
};
export const useTransactionFeedback = () => {
const context = useContext(TransactionContext);
if (!context) {
throw new Error("useTransactionFeedback must be used within a TransactionProvider");
}
return context;
};