mirror of
https://github.com/barthuijgen/factorio-sites.git
synced 2024-12-04 10:44:44 +02:00
Added ability to add and remove user favorites on the main page (#77)
* Added ability to add and remove user favorites on the main page * Style fixes * Fix pagination and list reload
This commit is contained in:
parent
6166e81c19
commit
b2a3264049
@ -5,12 +5,18 @@ import { css } from "@emotion/react";
|
||||
import { Box, Text } from "@chakra-ui/react";
|
||||
import { MdFavorite } from "react-icons/md";
|
||||
import { BlueprintPage } from "@factorio-sites/types";
|
||||
import clsx from "clsx";
|
||||
|
||||
const linkStyles = css`
|
||||
margin: 5px 10px 5px 0;
|
||||
background: #353535;
|
||||
background: #403f40;
|
||||
width: 210px;
|
||||
height: 232px;
|
||||
height: 255px;
|
||||
box-shadow: inset 3px 0 3px -3px #201815, inset 2px 0 2px -2px #201815,
|
||||
inset 1px 0 1px -1px #201815, inset 0 3px 3px -3px #8f8c8b, inset 0 2px 2px -2px #8f8c8b,
|
||||
inset 0 1px 1px -1px #8f8c8b, inset -3px 0 3px -3px #201815, inset -2px 0 2px -2px #201815,
|
||||
inset -2px 0 1px -1px #201815, inset 0 -3px 3px -3px #000, inset 0 -2px 2px -2px #000,
|
||||
inset 0 -1px 1px -1px #000, 0 0 2px 0 #201815, 0 0 4px 0 #201815;
|
||||
|
||||
.block {
|
||||
display: flex;
|
||||
@ -20,12 +26,45 @@ const linkStyles = css`
|
||||
|
||||
.image {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
width: 190px;
|
||||
height: 200px;
|
||||
background: #303030;
|
||||
margin: 10px;
|
||||
|
||||
box-shadow: inset 0 0 3px 0 #000, 0 -2px 2px -1px #000, -2px 0 2px -2px #28221f,
|
||||
-2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f,
|
||||
0 3px 3px -3px #8f8c8b, 0 2px 2px -2px #8f8c8b, 0 1px 1px -1px #8f8c8b;
|
||||
|
||||
& > div {
|
||||
margin: 2px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
margin: 0 10px;
|
||||
padding: 0 5px;
|
||||
background-color: #242324;
|
||||
box-shadow: inset 0 0 3px 0 #000, 0 -2px 2px -1px #000, -2px 0 2px -2px #28221f,
|
||||
-2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f, 2px 0 2px -2px #28221f,
|
||||
0 3px 3px -3px #8f8c8b, 0 2px 2px -2px #8f8c8b, 0 1px 1px -1px #8f8c8b;
|
||||
}
|
||||
|
||||
.favorite {
|
||||
margin-right: 5px;
|
||||
|
||||
&.user-favorite svg {
|
||||
fill: #fe5a5a;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
@ -36,7 +75,7 @@ const linkStyles = css`
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 5px;
|
||||
/* padding: 5px; */
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@ -47,11 +86,23 @@ const linkStyles = css`
|
||||
`;
|
||||
|
||||
interface BlueprintTileProps {
|
||||
blueprint: Pick<BlueprintPage, "id" | "title" | "image_hash" | "favorite_count" | "updated_at">;
|
||||
blueprint: Pick<
|
||||
BlueprintPage,
|
||||
"id" | "title" | "image_hash" | "favorite_count" | "updated_at"
|
||||
> & {
|
||||
user_favorite: boolean;
|
||||
};
|
||||
editLink?: boolean;
|
||||
disableFavorite?: boolean;
|
||||
onFavoriteClick?: (id: string) => void;
|
||||
}
|
||||
|
||||
export const BlueprintTile: React.FC<BlueprintTileProps> = ({ blueprint, editLink }) => {
|
||||
export const BlueprintTile: React.FC<BlueprintTileProps> = ({
|
||||
blueprint,
|
||||
editLink,
|
||||
disableFavorite,
|
||||
onFavoriteClick,
|
||||
}) => {
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const onImageError = () => {
|
||||
setImageError(true);
|
||||
@ -81,7 +132,17 @@ export const BlueprintTile: React.FC<BlueprintTileProps> = ({ blueprint, editLin
|
||||
</div>
|
||||
<Box className="details">
|
||||
<Text css={{ display: "flex", alignItems: "center", marginRight: "1rem" }}>
|
||||
<MdFavorite css={{ marginRight: "5px" }} />
|
||||
<button
|
||||
className={clsx("favorite", { "user-favorite": blueprint.user_favorite })}
|
||||
title={blueprint.user_favorite ? "Remove from favorites" : "Add to favorites"}
|
||||
disabled={disableFavorite}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onFavoriteClick?.(blueprint.id);
|
||||
}}
|
||||
>
|
||||
<MdFavorite />
|
||||
</button>
|
||||
{blueprint.favorite_count || "0"}
|
||||
</Text>
|
||||
<Text className="title">{blueprint.title}</Text>
|
||||
|
@ -1,7 +1,11 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage, NextPageContext } from "next";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import { searchBlueprintPages, init } from "@factorio-sites/database";
|
||||
import {
|
||||
searchBlueprintPages,
|
||||
init,
|
||||
getUserFavoriteBlueprintPages,
|
||||
} from "@factorio-sites/database";
|
||||
import { BlueprintPage } from "@factorio-sites/types";
|
||||
import { Panel } from "../components/Panel";
|
||||
import { Pagination } from "../components/Pagination";
|
||||
@ -26,6 +30,8 @@ import { css } from "@emotion/react";
|
||||
import { MdSearch } from "react-icons/md";
|
||||
import { TAGS } from "@factorio-sites/common-utils";
|
||||
import { mq } from "@factorio-sites/web-utils";
|
||||
import { useAuth } from "../providers/auth";
|
||||
import { pageHandler } from "../utils/page-handler";
|
||||
|
||||
const pageCss = css({
|
||||
display: "flex",
|
||||
@ -60,24 +66,30 @@ const sidebarCheckbox = css(SidebarRow, {
|
||||
},
|
||||
});
|
||||
|
||||
type BlueprintPageWithUserFavorite = Pick<
|
||||
BlueprintPage,
|
||||
"id" | "image_hash" | "favorite_count" | "title" | "updated_at"
|
||||
> & {
|
||||
user_favorite: boolean;
|
||||
};
|
||||
|
||||
interface IndexProps {
|
||||
totalItems: number;
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
blueprints: Pick<
|
||||
BlueprintPage,
|
||||
"id" | "image_hash" | "favorite_count" | "title" | "updated_at"
|
||||
>[];
|
||||
blueprints: BlueprintPageWithUserFavorite[];
|
||||
}
|
||||
|
||||
export const Index: NextPage<IndexProps> = ({
|
||||
totalItems,
|
||||
currentPage,
|
||||
totalPages,
|
||||
blueprints,
|
||||
blueprints: blueprintsProp,
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const auth = useAuth();
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [blueprints, setBlueprints] = useState<BlueprintPageWithUserFavorite[]>([]);
|
||||
const routerQueryToHref = useRouterQueryToHref();
|
||||
const data = useFbeData();
|
||||
|
||||
@ -85,6 +97,10 @@ export const Index: NextPage<IndexProps> = ({
|
||||
setSearchQuery((router.query.q as string) || "");
|
||||
}, [router?.query.q]);
|
||||
|
||||
useEffect(() => {
|
||||
setBlueprints(blueprintsProp);
|
||||
}, [blueprintsProp]);
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const entityOptions = Object.keys(data.entities).filter(
|
||||
@ -97,6 +113,30 @@ export const Index: NextPage<IndexProps> = ({
|
||||
value: tag.value,
|
||||
}));
|
||||
|
||||
const handleBlueprintFavoriteClick = async (blueprint_page_id: string) => {
|
||||
try {
|
||||
const response = await fetch("/api/user/favorite", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ blueprint_page_id }),
|
||||
});
|
||||
const { favorite } = await response.json();
|
||||
setBlueprints((blueprints) =>
|
||||
blueprints.map((bp) =>
|
||||
bp.id === blueprint_page_id
|
||||
? {
|
||||
...bp,
|
||||
user_favorite: favorite,
|
||||
favorite_count: bp.favorite_count + (favorite ? 1 : -1),
|
||||
}
|
||||
: bp
|
||||
)
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SimpleGrid columns={1}>
|
||||
<Panel title="Blueprints">
|
||||
@ -197,7 +237,14 @@ export const Index: NextPage<IndexProps> = ({
|
||||
<Box css={{ display: "flex", flexDirection: "column" }}>
|
||||
<Box css={{ display: "flex", flexWrap: "wrap", minHeight: "400px", flexGrow: 1 }}>
|
||||
{blueprints.length ? (
|
||||
blueprints.map((bp) => <BlueprintTile key={bp.id} blueprint={bp} />)
|
||||
blueprints.map((bp) => (
|
||||
<BlueprintTile
|
||||
key={bp.id}
|
||||
blueprint={bp}
|
||||
disableFavorite={!auth}
|
||||
onFavoriteClick={handleBlueprintFavoriteClick}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<p css={{ marginTop: "10px" }}>No results found</p>
|
||||
)}
|
||||
@ -212,7 +259,7 @@ export const Index: NextPage<IndexProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export async function getServerSideProps({ query }: NextPageContext) {
|
||||
export const getServerSideProps = pageHandler(async ({ query }, { session }) => {
|
||||
await init();
|
||||
const page = Number(query.page || "1");
|
||||
const perPage = Number(query["per-page"] || "20");
|
||||
@ -241,6 +288,14 @@ export async function getServerSideProps({ query }: NextPageContext) {
|
||||
user,
|
||||
absolute_snapping,
|
||||
});
|
||||
const userFavorites: string[] = session?.user_id
|
||||
? (
|
||||
await getUserFavoriteBlueprintPages(
|
||||
session.user_id,
|
||||
rows.map((row) => row.id)
|
||||
)
|
||||
).map((item) => item.id)
|
||||
: [];
|
||||
|
||||
return {
|
||||
props: {
|
||||
@ -253,9 +308,10 @@ export async function getServerSideProps({ query }: NextPageContext) {
|
||||
favorite_count: row.favorite_count,
|
||||
title: row.title,
|
||||
updated_at: row.updated_at,
|
||||
user_favorite: userFavorites.includes(row.id),
|
||||
})),
|
||||
} as IndexProps,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default Index;
|
||||
|
@ -55,12 +55,13 @@ export async function getBlueprintPageByFactorioprintsId(
|
||||
return result ? mapBlueprintPageEntityToObject(result) : null;
|
||||
}
|
||||
|
||||
export async function getUserFavoriteBlueprintPages(user_id: string) {
|
||||
export async function getUserFavoriteBlueprintPages(user_id: string, ids?: string[]) {
|
||||
const result = await prisma.blueprint_page.findMany({
|
||||
where: {
|
||||
user_favorites: {
|
||||
some: {
|
||||
user_id: user_id,
|
||||
blueprint_page_id: ids ? { in: ids } : undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user