@talismn/balances-react 0.1.7 → 0.1.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @talismn/balances-react
2
2
 
3
+ ## 0.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - d66c5bc: fix: evm native tokens
8
+ - Updated dependencies [d66c5bc]
9
+ - @talismn/balances@0.1.9
10
+ - @talismn/balances-evm-native@0.1.9
11
+ - @talismn/balances-example@0.1.9
12
+ - @talismn/balances-substrate-native@0.1.9
13
+ - @talismn/balances-substrate-orml@0.1.9
14
+ - @talismn/chaindata-provider@0.1.5
15
+ - @talismn/balances-evm-erc20@0.1.9
16
+ - @talismn/chain-connector@0.1.5
17
+ - @talismn/chaindata-provider-extension@0.1.5
18
+
19
+ ## 0.1.8
20
+
21
+ ### Patch Changes
22
+
23
+ - 3db774a: fix: useBalances creating too many subscriptions when called from multiple components
24
+ - Updated dependencies [cbd4770]
25
+ - Updated dependencies [3db774a]
26
+ - @talismn/balances-substrate-native@0.1.8
27
+ - @talismn/balances-substrate-orml@0.1.8
28
+ - @talismn/balances@0.1.8
29
+ - @talismn/balances-evm-erc20@0.1.8
30
+ - @talismn/balances-evm-native@0.1.8
31
+ - @talismn/balances-example@0.1.8
32
+
3
33
  ## 0.1.7
4
34
 
5
35
  ### Patch Changes
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { Balances, balances as balancesFn, } from "@talismn/balances";
11
11
  import { ChainConnector } from "@talismn/chain-connector";
12
+ import { ChainConnectorEvm } from "@talismn/chain-connector-evm";
12
13
  import { Dexie } from "dexie";
13
14
  import { useLiveQuery } from "dexie-react-hooks";
14
15
  import { useEffect, useState } from "react";
