diff --git a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx index 0203124e8..51d925530 100644 --- a/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx +++ b/apps/cyberstorm-remix/app/commonComponents/Connection/Connection.tsx @@ -11,10 +11,12 @@ interface ConnectionProps { connection?: OAuthConnection; connectionLink: string; disconnectFunction: (data: userLinkedAccountDisconnectProviders) => void; + disabled: boolean; } export function Connection(props: ConnectionProps) { - const { connection, identifier, icon, name, connectionLink } = props; + const { connection, identifier, icon, name, connectionLink, disabled } = + props; return (
@@ -30,6 +32,7 @@ export function Connection(props: ConnectionProps) {
) : null} { if (connection) { diff --git a/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx b/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx index bdf9bea79..6b88d8b89 100644 --- a/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx +++ b/apps/cyberstorm-remix/app/settings/user/Connections/Connections.tsx @@ -1,26 +1,31 @@ +import { type ReactElement, useRef } from "react"; + +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"; + +import { useOutletContext, useRevalidator } from "react-router"; + import { NewLink, OverwolfLogo, useToast } from "@thunderstore/cyberstorm"; +import { ApiAction } from "@thunderstore/ts-api-react-actions"; +import { ApiError } from "@thunderstore/thunderstore-api"; -import { useOutletContext } from "react-router"; import { buildAuthLoginUrl } from "cyberstorm/utils/ThunderstoreAuth"; - -import { userLinkedAccountDisconnect } from "../../../../../../packages/thunderstore-api/src"; +import { getPublicEnvVariables } from "cyberstorm/security/publicEnvVariables"; import { Connection } from "~/commonComponents/Connection/Connection"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"; -import { type ReactElement } from "react"; -import { type OutletContextShape } from "~/root"; -import { ApiAction } from "@thunderstore/ts-api-react-actions"; import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; -import { getPublicEnvVariables } from "cyberstorm/security/publicEnvVariables"; +import { type OutletContextShape } from "~/root"; + +import { userLinkedAccountDisconnect } from "../../../../../../packages/thunderstore-api/src"; +import { useHydrated } from "remix-utils/use-hydrated"; type ProvidersType = { name: string; identifier: "discord" | "github" | "overwolf"; icon: ReactElement; -}[]; +}; -export const PROVIDERS: ProvidersType = [ +export const PROVIDERS: ProvidersType[] = [ { name: "Discord", identifier: "discord", @@ -36,31 +41,59 @@ export const PROVIDERS: ProvidersType = [ export default function Connections() { const outletContext = useOutletContext() as OutletContextShape; + const isHydrated = useHydrated(); + const revalidator = useRevalidator(); + const toast = useToast(); + const disconnectingProviderRef = useRef(null); - if (!outletContext.currentUser || !outletContext.currentUser.username) - return ; + const onlyOneConnected = () => { + const connectedProviders = + outletContext.currentUser?.connections?.length ?? 0; + return connectedProviders === 1; + }; - const toast = useToast(); + const getConnection = (provider: ProvidersType) => + outletContext.currentUser?.connections?.find( + (c) => c.provider?.toLowerCase() === provider.identifier + ); // eslint-disable-next-line @typescript-eslint/no-unused-vars const onSubmitSuccess = (_r: unknown) => { - if (!outletContext.currentUser || !outletContext.currentUser.username) - throw new Error("User not logged in"); + const username = outletContext.currentUser?.username; + + revalidator.revalidate(); + toast.addToast({ csVariant: "success", children: ( <> - User {outletContext.currentUser.username} was disconnected from TODO + User {username} was disconnected from{" "} + {disconnectingProviderRef.current} ), duration: 30000, }); + + disconnectingProviderRef.current = null; }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const onSubmitError = (_e: unknown) => { + + const onSubmitError = (error: unknown) => { + let message = "Error when disconnecting account."; + + if (error instanceof ApiError) { + const fieldErrors = error.getFieldErrors(); + message = + fieldErrors.non_field_errors?.[0] || + fieldErrors.detail?.[0] || + fieldErrors.root?.[0] || + error.message || + message; + } + toast.addToast({ csVariant: "danger", - children: <>Unknown error occurred. The error has been logged, + children: <>{message}, + duration: 8000, }); }; @@ -73,8 +106,17 @@ export default function Connections() { const publicEnvVariables = getPublicEnvVariables([ "VITE_AUTH_BASE_URL", "VITE_AUTH_RETURN_URL", + "VITE_BETA_SITE_URL", ]); + if (!isHydrated) { + return
Loading...
; + } + + if (!outletContext.currentUser) { + return ; + } + return (
@@ -94,45 +136,31 @@ export default function Connections() {

- {PROVIDERS.map((p) => { - if ( - !outletContext.currentUser || - !outletContext.currentUser.username - ) - throw new Error("User not logged in"); - return ( - c.provider.toLowerCase() === p.identifier - )} - connectionLink={buildAuthLoginUrl({ - type: p.identifier, - authBaseDomain: publicEnvVariables.VITE_AUTH_BASE_URL || "", - authReturnDomain: - publicEnvVariables.VITE_AUTH_RETURN_URL || "", - })} - disconnectFunction={(p) => { - if ( - !outletContext.currentUser || - !outletContext.currentUser.username - ) - throw new Error("User not logged in"); - return onSubmit({ - params: { - provider: p, - }, - config: outletContext.requestConfig, - queryParams: {}, - data: { provider: p }, - }); - }} - /> - ); - })} + {PROVIDERS.map((provider) => ( + { + disconnectingProviderRef.current = provider; + return onSubmit({ + params: { provider }, + config: outletContext.requestConfig, + queryParams: {}, + data: { provider }, + }); + }} + /> + ))}
diff --git a/apps/cyberstorm-remix/app/settings/user/Settings.tsx b/apps/cyberstorm-remix/app/settings/user/Settings.tsx index 6b05b28fb..54ef6ac9e 100644 --- a/apps/cyberstorm-remix/app/settings/user/Settings.tsx +++ b/apps/cyberstorm-remix/app/settings/user/Settings.tsx @@ -1,56 +1,24 @@ +import "./Settings.css"; import { Outlet, useLocation, useOutletContext } from "react-router"; import { NewLink, Tabs } from "@thunderstore/cyberstorm"; import { PageHeader } from "~/commonComponents/PageHeader/PageHeader"; - import { type OutletContextShape } from "../../root"; -import "./Settings.css"; -import { NotLoggedIn } from "~/commonComponents/NotLoggedIn/NotLoggedIn"; - -// export async function clientLoader() { -// const _storage = new NamespacedStorageManager(SESSION_STORAGE_KEY); -// const currentUser = getSessionCurrentUser(_storage, true, undefined, () => { -// clearSession(_storage); -// throw new Response("Your session has expired, please log in again", { -// status: 401, -// }); -// // redirect("/"); -// }); - -// if ( -// !currentUser.username || -// (currentUser.username && currentUser.username === "") -// ) { -// clearSession(_storage); -// throw new Response("Not logged in.", { status: 401 }); -// } else { -// return { -// currentUser: currentUser as typeof currentUserSchema._type, -// }; -// } -// } -// export function HydrateFallback() { -// return
Loading...
; -// } +export default function UserSettings() { + const context = useOutletContext(); + const { pathname } = useLocation(); + const currentTab = pathname.endsWith("/account/") ? "account" : "settings"; -export default function Community() { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - // const { currentUser } = useLoaderData(); - const location = useLocation(); - const outletContext = useOutletContext() as OutletContextShape; - - if (!outletContext.currentUser || !outletContext.currentUser.username) - return ; - - const currentTab = location.pathname.endsWith("/account/") - ? "account" - : "settings"; + function tabClass(tab: string) { + return `tabs-item${currentTab === tab ? " tabs-item--current" : ""}`; + } return ( <> Settings +
Settings @@ -69,15 +35,14 @@ export default function Community() { primitiveType="cyberstormLink" linkId="SettingsAccount" aria-current={currentTab === "account"} - rootClasses={`tabs-item${ - currentTab === "account" ? " tabs-item--current" : "" - }`} + rootClasses={tabClass("account")} > Account +
- +