MoonMiners/src/app/Components/StakingSource.tsx
Joseph Ferano 9b753cf103 Porting database from SQLite to PostgreSQL
- New Postgres table schemas
- Using Stored Procedures with transactions that validate business logic
- User Ids now use UUID
- Updated and simplified all endpoints to call the stored procedures

Notes: There are still a few things missing that broke because of the migration,
in particular, because we moved a lot of the business logic into the database,
we now require that certain data that lived in the game-config.json to be
present in the database as well, to prevent cheating and truly have a single
source of truth.
2023-03-30 14:13:30 +07:00

197 lines
6.0 KiB
TypeScript

import React, { useState, useEffect } from "react";
import { IInventoryItem, IStakingSource, IStake, IOption } from "typings";
import CardLayout from "../Layouts/CardLayout";
import {
calculateRemainingTime,
getObjectFromArray,
} from "../../utils/helpers";
import SelectDropdown from "./SelectDropdown";
const StakingSource = (props: {
stakingSource: IStakingSource;
inventoryItems: IInventoryItem[] | null | undefined;
claimStake: (stakingEventId: number) => void;
startStake: (
inventoryItemId: number,
storeItemId: string,
wellId: number
) => void;
}) => {
const [activeStakes, setActiveStakes] = useState<IStake[]>([]);
const [selectedItemId, setSelectedItemId] = useState<number | null>(null);
const [selectedStoreItemId, setSelectedStoreItemId] = useState<string | null>(
null
);
const [selectedWellId, setSelectedWellId] = useState<number | null>(null);
// Check if claimable every second
useEffect(() => {
const updatedActiveStakes = props.stakingSource.activeStakes.map(
(stake) => {
const remainingTime = calculateRemainingTime(
stake.startTime,
stake.durationInMins
);
const obj = {
...stake,
remainingTime: remainingTime,
};
return obj;
}
);
const intervalId = setInterval(() => {
setActiveStakes(updatedActiveStakes);
}, 1000);
return () => {
clearInterval(intervalId);
};
});
const handleStartMining = () => {
if (selectedItemId && selectedWellId && selectedStoreItemId) {
props.startStake(selectedItemId, selectedStoreItemId, selectedWellId);
}
};
const handleClaim = (stakingEventId: number) => {
props.claimStake(stakingEventId);
};
const handleSelectChange = (
wellId: string,
itemId: number,
storeitemId: string
) => {
setSelectedWellId(Number(wellId));
setSelectedStoreItemId(storeitemId);
setSelectedItemId(itemId);
};
const Countdown = (props: { remainingTime: number }) => {
const hours = Math.floor((props.remainingTime / (1000 * 60 * 60)) % 24)
.toString()
.padStart(2, "0");
const minutes = Math.floor((props.remainingTime / (1000 * 60)) % 60)
.toString()
.padStart(2, "0");
const seconds = Math.floor((props.remainingTime / 1000) % 60)
.toString()
.padStart(2, "0");
return (
<span className="font-bold">
{hours} : {minutes} : {seconds}
</span>
);
};
const RenderButtonOrCountdown = (props: { stake: IStake }) => {
if (!props.stake.remainingTime) return <p>Error</p>;
if (props.stake.remainingTime <= 0) {
return (
<button
onClick={() => handleClaim(props.stake.id)}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold text-center"
>
Claim
</button>
);
} else {
return (
// I'll fix it
<Countdown remainingTime={props.stake.remainingTime} />
);
}
};
const isActive = (item: IInventoryItem): boolean => {
return props.stakingSource.activeStakes.some(
(obj) => obj.inventoryItemId === item.id
);
};
return (
<CardLayout>
<h3 className="text-xl font-bold mb-2">{props.stakingSource.name}</h3>
<p className="text-sm">{props.stakingSource.description}</p>
<div className="flex gap-4">
<div className="flex-1">
<p className="font-bold mt-4 mb-2">Active Drills</p>
{activeStakes &&
activeStakes.map((stake, id) => (
<div
key={id}
className="border border-white/20 rounded-xl p-3 mb-2 bg-black/20"
>
<p>
<span className="font-bold">Drill: </span>
{props.inventoryItems &&
getObjectFromArray(
props.inventoryItems,
"id",
stake.inventoryItemId
)?.storeItem.name}
</p>
<p>
<span className="font-bold">Resource: </span>
{stake.resourceType}
</p>
<p className="mb-2">
<span className="font-bold">Yield: </span>
{stake.stakeAmount}
</p>
<RenderButtonOrCountdown stake={stake} />
</div>
))}
</div>
<div className="flex-1">
<p className="font-bold mt-4 mb-2">Inactive Drills</p>
{props.inventoryItems &&
props.inventoryItems.map(
(item, id) =>
!isActive(item) && (
<div
key={id}
className="border border-white/20 rounded-xl p-3 mb-2 bg-black/20"
>
<p className="font-bold">{item.storeItem.name}</p>
<p className="mt-2">Select Resource</p>
<div className="flex">
<SelectDropdown
options={props.stakingSource.resourceWells.map(
(well): IOption => ({
value: well.id,
label: well.resourceType,
})
)}
onChange={(value) =>
handleSelectChange(value, item.id, item.storeItem.id)
}
isActive={selectedItemId === item.id}
/>
{selectedItemId === item.id ? (
<button
onClick={handleStartMining}
className="bg-slate-100 text-slate-900 px-4 py-2 rounded-lg font-bold w-28 text-center ml-2"
>
Activate
</button>
) : (
<></>
)}
</div>
</div>
)
)}
</div>
</div>
</CardLayout>
);
};
export default StakingSource;