@@ -18,36 +19,110 @@ import { useChains, useEvmNetworks, useTokens } from "./useChaindata";
18
19
  export function useBalances(
19
20
  // TODO: Make this array of BalanceModules more type-safe
20
21
  balanceModules, chaindataProvider, addressesByToken) {
22
+ useBalancesSubscriptions(balanceModules, chaindataProvider, addressesByToken);
23
+ const chains = useChains(chaindataProvider);
24
+ const evmNetworks = useEvmNetworks(chaindataProvider);
25
+ const tokens = useTokens(chaindataProvider);
26
+ const balances = useLiveQuery(() => __awaiter(this, void 0, void 0, function* () {
27
+ return new Balances(yield db.balances
28
+ .filter((balance) => {
29
+ if (!balanceModules.map(({ type }) => type).includes(balance.source))
30
+ return false;
31
+ if (!addressesByToken)
32
+ return false;
33
+ if (!Object.keys(addressesByToken).includes(balance.tokenId))
34
+ return false;
35
+ if (!addressesByToken[balance.tokenId].includes(balance.address))
36
+ return false;
37
+ return true;
38
+ })
39
+ .toArray(), { chains, evmNetworks, tokens });
40
+ }), [balanceModules, addressesByToken, chains, evmNetworks, tokens]);
41
+ // debounce every 100ms to prevent hammering UI with updates
42
+ const [debouncedBalances, setDebouncedBalances] = useState(balances);
43
+ useDebounce(() => balances && setDebouncedBalances(balances), 100, [balances]);
44
+ return debouncedBalances;
45
+ }
46
+ // TODO: Turn into react context
47
+ const subscriptions = {};
48
+ // This hook is responsible for allowing us to call useBalances
49
+ // from multiple components, without setting up unnecessary
50
+ // balance subscriptions
51
+ function useBalancesSubscriptions(
52
+ // TODO: Make this array of BalanceModules more type-safe
53
+ balanceModules, chaindataProvider, addressesByToken) {
54
+ // const subscriptions = useRef<
55
+ // Record<string, { unsub: Promise<() => void>; refcount: number; generation: number }>
56
+ // >({})
57
+ const addSubscription = (key, balanceModule, chainConnectors, chaindataProvider, addressesByToken) => {
58
+ var _a;
59
+ // create subscription if it doesn't already exist
60
+ if (!subscriptions[key] || subscriptions[key].refcount === 0) {
61
+ const generation = ((((_a = subscriptions[key]) === null || _a === void 0 ? void 0 : _a.generation) || 0) + 1) % Number.MAX_SAFE_INTEGER;
62
+ const unsub = balancesFn(balanceModule, chainConnectors, chaindataProvider, addressesByToken, (error, balances) => {
63
+ if (error)
64
+ return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
65
+ if (!balances)
66
+ return;
67
+ // ignore balances from old subscriptions which are still in the process of unsubscribing
68
+ if (subscriptions[key].generation !== generation)
69
+ return;
70
+ const putBalances = Object.entries(balances.toJSON()).map(([id, balance]) => (Object.assign({ id }, balance)));
71
+ db.transaction("rw", db.balances, () => __awaiter(this, void 0, void 0, function* () { return yield db.balances.bulkPut(putBalances); }));
72
+ });
73
+ subscriptions[key] = { unsub, refcount: 0, generation };
74
+ }
75
+ // bump up the refcount by 1
76
+ subscriptions[key].refcount += 1;
77
+ };
78
+ const removeSubscription = (key, balanceModule, addressesByToken) => {
79
+ // ignore dead subscriptions
80
+ if (!subscriptions[key] || subscriptions[key].refcount === 0)
81
+ return;
82
+ // drop the refcount by one
83
+ subscriptions[key].refcount -= 1;
84
+ // unsubscribe if refcount is now 0 (nobody wants this subcription anymore)
85
+ if (subscriptions[key].refcount < 1) {
86
+ // remove subscription
87
+ subscriptions[key].unsub.then((unsub) => unsub());
88
+ delete subscriptions[key];
89
+ // set this subscription's balances in the store to status: cache
90
+ db.transaction("rw", db.balances, () => __awaiter(this, void 0, void 0, function* () {
91
+ return yield db.balances
92
+ .filter((balance) => {
93
+ if (balance.source !== balanceModule.type)
94
+ return false;
95
+ if (!Object.keys(addressesByToken).includes(balance.tokenId))
96
+ return false;
97
+ if (!addressesByToken[balance.tokenId].includes(balance.address))
98
+ return false;
99
+ return true;
100
+ })
101
+ .modify({ status: "cache" });
102
+ }));
103
+ }
104
+ };
21
105
  const chainConnector = useChainConnector(chaindataProvider);
106
+ const chainConnectorEvm = useChainConnectorEvm();
22
107
  useEffect(() => {
23
108
  if (chainConnector === null)
24
109
  return;
110
+ if (chainConnectorEvm === null)
111
+ return;
25
112
  if (chaindataProvider === null)
26
113
  return;
27
114
  if (addressesByToken === null)
28
115
  return;
29
- const unsubscribePromises = balanceModules.map((balanceModule) => balancesFn(balanceModule, chainConnector, chaindataProvider, addressesByToken, (error, balances) => {
30
- if (error)
31
- return log.error(`Failed to fetch ${balanceModule.type} balances`, error);
32
- if (!balances)
33
- return;
34
- db.transaction("rw", db.balances, () => __awaiter(this, void 0, void 0, function* () {
35
- yield db.balances.bulkPut(Object.entries(balances.toJSON()).map(([id, balance]) => (Object.assign({ id }, balance))));
36
- }));
37
- }));
38
- // TODO: Set balances status to cache on unmount
39
- return () => {
40
- unsubscribePromises.forEach((unsubscribePromise) => unsubscribePromise.then((unsub) => unsub()));
41
- };
42
- }, [addressesByToken, chainConnector]);
43
- const chains = useChains(chaindataProvider);
44
- const evmNetworks = useEvmNetworks(chaindataProvider);
45
- const tokens = useTokens(chaindataProvider);
46
- const balances = useLiveQuery(() => __awaiter(this, void 0, void 0, function* () { return new Balances(yield db.balances.toArray(), { chains, evmNetworks, tokens }); }), [chains, evmNetworks, tokens]);
47
- // debounce every 100ms to prevent hammering UI with updates
48
- const [debouncedBalances, setDebouncedBalances] = useState(balances);
49
- useDebounce(() => balances && setDebouncedBalances(balances), 100, [balances]);
50
- return debouncedBalances;
116
+ const unsubs = balanceModules.map((balanceModule) => {
117
+ const subscriptionKey = `${balanceModule.type}-${JSON.stringify(addressesByToken)}`;
118
+ // add balance subscription for this module
119
+ addSubscription(subscriptionKey, balanceModule, { substrate: chainConnector, evm: chainConnectorEvm }, chaindataProvider, addressesByToken);
120
+ // return an unsub method, to be called when this effect unmounts
121
+ return () => removeSubscription(subscriptionKey, balanceModule, addressesByToken);
122
+ });
123
+ const unsubAll = () => unsubs.forEach((unsub) => unsub());
124
+ return unsubAll;
125
+ }, [addressesByToken, chainConnector, chainConnectorEvm]);
51
126
  }
