diff --git a/src/app/flow-councils/flow-councils.tsx b/src/app/flow-councils/flow-councils.tsx
index d9ce7480..2fdf11e1 100644
--- a/src/app/flow-councils/flow-councils.tsx
+++ b/src/app/flow-councils/flow-councils.tsx
@@ -245,8 +245,11 @@ export default function FlowCouncils(props: FlowCouncilsProps) {
return (
+ router.push(`/flow-councils/${selectedNetwork.id}/${flowCouncil.id}`)
+ }
>
e.stopPropagation()}
>
{token && (
@@ -277,7 +281,8 @@ export default function FlowCouncils(props: FlowCouncilsProps) {
)}
{flowCouncil.isRecipient && !flowCouncil.isConnected ? (
-
+ e.stopPropagation()}>
+
+
) : (
diff --git a/src/app/flow-splitters/[chainId]/[poolId]/flow-splitter.tsx b/src/app/flow-splitters/[chainId]/[poolId]/flow-splitter.tsx
index f0ece98f..67185b6a 100644
--- a/src/app/flow-splitters/[chainId]/[poolId]/flow-splitter.tsx
+++ b/src/app/flow-splitters/[chainId]/[poolId]/flow-splitter.tsx
@@ -398,6 +398,7 @@ export default function FlowSplitter(props: FlowSplitterProps) {
{" "}
- removed as a Flow Splitter admin.
+ removed as a {poolLabel} admin.
) {
const { selected, data } = props;
const [showToolbar, setShowToolbar] = useState(false);
+ const [connectStatus, setConnectStatus] = useState("idle");
+ const [connectAllStatus, setConnectAllStatus] =
+ useState("idle");
+
+ const { address, chain: connectedChain } = useAccount();
+ const { openConnectModal } = useConnectModal();
+ const { switchChainAsync } = useSwitchChain();
+ const publicClient = usePublicClient();
+ const { writeContractAsync } = useWriteContract();
const totalFlowed = useFlowingAmount(
BigInt((data?.totalAmountFlowedDistributedUntilUpdatedAt as string) ?? 0) +
@@ -93,27 +122,247 @@ function CustomNode(props: NodeProps) {
BigInt((data?.flowRate as string) ?? 0),
);
+ useEffect(() => {
+ if (connectStatus === "success" || connectStatus === "error") {
+ const timeout = setTimeout(() => setConnectStatus("idle"), 2000);
+ return () => clearTimeout(timeout);
+ }
+ }, [connectStatus]);
+
+ useEffect(() => {
+ if (connectAllStatus === "success" || connectAllStatus === "error") {
+ const timeout = setTimeout(() => setConnectAllStatus("idle"), 2000);
+ return () => clearTimeout(timeout);
+ }
+ }, [connectAllStatus]);
+
+ const network = data.network as Network | undefined;
+
+ const handleTryConnect = useCallback(async () => {
+ if (!address) {
+ openConnectModal?.();
+ return;
+ }
+
+ if (!network || !publicClient) return;
+
+ try {
+ setConnectStatus("pending");
+
+ if (connectedChain?.id !== data.chainId) {
+ await switchChainAsync({ chainId: data.chainId as number });
+ }
+
+ const callData = encodeFunctionData({
+ abi: gdaAbi,
+ functionName: "tryConnectPoolFor",
+ args: [
+ data.poolAddress as Address,
+ data.address as Address,
+ "0x" as `0x${string}`,
+ ],
+ });
+
+ const hash = await writeContractAsync({
+ address: network.superfluidHost,
+ abi: superfluidHostAbi,
+ functionName: "callAgreement",
+ args: [network.gda, callData, "0x"],
+ });
+
+ await publicClient.waitForTransactionReceipt({ hash, confirmations: 3 });
+
+ const isConnected = await publicClient.readContract({
+ address: network.gdaForwarder,
+ abi: gdaForwarderAbi,
+ functionName: "isMemberConnected",
+ args: [data.poolAddress as Address, data.address as Address],
+ });
+
+ if (isConnected) {
+ setConnectStatus("success");
+ } else {
+ setConnectStatus("slots-full");
+ }
+ } catch (err) {
+ console.error(err);
+ setConnectStatus("error");
+ }
+ }, [
+ address,
+ openConnectModal,
+ network,
+ publicClient,
+ writeContractAsync,
+ switchChainAsync,
+ connectedChain?.id,
+ data.chainId,
+ data.poolAddress,
+ data.address,
+ ]);
+
+ const handleConnectAll = useCallback(async () => {
+ if (!address) {
+ openConnectModal?.();
+ return;
+ }
+
+ if (!network || !publicClient) return;
+
+ const disconnectedMembers = data.disconnectedMembers as Address[];
+ if (!disconnectedMembers?.length) return;
+
+ const poolAddress = data.poolAddress as Address;
+
+ try {
+ setConnectAllStatus("pending");
+
+ if (connectedChain?.id !== data.chainId) {
+ await switchChainAsync({ chainId: data.chainId as number });
+ }
+
+ const operations = disconnectedMembers.map((member) => ({
+ operationType: 201,
+ target: network.gda,
+ data: encodeAbiParameters(
+ [{ type: "bytes" }, { type: "bytes" }],
+ [
+ encodeFunctionData({
+ abi: gdaAbi,
+ functionName: "tryConnectPoolFor",
+ args: [poolAddress, member, "0x" as `0x${string}`],
+ }),
+ "0x" as `0x${string}`,
+ ],
+ ),
+ }));
+
+ const hash = await writeContractAsync({
+ address: network.superfluidHost,
+ abi: superfluidHostAbi,
+ functionName: "batchCall",
+ args: [operations],
+ });
+
+ await publicClient.waitForTransactionReceipt({ hash, confirmations: 3 });
+
+ setConnectAllStatus("success");
+ } catch (err) {
+ console.error(err);
+ setConnectAllStatus("error");
+ }
+ }, [
+ address,
+ openConnectModal,
+ network,
+ publicClient,
+ writeContractAsync,
+ switchChainAsync,
+ connectedChain?.id,
+ data.chainId,
+ data.disconnectedMembers,
+ data.poolAddress,
+ ]);
+
+ const statusOverlay =
+ connectStatus === "pending" ? (
+
+
+
+ ) : connectStatus === "success" ? (
+
+ ✓
+
+ ) : connectStatus === "error" ? (
+
+ ✗
+
+ ) : null;
+
if (data.isPool) {
return (
setShowToolbar(true)}
onMouseLeave={() => setShowToolbar(false)}
>
-
-
- {data?.label?.toString() ?? ""}
-
-
- Total{" "}
- {`${formatNumber(
- Number(formatEther(totalFlowed)),
- )} ${(data.token as { symbol: string }).symbol}`}
-
-
+
+
+
+ {data?.label?.toString() ?? ""}
+
+
+ Total{" "}
+ {`${formatNumber(
+ Number(formatEther(totalFlowed)),
+ )} ${(data.token as { symbol: string }).symbol}`}
+
+
+ {connectAllStatus === "pending" && (
+
+
+
+ )}
+ {connectAllStatus === "success" && (
+
+ ✓
+
+ )}
+ {connectAllStatus === "error" && (
+
+ ✗
+
+ )}
+
) {
offset={0}
>
+ {(data.disconnectedMembers as Address[])?.length > 0 ? (
+
+ ) : (
+
+ )}