Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Code Smells #1603

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions app/basicComponents/AutocompleteDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useState, useRef, useEffect } from 'react';
// import styled from 'styled-components';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the trash code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make the corrections and resend. Thank you for the feedback.

import styled from 'styled-components';
import { getAbbreviatedAddress } from '../infra/utils';
import { chevronBottomBlack, chevronBottomWhite } from '../assets/images';
Expand Down Expand Up @@ -68,7 +69,7 @@ const InputField = styled.div<{
}) =>
`
color: ${isDarkSkin ? light.states.normal.color : dark.states.normal.color};

&:hover {
color: ${
isDarkSkin ? light.states.normal.color : dark.states.normal.color
Expand Down Expand Up @@ -157,7 +158,7 @@ const ActualInput = styled.input<{
}) =>
`
color: ${isDarkSkin ? light.states.normal.color : dark.states.normal.color};

&:hover {
color: ${
isDarkSkin ? light.states.normal.color : dark.states.normal.color
Expand Down Expand Up @@ -214,7 +215,7 @@ const AutocompleteList = styled.div<{
isDarkSkin
? light.states.hover.backgroundColor
: dark.states.hover.backgroundColor
};
};
color: ${
isDarkSkin ? light.states.hover.color : dark.states.hover.color
};
Expand Down Expand Up @@ -293,11 +294,13 @@ type Props = {

export type AutocompleteDropdownProps = Props;

type TimeoutOrNull = NodeJS.Timeout | null;

const AutocompleteDropdown = (props: Props) => {
const { value, autofocus, autocomplete = true } = props;
let inputBlurTimer: any;
let inputBlurTimer: TimeoutOrNull;
const [isOpen, setIsOpen] = useState(false);
const [list, setList] = useState<Array<any>>([]);
const [list, setList] = useState<string[]>([]);
const [isFocus, setIsFocus] = useState(-1);
const [editField, setEditField] = useState(value || '');
const [move, setMove] = useState<string | null>(null);
Expand All @@ -317,7 +320,7 @@ const AutocompleteDropdown = (props: Props) => {
// @ts-ignore
const { value } = inputField.current || {};

let list: any = [];
let list: typeof data = []; // typeof data = []

if (value && value.trim() && autocomplete) {
list = data.filter(
Expand Down Expand Up @@ -363,7 +366,7 @@ const AutocompleteDropdown = (props: Props) => {
setIsOpen(!isOpen);
};

const pressEnterKey = (e: KeyboardEvent) => {
const pressEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { onChange, getItemValue, onEnter } = props;
const data = list[isFocus];

Expand All @@ -374,11 +377,13 @@ const AutocompleteDropdown = (props: Props) => {
setList([]);
setIsOpen(false);

const genericEvent: React.KeyboardEvent = e as React.KeyboardEvent;

// @ts-ignore
onEnter(e.target.value);
onEnter(genericEvent);
};

const handleInputKeyUp = (e: any) => {
const handleInputKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { keyCode } = e;

e.stopPropagation();
Expand Down Expand Up @@ -437,7 +442,12 @@ const AutocompleteDropdown = (props: Props) => {
}
};

const renderItem = (item: any) => (
interface ItemType {
nickname: string;
address: string;
}

const renderItem = (item: ItemType) => (
<div role="button" tabIndex={-1}>
{item.nickname} - {getAbbreviatedAddress(item.address)}
</div>
Expand All @@ -461,7 +471,7 @@ const AutocompleteDropdown = (props: Props) => {
return menus;
};

const handleInputChange = (e: any) => {
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { target } = e;
const { value } = target;
const { onChange } = props;
Expand Down
4 changes: 3 additions & 1 deletion app/basicComponents/CopyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ const CopyIcon = styled.img.attrs<{ secondary?: boolean }>(
}
`;

type TypeOrNumber = NodeJS.Timeout | number;

const CopyButton = ({
secondary,
children,
hideCopyIcon,
value,
onClick,
}: Props) => {
let t: NodeJS.Timeout | number = 0;
let t: TypeOrNumber = 0;
const handleCopy = async (e: React.MouseEvent) => {
e.stopPropagation();

Expand Down
18 changes: 10 additions & 8 deletions app/basicComponents/DropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const HeaderWrapper = styled.div<{
}

color: ${isDarkMode ? dark.states.normal.color : light.states.normal.color};

&:hover {
color: ${
isDarkMode ? dark.states.hover.color : light.states.hover.color
Expand Down Expand Up @@ -157,7 +157,7 @@ const DropdownRow = styled.div<{
isDarkMode
? dark.states.hover.backgroundColor
: light.states.hover.backgroundColor
};
};
color: ${
isDarkMode ? dark.states.hover.color : light.states.hover.color
};
Expand Down Expand Up @@ -241,17 +241,17 @@ const ItemsWrapper = styled.div<{
},
},
}) => `

> div:first-child {
border-top: 1px solid ${isDarkMode ? dark.borderColor : light.borderColor};
}

> div:last-child {
border-bottom: none;
}
}

> div {

border-bottom: 1px solid ${
isDarkMode ? dark.borderColor : light.borderColor
};
Expand Down Expand Up @@ -352,12 +352,14 @@ const DropDownItem: React.FC<DropDownItemProps> = ({
</StyledDropDownItem>
);

type NumberOrString = number | string;

type Props<T extends ADataItem> = {
onClick: ({ index }: { index: number }) => void | Promise<number>;
onClose?: () => void;
data: Partial<T>[];
selectedItemIndex: number;
rowHeight?: number | string;
rowHeight?: NumberOrString;
isOpened?: boolean;
isDisabled?: boolean;
bold?: boolean;
Expand Down
12 changes: 6 additions & 6 deletions app/basicComponents/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ const ActualInput = styled.input<{
}) =>
!isDisabled &&
`&:hover, &:focus, &:active {
background-color: ${states.focus.backgroundColor};
color: ${states.focus.color};
background-color: ${states.focus.backgroundColor};
color: ${states.focus.color};
} `}
font-size: 14px;
line-height: 16px;
Expand Down Expand Up @@ -155,22 +155,22 @@ const Input = ({
};
});

const handleEnterPress = (event: any) => {
const handleEnterPress = (event: React.KeyboardEvent) => {
if (event.key === 'Enter' && !!onEnterPress) {
onEnterPress();
onPaste();
}
};

const handleOnBlur = ({ target }: { target: any }) => {
const handleOnBlur = ({ target }: { target: HTMLInputElement }) => {
setIsFocused(false);
const { value } = target;

onBlur?.({ value });
};

const handleChange = ({ target }: { target: any }) => {
const { value } = target;
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.currentTarget;
onChange?.({ value });

if (onChangeDebounced) {
Expand Down
2 changes: 1 addition & 1 deletion app/components/common/Feedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ const FeedbackButton = () => {

const formData = {
event_id: captureReactMessage(`
User has submitted an issue and asked to check it. Discord handle: ${userData.name} and email: ${userData.email},
User has submitted an issue and asked to check it. Discord handle: ${userData.name} and email: ${userData.email},
`),
...userData,
};
Expand Down
8 changes: 4 additions & 4 deletions app/components/common/Version.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,10 @@ const CheckForUpdates = () => {
const network = useSelector(getNetworkInfo);
enum CheckState {
Idle = 0,
Checking,
NoUpdates,
HasUpdate,
ManualUpdate,
Checking = 1,
NoUpdates = 2,
HasUpdate = 3,
ManualUpdate = 4,
}
const [curState, setCurState] = useState(CheckState.Idle);
const [manualUpdateSmappVersion, setManualUpdateSmappVersion] = useState('');
Expand Down
2 changes: 1 addition & 1 deletion app/components/settings/SignMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type Props = {
};

const SignMessage = ({ index, close }: Props) => {
let copiedTimeout: any = null;
let copiedTimeout: NodeJS.Timeout;
const [message, setMessage] = useState('');
const [isCopied, setIsCopied] = useState(false);
const [lastResult, setLastResult] = useState('');
Expand Down
89 changes: 77 additions & 12 deletions app/components/transactions/TransactionsMeta.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { RewardView, TxView } from 'app/redux/wallet/selectors';
import { TIME_SPANS } from 'app/screens/transactions/Transactions';
import React from 'react';
import { DAY } from 'shared/constants';
import { SingleSigMethods } from 'shared/templateConsts';
import { Bech32Address, TxState } from 'shared/types';
import { isReward, isTx } from 'shared/types/guards';
import styled from 'styled-components';
import { formatSmidge } from '../../infra/utils';
import { smColors } from '../../vars';
Expand Down Expand Up @@ -69,25 +75,84 @@ const Progress = styled.div<{ total: number; coins: number }>`

type Props = {
filterName: string;
mined: number;
sent: number;
received: number;
totalMined: number;
totalSent: number;
totalReceived: number;
nonce: number;
transactions: (TxView | RewardView)[];
address: Bech32Address;
selectedTimeSpan: number;
};

const TransactionsMeta = ({
mined,
sent,
received,
totalMined,
totalSent,
totalReceived,
filterName,
nonce,
transactions,
address,
selectedTimeSpan,
}: Props) => {
const getNumOfCoinsFromTransactions = (
address: Bech32Address,
transactions: (TxView | RewardView)[]
) => {
const coins = { mined: 0, sent: 0, received: 0 };
return transactions.reduce((coins, txOrReward: TxView | RewardView) => {
if (isTx(txOrReward)) {
const { status, principal: sender, method, payload } = txOrReward;
const amount =
method === SingleSigMethods.Spend
? parseInt(payload?.Arguments?.Amount || 0, 10)
: 0;
if (
status !== TxState.REJECTED &&
status !== TxState.INSUFFICIENT_FUNDS &&
status !== TxState.CONFLICTING &&
status !== TxState.FAILURE
) {
return sender === address
? { ...coins, sent: coins.sent + amount }
: { ...coins, received: coins.received + amount };
}
} else if (isReward(txOrReward)) {
const { amount } = txOrReward;
return { ...coins, mined: coins.mined + amount };
}
return coins;
}, coins);
};

const filterLastDays = (txs: (TxView | RewardView)[], days = 1) => {
const startDate = Date.now() - days * DAY;
return txs.filter(
(tx) => (tx.timestamp && tx.timestamp >= startDate) || !tx.timestamp
);
};
const filteredTransactions = filterLastDays(
transactions,
TIME_SPANS[selectedTimeSpan].days
);
const getCoinStatistics = (filteredTransactions: (TxView | RewardView)[]) => {
const coins = getNumOfCoinsFromTransactions(address, filteredTransactions);
const totalCoins = getNumOfCoinsFromTransactions(
address,
transactions || []
);
return {
...coins,
totalMined: totalCoins.mined,
totalSent: totalCoins.sent,
totalReceived: totalCoins.received,
};
};

const coinStats = getCoinStatistics(filteredTransactions);

const {
mined,
sent,
received,
totalMined,
totalSent,
totalReceived,
} = coinStats;

const totalFilteredCoins = mined + sent + received;
const coinsMeta = [
{ title: 'SMESHED', coins: mined },
Expand Down
4 changes: 3 additions & 1 deletion app/screens/auth/SwitchNetwork.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ const SwitchNetwork = ({ history, location }: AuthRouterParams) => {
return [{ label: 'NO NETWORKS AVAILABLE', isDisabled: true }];
};

const goNext = (genesisID: string | undefined) => {
type GoNextType = string | undefined;

const goNext = (genesisID: GoNextType) => {
Comment on lines +111 to +113
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be just

Suggested change
type GoNextType = string | undefined;
const goNext = (genesisID: GoNextType) => {
const goNext = (genesisID?: HexString) => {

What is the purpose of extracting it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a tool that my teacher recommended for detecting code smells. One of these smells was 'Missing Union Type Abstractions' which suggests using type aliases to avoid the repetition of union types.

The tool is called ReactSniffer and is still in development (it's part of a research project at my university). If you have any observations about this code smell, please feel free to share them in the comments, and I'll pass them on to the responsible parties.

Link: https://github.com/maykongsn/react-typescript-code-smells

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in case these types will be used (or at least may be used or it is something tricky) — it is fine.
But most of the extracted union types look silly: they would not be used more than once or it is a simple union of primitives — I don't see any benefits.

For example, it is good to have:
type HexOrBytes = Uint8Array | HexString
which also may have a guard, some utils (e.g. toHex or toBytes), may be reused, and so on

But having NumberOrString instead of number | string in some specific function argument — looks silly.
And especially type WeirdName = string | undefined for an argument of the function that accepts optional string +)

I believe that no tool can determine what exactly you need in which case (at least without some well-trained AI model), so it is good to have automatic suggestions, but afterward, the coder should make a decision on what to replace, and what to keep...


Let's keep commits that fix Anys and refactoring of transactions components.

if (creatingWallet) {
if (isWalletOnly && genesisID?.length) {
return history.push(AuthPath.ConnectToAPI, {
Expand Down
17 changes: 9 additions & 8 deletions app/screens/backup/FileBackup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ const BottomRow = styled(MiddleSectionRow)`
justify-content: space-between;
`;

const FileBackup = ({
history,
location,
}: RouteComponentProps<
Record<string, any>,
StaticContext,
{ filePath: string }
>) => {
interface FileBackupProps {
filePath: string;
}

const FileBackup: React.FC<
RouteComponentProps<Record<string, never>, StaticContext, FileBackupProps>
> = (props) => {
const { history, location } = props;

const showBackupFile = () => {
eventsService.showFileInFolder({ filePath: location.state.filePath });
};
Expand Down
Loading