From 001a50e7629ff644501e30af1da4a131855a7911 Mon Sep 17 00:00:00 2001 From: Zack Rauen Date: Wed, 8 Mar 2023 20:09:10 -0500 Subject: [PATCH] Convert various remaining components --- renderer/src/ui/addonerrormodal.jsx | 142 +++++++++---------------- renderer/src/ui/publicservers/card.jsx | 114 +++++++++----------- renderer/src/ui/updater.jsx | 132 ++++++++++------------- 3 files changed, 163 insertions(+), 225 deletions(-) diff --git a/renderer/src/ui/addonerrormodal.jsx b/renderer/src/ui/addonerrormodal.jsx index 4e0fbe04..fdc8c120 100644 --- a/renderer/src/ui/addonerrormodal.jsx +++ b/renderer/src/ui/addonerrormodal.jsx @@ -3,24 +3,19 @@ import Extension from "./icons/extension"; import ThemeIcon from "./icons/theme"; import Divider from "./divider"; + const Parser = Object(WebpackModules.getByProps("defaultRules", "parse")).defaultRules; +const {useState, useCallback, useMemo} = React; const joinClassNames = (...classNames) => classNames.filter(e => e).join(" "); -class AddonError extends React.Component { - constructor(props) { - super(props); +function AddonError({err, index}) { + const [expanded, setExpanded] = useState(false); + const toggle = useCallback(() => setExpanded(!expanded), [expanded]); - this.state = { - expanded: false - }; - } - toggle() { - this.setState({expanded: !this.state.expanded}); - } - renderErrorBody(err) { + function renderErrorBody() { const stack = err?.error?.stack ?? err.stack; - if (!this.state.expanded || !stack) return null; + if (!expanded || !stack) return null; return
@@ -28,92 +23,57 @@ class AddonError extends React.Component {
; } - render() { - const err = this.props.err; - return
-
{this.toggle();}} > -
- {err.type == "plugin" ? : } -
-
-

{err.name}

-
- - - -
{err.message}
-
-
- - - + + return
+
+
+ {err.type == "plugin" ? : }
- {this.renderErrorBody(err)} -
; - } +
+

{err.name}

+
+ + + +
{err.message}
+
+
+ + + +
+ {renderErrorBody(err)} +
; } -export default class AddonErrorModal extends React.Component { - constructor(props) { - super(props); - const tabs = this.getTabs(); - this.state = { - selectedTab: tabs[0].id, - }; - } +function generateTab(id, errors) { + return {id, errors, name: Strings.Panels[id]}; +} - mergeErrors(errors1 = [], errors2 = []) { - const list = []; - const allErrors = [...errors2, ...errors1]; - for (const error of allErrors) { - if (list.find(e => e.file === error.file)) continue; - list.push(error); - } - return list; - } +export default function AddonErrorModal({pluginErrors, themeErrors}) { + const tabs = useMemo(() => { + return [ + pluginErrors.length && generateTab("plugins", pluginErrors), + themeErrors.length && generateTab("themes", themeErrors) + ].filter(e => e); + }, [pluginErrors, themeErrors]); - refreshTabs(pluginErrors, themeErrors) { - this._tabs = null; - this.props.pluginErrors = this.mergeErrors(this.props.pluginErrors, pluginErrors); - this.props.themeErrors = this.mergeErrors(this.props.themeErrors, themeErrors); - this.forceUpdate(); - } + const [tabId, setTab] = useState(tabs[0].id); + const switchToTab = useCallback((id) => setTab(id), [tabId]); + const selectedTab = tabs.find(e => e.id === tabId); - generateTab(id, errors) { - return { - id: id, - name: Strings.Panels[id], - errors: errors - }; - } - - getTabs() { - return this._tabs || (this._tabs = [ - this.props.pluginErrors.length && this.generateTab("plugins", this.props.pluginErrors), - this.props.themeErrors.length && this.generateTab("themes", this.props.themeErrors) - ].filter(e => e)); - } - - switchToTab(id) { - this.setState({selectedTab: id}); - } - - render() { - const selectedTab = this.getTabs().find(e => this.state.selectedTab === e.id); - const tabs = this.getTabs(); - return <> -
-

{Strings.Modals.addonErrors}

-
- {tabs.map(tab =>
{this.switchToTab(tab.id);}} className={joinClassNames("bd-tab-item", tab.id === selectedTab.id && "selected")}>{tab.name}
)} -
+ return <> +
+

{Strings.Modals.addonErrors}

+
+ {tabs.map(tab =>
{switchToTab(tab.id);}} className={joinClassNames("bd-tab-item", tab.id === selectedTab.id && "selected")}>{tab.name}
)}
-
-
- {selectedTab.errors.map((error, index) => )} -
+
+
+
+ {selectedTab.errors.map((error, index) => )}
- ; - } +
+ ; } \ No newline at end of file diff --git a/renderer/src/ui/publicservers/card.jsx b/renderer/src/ui/publicservers/card.jsx index 824b83ae..909bffe5 100644 --- a/renderer/src/ui/publicservers/card.jsx +++ b/renderer/src/ui/publicservers/card.jsx @@ -1,70 +1,62 @@ import {React, Strings} from "modules"; -const badge =
- - - - -
- - - -
-
; +const {useState, useCallback, useMemo} = React; -export default class ServerCard extends React.Component { - constructor(props) { - super(props); - if (!this.props.server.iconUrl) this.props.server.iconUrl = this.props.defaultAvatar(); - this.state = { - joined: this.props.joined - }; - this.join = this.join.bind(this); - this.handleError = this.handleError.bind(this); - } - render() { - const {server} = this.props; - const addedDate = new Date(server.insertDate * 1000); // Convert from unix timestamp - const buttonText = typeof(this.state.joined) == "string" ? `${Strings.PublicServers.joining}...` : this.state.joined ? Strings.PublicServers.joined : Strings.PublicServers.join; - - return
-
-
- -
-
-
- {server.pinned && badge} -
{server.name}
- {this.state.joined &&
{buttonText}
} -
-
{server.description}
-
-
-
-
{server.members.toLocaleString()} Members
-
-
-
-
Added {addedDate.toLocaleDateString()}
-
-
+const badge =
+ + + +
+ + +
; - } - handleError() { - this.props.server.iconUrl = this.props.defaultAvatar(); - } - async join() { - if (this.state.joined) return this.props.navigateTo(this.props.server.identifier); - this.setState({joined: "joining"}); - const didJoin = await this.props.join(this.props.server.identifier, this.props.server.nativejoin); - this.setState({joined: didJoin}); - } +export default function ServerCard({server, joined, join, navigateTo, defaultAvatar}) { + const [isError, setError] = useState(false); + const handleError = useCallback(() => { + setError(true); + }, []); + + const [hasJoined, setJoined] = useState(joined); + const doJoin = useCallback(async () => { + if (hasJoined) return navigateTo(server.identifier); + setJoined("joining"); + const didJoin = await join(server.identifier, server.nativeJoin); + setJoined(didJoin); + }, [hasJoined]); + + const defaultIcon = useMemo(() => defaultAvatar(), []); + const currentIcon = !server.iconUrl || isError ? defaultIcon : server.iconUrl; + + const addedDate = new Date(server.insertDate * 1000); // Convert from unix timestamp + const buttonText = typeof(hasJoined) == "string" ? `${Strings.PublicServers.joining}...` : hasJoined ? Strings.PublicServers.joined : Strings.PublicServers.join; + + return
+
+
+ +
+
+
+ {server.pinned && badge} +
{server.name}
+ {hasJoined &&
{buttonText}
} +
+
{server.description}
+
+
+
+
{server.members.toLocaleString()} Members
+
+
+
+
Added {addedDate.toLocaleDateString()}
+
+
+
+
; } \ No newline at end of file diff --git a/renderer/src/ui/updater.jsx b/renderer/src/ui/updater.jsx index 367a256e..500e32ac 100644 --- a/renderer/src/ui/updater.jsx +++ b/renderer/src/ui/updater.jsx @@ -7,6 +7,8 @@ import Toasts from "./toasts"; import Checkmark from "./icons/check"; +const {useState, useCallback, useEffect} = React; + function CoreUpdaterPanel(props) { return @@ -37,88 +39,72 @@ function AddonUpdaterPanel(props) { ; } -export default class UpdaterPanel extends React.Component { - constructor(props) { - super(props); +export default function UpdaterPanel(props) { + const [hasCoreUpdate, setCoreUpdate] = useState(props.coreUpdater.hasUpdate); + const [updates, setUpdates] = useState({plugins: props.pluginUpdater.pending.slice(0), themes: props.themeUpdater.pending.slice(0)}); - this.state = { - hasCoreUpdate: this.props.coreUpdater.hasUpdate, - plugins: this.props.pluginUpdater.pending.slice(0), - themes: this.props.themeUpdater.pending.slice(0) - }; - - this.checkForUpdates = this.checkForUpdates.bind(this); - this.updateAddon = this.updateAddon.bind(this); - this.updateCore = this.updateCore.bind(this); - this.updateAllAddons = this.updateAllAddons.bind(this); - this.update = this.update.bind(this); - } - - update() { - this.checkAddons("plugins"); - this.checkAddons("themes"); - } - - componentDidMount() { - Events.on(`plugin-loaded`, this.update); - Events.on(`plugin-unloaded`, this.update); - Events.on(`theme-loaded`, this.update); - Events.on(`theme-unloaded`, this.update); - } - - componentWillUnmount() { - Events.off(`plugin-loaded`, this.update); - Events.off(`plugin-unloaded`, this.update); - Events.off(`theme-loaded`, this.update); - Events.off(`theme-unloaded`, this.update); - } - - async checkForUpdates() { - Toasts.info(Strings.Updater.checking); - await this.checkCoreUpdate(); - await this.checkAddons("plugins"); - await this.checkAddons("themes"); - Toasts.info(Strings.Updater.finishedChecking); - } - - async checkCoreUpdate() { - await this.props.coreUpdater.checkForUpdate(false); - this.setState({hasCoreUpdate: this.props.coreUpdater.hasUpdate}); - } - - async updateCore() { - await this.props.coreUpdater.update(); - this.setState({hasCoreUpdate: false}); - } - - async checkAddons(type) { - const updater = type === "plugins" ? this.props.pluginUpdater : this.props.themeUpdater; + const checkAddons = useCallback(async (type) => { + const updater = type === "plugins" ? props.pluginUpdater : props.themeUpdater; await updater.checkAll(false); - this.setState({[type]: updater.pending.slice(0)}); - } + setUpdates({...updates, [type]: updater.pending.slice(0)}); + }, []); - async updateAddon(type, filename) { - const updater = type === "plugins" ? this.props.pluginUpdater : this.props.themeUpdater; + const update = useCallback(() => { + checkAddons("plugins"); + checkAddons("themes"); + }, []); + + useEffect(() => { + Events.on(`plugin-loaded`, update); + Events.on(`plugin-unloaded`, update); + Events.on(`theme-loaded`, update); + Events.on(`theme-unloaded`, update); + return () => { + Events.off(`plugin-loaded`, update); + Events.off(`plugin-unloaded`, update); + Events.off(`theme-loaded`, update); + Events.off(`theme-unloaded`, update); + }; + }, []); + + const checkCoreUpdate = useCallback(async () => { + await props.coreUpdater.checkForUpdate(false); + setCoreUpdate(props.coreUpdater.hasUpdate); + }, []); + + const checkForUpdates = useCallback(async () => { + Toasts.info(Strings.Updater.checking); + await checkCoreUpdate(); + await checkAddons("plugins"); + await checkAddons("themes"); + Toasts.info(Strings.Updater.finishedChecking); + }); + + const updateCore = useCallback(async () => { + await props.coreUpdater.update(); + setCoreUpdate(false); + }, []); + + const updateAddon = useCallback(async (type, filename) => { + const updater = type === "plugins" ? props.pluginUpdater : props.themeUpdater; await updater.updateAddon(filename); - this.setState(prev => { + setUpdates(prev => { prev[type].splice(prev[type].indexOf(filename), 1); return prev; }); - } + }, []); - async updateAllAddons(type) { - const toUpdate = this.state[type].slice(0); + const updateAllAddons = useCallback(async (type) => { + const toUpdate = updates[type].slice(0); for (const filename of toUpdate) { - await this.updateAddon(type, filename); + await updateAddon(type, filename); } - } + }, []); - render() { - return [ - , - , - , - , - ]; - } + return [ + , + , + , + , + ]; } \ No newline at end of file