Skip to content

Commit

Permalink
feat: added bid status endpoint, insert strategy
Browse files Browse the repository at this point in the history
  • Loading branch information
Kozjar committed Dec 3, 2023
1 parent 1228732 commit eb6c08c
Show file tree
Hide file tree
Showing 18 changed files with 199 additions and 42 deletions.
9 changes: 9 additions & 0 deletions src/assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
"primaryColor": "Основной цвет",
"backgroundTone": "Тон фона"
},
"integrationCommon": {
"title": "Общее",
"insertStrategyLabel": "Автодобавление ставок",
"insertStrategy": {
"force": "Всегда",
"match": "При полном совпадении имени",
"none": "Никогда"
}
},
"twitch": {
"openRewards": "Включить награды",
"closeRewards": "Скрыть награды",
Expand Down
1 change: 0 additions & 1 deletion src/components/Changelog/ChangelogModal/ChangelogModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FC, useEffect, useMemo, useState } from 'react';
import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
import dayjs from 'dayjs';

import { getUpdates } from '@utils/changelog.tsx';

Expand Down
50 changes: 50 additions & 0 deletions src/components/IntegrationPage/IntegrationCommon/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Controller, useFormContext } from 'react-hook-form';
import { FormControl, MenuItem, Select, Typography, FormGroup } from '@mui/material';

import SettingsGroupTitle from '@components/SettingsGroupTitle/SettingsGroupTitle.tsx';
import { InsertStrategy } from '@enums/settings.enum.ts';

const IntegrationCommon = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'settings.integrationCommon' });
const { control } = useFormContext();

return (
<>
<SettingsGroupTitle title={t('title')} />
<FormGroup row className='auc-settings-row'>
<Typography variant='body1' className='MuiFormControlLabel-label'>
{t('insertStrategyLabel')}
</Typography>
<Controller
name='insertStrategy'
control={control}
render={({ field: { onChange, value, onBlur } }) => (
<FormControl>
<Select
value={value}
onChange={(e) => {
onChange(e.target.value);
onBlur();
}}
>
<MenuItem value={InsertStrategy.Force}>
<Typography>{t('insertStrategy.force')}</Typography>
</MenuItem>
<MenuItem value={InsertStrategy.Match}>
<Typography>{t('insertStrategy.match')}</Typography>
</MenuItem>
<MenuItem value={InsertStrategy.None}>
<Typography>{t('insertStrategy.none')}</Typography>
</MenuItem>
</Select>
</FormControl>
)}
/>
</FormGroup>
</>
);
};

export default IntegrationCommon;
14 changes: 9 additions & 5 deletions src/components/IntegrationPage/IntegrationPage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { FC, useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useForm } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import { initialState, saveSettings } from '@reducers/AucSettings/AucSettings.ts';
import { getDirtyValues } from '@utils/common.utils.ts';
import { SettingsForm } from '@models/settings.model.ts';
import { RootState } from '@reducers';
import IntegrationCommon from '@components/IntegrationPage/IntegrationCommon';

import PageContainer from '../PageContainer/PageContainer';

