ps99-api 2.3.3 โ 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release-on-main.yml +1 -2
- package/.idea/node-ps99-api.iml +1 -0
- package/.idea/runConfigurations/test_changing.xml +1 -1
- package/README.md +1 -1
- package/debug_currency.json +57 -0
- package/debug_goals.json +271 -0
- package/dist/ps99-api.d.ts +2 -0
- package/dist/ps99-api.js +4 -1
- package/dist/ps99-api.js.map +1 -1
- package/dist/request-client/axios.js +6 -1
- package/dist/request-client/axios.js.map +1 -1
- package/dist/responses/collection/achievement.d.ts +2 -0
- package/dist/responses/collection/guild-battle.d.ts +2 -4
- package/dist/responses/collection/index.d.ts +1 -0
- package/dist/responses/collection/index.js +15 -0
- package/dist/responses/collection/index.js.map +1 -1
- package/dist/responses/collection/rank.d.ts +1 -0
- package/dist/responses/collection/rarity.d.ts +1 -0
- package/dist/responses/collection/seed.d.ts +1 -0
- package/dist/responses/exists.d.ts +2 -0
- package/example-web/react/package-lock.json +1504 -1470
- package/example-web/react2/package-lock.json +3082 -2759
- package/example-web/react2/package.json +6 -1
- package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
- package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
- package/example-web/react2/public/index.html +34 -31
- package/example-web/react2/src/App.tsx +6 -9
- package/example-web/react2/src/assets/guild_placeholder.png +0 -0
- package/example-web/react2/src/components/AchievementsComponent.tsx +78 -30
- package/example-web/react2/src/components/BoostsComponent.tsx +18 -14
- package/example-web/react2/src/components/BoothsComponent.tsx +24 -22
- package/example-web/react2/src/components/BoxesComponent.tsx +46 -21
- package/example-web/react2/src/components/BuffsComponent.tsx +83 -13
- package/example-web/react2/src/components/CharmsComponent.tsx +47 -29
- package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
- package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
- package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
- package/example-web/react2/src/components/CurrencyComponent.tsx +59 -50
- package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +178 -11
- package/example-web/react2/src/components/EggsComponent.tsx +77 -44
- package/example-web/react2/src/components/EnchantsComponent.tsx +84 -34
- package/example-web/react2/src/components/FishingRodsComponent.tsx +38 -31
- package/example-web/react2/src/components/Footer.tsx +75 -18
- package/example-web/react2/src/components/FruitsComponent.tsx +41 -25
- package/example-web/react2/src/components/GenericFetchComponent.tsx +40 -22
- package/example-web/react2/src/components/GuildBattlesComponent.tsx +93 -65
- package/example-web/react2/src/components/Header.tsx +5 -37
- package/example-web/react2/src/components/HomePage.tsx +16 -16
- package/example-web/react2/src/components/HoverboardsComponent.tsx +25 -37
- package/example-web/react2/src/components/ImageComponent.tsx +255 -45
- package/example-web/react2/src/components/ItemCard.tsx +240 -0
- package/example-web/react2/src/components/LootboxesComponent.tsx +24 -16
- package/example-web/react2/src/components/MasteryComponent.tsx +93 -37
- package/example-web/react2/src/components/MerchantsComponent.tsx +46 -28
- package/example-web/react2/src/components/MiscItemsComponent.tsx +28 -26
- package/example-web/react2/src/components/PetsComponent.tsx +115 -46
- package/example-web/react2/src/components/PotionsComponent.tsx +53 -36
- package/example-web/react2/src/components/RandomEventsComponent.tsx +39 -35
- package/example-web/react2/src/components/RanksComponent.tsx +187 -71
- package/example-web/react2/src/components/RarityComponent.tsx +124 -13
- package/example-web/react2/src/components/RebirthsComponent.tsx +37 -26
- package/example-web/react2/src/components/SecretRoomsComponent.tsx +7 -13
- package/example-web/react2/src/components/SeedsComponent.tsx +45 -32
- package/example-web/react2/src/components/ShovelsComponent.tsx +23 -18
- package/example-web/react2/src/components/Sidebar.tsx +105 -0
- package/example-web/react2/src/components/SprinklersComponent.tsx +27 -19
- package/example-web/react2/src/components/Tooltip.tsx +36 -0
- package/example-web/react2/src/components/UltimatesComponent.tsx +29 -24
- package/example-web/react2/src/components/UpgradesComponent.tsx +99 -55
- package/example-web/react2/src/components/WateringCansComponent.tsx +23 -21
- package/example-web/react2/src/components/WorldsComponent.tsx +27 -24
- package/example-web/react2/src/components/XPPotionsComponent.tsx +29 -19
- package/example-web/react2/src/components/ZoneFlagsComponent.tsx +27 -23
- package/example-web/react2/src/components/ZonesComponent.tsx +56 -73
- package/example-web/react2/src/constants/collectionIcons.ts +29 -0
- package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
- package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
- package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
- package/example-web/react2/src/index.css +257 -0
- package/example-web/react2/src/index.tsx +2 -1
- package/example-web/react2/temp_model.rbxm +0 -0
- package/example-web/react2/webpack.config.js +103 -47
- package/package.json +11 -11
- package/ranks.json +1 -0
- package/repro_collection_fetch.ts +33 -0
- package/repro_image_fetch.ts +50 -0
- package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
- package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
- package/src/ps99-api.ts +9 -5
- package/src/request-client/axios.ts +6 -2
- package/src/responses/collection/achievement.ts +2 -0
- package/src/responses/collection/guild-battle.ts +2 -4
- package/src/responses/collection/index.ts +1 -0
- package/src/responses/collection/rank.ts +1 -0
- package/src/responses/collection/rarity.ts +1 -0
- package/src/responses/collection/seed.ts +1 -0
- package/src/responses/exists.ts +2 -0
- package/tsconfig.json +1 -1
- package/example-web/react2/public/service-worker.js +0 -63
|
@@ -1,35 +1,38 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import { GenericFetchComponent } from "./GenericFetchComponent";
|
|
4
3
|
|
|
5
4
|
const WorldComponent: React.FC<{
|
|
6
|
-
configData
|
|
5
|
+
configData: CollectionConfigData<"Worlds">;
|
|
7
6
|
}> = ({ configData }) => {
|
|
8
7
|
return (
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
9
|
+
<div style={{ textAlign: 'left', width: '100%' }}>
|
|
10
|
+
<p><strong>World #:</strong> {configData.WorldNumber}</p>
|
|
11
|
+
<p><strong>Spawn ID:</strong> {configData.SpawnId}</p>
|
|
12
|
+
<p><strong>Currency:</strong> {configData.WorldCurrency}</p>
|
|
13
|
+
<p><strong>Place ID:</strong> {configData.PlaceId}</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
{configData.AdditionalMusic && configData.AdditionalMusic.length > 0 && (
|
|
17
|
+
<div style={{ marginTop: '15px', width: '100%' }}>
|
|
18
|
+
<h4>Additional Music</h4>
|
|
19
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
|
|
20
|
+
{configData.AdditionalMusic.map((music, index) => (
|
|
21
|
+
<span key={index} style={{
|
|
22
|
+
background: '#e0f7fa',
|
|
23
|
+
color: '#006064',
|
|
24
|
+
padding: '5px 10px',
|
|
25
|
+
borderRadius: '15px',
|
|
26
|
+
fontSize: '0.9em',
|
|
27
|
+
fontWeight: 'bold'
|
|
28
|
+
}}>
|
|
29
|
+
{music}
|
|
30
|
+
</span>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
30
33
|
</div>
|
|
31
34
|
)}
|
|
32
|
-
|
|
35
|
+
</div>
|
|
33
36
|
);
|
|
34
37
|
};
|
|
35
38
|
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
5
5
|
|
|
6
6
|
const XPPotionsComponent: React.FC<{
|
|
7
|
-
configData
|
|
7
|
+
configData: CollectionConfigData<"XPPotions">;
|
|
8
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
9
12
|
return (
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
14
|
+
|
|
15
|
+
<div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
|
|
16
|
+
<ItemCard
|
|
17
|
+
id={configData.DisplayName}
|
|
18
|
+
amount={1}
|
|
19
|
+
label={configData.DisplayName}
|
|
20
|
+
itemData={{
|
|
21
|
+
icon: configData.Icon,
|
|
22
|
+
rarity: configData.Rarity,
|
|
23
|
+
name: configData.DisplayName
|
|
24
|
+
}}
|
|
25
|
+
rarityColor={rarityColor}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
|
|
30
|
+
<p style={{ marginBottom: '10px' }}>{configData.Desc}</p>
|
|
31
|
+
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
|
|
32
|
+
<span className="badge">Amount: {configData.Amount}</span>
|
|
33
|
+
<span className="badge" style={{ background: '#eee', color: '#333' }}>ID: {configData.ItemId}</span>
|
|
24
34
|
</div>
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
27
37
|
);
|
|
28
38
|
};
|
|
29
39
|
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
5
5
|
|
|
6
6
|
const ZoneFlagComponent: React.FC<{
|
|
7
|
-
configData
|
|
7
|
+
configData: CollectionConfigData<"ZoneFlags">;
|
|
8
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
9
12
|
return (
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
14
|
+
|
|
15
|
+
<div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
|
|
16
|
+
<ItemCard
|
|
17
|
+
id={configData.Name}
|
|
18
|
+
amount={1}
|
|
19
|
+
label={configData.Name}
|
|
20
|
+
itemData={{
|
|
21
|
+
icon: configData.Icon,
|
|
22
|
+
rarity: configData.Rarity,
|
|
23
|
+
name: configData.Name
|
|
24
|
+
}}
|
|
25
|
+
rarityColor={rarityColor}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
|
|
30
|
+
<p>{configData.Desc}</p>
|
|
31
|
+
<p style={{ fontWeight: 'bold', marginTop: '5px' }}>Duration: {configData.Duration}s</p>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
30
34
|
);
|
|
31
35
|
};
|
|
32
36
|
|
|
@@ -1,88 +1,71 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import { GenericFetchComponent } from "./GenericFetchComponent";
|
|
4
3
|
|
|
5
4
|
const ZoneComponent: React.FC<{
|
|
6
|
-
configData
|
|
5
|
+
configData: CollectionConfigData<"Zones">;
|
|
7
6
|
}> = ({ configData }) => {
|
|
8
7
|
return (
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<p>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</
|
|
8
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
9
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', width: '100%', textAlign: 'left' }}>
|
|
10
|
+
<p><strong>Zone #:</strong> {configData.ZoneNumber}</p>
|
|
11
|
+
<p><strong>World #:</strong> {configData.WorldNumber}</p>
|
|
12
|
+
<p><strong>Currency:</strong> {configData.Currency}</p>
|
|
13
|
+
<p><strong>Max Egg:</strong> {configData.MaximumAvailableEgg}</p>
|
|
14
|
+
{configData.Price && <p><strong>Price:</strong> {configData.Price}</p>}
|
|
15
|
+
{configData.GateHealth && <p><strong>Gate Health:</strong> {configData.GateHealth}</p>}
|
|
16
|
+
{configData.TeleportToZoneOnFall && (
|
|
17
|
+
<p><strong>Teleport on Fall:</strong> Yes</p>
|
|
18
|
+
)}
|
|
19
|
+
{configData.Ambience && (
|
|
20
|
+
<p><strong>Ambience ID:</strong> {configData.Ambience.SoundId}</p>
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
{configData.QuestsRequired && configData.QuestsRequired.length > 0 && (
|
|
25
|
+
<div style={{ width: '100%', marginTop: '20px' }}>
|
|
26
|
+
<h4>Quests Required</h4>
|
|
27
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', justifyContent: 'center' }}>
|
|
28
|
+
{configData.QuestsRequired.map((quest, index) => (
|
|
29
|
+
<div key={index} style={{
|
|
30
|
+
background: '#f0f0f0',
|
|
31
|
+
padding: '10px',
|
|
32
|
+
borderRadius: '8px',
|
|
33
|
+
border: '1px solid #ddd'
|
|
34
|
+
}}>
|
|
35
|
+
<strong>{quest.Type}</strong>: {quest.Amount}
|
|
36
|
+
</div>
|
|
37
|
+
))}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{configData.Lighting && (
|
|
43
|
+
<div style={{ width: '100%', marginTop: '20px', textAlign: 'left', background: '#f9f9f9', padding: '15px', borderRadius: '8px' }}>
|
|
44
|
+
<h4 style={{ textAlign: 'center' }}>Lighting Settings</h4>
|
|
45
|
+
<p>Brightness: {configData.Lighting.Brightness}</p>
|
|
46
|
+
<p>Clock Time: {configData.Lighting.ClockTime}</p>
|
|
47
|
+
|
|
48
|
+
{configData.Lighting.Bloom && (
|
|
49
|
+
<div style={{ marginTop: '10px' }}>
|
|
50
|
+
<strong>Bloom:</strong>
|
|
51
|
+
<span style={{ fontSize: '0.9em', marginLeft: '10px' }}>
|
|
52
|
+
Enabled: {configData.Lighting.Bloom.Enabled.toString()},
|
|
53
|
+
Intensity: {configData.Lighting.Bloom.Intensity}
|
|
54
|
+
</span>
|
|
38
55
|
</div>
|
|
39
56
|
)}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<h5>Bloom</h5>
|
|
48
|
-
<p>Enabled: {data.Lighting.Bloom.Enabled.toString()}</p>
|
|
49
|
-
<p>Intensity: {data.Lighting.Bloom.Intensity}</p>
|
|
50
|
-
<p>Size: {data.Lighting.Bloom.Size}</p>
|
|
51
|
-
<p>Threshold: {data.Lighting.Bloom.Threshold}</p>
|
|
52
|
-
</div>
|
|
53
|
-
)}
|
|
54
|
-
{data.Lighting.ColorCorrection && (
|
|
55
|
-
<div>
|
|
56
|
-
<h5>Color Correction</h5>
|
|
57
|
-
<p>
|
|
58
|
-
Enabled: {data.Lighting.ColorCorrection.Enabled.toString()}
|
|
59
|
-
</p>
|
|
60
|
-
<p>Saturation: {data.Lighting.ColorCorrection.Saturation}</p>
|
|
61
|
-
<p>Contrast: {data.Lighting.ColorCorrection.Contrast}</p>
|
|
62
|
-
<p>Brightness: {data.Lighting.ColorCorrection.Brightness}</p>
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
{data.Lighting.Sky && (
|
|
66
|
-
<div>
|
|
67
|
-
<h5>Sky</h5>
|
|
68
|
-
<p>Star Count: {data.Lighting.Sky.StarCount}</p>
|
|
69
|
-
<p>
|
|
70
|
-
Celestial Bodies Shown:{" "}
|
|
71
|
-
{data.Lighting.Sky.CelestialBodiesShown.toString()}
|
|
72
|
-
</p>
|
|
73
|
-
<p>Skybox Up: {data.Lighting.Sky.SkyboxUp}</p>
|
|
74
|
-
<p>Skybox Bk: {data.Lighting.Sky.SkyboxBk}</p>
|
|
75
|
-
<p>Skybox Dn: {data.Lighting.Sky.SkyboxDn}</p>
|
|
76
|
-
<p>Skybox Lf: {data.Lighting.Sky.SkyboxLf}</p>
|
|
77
|
-
<p>Skybox Rt: {data.Lighting.Sky.SkyboxRt}</p>
|
|
78
|
-
<p>Skybox Ft: {data.Lighting.Sky.SkyboxFt}</p>
|
|
79
|
-
</div>
|
|
80
|
-
)}
|
|
57
|
+
|
|
58
|
+
{configData.Lighting.Sky && (
|
|
59
|
+
<div style={{ marginTop: '10px' }}>
|
|
60
|
+
<strong>Sky:</strong>
|
|
61
|
+
<span style={{ fontSize: '0.9em', marginLeft: '10px' }}>
|
|
62
|
+
Stars: {configData.Lighting.Sky.StarCount}
|
|
63
|
+
</span>
|
|
81
64
|
</div>
|
|
82
65
|
)}
|
|
83
66
|
</div>
|
|
84
67
|
)}
|
|
85
|
-
|
|
68
|
+
</div>
|
|
86
69
|
);
|
|
87
70
|
};
|
|
88
71
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const COLLECTION_ICONS: Record<string, string> = {
|
|
2
|
+
Pets: "๐ฑ",
|
|
3
|
+
Eggs: "๐ฅ",
|
|
4
|
+
Enchants: "๐",
|
|
5
|
+
Potions: "๐งช",
|
|
6
|
+
Items: "๐",
|
|
7
|
+
Hoverboards: "๐น",
|
|
8
|
+
Booths: "๐ช",
|
|
9
|
+
Ultimates: "๐",
|
|
10
|
+
MiscItems: "๐ฆ",
|
|
11
|
+
Keys: "๐๏ธ",
|
|
12
|
+
Tickets: "๐๏ธ",
|
|
13
|
+
Charms: "๐งฟ",
|
|
14
|
+
Fruits: "๐",
|
|
15
|
+
Seeds: "๐ฑ",
|
|
16
|
+
Flags: "๐ฉ",
|
|
17
|
+
Glitch: "๐พ",
|
|
18
|
+
Achievements: "๐",
|
|
19
|
+
Boosts: "๐",
|
|
20
|
+
Upgrades: "โฌ๏ธ",
|
|
21
|
+
Relics: "๐ฟ",
|
|
22
|
+
Ranks: "๐",
|
|
23
|
+
RandomEvents: "๐ฒ",
|
|
24
|
+
SecretRooms: "๐",
|
|
25
|
+
Zones: "uq",
|
|
26
|
+
World: "๐",
|
|
27
|
+
Clan: "๐ก๏ธ",
|
|
28
|
+
Mastery: "๐",
|
|
29
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
|
|
2
|
+
import { PetSimulator99API, Collections, CollectionName } from "ps99-api";
|
|
3
|
+
|
|
4
|
+
const api = new PetSimulator99API();
|
|
5
|
+
|
|
6
|
+
type CollectionDataCache = Partial<Record<string, Collections[]>>;
|
|
7
|
+
|
|
8
|
+
interface CollectionDataContextType {
|
|
9
|
+
data: CollectionDataCache;
|
|
10
|
+
fetchCollection: (collectionName: CollectionName) => Promise<void>;
|
|
11
|
+
isLoading: (collectionName: string) => boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const CollectionDataContext = createContext<CollectionDataContextType | undefined>(undefined);
|
|
15
|
+
|
|
16
|
+
export const CollectionDataProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
17
|
+
const [data, setData] = useState<CollectionDataCache>({});
|
|
18
|
+
const [loadingStates, setLoadingStates] = useState<Record<string, boolean>>({});
|
|
19
|
+
|
|
20
|
+
const fetchCollection = useCallback(async (collectionName: CollectionName) => {
|
|
21
|
+
// If data already exists, don't fetch again unless force refresh is needed (not implemented yet)
|
|
22
|
+
if (data[collectionName]) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (loadingStates[collectionName]) return; // Already loading
|
|
27
|
+
|
|
28
|
+
setLoadingStates(prev => ({ ...prev, [collectionName]: true }));
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
console.log(`[CollectionDataContext] Fetching ${collectionName}...`);
|
|
32
|
+
const result = await api.getCollection(collectionName);
|
|
33
|
+
if (result.status === "ok") {
|
|
34
|
+
setData(prev => ({ ...prev, [collectionName]: result.data as Collections[] }));
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Status error fetching ${collectionName}:`, result);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`Failed to fetch collection ${collectionName}`, error);
|
|
40
|
+
} finally {
|
|
41
|
+
setLoadingStates(prev => ({ ...prev, [collectionName]: false }));
|
|
42
|
+
}
|
|
43
|
+
}, [data, loadingStates]);
|
|
44
|
+
|
|
45
|
+
const isLoading = useCallback((collectionName: string) => {
|
|
46
|
+
return !!loadingStates[collectionName];
|
|
47
|
+
}, [loadingStates]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<CollectionDataContext.Provider value={{ data, fetchCollection, isLoading }}>
|
|
51
|
+
{children}
|
|
52
|
+
</CollectionDataContext.Provider>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const useCollectionData = () => {
|
|
57
|
+
const context = useContext(CollectionDataContext);
|
|
58
|
+
if (!context) {
|
|
59
|
+
throw new Error('useCollectionData must be used within a CollectionDataProvider');
|
|
60
|
+
}
|
|
61
|
+
return context;
|
|
62
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useExpandableList = (totalItems: number, initialExpanded: boolean = false) => {
|
|
4
|
+
const [expandedIndices, setExpandedIndices] = useState<Set<number>>(new Set());
|
|
5
|
+
|
|
6
|
+
// Initialize state when totalItems changes or on mount
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (initialExpanded) {
|
|
9
|
+
setExpandedIndices(new Set(Array.from({ length: totalItems }, (_, i) => i)));
|
|
10
|
+
} else {
|
|
11
|
+
setExpandedIndices(new Set());
|
|
12
|
+
}
|
|
13
|
+
}, [totalItems, initialExpanded]);
|
|
14
|
+
|
|
15
|
+
const toggle = useCallback((index: number) => {
|
|
16
|
+
setExpandedIndices(prev => {
|
|
17
|
+
const next = new Set(prev);
|
|
18
|
+
if (next.has(index)) {
|
|
19
|
+
next.delete(index);
|
|
20
|
+
} else {
|
|
21
|
+
next.add(index);
|
|
22
|
+
}
|
|
23
|
+
return next;
|
|
24
|
+
});
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
const expandAll = useCallback(() => {
|
|
28
|
+
setExpandedIndices(new Set(Array.from({ length: totalItems }, (_, i) => i)));
|
|
29
|
+
}, [totalItems]);
|
|
30
|
+
|
|
31
|
+
const collapseAll = useCallback(() => {
|
|
32
|
+
setExpandedIndices(new Set());
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
const isExpanded = useCallback((index: number) => expandedIndices.has(index), [expandedIndices]);
|
|
36
|
+
|
|
37
|
+
return { expandedIndices, toggle, expandAll, collapseAll, isExpanded };
|
|
38
|
+
};
|