52
127
  // TODO: Allow advanced users of this library to provide their own chain connector
53
128
  function useChainConnector(chaindataProvider) {
@@ -59,6 +134,13 @@ function useChainConnector(chaindataProvider) {
59
134
  }, [chaindataProvider]);
60
135
  return chainConnector;
61
136
  }
137
+ function useChainConnectorEvm() {
138
+ const [chainConnectorEvm, setChainConnectorEvm] = useState(null);
139
+ useEffect(() => {
140
+ setChainConnectorEvm(new ChainConnectorEvm());
141
+ }, []);
142
+ return chainConnectorEvm;
143
+ }
62
144
  export class BalancesDatabase extends Dexie {
63
145
  constructor() {
64
146
  super("Balances");
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { ChaindataProviderExtension } from "@talismn/chaindata-provider-extension";
11
11
  import { useEffect, useState } from "react";
12
12
  import log from "../log";
13
+ // TODO: Allow user to call useChaindata from multiple places
13
14
  export function useChaindata() {
14
15
  const [chaindataProvider, setChaindataProvider] = useState(null);
15
16
  // this number is incremented each time the chaindataProvider has fetched new data
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/balances-react",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "UNLICENSED",
@@ -30,15 +30,15 @@
30
30
  "clean": "rm -rf dist && rm -rf .turbo rm -rf node_modules"
31
31
  },
32
32
  "dependencies": {
33
- "@talismn/balances": "^0.1.7",
34
- "@talismn/balances-evm-erc20": "^0.1.7",
35
- "@talismn/balances-evm-native": "^0.1.7",
36
- "@talismn/balances-example": "^0.1.7",
37
- "@talismn/balances-substrate-native": "^0.1.7",
38
- "@talismn/balances-substrate-orml": "^0.1.7",
39
- "@talismn/chain-connector": "^0.1.4",
40
- "@talismn/chaindata-provider": "^0.1.4",
41
- "@talismn/chaindata-provider-extension": "^0.1.4",
33
+ "@talismn/balances": "^0.1.9",
34
+ "@talismn/balances-evm-erc20": "^0.1.9",
35
+ "@talismn/balances-evm-native": "^0.1.9",
36
+ "@talismn/balances-example": "^0.1.9",
37
+ "@talismn/balances-substrate-native": "^0.1.9",
38
+ "@talismn/balances-substrate-orml": "^0.1.9",
39
+ "@talismn/chain-connector": "^0.1.5",
40
+ "@talismn/chaindata-provider": "^0.1.5",
41
+ "@talismn/chaindata-provider-extension": "^0.1.5",
42
42
  "anylogger": "^1.0.11",
43
43
  "dexie": "^3.2.2",
44
44
  "dexie-react-hooks": "^1.1.1",