@trustless-work/blocks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -0
- package/bin/index.js +1123 -0
- package/package.json +44 -0
- package/templates/deps.json +29 -0
- package/templates/escrows/details/Actions.tsx +149 -0
- package/templates/escrows/details/Entities.tsx +48 -0
- package/templates/escrows/details/EntityCard.tsx +98 -0
- package/templates/escrows/details/EscrowDetailDialog.tsx +154 -0
- package/templates/escrows/details/GeneralInformation.tsx +329 -0
- package/templates/escrows/details/MilestoneCard.tsx +254 -0
- package/templates/escrows/details/MilestoneDetailDialog.tsx +276 -0
- package/templates/escrows/details/Milestones.tsx +87 -0
- package/templates/escrows/details/ProgressEscrow.tsx +191 -0
- package/templates/escrows/details/StatisticsCard.tsx +79 -0
- package/templates/escrows/details/SuccessReleaseDialog.tsx +101 -0
- package/templates/escrows/details/useDetailsEscrow.ts +126 -0
- package/templates/escrows/escrow-context/EscrowAmountProvider.tsx +86 -0
- package/templates/escrows/escrow-context/EscrowDialogsProvider.tsx +108 -0
- package/templates/escrows/escrow-context/EscrowProvider.tsx +124 -0
- package/templates/escrows/escrows-by-role/cards/EscrowsCards.tsx +503 -0
- package/templates/escrows/escrows-by-role/cards/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/table/EscrowsTable.tsx +427 -0
- package/templates/escrows/escrows-by-role/table/Filters.tsx +421 -0
- package/templates/escrows/escrows-by-role/useEscrowsByRole.shared.ts +336 -0
- package/templates/escrows/escrows-by-signer/cards/EscrowsCards.tsx +502 -0
- package/templates/escrows/escrows-by-signer/cards/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/table/EscrowsTable.tsx +422 -0
- package/templates/escrows/escrows-by-signer/table/Filters.tsx +389 -0
- package/templates/escrows/escrows-by-signer/useEscrowsBySigner.shared.ts +320 -0
- package/templates/escrows/single-release/approve-milestone/button/ApproveMilestone.tsx +78 -0
- package/templates/escrows/single-release/approve-milestone/dialog/ApproveMilestone.tsx +102 -0
- package/templates/escrows/single-release/approve-milestone/form/ApproveMilestone.tsx +80 -0
- package/templates/escrows/single-release/approve-milestone/shared/schema.ts +9 -0
- package/templates/escrows/single-release/approve-milestone/shared/useApproveMilestone.ts +67 -0
- package/templates/escrows/single-release/change-milestone-status/button/ChangeMilestoneStatus.tsx +78 -0
- package/templates/escrows/single-release/change-milestone-status/dialog/ChangeMilestoneStatus.tsx +167 -0
- package/templates/escrows/single-release/change-milestone-status/form/ChangeMilestoneStatus.tsx +114 -0
- package/templates/escrows/single-release/change-milestone-status/shared/schema.ts +15 -0
- package/templates/escrows/single-release/change-milestone-status/shared/useChangeMilestoneStatus.ts +77 -0
- package/templates/escrows/single-release/dispute-escrow/button/DisputeEscrow.tsx +68 -0
- package/templates/escrows/single-release/fund-escrow/button/FundEscrow.tsx +84 -0
- package/templates/escrows/single-release/fund-escrow/dialog/FundEscrow.tsx +77 -0
- package/templates/escrows/single-release/fund-escrow/form/FundEscrow.tsx +54 -0
- package/templates/escrows/single-release/fund-escrow/shared/schema.ts +10 -0
- package/templates/escrows/single-release/fund-escrow/shared/useFundEscrow.ts +66 -0
- package/templates/escrows/single-release/initialize-escrow/dialog/InitializeEscrow.tsx +526 -0
- package/templates/escrows/single-release/initialize-escrow/form/InitializeEscrow.tsx +504 -0
- package/templates/escrows/single-release/initialize-escrow/shared/schema.ts +232 -0
- package/templates/escrows/single-release/initialize-escrow/shared/useInitializeEscrow.ts +115 -0
- package/templates/escrows/single-release/release-escrow/button/ReleaseEscrow.tsx +80 -0
- package/templates/escrows/single-release/resolve-dispute/button/ResolveDispute.tsx +94 -0
- package/templates/escrows/single-release/resolve-dispute/dialog/ResolveDispute.tsx +123 -0
- package/templates/escrows/single-release/resolve-dispute/form/ResolveDispute.tsx +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/schema.ts +82 -0
- package/templates/escrows/single-release/resolve-dispute/shared/useResolveDispute.ts +58 -0
- package/templates/escrows/single-release/update-escrow/dialog/UpdateEscrow.tsx +485 -0
- package/templates/escrows/single-release/update-escrow/form/UpdateEscrow.tsx +463 -0
- package/templates/escrows/single-release/update-escrow/shared/schema.ts +139 -0
- package/templates/escrows/single-release/update-escrow/shared/useUpdateEscrow.ts +211 -0
- package/templates/handle-errors/errors.enum.ts +6 -0
- package/templates/handle-errors/handle.ts +47 -0
- package/templates/helpers/format.helper.ts +27 -0
- package/templates/helpers/useCopy.ts +13 -0
- package/templates/providers/ReactQueryClientProvider.tsx +28 -0
- package/templates/providers/TrustlessWork.tsx +30 -0
- package/templates/tanstak/useEscrowsByRoleQuery.ts +87 -0
- package/templates/tanstak/useEscrowsBySignerQuery.ts +78 -0
- package/templates/tanstak/useEscrowsMutations.ts +411 -0
- package/templates/wallet-kit/WalletButtons.tsx +116 -0
- package/templates/wallet-kit/WalletProvider.tsx +94 -0
- package/templates/wallet-kit/trustlines.ts +40 -0
- package/templates/wallet-kit/useWallet.ts +77 -0
- package/templates/wallet-kit/validators.ts +12 -0
- package/templates/wallet-kit/wallet-kit.ts +30 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useMemo } from "react";
|
|
4
|
+
import {
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogDescription,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
} from "__UI_BASE__/dialog";
|
|
11
|
+
import { Card } from "__UI_BASE__/card";
|
|
12
|
+
import EntityCard from "./EntityCard";
|
|
13
|
+
import { useEscrowContext } from "../../escrow-context/EscrowProvider";
|
|
14
|
+
import { useEscrowAmountContext } from "../../escrow-context/EscrowAmountProvider";
|
|
15
|
+
import { CircleCheckBig } from "lucide-react";
|
|
16
|
+
|
|
17
|
+
interface SuccessReleaseDialogProps {
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
onOpenChange: (open: boolean) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function SuccessReleaseDialog({
|
|
23
|
+
isOpen,
|
|
24
|
+
onOpenChange,
|
|
25
|
+
}: SuccessReleaseDialogProps) {
|
|
26
|
+
const { selectedEscrow } = useEscrowContext();
|
|
27
|
+
const { receiverAmount, platformFeeAmount, trustlessWorkAmount } =
|
|
28
|
+
useEscrowAmountContext();
|
|
29
|
+
|
|
30
|
+
const platformFee = Number(selectedEscrow?.platformFee || 0);
|
|
31
|
+
const trustlessPercentage = 0.3;
|
|
32
|
+
const receiverPercentage = 100 - (platformFee + trustlessPercentage);
|
|
33
|
+
|
|
34
|
+
const currency = selectedEscrow?.trustline?.name ?? "";
|
|
35
|
+
|
|
36
|
+
const cards = useMemo(
|
|
37
|
+
() => [
|
|
38
|
+
{
|
|
39
|
+
type: "Platform",
|
|
40
|
+
entity: selectedEscrow?.roles?.platformAddress,
|
|
41
|
+
percentage: platformFee,
|
|
42
|
+
amount: platformFeeAmount,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "Trustless Work",
|
|
46
|
+
entity: "Private",
|
|
47
|
+
percentage: trustlessPercentage,
|
|
48
|
+
amount: trustlessWorkAmount,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: "Receiver",
|
|
52
|
+
entity: selectedEscrow?.roles?.receiver,
|
|
53
|
+
percentage: receiverPercentage,
|
|
54
|
+
amount: receiverAmount,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
platformFee,
|
|
59
|
+
receiverPercentage,
|
|
60
|
+
trustlessPercentage,
|
|
61
|
+
platformFeeAmount,
|
|
62
|
+
trustlessWorkAmount,
|
|
63
|
+
receiverAmount,
|
|
64
|
+
selectedEscrow?.roles?.platformAddress,
|
|
65
|
+
selectedEscrow?.roles?.receiver,
|
|
66
|
+
]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
|
71
|
+
<DialogContent className="max-w-md sm:max-w-lg md:max-w-2xl">
|
|
72
|
+
<DialogHeader>
|
|
73
|
+
<DialogTitle className="flex items-center gap-2">
|
|
74
|
+
<CircleCheckBig className="h-5 w-5 text-green-600" />
|
|
75
|
+
Release Successful
|
|
76
|
+
</DialogTitle>
|
|
77
|
+
<DialogDescription>
|
|
78
|
+
Funds were distributed successfully to the corresponding parties.
|
|
79
|
+
</DialogDescription>
|
|
80
|
+
</DialogHeader>
|
|
81
|
+
|
|
82
|
+
<div className="flex flex-col gap-3">
|
|
83
|
+
{cards.map((c) => (
|
|
84
|
+
<EntityCard
|
|
85
|
+
key={c.type}
|
|
86
|
+
type={c.type}
|
|
87
|
+
entity={c.entity}
|
|
88
|
+
hasPercentage
|
|
89
|
+
percentage={Number(c.percentage.toFixed(2))}
|
|
90
|
+
hasAmount
|
|
91
|
+
amount={Number(c.amount.toFixed(2))}
|
|
92
|
+
currency={currency}
|
|
93
|
+
/>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
</DialogContent>
|
|
97
|
+
</Dialog>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default SuccessReleaseDialog;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
|
|
3
|
+
import { useWalletContext } from "@/components/tw-blocks/wallet-kit/WalletProvider";
|
|
4
|
+
import { useEscrowContext } from "../../escrow-context/EscrowProvider";
|
|
5
|
+
import { useEscrowAmountContext } from "../../escrow-context/EscrowAmountProvider";
|
|
6
|
+
|
|
7
|
+
interface EscrowDetailDialogProps {
|
|
8
|
+
setIsDialogOpen: (value: boolean) => void;
|
|
9
|
+
setSelectedEscrow: (selectedEscrow?: Escrow) => void;
|
|
10
|
+
selectedEscrow: Escrow | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const useEscrowDetailDialog = ({
|
|
14
|
+
setIsDialogOpen,
|
|
15
|
+
setSelectedEscrow,
|
|
16
|
+
selectedEscrow,
|
|
17
|
+
}: EscrowDetailDialogProps) => {
|
|
18
|
+
const { walletAddress } = useWalletContext();
|
|
19
|
+
const { userRolesInEscrow, setUserRolesInEscrow } = useEscrowContext();
|
|
20
|
+
|
|
21
|
+
const { setAmounts } = useEscrowAmountContext();
|
|
22
|
+
|
|
23
|
+
const fetchingRef = useRef(false);
|
|
24
|
+
const lastFetchKey = useRef("");
|
|
25
|
+
const [evidenceVisibleMap, setEvidenceVisibleMap] = useState<{
|
|
26
|
+
[key: number]: boolean;
|
|
27
|
+
}>({});
|
|
28
|
+
|
|
29
|
+
const totalAmount = Number(selectedEscrow?.amount || 0);
|
|
30
|
+
const platformFeePercentage = Number(selectedEscrow?.platformFee || 0);
|
|
31
|
+
|
|
32
|
+
const handleClose = useCallback(() => {
|
|
33
|
+
setIsDialogOpen(false);
|
|
34
|
+
setSelectedEscrow(undefined);
|
|
35
|
+
}, [setIsDialogOpen, setSelectedEscrow]);
|
|
36
|
+
|
|
37
|
+
const areAllMilestonesApproved =
|
|
38
|
+
selectedEscrow?.milestones?.every((milestone) => {
|
|
39
|
+
if ("flags" in milestone) {
|
|
40
|
+
return milestone.flags?.approved === true;
|
|
41
|
+
}
|
|
42
|
+
return "approved" in milestone && milestone.approved === true;
|
|
43
|
+
}) ?? false;
|
|
44
|
+
|
|
45
|
+
const fetchUserRoleInEscrow = useCallback(async () => {
|
|
46
|
+
if (!selectedEscrow?.contractId || !walletAddress) return null;
|
|
47
|
+
|
|
48
|
+
const roleMappings = [
|
|
49
|
+
{ name: "approver", address: selectedEscrow.roles.approver },
|
|
50
|
+
{
|
|
51
|
+
name: "serviceProvider",
|
|
52
|
+
address: selectedEscrow.roles.serviceProvider,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "platformAddress",
|
|
56
|
+
address: selectedEscrow.roles.platformAddress,
|
|
57
|
+
},
|
|
58
|
+
{ name: "releaseSigner", address: selectedEscrow.roles.releaseSigner },
|
|
59
|
+
{
|
|
60
|
+
name: "disputeResolver",
|
|
61
|
+
address: selectedEscrow.roles.disputeResolver,
|
|
62
|
+
},
|
|
63
|
+
{ name: "receiver", address: selectedEscrow.roles.receiver },
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const userRoles = roleMappings
|
|
67
|
+
.filter((role) => role.address === walletAddress)
|
|
68
|
+
.map((role) => role.name);
|
|
69
|
+
|
|
70
|
+
return userRoles;
|
|
71
|
+
}, [selectedEscrow?.contractId, walletAddress]);
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
let timeoutId: NodeJS.Timeout | undefined = undefined;
|
|
75
|
+
let isMounted = true;
|
|
76
|
+
|
|
77
|
+
const fetchRoles = async () => {
|
|
78
|
+
if (!selectedEscrow || !walletAddress || fetchingRef.current) return;
|
|
79
|
+
|
|
80
|
+
const fetchKey = `${selectedEscrow.contractId}-${walletAddress}`;
|
|
81
|
+
if (fetchKey === lastFetchKey.current) return;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
fetchingRef.current = true;
|
|
85
|
+
lastFetchKey.current = fetchKey;
|
|
86
|
+
const roleData = await fetchUserRoleInEscrow();
|
|
87
|
+
if (isMounted && roleData) {
|
|
88
|
+
setUserRolesInEscrow(roleData);
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error("[EscrowDetailDialog] Error fetching roles:", error);
|
|
92
|
+
} finally {
|
|
93
|
+
fetchingRef.current = false;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
timeoutId = setTimeout(fetchRoles, 100);
|
|
98
|
+
|
|
99
|
+
return () => {
|
|
100
|
+
isMounted = false;
|
|
101
|
+
if (timeoutId) {
|
|
102
|
+
clearTimeout(timeoutId);
|
|
103
|
+
}
|
|
104
|
+
fetchingRef.current = false;
|
|
105
|
+
};
|
|
106
|
+
}, [
|
|
107
|
+
selectedEscrow,
|
|
108
|
+
fetchUserRoleInEscrow,
|
|
109
|
+
setUserRolesInEscrow,
|
|
110
|
+
walletAddress,
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
setAmounts(totalAmount, platformFeePercentage);
|
|
115
|
+
}, [totalAmount, platformFeePercentage, setAmounts]);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
handleClose,
|
|
119
|
+
setEvidenceVisibleMap,
|
|
120
|
+
evidenceVisibleMap,
|
|
121
|
+
areAllMilestonesApproved,
|
|
122
|
+
userRolesInEscrow,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export default useEscrowDetailDialog;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useContext,
|
|
7
|
+
useMemo,
|
|
8
|
+
useState,
|
|
9
|
+
ReactNode,
|
|
10
|
+
useCallback,
|
|
11
|
+
} from "react";
|
|
12
|
+
|
|
13
|
+
export type AmountEscrowStore = {
|
|
14
|
+
receiverAmount: number;
|
|
15
|
+
platformFeeAmount: number;
|
|
16
|
+
trustlessWorkAmount: number;
|
|
17
|
+
receiverResolve: number;
|
|
18
|
+
approverResolve: number;
|
|
19
|
+
amountMoonpay: number;
|
|
20
|
+
setAmounts: (totalAmount: number, platformFee: number) => void;
|
|
21
|
+
setReceiverResolve: (value: number) => void;
|
|
22
|
+
setApproverResolve: (value: number) => void;
|
|
23
|
+
setAmountMoonpay: (value: number) => void;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const EscrowAmountContext = createContext<AmountEscrowStore | undefined>(
|
|
27
|
+
undefined
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const EscrowAmountProvider = ({ children }: { children: ReactNode }) => {
|
|
31
|
+
const [receiverAmount, setReceiverAmount] = useState(0);
|
|
32
|
+
const [platformFeeAmount, setPlatformFeeAmount] = useState(0);
|
|
33
|
+
const [trustlessWorkAmount, setTrustlessWorkAmount] = useState(0);
|
|
34
|
+
const [receiverResolve, setReceiverResolve] = useState(0);
|
|
35
|
+
const [approverResolve, setApproverResolve] = useState(0);
|
|
36
|
+
const [amountMoonpay, setAmountMoonpay] = useState(0);
|
|
37
|
+
|
|
38
|
+
const setAmounts: AmountEscrowStore["setAmounts"] = useCallback(
|
|
39
|
+
(totalAmount, platformFee) => {
|
|
40
|
+
const trustlessPercentage = 0.3;
|
|
41
|
+
const receiverPercentage = 100 - (trustlessPercentage + platformFee);
|
|
42
|
+
|
|
43
|
+
setReceiverAmount((totalAmount * receiverPercentage) / 100);
|
|
44
|
+
setPlatformFeeAmount((totalAmount * platformFee) / 100);
|
|
45
|
+
setTrustlessWorkAmount((totalAmount * trustlessPercentage) / 100);
|
|
46
|
+
},
|
|
47
|
+
[]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const value = useMemo<AmountEscrowStore>(
|
|
51
|
+
() => ({
|
|
52
|
+
receiverAmount,
|
|
53
|
+
platformFeeAmount,
|
|
54
|
+
trustlessWorkAmount,
|
|
55
|
+
receiverResolve,
|
|
56
|
+
approverResolve,
|
|
57
|
+
amountMoonpay,
|
|
58
|
+
setAmounts,
|
|
59
|
+
setReceiverResolve,
|
|
60
|
+
setApproverResolve,
|
|
61
|
+
setAmountMoonpay,
|
|
62
|
+
}),
|
|
63
|
+
[
|
|
64
|
+
receiverAmount,
|
|
65
|
+
platformFeeAmount,
|
|
66
|
+
trustlessWorkAmount,
|
|
67
|
+
receiverResolve,
|
|
68
|
+
approverResolve,
|
|
69
|
+
amountMoonpay,
|
|
70
|
+
]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<EscrowAmountContext.Provider value={value}>
|
|
75
|
+
{children}
|
|
76
|
+
</EscrowAmountContext.Provider>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export function useEscrowAmountContext() {
|
|
81
|
+
const ctx = useContext(EscrowAmountContext);
|
|
82
|
+
if (!ctx) {
|
|
83
|
+
throw new Error("useEscrowAmount must be used within EscrowAmountProvider");
|
|
84
|
+
}
|
|
85
|
+
return ctx;
|
|
86
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { createContext, useContext, useMemo, useState } from "react";
|
|
5
|
+
|
|
6
|
+
export type DialogState = {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
setIsOpen: (open: boolean) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type DialogStates = {
|
|
12
|
+
second: DialogState;
|
|
13
|
+
completeMilestone: DialogState;
|
|
14
|
+
qr: DialogState;
|
|
15
|
+
resolveDispute: DialogState;
|
|
16
|
+
editMilestone: DialogState;
|
|
17
|
+
editEntities: DialogState;
|
|
18
|
+
editBasicProperties: DialogState;
|
|
19
|
+
successRelease: DialogState;
|
|
20
|
+
successResolveDispute: DialogState;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type StatusStates = {};
|
|
24
|
+
|
|
25
|
+
type EscrowDialogsContextType = DialogStates & StatusStates;
|
|
26
|
+
|
|
27
|
+
const EscrowDialogsContext = createContext<
|
|
28
|
+
EscrowDialogsContextType | undefined
|
|
29
|
+
>(undefined);
|
|
30
|
+
|
|
31
|
+
export function EscrowDialogsProvider({
|
|
32
|
+
children,
|
|
33
|
+
}: {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
}) {
|
|
36
|
+
const [secondOpen, setSecondOpen] = useState(false);
|
|
37
|
+
const [completeMilestoneOpen, setCompleteMilestoneOpen] = useState(false);
|
|
38
|
+
const [qrOpen, setQrOpen] = useState(false);
|
|
39
|
+
const [resolveDisputeOpen, setResolveDisputeOpen] = useState(false);
|
|
40
|
+
const [editMilestoneOpen, setEditMilestoneOpen] = useState(false);
|
|
41
|
+
const [editEntitiesOpen, setEditEntitiesOpen] = useState(false);
|
|
42
|
+
const [editBasicPropertiesOpen, setEditBasicPropertiesOpen] = useState(false);
|
|
43
|
+
const [successReleaseOpen, setSuccessReleaseOpen] = useState(false);
|
|
44
|
+
const [successResolveDisputeOpen, setSuccessResolveDisputeOpen] =
|
|
45
|
+
useState(false);
|
|
46
|
+
|
|
47
|
+
const value = useMemo<EscrowDialogsContextType>(
|
|
48
|
+
() => ({
|
|
49
|
+
second: { isOpen: secondOpen, setIsOpen: setSecondOpen },
|
|
50
|
+
completeMilestone: {
|
|
51
|
+
isOpen: completeMilestoneOpen,
|
|
52
|
+
setIsOpen: setCompleteMilestoneOpen,
|
|
53
|
+
},
|
|
54
|
+
qr: { isOpen: qrOpen, setIsOpen: setQrOpen },
|
|
55
|
+
resolveDispute: {
|
|
56
|
+
isOpen: resolveDisputeOpen,
|
|
57
|
+
setIsOpen: setResolveDisputeOpen,
|
|
58
|
+
},
|
|
59
|
+
editMilestone: {
|
|
60
|
+
isOpen: editMilestoneOpen,
|
|
61
|
+
setIsOpen: setEditMilestoneOpen,
|
|
62
|
+
},
|
|
63
|
+
editEntities: {
|
|
64
|
+
isOpen: editEntitiesOpen,
|
|
65
|
+
setIsOpen: setEditEntitiesOpen,
|
|
66
|
+
},
|
|
67
|
+
editBasicProperties: {
|
|
68
|
+
isOpen: editBasicPropertiesOpen,
|
|
69
|
+
setIsOpen: setEditBasicPropertiesOpen,
|
|
70
|
+
},
|
|
71
|
+
successRelease: {
|
|
72
|
+
isOpen: successReleaseOpen,
|
|
73
|
+
setIsOpen: setSuccessReleaseOpen,
|
|
74
|
+
},
|
|
75
|
+
successResolveDispute: {
|
|
76
|
+
isOpen: successResolveDisputeOpen,
|
|
77
|
+
setIsOpen: setSuccessResolveDisputeOpen,
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
[
|
|
81
|
+
secondOpen,
|
|
82
|
+
completeMilestoneOpen,
|
|
83
|
+
qrOpen,
|
|
84
|
+
resolveDisputeOpen,
|
|
85
|
+
editMilestoneOpen,
|
|
86
|
+
editEntitiesOpen,
|
|
87
|
+
editBasicPropertiesOpen,
|
|
88
|
+
successReleaseOpen,
|
|
89
|
+
successResolveDisputeOpen,
|
|
90
|
+
]
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<EscrowDialogsContext.Provider value={value}>
|
|
95
|
+
{children}
|
|
96
|
+
</EscrowDialogsContext.Provider>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function useEscrowDialogs() {
|
|
101
|
+
const ctx = useContext(EscrowDialogsContext);
|
|
102
|
+
if (!ctx) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
"useEscrowDialogs must be used within EscrowDialogsProvider"
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return ctx;
|
|
108
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState,
|
|
10
|
+
ReactNode,
|
|
11
|
+
useCallback,
|
|
12
|
+
} from "react";
|
|
13
|
+
import { GetEscrowsFromIndexerResponse as Escrow } from "@trustless-work/escrow/types";
|
|
14
|
+
|
|
15
|
+
type EscrowContextType = {
|
|
16
|
+
selectedEscrow: Escrow | null;
|
|
17
|
+
hasEscrow: boolean;
|
|
18
|
+
userRolesInEscrow: string[];
|
|
19
|
+
updateEscrow: (
|
|
20
|
+
updater: Partial<Escrow> | ((previous: Escrow) => Escrow)
|
|
21
|
+
) => void;
|
|
22
|
+
setEscrowField: <K extends keyof Escrow>(key: K, value: Escrow[K]) => void;
|
|
23
|
+
clearEscrow: () => void;
|
|
24
|
+
setSelectedEscrow: (escrow?: Escrow) => void;
|
|
25
|
+
setUserRolesInEscrow: (roles: string[]) => void;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const EscrowContext = createContext<EscrowContextType | undefined>(undefined);
|
|
29
|
+
|
|
30
|
+
const LOCAL_STORAGE_KEY = "selectedEscrow";
|
|
31
|
+
|
|
32
|
+
export const EscrowProvider = ({ children }: { children: ReactNode }) => {
|
|
33
|
+
const [selectedEscrow, setSelectedEscrowState] = useState<Escrow | null>(
|
|
34
|
+
null
|
|
35
|
+
);
|
|
36
|
+
const [userRolesInEscrow, setUserRolesInEscrowState] = useState<string[]>([]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
try {
|
|
40
|
+
const stored = localStorage.getItem(LOCAL_STORAGE_KEY);
|
|
41
|
+
if (stored) {
|
|
42
|
+
const parsed: Escrow = JSON.parse(stored);
|
|
43
|
+
setSelectedEscrowState(parsed);
|
|
44
|
+
}
|
|
45
|
+
} catch (_err) {
|
|
46
|
+
// ignore malformed localStorage content
|
|
47
|
+
}
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const persist = (value: Escrow | null) => {
|
|
51
|
+
if (value) {
|
|
52
|
+
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(value));
|
|
53
|
+
} else {
|
|
54
|
+
localStorage.removeItem(LOCAL_STORAGE_KEY);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const updateEscrow: EscrowContextType["updateEscrow"] = (updater) => {
|
|
59
|
+
setSelectedEscrowState((current) => {
|
|
60
|
+
if (!current) return current;
|
|
61
|
+
const next =
|
|
62
|
+
typeof updater === "function"
|
|
63
|
+
? updater(current)
|
|
64
|
+
: { ...current, ...updater };
|
|
65
|
+
persist(next);
|
|
66
|
+
return next;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const setEscrowField: EscrowContextType["setEscrowField"] = (key, value) => {
|
|
71
|
+
setSelectedEscrowState((current) => {
|
|
72
|
+
if (!current) return current;
|
|
73
|
+
const next = { ...current, [key]: value } as Escrow;
|
|
74
|
+
persist(next);
|
|
75
|
+
return next;
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const clearEscrow = () => {
|
|
80
|
+
setSelectedEscrowState(null);
|
|
81
|
+
persist(null);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const setUserRolesInEscrow = useCallback((roles: string[]) => {
|
|
85
|
+
setUserRolesInEscrowState((prev) => {
|
|
86
|
+
// Avoid unnecessary updates to prevent re-renders
|
|
87
|
+
if (
|
|
88
|
+
prev.length === roles.length &&
|
|
89
|
+
prev.every((r, i) => r === roles[i])
|
|
90
|
+
) {
|
|
91
|
+
return prev;
|
|
92
|
+
}
|
|
93
|
+
return roles;
|
|
94
|
+
});
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
const hasEscrow = useMemo(() => Boolean(selectedEscrow), [selectedEscrow]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<EscrowContext.Provider
|
|
101
|
+
value={{
|
|
102
|
+
selectedEscrow,
|
|
103
|
+
hasEscrow,
|
|
104
|
+
updateEscrow,
|
|
105
|
+
setEscrowField,
|
|
106
|
+
clearEscrow,
|
|
107
|
+
setSelectedEscrow: (value?: Escrow) =>
|
|
108
|
+
setSelectedEscrowState(value ?? null),
|
|
109
|
+
setUserRolesInEscrow,
|
|
110
|
+
userRolesInEscrow,
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</EscrowContext.Provider>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const useEscrowContext = () => {
|
|
119
|
+
const context = useContext(EscrowContext);
|
|
120
|
+
if (!context) {
|
|
121
|
+
throw new Error("useEscrowContext must be used within EscrowProvider");
|
|
122
|
+
}
|
|
123
|
+
return context;
|
|
124
|
+
};
|