mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
migrate to soapstone
This commit is contained in:
1 parent
e5b44d4eb8
commit
127c55ec93
25 files changed
+61
-188
No files matched your search
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"name": "varuna",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"dependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"immer": "^10.1.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react": "^19.1.1"
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import { produce } from "immer";
|
||||
import { debounce, merge } from "lodash-es";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export class Varuna<Type, Arguments = void> {
|
||||
private listeners = new Set<(state: Type) => void>();
|
||||
|
||||
private initialized = false;
|
||||
private _initial?: Type;
|
||||
private _state?: Type;
|
||||
|
||||
constructor(
|
||||
private creator: Type | ((...args: Arguments[]) => Type),
|
||||
private persistence?: string
|
||||
) {
|
||||
if (typeof creator !== "function") this.initialize(creator);
|
||||
}
|
||||
|
||||
private initialize(initial: Type) {
|
||||
let data = initial;
|
||||
|
||||
if (this.persistence && typeof localStorage !== "undefined") {
|
||||
const dehydrated = localStorage.getItem(this.persistence);
|
||||
|
||||
if (dehydrated) {
|
||||
const rehydrated = JSON.parse(dehydrated) as unknown;
|
||||
data = merge(data, rehydrated);
|
||||
}
|
||||
|
||||
window.addEventListener("beforeunload", this.store.bind(this));
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
this._initial = data;
|
||||
this._state = data;
|
||||
}
|
||||
|
||||
useInitialization(...args: Arguments[]) {
|
||||
const initializedThisMount = useRef(false);
|
||||
|
||||
if (initializedThisMount.current) return;
|
||||
|
||||
if (typeof this.creator !== "function") {
|
||||
throw new Error("Provider must be used with a function creator");
|
||||
}
|
||||
|
||||
this.initialize((this.creator as (...args: Arguments[]) => Type)(...args));
|
||||
initializedThisMount.current = true;
|
||||
}
|
||||
|
||||
private dispatch() {
|
||||
const state = this.state;
|
||||
this.listeners.forEach((callback) => callback(state));
|
||||
}
|
||||
|
||||
on<Slice>(slicer: (state: Type) => Slice, callback: (state: Slice) => void) {
|
||||
let last = slicer(this.state);
|
||||
|
||||
function listener(state: Type) {
|
||||
const next = slicer(state);
|
||||
|
||||
if (next !== last) {
|
||||
callback(next);
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
|
||||
this.listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
mutate(mutator: (state: Type) => void) {
|
||||
this.set(produce(this.state, mutator));
|
||||
}
|
||||
|
||||
private assertInitialized() {
|
||||
if (!this.initialized) {
|
||||
throw new Error("Varuna store used before initialization");
|
||||
}
|
||||
}
|
||||
|
||||
get state() {
|
||||
this.assertInitialized();
|
||||
return this._state as Type;
|
||||
}
|
||||
|
||||
set(state: Type) {
|
||||
this.assertInitialized();
|
||||
this._state = state;
|
||||
this.dispatch();
|
||||
this.storeDeferred();
|
||||
}
|
||||
|
||||
get initial() {
|
||||
this.assertInitialized();
|
||||
return this._initial as Type;
|
||||
}
|
||||
|
||||
use<Slice = Type>(
|
||||
slicer: (state: Type) => Slice = (state) => state as unknown as Slice
|
||||
) {
|
||||
return this.useDeferred(slicer, slicer(this.state));
|
||||
}
|
||||
|
||||
useDeferred<Slice = Type>(slicer: (state: Type) => Slice, initial: Slice) {
|
||||
const instance = this;
|
||||
const [slice, setSlice] = useState(initial);
|
||||
|
||||
useEffect(() => {
|
||||
setSlice(slicer(instance.state));
|
||||
return instance.on(slicer, setSlice);
|
||||
}, []);
|
||||
|
||||
return slice;
|
||||
}
|
||||
|
||||
store() {
|
||||
localStorage.setItem(this.persistence!, JSON.stringify(this.state));
|
||||
}
|
||||
|
||||
storeDeferred = debounce(this.store.bind(this), 500);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -45,12 +45,12 @@
|
||||
"react-dom": "^19.2.4",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"soapstone": "^1.0.0",
|
||||
"three": "^0.180.0",
|
||||
"three-stdlib": "^2.36.0",
|
||||
"transliteration": "^2.6.1",
|
||||
"typescript": "^5.9.3",
|
||||
"urlon": "^3.1.0",
|
||||
"varuna": "workspace:*",
|
||||
"vite": "^7.1.9"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export interface WargamingLogin {
|
||||
token: string;
|
||||
@@ -14,7 +14,7 @@ export interface App {
|
||||
};
|
||||
}
|
||||
|
||||
export const App = new Varuna<App>(
|
||||
export const App = new Soapstone<App>(
|
||||
{
|
||||
developerMode: false,
|
||||
policiesAgreementIndex: -1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
import type { DuelMember } from "./duel";
|
||||
|
||||
export interface CompareMember extends DuelMember {
|
||||
@@ -15,9 +15,9 @@ export interface CompareEphemeral {
|
||||
};
|
||||
}
|
||||
|
||||
export const CompareEphemeral = new Varuna<
|
||||
export const CompareEphemeral = new Soapstone<
|
||||
CompareEphemeral,
|
||||
Record<string, number>
|
||||
[Record<string, number>]
|
||||
>((crewSkills) => ({
|
||||
crewSkills,
|
||||
equalize: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export type DeltaMode = "none" | "percentage" | "absolute";
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface ComparePersistent {
|
||||
deltaMode: DeltaMode;
|
||||
}
|
||||
|
||||
export const ComparePersistent = new Varuna<ComparePersistent>(
|
||||
export const ComparePersistent = new Soapstone<ComparePersistent>(
|
||||
{ deltaMode: "none" },
|
||||
"compare-2"
|
||||
);
|
||||
@@ -1,9 +1,9 @@
|
||||
import { type IndividualTankStats } from "@blitzkit/core";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
type EmbedBreakdown = Record<number, IndividualTankStats[]>;
|
||||
|
||||
export const EmbedBreakdown = new Varuna<EmbedBreakdown>(
|
||||
export const EmbedBreakdown = new Soapstone<EmbedBreakdown>(
|
||||
{},
|
||||
"embed-breakdown-2"
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as radixColors from "@radix-ui/colors";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
import {
|
||||
type EmbedConfigItemType,
|
||||
EmbedItemType,
|
||||
@@ -103,6 +103,6 @@ export type EmbedConfigItem = (
|
||||
|
||||
export type EmbedStateStore = Record<string, EmbedConfigItem["default"]>;
|
||||
|
||||
export const EmbedState = new Varuna<EmbedStateStore, EmbedStateStore>(
|
||||
export const EmbedState = new Soapstone<EmbedStateStore, [EmbedStateStore]>(
|
||||
(data) => data
|
||||
);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
interface Gallery {
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export const Gallery = new Varuna<Gallery>({});
|
||||
export const Gallery = new Soapstone<Gallery>({});
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { TankDefinition } from "@blitzkit/core";
|
||||
import { times } from "lodash-es";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export enum GuessState {
|
||||
Correct,
|
||||
@@ -18,7 +18,7 @@ export interface Guess {
|
||||
tiers: number[];
|
||||
}
|
||||
|
||||
export const Guess = new Varuna<Guess, TankDefinition>((tank) => ({
|
||||
export const Guess = new Soapstone<Guess, [TankDefinition]>((tank) => ({
|
||||
tank,
|
||||
guessState: GuessState.NotGuessed,
|
||||
totalGuesses: 0,
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
TankDefinition,
|
||||
TurretDefinition,
|
||||
} from "@blitzkit/core";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export interface Mixer {
|
||||
hull: TankDefinition;
|
||||
@@ -18,4 +18,4 @@ export interface Mixer {
|
||||
};
|
||||
}
|
||||
|
||||
export const Mixer = new Varuna<Mixer, Mixer>((data) => data);
|
||||
export const Mixer = new Soapstone<Mixer, [Mixer]>((data) => data);
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Samples } from "@blitzkit/core";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export interface Performance {
|
||||
playerCountPeriod: PlayerCountPeriod;
|
||||
@@ -7,6 +7,6 @@ export interface Performance {
|
||||
|
||||
export type PlayerCountPeriod = keyof Samples;
|
||||
|
||||
export const Performance = new Varuna<Performance>({
|
||||
export const Performance = new Soapstone<Performance>({
|
||||
playerCountPeriod: "total",
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export interface PlaylistEntry {
|
||||
id: number;
|
||||
@@ -11,4 +11,4 @@ export interface Playlist {
|
||||
list?: PlaylistEntry[];
|
||||
}
|
||||
|
||||
export const Playlist = new Varuna<Playlist>({}, "playlist");
|
||||
export const Playlist = new Soapstone<Playlist>({}, "playlist");
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
IndividualTankStats,
|
||||
Region,
|
||||
} from "@blitzkit/core";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
interface SessionBase {
|
||||
columns: CompositeStatsKey[];
|
||||
@@ -25,7 +25,7 @@ interface SessionNotTracking extends SessionBase {
|
||||
|
||||
type Session = SessionTracking | SessionNotTracking;
|
||||
|
||||
export const Session = new Varuna<Session>(
|
||||
export const Session = new Soapstone<Session>(
|
||||
{
|
||||
columns: [
|
||||
"cumulative_battles",
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
TankClass,
|
||||
TankType,
|
||||
} from "@blitzkit/protos";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export interface TankFilters {
|
||||
tiers: number[];
|
||||
@@ -26,7 +26,7 @@ export interface TankFilters {
|
||||
showUnowned: boolean;
|
||||
}
|
||||
|
||||
export const TankFilters = new Varuna<TankFilters>({
|
||||
export const TankFilters = new Soapstone<TankFilters>({
|
||||
tiers: [],
|
||||
nations: [],
|
||||
classes: [],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type en from "@blitzkit/i18n/strings/en.json";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export type TankPerformanceSortType =
|
||||
keyof typeof en.website.tools.performance.table.stats;
|
||||
@@ -9,7 +9,7 @@ export interface TankPerformanceSort {
|
||||
direction: -1 | 1;
|
||||
}
|
||||
|
||||
export const TankPerformanceSort = new Varuna<TankPerformanceSort>({
|
||||
export const TankPerformanceSort = new Soapstone<TankPerformanceSort>({
|
||||
type: "winrate",
|
||||
direction: -1,
|
||||
});
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
TankDefinition,
|
||||
} from "@blitzkit/protos";
|
||||
import type { Vector3 } from "three";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
import { api } from "../api/dynamic";
|
||||
import type { ArmorType } from "../components/SpacedArmorScene";
|
||||
import type { ExternalModuleVariant } from "../components/SpacedArmorSceneComponent";
|
||||
@@ -117,7 +117,7 @@ interface Tankopedia {
|
||||
const skills = await api.skills();
|
||||
const models = await api.models();
|
||||
|
||||
export const Tankopedia = new Varuna<Tankopedia, TankDefinition>((tank) => ({
|
||||
export const Tankopedia = new Soapstone<Tankopedia, [TankDefinition]>((tank) => ({
|
||||
disturbed: false,
|
||||
revealed: false,
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type en from "@blitzkit/i18n/strings/en.json";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
|
||||
export type TankopediaSortBy = keyof typeof en.website.common.tank_search.sort;
|
||||
export type TankopediaSortDirection = "ascending" | "descending";
|
||||
@@ -23,7 +23,7 @@ export interface TankopediaPersistent {
|
||||
};
|
||||
}
|
||||
|
||||
export const TankopediaPersistent = new Varuna<TankopediaPersistent>(
|
||||
export const TankopediaPersistent = new Soapstone<TankopediaPersistent>(
|
||||
{
|
||||
hideTankModelUnderArmor: false,
|
||||
wireframe: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Varuna } from 'varuna';
|
||||
import { Soapstone } from 'soapstone';
|
||||
import {
|
||||
type TankopediaSortBy,
|
||||
type TankopediaSortDirection,
|
||||
@@ -9,7 +9,7 @@ export interface TankSort {
|
||||
direction: TankopediaSortDirection;
|
||||
}
|
||||
|
||||
export const TankSort = new Varuna<TankSort>({
|
||||
export const TankSort = new Soapstone<TankSort>({
|
||||
by: 'meta.none',
|
||||
direction: 'descending',
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import { enableMapSet } from "immer";
|
||||
import { Varuna } from "varuna";
|
||||
import { Soapstone } from "soapstone";
|
||||
import { tierListRows } from "../components/TierList/Table/constants";
|
||||
|
||||
export interface TierList {
|
||||
@@ -8,7 +8,7 @@ export interface TierList {
|
||||
placedTanks: Set<number>;
|
||||
}
|
||||
|
||||
export const TierList = new Varuna<TierList>({
|
||||
export const TierList = new Soapstone<TierList>({
|
||||
dragging: false,
|
||||
rows: tierListRows.map((row) => ({ name: row.name, tanks: [] })),
|
||||
placedTanks: new Set(),
|
||||
|
||||
Reference in new issue
Block a user