Skip to content

Commit

Permalink
[REFACTOR] History data fetching (#1698)
Browse files Browse the repository at this point in the history
* adds use-get-history hook, and replaces data fetching for history using it

* removes stray log

* move loading render branch out of main render to avoid ternary

* tweak hasEmptyHistory check to account for an empty operations list

* renames use get history hook file to match repo conventions

* renames var to follow code convention
  • Loading branch information
aristidesstaffieri authored Nov 27, 2024
1 parent 6ff6938 commit 0be856e
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 103 deletions.
85 changes: 85 additions & 0 deletions extension/src/helpers/hooks/useGetHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useReducer } from "react";

import { getAccountHistory } from "@shared/api/internal";
import { NetworkDetails } from "@shared/constants/stellar";
import { ServerApi } from "stellar-sdk/lib/horizon";

enum RequestState {
IDLE = "IDLE",
LOADING = "LOADING",
SUCCESS = "SUCCESS",
ERROR = "ERROR",
}

interface SuccessState {
state: RequestState.SUCCESS;
data: ServerApi.OperationRecord[];
error: null;
}

interface ErrorState {
state: RequestState.ERROR;
data: null;
error: unknown;
}

interface IdleState {
state: RequestState.IDLE;
data: null;
error: null;
}

interface LoadingState {
state: RequestState.LOADING;
data: null;
error: null;
}

type State = IdleState | LoadingState | SuccessState | ErrorState;

type Action =
| { type: "FETCH_DATA_START" }
| { type: "FETCH_DATA_SUCCESS"; payload: SuccessState["data"] }
| { type: "FETCH_DATA_ERROR"; payload: ErrorState["error"] };

const initialState: IdleState = {
state: RequestState.IDLE,
data: null,
error: null,
};

const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "FETCH_DATA_START":
return { state: RequestState.LOADING, error: null, data: null };
case "FETCH_DATA_SUCCESS":
return { error: null, state: RequestState.SUCCESS, data: action.payload };
case "FETCH_DATA_ERROR":
return { data: null, state: RequestState.ERROR, error: action.payload };
default:
return state;
}
};

function useGetHistory(publicKey: string, networkDetails: NetworkDetails) {
const [state, dispatch] = useReducer(reducer, initialState);

const fetchData = async () => {
dispatch({ type: "FETCH_DATA_START" });
try {
const data = await getAccountHistory(publicKey, networkDetails);
dispatch({ type: "FETCH_DATA_SUCCESS", payload: data });
return data;
} catch (error) {
dispatch({ type: "FETCH_DATA_ERROR", payload: error });
return error;
}
};

return {
state,
fetchData,
};
}

