Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 17 additions & 46 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,49 @@
import { useCallback } from "react";
import { memo } from "react";
import { Route, MemoryRouter as Router, Routes } from "react-router-dom";
import "./App.css";
import { ChooseAction, ChoosePlatform, Download, License, Progress } from "./steps";
import logo from "../../assets/logo.png";
import { useEffect, useState } from "react";
import { DiscordPlatform, PlatformData } from "./types";
import { getPlatforms } from "./util";

function Header(): React.ReactElement {
return (
<div className="header">
const Header = memo(function Header() {
return <div className="header">
<img src={logo} alt="Replugged logo" className="header-logo" />
<span className="header-name">Replugged Installer</span>
</div>
);
}

export default function App(): React.ReactElement {
</div>;
});
export default memo(function App() {
const [action, setAction] = useState<"plug" | "unplug">("plug");
const [platforms, setPlatforms] = useState<DiscordPlatform[]>([]);
const [platformData, setPlatformData] = useState<Record<DiscordPlatform, PlatformData> | null>(
null,
);

const init = async (reset = false): Promise<void> => {
const [platformData, setPlatformData] = useState<Record<DiscordPlatform, PlatformData> | null>(null);
const init = useCallback(async (reset = false): Promise<void> => {
if (reset) {
setAction("plug");
setPlatforms([]);
setPlatformData(null);
}

const data = await getPlatforms();
setPlatformData(data);

const unplugged = Object.entries(data)
.filter(([, value]) => value.installed && !value.plugged)
.map(([key]) => key as DiscordPlatform);

const unplugged = Object.entries(data).filter(([, value]) => value.installed && !value.plugged).map(([key]) => (key as DiscordPlatform));
setPlatforms(unplugged);
};

}, [setAction, setPlatforms, setPlatformData]);
useEffect(() => {
window.electron.ipcRenderer.on("ERROR", (event) => {
window.electron.ipcRenderer.on("ERROR", event => {
console.error(event);
});

void init();
}, []);

return (
<Router>
return <Router>
<Header />
<div className="wrapper">
<Routes>
<Route path="/" element={<License />} />
<Route path="/download" element={<Download />} />
<Route path="/action" element={<ChooseAction action={action} setAction={setAction} />} />
<Route
path="/platform"
element={
<ChoosePlatform
platformData={platformData}
action={action}
platforms={platforms}
setPlatforms={setPlatforms}
init={init}
/>
}
/>
<Route
path="/progress"
element={<Progress action={action} platforms={platforms} init={init} />}
/>
<Route path="/platform" element={<ChoosePlatform platformData={platformData} action={action} platforms={platforms} setPlatforms={setPlatforms} init={init} />} />
<Route path="/progress" element={<Progress action={action} platforms={platforms} init={init} />} />
</Routes>
</div>
</Router>
);
}
</Router>;
});
42 changes: 11 additions & 31 deletions src/renderer/steps/ChooseAction.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,23 @@
import { memo } from "react";
import "./ChooseAction.css";
import "../App.css";
import { Link } from "react-router-dom";

export function ChooseAction({
export const ChooseAction = memo(function ChooseAction({
action,
setAction,
setAction
}: {
action: "plug" | "unplug";
setAction: (action: "plug" | "unplug") => void;
}): React.ReactElement {
return (
<div className="page action-page">
}) {
return <div className="page action-page">
<div className="action-page-header">Choose Action</div>
<div className="choose-action">
<label
htmlFor="radio-plug"
className={`button action-button ${action === "plug" ? "action-button-active" : ""}`}>
<input
id="radio-plug"
type="radio"
name="action"
value="plug"
checked={action === "plug"}
onChange={() => setAction("plug")}
/>
<label htmlFor="radio-plug" className={`button action-button ${action === "plug" ? "action-button-active" : ""}`}>
<input id="radio-plug" type="radio" name="action" value="plug" checked={action === "plug"} onChange={() => setAction("plug")} />
<span>Plug</span>
</label>
<label
htmlFor="radio-unplug"
className={`button action-button ${action === "unplug" ? "action-button-active" : ""}`}>
<input
id="radio-unplug"
type="radio"
name="action"
value="unplug"
checked={action === "unplug"}
onChange={() => setAction("unplug")}
/>
<label htmlFor="radio-unplug" className={`button action-button ${action === "unplug" ? "action-button-active" : ""}`}>
<input id="radio-unplug" type="radio" name="action" value="unplug" checked={action === "unplug"} onChange={() => setAction("unplug")} />
<span>Unplug</span>
</label>
</div>
Expand All @@ -45,6 +26,5 @@ export function ChooseAction({
Continue
</Link>
</div>
</div>
);
}
</div>;
});
111 changes: 30 additions & 81 deletions src/renderer/steps/ChoosePlatform.tsx
Original file line number Diff line number Diff line change
@@ -1,129 +1,78 @@
import { memo } from "react";
import { Link } from "react-router-dom";
import { DiscordPlatform, PlatformData } from "../types";
import "./ChoosePlatform.css";
import "../App.css";
import { PLATFORM_LABELS } from "../util";
import { useState } from "react";

export function ChoosePlatform({
export const ChoosePlatform = memo(function ChoosePlatform({
platformData,
action,
platforms,
setPlatforms,
init,
init
}: {
platformData: Record<DiscordPlatform, PlatformData> | null;
action: "plug" | "unplug";
platforms: DiscordPlatform[];
setPlatforms: (platforms: DiscordPlatform[]) => void;
init: (reset?: boolean) => Promise<void>;
}): React.ReactElement {
}) {
const [isFetchingPlatforms, setIsFetchingPlatforms] = useState(false);

const availablePlatforms = (
platformData
? Object.entries(platformData)
.filter(([, v]) => v.installed)
.map((x) => x[0])
: []
) as DiscordPlatform[];

const pluggedPlatforms = (
platformData
? Object.entries(platformData)
.filter(([, v]) => v.plugged)
.map((x) => x[0])
: []
) as DiscordPlatform[];

const hasAlreadyPluggedPlatform = platforms.some((platform) =>
pluggedPlatforms.includes(platform),
);

const availablePlatforms = ((platformData ? Object.entries(platformData).filter(([, v]) => v.installed).map(x => x[0]) : []) as DiscordPlatform[]);
const pluggedPlatforms = ((platformData ? Object.entries(platformData).filter(([, v]) => v.plugged).map(x => x[0]) : []) as DiscordPlatform[]);
const hasAlreadyPluggedPlatform = platforms.some(platform => pluggedPlatforms.includes(platform));
const togglePlatform = (platform: DiscordPlatform): void => {
if (platforms.includes(platform)) {
setPlatforms(platforms.filter((p) => p !== platform));
setPlatforms(platforms.filter(p => p !== platform));
} else {
setPlatforms([...platforms, platform]);
}
};

return (
<div className="page platform-page">
return <div className="page platform-page">
<div className="platform-page-header">Choose Platforms</div>
{availablePlatforms.length ? (
<>
{availablePlatforms.length ? <>
<div className="choose-platform">
{availablePlatforms.map((platform) => (
<label
htmlFor={`checkbox-${platform}`}
key={platform}
className={`button platform-button ${
platforms.includes(platform) ? "platform-button-active" : ""
}`}>
<input
id={`checkbox-${platform}`}
type="checkbox"
name="platform"
value={platform}
checked={platforms.includes(platform)}
onChange={() => togglePlatform(platform)}
/>
{availablePlatforms.map(platform => <label htmlFor={`checkbox-${platform}`} key={platform} className={`button platform-button ${platforms.includes(platform) ? "platform-button-active" : ""}`}>
<input id={`checkbox-${platform}`} type="checkbox" name="platform" value={platform} checked={platforms.includes(platform)} onChange={() => togglePlatform(platform)} />
<span>{PLATFORM_LABELS[platform]}</span>
</label>
))}
</label>)}
</div>
<div className="platform-note">Please quit Discord before continuing.</div>
{action === "plug" && hasAlreadyPluggedPlatform && (
<div className="platform-warning">
{action === "plug" && hasAlreadyPluggedPlatform && <div className="platform-warning">
One of the selected platforms is already plugged or has another client mod installed.
Plugging it again will overwrite the existing mod.
</div>
)}
</div>}
<div className="platform-bottom">
<Link to="/action" className="button button-secondary">
Back
</Link>
<Link
onClick={(e) => {
if (platforms.length === 0) e.preventDefault();
}}
to="/progress"
className={`button ${platforms.length === 0 ? "button-disabled" : ""}`}>
<Link onClick={e => {
if (platforms.length === 0) e.preventDefault();
}} to="/progress" className={`button ${platforms.length === 0 ? "button-disabled" : ""}`}>
Continue
</Link>
</div>
</>
) : (
<>
</> : <>
<div className="platform-note platform-troubleshooting">
{isFetchingPlatforms ? (
<>Please wait...</>
) : (
<>
{isFetchingPlatforms ? <>Please wait...</> : <>
No platforms available. Please{" "}
<a target="_blank" href="https://discord.com/download">
install Discord
</a>{" "}
and try again.
</>
)}
</>}
</div>
<div className="platform-bottom">
<button
onClick={async () => {
if (isFetchingPlatforms) return;
setIsFetchingPlatforms(true);
await Promise.all([init, new Promise((resolve) => setTimeout(resolve, 1000))]);
setIsFetchingPlatforms(false);
}}
className="button"
disabled={isFetchingPlatforms}>
<button onClick={async () => {
if (isFetchingPlatforms) return;
setIsFetchingPlatforms(true);
await Promise.all([init, new Promise(resolve => setTimeout(resolve, 1000))]);
setIsFetchingPlatforms(false);
}} className="button" disabled={isFetchingPlatforms}>
Try again
</button>
</div>
</>
)}
</div>
);
}
</>}
</div>;
});
Loading