on master help sign in
grants: rewrite everything to only use queries
commitcedf6d4e3433b893a4b8e96c6cd5b557d89e77e4 authorMatilde Park <matilde.park@urbit.org> dateWed 11 May 2022, 18:28 (4 years ago) parent111e592
1 file changed+137−224
pages/grants.js+137−224
pages/grants.js+137−224
@@ -1,16 +1,12 @@
11import Head from "next/head";
2−import { TableOfContents } from "../components/TableOfContents";
32import Meta from "../components/Meta";
43import Link from "next/link";
54import { useRouter } from "next/router";
6−import { useState } from "react";
7−import classnames from "classnames";
85import Container from "../components/Container";
96import Footer from "../components/Footer";
107import Header from "../components/Header";
118import SingleColumn from "../components/SingleColumn";
129import Section from "../components/Section";
13−import PostPreview from "../components/PostPreview";
1410import GrantPreview from "../components/GrantPreview";
1511import JoinGroup from "../components/JoinGroup";
1612import {
@@ -20,73 +16,32 @@ import {
2016 getPostBySlug,
2117} from "../lib/lib";
2218
23−function addTag(array, tag) {
24− return [...array, tag];
19+function isArray(arr) {
20+ return Array.isArray(arr);
2521}
2622
27−function removeTag(array, tag) {
28− const location = array.indexOf(tag);
29− if (location === -1) {
30− return array;
23+function isOrIsIn(type, query) {
24+ if (!query) {
25+ return false;
3126 }
32− const newArr = [...array];
33− newArr.splice(location, 1);
34− return newArr;
27+ return isArray(query) ? query.includes(type) : type === query;
3528}
3629
37−function setAllActive(fullSet) {
38− return fullSet;
39−}
40−
41−function setAllInactive() {
42− return [];
43−}
44−
45−function setOneTagOnly(tag) {
46− return [tag];
47−}
48−
49−function toggleTag(array, tag) {
50− if (array.includes(tag)) {
51− return removeTag(array, tag);
52− } else {
53− return addTag(array, tag);
54− }
55−}
56−
57−function toggleAll(currentArray, fullSet) {
58− if (currentArray.length === 0) {
59− return setAllActive(fullSet);
60− } else {
61− return setAllInactive();
62− }
63−}
64−
65−export default function Grants({
66− posts,
67− categories,
68− types,
69− featuredGrants,
70− search,
71− giftPosts,
72− gifts,
73−}) {
30+export default function Grants({ posts, categories, types, search }) {
7431 const router = useRouter();
75− const { open, completed, program, wip } = router.query;
76− const [activeTags, setTags] = useState([]);
77− const [activeTypes, setTypes] = useState(types);
78− const includeOpen = open === "true";
79− const includeCompleted = completed === "true";
80− const programFilter = program;
81− const post = {
82− title: "Grants",
83− description: "Contribute to the Urbit project while earning address space.",
84− };
85−
86− let includeInProgress = wip === undefined || wip === "true";
87−
88− if (open === undefined && completed === undefined && wip === undefined) {
89− includeInProgress = true;
32+ let { status, type, category } = router.query;
33+ if (status === undefined) {
34+ status = "open";
35+ }
36+ function push(toQuery) {
37+ router.push(
38+ {
39+ pathname: "/grants",
40+ query: toQuery,
41+ },
42+ "",
43+ { scroll: false }
44+ );
9045 }
9146
9247 const annotatedPosts = posts.map((post) => {
@@ -103,33 +58,24 @@ export default function Grants({
10358
10459 const byStatus = (post) => {
10560 return (
106− (includeOpen ? post.status === "open" : false) ||
107− (includeCompleted ? post.status === "completed" : false) ||
108− (includeInProgress ? post.status === "wip" : false)
61+ (isOrIsIn("open", status) ? post.status === "open" : false) ||
62+ (isOrIsIn("complete", status) ? post.status === "completed" : false) ||
63+ (isOrIsIn("wip", status) ? post.status === "wip" : false)
10964 );
11065 };
11166
11267 const postsByStatus = annotatedPosts.filter(byStatus);
11368
114− const filteredPosts = annotatedPosts.filter((post) => {
115− // Posts are returned if they match both the selected category and selected tags, or if the user has no category filters set.
116− const hasCategory = post.taxonomies.grant_category.some((category) =>
117− activeTags.includes(category)
118− );
69+ const filteredPosts = postsByStatus.filter((post) => {
70+ const hasCategory = category
71+ ? isArray(category)
72+ ? post.taxonomies.grant_category.some((cat) => category.includes(cat))
73+ : post.taxonomies.grant_category.includes(category)
74+ : true;
11975
12076 const notCanceled = !post.extra.canceled;
121−
122− const noTagsSelected = activeTags.length === 0;
123− const hasType = post.taxonomies.grant_type.some((type) =>
124− activeTypes.includes(type)
125− );
126−
127− return (
128− (hasCategory || noTagsSelected) &&
129− byStatus(post) &&
130− hasType &&
131− notCanceled
132− );
77+ const hasType = type ? post.taxonomies.grant_type.includes(type) : true;
78+ return hasCategory && notCanceled && hasType;
13379 });
13480
13581 const allCount = postsByStatus.length;
@@ -150,7 +96,11 @@ export default function Grants({
15096 <Container>
15197 <Head>
15298 <title>Grants • urbit.org</title>
153− {Meta(post)}
99+ {Meta({
100+ title: "Grants",
101+ description:
102+ "Contribute to the Urbit project while earning address space.",
103+ })}
154104 </Head>
155105 <SingleColumn>
156106 <Header search={search} />
@@ -313,139 +263,124 @@ export default function Grants({
313263 </h2>
314264 <h5 className="text-wall-600 font-semibold my-2">Programs</h5>
315265 <div className="flex flex-wrap items-center pb-2">
316− <button
317− onClick={() => {
318− let query = { ...router.query };
319− delete query.program;
320− router.push(
321− {
322− pathname: "/grants",
323− query: { ...query },
324− },
325− "",
326− { scroll: false }
327− );
328− setTypes(types);
329− }}
330− className={`badge-lg my-2 mr-2 ${
331− programFilter === undefined
332− ? "text-white bg-black"
333− : "text-wall-500 bg-wall-100"
266+ <a
267+ className={`badge-lg mr-2 ${
268+ !type ? "bg-black text-white" : "bg-wall-100 text-wall-500"
334269 }`}
270+ onClick={() => push((({ type, ...obj }) => obj)(router.query))}
335271 >
336272 All <div className="opacity-50 ml-2">{allCount}</div>
337− </button>
338− {types.map((type) => {
339− const className = classnames({
340− "bg-blue-400 text-white":
341− programFilter === "proposal" && type === "Proposal",
342− "bg-green-400 text-white":
343− programFilter === "apprenticeship" &&
344− type === "Apprenticeship",
345− "bg-yellow-300":
346− programFilter === "bounty" && type === "Bounty",
347− "bg-wall-100 text-wall-500": type !== programFilter,
348− });
273+ </a>
274+ {types.map((each) => {
275+ let activeBg;
276+ switch (each) {
277+ case "Proposal":
278+ activeBg = "bg-blue-400 text-white";
279+ break;
280+ case "Bounty":
281+ activeBg = "bg-yellow-300 text-white";
282+ break;
283+ case "Apprenticeship":
284+ activeBg = "bg-green-400 text-white";
285+ break;
286+ default:
287+ activeBg = "bg-black";
288+ }
349289 return (
350− <button
351− onClick={() => {
352− // + 1 is added here because the 'all' button precedes this sequence
353− router.push(
354− {
355− pathname: "/grants",
356− query: { ...router.query, program: type.toLowerCase() },
357− },
358− "",
359− { scroll: false }
360− );
361− setTypes([type]);
362− }}
363− className={`badge-lg mr-2 ${className}`}
290+ <a
291+ className={`badge-lg mr-2 ${
292+ isOrIsIn(each, type)
293+ ? activeBg
294+ : "bg-wall-100 text-wall-500"
295+ }`}
296+ onClick={() => push({ ...router.query, type: each })}
364297 >
365− {type} <div className="opacity-50 ml-2">{counts[type]}</div>
366− </button>
298+ {each}
299+ <div className="opacity-50 ml-2">{counts[each]}</div>
300+ </a>
367301 );
368302 })}
369303 </div>
370304 <h5 className="text-wall-600 font-semibold my-2">Work Categories</h5>
371305 <div className="flex flex-wrap mb-12">
372− {categories.map((category) => {
373− const isActive = activeTags.includes(category);
374− const activeClasses = classnames({
375− "bg-green-400 text-white": isActive,
376− "bg-wall-100 text-wall-500": !isActive,
377− });
306+ {categories.map((each) => {
307+ const active = isOrIsIn(each, category)
308+ ? "bg-green-400 text-white"
309+ : "bg-wall-100 text-wall-500";
310+ let to, categoryQuery;
311+ if (isArray(category)) {
312+ to = isOrIsIn(each, category)
313+ ? category.filter((e) => e !== each)
314+ : [...category, each];
315+ } else {
316+ to = isOrIsIn(each, category)
317+ ? null
318+ : [category, each].filter((e) => e !== undefined);
319+ }
320+ if (!to) {
321+ categoryQuery = (({ category, ...obj }) => obj)(router.query);
322+ } else {
323+ categoryQuery = { ...router.query, category: to };
324+ }
378325 return (
379326 <button
380− onClick={() => setTags(toggleTag(activeTags, category))}
381− className={`${activeClasses} badge-sm mr-1 my-1`}
327+ className={`${active} badge-sm mr-1 my-1`}
328+ onClick={() => push(categoryQuery)}
382329 >
383− {category}
330+ {each}
384331 </button>
385332 );
386333 })}
387334 </div>
388− {
389− <div className="pb-8 flex items-center">
390− <button
391− className="mr-4 badge-sm bg-black text-white"
392− onClick={() =>
393− router.push(
394− {
395− pathname: "/grants",
396− query: { ...router.query, open: !includeOpen },
397− },
398− "",
399− { scroll: false }
400− )
401− }
402− >
403− {includeOpen ? "Exclude Open" : "Include Open"}
404− </button>
405−
406− <button
407− className="mr-4 badge-sm bg-black text-white"
408− onClick={() =>
409− router.push(
410− {
411− pathname: "/grants",
412− query: { ...router.query, completed: !includeCompleted },
413− },
414− "",
415− { scroll: false }
416− )
417− }
418− >
419− {includeCompleted ? "Exclude Completed" : "Include Completed"}
420− </button>
421−
422− <button
423− className="mr-4 badge-sm bg-black text-white"
424− onClick={() =>
425− router.push(
426− {
427− pathname: "/grants",
428− query: { ...router.query, wip: !includeInProgress },
429− },
430− "",
431− { scroll: false }
432− )
433− }
434− >
435− {includeInProgress
436− ? "Exclude In Progress"
437− : "Include In Progress"}
438− </button>
439−
440− <h4>
441− Showing {filteredPosts.length} grant
442− {filteredPosts.length === 1 ? "" : "s"}
443− </h4>
444− </div>
445− }
446− {filteredPosts.map((post) => {
447− return <GrantPreview grant={post} />;
448− })}
335+ <div className="pb-8 flex items-center">
336+ {["open", "complete", "wip"].map((each) => {
337+ let title;
338+ switch (each) {
339+ case "open":
340+ title = isOrIsIn(each, status)
341+ ? "Exclude Open"
342+ : "Include Open";
343+ break;
344+ case "complete":
345+ title = isOrIsIn(each, status)
346+ ? "Exclude Completed"
347+ : "Include Completed";
348+ break;
349+ case "wip":
350+ title = isOrIsIn(each, status)
351+ ? "Exclude In Progress"
352+ : "Include In Progress";
353+ }
354+ let to, statusQuery;
355+ if (isArray(status)) {
356+ to = isOrIsIn(each, status)
357+ ? status.filter((e) => e !== each)
358+ : [...status, each];
359+ } else {
360+ to = isOrIsIn(each, status) ? null : [status, each];
361+ }
362+ if (!to) {
363+ statusQuery = (({ status, ...obj }) => obj)(router.query);
364+ } else {
365+ statusQuery = { ...router.query, status: to };
366+ }
367+ return (
368+ <button
369+ className="mr-4 badge-sm bg-black text-white"
370+ onClick={() => push(statusQuery)}
371+ >
372+ {title}
373+ </button>
374+ );
375+ })}
376+ <h4>
377+ Showing {filteredPosts.length} grant
378+ {filteredPosts.length === 1 ? "" : "s"}
379+ </h4>
380+ </div>
381+ {filteredPosts.map((post) => (
382+ <GrantPreview grant={post} />
383+ ))}
449384 </Section>
450385 </SingleColumn>
451386 <Footer />
@@ -461,33 +396,11 @@ export async function getStaticProps() {
461396 "grants",
462397 "date"
463398 );
464−
465− // The layout expects exactly 3
466− const featuredGrants = [
467− "bitcoin-full-node-provider-and-wallet",
468− "webrtc-gall-agent-and-external-app",
469− "urbian-a-customized-linux-distribution-for-urbit-appliances",
470− ].map((slug) =>
471− getPostBySlug(slug, ["title", "slug", "date", "extra"], "grants")
472− );
473−
474− const giftPosts = [
475− getPostBySlug(
476− "2021-06-16-update",
477− ["title", "slug", "date", "extra"],
478− "updates"
479− ),
480− getPostBySlug("gifts-q3-2020", ["title", "slug", "date", "extra"], "blog"),
481− ];
482−
483399 return {
484400 props: {
485401 posts: posts,
486402 categories,
487403 types,
488− featuredGrants,
489− giftPosts,
490− gifts: [],
491404 },
492405 };
493406}