188 lines
4.5 KiB
Svelte
188 lines
4.5 KiB
Svelte
<script lang="ts">
|
|
import { Link } from "svelte-navigator";
|
|
import { UpListing } from "upend";
|
|
import AttributeView from "../components/AttributeView.svelte";
|
|
import UpObjectCard from "../components/display/UpObjectCard.svelte";
|
|
import Spinner from "../components/utils/Spinner.svelte";
|
|
import { fetchRoots } from "../lib/api";
|
|
import { query } from "../lib/entity";
|
|
import { UpType } from "../lib/types";
|
|
import { vaultInfo } from "../util/info";
|
|
import { updateTitle } from "../util/title";
|
|
|
|
const roots = (async () => {
|
|
const data = await fetchRoots();
|
|
const listing = new UpListing(data);
|
|
return Object.values(listing.objects)
|
|
.filter((obj) => Boolean(obj.attr["LBL"]))
|
|
.map((obj) => [obj.address, obj.identify().join(" | ")])
|
|
.sort(([_, i1], [__, i2]) => i1.localeCompare(i2));
|
|
})();
|
|
|
|
const { result: frecencyQuery } = query(
|
|
`(matches ? (in "LAST_VISITED" "NUM_VISITED") ? )`
|
|
);
|
|
let frecent = [];
|
|
$: {
|
|
const entries = ($frecencyQuery?.entries || []).filter(
|
|
(e) => e.value.t == "Number"
|
|
);
|
|
|
|
const recentEntities = entries
|
|
.filter((e) => e.attribute == "LAST_VISITED")
|
|
.sort((a, b) => (b.value.c as number) - (a.value.c as number))
|
|
.map((e) => e.entity);
|
|
|
|
const frequentEntities = entries
|
|
.filter((e) => e.attribute == "NUM_VISITED")
|
|
.sort((a, b) => (b.value.c as number) - (a.value.c as number))
|
|
.map((e) => e.entity);
|
|
|
|
function indexOfDefault<T>(arr: T[], el: T, _default: number): number {
|
|
const index = arr.indexOf(el);
|
|
return index === -1 ? _default : index;
|
|
}
|
|
|
|
const result = entries.filter((e) => e.attribute == "LAST_VISITED");
|
|
result.sort((a, b) => {
|
|
const aRank =
|
|
indexOfDefault(recentEntities, a.entity, 9999) +
|
|
indexOfDefault(frequentEntities, a.entity, 9999);
|
|
const bRank =
|
|
indexOfDefault(recentEntities, b.entity, 9999) +
|
|
indexOfDefault(frequentEntities, b.entity, 9999);
|
|
return aRank - bRank;
|
|
});
|
|
|
|
frecent = result.slice(0, 25);
|
|
}
|
|
|
|
const { result: latestQuery } = query(`(matches ? "ADDED" ?)`);
|
|
$: latest = ($latestQuery?.entries || [])
|
|
.filter((e) => e.value.t == "Number")
|
|
.sort((a, b) => (b.value.c as number) - (a.value.c as number))
|
|
.slice(0, 25);
|
|
|
|
const HomeViewType = new UpType();
|
|
HomeViewType.name = "HOME_VIEW";
|
|
|
|
updateTitle("Home");
|
|
</script>
|
|
|
|
<div class="home">
|
|
<h1>
|
|
{$vaultInfo?.name || "UpEnd"}
|
|
</h1>
|
|
|
|
<section class="roots">
|
|
<h2>Roots</h2>
|
|
{#await roots}
|
|
<Spinner centered />
|
|
{:then data}
|
|
<ul>
|
|
{#each data as [address, _]}
|
|
<li class="root">
|
|
<UpObjectCard {address} />
|
|
</li>
|
|
{:else}
|
|
<li>No roots :(</li>
|
|
{/each}
|
|
</ul>
|
|
{/await}
|
|
</section>
|
|
|
|
{#if frecent.length || $frecencyQuery === undefined}
|
|
<section class="frecent">
|
|
<h2>Frequently or recently visited</h2>
|
|
{#if $frecencyQuery == undefined}
|
|
<Spinner centered />
|
|
{:else}
|
|
<AttributeView
|
|
--current-background="var(--background)"
|
|
entries={frecent}
|
|
type={HomeViewType}
|
|
/>
|
|
{/if}
|
|
</section>
|
|
{/if}
|
|
|
|
{#if latest.length || $latestQuery === undefined}
|
|
<section class="latest">
|
|
<h2>Most recently added</h2>
|
|
{#if $latestQuery == undefined}
|
|
<Spinner centered />
|
|
{:else}
|
|
<AttributeView
|
|
--current-background="var(--background)"
|
|
entries={latest}
|
|
type={HomeViewType}
|
|
/>
|
|
{/if}
|
|
</section>
|
|
{/if}
|
|
|
|
<div class="button store-button">
|
|
<Link to="/store">View store statistics</Link>
|
|
</div>
|
|
|
|
<footer>
|
|
<div>
|
|
<strong>UpEnd</strong> - a database for the complex, the changing, and the
|
|
indeterminate
|
|
</div>
|
|
<div>
|
|
<a target="_blank" href="https://upendproject.net">
|
|
v{$vaultInfo?.version || "???"}
|
|
</a>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
.home {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
h1,
|
|
h2 {
|
|
text-align: center;
|
|
}
|
|
|
|
.latest,
|
|
.frecent {
|
|
margin: 2rem;
|
|
}
|
|
|
|
.roots {
|
|
ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0 2rem;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
gap: 2rem;
|
|
}
|
|
|
|
.root {
|
|
font-size: 24px;
|
|
}
|
|
}
|
|
|
|
footer {
|
|
border-top: 1px solid var(--foreground);
|
|
text-align: center;
|
|
margin: 3em 3em 1em 3em;
|
|
& > * {
|
|
margin: 0.5em;
|
|
}
|
|
}
|
|
|
|
.store-button {
|
|
display: inline-block;
|
|
padding: 1em;
|
|
margin: auto;
|
|
}
|
|
</style>
|