mirror of
https://github.com/ow-mods/ow-mod-man.git
synced 2025-12-11 20:15:50 +01:00
Virtualize remote mods
This commit is contained in:
parent
26324a1dbd
commit
827cee3f0b
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -38,17 +38,6 @@ version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "async-recursion"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atk"
|
||||
version = "0.15.1"
|
||||
@ -1918,7 +1907,6 @@ name = "owmods_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
"directories",
|
||||
"futures",
|
||||
"glob",
|
||||
|
||||
@ -17,7 +17,6 @@ directories = "4.0.1"
|
||||
reqwest = { version = "0.11.14", features = ["blocking"] }
|
||||
glob = "0.3.1"
|
||||
version-compare = "0.1.1"
|
||||
async-recursion = "1.0.2"
|
||||
anyhow = "1.0.68"
|
||||
zip = { version = "0.6.3", default-features = false, features = ["deflate", "zstd"] }
|
||||
tempdir = "0.3.7"
|
||||
|
||||
@ -68,8 +68,8 @@ pub fn get_remote_mods(state: tauri::State<'_, State>) -> Vec<String> {
|
||||
let db = state.remote_db.read().unwrap();
|
||||
let mut mods: Vec<&RemoteMod> = db.mods.values().collect();
|
||||
mods.sort_by(|a, b| b.download_count.cmp(&a.download_count));
|
||||
mods
|
||||
.into_iter().map(|m| m.unique_name.clone())
|
||||
mods.into_iter()
|
||||
.map(|m| m.unique_name.clone())
|
||||
.filter(|m| m != "Alek.OWML")
|
||||
.collect::<Vec<String>>()
|
||||
}
|
||||
|
||||
@ -19,3 +19,4 @@ plugins:
|
||||
- "@typescript-eslint"
|
||||
rules:
|
||||
"react/display-name": off
|
||||
"@typescript-eslint/no-non-null-assertion": off
|
||||
|
||||
@ -11,11 +11,11 @@ interface DownloadPayload {
|
||||
}
|
||||
|
||||
const ActiveDownload = (props: ActiveDownloadProps) => {
|
||||
// Temp state for rn, will use useSyncExternalStore later.
|
||||
const [progress, setProgress] = useState<DownloadPayload>({
|
||||
// Temp state for rn, will use tauri later.
|
||||
const progress = useState<DownloadPayload>({
|
||||
id: props.id,
|
||||
message: "Downloading xen.NewHorizons"
|
||||
});
|
||||
})[0];
|
||||
|
||||
return (
|
||||
<div className="downloads-row">
|
||||
|
||||
@ -2,11 +2,37 @@ import Icon from "@components/Icon";
|
||||
import ModActionButton from "@components/mods/ModActionButton";
|
||||
import ModHeader from "@components/mods/ModHeader";
|
||||
import { useTauri } from "@hooks";
|
||||
import { memo } from "react";
|
||||
import { CSSProperties, memo } from "react";
|
||||
import { FaArrowDown, FaGlobe } from "react-icons/fa";
|
||||
import { RemoteMod } from "src/types";
|
||||
|
||||
const RemoteModRow = memo((props: { uniqueName: string }) => {
|
||||
// Stolen from mods website, Rai will never catch me!
|
||||
const magnitudeMap = [
|
||||
{ value: 1, symbol: "" },
|
||||
{ value: 1e3, symbol: "k" },
|
||||
{ value: 1e6, symbol: "M" },
|
||||
{ value: 1e9, symbol: "G" },
|
||||
{ value: 1e12, symbol: "T" },
|
||||
{ value: 1e15, symbol: "P" },
|
||||
{ value: 1e18, symbol: "E" }
|
||||
];
|
||||
|
||||
const numberFormatRegex = /\.0+$|(\.[0-9]*[1-9])0+$/;
|
||||
|
||||
export const formatNumber = (value: number, digits = 1) => {
|
||||
const magnitude = magnitudeMap
|
||||
.slice()
|
||||
.reverse()
|
||||
.find((item) => {
|
||||
return value >= item.value;
|
||||
});
|
||||
return magnitude
|
||||
? (value / magnitude.value).toFixed(digits).replace(numberFormatRegex, "$1") +
|
||||
magnitude.symbol
|
||||
: "0";
|
||||
};
|
||||
|
||||
const RemoteModRow = memo((props: { uniqueName: string; style?: CSSProperties }) => {
|
||||
const [status, mod, err] = useTauri<RemoteMod, { uniqueName: string }>(
|
||||
"REMOTE-REFRESH",
|
||||
"get_remote_mod",
|
||||
@ -14,15 +40,17 @@ const RemoteModRow = memo((props: { uniqueName: string }) => {
|
||||
);
|
||||
|
||||
if (status === "Loading") {
|
||||
return <p>Loading...</p>;
|
||||
return <div className="mod-row center-loading" aria-busy style={props.style}></div>;
|
||||
} else if (status === "Error") {
|
||||
return <p>{err}</p>;
|
||||
return <p style={props.style}>{err}</p>;
|
||||
} else {
|
||||
const remote_mod = mod!;
|
||||
let desc = remote_mod.description ?? "No Description Provided";
|
||||
if (desc.trim() === "") desc = "No Description Provided";
|
||||
return (
|
||||
<details>
|
||||
<ModHeader {...remote_mod}>
|
||||
<small>{remote_mod.downloadCount}</small>
|
||||
<div style={props.style} className="mod-row">
|
||||
<ModHeader {...remote_mod} author={remote_mod.authorDisplay ?? remote_mod.author}>
|
||||
<small>{formatNumber(remote_mod.downloadCount)}</small>
|
||||
<ModActionButton ariaLabel="Install With Dependencies">
|
||||
<Icon iconType={FaArrowDown} />
|
||||
</ModActionButton>
|
||||
@ -30,8 +58,8 @@ const RemoteModRow = memo((props: { uniqueName: string }) => {
|
||||
<Icon iconType={FaGlobe} />
|
||||
</ModActionButton>
|
||||
</ModHeader>
|
||||
<small>{remote_mod.description}</small>
|
||||
</details>
|
||||
<small className="mod-description">{desc}</small>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,23 +1,36 @@
|
||||
import { useTauri } from "@hooks";
|
||||
import { memo } from "react";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import { FixedSizeList } from "react-window";
|
||||
import RemoteModRow from "./RemoteModRow";
|
||||
|
||||
const RemoteMods = () => {
|
||||
const RemoteMods = memo(() => {
|
||||
const [status, mods, err] = useTauri<string[], undefined>("REMOTE-REFRESH", "get_remote_mods");
|
||||
|
||||
if (status === "Loading") {
|
||||
return <p>Loading...</p>;
|
||||
return <div className="mod-list center-loading" aria-busy></div>;
|
||||
} else if (status === "Error") {
|
||||
return <p>{err}</p>;
|
||||
} else {
|
||||
const remote_mods = mods!;
|
||||
return (
|
||||
<div className="mod-list">
|
||||
{remote_mods.map((m) => (
|
||||
<RemoteModRow key={m} uniqueName={m} />
|
||||
))}
|
||||
</div>
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<FixedSizeList
|
||||
itemCount={remote_mods.length}
|
||||
itemSize={120}
|
||||
width={width}
|
||||
height={height}
|
||||
className="mod-list"
|
||||
>
|
||||
{({ index, style }) => (
|
||||
<RemoteModRow style={style} uniqueName={remote_mods[index]} />
|
||||
)}
|
||||
</FixedSizeList>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export default RemoteMods;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { FaPlay, FaQuestion, FaArrowDown, FaCog, FaInfoCircle } from "react-icons/fa";
|
||||
import { FaPlay, FaQuestion, FaCog, FaInfoCircle } from "react-icons/fa";
|
||||
import { TbRefresh } from "react-icons/tb";
|
||||
import { RiInstallFill } from "react-icons/ri";
|
||||
import { RxActivityLog } from "react-icons/rx";
|
||||
@ -11,8 +11,6 @@ import { useRef } from "react";
|
||||
import SettingsModal from "@components/modals/SettingsModal";
|
||||
import InstallFromModal from "@components/modals/InstallFromModal";
|
||||
import AboutModal from "@components/modals/AboutModal";
|
||||
import DownloadsBadge from "./DownloadsBadge";
|
||||
import DownloadsPopout from "./DownloadsPopout";
|
||||
import Downloads from "../downloads/Downloads";
|
||||
|
||||
const Nav = () => {
|
||||
|
||||
@ -26,12 +26,9 @@ export const useTauri = <T, P>(
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let u = () => {};
|
||||
if (status !== "Loading") {
|
||||
console.debug(`Begin subscribe to ${eventName}`);
|
||||
subscribeTauri(eventName)(() => setStatus("Loading")).then((unsubscribe) => {
|
||||
u = unsubscribe;
|
||||
});
|
||||
subscribeTauri(eventName)(() => setStatus("Loading"));
|
||||
} else {
|
||||
console.debug(`Invoking ${commandName} with args ${commandPayload ?? "null"}`);
|
||||
getTauriSnapshot(commandName, commandPayload)()
|
||||
@ -44,7 +41,6 @@ export const useTauri = <T, P>(
|
||||
setStatus("Error");
|
||||
});
|
||||
}
|
||||
return u;
|
||||
}, [status]);
|
||||
|
||||
return [status, data, error];
|
||||
|
||||
@ -2,6 +2,17 @@
|
||||
margin-top: $margin-lg;
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
$row-border: 1px solid $accent-bg;
|
||||
|
||||
.mod-row {
|
||||
border-bottom: $row-border;
|
||||
padding: $margin;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mod-header {
|
||||
@ -41,3 +52,24 @@
|
||||
justify-self: right;
|
||||
column-gap: $margin;
|
||||
}
|
||||
|
||||
.mod-actions svg {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mod-authors {
|
||||
color: $primary-muted;
|
||||
}
|
||||
|
||||
.mod-description {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding-right: 6.5em;
|
||||
}
|
||||
|
||||
.center-loading {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user