Skip to content

Commit

Permalink
Merge pull request #540 from DeXter-on-Radix/pairs-toggle-4
Browse files Browse the repository at this point in the history
Pairs toggle
  • Loading branch information
dcts authored Aug 4, 2024
2 parents 88916b0 + 628cd61 commit 18c1a99
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 12 deletions.
62 changes: 59 additions & 3 deletions src/app/components/AccountHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ import Papa from "papaparse";
import HoverGradientButton from "./HoverGradientButton";
import { twMerge } from "tailwind-merge";

import {
setHideOtherPairs,
selectCombinedOrderHistory,
selectCombinedOpenOrders,
} from "../state/accountHistorySlice";

function createOrderReceiptAddressLookup(
pairsList: PairInfo[]
): Record<string, string> {
Expand Down Expand Up @@ -201,19 +207,35 @@ function DisplayTable() {
);
const openOrders = useAppSelector(selectOpenOrders);
const orderHistory = useAppSelector(selectOrderHistory);
const dispatch = useAppDispatch();
const hideOtherPairs = useAppSelector(
(state) => state.accountHistory.hideOtherPairs
);
const combinedOrderHistory = useAppSelector(selectCombinedOrderHistory);
const combinedOpenOrders = useAppSelector(selectCombinedOpenOrders);

const handleToggleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
dispatch(setHideOtherPairs(e.target.checked));
};

const tableToShow = useMemo(() => {
switch (selectedTable) {
case Tables.OPEN_ORDERS:
const filteredRowsForOpenOrders = hideOtherPairs
? openOrders
: combinedOpenOrders;
return {
headers: headers[Tables.OPEN_ORDERS],
rows: <OpenOrdersRows data={openOrders} />,
rows: <OpenOrdersRows data={filteredRowsForOpenOrders} />,
};

case Tables.ORDER_HISTORY:
const filteredRowsForOrderHistory = hideOtherPairs
? orderHistory
: combinedOrderHistory;
return {
headers: headers[Tables.ORDER_HISTORY],
rows: <OrderHistoryRows data={orderHistory} />,
rows: <OrderHistoryRows data={filteredRowsForOrderHistory} />,
};

default:
Expand All @@ -222,10 +244,44 @@ function DisplayTable() {
rows: <></>,
};
}
}, [openOrders, orderHistory, selectedTable]);
}, [
openOrders,
orderHistory,
selectedTable,
hideOtherPairs,
combinedOrderHistory,
combinedOpenOrders,
]);

return (
<div className="overflow-x-auto scrollbar-none">
<div className="flex flex-col md:items-end xs:items-start">
<label className="label cursor-pointer">
<input
type="checkbox"
className="peer hidden"
checked={hideOtherPairs}
role="switch"
onChange={handleToggleChange}
/>
<span
className={`relative inline-flex items-center w-8 h-4 rounded-lg transition-colors duration-300 ease-in-out xs:ml-4 ${
hideOtherPairs ? "bg-content-dark" : "border border-white"
}`}
>
<span
className={`absolute left-0.5 top-0.3 w-3 h-3 rounded-lg shadow-md transform transition-transform duration-300 ease-in-out ${
hideOtherPairs
? "translate-x-4 bg-dexter-green"
: "translate-x-0 bg-gray-200"
}`}
/>
</span>
<span className="label-text text-xs md:pl-0 mr-16 xs:ml-2">
Hide other pairs
</span>
</label>
</div>
<table className="table table-zebra table-xs !mt-0 mb-16 w-full max-w-[100%]">
<thead>
<tr className="h-12">
Expand Down
119 changes: 111 additions & 8 deletions src/app/state/accountHistorySlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export interface AccountHistoryState {
selectedTable: Tables;
tables: Tables[];
selectedOrdersToCancel: Record<string, Order>; // the key is `${orderRecieptAddress}_${nftRecieptId}`
hideOtherPairs: boolean;
orderHistoryAllPairs: adex.OrderReceipt[];
}

// INITIAL STATE
Expand All @@ -40,6 +42,8 @@ const initialState: AccountHistoryState = {
selectedTable: Tables.OPEN_ORDERS,
tables: Object.values(Tables),
selectedOrdersToCancel: {},
hideOtherPairs: true,
orderHistoryAllPairs: [],
};

// ASYNC THUNKS
Expand All @@ -65,6 +69,40 @@ export const fetchAccountHistory = createAsyncThunk<
}
});

export const fetchAccountHistoryAllPairs = createAsyncThunk<
adex.OrderReceipt[],
undefined,
{ state: RootState }
>("accountHistory/fetchAccountHistoryAllPairs", async (_, thunkAPI) => {
const state = thunkAPI.getState();
const pairAddresses = state.pairSelector.pairsList.map(
(pairInfo) => pairInfo.address
);

const account = state.radix?.walletData.accounts[0]?.address || "";

const orderHistoryPromises = pairAddresses.map((pairAddress) =>
adex.getAccountOrders(account, pairAddress, 0)
);

const apiResponses = await Promise.all(orderHistoryPromises);
const allOrders: adex.OrderReceipt[] = [];

apiResponses.forEach((apiResponse) => {
const plainApiResponse: SdkResult = JSON.parse(JSON.stringify(apiResponse));

if (plainApiResponse.status === "SUCCESS") {
allOrders.push(...plainApiResponse.data.orders);
} else {
console.error(
`Error fetching orders for pair: ${plainApiResponse.message}`
);
}
});

return allOrders;
});