export { useGetHistory, RequestState };
171 changes: 81 additions & 90 deletions extension/src/popup/views/AccountHistory/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import React, { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { Loader } from "@stellar/design-system";
import { Horizon } from "stellar-sdk";
import BigNumber from "bignumber.js";

import { getAccountHistory } from "@shared/api/internal";
import { ActionStatus } from "@shared/api/types";
import { SorobanTokenInterface } from "@shared/constants/soroban/token";

import { publicKeySelector } from "popup/ducks/accountServices";
Expand All @@ -33,8 +30,10 @@ import {
TransactionDetailProps,
} from "popup/components/accountHistory/TransactionDetail";
import { View } from "popup/basics/layout/View";
import { RequestState, useGetHistory } from "helpers/hooks/useGetHistory";

import "./styles.scss";
import { Loading } from "popup/components/Loading";

enum SELECTOR_OPTIONS {
ALL = "ALL",
Expand All @@ -55,13 +54,14 @@ export const AccountHistory = () => {
| null;

const { t } = useTranslation();
const dispatch = useDispatch();
const publicKey = useSelector(publicKeySelector);
const networkDetails = useSelector(settingsNetworkDetailsSelector);
const { accountBalances, accountBalanceStatus } = useSelector(
transactionSubmissionSelector,
);
const { accountBalances } = useSelector(transactionSubmissionSelector);
const { isHideDustEnabled } = useSelector(settingsSelector);
const { state: getHistoryState, fetchData } = useGetHistory(
publicKey,
networkDetails,
);

const [selectedSegment, setSelectedSegment] = useState(SELECTOR_OPTIONS.ALL);
const [historySegments, setHistorySegments] = useState(
Expand All @@ -76,14 +76,16 @@ export const AccountHistory = () => {
const [detailViewProps, setDetailViewProps] = useState(
defaultDetailViewProps,
);
const [isLoading, setIsLoading] = useState(false);

const stellarExpertUrl = getStellarExpertUrl(networkDetails);

const isAccountHistoryLoading =
historySegments === null ||
accountBalanceStatus === ActionStatus.IDLE ||
accountBalanceStatus === ActionStatus.PENDING;
useEffect(() => {
const getData = async () => {
await fetchData();
};
getData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const createSegments = (
Expand Down Expand Up @@ -141,86 +143,75 @@ export const AccountHistory = () => {
return segments;
};

const fetchAccountHistory = async () => {
try {
const operations = await getAccountHistory(publicKey, networkDetails);
setHistorySegments(createSegments(operations));
} catch (e) {
console.error(e);
}
};

const getData = async () => {
setIsLoading(true);
await fetchAccountHistory();
setIsLoading(false);
};

getData();
}, [publicKey, networkDetails, dispatch, isHideDustEnabled]);

return isDetailViewShowing ? (
<TransactionDetail {...detailViewProps} />
) : (
<>
<View.Content>
<div className="AccountHistory" data-testid="AccountHistory">
{isLoading ? (
<div className="AccountHistory__loader">
<Loader size="2rem" />
if (getHistoryState.state === RequestState.SUCCESS) {
setHistorySegments(createSegments(getHistoryState.data));
}
}, [
getHistoryState.state,
getHistoryState.data,
publicKey,
networkDetails,
isHideDustEnabled,
]);

const isLoaderShowing =
getHistoryState.state === RequestState.IDLE ||
getHistoryState.state === RequestState.LOADING;

if (isDetailViewShowing) {
return <TransactionDetail {...detailViewProps} />;
}

if (isLoaderShowing) {
return <Loading />;
}

const hasEmptyHistory = !getHistoryState?.data?.length;

return (
<View.Content>
<div className="AccountHistory" data-testid="AccountHistory">
<header className="AccountHistory__header">{t("Transactions")}</header>
<div className="AccountHistory__selector">
{Object.values(SELECTOR_OPTIONS).map((option) => (
<div
key={option}
className={`AccountHistory__selector__item ${
option === selectedSegment
? "AccountHistory__selector__item--active"
: ""
}`}
onClick={() => setSelectedSegment(option)}
>
{t(option)}
</div>
) : (
<>
<header className="AccountHistory__header">
{t("Transactions")}
</header>
<div className="AccountHistory__selector">
{Object.values(SELECTOR_OPTIONS).map((option) => (
<div
key={option}
className={`AccountHistory__selector__item ${
option === selectedSegment
? "AccountHistory__selector__item--active"
: ""
}`}
onClick={() => setSelectedSegment(option)}
>
{t(option)}
</div>
))}
</div>
<div className="AccountHistory__list">
{historySegments?.[SELECTOR_OPTIONS[selectedSegment]].length ? (
<HistoryList>
<>
{historySegments[SELECTOR_OPTIONS[selectedSegment]].map(
(operation: HistoryItemOperation) => (
<HistoryItem
key={operation.id}
accountBalances={accountBalances}
operation={operation}
publicKey={publicKey}
url={stellarExpertUrl}
networkDetails={networkDetails}
setDetailViewProps={setDetailViewProps}
setIsDetailViewShowing={setIsDetailViewShowing}
/>
),
)}
</>
</HistoryList>
) : (
<div>
{isAccountHistoryLoading
? null
: t("No transactions to show")}
</div>
))}
</div>
<div className="AccountHistory__list">
{historySegments?.[SELECTOR_OPTIONS[selectedSegment]].length ? (
<HistoryList>
<>
{historySegments[SELECTOR_OPTIONS[selectedSegment]].map(
(operation: HistoryItemOperation) => (
<HistoryItem
key={operation.id}
accountBalances={accountBalances}
operation={operation}
publicKey={publicKey}
url={stellarExpertUrl}
networkDetails={networkDetails}
setDetailViewProps={setDetailViewProps}
setIsDetailViewShowing={setIsDetailViewShowing}
/>
),
)}
</div>
</>
</>
</HistoryList>
) : (
<div>{hasEmptyHistory ? t("No transactions to show") : null}</div>
)}
</div>
</View.Content>
</>
</div>
</View.Content>
);
};
13 changes: 0 additions & 13 deletions extension/src/popup/views/AccountHistory/styles.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
.AccountHistory {
&__loader {
height: 100%;
width: 100%;
z-index: calc(var(--back--button-z-index) + 1);
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
top: 0;
left: 0;
}

&__header {
font-size: 1.25rem;
margin-bottom: 1.5rem;
Expand Down

0 comments on commit 0be856e

Please sign in to comment.