/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import React, { useContext, useState, useMemo, useEffect, useCallback, } from 'react'; import {MockResponseDetails} from './MockResponseDetails'; import {NetworkRouteContext, Route} from './NetworkRouteManager'; import {RequestId} from '../types'; import {Checkbox, Modal, Tooltip, Button, Typography} from 'antd'; import { NUX, Layout, DataList, Toolbar, createState, useValue, } from 'flipper-plugin'; import {CloseCircleOutlined, WarningOutlined} from '@ant-design/icons'; const {Text} = Typography; type Props = { routes: {[id: string]: Route}; }; type RouteItem = { id: string; title: string; route: Route; isDuplicate: boolean; }; // return ids that have the same pair of requestUrl and method; this will return only the duplicate function _duplicateIds(routes: {[id: string]: Route}): Array { const idSet: {[id: string]: {[method: string]: boolean}} = {}; return Object.entries(routes).reduce((acc: Array, [id, route]) => { if (idSet.hasOwnProperty(route.requestUrl)) { if (idSet[route.requestUrl].hasOwnProperty(route.requestMethod)) { return acc.concat(id); } idSet[route.requestUrl] = { ...idSet[route.requestUrl], [route.requestMethod]: true, }; return acc; } else { idSet[route.requestUrl] = {[route.requestMethod]: true}; return acc; } }, []); } export function ManageMockResponsePanel(props: Props) { const networkRouteManager = useContext(NetworkRouteContext); const [selectedIdAtom] = useState(() => createState()); const selectedId = useValue(selectedIdAtom); useEffect(() => { selectedIdAtom.update((selectedId) => { const keys = Object.keys(props.routes); let returnValue: string | undefined = undefined; // selectId is undefined when there are no rows or it is the first time rows are shown if (selectedId === undefined) { if (keys.length === 0) { // there are no rows returnValue = undefined; } else { // first time rows are shown returnValue = keys[0]; } } else { if (keys.includes(selectedId)) { returnValue = selectedId; } else { // selectedId row value not in routes so default to first line returnValue = keys[0]; } } return returnValue; }); }, [props.routes, selectedIdAtom]); const duplicatedIds = useMemo( () => _duplicateIds(props.routes), [props.routes], ); const items: RouteItem[] = Object.entries(props.routes).map( ([id, route]) => ({ id, route, title: route.requestUrl, isDuplicate: duplicatedIds.includes(id), }), ); const handleDelete = useCallback( (id: string) => { Modal.confirm({ title: 'Are you sure you want to delete this item?', icon: '', onOk() { networkRouteManager.removeRoute(id); selectedIdAtom.set(undefined); }, onCancel() {}, }); }, [networkRouteManager, selectedIdAtom], ); const handleToggle = useCallback( (id: string) => { networkRouteManager.enableRoute(id); }, [networkRouteManager], ); const handleRender = useCallback( (item: RouteItem) => ( ), [handleDelete, handleToggle], ); const handleSelect = useCallback( (id: string) => { if (id) { selectedIdAtom.set(id); } }, [selectedIdAtom], ); return ( {selectedId && props.routes.hasOwnProperty(selectedId) && ( )} ); } const RouteEntry = ({ item, onToggle, onDelete, }: { item: RouteItem; onToggle(id: string): void; onDelete(id: string): void; }) => { const tip = item.route.enabled ? 'Un-check to disable mock route' : 'Check to enable mock route'; return ( onToggle(item.id)} checked={item.route.enabled}> {item.route.requestUrl.length === 0 ? ( untitled ) : ( {item.route.requestUrl} )} onDelete(item.id)}> {item.isDuplicate && } ); };