mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-09 22:43:33 -07:00
general: snapshot
This commit is contained in:
+17
-10
@@ -2,23 +2,26 @@
|
||||
|
||||
## v. 0.1.2 | WIP
|
||||
|
||||
### Parser
|
||||
|
||||
-
|
||||

|
||||
|
||||
### App
|
||||
|
||||
- Performance
|
||||
- Improved app's reactivity
|
||||
- Added some chunk splitting for a faster initial load
|
||||
- Global improvements that increased the Lighthouse's performance score from the low 30s to the high 70s
|
||||
- Chart
|
||||
- Fix legend hovering on mobile not resetting on touch end
|
||||
- Fixed legend hovering on mobile not resetting on touch end
|
||||
- Updated legend padding so that the scrollbar, if visible, is less in the way
|
||||
- Added yearly time scale setters (from year 2009 to today)
|
||||
- Misc
|
||||
- Support mini window size, could be useful for embedded views
|
||||
|
||||
### Server
|
||||
|
||||
-
|
||||
- Hopefully made scrollbars a little more subtle on WIndows and Linux, can't test
|
||||
|
||||
## v. 0.1.1 | 849240 - 2024/06/24
|
||||
|
||||

|
||||
|
||||
### Parser
|
||||
|
||||
- Fixed overflow in `Price` struct which caused many Realized Caps and Realized Prices to have completely bogus data
|
||||
@@ -26,7 +29,7 @@
|
||||
|
||||
### Server
|
||||
|
||||
- Added the chunk, date and time in the terminal logs
|
||||
- Added the chunk, date and time of the request to the terminal logs
|
||||
|
||||
### App
|
||||
|
||||
@@ -53,7 +56,7 @@
|
||||
- Strip
|
||||
- Temporarily removed the Home button on the strip bar on desktop as there is no landing page yet
|
||||
- Settings
|
||||
- Add version
|
||||
- Added version
|
||||
- PWA
|
||||
- Fixed background update
|
||||
- Changed update check frequency to 1 minute (~1kb to fetch every minute which is very reasonable)
|
||||
@@ -64,3 +67,7 @@
|
||||
### Price
|
||||
|
||||
- Deleted old price datasets and their backups
|
||||
|
||||
## v. 0.1.0 | 848642 - 2024/06/19
|
||||
|
||||

