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

Account activity loading enhancement #334

Draft
wants to merge 8 commits into
base: dev
Choose a base branch
from
Draft
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/components/elements/transaction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

let { id }: { id?: Checksum256 | string } = $props();

const truncatedString = truncateCenter(String(id));
const truncatedString = $derived(truncateCenter(String(id)));
</script>

{#if id}
Expand Down
5 changes: 3 additions & 2 deletions src/routes/[network]/(explorer)/account/[name]/+layout.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AccountState } from '$lib/state/client/account.svelte';
import { error, type LoadEvent } from '@sveltejs/kit';
import { error } from '@sveltejs/kit';
import { Name } from '@wharfkit/antelope';
import * as m from '$lib/paraglide/messages';
import type { LayoutLoad } from './$types';

export const load = async ({ fetch, params, parent }: LoadEvent) => {
export const load: LayoutLoad = async ({ fetch, params, parent }) => {
const { network } = await parent();
let account: AccountState;
try {
Expand Down
4 changes: 4 additions & 0 deletions src/routes/[network]/(explorer)/account/[name]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
const netAvailable = $derived(data.account.net?.available);
</script>

<svelte:head>
<link rel="prefetch" href={`/${context.network}/api/account/${data.account.name}/activity/0`} />
</svelte:head>

<!-- What gets shown on this page if data.account doesn't exist? -->
{#if data.account}
<MultiCard>
Expand Down
166 changes: 35 additions & 131 deletions src/routes/[network]/(explorer)/account/[name]/activity/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,142 +1,46 @@
<script lang="ts">
import Stack from '$lib/components/layout/stack.svelte';
import { onMount } from 'svelte';
import { ActivityLoader } from './state.svelte.js';
import type { ActivityActionWrapper } from '$lib/types.js';
import Code from '$lib/components/code.svelte';
import Transaction from '$lib/components/elements/transaction.svelte';
import Button from '$lib/components/button/button.svelte';
import Contract from '$lib/components/elements/contract.svelte';
import * as m from '$lib/paraglide/messages';

const { data } = $props();

const networkName = String(data.network);

onMount(() => {
const loader = ActivityLoader.getInst(networkName);
loader.setAccount(String(data.name));
loader.load();
});

const activityLoader: ActivityLoader = $derived.by(() => {
return ActivityLoader.getInst(networkName);
});

const isLoading = $derived.by(() => {
const scence = activityLoader.scene;
return scence.isLoading && !scence.list.length;
});

const hasMore = $derived(activityLoader.scene.hasMore);
const loadingText = $derived.by(() => {
const scence = activityLoader.scene;
if (!scence.hasMore) return 'No more';
if (scence.isLoading) return 'Loading';
return 'Load more';
});

const activityActions: ActivityActionWrapper[] = $derived.by(() => {
const scence = activityLoader.scene;
return [...scence.list];
});

function clickLoadMore() {
activityLoader.laodMore();
}
</script>

<Stack class="pb-8">
{#if isLoading}
<div class="flex items-center justify-center gap-4 py-20">
<div class="bounce bounce-1 h-3 w-3 rounded-full bg-white"></div>
<div class="bounce bounce-2 h-3 w-3 rounded-full bg-white"></div>
<div class="bounce bounce-3 h-3 w-3 rounded-full bg-white"></div>
<svelte:head>
<link
rel="prefetch"
href={`/${data.network}/api/account/${data.account}/activity/${-data.json.activity.last}`}
/>
</svelte:head>

{#if data.json.activity.actions.length}
<div class="grid gap-6 md:grid-cols-[auto_auto_auto_1fr]">
<div
class="col-span-full grid grid-cols-subgrid gap-2 border-b border-mineShaft-900 pb-4 *:text-mineShaft-50 md:gap-4"
>
<p>ID</p>
<p>Time</p>
<p>Action</p>
<p>Info</p>
</div>
{/if}
{#if activityActions.length}
<div>
<div class="hidden border-b border-mineShaft-900 lg:flex lg:flex-row">
<div class="grow-0 basis-[12%] px-2 py-3">ID</div>
<div class="grow-0 basis-[20%] px-2 py-3">Time</div>
<div class="grow-0 basis-[23%] px-2 py-3">Action</div>
<div class="grow-0 basis-[45%] px-2 py-3">Info</div>
</div>

{#each activityActions as activityAction}
<div
class="text-muted box-border flex flex-col break-words border-b border-mineShaft-900 py-4 lg:flex-row"
>
<div class="flex flex-1 gap-2 px-2 py-1 lg:max-w-[12%] lg:grow-0 lg:basis-[11%] lg:py-3">
<div class="block lg:hidden">
<span class="text-white">ID:</span>
</div>
<Transaction id={activityAction.id} />
</div>
<div
class="flex flex-1 gap-2 px-2 py-1 lg:max-w-[20%] lg:grow-0 lg:basis-[20%] lg:gap-0 lg:py-3"
>
<div class="block lg:hidden">
<span class="text-white">Time:</span>
</div>
<div>
<span>{activityAction.date}&nbsp&nbsp{activityAction.timeInDay}</span>
</div>
</div>
<div
class="flex flex-1 break-all px-2 py-1 lg:max-w-[23%] lg:grow-0 lg:basis-[23%] lg:py-3"
>
<div>
<span class="inline-block rounded px-3 py-0.5 {activityAction.actionStyle} text-white"
>{activityAction.actionName}</span
>
</div>
</div>
<div
class="flex flex-1 flex-col px-2 py-1 lg:max-w-[45%] lg:grow-0 lg:basis-[45%] lg:py-3"
>
<Code>{JSON.stringify(activityAction.actionData, null, 2)}</Code>
</div>
{#each data.json.activity.actions as action}
<div
class="col-span-full grid grid-cols-subgrid gap-2 border-b border-mineShaft-900 pb-6 last:border-none md:gap-4"
>
<Transaction id={action.id} />
<p>{action.timestamp}</p>
<div class="flex gap-2">
<Contract name={action.contract} />
<p>{action.action}</p>
</div>
{/each}
</div>

{#if hasMore}
<Button onclick={clickLoadMore} variant="primary" class="">
{loadingText}
</Button>
{/if}
{/if}
</Stack>

<style>
@keyframes bounce {
0%,
20%,
50%,
80%,
100% {
transform: translateY(0);
}
40% {
transform: translateY(-10px);
}
60% {
transform: translateY(-5px);
}
}

.bounce {
animation: bounce 1.4s infinite ease-in-out;
}

.bounce-1 {
animation-delay: 0s;
}

.bounce-2 {
animation-delay: 0.2s;
}
<Code>{JSON.stringify(action.data, null, 2)}</Code>
</div>
{/each}
</div>
{/if}

.bounce-3 {
animation-delay: 0.4s;
}
</style>
{#if data.json.activity.last}
<Button href="?start={data.json.activity.last}">{m.common_next()}</Button>
{/if}
12 changes: 11 additions & 1 deletion src/routes/[network]/(explorer)/account/[name]/activity/+page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import type { PageLoad } from './$types';
import * as m from '$lib/paraglide/messages';

export const load: PageLoad = async ({ params, parent }) => {
export const load: PageLoad = async ({ params, parent, fetch, url }) => {
const { network } = await parent();

const account = params.name;
const startIndex = Number(url.searchParams.get('start')) * -1;

const response = await fetch(`/${network}/api/account/${account}/activity/${startIndex}`);
const json = await response.json();

return {
account,
network,
json,
subtitle: m.explorer_account_activity_subtitle({
network: network.chain.name
}),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ export async function GET({ fetch, params }: RequestEvent) {
return json({ error: `Activity not supported on ${network.chain.name}.` }, { status: 500 });
}

const start = Number(params.start) || 1;
const requests = [getActivity(network.client, params.name, start)];
const headers = getCacheHeaders(5);
const start = params.start === '0' ? 1 : Number(params.start);

// Aggressively cache older activity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't do this, because older activity shifts in time and the results aren't in the same sequence because its in descending order.

If the initial page shows records 99 through 90 (walking back in time), and page 2 shows 89 to 80 - then if a new entry is added to the front, record 90 would end up on page 2. If page 2 is cached, that record won't show.

const headers = start === 1 ? getCacheHeaders(5) : getCacheHeaders(3600);

try {
const [activity] = await Promise.all(requests);
// This call is intermittently very slow...
const activity = await getActivity(network.client, params.name, start);
return json(
{
ts: new Date(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ export async function getActivity(
start: number
): Promise<Activity> {
const robo = new RoborovskiClient(client);

let response: API.v1.GetActionsResponse;
try {
// This is where we see some occasional performance slowdowns
// const robo_begin = performance.now();
response = await robo.get_actions(name, {
limit: 20,
start: start,
reverse: true
});
// const robo_end = performance.now();
// console.log('roboGetActions', robo_end - robo_begin);
} catch {
throw new Error(`Error while loading activity for ${name}.`);
}
Expand Down
Loading