Skip to content

Commit

Permalink
feat/264 - Handle third-party extension events (#310)
Browse files Browse the repository at this point in the history
* Enable event handling for Keplr account changes

* Clean up, add placeholders for Metamask

* Register ethereum provider events for Metamask

* Provide optional callbacks for handling extension events (for ethereum)

* Remove unnecessary log

* Removed erroneous (and unused) event definition
  • Loading branch information
jurevans authored Jul 5, 2023
1 parent 6fe3564 commit e1d9af1
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import { Dispatch } from "react";

import { chains } from "@anoma/chains";
import { Keplr } from "@anoma/integrations";

import { addAccounts, fetchBalances } from "slices/accounts";

export const KeplrAccountChangedHandler =
(dispatch: Dispatch<unknown>, integration: Keplr) =>
async (event: CustomEventInit) => {
const chainId = event.detail?.chainId;
const chain = chains[chainId];
(dispatch: Dispatch<unknown>, integration: Keplr) => async () => {
const accounts = await integration.accounts();

if (chain.extension.id === "keplr") {
const accounts = await integration.accounts();

if (accounts) {
dispatch(addAccounts(accounts));
dispatch(fetchBalances());
}
if (accounts) {
dispatch(addAccounts(accounts));
dispatch(fetchBalances());
}
};
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import { Dispatch } from "react";

import { chains } from "@anoma/chains";
import { Metamask } from "@anoma/integrations";

import { addAccounts, fetchBalances } from "slices/accounts";

export const MetamaskAccountChangedHandler =
(dispatch: Dispatch<unknown>, integration: Metamask) =>
async (event: CustomEventInit) => {
const chainId = event.detail?.chainId;
const chain = chains[chainId];

if (chain.extension.id === "metamask") {
const accounts = await integration.accounts();
(dispatch: Dispatch<unknown>, integration: Metamask) => async () => {
const accounts = await integration.accounts();

if (accounts) {
dispatch(addAccounts(accounts));
dispatch(fetchBalances());
}
if (accounts) {
dispatch(addAccounts(accounts));
dispatch(fetchBalances());
}
};
41 changes: 36 additions & 5 deletions apps/namada-interface/src/services/extensionEvents/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { createContext } from "react";

import { Events } from "@anoma/types";
import { defaultChainId } from "@anoma/chains";
import { Anoma } from "@anoma/integrations";
import { Events, KeplrEvents, MetamaskEvents } from "@anoma/types";
import {
defaultChainId,
defaultCosmosChainId,
defaultEthereumChainId,
} from "@anoma/chains";
import { Anoma, Keplr, Metamask } from "@anoma/integrations";
import { useEventListenerOnce, useIntegration } from "@anoma/hooks";

import { useAppDispatch } from "store";
Expand All @@ -11,13 +15,17 @@ import {
AnomaTransferCompletedHandler,
AnomaTransferStartedHandler,
AnomaUpdatedBalancesHandler,
} from "./handlers/anoma";
KeplrAccountChangedHandler,
MetamaskAccountChangedHandler,
} from "./handlers";

export const ExtensionEventsContext = createContext({});

export const ExtensionEventsProvider: React.FC = (props): JSX.Element => {
const dispatch = useAppDispatch();
const anomaIntegration = useIntegration(defaultChainId);
const keplrIntegration = useIntegration(defaultCosmosChainId);
const metamaskIntegration = useIntegration(defaultEthereumChainId);

// Instantiate handlers:
const anomaAccountChangedHandler = AnomaAccountChangedHandler(
Expand All @@ -26,14 +34,37 @@ export const ExtensionEventsProvider: React.FC = (props): JSX.Element => {
);
const anomaTransferStartedHandler = AnomaTransferStartedHandler(dispatch);
const anomaTransferCompletedHandler = AnomaTransferCompletedHandler(dispatch);

const anomaUpdatedBalancesHandler = AnomaUpdatedBalancesHandler(dispatch);

// Keplr handlers
const keplrAccountChangedHandler = KeplrAccountChangedHandler(
dispatch,
keplrIntegration as Keplr
);

// Metamask handlers
const metamaskAccountChangedHandler = MetamaskAccountChangedHandler(
dispatch,
metamaskIntegration as Metamask
);

// Register handlers:
useEventListenerOnce(Events.AccountChanged, anomaAccountChangedHandler);
useEventListenerOnce(Events.TransferStarted, anomaTransferStartedHandler);
useEventListenerOnce(Events.TransferCompleted, anomaTransferCompletedHandler);
useEventListenerOnce(Events.UpdatedBalances, anomaUpdatedBalancesHandler);
useEventListenerOnce(KeplrEvents.AccountChanged, keplrAccountChangedHandler);
useEventListenerOnce(
MetamaskEvents.AccountChanged,
metamaskAccountChangedHandler,
false,
(event, handler) => {
window.ethereum.on(event, handler);
},
(event, handler) => {
window.ethereum.removeListener(event, handler);
}
);

return (
<ExtensionEventsContext.Provider value={{}}>
Expand Down
2 changes: 2 additions & 0 deletions packages/chains/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import namada from "./chains/namada";
import ethereum from "./chains/ethereum";

export const defaultChainId = namada.chainId;
export const defaultCosmosChainId = cosmos.chainId;
export const defaultEthereumChainId = ethereum.chainId;

export const chains = {
[cosmos.chainId]: cosmos,
Expand Down
31 changes: 25 additions & 6 deletions packages/hooks/src/useEvent.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import { useEffect } from "react";

type EventHandler = (e: CustomEventInit) => void;
type EventCallback = (event: string, handler: EventHandler) => void;

export const useEventListener = (
event: string,
handler: (e: CustomEventInit) => void,
handler: EventHandler,
deps?: React.DependencyList,
useCapture = false
useCapture = false,
registerCallback?: EventCallback,
removeCallback?: EventCallback
): void => {
useEffect(() => {
window.addEventListener(event, handler, useCapture);
registerCallback
? registerCallback(event, handler)
: window.addEventListener(event, handler, useCapture);

return () => {
if (removeCallback) {
return removeCallback(event, handler);
}
window.removeEventListener(event, handler);
};
}, deps);
};

export const useEventListenerOnce = (
event: string,
handler: (e: CustomEventInit) => void,
useCapture = false
handler: EventHandler,
useCapture = false,
registerCallback?: EventCallback,
removeCallback?: EventCallback
): void => {
useEventListener(event, handler, [], useCapture);
useEventListener(
event,
handler,
[],
useCapture,
registerCallback,
removeCallback
);
};
11 changes: 11 additions & 0 deletions packages/types/src/events.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
// Constants defining events which may be subscribed to

// Anoma extension events
export enum Events {
AccountChanged = "anoma-account-changed",
TransferStarted = "anoma-transfer-started",
TransferCompleted = "anoma-transfer-completed",
UpdatedBalances = "anoma-updated-balances",
}

// Keplr extension events
export enum KeplrEvents {
AccountChanged = "keplr_keystorechange",
}

// Metamask extension window.ethereum events
export enum MetamaskEvents {
AccountChanged = "accountsChanged",
}

0 comments on commit e1d9af1

Please sign in to comment.