Skip to content

Commit

Permalink
feat: Adds optimistic update for filtering on pages
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMint committed Apr 6, 2024
1 parent 802b29e commit 30dacbb
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 130 deletions.
19 changes: 10 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 15 additions & 43 deletions src/app/(authenticated)/cellars/[cellarId]/items/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
import { Box, Button, Grid, Stack } from "@mui/joy";
import { useUserId } from "@nhost/nextjs";
import { graphql } from "@shared/gql";
import { Cellar_Items_Bool_Exp, ItemType } from "@shared/gql/graphql";
import { Cellar_Items_Bool_Exp, Item_Type_Enum } from "@shared/gql/graphql";
import { getSearchVectorQuery } from "@shared/queries";
import { useRouter, useSearchParams } from "next/navigation";
import {
ascend,
defaultTo,
Expand All @@ -25,14 +24,12 @@ import { HeaderBar } from "@/components/common/HeaderBar";
import { Link } from "@/components/common/Link";
import { ItemCard, ItemCardItem } from "@/components/item/ItemCard";
import withAuth from "@/hocs/withAuth";
import { formatItemType, getEnumKeys, getItemType } from "@/utilities";
import { useScrollRestore } from "@/utilities/hooks";

type Item = {
item: ItemCardItem;
distance: number;
type: ItemType;
};
import { formatItemType, getItemType } from "@/utilities";
import {
useScrollRestore,
useSearchFilterState,
useTypesFilterState,
} from "@/utilities/hooks";

const cellarQuery = graphql(`
query GetCellarItemsQuery(
Expand Down Expand Up @@ -98,37 +95,12 @@ const cellarQuery = graphql(`
}
`);

const Items = ({
params: { cellarId },
searchParams: { search, types },
}: {
params: { cellarId: string };
searchParams: { search?: string; types?: string };
}) => {
const Items = ({ params: { cellarId } }: { params: { cellarId: string } }) => {
const userId = useUserId();
if (isNil(userId)) throw new Error("Invalid user id");
const parsedTypes = isNotNil(types) ? JSON.parse(types) : undefined;
const router = useRouter();
const searchParams = new URLSearchParams(useSearchParams());
const { scrollId, setScrollId, scrollTargetRef } = useScrollRestore();

const handleSearchChange = (search: string) => {
if (isEmpty(search)) {
searchParams.delete("search");
} else {
searchParams.set("search", search);
}
router.replace(`?${searchParams}`);
};

const handleTypesChange = (types: ItemType[]) => {
if (isEmpty(types) || types.length >= getEnumKeys(ItemType).length) {
searchParams.delete("types");
} else {
searchParams.set("types", JSON.stringify(types));
}
router.replace(`?${searchParams}`);
};
const { types, setTypes } = useTypesFilterState();
const { search, setSearch } = useSearchFilterState();

const [searchVectorResponse] = useQuery({
query: getSearchVectorQuery,
Expand All @@ -140,8 +112,8 @@ const Items = ({
empty_at: { _is_null: true },
};

if (isNotNil(parsedTypes)) {
itemsWhereClause.type = { _in: parsedTypes };
if (isNotNil(types)) {
itemsWhereClause.type = { _in: types as string[] as Item_Type_Enum[] };
}

const [res] = useQuery({
Expand Down Expand Up @@ -216,7 +188,7 @@ const Items = ({
<HeaderBar
defaultSearchValue={search}
isSearching={isSearching}
onSearchChange={handleSearchChange}
onSearchChange={setSearch}
breadcrumbs={[
{ url: "/cellars", text: "Cellars" },
{
Expand All @@ -227,8 +199,8 @@ const Items = ({
endComponent={
<Stack direction="row" spacing={2}>
<CellarItemsFilter
types={parsedTypes}
onTypesChange={handleTypesChange}
types={types}
onTypesChange={setTypes}
counts={counts}
/>
<Button
Expand Down
49 changes: 12 additions & 37 deletions src/app/(authenticated)/favorites/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,17 @@
import { Grid, Stack, Typography } from "@mui/joy";
import { useUserId } from "@nhost/nextjs";
import { graphql } from "@shared/gql";
import { ItemType, Item_Favorites_Bool_Exp } from "@shared/gql/graphql";
import { useRouter, useSearchParams } from "next/navigation";
import {
always,
cond,
defaultTo,
equals,
isEmpty,
isNil,
isNotNil,
nth,
without,
} from "ramda";
ItemType,
Item_Favorites_Bool_Exp,
Item_Type_Enum,
} from "@shared/gql/graphql";
import { defaultTo, isNil, isNotNil, nth, without } from "ramda";
import { useQuery } from "urql";
import { CellarItemsFilter } from "@/components/cellar/CellarItemsFilter";
import { ItemCard, ItemCardItem } from "@/components/item/ItemCard";
import {
RankingsFilter,
RankingsFilterValue,
} from "@/components/ranking/RankingsFilter";
import { formatItemType, getEnumKeys, getItemType } from "@/utilities";
import { useScrollRestore } from "@/utilities/hooks";
import { formatItemType, getItemType } from "@/utilities";
import { useScrollRestore, useTypesFilterState } from "@/utilities/hooks";

const friendsQuery = graphql(`
query FriendsQuery($userId: uuid!) {
Expand Down Expand Up @@ -60,24 +49,13 @@ const favoritesQuery = graphql(`
const Rankings = () => {
const userId = useUserId();
if (isNil(userId)) throw new Error("Nil UserId");
const router = useRouter();
const { scrollId, setScrollId, scrollTargetRef } = useScrollRestore();
const searchParams = new URLSearchParams(useSearchParams());
const types = searchParams.get("types");
const parsedTypes = isNotNil(types) ? JSON.parse(types) : undefined;

const handleTypesChange = (t: ItemType[]) => {
if (isEmpty(t) || t.length >= getEnumKeys(ItemType).length) {
searchParams.delete("types");
} else {
searchParams.set("types", JSON.stringify(t));
}
router.replace(`?${searchParams}`);
};
const { types, setTypes } = useTypesFilterState();

const favoritesWhereClause: Item_Favorites_Bool_Exp = {};
if (isNotNil(parsedTypes)) {
favoritesWhereClause.type = { _in: parsedTypes };
if (isNotNil(types)) {
const itemTypes = types as string[] as Item_Type_Enum[];
favoritesWhereClause.type = { _in: itemTypes };
}

const [{ data }] = useQuery({
Expand Down Expand Up @@ -136,10 +114,7 @@ const Rankings = () => {
>
<Typography level="title-lg">Favorites</Typography>
<Stack direction="row" spacing={2}>
<CellarItemsFilter
types={parsedTypes}
onTypesChange={handleTypesChange}
/>
<CellarItemsFilter types={types} onTypesChange={setTypes} />
</Stack>
</Stack>
</Grid>
Expand Down
53 changes: 14 additions & 39 deletions src/app/(authenticated)/rankings/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { graphql } from "@shared/gql";
import { ItemType, Item_Score_Bool_Exp_Bool_Exp } from "@shared/gql/graphql";
import { useRouter, useSearchParams } from "next/navigation";
import { defaultTo, isEmpty, isNil, isNotNil, nth, without } from "ramda";
import { startTransition, useOptimistic } from "react";
import { useQuery } from "urql";
import { CellarItemsFilter } from "@/components/cellar/CellarItemsFilter";
import { ItemCard, ItemCardItem } from "@/components/item/ItemCard";
Expand All @@ -14,7 +15,11 @@ import {
RankingsFilterValue,
} from "@/components/ranking/RankingsFilter";
import { formatItemType, getEnumKeys, getItemType } from "@/utilities";
import { useScrollRestore } from "@/utilities/hooks";
import {
useReviewersFilterState,
useScrollRestore,
useTypesFilterState,
} from "@/utilities/hooks";

const friendsQuery = graphql(`
query FriendsQuery($userId: uuid!) {
Expand Down Expand Up @@ -143,52 +148,28 @@ const rankingQuery = graphql(`
const Rankings = () => {
const userId = useUserId();
if (isNil(userId)) throw new Error("Nil UserId");
const router = useRouter();
const { scrollId, setScrollId, scrollTargetRef } = useScrollRestore();
const searchParams = new URLSearchParams(useSearchParams());
const reviewers = searchParams.get("reviewers");
const types = searchParams.get("types");
const parsedReviewers = isNotNil(reviewers)
? JSON.parse(reviewers)
: undefined;
const parsedTypes = isNotNil(types) ? JSON.parse(types) : undefined;

const handleReviewersChange = (r: RankingsFilterValue[]) => {
if (isEmpty(r)) {
searchParams.delete("reviewers");
} else {
searchParams.set("reviewers", JSON.stringify(r));
}
router.replace(`?${searchParams}`);
};

const handleTypesChange = (t: ItemType[]) => {
if (isEmpty(t) || t.length >= getEnumKeys(ItemType).length) {
searchParams.delete("types");
} else {
searchParams.set("types", JSON.stringify(t));
}
router.replace(`?${searchParams}`);
};
const { types, setTypes } = useTypesFilterState();
const { reviewers, setReviewers } = useReviewersFilterState();

const [{ data: friendData }] = useQuery({
query: friendsQuery,
variables: { userId },
});

const reviewWhereClause: Item_Score_Bool_Exp_Bool_Exp = {};
if (isNotNil(parsedTypes)) {
reviewWhereClause.type = { _in: parsedTypes };
if (isNotNil(types)) {
reviewWhereClause.type = { _in: types };
}

let reviewersFilter = new Array<string>();
if (parsedReviewers?.includes(RankingsFilterValue.ME)) {
if (reviewers?.includes(RankingsFilterValue.ME)) {
reviewersFilter = reviewersFilter.concat([userId]);
}
if (
isNotNil(friendData) &&
isNotNil(friendData.user) &&
parsedReviewers?.includes(RankingsFilterValue.FRIENDS)
reviewers?.includes(RankingsFilterValue.FRIENDS)
) {
reviewersFilter = reviewersFilter.concat(
friendData.user.friends.map((x) => x.friend_id),
Expand Down Expand Up @@ -253,14 +234,8 @@ const Rankings = () => {
>
<Typography level="title-lg">Rankings</Typography>
<Stack direction="row" spacing={2}>
<RankingsFilter
types={parsedReviewers}
onTypesChange={handleReviewersChange}
/>
<CellarItemsFilter
types={parsedTypes}
onTypesChange={handleTypesChange}
/>
<RankingsFilter types={reviewers} onTypesChange={setReviewers} />
<CellarItemsFilter types={types} onTypesChange={setTypes} />
</Stack>
</Stack>
</Grid>
Expand Down
Loading

0 comments on commit 30dacbb

Please sign in to comment.