|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# SATONOMICS
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
TLDR: Free, open source, verifiable and self-hostable Bitcoin on-chain data generator and visualizer
|
||||
|
||||
@@ -4,6 +4,7 @@ const texts = [
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
"satonomics",
|
||||
|
||||
"stay humble, stack sats",
|
||||
"21 million",
|
||||
@@ -29,6 +30,14 @@ const texts = [
|
||||
"be your own bank",
|
||||
"resistance money",
|
||||
"foss",
|
||||
"permissionless",
|
||||
"great reset",
|
||||
"orange pill",
|
||||
"borderless",
|
||||
"anonymous",
|
||||
"nyknyc",
|
||||
"low time preference",
|
||||
"absolute scarcity",
|
||||
];
|
||||
|
||||
export const LOCAL_STORAGE_MARQUEE_KEY = "bg-marquee";
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { generate } from "lean-qr";
|
||||
|
||||
import { chartState } from "/src/scripts/lightweightCharts/chart/state";
|
||||
import { setTimeScale } from "/src/scripts/lightweightCharts/chart/time";
|
||||
import { classPropToString } from "/src/solid/classes";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { Button } from "./button";
|
||||
|
||||
export function Actions({
|
||||
presets,
|
||||
@@ -14,6 +12,10 @@ export function Actions({
|
||||
qrcode: RWS<string>;
|
||||
fullscreen?: RWS<boolean>;
|
||||
}) {
|
||||
const ButtonShare = lazy(() =>
|
||||
import("./buttonShare").then((d) => ({ default: d.ButtonShare })),
|
||||
);
|
||||
|
||||
return (
|
||||
<div class="flex space-x-1">
|
||||
<Show when={fullscreen}>
|
||||
@@ -37,18 +39,8 @@ export function Actions({
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<Button
|
||||
title="Share"
|
||||
icon={() => IconTablerShare}
|
||||
onClick={() => {
|
||||
qrcode.set(() =>
|
||||
generate(document.location.href).toDataURL({
|
||||
on: [0xff, 0xff, 0xff, 0xff],
|
||||
off: [0x00, 0x00, 0x00, 0x00],
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<ButtonShare qrcode={qrcode} />
|
||||
|
||||
<Button
|
||||
title="Favorite"
|
||||
colors={() =>
|
||||
@@ -66,38 +58,3 @@ export function Actions({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Button({
|
||||
title,
|
||||
icon,
|
||||
colors,
|
||||
onClick,
|
||||
disabled,
|
||||
classes,
|
||||
}: {
|
||||
title: string;
|
||||
icon: () => ValidComponent;
|
||||
colors?: () => string;
|
||||
onClick: VoidFunction;
|
||||
disabled?: () => boolean;
|
||||
classes?: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
title={title}
|
||||
disabled={disabled?.()}
|
||||
class={classPropToString([
|
||||
colors?.() || (disabled?.() ? "" : "hover:bg-orange-200/15"),
|
||||
!disabled?.() && "group",
|
||||
classes,
|
||||
"flex-none rounded-lg p-2 disabled:opacity-50",
|
||||
])}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Dynamic
|
||||
component={icon()}
|
||||
class="size-[1.125rem] group-active:scale-90"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { classPropToString } from "/src/solid/classes";
|
||||
|
||||
export function Button({
|
||||
title,
|
||||
icon,
|
||||
colors,
|
||||
onClick,
|
||||
disabled,
|
||||
classes,
|
||||
}: {
|
||||
title: string;
|
||||
icon: () => ValidComponent;
|
||||
colors?: () => string;
|
||||
onClick: VoidFunction;
|
||||
disabled?: () => boolean;
|
||||
classes?: string;
|
||||
}) {
|
||||
return (
|
||||
<button
|
||||
title={title}
|
||||
disabled={disabled?.()}
|
||||
class={classPropToString([
|
||||
colors?.() || (disabled?.() ? "" : "hover:bg-orange-200/15"),
|
||||
!disabled?.() && "group",
|
||||
classes,
|
||||
"flex-none rounded-lg p-2 disabled:opacity-50",
|
||||
])}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Dynamic
|
||||
component={icon()}
|
||||
class="size-[1.125rem] group-active:scale-90"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { generate } from "lean-qr";
|
||||
|
||||
import { Button } from "./button";
|
||||
|
||||
export function ButtonShare({ qrcode }: { qrcode: RWS<string> }) {
|
||||
return (
|
||||
<Button
|
||||
title="Share"
|
||||
icon={() => IconTablerShare}
|
||||
onClick={() => {
|
||||
qrcode.set(() =>
|
||||
generate(document.location.href).toDataURL({
|
||||
on: [0xff, 0xff, 0xff, 0xff],
|
||||
off: [0x00, 0x00, 0x00, 0x00],
|
||||
}),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export function Legend({
|
||||
let toggle = false;
|
||||
|
||||
return (
|
||||
<div class="flex flex-1 items-center gap-1 overflow-y-auto">
|
||||
<div class="-my-1.5 -ml-1.5 flex flex-1 items-center gap-1 overflow-y-auto p-1.5">
|
||||
<For each={legendList()}>
|
||||
{(legend) => {
|
||||
const initialColors = {} as any;
|
||||
|
||||
@@ -5,29 +5,43 @@ import { ONE_DAY_IN_MS } from "/src/scripts/utils/time";
|
||||
import { Box } from "../../box";
|
||||
|
||||
export function TimeScale() {
|
||||
const today = new Date();
|
||||
|
||||
return (
|
||||
<Box dark padded overflowY classes="short:hidden">
|
||||
<Button onClick={() => setTimeScale()}>All Time</Button>
|
||||
<Button onClick={() => setTimeScale(7)}>1 Week</Button>
|
||||
<Button onClick={() => setTimeScale(30)}>1 Month</Button>
|
||||
<Button onClick={() => setTimeScale(30 * 6)}>6 Months</Button>
|
||||
<Button onClick={() => setTimeScale({})}>All Time</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 7 })}>1 Week</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 30 })}>1 Month</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 30 * 6 })}>6 Months</Button>
|
||||
<Button
|
||||
onClick={() =>
|
||||
setTimeScale(
|
||||
Math.ceil(
|
||||
(new Date().valueOf() -
|
||||
new Date(`${new Date().getUTCFullYear()}-01-01`).valueOf()) /
|
||||
setTimeScale({
|
||||
days: Math.ceil(
|
||||
(today.valueOf() -
|
||||
new Date(`${today.getUTCFullYear()}-01-01`).valueOf()) /
|
||||
ONE_DAY_IN_MS,
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
>
|
||||
Year To Date
|
||||
</Button>
|
||||
<Button onClick={() => setTimeScale(365)}>1 Year</Button>
|
||||
<Button onClick={() => setTimeScale(2 * 365)}>2 Years</Button>
|
||||
<Button onClick={() => setTimeScale(4 * 365)}>4 Years</Button>
|
||||
<Button onClick={() => setTimeScale(8 * 365)}>8 Years</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 365 })}>1 Year</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 2 * 365 })}>2 Years</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 4 * 365 })}>4 Years</Button>
|
||||
<Button onClick={() => setTimeScale({ days: 8 * 365 })}>8 Years</Button>
|
||||
<For
|
||||
each={new Array(
|
||||
new Date().getFullYear() - new Date("2009-01-01").getFullYear(),
|
||||
)
|
||||
.fill(0)
|
||||
.map((_, index) => index + 2009)
|
||||
.reverse()}
|
||||
>
|
||||
{(year) => (
|
||||
<Button onClick={() => setTimeScale({ year })}>{year}</Button>
|
||||
)}
|
||||
</For>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -43,25 +57,25 @@ function Button(props: ParentProps & { onClick: VoidFunction }) {
|
||||
);
|
||||
}
|
||||
|
||||
function setTimeScale(days?: number) {
|
||||
const to = new Date();
|
||||
function setTimeScale({ days, year }: { days?: number; year?: number }) {
|
||||
let from = new Date();
|
||||
let to = new Date();
|
||||
|
||||
if (days) {
|
||||
const from = new Date();
|
||||
if (year) {
|
||||
from = new Date(`${year}-01-01`);
|
||||
to = new Date(`${year}-12-31`);
|
||||
} else if (days) {
|
||||
from.setDate(from.getUTCDate() - days);
|
||||
|
||||
chartState.chart?.timeScale().setVisibleRange({
|
||||
from: (from.getTime() / 1000) as Time,
|
||||
to: (to.getTime() / 1000) as Time,
|
||||
});
|
||||
} else {
|
||||
// chartState.chart?.timeScale().fitContent();
|
||||
chartState.chart?.timeScale().setVisibleRange({
|
||||
from: (new Date(
|
||||
// datasets.candlesticks.values()?.[0]?.date || "",
|
||||
GENESIS_DAY,
|
||||
).getTime() / 1000) as Time,
|
||||
to: (to.getTime() / 1000) as Time,
|
||||
});
|
||||
from = new Date(GENESIS_DAY);
|
||||
}
|
||||
|
||||
setRange({
|
||||
from: (from.getTime() / 1000) as Time,
|
||||
to: (to.getTime() / 1000) as Time,
|
||||
});
|
||||
}
|
||||
|
||||
function setRange(range: TimeRange) {
|
||||
chartState.chart?.timeScale().setVisibleRange(range);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ export function ChartFrame({
|
||||
}) {
|
||||
const legend = createRWS<PresetLegend>([]);
|
||||
|
||||
const Chart = lazy(() =>
|
||||
import("./components/chart").then((d) => ({ default: d.Chart })),
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
class={classPropToString([
|
||||
@@ -35,7 +39,7 @@ export function ChartFrame({
|
||||
"flex size-full min-h-0 flex-1 flex-col overflow-hidden",
|
||||
])}
|
||||
style={{
|
||||
display: (hide ? !hide() : true) ? undefined : "none",
|
||||
display: (hide ? hide() : false) ? "none" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box flex={false} dark classes="short:hidden">
|
||||
@@ -56,7 +60,6 @@ export function ChartFrame({
|
||||
<Chart
|
||||
activeResources={activeResources}
|
||||
datasets={datasets}
|
||||
// fetchedDatasets={fetchedDatasets}
|
||||
legendSetter={legend.set}
|
||||
presets={presets}
|
||||
/>
|
||||
|
||||
@@ -12,7 +12,9 @@ export function FavoritesFrame({
|
||||
return (
|
||||
<div
|
||||
class="flex-1 overflow-y-auto"
|
||||
hidden={selectedFrame() !== "Favorites"}
|
||||
style={{
|
||||
display: selectedFrame() !== "Favorites" ? "none" : undefined,
|
||||
}}
|
||||
>
|
||||
<div class="flex max-h-full min-h-0 flex-1 flex-col gap-4 p-4">
|
||||
<Header title="Favorites">
|
||||
|
||||
@@ -11,7 +11,12 @@ export function HistoryFrame({
|
||||
selectedFrame: Accessor<FrameName>;
|
||||
}) {
|
||||
return (
|
||||
<div class="flex-1 overflow-y-auto" hidden={selectedFrame() !== "History"}>
|
||||
<div
|
||||
class="flex-1 overflow-y-auto"
|
||||
style={{
|
||||
display: selectedFrame() !== "History" ? "none" : undefined,
|
||||
}}
|
||||
>
|
||||
<div class="flex max-h-full min-h-0 flex-1 flex-col p-4">
|
||||
<Header title="History">List of previously visited presets.</Header>
|
||||
|
||||
|
||||
@@ -12,9 +12,14 @@ export function SettingsFrame({
|
||||
const value = marquee();
|
||||
|
||||
return (
|
||||
<div class="flex-1 overflow-y-auto" hidden={selectedFrame() !== "Settings"}>
|
||||
<div
|
||||
class="flex-1 overflow-y-auto"
|
||||
style={{
|
||||
display: selectedFrame() !== "Settings" ? "none" : undefined,
|
||||
}}
|
||||
>
|
||||
<div class="space-y-4 p-4">
|
||||
<Header title="Settings" />
|
||||
<Header title="Settings">And other stuff.</Header>
|
||||
|
||||
<div class="-mx-4 border-t border-orange-200/10" />
|
||||
|
||||
|
||||
@@ -21,80 +21,82 @@ export function Tree({
|
||||
favorites: Accessor<Preset[]>;
|
||||
}) {
|
||||
return (
|
||||
<div style={{ display: visible?.() === false ? "none" : undefined }}>
|
||||
<For each={tree}>
|
||||
{(thing) => {
|
||||
const active = createMemo(() => thing.id === selected().id);
|
||||
const favorite = createMemo(() =>
|
||||
favorites().includes(thing as Preset),
|
||||
);
|
||||
const visited = (thing as Preset).visited;
|
||||
|
||||
if (!("tree" in thing)) {
|
||||
return (
|
||||
<File
|
||||
id={thing.id}
|
||||
name={thing.name}
|
||||
active={active}
|
||||
depth={depth}
|
||||
icon={thing.icon || IconTablerFile}
|
||||
favorite={favorite}
|
||||
visited={visited}
|
||||
onClick={() => {
|
||||
const selectedId = selected().id;
|
||||
|
||||
if (selectedId === thing.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Has been filled in createPresets
|
||||
selectPreset(thing as Preset);
|
||||
}}
|
||||
/>
|
||||
<Show when={visible?.() || !visible}>
|
||||
<div>
|
||||
<For each={tree}>
|
||||
{(thing) => {
|
||||
const active = createMemo(() => thing.id === selected().id);
|
||||
const favorite = createMemo(() =>
|
||||
favorites().includes(thing as Preset),
|
||||
);
|
||||
}
|
||||
const visited = (thing as Preset).visited;
|
||||
|
||||
const childrenVisible = createMemo(() =>
|
||||
openedFolders().has(thing.id),
|
||||
);
|
||||
if (!("tree" in thing)) {
|
||||
return (
|
||||
<File
|
||||
id={thing.id}
|
||||
name={thing.name}
|
||||
active={active}
|
||||
depth={depth}
|
||||
icon={thing.icon || IconTablerFile}
|
||||
favorite={favorite}
|
||||
visited={visited}
|
||||
onClick={() => {
|
||||
const selectedId = selected().id;
|
||||
|
||||
const childCount = countChildren(thing);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Folder
|
||||
id={thing.id}
|
||||
name={thing.name}
|
||||
depth={depth}
|
||||
open={childrenVisible}
|
||||
children={childCount}
|
||||
onClick={() => {
|
||||
openedFolders.set((s) => {
|
||||
if (childrenVisible()) {
|
||||
s.delete(thing.id);
|
||||
} else {
|
||||
s.add(thing.id);
|
||||
if (selectedId === thing.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return s;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Tree
|
||||
tree={thing.tree}
|
||||
selected={selected}
|
||||
depth={depth + 1}
|
||||
openedFolders={openedFolders}
|
||||
visible={childrenVisible}
|
||||
path={[...path, { name: thing.name, id: thing.id }]}
|
||||
selectPreset={selectPreset}
|
||||
favorites={favorites}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
// Has been filled in createPresets
|
||||
selectPreset(thing as Preset);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const childrenVisible = createMemo(() =>
|
||||
openedFolders().has(thing.id),
|
||||
);
|
||||
|
||||
const childCount = countChildren(thing);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Folder
|
||||
id={thing.id}
|
||||
name={thing.name}
|
||||
depth={depth}
|
||||
open={childrenVisible}
|
||||
children={childCount}
|
||||
onClick={() => {
|
||||
openedFolders.set((s) => {
|
||||
if (childrenVisible()) {
|
||||
s.delete(thing.id);
|
||||
} else {
|
||||
s.add(thing.id);
|
||||
}
|
||||
|
||||
return s;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Tree
|
||||
tree={thing.tree}
|
||||
selected={selected}
|
||||
depth={depth + 1}
|
||||
openedFolders={openedFolders}
|
||||
visible={childrenVisible}
|
||||
path={[...path, { name: thing.name, id: thing.id }]}
|
||||
selectPreset={selectPreset}
|
||||
favorites={favorites}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,12 +62,12 @@ export function Update() {
|
||||
<Show when={needRefresh()}>
|
||||
<div class="absolute inset-x-1.5 top-1.5 z-[99999] flex items-center justify-between rounded-lg bg-orange-700/75 p-1.5 shadow backdrop-blur-sm">
|
||||
<div>
|
||||
<span class="truncate px-1">New version available, please</span>
|
||||
<span class="truncate px-1">New version available,</span>
|
||||
<button
|
||||
class="mr-2 rounded-md bg-orange-50 bg-opacity-60 px-1.5 py-0.5 font-medium text-orange-950 hover:bg-opacity-100"
|
||||
onClick={async () => await updateServiceWorker()}
|
||||
>
|
||||
install
|
||||
Install
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { env } from "../env";
|
||||
import { standalone } from "../env";
|
||||
import { createDatasets } from "../scripts/datasets";
|
||||
import { chartState } from "../scripts/lightweightCharts/chart/state";
|
||||
import { setTimeScale } from "../scripts/lightweightCharts/chart/time";
|
||||
@@ -18,7 +18,6 @@ import { Background, LOCAL_STORAGE_MARQUEE_KEY } from "./components/background";
|
||||
import { ChartFrame } from "./components/frames/chart";
|
||||
import { FavoritesFrame } from "./components/frames/favorites";
|
||||
import { HistoryFrame } from "./components/frames/history";
|
||||
import { SearchFrame } from "./components/frames/search";
|
||||
import { SettingsFrame } from "./components/frames/settings";
|
||||
import { TreeFrame } from "./components/frames/tree";
|
||||
import { Qrcode } from "./components/qrcode";
|
||||
@@ -156,6 +155,12 @@ export function App() {
|
||||
|
||||
const resizeInitialRange = createRWS<TimeRange | null>(null);
|
||||
|
||||
const SearchFrame = lazy(() =>
|
||||
import("./components/frames/search").then((d) => ({
|
||||
default: d.SearchFrame,
|
||||
})),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Background marquee={marquee} focused={tabFocused} />
|
||||
@@ -194,7 +199,7 @@ export function App() {
|
||||
<Show when={!windowSizeIsAtLeastMedium() || !fullscreen()}>
|
||||
<div
|
||||
class={classPropToString([
|
||||
env.standalone && "border-t",
|
||||
standalone && "border-t",
|
||||
"md:short:hidden flex h-full flex-col overflow-hidden border-white/10 bg-gradient-to-b from-orange-500/10 to-orange-950/10 md:flex-row md:rounded-2xl md:border",
|
||||
])}
|
||||
>
|
||||
@@ -242,7 +247,7 @@ export function App() {
|
||||
|
||||
<div
|
||||
class={classPropToString([
|
||||
env.standalone && "pb-6",
|
||||
standalone && "pb-6",
|
||||
"short:hidden flex justify-between gap-3 border-t border-white/10 bg-black/30 p-2 backdrop-blur-sm md:hidden",
|
||||
])}
|
||||
>
|
||||
|
||||
+7
-3
@@ -1,3 +1,7 @@
|
||||
export const env = {
|
||||
standalone: "standalone" in window.navigator && !!window.navigator.standalone,
|
||||
};
|
||||
export const standalone =
|
||||
"standalone" in window.navigator && !!window.navigator.standalone;
|
||||
|
||||
export const touchScreen =
|
||||
"ontouchstart" in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
(navigator as any).msMaxTouchPoints > 0;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { createResourceDataset } from "./resource";
|
||||
|
||||
export { averages } from "./consts/averages";
|
||||
|
||||
export function createScaleDatasets<Scale extends ResourceScale>({
|
||||
scale,
|
||||
setActiveResources,
|
||||
groupedKeysToURLPath,
|
||||
}: {
|
||||
scale: Scale;
|
||||
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
|
||||
groupedKeysToURLPath: GroupedKeysToURLPath[Scale];
|
||||
}) {
|
||||
type Key = keyof typeof groupedKeysToURLPath;
|
||||
type ResourceData = ReturnType<typeof createResourceDataset<Scale>>;
|
||||
|
||||
type ResourceDatasets = Record<Exclude<Key, "ohlc">, ResourceData>;
|
||||
|
||||
const datasets = groupedKeysToURLPath as any as ResourceDatasets;
|
||||
|
||||
for (const key in groupedKeysToURLPath) {
|
||||
if ((key as Key) !== "ohlc") {
|
||||
datasets[key as unknown as Exclude<Key, "ohlc">] = createResourceDataset({
|
||||
scale,
|
||||
path: groupedKeysToURLPath[key as Key] as any,
|
||||
setActiveResources,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const price = createResourceDataset<Scale, OHLC>({
|
||||
scale,
|
||||
path: `/${scale}-to-ohlc`,
|
||||
setActiveResources,
|
||||
});
|
||||
|
||||
Object.assign(datasets, { price });
|
||||
|
||||
return datasets;
|
||||
}
|
||||
@@ -14,33 +14,27 @@ export function createDateDatasets({
|
||||
|
||||
type ResourceDatasets = Record<Exclude<Key, "ohlc">, ResourceData>;
|
||||
|
||||
for (const _key in groupedKeysToURLPath) {
|
||||
const key = _key as Key;
|
||||
const datasets = groupedKeysToURLPath as any as ResourceDatasets;
|
||||
|
||||
if (key !== "ohlc") {
|
||||
const path = groupedKeysToURLPath[key];
|
||||
|
||||
(groupedKeysToURLPath as any as ResourceDatasets)[key] =
|
||||
createResourceDataset<"date">({
|
||||
scale: "date",
|
||||
path,
|
||||
setActiveResources,
|
||||
});
|
||||
for (const key in groupedKeysToURLPath) {
|
||||
if ((key as Key) !== "ohlc") {
|
||||
datasets[key as Exclude<Key, "ohlc">] = createResourceDataset<"date">({
|
||||
scale: "date",
|
||||
path: groupedKeysToURLPath[key as Key],
|
||||
setActiveResources,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resourceDatasets = groupedKeysToURLPath as any as ResourceDatasets;
|
||||
|
||||
const price = createResourceDataset<"date", OHLC>({
|
||||
scale: "date",
|
||||
path: "/date-to-ohlc",
|
||||
setActiveResources,
|
||||
});
|
||||
|
||||
const datasets = {
|
||||
price,
|
||||
...resourceDatasets,
|
||||
};
|
||||
Object.assign(datasets, { price });
|
||||
|
||||
return datasets;
|
||||
return datasets as ResourceDatasets & {
|
||||
price: ResourceDataset<"date", OHLC>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,31 +12,27 @@ export function createHeightDatasets({
|
||||
|
||||
type ResourceDatasets = Record<Exclude<Key, "ohlc">, ResourceData>;
|
||||
|
||||
for (const _key in groupedKeysToURLPath) {
|
||||
const key = _key as Key;
|
||||
const datasets = groupedKeysToURLPath as any as ResourceDatasets;
|
||||
|
||||
if (key !== "ohlc") {
|
||||
const path = groupedKeysToURLPath[key];
|
||||
|
||||
(groupedKeysToURLPath as any as ResourceDatasets)[key] =
|
||||
createResourceDataset<"height">({
|
||||
scale: "height",
|
||||
path,
|
||||
setActiveResources,
|
||||
});
|
||||
for (const key in groupedKeysToURLPath) {
|
||||
if ((key as Key) !== "ohlc") {
|
||||
datasets[key as Exclude<Key, "ohlc">] = createResourceDataset<"height">({
|
||||
scale: "height",
|
||||
path: groupedKeysToURLPath[key as Key],
|
||||
setActiveResources,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const resourceDatasets = groupedKeysToURLPath as any as ResourceDatasets;
|
||||
|
||||
const price = createResourceDataset<"height", OHLC>({
|
||||
scale: "height",
|
||||
path: "/height-to-ohlc",
|
||||
setActiveResources,
|
||||
});
|
||||
|
||||
return {
|
||||
...resourceDatasets,
|
||||
price,
|
||||
Object.assign(datasets, { price });
|
||||
|
||||
return datasets as ResourceDatasets & {
|
||||
price: ResourceDataset<"height", OHLC>;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,13 +21,6 @@ export function createResourceDataset<
|
||||
path: string;
|
||||
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
|
||||
}) {
|
||||
const baseURL = `${
|
||||
// location.hostname === "localhost"
|
||||
// ? "http://localhost:3110"
|
||||
// : "https://api.satonomics.xyz"
|
||||
"https://api.satonomics.xyz"
|
||||
}${path}`;
|
||||
|
||||
type Dataset = Scale extends "date"
|
||||
? FetchedDateDataset<Type>
|
||||
: FetchedHeightDataset<Type>;
|
||||
@@ -36,6 +29,13 @@ export function createResourceDataset<
|
||||
Type extends number ? SingleValueData : CandlestickData
|
||||
>;
|
||||
|
||||
const baseURL = `${
|
||||
// location.hostname === "localhost"
|
||||
// ? "http://localhost:3110"
|
||||
// : "https://api.satonomics.xyz"
|
||||
"https://api.satonomics.xyz"
|
||||
}${path}`;
|
||||
|
||||
const fetchedJSONs = new Array(
|
||||
(new Date().getFullYear() - new Date("2009-01-01").getFullYear() + 2) *
|
||||
(scale === "date" ? 1 : 6),
|
||||
@@ -51,12 +51,12 @@ export function createResourceDataset<
|
||||
vec: createMemo(() => {
|
||||
const map = json()?.dataset.map || null;
|
||||
|
||||
const chunkId = json()?.chunk.id!;
|
||||
|
||||
if (!map) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const chunkId = json()?.chunk.id!;
|
||||
|
||||
if (Array.isArray(map)) {
|
||||
return map.map(
|
||||
(value, index) =>
|
||||
|
||||
@@ -40,6 +40,9 @@ export function createChart(scale: ResourceScale) {
|
||||
shiftVisibleRangeOnNewBar: false,
|
||||
allowShiftVisibleRangeOnWhitespaceReplacement: false,
|
||||
},
|
||||
handleScale: {
|
||||
axisDoubleClickReset: false,
|
||||
},
|
||||
crosshair: {
|
||||
mode: CrosshairMode.Normal,
|
||||
horzLine: {
|
||||
|
||||
@@ -34,9 +34,9 @@ export const applyPriceSeries = <
|
||||
const id = options?.id || "price";
|
||||
const title = options?.title || "Price";
|
||||
|
||||
const dataset = createMemo(() => _dataset || datasets[preset.scale].price);
|
||||
const dataset = _dataset || datasets[preset.scale].price;
|
||||
|
||||
const url = "url" in dataset() ? (dataset() as any).url : undefined;
|
||||
const url = "url" in dataset ? (dataset as any).url : undefined;
|
||||
|
||||
const priceScaleOptions: DeepPartial<PriceScaleOptions> = {
|
||||
...(options?.halved
|
||||
@@ -51,7 +51,6 @@ export const applyPriceSeries = <
|
||||
? {}
|
||||
: {
|
||||
mode: 1,
|
||||
// mode: PriceScaleMode.Logarithmic,
|
||||
}),
|
||||
...options?.priceScaleOptions,
|
||||
};
|
||||
@@ -139,9 +138,12 @@ export const applyPriceSeries = <
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const d = dataset();
|
||||
lineSeries.setData(d.values());
|
||||
ohlcSeries.setData(d.values());
|
||||
const values = dataset.values();
|
||||
|
||||
if (values) {
|
||||
lineSeries.setData(values);
|
||||
ohlcSeries.setData(values);
|
||||
}
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export function tick() {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 0);
|
||||
});
|
||||
}
|
||||
@@ -21,6 +21,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
/* Foreground, Background */
|
||||
scrollbar-color: #ffffff66 #00000066;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-orange-300 hover:underline;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 515 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 564 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 592 KiB |
Reference in New Issue
Block a user