-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add accounts-balance loading service for the Portfolio page (#6090
) # Motivation The new Portfolio page shares some similarities with the existing `/token` page, as both aim to load the balancers for SNS, ICRP, and BTC. They load these accounts each time the user navigates to the respective pages, but they try to avoid loading them multiple times due to re-renders by tracking the requested accounts. This first PR extracts some of the logic for use in both pages. A follow-up PR will implement this logic for the Portfolio page. Later, it can also be applied to the Token page. [Ticket](https://dfinity.atlassian.net/browse/NNS1-3520) # Changes - `loadSnsAccountsBalances` loads the balances of SNS projects. - `loadAccountsBalances` loadsthe balances of ICRC and ckBTC. - A mechanism tracks loaded balances and cleans them when the consumer page is mounted. # Tests - Unit tests # Todos - [ ] Add entry to changelog (if necessary). Not necessary Next PR: #6116
- Loading branch information
Showing
2 changed files
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { uncertifiedLoadSnsesAccountsBalances } from "$lib/services/sns-accounts-balance.services"; | ||
import { uncertifiedLoadAccountsBalance } from "$lib/services/wallet-uncertified-accounts.services"; | ||
import type { UniverseCanisterIdText } from "$lib/types/universe"; | ||
import type { CanisterIdString } from "@dfinity/nns"; | ||
import { Principal } from "@dfinity/principal"; | ||
|
||
const loadedBalances = new Set<CanisterIdString>(); | ||
export const resetBalanceLoading = (): void => { | ||
loadedBalances.clear(); | ||
}; | ||
|
||
const getNotLoadedIds = (ids: CanisterIdString[]): CanisterIdString[] => { | ||
const notLoadedIds = ids.filter((id) => !loadedBalances.has(id)); | ||
notLoadedIds.forEach((id) => loadedBalances.add(id)); | ||
return notLoadedIds; | ||
}; | ||
|
||
export const loadSnsAccountsBalances = async ( | ||
rootCanisterIds: Principal[] | ||
): Promise<void> => { | ||
const stringIds = rootCanisterIds.map((id) => id.toText()); | ||
const notLoadedIds = getNotLoadedIds(stringIds); | ||
|
||
if (notLoadedIds.length === 0) return; | ||
|
||
await uncertifiedLoadSnsesAccountsBalances({ | ||
rootCanisterIds: notLoadedIds.map((id) => Principal.fromText(id)), | ||
}); | ||
}; | ||
|
||
export const loadAccountsBalances = async ( | ||
universeIds: UniverseCanisterIdText[] | ||
): Promise<void> => { | ||
const notLoadedIds = getNotLoadedIds(universeIds); | ||
|
||
if (notLoadedIds.length === 0) return; | ||
|
||
await uncertifiedLoadAccountsBalance({ | ||
universeIds: notLoadedIds, | ||
}); | ||
}; |
109 changes: 109 additions & 0 deletions
109
frontend/src/tests/lib/services/accounts-balances.services.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import { | ||
loadAccountsBalances, | ||
loadSnsAccountsBalances, | ||
resetBalanceLoading, | ||
} from "$lib/services/accounts-balances.services"; | ||
import * as snsBalanceServices from "$lib/services/sns-accounts-balance.services"; | ||
import * as walletServices from "$lib/services/wallet-uncertified-accounts.services"; | ||
import type { CanisterIdString } from "@dfinity/nns"; | ||
import { Principal } from "@dfinity/principal"; | ||
|
||
vi.mock("$lib/services/icrc-accounts.services", () => { | ||
return { | ||
loadAccounts: vi.fn(), | ||
loadIcrcToken: vi.fn(), | ||
}; | ||
}); | ||
|
||
vi.mock("$lib/services/sns-accounts.services", () => { | ||
return { | ||
loadSnsAccounts: vi.fn(), | ||
}; | ||
}); | ||
|
||
describe("accounts-balances services", () => { | ||
let accountsBalanceSpy; | ||
let snsBalancesSpy; | ||
|
||
beforeEach(() => { | ||
resetBalanceLoading(); | ||
|
||
accountsBalanceSpy = vi.spyOn( | ||
walletServices, | ||
"uncertifiedLoadAccountsBalance" | ||
); | ||
|
||
snsBalancesSpy = vi.spyOn( | ||
snsBalanceServices, | ||
"uncertifiedLoadSnsesAccountsBalances" | ||
); | ||
}); | ||
|
||
describe("loadSnsBalances", () => { | ||
it("should not call service if array is empty", async () => { | ||
await loadSnsAccountsBalances([]); | ||
expect(snsBalancesSpy).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should call service with correct parameters", async () => { | ||
const principal1 = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); | ||
const principal2 = Principal.fromText("ryjl3-tyaaa-aaaaa-aaaba-cai"); | ||
|
||
await loadSnsAccountsBalances([principal1, principal2]); | ||
|
||
expect(snsBalancesSpy).toHaveBeenCalledWith({ | ||
rootCanisterIds: [principal1, principal2], | ||
}); | ||
}); | ||
|
||
it("should not reload already loaded canister IDs", async () => { | ||
const principal1 = Principal.fromText("rrkah-fqaaa-aaaaa-aaaaq-cai"); | ||
|
||
await loadSnsAccountsBalances([principal1]); | ||
await loadSnsAccountsBalances([principal1]); | ||
|
||
expect(snsBalancesSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe("loadAccountsBalances", () => { | ||
it("should not call service if array is empty", async () => { | ||
await loadAccountsBalances([]); | ||
expect(accountsBalanceSpy).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should call service with correct parameters", async () => { | ||
const universeIds: CanisterIdString[] = [ | ||
"rrkah-fqaaa-aaaaa-aaaaq-cai", | ||
"ryjl3-tyaaa-aaaaa-aaaba-cai", | ||
]; | ||
|
||
await loadAccountsBalances(universeIds); | ||
|
||
expect(accountsBalanceSpy).toHaveBeenCalledWith({ | ||
universeIds, | ||
}); | ||
}); | ||
|
||
it("should not reload already loaded universe IDs", async () => { | ||
const universeIds: CanisterIdString[] = ["rrkah-fqaaa-aaaaa-aaaaq-cai"]; | ||
|
||
await loadAccountsBalances(universeIds); | ||
await loadAccountsBalances(universeIds); | ||
|
||
expect(accountsBalanceSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe("resetBalanceLoading", () => { | ||
it("should allow reloading previously loaded IDs after reset", async () => { | ||
const universeIds: CanisterIdString[] = ["rrkah-fqaaa-aaaaa-aaaaq-cai"]; | ||
|
||
await loadAccountsBalances(universeIds); | ||
resetBalanceLoading(); | ||
await loadAccountsBalances(universeIds); | ||
|
||
expect(accountsBalanceSpy).toHaveBeenCalledTimes(2); | ||
}); | ||
}); | ||
}); |