-
-
Notifications
You must be signed in to change notification settings - Fork 183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added options to PollingController to help handle cases where the Controller need more than just networkClientId #1776
Changes from 17 commits
03e93b3
bf36d98
10d36e2
7234504
2c0c219
472119d
f6fdd42
01192af
556f1cf
b8c0e00
ac6b7a7
4286bbc
4919cb3
95d54e8
585544e
5da575d
bed5016
d35f471
b689e51
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,11 +1,25 @@ | ||||||||||
import { BaseController, BaseControllerV2 } from '@metamask/base-controller'; | ||||||||||
import type { NetworkClientId } from '@metamask/network-controller'; | ||||||||||
import type { Json } from '@metamask/utils'; | ||||||||||
import stringify from 'fast-json-stable-stringify'; | ||||||||||
import { v4 as random } from 'uuid'; | ||||||||||
|
||||||||||
// Mixin classes require a constructor with an `...any[]` parameter | ||||||||||
// See TS2545 | ||||||||||
type Constructor = new (...args: any[]) => object; | ||||||||||
|
||||||||||
/** | ||||||||||
* Returns a unique key for a networkClientId and options. This is used to group networkClientId polls with the same options | ||||||||||
* @param networkClientId - The networkClientId to get a key for | ||||||||||
* @param options - The options used to group the polling events | ||||||||||
* @returns The unique key | ||||||||||
*/ | ||||||||||
export const getKey = ( | ||||||||||
networkClientId: NetworkClientId, | ||||||||||
options: Json, | ||||||||||
): PollingGroupId => `${networkClientId}:${stringify(options)}`; | ||||||||||
|
||||||||||
type PollingGroupId = `${NetworkClientId}:${string}`; | ||||||||||
/** | ||||||||||
* PollingControllerMixin | ||||||||||
* | ||||||||||
|
@@ -20,10 +34,9 @@ function PollingControllerMixin<TBase extends Constructor>(Base: TBase) { | |||||||||
* | ||||||||||
*/ | ||||||||||
abstract class PollingControllerBase extends Base { | ||||||||||
readonly #networkClientIdTokensMap: Map<NetworkClientId, Set<string>> = | ||||||||||
new Map(); | ||||||||||
readonly #pollingGroupIds: Map<PollingGroupId, Set<string>> = new Map(); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
On the other hand, it seems like when we use this variable we really care about the poll tokens. Also,
Suggested change
|
||||||||||
|
||||||||||
readonly #intervalIds: Record<NetworkClientId, NodeJS.Timeout> = {}; | ||||||||||
readonly #intervalIds: Record<PollingGroupId, NodeJS.Timeout> = {}; | ||||||||||
|
||||||||||
#callbacks: Map< | ||||||||||
NetworkClientId, | ||||||||||
|
@@ -49,27 +62,34 @@ function PollingControllerMixin<TBase extends Constructor>(Base: TBase) { | |||||||||
* Starts polling for a networkClientId | ||||||||||
* | ||||||||||
* @param networkClientId - The networkClientId to start polling for | ||||||||||
* @param options - The options used to group the polling events | ||||||||||
* @returns void | ||||||||||
*/ | ||||||||||
startPollingByNetworkClientId(networkClientId: NetworkClientId) { | ||||||||||
startPollingByNetworkClientId( | ||||||||||
networkClientId: NetworkClientId, | ||||||||||
options: Json = {}, | ||||||||||
) { | ||||||||||
const innerPollToken = random(); | ||||||||||
if (this.#networkClientIdTokensMap.has(networkClientId)) { | ||||||||||
const set = this.#networkClientIdTokensMap.get(networkClientId); | ||||||||||
set?.add(innerPollToken); | ||||||||||
|
||||||||||
const key = getKey(networkClientId, options); | ||||||||||
|
||||||||||
const pollingGroupId = this.#pollingGroupIds.get(key); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this right? I thought each value in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah currently the type you have returned for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea really it should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably |
||||||||||
if (pollingGroupId) { | ||||||||||
pollingGroupId.add(innerPollToken); | ||||||||||
} else { | ||||||||||
const set = new Set<string>(); | ||||||||||
set.add(innerPollToken); | ||||||||||
this.#networkClientIdTokensMap.set(networkClientId, set); | ||||||||||
this.#pollingGroupIds.set(key, set); | ||||||||||
} | ||||||||||
this.#poll(networkClientId); | ||||||||||
this.#poll(networkClientId, options); | ||||||||||
return innerPollToken; | ||||||||||
} | ||||||||||
|
||||||||||
/** | ||||||||||
* Stops polling for all networkClientIds | ||||||||||
*/ | ||||||||||
stopAllPolling() { | ||||||||||
this.#networkClientIdTokensMap.forEach((tokens, _networkClientId) => { | ||||||||||
this.#pollingGroupIds.forEach((tokens, _networkClientId) => { | ||||||||||
tokens.forEach((token) => { | ||||||||||
this.stopPollingByNetworkClientId(token); | ||||||||||
}); | ||||||||||
|
@@ -86,20 +106,18 @@ function PollingControllerMixin<TBase extends Constructor>(Base: TBase) { | |||||||||
throw new Error('pollingToken required'); | ||||||||||
} | ||||||||||
let found = false; | ||||||||||
this.#networkClientIdTokensMap.forEach((tokens, networkClientId) => { | ||||||||||
this.#pollingGroupIds.forEach((tokens, key) => { | ||||||||||
if (tokens.has(pollingToken)) { | ||||||||||
found = true; | ||||||||||
this.#networkClientIdTokensMap | ||||||||||
.get(networkClientId) | ||||||||||
?.delete(pollingToken); | ||||||||||
if (this.#networkClientIdTokensMap.get(networkClientId)?.size === 0) { | ||||||||||
clearTimeout(this.#intervalIds[networkClientId]); | ||||||||||
delete this.#intervalIds[networkClientId]; | ||||||||||
this.#networkClientIdTokensMap.delete(networkClientId); | ||||||||||
this.#callbacks.get(networkClientId)?.forEach((callback) => { | ||||||||||
callback(networkClientId); | ||||||||||
tokens.delete(pollingToken); | ||||||||||
if (tokens.size === 0) { | ||||||||||
clearTimeout(this.#intervalIds[key]); | ||||||||||
delete this.#intervalIds[key]; | ||||||||||
this.#pollingGroupIds.delete(key); | ||||||||||
this.#callbacks.get(key)?.forEach((callback) => { | ||||||||||
callback(key); | ||||||||||
}); | ||||||||||
this.#callbacks.get(networkClientId)?.clear(); | ||||||||||
this.#callbacks.get(key)?.clear(); | ||||||||||
} | ||||||||||
} | ||||||||||
}); | ||||||||||
|
@@ -112,24 +130,29 @@ function PollingControllerMixin<TBase extends Constructor>(Base: TBase) { | |||||||||
* Executes the poll for a networkClientId | ||||||||||
* | ||||||||||
* @param networkClientId - The networkClientId to execute the poll for | ||||||||||
* @param options - The options passed to startPollingByNetworkClientId | ||||||||||
*/ | ||||||||||
abstract executePoll(networkClientId: NetworkClientId): Promise<void>; | ||||||||||
abstract executePoll( | ||||||||||
shanejonas marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||
networkClientId: NetworkClientId, | ||||||||||
options: Json, | ||||||||||
): Promise<void>; | ||||||||||
|
||||||||||
#poll(networkClientId: NetworkClientId) { | ||||||||||
if (this.#intervalIds[networkClientId]) { | ||||||||||
clearTimeout(this.#intervalIds[networkClientId]); | ||||||||||
delete this.#intervalIds[networkClientId]; | ||||||||||
#poll(networkClientId: NetworkClientId, options: Json) { | ||||||||||
const key = getKey(networkClientId, options); | ||||||||||
if (this.#intervalIds[key]) { | ||||||||||
clearTimeout(this.#intervalIds[key]); | ||||||||||
delete this.#intervalIds[key]; | ||||||||||
} | ||||||||||
// setTimeout is not `await`ing this async function, which is expected | ||||||||||
// We're just using async here for improved stack traces | ||||||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises | ||||||||||
this.#intervalIds[networkClientId] = setTimeout(async () => { | ||||||||||
this.#intervalIds[key] = setTimeout(async () => { | ||||||||||
try { | ||||||||||
await this.executePoll(networkClientId); | ||||||||||
await this.executePoll(networkClientId, options); | ||||||||||
} catch (error) { | ||||||||||
console.error(error); | ||||||||||
} | ||||||||||
this.#poll(networkClientId); | ||||||||||
this.#poll(networkClientId, options); | ||||||||||
}, this.#intervalLength); | ||||||||||
} | ||||||||||
|
||||||||||
|
@@ -138,17 +161,22 @@ function PollingControllerMixin<TBase extends Constructor>(Base: TBase) { | |||||||||
* | ||||||||||
* @param networkClientId - The networkClientId to listen for polling complete events | ||||||||||
* @param callback - The callback to execute when polling is complete | ||||||||||
* @param options - The options used to group the polling events | ||||||||||
*/ | ||||||||||
onPollingCompleteByNetworkClientId( | ||||||||||
networkClientId: NetworkClientId, | ||||||||||
callback: (networkClientId: NetworkClientId) => void, | ||||||||||
options: Json = {}, | ||||||||||
) { | ||||||||||
if (this.#callbacks.has(networkClientId)) { | ||||||||||
this.#callbacks.get(networkClientId)?.add(callback); | ||||||||||
} else { | ||||||||||
const key = getKey(networkClientId, options); | ||||||||||
const callbacks = this.#callbacks.get(key); | ||||||||||
|
||||||||||
if (callbacks === undefined) { | ||||||||||
const set = new Set<typeof callback>(); | ||||||||||
set.add(callback); | ||||||||||
this.#callbacks.set(networkClientId, set); | ||||||||||
this.#callbacks.set(key, set); | ||||||||||
} else { | ||||||||||
callbacks.add(callback); | ||||||||||
} | ||||||||||
} | ||||||||||
} | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] last thing is maybe we should align with the
pollingTokenSet
verbiage ->PollingTokenSetId
?