Skip to content

Commit

Permalink
Start creating City page
Browse files Browse the repository at this point in the history
  • Loading branch information
wilsonzlin committed May 5, 2024
1 parent 07c3338 commit 2d98022
Show file tree
Hide file tree
Showing 12 changed files with 421 additions and 68 deletions.
41 changes: 41 additions & 0 deletions api/endpoint/urlMetas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { decode } from "@msgpack/msgpack";
import {
VArray,
VInteger,
VString,
VStruct,
VUtf8Bytes,
Valid,
} from "@wzlin/valid";
import { UrlMeta, vUrlMeta } from "../../common/const";
import { db, getKvRow } from "../../common/res";

const input = new VStruct({
// Must be normalized.
urls: new VArray(new VString(), 1, 250),
});

export const endpointUrlMetas = {
input,
handler: async ({ urls }: Valid<typeof input>) => {
const ids = await db.query(
`select id, url from url where url in (${urls.map(() => "?").join(",")})`,
urls,
new VStruct({
id: new VInteger(1),
url: new VUtf8Bytes(new VString()),
}),
);
const res: Record<string, UrlMeta> = {};
await Promise.all(
ids.map(async ({ id, url }) => {
const raw = await getKvRow.execute(`url/${id}/meta`);
if (!raw) {
return;
}
res[url] = vUrlMeta.parseRoot(decode(raw));
}),
);
return res;
},
};
2 changes: 2 additions & 0 deletions api/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { endpointHeatmap } from "./endpoint/heatmap";
import { endpointSearch } from "./endpoint/search";
import { endpointTopPosts } from "./endpoint/topPosts";
import { endpointTopUsers } from "./endpoint/topUsers";
import { endpointUrlMetas } from "./endpoint/urlMetas";

