Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve like action and add manage form #1070

Open
wants to merge 1 commit into
base: 03-21-Make_like_button_work
Choose a base branch
from
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
Original file line number Diff line number Diff line change
@@ -1,60 +1,72 @@
"use client";
import { Alert, Button, Tag, TextInput } from "@thunderstore/cyberstorm";
import styles from "./PackageManagementForm.module.css";
import { Button } from "@thunderstore/cyberstorm";
import { useDapper } from "@thunderstore/dapper";
import { usePromise } from "@thunderstore/use-promise";
import { faCircleExclamation } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
PackageDeprecateAction,
PackageEditForm,
} from "@thunderstore/cyberstorm-forms";
import { useState } from "react";

export default function Page({
params,
}: {
params: { community: string; namespace: string; package: string };
}) {
const dapper = useDapper();
const packageData = usePromise(dapper.getPackageListingDetails, [
const packageDataInitial = usePromise(dapper.getPackageListingDetails, [
params.community,
params.namespace,
params.package,
]);
const communityData = usePromise(dapper.getCommunityFilters, [
params.community,
]);
const options = communityData.package_categories.map((cat) => {
return { label: cat.name, value: cat.slug };
});

const [packageData, setPackageData] = useState(packageDataInitial);

// TODO: Convert to using usePromise's cache when it can handle manual busts
// Or React Query stuff
async function useUpdatePackageData() {
const dapper = useDapper();
const packageDataUpdate = await dapper.getPackageListingDetails(
params.community,
params.namespace,
params.package
);
setPackageData(packageDataUpdate);
}

return (
<div className={styles.root}>
<div className={styles.section}>
<Alert
icon={<FontAwesomeIcon icon={faCircleExclamation} />}
content={
"Changes might take several minutes to show publicly! Info shown below is always up to date."
}
variant="info"
/>
<div className={styles.title}>Package status</div>
<div className={styles.statusTag}>
<Tag
size="medium"
label={packageData.is_deprecated ? "DEPRECATED" : "ACTIVE"}
colorScheme={packageData.is_deprecated ? "yellow" : "success"}
/>
</div>
</div>
<div className={styles.section}>
<div className={styles.title}>Edit categories</div>
<TextInput />
</div>
<div className={styles.footer}>
{packageData.is_deprecated ? (
<Button.Root paddingSize="large" colorScheme="default">
<PackageEditForm
options={options}
community={params.community}
namespace={params.namespace}
package={params.package}
current_categories={packageData.categories.map((cat) => cat.slug)}
isDeprecated={packageData.is_deprecated}
deprecationButton={
<Button.Root
type="button"
onClick={PackageDeprecateAction({
packageName: params.package,
namespace: packageData.namespace,
isDeprecated: packageData.is_deprecated,
packageDataUpdateTrigger: useUpdatePackageData,
})}
colorScheme={packageData.is_deprecated ? "warning" : "default"}
paddingSize="large"
>
{packageData.is_deprecated ? (
<Button.ButtonLabel>Undeprecate</Button.ButtonLabel>
</Button.Root>
) : (
<Button.Root paddingSize="large" colorScheme="warning">
) : (
<Button.ButtonLabel>Deprecate</Button.ButtonLabel>
</Button.Root>
)}
<Button.Root paddingSize="large" colorScheme="success">
<Button.ButtonLabel>Save changes</Button.ButtonLabel>
)}
</Button.Root>
</div>
</div>
}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export default function PackageDetailsLayout({
currentUser.rated_packages.includes(packageData.uuid4)
);

// TODO: Convert to using usePromise's cache when it can handle manual busts
// Or React Query stuff
async function useUpdateLikeStatus() {
const dapper = useDapper();
const currentUser = await dapper.getCurrentUser();
Expand Down Expand Up @@ -73,6 +75,7 @@ export default function PackageDetailsLayout({
) : null}
<Button.Root
onClick={PackageLikeAction({
isLoggedIn: Boolean(currentUser.username),
packageName: params.package,
uuid4: packageData.uuid4,
isLiked: isLiked,
Expand Down
44 changes: 44 additions & 0 deletions packages/cyberstorm-forms/src/actions/PackageDeprecateAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"use client";

import { useFormToaster } from "@thunderstore/cyberstorm-forms";
import {
ApiAction,
PackageDeprecateActionSchema,
} from "@thunderstore/ts-api-react-actions";
import { packageDeprecate } from "@thunderstore/thunderstore-api";

export function PackageDeprecateAction(props: {
packageName: string;
namespace: string;
isDeprecated: boolean;
packageDataUpdateTrigger: () => Promise<void>;
}) {
const { onSubmitSuccess, onSubmitError } = useFormToaster({
successMessage: `${
props.isDeprecated ? "Undeprecated" : "Deprecated"
} package ${props.packageName}`,
});

function onActionSuccess() {
props.packageDataUpdateTrigger();
onSubmitSuccess();
}

function onActionError() {
onSubmitError();
}

const onSubmit = ApiAction({
schema: PackageDeprecateActionSchema,
meta: { packageName: props.packageName, namespace: props.namespace },
endpoint: packageDeprecate,
onSubmitSuccess: onActionSuccess,
onSubmitError: onActionError,
});

return function () {
onSubmit({ is_deprecated: !props.isDeprecated });
};
}

PackageDeprecateAction.displayName = "PackageDeprecateAction";
4 changes: 4 additions & 0 deletions packages/cyberstorm-forms/src/actions/PackageLikeAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { packageLike } from "@thunderstore/thunderstore-api";

export function PackageLikeAction(props: {
isLoggedIn: boolean;
packageName: string;
uuid4: string;
isLiked: boolean;
Expand All @@ -17,6 +18,9 @@ export function PackageLikeAction(props: {
successMessage: `${props.isLiked ? "Unliked" : "Liked"} package ${
props.packageName
}`,
errorMessage: props.isLoggedIn
? "Unknown error occurred. The error has been logged"
: "You must be logged in to like a package!",
});

function onActionSuccess() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@
display: flex;
flex-direction: column;
gap: var(--gap--32);
max-height: 70%;
}

.section {
.main {
display: flex;
flex-direction: column;
gap: var(--space--32);
padding: var(--space--32);
}

.dialog {
border-top: var(--border-width--px) solid var(--color-surface--5);
}

.statusSection {
display: flex;
flex-direction: column;
gap: var(--gap--16);
Expand All @@ -23,6 +33,9 @@

.footer {
display: flex;
flex-direction: row;
flex-direction: row-reverse;
gap: 1rem;
justify-content: space-between;
padding: var(--space--16) var(--space--24);
border-top: var(--border-width--px) solid var(--color-surface--5);
}
99 changes: 99 additions & 0 deletions packages/cyberstorm-forms/src/forms/PackageEditForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"use client";

import styles from "./PackageEditForm.module.css";
import { packageEditCategories } from "@thunderstore/thunderstore-api";
import {
ApiForm,
packageEditFormSchema,
} from "@thunderstore/ts-api-react-forms";
import {
FormMultiSelectSearch,
FormSubmitButton,
useFormToaster,
} from "@thunderstore/cyberstorm-forms";
import { Alert, MultiSelectSearchOption, Tag } from "@thunderstore/cyberstorm";
import { ReactNode } from "react";
import { faCircleExclamation } from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

// TODO: Separate deprecation action from this form
// when we move away from the modal
// Package deprecation is done for the package, while other edits
// are for the package listing
export function PackageEditForm(props: {
dialogOnChange?: (v: boolean) => void;
options: MultiSelectSearchOption[];
community: string;
namespace: string;
package: string;
current_categories: string[];
isDeprecated: boolean;
deprecationButton: ReactNode;
}) {
const { onSubmitSuccess, onSubmitError } = useFormToaster({
successMessage: "Changes saved!",
});

return (
<ApiForm
onSubmitSuccess={onSubmitSuccess}
onSubmitError={onSubmitError}
schema={packageEditFormSchema}
meta={{
community: props.community,
namespace: props.namespace,
package: props.package,
current_categories: props.current_categories,
}}
endpoint={packageEditCategories}
formProps={{ className: styles.root }}
>
<div className={styles.dialog}>
<div className={styles.main}>
<div>
<Alert
icon={<FontAwesomeIcon icon={faCircleExclamation} />}
content={
"Changes might take several minutes to show publicly! Info shown below is always up to date."
}
variant="info"
/>
</div>
<div>
<div className={styles.statusSection}>
<div className={styles.title}>Package status</div>
<div className={styles.statusTag}>
<Tag
size="medium"
label={props.isDeprecated ? "DEPRECATED" : "ACTIVE"}
colorScheme={props.isDeprecated ? "yellow" : "success"}
/>
</div>
</div>
</div>
<div className={styles.categoriesSelect}>
<div className={styles.title}>Edit categories</div>
<div className={styles.title}>Current categories</div>
{props.current_categories}
<div className={styles.title}>New categories</div>
<FormMultiSelectSearch
schema={packageEditFormSchema}
name={"new_categories"}
placeholder={"Select categories"}
options={props.options}
fieldFormFormatParser={(v: MultiSelectSearchOption[]) =>
v.map((x) => x.value)
}
/>
</div>
</div>
<div className={styles.footer}>
<FormSubmitButton text="Save changes" />
{props.deprecationButton}
</div>
</div>
</ApiForm>
);
}

PackageEditForm.displayName = "PackageEditForm";
2 changes: 2 additions & 0 deletions packages/cyberstorm-forms/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { useFormToaster } from "./useFormToaster";
export { TeamMemberChangeRoleAction } from "./actions/TeamMemberChangeRoleAction";
export { PackageLikeAction } from "./actions/PackageLikeAction";
export { PackageDeprecateAction } from "./actions/PackageDeprecateAction";
export { FormSubmitButton } from "./components/FormSubmitButton";
export { FormSelectSearch } from "./components/FormSelectSearch";
export { FormMultiSelectSearch } from "./components/FormMultiSelectSearch";
Expand All @@ -15,3 +16,4 @@ export { RemoveServiceAccountForm } from "./forms/RemoveServiceAccountForm";
export { DeleteAccountForm } from "./forms/DeleteAccountForm";
export { DisbandTeamForm } from "./forms/DisbandTeamForm";
export { LeaveTeamForm } from "./forms/LeaveTeamForm";
export { PackageEditForm } from "./forms/PackageEditForm";
6 changes: 5 additions & 1 deletion packages/cyberstorm-forms/src/useFormToaster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { useToast } from "@thunderstore/cyberstorm/src/components/Toast/Provider

export type UseFormToasterArgs = {
successMessage: string;
errorMessage?: string;
};
export type UseFormToasterReturn = {
onSubmitSuccess: () => void;
onSubmitError: () => void;
};
export function useFormToaster({
successMessage,
errorMessage,
}: UseFormToasterArgs): UseFormToasterReturn {
const toast = useToast();

Expand All @@ -25,7 +27,9 @@ export function useFormToaster({
onSubmitError: () => {
toast.addToast({
variant: "danger",
message: "Unknown error occurred. The error has been logged",
message: errorMessage
? errorMessage
: "Unknown error occurred. The error has been logged",
});
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
}

.text {
max-width: 45rem;
overflow: hidden;
color: var(--color-text--secondary);
font-size: var(--font-size--l);
Expand Down
Loading
Loading