Skip to content

Commit 59dbb03

Browse files
authored
Merge pull request #155 from communitycenter/islandupgrade
Island Upgrade Tracker with Changes (#154)
2 parents 328fb19 + d2389b7 commit 59dbb03

File tree

6 files changed

+282
-5
lines changed

6 files changed

+282
-5
lines changed

apps/stardew.app/public/sitemap.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@
6464
<lastmod>2024-03-19T15:09:23+00:00</lastmod>
6565
<priority>0.80</priority>
6666
</url>
67+
<url>
68+
<loc>https://stardew.app/island/upgrades</loc>
69+
<lastmod>2025-07-17T15:09:23+00:00</lastmod>
70+
<priority>0.80</priority>
71+
</url>
6772
<url>
6873
<loc>https://stardew.app/editor/create</loc>
6974
<lastmod>2024-03-19T15:09:23+00:00</lastmod>

apps/stardew.app/src/components/cards/dialog-card.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from "@/components/ui/dialog";
2828

2929
import { useMultiSelect } from "@/contexts/multi-select-context";
30-
import { Stardrop } from "@/lib/parsers/general";
30+
import { Stardrop, IslandUpgradeMail } from "@/lib/parsers/general";
3131
import { ChevronRightIcon } from "@radix-ui/react-icons";
3232

3333
interface Props {
@@ -36,7 +36,7 @@ interface Props {
3636
iconURL: string;
3737
completed?: boolean;
3838
_id: string;
39-
_type: "stardrop" | "note" | "scrap" | "walnut" | "power" | "rarecrow";
39+
_type: "stardrop" | "note" | "scrap" | "walnut" | "power" | "rarecrow" | "island_upgrade";
4040
/**
4141
* Whether the user prefers to see new content
4242
*
@@ -147,6 +147,16 @@ export const DialogCard = ({
147147
},
148148
};
149149

150+
case "island_upgrade":
151+
const islandUpgrades = new Set(activePlayer.general?.islandUpgrades ?? []);
152+
if (status) islandUpgrades.add(_id as IslandUpgradeMail);
153+
else islandUpgrades.delete(_id as IslandUpgradeMail);
154+
155+
patch = {
156+
general: {
157+
islandUpgrades: Array.from(islandUpgrades),
158+
},
159+
};
150160
break;
151161

152162
case "rarecrow":

apps/stardew.app/src/components/sidebar.tsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
IconHome2,
2121
IconId,
2222
IconNote,
23+
IconPencilUp,
2324
IconProgress,
2425
IconSettings,
2526
IconShirt,
@@ -39,10 +40,8 @@ interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {}
3940

4041
export const miscNavigation = [
4142
{ name: "Bundles", href: "/bundles", icon: IconBox },
42-
{ name: "Rarecrows", href: "/rarecrows", icon: IconCarrot },
43-
{ name: "Walnuts", href: "/island/walnuts", icon: IconProgress },
4443
{ name: "Secret Notes", href: "/notes", icon: IconNote },
45-
{ name: "Journal Scraps", href: "/island/scraps", icon: IconBook },
44+
{ name: "Rarecrows", href: "/rarecrows", icon: IconCarrot },
4645
{ name: "Account Settings", href: "/account", icon: IconSettings },
4746
];
4847

@@ -63,6 +62,12 @@ export const collectionsNavigation = [
6362
{ name: "Museum & Artifacts", href: "/museum", icon: IconBuildingWarehouse },
6463
];
6564

65+
export const islandNavigation = [
66+
{ name: "Golden Walnuts", href: "/island/walnuts", icon: IconProgress },
67+
{ name: "Journal Scraps", href: "/island/scraps", icon: IconBook },
68+
{ name: "Island Upgrades", href: "/island/upgrades", icon: IconPencilUp },
69+
];
70+
6671
export const linksNavigation = [
6772
{ name: "Discord", href: "/discord", icon: DiscordLogoIcon },
6873
{ name: "GitHub", href: "/github", icon: GitHubLogoIcon },
@@ -218,6 +223,28 @@ export function Sidebar({ className }: SidebarProps) {
218223
))}
219224
</div>
220225

226+
<SidebarCategory>Ginger Island</SidebarCategory>
227+
<div className="space-y-1">
228+
{islandNavigation.map((item) => (
229+
<Button
230+
key={item.href}
231+
variant={pathname === item.href ? "secondary" : "ghost"}
232+
className={cn(
233+
"w-full justify-start",
234+
item.href === pathname
235+
? ""
236+
: "text-neutral-600 dark:text-neutral-400",
237+
)}
238+
asChild
239+
>
240+
<Link href={item.href}>
241+
<item.icon className="mr-2 h-4 w-4" aria-hidden="true" />
242+
{item.name}
243+
</Link>
244+
</Button>
245+
))}
246+
</div>
247+
221248
<SidebarCategory>Misc</SidebarCategory>
222249
<div className="space-y-1">
223250
{miscNavigation.map((item) => (
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"Island_FirstParrot": {
3+
"cost": "1",
4+
"name": "Ginger Island North",
5+
"description": "Unlock access to the north side of the island",
6+
"location": "Leo's house"
7+
},
8+
"Island_Turtle": {
9+
"cost": "10",
10+
"name": "Ginger Island West",
11+
"description": "Unlock access to the west side of the island",
12+
"location": "Ginger Island South"
13+
},
14+
"Island_UpgradeHouse": {
15+
"cost": "20",
16+
"name": "Ginger Island Farmhouse",
17+
"description": "Unlock the Farmhouse on the west side of the island, which can be used to sleep in to pass the day without leaving the island",
18+
"location": "Ginger Island West"
19+
},
20+
"Island_UpgradeHouse_Mailbox": {
21+
"cost": "5",
22+
"name": "Farmhouse Mailbox",
23+
"description": "Allow players to access their mail without leaving the island",
24+
"location": "Ginger Island Farmhouse"
25+
},
26+
"Island_W_Obelisk": {
27+
"cost": "20",
28+
"name": "Farm Obelisk",
29+
"description": "Allow players to teleport back to the farm, similar to a warp totem",
30+
"location": "Ginger Island Farmhouse"
31+
},
32+
"Island_UpgradeBridge": {
33+
"cost": "10",
34+
"name": "Island Dig Site",
35+
"description": "Repair the bridge to access the island dig site",
36+
"location": "Ginger Island North"
37+
},
38+
"Island_UpgradeTrader": {
39+
"cost": "10",
40+
"name": "Island Trader",
41+
"description": "Unlock the Island Trader shop",
42+
"location": "Ginger Island North"
43+
},
44+
"Island_VolcanoBridge": {
45+
"cost": "5",
46+
"name": "Volcano Bridge",
47+
"description": "Unlock a permanent bridge into the volcano so that players no longer have to have their watering can to get in",
48+
"location": "Volcano Dungeon entrance"
49+
},
50+
"Island_VolcanoShortcutOut": {
51+
"cost": "5",
52+
"name": "Volcano Exit Shortcut",
53+
"description": "Unlock an escape shortcut from level 5 of the Volcano Dungeon, allowing quick access to Island North",
54+
"location": "Volcano Dungeon level 5"
55+
},
56+
"Island_Resort": {
57+
"cost": "20",
58+
"name": "Island Resort",
59+
"description": "Unlock a beach resort. NPCs from the town will occasionally come to visit the resort. This also unlocks Island Southeast and the Pirate Cove",
60+
"location": "Ginger Island South"
61+
},
62+
"Island_UpgradeParrotPlatform": {
63+
"cost": "10",
64+
"name": "Parrot Express",
65+
"description": "Unlock a fast travel system to quickly get around the island",
66+
"location": "Ginger Island"
67+
}
68+
}

apps/stardew.app/src/lib/parsers/general.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,54 @@ function parseAchievements(player: any): AchievementsRet {
193193
}
194194
}
195195

196+
/* ------------------------------ island upgrade parser -------------------------------- */
197+
export type IslandUpgradeMail =
198+
| "Island_FirstParrot"
199+
| "Island_Turtle"
200+
| "Island_UpgradeHouse"
201+
| "Island_Resort"
202+
| "Island_UpgradeTrader"
203+
| "Island_UpgradeBridge"
204+
| "Island_UpgradeParrotPlatform"
205+
| "Island_UpgradeHouse_Mailbox"
206+
| "Island_W_Obelisk"
207+
| "Island_VolcanoBridge"
208+
| "Island_VolcanoShortcutOut";
209+
const ISLANDUPGRADEMAIL = new Set<IslandUpgradeMail>([
210+
"Island_FirstParrot",
211+
"Island_Turtle",
212+
"Island_UpgradeHouse",
213+
"Island_Resort",
214+
"Island_UpgradeTrader",
215+
"Island_UpgradeBridge",
216+
"Island_UpgradeParrotPlatform",
217+
"Island_UpgradeHouse_Mailbox",
218+
"Island_W_Obelisk",
219+
"Island_VolcanoBridge",
220+
"Island_VolcanoShortcutOut",
221+
]);
222+
223+
type IslandUpgradesRet = IslandUpgradeMail[];
224+
225+
function parseIslandUpgrades(player: any): IslandUpgradesRet {
226+
try {
227+
let islandUpgrades: IslandUpgradeMail[] = [];
228+
229+
const mailReceived = new Set<string>(
230+
GetListOrEmpty(player.mailReceived, "string"),
231+
);
232+
233+
for (const mail of Array.from(mailReceived)) {
234+
if (ISLANDUPGRADEMAIL.has(mail as IslandUpgradeMail))
235+
islandUpgrades.push(mail as IslandUpgradeMail);
236+
}
237+
238+
return islandUpgrades;
239+
} catch (error) {
240+
throw error;
241+
}
242+
}
243+
196244
/* ----------------------------- general parser ----------------------------- */
197245
const farmTypes = [
198246
"Standard",
@@ -217,6 +265,7 @@ export interface GeneralRet {
217265
gameVersion?: string;
218266
jojaMembership?: JojaRet;
219267
achievements?: AchievementsRet;
268+
islandUpgrades?: IslandUpgradesRet;
220269
}
221270

222271
export function parseGeneral(
@@ -249,6 +298,7 @@ export function parseGeneral(
249298
const experience = parseExperience(player);
250299
const jojaMembership = parseJoja(player);
251300
const achievements = parseAchievements(player);
301+
const islandUpgrades = parseIslandUpgrades(player);
252302

253303
return {
254304
name,
@@ -262,6 +312,7 @@ export function parseGeneral(
262312
gameVersion,
263313
jojaMembership,
264314
achievements,
315+
islandUpgrades,
265316
};
266317
} catch (e) {
267318
if (process.env.NODE_ENV === "development") {
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { DialogCard } from "@/components/cards/dialog-card";
2+
import { usePlayers } from "@/contexts/players-context";
3+
import upgrades from "@/data/island_upgrades.json";
4+
import { Inter } from "next/font/google";
5+
import Head from "next/head";
6+
import Image from "next/image";
7+
import { useEffect, useState } from "react";
8+
9+
const inter = Inter({ subsets: ["latin"] });
10+
11+
export default function IslandUpgrades() {
12+
const { activePlayer } = usePlayers();
13+
const [islandUpgrades, setIslandUpgrades] = useState<Set<String>>(new Set());
14+
15+
useEffect(() => {
16+
if (
17+
activePlayer &&
18+
activePlayer.general &&
19+
activePlayer.general.islandUpgrades
20+
) {
21+
setIslandUpgrades(new Set(activePlayer.general.islandUpgrades));
22+
}
23+
}, [activePlayer]);
24+
25+
let upgradeCards: JSX.Element[] = [];
26+
Object.entries(upgrades).forEach(([id, upgrade]) => {
27+
upgradeCards.push(
28+
<DialogCard
29+
key={id}
30+
title={upgrade.name}
31+
description={
32+
<div className="space-y-6 text-white">
33+
<section className="space-y-1">
34+
<h3 className="font-semibold text-white">Cost</h3>
35+
<span className="text-white flex items-center gap-1">
36+
<span
37+
className="align-middle"
38+
style={{ imageRendering: "pixelated" }}
39+
>
40+
{upgrade.cost}
41+
</span>
42+
<span
43+
className="w-5 h-5 align-middle relative flex items-center"
44+
style={{ imageRendering: "pixelated" }}
45+
>
46+
<Image
47+
src="https://stardewvalleywiki.com/mediawiki/images/5/54/Golden_Walnut.png"
48+
alt="Golden Walnut"
49+
fill
50+
className="object-contain"
51+
style={{ imageRendering: "pixelated" }}
52+
unoptimized
53+
/>
54+
</span>
55+
</span>
56+
</section>
57+
<section className="space-y-1">
58+
<h3 className="font-semibold text-white">Description</h3>
59+
<span className="block text-white">{upgrade.description}</span>
60+
</section>
61+
<section className="space-y-1">
62+
<h3 className="font-semibold text-white">Location</h3>
63+
<span className="block text-white">{upgrade.location}</span>
64+
</section>
65+
</div>
66+
}
67+
iconURL="https://stardewvalleywiki.com/mediawiki/images/5/54/Golden_Walnut.png"
68+
completed={activePlayer ? islandUpgrades.has(id) : false}
69+
_id={id}
70+
_type="island_upgrade"
71+
/>,
72+
);
73+
});
74+
75+
return (
76+
<>
77+
<Head>
78+
<meta
79+
name="title"
80+
content="Stardew Valley Ginger Island Upgrades Tracker | stardew.app"
81+
/>
82+
<title>
83+
Stardew Valley Ginger Island Upgrades Tracker | stardew.app
84+
</title>
85+
<meta
86+
name="description"
87+
content="Track and discover Ginger Island Upgrades in Stardew Valley. Keep tabs on the upgrades you've discovered and monitor your progress towards completing them all. Discover the locations and secrets of each upgrade and unlock valuable rewards on the island."
88+
/>
89+
<meta
90+
name="og:description"
91+
content="Track and discover Ginger Island Upgrades in Stardew Valley."
92+
/>
93+
<meta
94+
name="twitter:description"
95+
content="Track and discover Ginger Island Upgrades in Stardew Valley,"
96+
/>
97+
<meta name="keywords" content="stardew valley Ginger Island" />
98+
</Head>
99+
<main
100+
className={`flex min-h-screen border-neutral-200 dark:border-neutral-800 md:border-l ${inter.className} px-8 py-2`}
101+
>
102+
<div className="mx-auto mt-4 w-full space-y-4">
103+
<h1 className="ml-1 text-2xl font-semibold text-gray-900 dark:text-white">
104+
Island Upgrades Tracker{" "}
105+
{activePlayer
106+
? `(${islandUpgrades.size}/${upgradeCards.length})`
107+
: `(0/${upgradeCards.length})`}
108+
</h1>
109+
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
110+
{upgradeCards}
111+
</div>
112+
</div>
113+
</main>
114+
</>
115+
);
116+
}

0 commit comments

Comments
 (0)