const getPemEnv = (name: string) =>
uint8ArrayToBuffer(
Expand All @@ -31,6 +32,7 @@ const ENDPOINTS: Record<string, Endpoint> = {
search: endpointSearch,
topPosts: endpointTopPosts,
topUsers: endpointTopUsers,
urlMetas: endpointUrlMetas,
};

createSecureServer(
Expand Down
20 changes: 19 additions & 1 deletion app/component/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useEffect, useMemo, useState } from "react";
import { AnalysisPage } from "../page/Analysis";
import { CityPage } from "../page/City";
import { NotFoundPage } from "../page/NotFound";
import { SearchPage } from "../page/Search";
import { DEFAULT_EDGE, EdgeContext, findClosestEdge } from "../util/item";
import { router } from "../util/router";
import "./App.css";

Expand All @@ -16,10 +18,26 @@ export const App = () => {
() =>
({
"/": SearchPage,
"/city": CityPage,
"/analysis": AnalysisPage,
})[path] ?? NotFoundPage,
[path],
);

return <Page />;
const [edge, setEdge] = useState(DEFAULT_EDGE);
useEffect(() => {
const ac = new AbortController();
(async () => {
const edge = await findClosestEdge(ac.signal);
console.log("Closest edge:", edge);
setEdge(edge);
})();
return ac.abort();
}, []);

return (
<EdgeContext.Provider value={edge}>
<Page />
</EdgeContext.Provider>
);
};
10 changes: 3 additions & 7 deletions app/component/PageSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,9 @@ export const PageSwitcher = () => {
<span>Search</span>
<Ico i="public" size={ICO_SIZE} />
</RouteLink>
<RouteLink href="/journey">
<span>Journey</span>
<Ico i="travel" size={ICO_SIZE} />
</RouteLink>
<RouteLink href="/recommend">
<span>Recommend</span>
<Ico i="lightbulb" size={ICO_SIZE} />
<RouteLink href="/city">
<span>City</span>
<Ico i="groups" size={ICO_SIZE} />
</RouteLink>
<RouteLink href="/analysis">
<span>Analysis</span>
Expand Down
65 changes: 23 additions & 42 deletions app/component/PointMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import Dict from "@xtjs/lib/Dict";
import UnreachableError from "@xtjs/lib/UnreachableError";
import assertExists from "@xtjs/lib/assertExists";
import bounded from "@xtjs/lib/bounded";
import { MutableRefObject, useEffect, useMemo, useRef, useState } from "react";
import {
MutableRefObject,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { EdgeContext } from "../util/item";
import {
MAP_DATASET,
MapState,
Expand All @@ -24,13 +32,6 @@ const vMapMeta = new VStruct({
lod_levels: new VInteger(0),
});

const EDGES = [
"ap-sydney-1",
"uk-london-1",
"us-ashburn-1",
"us-sanjose-1",
] as const;

export type PointMapController = {
animate(
vp: {
Expand Down Expand Up @@ -114,61 +115,41 @@ export const PointMap = ({
);
useEffect(() => localStorage.setItem("hndr-map-theme", theme), [theme]);

const edge = useContext(EdgeContext);
const $canvas = useRef<HTMLCanvasElement>(null);
const [meta, setMeta] = useState<MapState>();
const [map, setMap] = useState<ReturnType<typeof createCanvasPointMap>>();
useEffect(() => map?.setTheme(theme), [map, theme]);
useEffect(() => map?.setEdge(edge), [map, edge]);
useEffect(() => map?.setHeatmaps(heatmaps), [map, heatmaps]);
const [meta, setMeta] = useState<MapState>();
useEffect(() => map?.setTheme(theme), [map, theme]);
useEffect(() => {
const ac = new AbortController();

// Use Promise.race so we don't wait for the slow ones.
const findClosestEdge = () =>
Promise.race(
EDGES.map(async (edge) => {
// Run a few times to avoid potential cold start biases.
for (let i = 0; i < 3; i++) {
await fetch(`https://${edge}.edge-hndr.wilsonl.in/healthz`, {
signal: ac.signal,
});
}
return edge;
}),
);

const fetchMeta = async () => {
(async () => {
const res = await fetch(
`https://us-ashburn-1.edge-hndr.wilsonl.in/map/${MAP_DATASET}/meta`,
{ signal: ac.signal },
);
const raw = await res.arrayBuffer();
const meta = vMapMeta.parseRoot(decode(raw));
return new MapState({
lodLevels: meta.lod_levels,
scoreMax: meta.score_max,
scoreMin: meta.score_min,
xMaxPt: meta.x_max,
xMinPt: meta.x_min,
yMaxPt: meta.y_max,
yMinPt: meta.y_min,
const metaInit = vMapMeta.parseRoot(decode(raw));
const meta = new MapState({
lodLevels: metaInit.lod_levels,
scoreMax: metaInit.score_max,
scoreMin: metaInit.score_min,
xMaxPt: metaInit.x_max,
xMinPt: metaInit.x_min,
yMaxPt: metaInit.y_max,
yMinPt: metaInit.y_min,
});
};

(async () => {
const [edge, meta] = await Promise.all([findClosestEdge(), fetchMeta()]);
console.log("Closest edge:", edge);
console.log("Map metadata:", meta);
setMeta(meta);
setVpCtrXPt(meta.xMinPt + meta.xRangePt / 2);
setVpCtrYPt(meta.yMinPt + meta.yRangePt / 2);
const map = createCanvasPointMap({
canvas: assertExists($canvas.current),
edge,
map: meta,
});
setMap(map);
})();

return () => ac.abort();
}, []);

Expand Down
2 changes: 1 addition & 1 deletion app/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"liga" 1,
"calt" 1; /* https://rsms.me/inter/download/ */
font-size: 16px;
line-height: 1.5;
line-height: 1.4;

user-select: none;
}
Expand Down
124 changes: 124 additions & 0 deletions app/page/City.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
.City {
position: fixed;
inset: 0;
max-width: 108rem;
margin: 0 auto;

display: flex;
flex-direction: column;

img:not([data-loaded="true"]) {
position: fixed;
opacity: 0;
}

.query-form {
flex-shrink: 0;

background: white;
border-radius: 0.25rem;
box-shadow: 0 2px 1rem rgba(0, 0, 0, 0.25);

padding: 0.75rem;
margin: 1rem;

display: flex;
align-items: center;
gap: 0.75rem;
width: 35rem;

> .Ico {
flex-shrink: 0;
}

> input {
flex-grow: 1;
font-size: 1.5rem;
}
}

> main {
min-height: 0;
flex-grow: 1;

display: flex;
align-items: stretch;
}

.panel {
width: 24rem;
flex-shrink: 0;
border-left: 1px solid #eee;
overflow: auto;
}

.posts {
min-width: 0;
flex-grow: 1;
overflow: auto;

padding: 1rem;

display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;

> .post {
flex-shrink: 0;
width: 45rem;

border: 1px solid #eee;
border-radius: 0.5rem;
overflow: hidden;

&:hover {
background-color: #fdfdfd;
text-decoration: none;
}

> .text {
display: flex;
flex-direction: column;
gap: 0.5rem;

padding: 0.75rem;

> .header {
display: flex;
gap: 0.75rem;
align-items: center;

> .favicon {
width: 2rem;
height: 2rem;
border-radius: 100%;
border: 1px solid #eee;
object-fit: contain;
}

> .right {
> .site {
font-size: 0.8rem;
color: #555;
}

> h2 {
font-size: 1.2rem;
}
}
}

> .snippet {
color: #333;
font-size: 0.9rem;
}
}

> .image {
display: block;
width: 100%;
}
}
}
}
Loading

0 comments on commit 2d98022

Please sign in to comment.