diff --git a/frontend/src/lib/components/Callout.svelte b/frontend/src/lib/components/Callout.svelte
index 526cb9da4..e201cdf2e 100644
--- a/frontend/src/lib/components/Callout.svelte
+++ b/frontend/src/lib/components/Callout.svelte
@@ -29,17 +29,21 @@
>
-
+
{title}
-
-
+ {#if $$slots['top-right']}
+
+
+ {/if}
-
-
-
+ {#if $$slots.default}
+
+
+
+ {/if}
diff --git a/frontend/src/routes/explore/+page.svelte b/frontend/src/routes/explore/+page.svelte
index 57e20c71e..85099222f 100644
--- a/frontend/src/routes/explore/+page.svelte
+++ b/frontend/src/routes/explore/+page.svelte
@@ -19,30 +19,46 @@
let chosenSites: string[] = [];
let similarSites: ScoredSite[] = [];
- let errorMessage = false;
+ type ValidationState = 'idle' | 'validating url' | 'loading similar' | 'error';
+ let validation: ValidationState = 'idle';
$: {
- api
- .webgraphHostSimilar({ sites: chosenSites, topN: limit })
- .data.then((res) => (similarSites = res));
+ validation = 'loading similar';
+ api.webgraphHostSimilar({ sites: chosenSites, topN: limit }).data.then(async (res) => {
+ await wait(1000);
+ similarSites = res;
+ validation = 'idle';
+ });
}
+ const wait = (ms: number) => new Promise((res) => setTimeout(res, ms));
+
const removeWebsite = async (site: string) => {
if (chosenSites.includes(site)) {
chosenSites = chosenSites.filter((s) => s != site);
}
};
- const addWebsite = async (site: string, clear = false) => {
- errorMessage = false;
+ const addWebsite = async (
+ site: string,
+ { clear = false, validate = true }: { clear?: boolean; validate?: boolean } = {},
+ ) => {
+ validation = 'idle';
site = site.trim();
if (!site) return;
+ if (!validate) {
+ chosenSites = [...chosenSites, site];
+ return;
+ }
+ validation = 'validating url';
+ await wait(1000);
const result = await api.webgraphHostKnows({ site }).data;
match(result)
.with({ type: 'unknown' }, () => {
- errorMessage = true;
+ validation = 'error';
})
.with({ type: 'known' }, async ({ site }) => {
+ validation = 'idle';
if (clear) inputWebsite = '';
if (!chosenSites.includes(site)) chosenSites = [...chosenSites, site];
})
@@ -74,12 +90,13 @@
'mb-2 flex w-full max-w-lg rounded-full border border-base-400 bg-base-100 p-[2px] pl-3 transition focus-within:shadow',
)}
id="site-input-container"
- on:submit|preventDefault={() => addWebsite(inputWebsite, true)}
+ novalidate
+ on:submit|preventDefault={() => addWebsite(inputWebsite, { clear: true })}
>
- {#if errorMessage}
+ {#if validation == 'error'}
-
+ {:else if validation == 'validating url'}
+
+
+
+ {:else if validation == 'loading similar'}
+
+
+
{/if}
{#each chosenSites as site (site)}
@@ -115,9 +140,11 @@
{#if chosenSites.length > 0 && similarSites.length > 0}
-
-
Similar sites
-
+
+
Similar sites
+
-
-
Export as optic
+
+ Export as optic
+
{#each similarSites as site (site.site)}
@@ -141,7 +169,7 @@
on:click={() =>
chosenSites.includes(site.site)
? removeWebsite(site.site)
- : addWebsite(site.site)}
+ : addWebsite(site.site, { validate: false })}
>