Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Notuom committed Nov 23, 2021
0 parents commit b9b1c15
Show file tree
Hide file tree
Showing 42 changed files with 10,395 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# pikmin-bloom-checklist

A checklist to help you keep track of which type of Decor Pikmin you have collected in the Pikmin Bloom game.

Published on [pikmin-bloom-checklist.notuom.com](https://pikmin-bloom-checklist.notuom.com/).

## Local Development

This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). Tested with Node 16 LTS.

First, run the development server:

```bash
npm install
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

141 changes: 141 additions & 0 deletions components/About.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
export const About = () => (
<>
<section>
<h3>What is this?</h3>
<p>
This web app is a checklist to help you keep track of which type of
Decor Pikmin you have collected in the Pikmin Bloom game available for
Android and iOS.
</p>
<h3>How does it work?</h3>
<p>
Click on the icon corresponding to the collected Decor Pikmin to change
from ❌ to ✅. It will be saved on this device until you clear your
browser's storage.
</p>
<p>
Click on the icon again to change to 🥚, indicating you are currently
growing this type/color of Decor Pikmin.
</p>
<p>
You can also share your progress between devices by using the "Share"
button which will generate a URL containing your collection to be sent
to the other device, and clear your progress with the "Clear" button.
</p>
<h3>Why?</h3>
<p>
The collection screen in Pikmin is hard to browse. This web app makes it
easier to quickly see which Decor Pikmin you are missing.
</p>
<h3>Decor Pikmin?</h3>
<p>
If you have no idea how Decor Pikmin work, I'd suggest starting here:{" "}
<a href="https://www.pikminwiki.com/Decor_Pikmin" target="_blank">
https://www.pikminwiki.com/Decor_Pikmin
</a>{" "}
or reading the sections below.
</p>
<h4>How do I get Decor Pikmin?</h4>
<p>
In Pikmin Bloom, every Pikmin you get has the potential to become a
"Decor Pikmin". This can happen two different ways:
</p>
<ol>
<li>
You get the Pikmin from a Huge Seedling. Once it is grown into a
Pikmin, it will have decor based on the type of place it was found in.
</li>
<li>
You get up to the maximum level of friendship (4 full hearts). Once
this happens, a special expedition will be available for this Pikmin
to go on by itself, and it will come back with a decor matching the
type of place it was found in.
</li>
</ol>
<h4>How do I know which Decor Pikmin I have?</h4>
<p>
In order to know which ones you already have, you can check your "Decor
Collector" badge progress. To do so:
</p>
<ol>
<li>
Tap your user profile from the home screen (the panel that shows your
username)
</li>
<li>Tap the Decor Collector badge</li>
</ol>
<p>
This will show you all the Decor Pikmin you already have. If you want to
get all the Decors as swiftly as possible, you could mark the Seedlings
and Pikmin of a color and type of Decor you don't already have and
prioritize growing and making friends with those first.
</p>
<h4>How do I know which Decor a Pikmin or Seedling will get?</h4>
<p>
This depends on the type of location the Seedling was obtained in, which
you can find on both the Seedling page and the Pikmin page. The Forest
and Supermarket types have two different variations which the Pikmin
will randomly get when it is decorated.
</p>
</section>
<footer>
<h3>Credits and licensing</h3>
<p>
<strong>Disclaimer:</strong> This web app is not affiliated with Niantic
or Nintendo.
</p>
<ul>
<li>
Decor Icons retrieved from{" "}
<a href="https://www.pikminwiki.com/Decor_Pikmin" target="_blank">
https://www.pikminwiki.com/Decor_Pikmin
</a>
</li>
<li>
<a
href="https://thenounproject.com/term/beetle/396855/"
target="_blank"
>
Beetle by Erik Jensen from the Noun Project
</a>
</li>
<li>
<a
href="https://thenounproject.com/term/banana/2770445/"
target="_blank"
>
banana by Izwar Muis from the Noun Project
</a>
</li>
<li>
<a
href="https://thenounproject.com/term/mushroom/3194002/"
target="_blank"
>
Mushroom by Adrien Coquet from the Noun Project
</a>
</li>
<li>
<a
href="https://thenounproject.com/term/acorn/4408016/"
target="_blank"
>
Acorn by KP Arts from the Noun Project
</a>
</li>
</ul>
<p>
Created with{" "}
<a href="https://nextjs.org/" target="_blank">
Next.js
</a>
.
</p>
<h3>Contributions</h3>
<p>
<strong>Suggestions, corrections, bug reports?</strong> Please{" "}
<a href="#">file an issue or open a pull request on GitHub</a>.
</p>
</footer>
</>
);
110 changes: 110 additions & 0 deletions components/Checklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useRef, useState } from "react";
import {
nextValidStatus,
useDecorCollection,
} from "../hooks/useDecorCollection";
import styles from "../styles/Checklist.module.css";
import { colors, decors, imageSize, statusEmojis } from "../utils/constants";
import { getShareURL } from "../utils/encoding";
import {
capitalizeDecorTitle,
getDecorIcon,
getDecorKey,
} from "../utils/strings";

export const Checklist = () => {
const [sharedCollectionUrl, setSharedCollectionUrl] = useState("");
const [clipboardOutput, setClipboardOutput] = useState("");
const sharedCollectionInputRef = useRef();

const { collection, get, set, clear } = useDecorCollection();
const confirmClear = () =>
confirm(
"Are you sure you want to proceed with permanently deleting your progress?"
) && clear();

const share = (collection) => {
const url = getShareURL(collection);
setSharedCollectionUrl(url);
sharedCollectionInputRef.current?.focus();
sharedCollectionInputRef.current?.select();

navigator.clipboard
.writeText(url)
.then(() => setClipboardOutput("Share URL copied to clipboard!"))
.catch(() =>
setClipboardOutput(
"Could not access clipboard. Please try again or copy the URL from the text input above."
)
);
};

return (
<>
<table className={styles.table}>
<thead>
<tr>
<th></th>
{colors.map((color) => (
<th key={color} className={styles[color]} title={color} />
))}
</tr>
</thead>
<tbody>
{decors.map((decor) => {
const title = capitalizeDecorTitle(decor);
const imageSrc = getDecorIcon(decor);

return (
<tr key={decor}>
<th>
<img
className={styles.decor}
alt={title}
title={title}
src={imageSrc}
width={imageSize}
height={imageSize}
/>
</th>
{colors.map((color) => {
const key = getDecorKey(decor, color);
const status = get(key);

return (
<td
key={key}
className={styles.status}
onClick={() => set(key, nextValidStatus(status + 1))}
>
{statusEmojis[status]}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
<div>
<button className={styles.button} type="button" onClick={confirmClear}>
Clear
</button>
<button
className={styles.button}
type="button"
onClick={() => share(collection)}
>
Share
</button>
<input
type="text"
value={sharedCollectionUrl}
readOnly
ref={sharedCollectionInputRef}
/>
</div>
{clipboardOutput && <p>{clipboardOutput}</p>}
</>
);
};
51 changes: 51 additions & 0 deletions hooks/useDecorCollection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useState } from "react";
import { decodeCollection } from "../utils/encoding";

const storageKey = "decorCollection";

export const useDecorCollection = () => {
const [collection, setCollection] = useState({});

const setCollectionWithStorage = (newCollection) => {
setCollection(newCollection);
localStorage.setItem(storageKey, JSON.stringify(newCollection));
};

// Get initial collection status from storage on page load
React.useEffect(() => {
const urlCollection = new URL(window.location.href).searchParams.get(
"collection"
);
const storageCollection = localStorage.getItem(storageKey);
if (
urlCollection &&
confirm(
"There was a collection in the URL. Do you want to replace your current collection with that one? This can't be undone."
)
) {
setCollectionWithStorage(decodeCollection(urlCollection));
window.history.pushState({}, null, "/");
} else if (storageCollection) {
setCollection(JSON.parse(storageCollection));
}
}, []);

const get = (key) => collection[key] ?? 0;

const set = (key, status) => {
const newCollection = { ...collection };
if (status > 0) {
newCollection[key] = status;
} else {
delete newCollection[key];
}

setCollectionWithStorage(newCollection);
};

const clear = () => setCollectionWithStorage({});

return { collection, get, set, clear };
};

export const nextValidStatus = (i) => i % 3;
3 changes: 3 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
reactStrictMode: true,
}
Loading

0 comments on commit b9b1c15

Please sign in to comment.