Skip to content

Commit

Permalink
Make like button work
Browse files Browse the repository at this point in the history
  • Loading branch information
Oksamies committed Mar 20, 2024
1 parent e772719 commit 5acb38d
Show file tree
Hide file tree
Showing 17 changed files with 204 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import {
faDonate,
faDownload,
faThumbsUp,
faFlag,
// faFlag,
faBoxes,
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import styles from "./PackageDetailsLayout.module.css";
import { Button, Icon, Tag } from "@thunderstore/cyberstorm";
import { ReactNode, Suspense } from "react";
import { ReactNode, Suspense, useState } from "react";
import { WrapperCard } from "@thunderstore/cyberstorm/src/components/WrapperCard/WrapperCard";
import { PackageLikeAction } from "@thunderstore/cyberstorm-forms";

export default function PackageDetailsLayout({
packageMeta,
Expand All @@ -33,6 +34,22 @@ export default function PackageDetailsLayout({
params.namespace,
params.package,
]);
const teamData = usePromise(dapper.getTeamDetails, [params.namespace]);
const currentUser = usePromise(dapper.getCurrentUser, []);

const [isLiked, setIsLiked] = useState(
currentUser.rated_packages.includes(packageData.uuid4)
);

async function useUpdateLikeStatus() {
const dapper = useDapper();
const currentUser = await dapper.getCurrentUser();
if (currentUser.rated_packages.includes(packageData.uuid4)) {
setIsLiked(true);
} else {
setIsLiked(false);
}
}

const mappedPackageTagList = packageData.categories.map((category) => {
return (
Expand All @@ -51,9 +68,25 @@ export default function PackageDetailsLayout({
<a href={packageData.download_url} className={styles.download}>
<DownloadButton />
</a>
<DonateButton onClick={TODO} />
<LikeButton onClick={TODO} />
<ReportButton onClick={TODO} />
{teamData.donation_link ? (
<DonateButton donationLink={teamData.donation_link} />
) : null}
<Button.Root
onClick={PackageLikeAction({
packageName: params.package,
uuid4: packageData.uuid4,
isLiked: isLiked,
currentUserUpdateTrigger: useUpdateLikeStatus,
})}
tooltipText="Like"
colorScheme={isLiked ? "likeBlue" : "primary"}
paddingSize="mediumSquare"
>
<Button.ButtonIcon>
<FontAwesomeIcon icon={faThumbsUp} />
</Button.ButtonIcon>
</Button.Root>
{/* <ReportButton onClick={TODO} /> */}
</div>
<Suspense fallback={<p>TODO: SKELETON packageMeta</p>}>
{packageMeta}
Expand Down Expand Up @@ -82,28 +115,16 @@ export default function PackageDetailsLayout({
);
}

const TODO = () => Promise.resolve();
// const TODO = () => Promise.resolve();

interface Clickable {
onClick: () => Promise<void>;
}
// interface Clickable {
// onClick: () => Promise<void>;
// }

const LikeButton = (props: Clickable) => (
const DonateButton = (props: { donationLink: string }) => (
<Button.Root
onClick={props.onClick}
tooltipText="Like"
colorScheme="primary"
paddingSize="mediumSquare"
>
<Button.ButtonIcon>
<FontAwesomeIcon icon={faThumbsUp} />
</Button.ButtonIcon>
</Button.Root>
);

const DonateButton = (props: Clickable) => (
<Button.Root
onClick={props.onClick}
plain
href={props.donationLink}
tooltipText="Donate to author"
colorScheme="primary"
paddingSize="mediumSquare"
Expand All @@ -114,18 +135,19 @@ const DonateButton = (props: Clickable) => (
</Button.Root>
);

const ReportButton = (props: Clickable) => (
<Button.Root
onClick={props.onClick}
tooltipText="Report"
colorScheme="primary"
paddingSize="mediumSquare"
>
<Button.ButtonIcon>
<FontAwesomeIcon icon={faFlag} />
</Button.ButtonIcon>
</Button.Root>
);
// TODO: Enable and finish, when we have endpoint for submitting
// const ReportButton = (props: Clickable) => (
// <Button.Root
// onClick={props.onClick}
// tooltipText="Report"
// colorScheme="primary"
// paddingSize="mediumSquare"
// >
// <Button.ButtonIcon>
// <FontAwesomeIcon icon={faFlag} />
// </Button.ButtonIcon>
// </Button.Root>
// );

const DownloadButton = () => (
<Button.Root plain colorScheme="primary" paddingSize="medium">
Expand Down
1 change: 1 addition & 0 deletions apps/cyberstorm-nextjs/dapper/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export function ClientDapper(props: React.PropsWithChildren) {
const config = {
apiHost: process.env.NEXT_PUBLIC_API_DOMAIN || "https://thunderstore.io",
sessionId: getCookie("sessionid"),
csrfToken: getCookie("csrftoken"),
};
const dapperConstructor = () => new DapperTs(config);

Expand Down
44 changes: 44 additions & 0 deletions packages/cyberstorm-forms/src/actions/PackageLikeAction.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,
packageLikeActionSchema,
} from "@thunderstore/ts-api-react-actions";
import { packageLike } from "@thunderstore/thunderstore-api";

export function PackageLikeAction(props: {
packageName: string;
uuid4: string;
isLiked: boolean;
currentUserUpdateTrigger: () => Promise<void>;
}) {
const { onSubmitSuccess, onSubmitError } = useFormToaster({
successMessage: `${props.isLiked ? "Unliked" : "Liked"} package ${
props.packageName
}`,
});

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

function onActionError() {
onSubmitError();
}

const onSubmit = ApiAction({
schema: packageLikeActionSchema,
meta: { uuid4: props.uuid4 },
endpoint: packageLike,
onSubmitSuccess: onActionSuccess,
onSubmitError: onActionError,
});

return function () {
onSubmit({ target_state: props.isLiked ? "unrated" : "rated" });
};
}

PackageLikeAction.displayName = "PackageLikeAction";
1 change: 1 addition & 0 deletions packages/cyberstorm-forms/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { useFormToaster } from "./useFormToaster";
export { TeamMemberChangeRoleAction } from "./actions/TeamMemberChangeRoleAction";
export { PackageLikeAction } from "./actions/PackageLikeAction";
export { FormSubmitButton } from "./components/FormSubmitButton";
export { FormSelectSearch } from "./components/FormSelectSearch";
export { FormMultiSelectSearch } from "./components/FormMultiSelectSearch";
Expand Down
10 changes: 10 additions & 0 deletions packages/cyberstorm/src/components/Button/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,16 @@
--bg-color: #27275d;
}

.button__likeBlue {
background: #0b4162;

--button-border-color: #1ca3f5;
}

.button__likeBlue:hover {
background: #083149;
}

/* Button padding sizes */

.padding__none {
Expand Down
2 changes: 2 additions & 0 deletions packages/cyberstorm/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ButtonProps {
| "overwolf"
| "specialGreen"
| "specialPurple"
| "likeBlue"
| "transparentDanger"
| "transparentDefault"
| "transparentTertiary"
Expand Down Expand Up @@ -181,6 +182,7 @@ const getStyle = (scheme: string) => {
overwolf: styles.button__overwolf,
specialGreen: styles.button__specialGreen,
specialPurple: styles.button__specialPurple,
likeBlue: styles.button__likeBlue,
transparentDanger: styles.button__transparentDanger,
transparentDefault: styles.button__transparentDefault,
transparentTertiary: styles.button__transparentTertiary,
Expand Down
1 change: 1 addition & 0 deletions packages/dapper-fake/src/fakers/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export const getFakePackageListingDetails = async (
return {
...getFakePackageListing(community, namespace, name),

uuid4: faker.string.uuid(),
community_name: faker.word.sample(),
datetime_created: faker.date.past({ years: 2 }).toISOString(),
dependant_count: faker.number.int({ min: 0, max: 2000 }),
Expand Down
1 change: 1 addition & 0 deletions packages/dapper-ts/src/methods/packageListings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const dependencyShema = z.object({
});

const packageListingDetailSchema = packageListingSchema.extend({
uuid4: z.string().nonempty(),
community_name: z.string().nonempty(),
datetime_created: z.string().datetime(),
dependant_count: z.number().int().gte(0),
Expand Down
1 change: 1 addition & 0 deletions packages/dapper/src/types/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface PackageListing {
export type PackageListings = PaginatedList<PackageListing>;

export interface PackageListingDetails extends PackageListing {
uuid4: string;
community_name: string;
datetime_created: string;
dependant_count: number;
Expand Down
13 changes: 11 additions & 2 deletions packages/thunderstore-api/src/apiFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,29 @@ export async function apiFetch2(args: apiFetchArgs) {
return response.json();
}

export function apiFetch(config: RequestConfig, path: string, query?: string) {
export function apiFetch(
config: RequestConfig,
path: string,
query?: string,
request?: Omit<RequestInit, "headers">
) {
// TODO: Update the apiFetch signature to take in object args instead
// of positional arguments and then merge apiFetch and apiFetch2
// together. Someone else's job for now.
return apiFetch2({
config,
path,
query,
request,
});
}

function getAuthHeaders(config: RequestConfig): RequestInit["headers"] {
return config.sessionId
? { Authorization: `Session ${config.sessionId}` }
? {
Authorization: `Session ${config.sessionId}`,
"X-Csrftoken": config.csrfToken ? config.csrfToken : "",
}
: {};
}

Expand Down
3 changes: 2 additions & 1 deletion packages/thunderstore-api/src/fetch/currentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { apiFetch } from "../apiFetch";

export async function fetchCurrentUser(config: RequestConfig) {
const path = "api/experimental/current-user/";
const request = { cache: "no-store" as RequestCache };

return await apiFetch(config, path);
return await apiFetch(config, path, undefined, request);
}
28 changes: 28 additions & 0 deletions packages/thunderstore-api/src/fetch/packageLike.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { RequestConfig } from "../index";
import { apiFetch2 } from "../apiFetch";

export type packageLikeMetaArgs = {
uuid4: string;
};

export type packageLikeApiArgs = {
target_state: "rated" | "unrated";
};

export function packageLike(
config: RequestConfig,
data: packageLikeApiArgs,
meta: packageLikeMetaArgs
) {
const path = `/api/v1/package/${meta.uuid4}/rate/`;

return apiFetch2({
config,
path,
request: {
method: "POST",
cache: "no-store",
body: JSON.stringify(data),
},
});
}
2 changes: 2 additions & 0 deletions packages/thunderstore-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface RequestConfig {
// TODO: This should not be explicitly bound to a session ID but rather just
// accept any authorization header. Noting as currently out of scope.
sessionId?: string;
csrfToken?: string;
}

export * from "./fetch/community";
Expand All @@ -28,5 +29,6 @@ export * from "./fetch/teamServiceAccountRemove";
export * from "./fetch/userDelete";
export * from "./fetch/teamDisbandTeam";
export * from "./fetch/teamEditMember";
export * from "./fetch/packageLike";
export * from "./fetch/teamRemoveMember";
export * from "./errors";
1 change: 1 addition & 0 deletions packages/ts-api-react-actions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { ApiAction } from "./ApiAction";
export { packageLikeActionSchema } from "./schema";
5 changes: 5 additions & 0 deletions packages/ts-api-react-actions/src/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { z } from "zod";

export const packageLikeActionSchema = z.object({
target_state: z.union([z.literal("rated"), z.literal("unrated")]),
});
Loading

0 comments on commit 5acb38d

Please sign in to comment.