Skip to content
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

prevent load functions from rerunning excessively #1115

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lib/flows/create-stream-flow/input-details.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
import { getAddressDriverClient, getCallerClient } from '$lib/utils/get-drips-clients';
import assert from '$lib/utils/assert';
import { waitForAccountMetadata } from '$lib/utils/ipfs';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '$lib/stores/fetched-data-cache/invalidate';

const dispatch = createEventDispatcher<StepComponentEvents>();

Expand Down
2 changes: 1 addition & 1 deletion src/lib/flows/edit-stream-flow/enter-new-details.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
import { buildEditStreamBatch } from '$lib/utils/streams/streams';
import assert from '$lib/utils/assert';
import { waitForAccountMetadata } from '$lib/utils/ipfs';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '$lib/stores/fetched-data-cache/invalidate';
import walletStore from '$lib/stores/wallet/wallet.store';

const dispatch = createEventDispatcher<StepComponentEvents>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { invalidate } from '$app/navigation';
import { invalidate } from '$lib/stores/fetched-data-cache/invalidate';
import { makeStep } from '$lib/components/stepper/types';
import SuccessStep from '$lib/components/success-step/success-step.svelte';
import mapFilterUndefined from '$lib/utils/map-filter-undefined';
Expand Down
2 changes: 1 addition & 1 deletion src/lib/flows/pause-flow/pause.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
} from './__generated__/gql.generated';
import { buildPauseStreamPopulatedTx } from '$lib/utils/streams/streams';
import query from '$lib/graphql/dripsQL';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '$lib/stores/fetched-data-cache/invalidate';

const dispatch = createEventDispatcher<StepComponentEvents>();

Expand Down
2 changes: 1 addition & 1 deletion src/lib/flows/unpause-flow/unpause.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
} from './__generated__/gql.generated';
import { buildUnpauseStreamPopulatedTx } from '$lib/utils/streams/streams';
import query from '$lib/graphql/dripsQL';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '$lib/stores/fetched-data-cache/invalidate';

const dispatch = createEventDispatcher<StepComponentEvents>();

Expand Down
2 changes: 1 addition & 1 deletion src/lib/flows/vote/vote.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import ListEditor from '$lib/components/list-editor/list-editor.svelte';
import type { Writable } from 'svelte/store';
import type { State } from './vote-flow-steps';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '$lib/stores/fetched-data-cache/invalidate';
import type { VotingRound } from '$lib/utils/multiplayer/schemas';

const dispatch = createEventDispatcher<StepComponentEvents>();
Expand Down
60 changes: 60 additions & 0 deletions src/lib/stores/fetched-data-cache/fetched-data-cache.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { get, writable } from 'svelte/store';

interface State {
data: Record<string, unknown>;
}

const state = writable<State>({ data: {} });

function set(key: string, value: unknown) {
state.update((s) => {
s.data[key] = value;
return s;
});
}

export function clear(key: string) {
state.update((s) => {
delete s.data[key];
return s;
});
}

export function clearAll() {
state.set({ data: {} });
}

/**
* Create a local "fetched data cache" that can be used to prevent data from being re-fetched
* on navigations to a page that has already previously been navigated to.
*
* **Important**: There is no magic for removing data from this store. If data of a page that uses
* this cache should be re-fetched, ensure that `invalidate` functions from `./invalidate.ts` are
* called with the appropriate keys.
*
* **Also important**: No runtime type checking is being done. You need to make sure that
* 1. The key you pass to this function is unique
* 2. The type you pass to this function is correct
*
* **Also important**: What this returns is only a wrapper around a global store. Even if this
* wrapper is abandoned, the values it wrote persist in the global store.
* @param key
* @returns
*/
export const makeFetchedDataCache = <T extends Record<string, unknown> | unknown[]>(
key: string,
) => {
function read(): T | undefined {
return get(state).data[key] as T;
}

function write(v: T) {
set(key, v);
}

return {
subscribe: state.subscribe,
read,
write,
};
};
19 changes: 19 additions & 0 deletions src/lib/stores/fetched-data-cache/invalidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { invalidateAll as skInvalidateAll, invalidate as skInvalidate } from '$app/navigation';
import { clear, clearAll } from './fetched-data-cache.store';

