Skip to content

Commit

Permalink
Merge pull request Expensify#34549 from vadymbokatov/31976-report-rea…
Browse files Browse the repository at this point in the history
…ction-list-migration

[TS migration] Migrate 'ReportReactionList' component to TypeScript
  • Loading branch information
MonilBhavsar authored Feb 20, 2024
2 parents 7aa91a6 + 9dfd657 commit 52ed833
Show file tree
Hide file tree
Showing 13 changed files with 461 additions and 506 deletions.
129 changes: 129 additions & 0 deletions src/hooks/useBasePopoverReactionList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import {useEffect, useRef, useState} from 'react';
import type {SyntheticEvent} from 'react';
import {Dimensions} from 'react-native';
import * as EmojiUtils from '@libs/EmojiUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import type {BasePopoverReactionListHookProps, ReactionListAnchor, ShowReactionList} from './types';

export default function useBasePopoverReactionList({emojiName, emojiReactions, accountID, reportActionID, preferredLocale}: BasePopoverReactionListHookProps) {
const [isPopoverVisible, setIsPopoverVisible] = useState(false);
const [cursorRelativePosition, setCursorRelativePosition] = useState({horizontal: 0, vertical: 0});
const [popoverAnchorPosition, setPopoverAnchorPosition] = useState({horizontal: 0, vertical: 0});
const reactionListRef = useRef<ReactionListAnchor>(null);

function getReactionInformation() {
const selectedReaction = emojiReactions?.[emojiName];

if (!selectedReaction) {
// If there is no reaction, we return default values
return {
emojiName: '',
reactionCount: 0,
emojiCodes: [],
hasUserReacted: false,
users: [],
};
}

const {emojiCodes, reactionCount, hasUserReacted, userAccountIDs} = EmojiUtils.getEmojiReactionDetails(emojiName, selectedReaction, accountID);

const users = PersonalDetailsUtils.getPersonalDetailsByIDs(userAccountIDs, accountID, true);
return {
emojiName,
emojiCodes,
reactionCount,
hasUserReacted,
users,
};
}

/**
* Get the BasePopoverReactionList anchor position
* We calculate the achor coordinates from measureInWindow async method
*/
function getReactionListMeasuredLocation(): Promise<{x: number; y: number}> {
return new Promise((resolve) => {
const reactionListAnchor = reactionListRef.current;
if (reactionListAnchor && 'measureInWindow' in reactionListAnchor) {
reactionListAnchor.measureInWindow((x, y) => resolve({x, y}));
} else {
resolve({x: 0, y: 0});
}
});
}

/**
* Show the ReactionList modal popover.
*
* @param event - Object - A press event.
* @param reactionListAnchor - Element - reactionListAnchor
*/
const showReactionList: ShowReactionList = (event, reactionListAnchor) => {
// We get the cursor coordinates and the reactionListAnchor coordinates to calculate the popover position
const nativeEvent = (event as SyntheticEvent<ReactionListAnchor, MouseEvent>)?.nativeEvent || {};
reactionListRef.current = reactionListAnchor;
getReactionListMeasuredLocation().then(({x, y}) => {
setCursorRelativePosition({horizontal: nativeEvent.pageX - x, vertical: nativeEvent.pageY - y});
setPopoverAnchorPosition({
horizontal: nativeEvent.pageX,
vertical: nativeEvent.pageY,
});
setIsPopoverVisible(true);
});
};

/**
* Hide the ReactionList modal popover.
*/
function hideReactionList() {
setIsPopoverVisible(false);
}

useEffect(() => {
const dimensionsEventListener = Dimensions.addEventListener('change', () => {
if (!isPopoverVisible) {
// If the popover is not visible, we don't need to update the component
return;
}
getReactionListMeasuredLocation().then(({x, y}) => {
if (!x || !y) {
return;
}
setPopoverAnchorPosition({
horizontal: cursorRelativePosition.horizontal + x,
vertical: cursorRelativePosition.vertical + y,
});
});
});

return () => {
dimensionsEventListener.remove();
};
}, [
isPopoverVisible,
reportActionID,
preferredLocale,
cursorRelativePosition.horizontal,
cursorRelativePosition.vertical,
popoverAnchorPosition.horizontal,
popoverAnchorPosition.vertical,
]);

useEffect(() => {
if (!isPopoverVisible) {
// If the popover is not visible, we don't need to update the component
return;
}

// Hide the list when all reactions are removed
const users = emojiReactions?.[emojiName]?.users;

if (!users || Object.keys(users).length > 0) {
return;
}

hideReactionList();
}, [emojiReactions, emojiName, isPopoverVisible, reportActionID, preferredLocale]);

return {isPopoverVisible, cursorRelativePosition, popoverAnchorPosition, getReactionInformation, hideReactionList, reactionListRef, showReactionList};
}
60 changes: 60 additions & 0 deletions src/hooks/useBasePopoverReactionList/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type {OnyxEntry} from 'react-native-onyx';
import type {LocaleContextProps} from '@components/LocaleContextProvider';
import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
import type {ReactionListAnchor, ReactionListEvent} from '@pages/home/ReportScreenContext';
import type {AnchorPosition} from '@src/styles';
import type {ReportActionReactions} from '@src/types/onyx';