export const batchCancel = createAsyncThunk<
Order[], // return value
Order[], // input
Expand Down Expand Up @@ -170,21 +208,35 @@ export const accountHistorySlice = createSlice({
resetSelectedOrdersToCancel: (state) => {
state.selectedOrdersToCancel = {};
},
setHideOtherPairs: (state, action: PayloadAction<boolean>) => {
state.hideOtherPairs = action.payload;
},
},

extraReducers: (builder) => {
builder.addCase(fetchAccountHistory.fulfilled, (state, action) => {
state.orderHistory = action.payload.data.orders;
});
builder.addCase(batchCancel.fulfilled, (state) => {
state.selectedOrdersToCancel = {};
});
builder
.addCase(fetchAccountHistory.fulfilled, (state, action) => {
state.orderHistory = action.payload.data.orders;
})
.addCase(batchCancel.fulfilled, (state) => {
state.selectedOrdersToCancel = {};
})
.addCase(fetchAccountHistoryAllPairs.fulfilled, (state, action) => {
state.orderHistoryAllPairs = action.payload;
})
.addCase(fetchAccountHistoryAllPairs.rejected, (state, action) => {
console.error("Failed to fetch account history:", action.error.message);
});
},
});

// SELECTORS
export const { setSelectedTable, selectOrderToCancel, deselectOrderToCancel } =
accountHistorySlice.actions;
export const {
setSelectedTable,
selectOrderToCancel,
deselectOrderToCancel,
resetAccountHistory,
} = accountHistorySlice.actions;

// TODO: possibly remove, as this selector seems to not be used anywhere in the code
export const selectFilteredData = createSelector(
Expand Down Expand Up @@ -212,4 +264,55 @@ export const selectOrderHistory = createSelector(
(orderHistory) => orderHistory.filter((order) => order.status !== "PENDING")
);

// A function that checks an order against a filter condition and returns TRUE if it matches, FALSE otherwise
type FilterFunction = (order: adex.OrderReceipt) => boolean;

let selectCombinedOrders = (filterFunction: FilterFunction) =>
createSelector(
(state: RootState) => state.accountHistory.orderHistory,
(state: RootState) => state.accountHistory.orderHistoryAllPairs,
(orderHistory, orderHistoryAllPairs) => {
// Create a Map to handle duplicates
const orderMap = new Map<string, adex.OrderReceipt>();

// Generate unique orderId helper
const getUniqueOrderId = (order: adex.OrderReceipt) =>
`${order.pairName}_${order.id}`;

// Add orders from orderHistory
orderHistory.forEach((order) => {
orderMap.set(getUniqueOrderId(order), order);
});

// Add orders from orderHistoryAllPairs, will overwrite any duplicates from orderHistory
orderHistoryAllPairs.forEach((order) => {
orderMap.set(getUniqueOrderId(order), order);
});

return Array.from(orderMap.values())
.filter(filterFunction)
.sort((a, b) => {
const timeDifference =
new Date(b.timeSubmitted).getTime() -
new Date(a.timeSubmitted).getTime();
if (timeDifference !== 0) {
return timeDifference;
} else {
return b.id - a.id;
}
});
}
);

// create aliases for calling the selector with different filtering functions
export const selectCombinedOrderHistory = selectCombinedOrders(
(order) => order.status !== "PENDING"
);
export const selectCombinedOpenOrders = selectCombinedOrders(
(order) => order.status === "PENDING"
);

export const selectTables = (state: RootState) => state.accountHistory.tables;

export const { setHideOtherPairs } = accountHistorySlice.actions;
export const { actions, reducer } = accountHistorySlice;
26 changes: 25 additions & 1 deletion src/app/trade/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { AccountHistory } from "../components/AccountHistory";
import { PriceInfo } from "../components/PriceInfo";
import { fetchBalances, selectPair } from "state/pairSelectorSlice";
import { useAppDispatch, useAppSelector } from "../hooks";
import { fetchAccountHistory } from "../state/accountHistorySlice";
import {
fetchAccountHistory,
fetchAccountHistoryAllPairs,
} from "../state/accountHistorySlice";

import { PromoBannerCarousel } from "../components/PromoBannerCarousel";

Expand All @@ -22,6 +25,10 @@ export default function Trade() {
const pairName = pairSelector.name;
const pairsList = pairSelector.pairsList;

const hideOtherPairs = useAppSelector(
(state) => state.accountHistory.hideOtherPairs
);

// Detect changes in selected pair and adjust pagetitle
useEffect(() => {
document.title = pairName ? `DeXter • ${pairName.toUpperCase()}` : "DeXter";
Expand All @@ -42,6 +49,7 @@ export default function Trade() {
}
}, [pairsList, dispatch, searchParams]);

// Update orders of selected pair every 5 seconds
useEffect(() => {
const intervalId = setInterval(() => {
dispatch(fetchBalances());
Expand All @@ -51,6 +59,22 @@ export default function Trade() {
return () => clearInterval(intervalId); // Cleanup interval on component unmount
}, [dispatch]);

// Update orders of all pairs every 2 mins (if selected)
useEffect(() => {
const showAllPairs = !hideOtherPairs;
if (showAllPairs) {
dispatch(fetchAccountHistoryAllPairs());
}

const intervalId = setInterval(() => {
if (showAllPairs) {
dispatch(fetchAccountHistoryAllPairs());
}
}, 120000); // Dispatch every 2 mins (120 seconds)

return () => clearInterval(intervalId); // Cleanup interval on component unmount
}, [dispatch, hideOtherPairs]);

return (
<div className="grow">
<PromoBannerCarousel
Expand Down

0 comments on commit 18c1a99

Please sign in to comment.