From 2c9219f99a033a7dfaba190ae6c924d6ce74a74d Mon Sep 17 00:00:00 2001 From: Chaiwat Ekkaewnumchai Date: Tue, 17 Nov 2020 05:36:47 -0800 Subject: [PATCH] Add State to Notification Component Summary: This diff covers notification items to show data stored in the state as well as add button functionality to plugins. This diff doesn't cover notification search, clear, and setting, which will be covered in next diffs Reviewed By: mweststrate Differential Revision: D24986742 fbshipit-source-id: d42cfe8112881a7f0d2158cbce267a4d7c344305 --- .../notification/Notification.tsx | 150 +++++++++++------- 1 file changed, 91 insertions(+), 59 deletions(-) diff --git a/desktop/app/src/sandy-chrome/notification/Notification.tsx b/desktop/app/src/sandy-chrome/notification/Notification.tsx index 353d35801..51a13cdd0 100644 --- a/desktop/app/src/sandy-chrome/notification/Notification.tsx +++ b/desktop/app/src/sandy-chrome/notification/Notification.tsx @@ -7,9 +7,9 @@ * @format */ -import React from 'react'; +import React, {useCallback, useMemo} from 'react'; import {Layout, theme} from 'flipper-plugin'; -import {styled} from '../../ui'; +import {styled, Glyph} from '../../ui'; import {Input, Typography, Button, Collapse} from 'antd'; import { DownOutlined, @@ -20,53 +20,23 @@ import { DeleteOutlined, } from '@ant-design/icons'; import {LeftSidebar, SidebarTitle} from '../LeftSidebar'; -import {PluginNotification} from '../../reducers/notifications'; +import {Notification as NotificationData} from '../../plugin'; +import {useStore, useDispatch} from '../../utils/useStore'; +import {ClientQuery} from '../../Client'; +import {deconstructClientId} from '../../utils/clientUtils'; +import {selectPlugin} from '../../reducers/connections'; + +type NotificationExtra = { + onOpen: () => void; + clientName: string | undefined; + appName: string | undefined; + pluginName: string; + iconName: string | null | undefined; +}; +type PluginNotification = NotificationData & NotificationExtra; const {Title, Text, Paragraph} = Typography; -// NOTE: remove after the component link to state -const notificationExample: Array = [ - { - notification: { - id: 'testid_0', - title: ` - CRASH: FATAL EXCEPTION: - mainReason: java.lang.RuntimeException: Artificially triggered crash from Flipper sample app - `, - message: - 'very very very very very very very very very very very very very very very very very very very very very long', - severity: 'error', - }, - pluginId: 'testPluginId', - client: 'iPortaldroid', - }, - { - notification: { - id: 'testid_1', - title: `CRASH: FATAL EXCEPTION: - mainReason: java.lang.RuntimeException: Artificially triggered crash from Flipper sample app - `, - message: `FATAL EXCEPTION: main`, - severity: 'error', - }, - pluginId: 'testPluginId', - client: 'iPortaldroid', - }, - { - notification: { - id: 'testid_2', - action: '1', - title: `CRASH: FATAL EXCEPTION: mainReason: java.lang.RuntimeException: Artificially triggered`, - message: `Callstack: FATAL EXCEPTION: main Process: com.facebook.flipper.sample, PID: 1646 java.lang.RuntimeException: Artificially triggered crash from Flipper sample app at com.facebook.flipper.sample.RootComponentSpec`, - severity: 'error', - category: - 'java.lang.RuntimeException: Artificially triggered crash from Flipper sample app', - }, - pluginId: 'CrashReporter', - client: 'emulator-5554', - }, -]; - const CollapseContainer = styled.div({ '.ant-collapse-ghost .ant-collapse-item': { '& > .ant-collapse-header': { @@ -119,27 +89,39 @@ function DetailCollapse({detail}: {detail: string | React.ReactNode}) { } function NotificationEntry({notification}: {notification: PluginNotification}) { - const {notification: content, pluginId, client} = notification; - // TODO: figure out how to transform app name to icon - const icon = React.createElement(ExclamationCircleOutlined, { - style: {color: theme.primaryColor}, - }); + const { + onOpen, + message, + title, + clientName, + appName, + pluginName, + iconName, + } = notification; + + const icon = iconName ? ( + + ) : ( + + ); return ( {icon} - {pluginId} + {pluginName} - {content.title} + {title} - {client} + {clientName && appName + ? `${clientName}/${appName}` + : clientName ?? appName ?? 'Not Connected'} - - + ); } @@ -154,7 +136,7 @@ function NotificationList({ {notifications.map((notification) => ( ))} @@ -164,6 +146,56 @@ function NotificationList({ } export function Notification() { + const dispatch = useDispatch(); + + const clients = useStore((state) => state.connections.clients); + const getClientQuery = useCallback( + (id: string | null) => + id !== null + ? clients.reduce( + (query: ClientQuery | null, client) => + client.id === id ? client.query : query, + null, + ) ?? deconstructClientId(id) + : null, + [clients], + ); + + const clientPlugins = useStore((state) => state.plugins.clientPlugins); + const devicePlugins = useStore((state) => state.plugins.devicePlugins); + const getPlugin = useCallback( + (id: string) => clientPlugins.get(id) || devicePlugins.get(id), + [clientPlugins, devicePlugins], + ); + + const activeNotifications = useStore( + (state) => state.notifications.activeNotifications, + ); + + const displayedNotifications: Array = useMemo( + () => + activeNotifications.map((noti) => { + const plugin = getPlugin(noti.pluginId); + const client = getClientQuery(noti.client); + return { + ...noti.notification, + onOpen: () => + dispatch( + selectPlugin({ + selectedPlugin: noti.pluginId, + selectedApp: noti.client, + deepLinkPayload: noti.notification.action, + }), + ), + clientName: client?.device_id, + appName: client?.app, + pluginName: plugin?.title ?? noti.pluginId, + iconName: plugin?.icon, + }; + }), + [activeNotifications, getPlugin, getClientQuery, dispatch], + ); + const actions = (
@@ -181,7 +213,7 @@ export function Notification() { } /> - + );