diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.css b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.css index 73bc12b65..602ebf0bc 100644 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.css +++ b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.css @@ -1,5 +1,9 @@ @layer nimbus-layout { - .report-package--centered { + .report-package__body { + min-width: 600px; + } + + .report-package__body--centered { align-items: center; justify-content: center; text-align: center; @@ -42,4 +46,10 @@ font-size: var(--font-size-body-md); line-height: var(--line-height-md); } + + @media (width <= 650px) { + .report-package__body { + min-width: 95vw; + } + } } diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.tsx similarity index 51% rename from apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx rename to apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.tsx index 7b20fb251..5d46f58b0 100644 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageForm.tsx +++ b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackage.tsx @@ -1,11 +1,14 @@ +import "./ReportPackage.css"; import { useReducer, useState } from "react"; import { Modal, NewAlert, NewButton, + NewIcon, NewSelect, NewTextInput, + useToast, type SelectOption, } from "@thunderstore/cyberstorm"; import { @@ -13,8 +16,32 @@ import { type PackageListingReportRequestData, packageListingReport, } from "@thunderstore/thunderstore-api"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useStrongForm } from "cyberstorm/utils/StrongForm/useStrongForm"; +import { + faFaceSaluting, + faFlagSwallowtail, +} from "@fortawesome/pro-solid-svg-icons"; + +export function ReportPackageButton( + props: React.HTMLAttributes +) { + return ( + + + + + + ); +} + +ReportPackageButton.displayName = "ReportPackageButton"; const reportOptions: SelectOption[] = [ @@ -27,29 +54,20 @@ const reportOptions: SelectOption[] = { value: "Other", label: "Other" }, ]; -export interface ReportPackageFormProps { +export interface ReportPackageProps { community: string; namespace: string; package: string; config: () => RequestConfig; } -interface ReportPackageFormFullProps extends ReportPackageFormProps { - error: string | null; - onOpenChange: (isOpen: boolean) => void; - setError: (error: string | null) => void; - setIsSubmitted: (isSubmitted: boolean) => void; -} +export function ReportPackage(props: ReportPackageProps) { + const { config, ...requestParams } = props; + + const [submitted, setSubmitted] = useState(false); + const [modalOpen, setModalOpen] = useState(false); -export function ReportPackageForm(props: ReportPackageFormFullProps) { - const { - config, - onOpenChange, - setIsSubmitted, - error, - setError, - ...requestParams - } = props; + const toast = useToast(); function formFieldUpdateAction( state: PackageListingReportRequestData, @@ -95,21 +113,24 @@ export function ReportPackageForm(props: ReportPackageFormFullProps) { inputs: formInputs, submitor, onSubmitSuccess: () => { - setIsSubmitted(true); - setError(null); + setSubmitted(true); }, onSubmitError: (error) => { let message = `Error occurred: ${error.message || "Unknown error"}`; if (error.message === "401: Unauthorized") { message = "You must be logged in to report a package."; } - setError(message); + toast.addToast({ + csVariant: "danger", + children: message, + duration: 8000, + }); }, }); - return ( + const form = ( <> - +
- {error && ( + {strongForm.inputErrors || + strongForm.refineError || + strongForm.submitError ? (
- {error} + {strongForm.inputErrors ? ( + + {Object.entries(strongForm.inputErrors).map(([key, value]) => ( +
+ {key}:{" "} + {Array.isArray(value) ? value.join(", ") : value} +
+ ))} +
+ ) : null} + {strongForm.refineError ? ( + + Error while refining: {strongForm.refineError.message} + + ) : null} + {strongForm.submitError ? ( + + Error while submitting: {strongForm.submitError.message} + + ) : null}
- )} + ) : null}
- onOpenChange(false)}> - Cancel - + + Cancel + Send report ); + + const done = ( + <> + +
+ + + +
+

Thank you for your report

+

+ We've received your report and will review the content shortly. +
+ Your feedback helps keep our community safe. +

+
+ + + Close + + + + ); + + return ( + { + if (modalOpen && submitted && !isOpen) { + // If the modal is being closed after a successful submission, + // reset the form state. + updateFormFieldState({ field: "reason", value: "Other" }); + updateFormFieldState({ field: "description", value: "" }); + setSubmitted(false); + } + setModalOpen(isOpen); + }} + trigger={} + > + {submitted ? done : form} + + ); } -ReportPackageForm.displayName = "ReportPackageForm"; +ReportPackage.displayName = "ReportPackage"; diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx deleted file mode 100644 index 8a9480f90..000000000 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageButton.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { NewButton, NewIcon } from "@thunderstore/cyberstorm"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faFlagSwallowtail } from "@fortawesome/pro-solid-svg-icons"; - -export function ReportPackageButton(props: { onClick: () => void }) { - return ( - - - - - - ); -} - -ReportPackageButton.displayName = "ReportPackageButton"; diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageModal.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageModal.tsx deleted file mode 100644 index c3b54c2a0..000000000 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageModal.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Modal } from "@thunderstore/cyberstorm"; - -import "./ReportPackage.css"; - -interface ReportPackageModalProps { - isOpen: boolean; - onOpenChange: (isOpen: boolean) => void; - children: React.ReactNode; -} - -export function ReportPackageModal(props: ReportPackageModalProps) { - return ( - - {props.children} - - ); -} - -ReportPackageModal.displayName = "ReportPackageModal"; diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageSubmitted.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageSubmitted.tsx deleted file mode 100644 index a046b8db8..000000000 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/ReportPackageSubmitted.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { faFaceSaluting } from "@fortawesome/pro-light-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -import { Modal, NewButton, NewIcon } from "@thunderstore/cyberstorm"; - -export function ReportPackageSubmitted(props: { closeModal: () => void }) { - return ( - <> - -
- - - -
-

Thank you for your report

-

- We've received your report and will review the content shortly. -
- Your feedback helps keep our community safe. -

-
- - - Close - - - - ); -} - -ReportPackageSubmitted.displayName = "ReportPackageSubmitted"; diff --git a/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx b/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx deleted file mode 100644 index 8fb2b5019..000000000 --- a/apps/cyberstorm-remix/app/p/components/ReportPackage/useReportPackage.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useEffect, useState } from "react"; - -import { - ReportPackageForm, - type ReportPackageFormProps, -} from "./ReportPackageForm"; -import { ReportPackageButton } from "./ReportPackageButton"; -import { ReportPackageModal } from "./ReportPackageModal"; -import { ReportPackageSubmitted } from "./ReportPackageSubmitted"; - -export function useReportPackage(formProps: Promise) { - const [isOpen, setIsOpen] = useState(false); - const [isSubmitted, setIsSubmitted] = useState(false); - const [error, setError] = useState(null); - - const onOpenChange = (isOpen: boolean) => { - setIsOpen(isOpen); - setIsSubmitted(false); - setError(null); - }; - - const [props, setProps] = useState(null); - - async function awaitAndSetProps() { - if (!props) { - setProps(await formProps); - } - } - - useEffect(() => { - awaitAndSetProps(); - }, [formProps, props, awaitAndSetProps]); - - const button = onOpenChange(true)} />; - - const extraProps = { error, onOpenChange, setError, setIsSubmitted }; - const form = props && ; - - const done = ( - onOpenChange(false)} /> - ); - - const modal = ( - - {isSubmitted ? done : form} - - ); - - return { - ReportPackageButton: button, - ReportPackageModal: modal, - }; -} diff --git a/apps/cyberstorm-remix/app/p/packageListing.css b/apps/cyberstorm-remix/app/p/packageListing.css index 5f932c86f..65b0997e0 100644 --- a/apps/cyberstorm-remix/app/p/packageListing.css +++ b/apps/cyberstorm-remix/app/p/packageListing.css @@ -264,6 +264,13 @@ width: 100%; } + .package-listing__narrow-other-actions { + display: flex; + flex-direction: row; + gap: var(--gap-xs); + width: 100%; + } + @media (width < 41rem) { .package-listing__actions { position: unset; diff --git a/apps/cyberstorm-remix/app/p/packageListing.tsx b/apps/cyberstorm-remix/app/p/packageListing.tsx index 51d463b49..b66e1c787 100644 --- a/apps/cyberstorm-remix/app/p/packageListing.tsx +++ b/apps/cyberstorm-remix/app/p/packageListing.tsx @@ -33,7 +33,6 @@ import { faArrowUpRight, faLips } from "@fortawesome/pro-solid-svg-icons"; import { CopyButton } from "app/commonComponents/CopyButton/CopyButton"; import { PageHeader } from "app/commonComponents/PageHeader/PageHeader"; import TeamMembers from "app/p/components/TeamMembers/TeamMembers"; -import { useReportPackage } from "app/p/components/ReportPackage/useReportPackage"; import { type OutletContextShape } from "app/root"; import { isPromise } from "cyberstorm/utils/typeChecks"; import { @@ -73,6 +72,10 @@ import { import { ApiAction } from "@thunderstore/ts-api-react-actions"; import "./packageListing.css"; +import { + ReportPackage, + ReportPackageButton, +} from "./components/ReportPackage/ReportPackage"; type PackageListingOutletContext = OutletContextShape & { packageDownloadUrl?: string; @@ -181,15 +184,6 @@ export default function PackageListing() { const [isLiked, setIsLiked] = useState(false); const toast = useToast(); - const { ReportPackageButton, ReportPackageModal } = useReportPackage( - Promise.resolve(listing).then((listingData) => ({ - community: listingData.community_identifier, - namespace: listingData.namespace, - package: listingData.name, - config, - })) - ); - const fetchAndSetRatedPackages = async () => { const rp = await dapper.getRatedPackages(); if (isPromise(listing)) { @@ -426,22 +420,34 @@ export default function PackageListing() { - Loading...

}> - - {(resolvedValue) => ( - - )} - -
- - {ReportPackageButton} +
+ Loading...

}> + + {(resolvedValue) => ( + + )} + +
+ }> + + {(resolvedValue) => ( + + )} + + +
- - {ReportPackageButton} + }> + + {(resolvedValue) => ( + + )} + + - - {ReportPackageModal} ); } diff --git a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts index 7c8408b78..7780633d9 100644 --- a/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts +++ b/apps/cyberstorm-remix/cyberstorm/utils/StrongForm/useStrongForm.ts @@ -126,6 +126,7 @@ export function useStrongForm< ) as SubmissionError ); setInputErrors(error.error.formErrors as InputErrors); + throw error; } else if (error instanceof RequestQueryParamsParseError) { setSubmitError( new Error( @@ -133,6 +134,7 @@ export function useStrongForm< ) as SubmissionError ); setInputErrors(error.error.formErrors as InputErrors); + throw error; } else if (error instanceof ParseError) { setSubmitError( new Error( @@ -150,6 +152,7 @@ export function useStrongForm< if (props.onSubmitError) { props.onSubmitError(error as SubmissionError); } + setSubmitError((prev) => prev ?? (error as SubmissionError)); throw error; } finally { setSubmitting(false);