type BasePopoverReactionListOnyxProps = {
/** The reactions for the report action */
emojiReactions: OnyxEntry<ReportActionReactions>;
};

type BasePopoverReactionListProps = {
/** The ID of the report action */
reportActionID: string;

/** The emoji name */
emojiName: string;
};

type BasePopoverReactionListHookProps = BasePopoverReactionListProps & {
/** The reactions for the report action */
emojiReactions: OnyxEntry<ReportActionReactions>;

/** The current user's account ID */
accountID: WithCurrentUserPersonalDetailsProps['currentUserPersonalDetails']['accountID'];

preferredLocale: LocaleContextProps['preferredLocale'];
};

type BasePopoverReactionListPropsWithLocalWithOnyx = WithCurrentUserPersonalDetailsProps & BasePopoverReactionListOnyxProps & BasePopoverReactionListProps;
type BasePopoverReactionListState = {
/** Whether the popover is visible */
isPopoverVisible: boolean;

/** The horizontal and vertical position (relative to the screen) where the popover will display. */
popoverAnchorPosition: AnchorPosition;

/** The horizontal and vertical position (relative to the screen) where the cursor is. */
cursorRelativePosition: AnchorPosition;
};

type ShowReactionList = (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor) => void;

type InnerReactionListRef = {
showReactionList: ShowReactionList;
hideReactionList: () => void;
isActiveReportAction: (actionID: number | string) => boolean;
};

export type {
BasePopoverReactionListProps,
BasePopoverReactionListHookProps,
BasePopoverReactionListPropsWithLocalWithOnyx,
BasePopoverReactionListState,
BasePopoverReactionListOnyxProps,
ShowReactionList,
ReactionListAnchor,
InnerReactionListRef,
};
4 changes: 2 additions & 2 deletions src/pages/home/ReportScreenContext.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {RefObject} from 'react';
import type {RefObject, SyntheticEvent} from 'react';
import {createContext} from 'react';
import type {FlatList, GestureResponderEvent, View} from 'react-native';

type ReactionListAnchor = View | HTMLDivElement | null;

type ReactionListEvent = GestureResponderEvent | MouseEvent;
type ReactionListEvent = GestureResponderEvent | MouseEvent | SyntheticEvent<ReactionListAnchor, MouseEvent>;

type ReactionListRef = {
showReactionList: (event: ReactionListEvent | undefined, reactionListAnchor: ReactionListAnchor, emojiName: string, reportActionID: string) => void;
Expand Down
127 changes: 0 additions & 127 deletions src/pages/home/report/ReactionList/BaseReactionList.js

This file was deleted.

Loading

0 comments on commit 52ed833

Please sign in to comment.