Expand Down Expand Up @@ -58,10 +59,13 @@ const IntegrationPage: FC = () => {

return (
<PageContainer title={t('settings.integrations')} className='integration-page'>
<form className='settings'>
<TwitchIntegration control={control} />
<DaIntegration control={control} />
</form>
<FormProvider {...formMethods}>
<form className='settings'>
<IntegrationCommon />
<TwitchIntegration control={control} />
<DaIntegration control={control} />
</form>
</FormProvider>
</PageContainer>
);
};
Expand Down
8 changes: 2 additions & 6 deletions src/components/SettingsPage/AucSettings/AucSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { FC, useEffect } from 'react';
import { FormGroup, IconButton, MenuItem, Select, Typography, Grid, FormControl } from '@mui/material';
import { FormGroup, IconButton, Typography } from '@mui/material';
import ReplayIcon from '@mui/icons-material/Replay';
import { Control, Controller, UseFormReturn } from 'react-hook-form';
import ArrowUpwardOutlinedIcon from '@mui/icons-material/ArrowUpwardOutlined';
import ArrowDownwardOutlinedIcon from '@mui/icons-material/ArrowDownwardOutlined';
import { useSelector } from 'react-redux';
import { Control, UseFormReturn } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import ImageLinkInput from '@components/Form/ImageLinkInput/ImageLinkInput';
import SettingsGroupTitle from '@components/SettingsGroupTitle/SettingsGroupTitle';
import { RootState } from '@reducers';
import FormSwitch from '@components/Form/FormSwitch/FormSwitch';
import FormInput from '@components/Form/FormInput/FormInput';
import ImagePresetsInput from '@components/Form/ImagePresetsInput/ImagePresetsInput';
Expand Down
11 changes: 9 additions & 2 deletions src/components/SettingsPage/AucSettings/BidsSortSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,16 @@ const BidsSortSelect: FC<BidsSortSelectProps> = ({ control }) => {
control={control}
name='purchaseSort'
defaultValue={purchaseSort}
render={({ field: { onChange, value } }) => (
render={({ field: { onChange, onBlur, value } }) => (
<FormControl size='small'>
<Select className='auc-settings-option' value={value} onChange={(e) => onChange(e.target.value)}>
<Select
className='auc-settings-option'
value={value}
onChange={(e) => {
onChange(e.target.value);
onBlur();
}}
>
<MenuItem value={0}>
<Grid container>
<Typography>{t('settings.auc.dateSort')}</Typography>
Expand Down
6 changes: 6 additions & 0 deletions src/enums/settings.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum InsertStrategy {
Force = 'force',
Match = 'match',
None = 'none',
Auto = 'auto',
}
7 changes: 7 additions & 0 deletions src/models/publicApi.model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ namespace PublicApi {
query: LotQuery;
lot: LotUpdateData;
}

interface BidStatus {
status: 'pending' | 'processed' | 'rejected' | 'notFound';
lot?: {
id: string;
};
}
}
4 changes: 2 additions & 2 deletions src/models/purchase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export interface PurchaseSortOption {
}

export enum PurchaseStatusEnum {
Processed = 'Processed',
Deleted = 'Deleted',
Processed = 'processed',
Deleted = 'deleted',
}
3 changes: 3 additions & 0 deletions src/models/settings.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { InsertStrategy } from '@enums/settings.enum.ts';

import { TwitchRewardPresetDto } from './user.model';

export interface TwitchIntegration {
Expand Down Expand Up @@ -35,6 +37,7 @@ export interface Settings extends TwitchIntegration, DonationSettings {
luckyWheelEnabled: boolean;
luckyWheelSelectBet: boolean;
showUpdates: boolean;
insertStrategy: InsertStrategy;

// Appearance
primaryColor: string;
Expand Down
17 changes: 11 additions & 6 deletions src/pages/auction/AucActions/AucActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ const AucActions: React.FC = () => {
<Button startIcon={<AttachMoneyIcon />} />
</Link>
</Tooltip>
</ButtonGroup>
</Grid>

<Grid item>
<ButtonGroup size='large' className='actions-group'>
<Tooltip title={t('auc.downloadMarbles')}>
<Button onClick={downloadMarbles} startIcon={<DownloadIcon />} />
</Tooltip>
<Tooltip title={t('auc.saveLoad')}>
<Button onClick={handleRestoreOpen} startIcon={<SaveIcon />} />
</Tooltip>
<Tooltip title={t('auc.sendBugReport')}>
<Link
target='_blank'
Expand All @@ -141,12 +152,6 @@ const AucActions: React.FC = () => {
<Button startIcon={<PestControlIcon />} />
</Link>
</Tooltip>
<Tooltip title={t('auc.downloadMarbles')}>
<Button onClick={downloadMarbles} startIcon={<DownloadIcon />} />
</Tooltip>
<Tooltip title={t('auc.saveLoad')}>
<Button onClick={handleRestoreOpen} startIcon={<SaveIcon />} />
</Tooltip>
<Tooltip title={t('auc.clearAll')}>
<Button onClick={handleResetSlots} startIcon={<DeleteSweepIcon />} />
</Tooltip>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/auction/Slot/DroppableSlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const DroppableSlot: React.FC<DroppableSlotProps> = ({ index, slot }) => {

slotNamesMap.set(message, id);
dispatch(setSlotAmount({ id, amount: Number(amount) + getMarblesAmount(addedCost, !amount) }));
dispatch(logPurchase({ ...redemption, status: PurchaseStatusEnum.Processed, target: id.toString() }));
dispatch(logPurchase({ ...redemption, status: PurchaseStatusEnum.Processed, target: id }));
dispatch(removePurchase(redemptionId));
dispatch(setDraggedRedemption(null));
dispatch(updateExistBids);
Expand Down
10 changes: 7 additions & 3 deletions src/reducers/AucSettings/AucSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getUserData, updateSettings } from '@api/userApi.ts';
import { GetUserDto } from '@models/user.model.ts';
import { AucSettingsDto, SettingsForm } from '@models/settings.model.ts';
import { COLORS } from '@constants/color.constants.ts';
import { InsertStrategy } from '@enums/settings.enum.ts';

import { setUserState } from '../User/User';
import { RootState } from '../index';
Expand Down Expand Up @@ -57,6 +58,7 @@ export const initialState: AucSettingsState = {
incrementTime: 30,
rewardPresets: [],
showUpdates: true,
insertStrategy: InsertStrategy.Match,
primaryColor: COLORS.THEME.PRIMARY,
backgroundTone: COLORS.THEME.BACKGROUND_TONE,
},
Expand All @@ -69,9 +71,6 @@ const aucSettingsSlice = createSlice({
initialState,
reducers: {
setAucSettings(state, action: PayloadAction<Partial<AucSettingsDto>>): void {
if (action.payload.isMinTimeActive != null)
localStorage.setItem('isMinTimeActive', String(action.payload.isMinTimeActive));
if (action.payload.minTime != null) localStorage.setItem('minTime', String(action.payload.minTime));
state.settings = mergewith(state.settings, action.payload, mergeCheck);
},
setCompact(state, action: PayloadAction<boolean>): void {
Expand Down Expand Up @@ -115,6 +114,11 @@ export const loadUserData = async (dispatch: ThunkDispatch<RootState, {}, Action
activeSettingsPresetId,
}),
);
if (localStorage.getItem('isMinTimeActive') || localStorage.getItem('minTime')) {
dispatch(saveSettings({ minTime: minTime ? Number(minTime) : activeSettings.minTime, isMinTimeActive }));
localStorage.removeItem('isMinTimeActive');
localStorage.removeItem('minTime');
}
validateIntegrations(dispatch);

return user;
Expand Down
33 changes: 22 additions & 11 deletions src/reducers/Purchases/Purchases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AlertTypeEnum } from '@models/alert.model.ts';
import { PurchaseStatusEnum } from '@models/purchase.ts';
import bidUtils from '@utils/bid.utils.ts';
import slotNamesMap, { SlotNamesMap } from '@services/SlotNamesMap';
import { InsertStrategy } from '@enums/settings.enum.ts';

import { RootState } from '../index';
import { addBid, createSlotFromPurchase } from '../Slots/Slots';
Expand All @@ -21,6 +22,7 @@ export interface Purchase {
rewardId?: string;
isDonation?: boolean;
investorId?: string;
insertStrategy?: InsertStrategy;
}

export interface PurchaseLog extends Purchase {
Expand Down Expand Up @@ -107,22 +109,31 @@ export const fastAddBid =
};

export const processRedemption =
(redemption: Purchase) =>
(bid: Purchase) =>
(dispatch: ThunkDispatch<RootState, {}, Action>, getState: () => RootState): void => {
const {
aucSettings: {
settings: { luckyWheelEnabled, alwaysAddNew },
},
aucSettings: { settings },
} = getState();
const similarSlotId = slotNamesMap.get(redemption.message);
const insertStrategy =
!bid.insertStrategy || bid.insertStrategy === InsertStrategy.Auto ? settings.insertStrategy : bid.insertStrategy;

if (similarSlotId && !luckyWheelEnabled) {
dispatch(fastAddBid(redemption, similarSlotId));
} else if (alwaysAddNew) {
dispatch(createSlotFromPurchase(redemption));
} else {
dispatch(addPurchase(redemption));
if (insertStrategy === InsertStrategy.None || settings.luckyWheelEnabled) {
dispatch(addPurchase(bid));
return;
}

const similarSlotId = slotNamesMap.get(bid.message);
if (similarSlotId) {
dispatch(fastAddBid(bid, similarSlotId));
return;
}

if (insertStrategy === InsertStrategy.Match) {
dispatch(addPurchase(bid));
return;
}

dispatch(createSlotFromPurchase(bid));
};

export const updateExistBids = (dispatch: ThunkDispatch<RootState, {}, Action>, getState: () => RootState): void => {
Expand Down
6 changes: 3 additions & 3 deletions src/reducers/Slots/Slots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const slotsSlice = createSlice({
amount:
requestLot.amountChange != null && lot.amount
? lot.amount + requestLot.amountChange
: lot.amount ?? requestLot.amount ?? null,
: requestLot.amount ?? lot.amount ?? null,
});

state.slots = state.slots.map((lot) => (compare(lot) ? updateLot(lot) : lot));
Expand Down Expand Up @@ -197,7 +197,7 @@ export const createSlotFromPurchase =
aucSettings: { settings },
slots: { slots },
} = getState();
const { id, message: name, cost, isDonation } = bid;
const { message: name, cost, isDonation } = bid;
// eslint-disable-next-line no-plusplus
const newSlot: Slot = {
id: Math.random().toString(),
Expand All @@ -213,7 +213,7 @@ export const createSlotFromPurchase =

updateSlotPosition(updatedSlots, updatedSlots.length - 1);
dispatch(setSlots(sortSlots(updatedSlots)));
dispatch(logPurchase({ ...bid, status: PurchaseStatusEnum.Processed, target: id.toString(), cost }, true));
dispatch(logPurchase({ ...bid, status: PurchaseStatusEnum.Processed, target: newSlot.id, cost }, true));
};

interface BidHandleOptions {
Expand Down
29 changes: 29 additions & 0 deletions src/reducers/socketIo/socketIo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { ThunkAction } from 'redux-thunk';
import { getSocketIOUrl } from '@utils/url.utils.ts';
import { Slot, SlotResponse } from '@models/slot.model.ts';
import { mergeLot } from '@reducers/Slots/Slots.ts';
import { addedBidsMap, Purchase, PurchaseLog } from '@reducers/Purchases/Purchases.ts';
import { PurchaseStatusEnum } from '@models/purchase.ts';

import { RootState } from '../index';

Expand Down Expand Up @@ -39,6 +41,30 @@ const handleLotsRequest = (lots: Slot[], callback: SocketCallback<SlotResponse[]
callback(lots.map(({ extra, ...lot }) => lot));
};

const handleBidStatusRequest = (
bidId: string,
history: PurchaseLog[],
bids: Purchase[],
callback: SocketCallback<PublicApi.BidStatus>,
) => {
const lotId = addedBidsMap.get(bidId);
if (lotId) {
return callback({ status: 'processed', lot: { id: lotId } });
}

const historicData = history.find(({ id }) => id === bidId);
if (historicData?.status === PurchaseStatusEnum.Deleted) {
return callback({ status: 'rejected' });
}

const queueData = bids.find(({ id }) => id === bidId);
if (queueData) {
return callback({ status: 'pending' });
}

callback({ status: 'notFound' });
};

const handleLotUpdate = (request: PublicApi.LotUpdateRequest, dispatch: ThunkDispatch<RootState, any, any>) => {
dispatch(mergeLot(request));
};
Expand All @@ -47,6 +73,9 @@ export const connectToSocketIo: ThunkAction<void, RootState, {}, Action> = (disp
const globalSocket = io(`${getSocketIOUrl()}`, { query: { cookie: document.cookie } });
globalSocket.on('requestLots', (_, callback) => handleLotsRequest(getState().slots.slots, callback));
globalSocket.on('updateLot', (data) => handleLotUpdate(data, dispatch));
globalSocket.on('requestBidStatus', (bidId, callback) =>
handleBidStatusRequest(bidId, getState().purchases.history, getState().purchases.purchases, callback),
);

const twitchSocket = io(`${getSocketIOUrl()}/twitch`, { query: { cookie: document.cookie } });
const daSocket = io(`${getSocketIOUrl()}/da`, { query: { cookie: document.cookie } });
Expand Down
Loading

0 comments on commit eb6c08c

Please sign in to comment.