/**
* Invalidate `key` in the local fetched data cache and re-run all load functions
* that depend on `key`.
*/
export async function invalidate(key: string) {
clear(key);
await skInvalidate(key);
}

/**
* Invalidate all keys in the local fetched data cache and re-run all currently active load functions.
*/
export async function invalidateAll() {
clearAll();
await skInvalidateAll();
}
2 changes: 1 addition & 1 deletion src/lib/stores/wallet/wallet.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import storedWritable from '@efstajas/svelte-stored-writable';
import { z } from 'zod';
import { isWalletUnlocked } from './utils/is-wallet-unlocked';
import network, { getNetwork, isConfiguredChainId, type Network } from './network';
import { invalidateAll } from '$app/navigation';
import { invalidateAll } from '../fetched-data-cache/invalidate';

const appsSdk = new SafeAppsSDK();

Expand Down
7 changes: 5 additions & 2 deletions src/routes/app/(app)/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { PROJECT_CARD_FRAGMENT } from '$lib/components/project-card/project-card.svelte';
import query from '$lib/graphql/dripsQL';
import { gql } from 'graphql-request';
import type { ProjectsQuery, ProjectsQueryVariables } from './__generated__/gql.generated';
import type {
ExploreProjectsQuery,
ExploreProjectsQueryVariables,
} from './__generated__/gql.generated';
import {
ProjectSortField,
ProjectVerificationStatus,
Expand Down Expand Up @@ -73,7 +76,7 @@ export const load = async ({ fetch }) => {
);

const fetchProjects = async () => {
const projectsRes = await query<ProjectsQuery, ProjectsQueryVariables>(
const projectsRes = await query<ExploreProjectsQuery, ExploreProjectsQueryVariables>(
getProjectsQuery,
getProjectsVariables,
fetch,
Expand Down
31 changes: 25 additions & 6 deletions src/routes/app/(app)/drip-lists/+page.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
import query from '$lib/graphql/dripsQL.js';
import { error, redirect } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
import { gql } from 'graphql-request';
import { DRIP_LISTS_PAGE_DRIP_LIST_FRAGMENT } from './+page.svelte';
import { getVotingRounds } from '$lib/utils/multiplayer';
import { mapSplitsFromMultiplayerResults } from '$lib/components/splits/splits.svelte';
import {
mapSplitsFromMultiplayerResults,
type SplitsComponentSplitsReceiver,
} from '$lib/components/splits/splits.svelte';
import type {
DripListsPageQuery,
DripListsPageQueryVariables,
} from './__generated__/gql.generated';
import buildUrl from '$lib/utils/build-url';
import getConnectedAddress from '$lib/utils/get-connected-address';
import { makeFetchedDataCache } from '$lib/stores/fetched-data-cache/fetched-data-cache.store';
import type { VotingRound } from '$lib/utils/multiplayer/schemas';

type VotingRoundWithSplits = VotingRound & { splits: SplitsComponentSplitsReceiver[] };

const fetchedDataCache = makeFetchedDataCache<{
dripLists: DripListsPageQuery['dripLists'];
votingRounds: VotingRoundWithSplits[];
}>('dashboard:drip-lists');

export const load = async ({ fetch }) => {
const connectedAddress = getConnectedAddress();

if (!connectedAddress) {
redirect(307, buildUrl('/app/connect', { backTo: '/app/drip-lists' }));
throw redirect(307, buildUrl('/app/connect', { backTo: '/app/drip-lists' }));
}

const dripListsPageQuery = gql`
Expand All @@ -27,6 +39,12 @@ export const load = async ({ fetch }) => {
}
`;

const locallyCached = fetchedDataCache.read();

if (locallyCached) {
return locallyCached;
}

const [votingRounds, dripListsRes] = await Promise.all([
await getVotingRounds({ publisherAddress: connectedAddress }, fetch),
await query<DripListsPageQuery, DripListsPageQueryVariables>(
Expand All @@ -47,9 +65,10 @@ export const load = async ({ fetch }) => {
splits: votingRoundsSplits[votingRoundsWithResults.findIndex((vR) => vR.id === v.id)] ?? [],
}));

if (!connectedAddress) {
return error(401, 'Unauthorized');
}
fetchedDataCache.write({
dripLists: dripListsRes.dripLists,
votingRounds: votingRoundsWithSplits,
});

return { dripLists: dripListsRes.dripLists, votingRounds: votingRoundsWithSplits };
};
Expand Down
21 changes: 14 additions & 7 deletions src/routes/app/(app)/funds/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { redirect } from '@sveltejs/kit';
import { USER_BALANCES_FRAGMENT } from './sections/balances.section.svelte';
import buildUrl from '$lib/utils/build-url';
import getConnectedAddress from '$lib/utils/get-connected-address';
import { makeFetchedDataCache } from '$lib/stores/fetched-data-cache/fetched-data-cache.store';

const fetchedDataCache = makeFetchedDataCache<UserStreamsQuery>('dashboard:funds');

export const load = async ({ fetch }) => {
const connectedAddress = getConnectedAddress();
Expand All @@ -29,13 +32,17 @@ export const load = async ({ fetch }) => {
}
`;

const res = await query<UserStreamsQuery, UserStreamsQueryVariables>(
streamsQuery,
{
connectedAddress,
},
fetch,
);
const res =
fetchedDataCache.read() ??
(await query<UserStreamsQuery, UserStreamsQueryVariables>(
streamsQuery,
{
connectedAddress,
},
fetch,
));

fetchedDataCache.write(res);

return {
streams: res.userByAddress.streams,
Expand Down
17 changes: 12 additions & 5 deletions src/routes/app/(app)/projects/+page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import type { ProjectsPageQuery, ProjectsPageQueryVariables } from './__generate
import { redirect } from '@sveltejs/kit';
import buildUrl from '$lib/utils/build-url';
import getConnectedAddress from '$lib/utils/get-connected-address';
import { makeFetchedDataCache } from '$lib/stores/fetched-data-cache/fetched-data-cache.store';

const fetchedDataCache = makeFetchedDataCache<ProjectsPageQuery>('dashboard:projects');

export const load = async ({ fetch }) => {
const connectedAddress = getConnectedAddress();
Expand All @@ -22,11 +25,15 @@ export const load = async ({ fetch }) => {
}
`;

const res = await query<ProjectsPageQuery, ProjectsPageQueryVariables>(
projectsQuery,
{ address: connectedAddress },
fetch,
);
const res =
fetchedDataCache.read() ??
(await query<ProjectsPageQuery, ProjectsPageQueryVariables>(
projectsQuery,
{ address: connectedAddress },
fetch,
));

fetchedDataCache.write(res);

return { projects: res.projects };
};
Expand Down
29 changes: 18 additions & 11 deletions src/routes/app/+layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,32 @@ import query from '$lib/graphql/dripsQL.js';
import { gql } from 'graphql-request';
import type { UserQuery, UserQueryVariables } from './__generated__/gql.generated';
import getConnectedAddress from '$lib/utils/get-connected-address.js';
import { makeFetchedDataCache } from '$lib/stores/fetched-data-cache/fetched-data-cache.store';

const fetchedDataCache = makeFetchedDataCache<UserQuery>('app-layout:user');

export const load = async ({ url: { pathname }, fetch, depends }) => {
const connectedAddress = getConnectedAddress();

if (connectedAddress) {
depends('app-layout:user');

const user = await query<UserQuery, UserQueryVariables>(
gql`
${HEADER_USER_FRAGMENT}
query User($connectedAddress: String!) {
userByAddress(address: $connectedAddress) {
...HeaderUser
const user =
fetchedDataCache.read() ??
(await query<UserQuery, UserQueryVariables>(
gql`
${HEADER_USER_FRAGMENT}
query User($connectedAddress: String!) {
userByAddress(address: $connectedAddress) {
...HeaderUser
}
}
}
`,
{ connectedAddress },
fetch,
);
`,
{ connectedAddress },
fetch,
));

fetchedDataCache.write(user);

return { user: user.userByAddress, pathname };
}
Expand Down
Loading