Convert Flipper plugin "Fresco" to TypeScript
Summary: Moves the Fresco plugin to TypeScript, including fixing any typing/nullable warnings following this. Note that parameters of event handlers previously using the SyntheticInputEvent flow type now accept a parameter of type `any`. This is done since the InputEvent api is not covered by ts bindings, as the interface is deemed experimental and not fully covered by browsers (see https://fettblog.eu/typescript-react/events/#wheres-inputevent for more info) Reviewed By: passy Differential Revision: D18201893 fbshipit-source-id: 41d1e5fc1ceaa8f8453c0f5929e754b7c32c0eb8
This commit is contained in:
committed by
Facebook Github Bot
parent
7ca230a9c6
commit
d0ab63297f
@@ -7,15 +7,15 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {ImageId, ImageData} from './api.js';
|
||||
import {ImageId, ImageData} from './api.js';
|
||||
|
||||
export type ImagesMap = {[imageId: ImageId]: ImageData};
|
||||
export type ImagesMap = {[imageId in ImageId]: ImageData};
|
||||
|
||||
const maxInflightRequests = 10;
|
||||
|
||||
export default class ImagePool {
|
||||
cache: ImagesMap = {};
|
||||
requested: {[imageId: ImageId]: boolean} = {};
|
||||
requested: {[imageId in ImageId]: boolean} = {};
|
||||
queued: Array<ImageId> = [];
|
||||
inFlightRequests: number = 0;
|
||||
fetchImage: (imageId: ImageId) => void;
|
||||
@@ -59,7 +59,8 @@ export default class ImagePool {
|
||||
delete this.requested[image.imageId];
|
||||
|
||||
if (this.queued.length > 0) {
|
||||
this.fetchImage(this.queued.pop());
|
||||
const popped = this.queued.pop() as string;
|
||||
this.fetchImage(popped);
|
||||
} else {
|
||||
this.inFlightRequests--;
|
||||
}
|
||||
@@ -7,8 +7,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {ImageId, ImageData, ImagesList} from './api.js';
|
||||
import type {ImageEventWithId} from './index.js';
|
||||
import {CacheInfo, ImageId, ImageData, ImagesList} from './api';
|
||||
import {ImageEventWithId} from './index';
|
||||
|
||||
import {
|
||||
Toolbar,
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
ToggleButton,
|
||||
Text,
|
||||
} from 'flipper';
|
||||
import MultipleSelect from './MultipleSelect.js';
|
||||
import type {ImagesMap} from './ImagePool.js';
|
||||
import MultipleSelect from './MultipleSelect';
|
||||
import {ImagesMap} from './ImagePool';
|
||||
import {clipboard} from 'electron';
|
||||
import {PureComponent} from 'react';
|
||||
import React, {ChangeEvent, KeyboardEvent, PureComponent} from 'react';
|
||||
|
||||
function formatMB(bytes: number) {
|
||||
return Math.floor(bytes / (1024 * 1024)) + 'MB';
|
||||
@@ -37,11 +37,11 @@ function formatKB(bytes: number) {
|
||||
return Math.floor(bytes / 1024) + 'KB';
|
||||
}
|
||||
|
||||
type ToggleProps = {|
|
||||
label: string,
|
||||
onClick?: (newValue: boolean) => void,
|
||||
toggled: boolean,
|
||||
|};
|
||||
type ToggleProps = {
|
||||
label: string;
|
||||
onClick?: (newValue: boolean) => void;
|
||||
toggled: boolean;
|
||||
};
|
||||
|
||||
const ToolbarToggleButton = styled(ToggleButton)(_props => ({
|
||||
alignSelf: 'center',
|
||||
@@ -68,31 +68,31 @@ function Toggle(props: ToggleProps) {
|
||||
}
|
||||
|
||||
type ImagesCacheOverviewProps = {
|
||||
onColdStartChange: (checked: boolean) => void,
|
||||
coldStartFilter: boolean,
|
||||
allSurfacesOption: string,
|
||||
surfaceOptions: Set<string>,
|
||||
selectedSurfaces: Set<string>,
|
||||
onChangeSurface: (key: Set<string>) => void,
|
||||
images: ImagesList,
|
||||
onClear: (type: string) => void,
|
||||
onTrimMemory: () => void,
|
||||
onRefresh: () => void,
|
||||
onEnableDebugOverlay: (enabled: boolean) => void,
|
||||
isDebugOverlayEnabled: boolean,
|
||||
onEnableAutoRefresh: (enabled: boolean) => void,
|
||||
isAutoRefreshEnabled: boolean,
|
||||
onImageSelected: (selectedImage: ImageId) => void,
|
||||
imagesMap: ImagesMap,
|
||||
events: Array<ImageEventWithId>,
|
||||
onTrackLeaks: (enabled: boolean) => void,
|
||||
isLeakTrackingEnabled: boolean,
|
||||
onColdStartChange: (checked: boolean) => void;
|
||||
coldStartFilter: boolean;
|
||||
allSurfacesOption: string;
|
||||
surfaceOptions: Set<string>;
|
||||
selectedSurfaces: Set<string>;
|
||||
onChangeSurface: (key: Set<string>) => void;
|
||||
images: ImagesList;
|
||||
onClear: (type: string) => void;
|
||||
onTrimMemory: () => void;
|
||||
onRefresh: () => void;
|
||||
onEnableDebugOverlay: (enabled: boolean) => void;
|
||||
isDebugOverlayEnabled: boolean;
|
||||
onEnableAutoRefresh: (enabled: boolean) => void;
|
||||
isAutoRefreshEnabled: boolean;
|
||||
onImageSelected: (selectedImage: ImageId) => void;
|
||||
imagesMap: ImagesMap;
|
||||
events: Array<ImageEventWithId>;
|
||||
onTrackLeaks: (enabled: boolean) => void;
|
||||
isLeakTrackingEnabled: boolean;
|
||||
};
|
||||
|
||||
type ImagesCacheOverviewState = {|
|
||||
selectedImage: ?ImageId,
|
||||
size: number,
|
||||
|};
|
||||
type ImagesCacheOverviewState = {
|
||||
selectedImage: ImageId | null;
|
||||
size: number;
|
||||
};
|
||||
|
||||
const StyledSelect = styled(Select)(props => ({
|
||||
marginLeft: 6,
|
||||
@@ -103,8 +103,13 @@ const StyledSelect = styled(Select)(props => ({
|
||||
|
||||
export default class ImagesCacheOverview extends PureComponent<
|
||||
ImagesCacheOverviewProps,
|
||||
ImagesCacheOverviewState,
|
||||
ImagesCacheOverviewState
|
||||
> {
|
||||
state = {
|
||||
selectedImage: null,
|
||||
size: 150,
|
||||
};
|
||||
|
||||
static Container = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
});
|
||||
@@ -121,17 +126,12 @@ export default class ImagesCacheOverview extends PureComponent<
|
||||
width: '100%',
|
||||
});
|
||||
|
||||
state = {
|
||||
selectedImage: undefined,
|
||||
size: 150,
|
||||
};
|
||||
|
||||
onImageSelected = (selectedImage: ImageId) => {
|
||||
this.setState({selectedImage});
|
||||
this.props.onImageSelected(selectedImage);
|
||||
};
|
||||
|
||||
onKeyDown = (e: SyntheticKeyboardEvent<*>) => {
|
||||
onKeyDown = (e: KeyboardEvent) => {
|
||||
const selectedImage = this.state.selectedImage;
|
||||
const imagesMap = this.props.imagesMap;
|
||||
|
||||
@@ -151,7 +151,7 @@ export default class ImagesCacheOverview extends PureComponent<
|
||||
this.props.onEnableAutoRefresh(!this.props.isAutoRefreshEnabled);
|
||||
};
|
||||
|
||||
onChangeSize = (e: SyntheticInputEvent<HTMLInputElement>) =>
|
||||
onChangeSize = (e: ChangeEvent<HTMLInputElement>) =>
|
||||
this.setState({size: parseInt(e.target.value, 10)});
|
||||
|
||||
onSurfaceOptionsChange = (selectedItem: string, checked: boolean) => {
|
||||
@@ -199,7 +199,7 @@ export default class ImagesCacheOverview extends PureComponent<
|
||||
<ImagesCacheOverview.Container
|
||||
grow={true}
|
||||
onKeyDown={this.onKeyDown}
|
||||
tabIndex="0">
|
||||
tabIndex={0}>
|
||||
<Toolbar position="top">
|
||||
<Button icon="trash" onClick={this.props.onTrimMemory}>
|
||||
Trim Memory
|
||||
@@ -242,18 +242,19 @@ export default class ImagesCacheOverview extends PureComponent<
|
||||
</Toolbar>
|
||||
{!hasImages ? (
|
||||
<ImagesCacheOverview.Empty>
|
||||
<LoadingIndicator />
|
||||
<LoadingIndicator size={50} />
|
||||
</ImagesCacheOverview.Empty>
|
||||
) : (
|
||||
<ImagesCacheOverview.Content>
|
||||
{this.props.images.map((data, index) => {
|
||||
{this.props.images.map((data: CacheInfo, index: number) => {
|
||||
const maxSize = data.maxSizeBytes;
|
||||
const subtitle = maxSize
|
||||
? formatMB(data.sizeBytes) + ' / ' + formatMB(maxSize)
|
||||
: formatMB(data.sizeBytes);
|
||||
const onClear = data.clearKey
|
||||
? () => this.props.onClear(data.clearKey)
|
||||
: null;
|
||||
const onClear =
|
||||
data.clearKey !== undefined
|
||||
? () => this.props.onClear(data.clearKey as string)
|
||||
: undefined;
|
||||
return (
|
||||
<ImageGrid
|
||||
key={index}
|
||||
@@ -277,15 +278,15 @@ export default class ImagesCacheOverview extends PureComponent<
|
||||
}
|
||||
|
||||
class ImageGrid extends PureComponent<{
|
||||
title: string,
|
||||
subtitle: string,
|
||||
images: Array<ImageId>,
|
||||
selectedImage: ?ImageId,
|
||||
onImageSelected: (image: ImageId) => void,
|
||||
onClear: ?() => void,
|
||||
imagesMap: ImagesMap,
|
||||
size: number,
|
||||
events: Array<ImageEventWithId>,
|
||||
title: string;
|
||||
subtitle: string;
|
||||
images: Array<ImageId>;
|
||||
selectedImage: ImageId | null;
|
||||
onImageSelected: (image: ImageId) => void;
|
||||
onClear: (() => void) | undefined;
|
||||
imagesMap: ImagesMap;
|
||||
size: number;
|
||||
events: Array<ImageEventWithId>;
|
||||
}> {
|
||||
static Content = styled('div')({
|
||||
paddingLeft: 15,
|
||||
@@ -325,9 +326,9 @@ class ImageGrid extends PureComponent<{
|
||||
}
|
||||
|
||||
class ImageGridHeader extends PureComponent<{
|
||||
title: string,
|
||||
subtitle: string,
|
||||
onClear: ?() => void,
|
||||
title: string;
|
||||
subtitle: string;
|
||||
onClear: (() => void) | undefined;
|
||||
}> {
|
||||
static Container = styled(FlexRow)({
|
||||
color: colors.dark70,
|
||||
@@ -384,20 +385,20 @@ class ImageGridHeader extends PureComponent<{
|
||||
}
|
||||
|
||||
class ImageItem extends PureComponent<{
|
||||
imageId: ImageId,
|
||||
image: ?ImageData,
|
||||
selected: boolean,
|
||||
onSelected: (image: ImageId) => void,
|
||||
size: number,
|
||||
numberOfRequests: number,
|
||||
imageId: ImageId;
|
||||
image: ImageData;
|
||||
selected: boolean;
|
||||
onSelected: (image: ImageId) => void;
|
||||
size: number;
|
||||
numberOfRequests: number;
|
||||
}> {
|
||||
static Container = styled(FlexBox)(({size}) => ({
|
||||
static Container = styled(FlexBox)((props: {size: number}) => ({
|
||||
float: 'left',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexShrink: 0,
|
||||
height: size,
|
||||
width: size,
|
||||
height: props.size,
|
||||
width: props.size,
|
||||
borderRadius: 4,
|
||||
marginRight: 15,
|
||||
marginBottom: 15,
|
||||
@@ -415,7 +416,7 @@ class ImageItem extends PureComponent<{
|
||||
padding: '0 0',
|
||||
});
|
||||
|
||||
static SelectedHighlight = styled('div')(props => ({
|
||||
static SelectedHighlight = styled('div')((props: {selected: boolean}) => ({
|
||||
borderColor: colors.highlight,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: props.selected ? 3 : 0,
|
||||
@@ -428,7 +429,8 @@ class ImageItem extends PureComponent<{
|
||||
top: 0,
|
||||
}));
|
||||
|
||||
static HoverOverlay = styled(FlexColumn)(props => ({
|
||||
static HoverOverlay = styled(FlexColumn)(
|
||||
(props: {selected: boolean; size: number}) => ({
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.whiteAlpha80,
|
||||
bottom: props.selected ? 4 : 0,
|
||||
@@ -444,7 +446,8 @@ class ImageItem extends PureComponent<{
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
static MemoryLabel = styled('span')({
|
||||
fontWeight: 600,
|
||||
@@ -7,8 +7,8 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {ImageData} from './api.js';
|
||||
import type {ImageEventWithId} from './index.js';
|
||||
import {ImageData} from './api';
|
||||
import {ImageEventWithId} from './index';
|
||||
import {
|
||||
Component,
|
||||
DataDescription,
|
||||
@@ -20,10 +20,11 @@ import {
|
||||
colors,
|
||||
styled,
|
||||
} from 'flipper';
|
||||
import React from 'react';
|
||||
|
||||
type ImagesSidebarProps = {
|
||||
image: ?ImageData,
|
||||
events: Array<ImageEventWithId>,
|
||||
image: ImageData;
|
||||
events: Array<ImageEventWithId>;
|
||||
};
|
||||
|
||||
type ImagesSidebarState = {};
|
||||
@@ -38,7 +39,7 @@ const WordBreakFlexColumn = styled(FlexColumn)({
|
||||
|
||||
export default class ImagesSidebar extends Component<
|
||||
ImagesSidebarProps,
|
||||
ImagesSidebarState,
|
||||
ImagesSidebarState
|
||||
> {
|
||||
render() {
|
||||
return (
|
||||
@@ -81,7 +82,7 @@ export default class ImagesSidebar extends Component<
|
||||
}
|
||||
|
||||
class EventDetails extends Component<{
|
||||
event: ImageEventWithId,
|
||||
event: ImageEventWithId;
|
||||
}> {
|
||||
render() {
|
||||
const {event} = this.props;
|
||||
@@ -158,9 +159,9 @@ class EventDetails extends Component<{
|
||||
}
|
||||
|
||||
class RequestHeader extends Component<{
|
||||
event: ImageEventWithId,
|
||||
event: ImageEventWithId;
|
||||
}> {
|
||||
dateString = timestamp => {
|
||||
dateString = (timestamp: number) => {
|
||||
const date = new Date(timestamp);
|
||||
return `${date.toTimeString().split(' ')[0]}.${(
|
||||
'000' + date.getMilliseconds()
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import {Block, Button, colors, FlexColumn, styled, Glyph} from 'flipper';
|
||||
import React, {Component} from 'react';
|
||||
import React, {ChangeEvent, Component} from 'react';
|
||||
|
||||
const Container = styled(Block)({
|
||||
position: 'relative',
|
||||
@@ -54,26 +54,26 @@ const StyledGlyph = styled(Glyph)({
|
||||
});
|
||||
|
||||
type State = {
|
||||
visibleList: boolean,
|
||||
visibleList: boolean;
|
||||
};
|
||||
|
||||
export default class MultipleSelect extends Component<
|
||||
{
|
||||
selected: Set<string>,
|
||||
selected: Set<string>;
|
||||
|
||||
options: Set<string>,
|
||||
options: Set<string>;
|
||||
|
||||
onChange: (selectedItem: string, checked: boolean) => void,
|
||||
onChange: (selectedItem: string, checked: boolean) => void;
|
||||
|
||||
label: string,
|
||||
label: string;
|
||||
},
|
||||
State,
|
||||
State
|
||||
> {
|
||||
state = {
|
||||
visibleList: false,
|
||||
};
|
||||
|
||||
handleOnChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
handleOnChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const {
|
||||
target: {value, checked},
|
||||
} = event;
|
||||
@@ -7,23 +7,26 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import FrescoPlugin from '../index.js';
|
||||
import type {PersistedState, ImageEventWithId} from '../index.js';
|
||||
import type {AndroidCloseableReferenceLeakEvent} from '../api.js';
|
||||
import type {MetricType} from 'flipper';
|
||||
import type {Notification} from '../../../plugin.tsx';
|
||||
import FrescoPlugin from '../index';
|
||||
import {PersistedState, ImageEventWithId} from '../index';
|
||||
import {AndroidCloseableReferenceLeakEvent} from '../api';
|
||||
import {MetricType} from 'flipper';
|
||||
import {Notification} from '../../../plugin';
|
||||
import {ImagesMap} from '../ImagePool';
|
||||
|
||||
type ScanDisplayTime = {[scan_number: number]: number};
|
||||
|
||||
function mockPersistedState(
|
||||
imageSizes: Array<{
|
||||
width: number,
|
||||
height: number,
|
||||
width: number;
|
||||
height: number;
|
||||
}> = [],
|
||||
viewport: {
|
||||
width: number,
|
||||
height: number,
|
||||
width: number;
|
||||
height: number;
|
||||
} = {width: 150, height: 150},
|
||||
): PersistedState {
|
||||
const scanDisplayTime = {};
|
||||
const scanDisplayTime: ScanDisplayTime = {};
|
||||
scanDisplayTime[1] = 3;
|
||||
const events: Array<ImageEventWithId> = [
|
||||
{
|
||||
@@ -38,16 +41,19 @@ function mockPersistedState(
|
||||
},
|
||||
];
|
||||
|
||||
const imagesMap = imageSizes.reduce((acc, val, index) => {
|
||||
const imagesMap = imageSizes.reduce(
|
||||
(acc, val, index) => {
|
||||
acc[index] = {
|
||||
imageId: String(index),
|
||||
width: val.width,
|
||||
height: val.height,
|
||||
sizeBytes: 10,
|
||||
data: undefined,
|
||||
data: 'undefined',
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
{} as ImagesMap,
|
||||
);
|
||||
|
||||
return {
|
||||
surfaceList: new Set(),
|
||||
@@ -184,9 +190,8 @@ test('the metric reducer with the no viewPort data in events', () => {
|
||||
const metrics = metricsReducer(persistedState);
|
||||
return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0});
|
||||
});
|
||||
|
||||
test('the metric reducer with the multiple events', () => {
|
||||
const scanDisplayTime = {};
|
||||
const scanDisplayTime: ScanDisplayTime = {};
|
||||
scanDisplayTime[1] = 3;
|
||||
const events: Array<ImageEventWithId> = [
|
||||
{
|
||||
@@ -228,22 +233,27 @@ test('the metric reducer with the multiple events', () => {
|
||||
height: 300,
|
||||
},
|
||||
];
|
||||
const imagesMap = imageSizes.reduce((acc, val, index) => {
|
||||
const imagesMap = imageSizes.reduce(
|
||||
(acc, val, index) => {
|
||||
acc[index] = {
|
||||
imageId: String(index),
|
||||
width: val.width,
|
||||
height: val.height,
|
||||
sizeBytes: 10,
|
||||
data: undefined,
|
||||
data: 'undefined',
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
},
|
||||
{} as ImagesMap,
|
||||
);
|
||||
const persistedState = {
|
||||
surfaceList: new Set(),
|
||||
surfaceList: new Set<string>(),
|
||||
images: [],
|
||||
nextEventId: 0,
|
||||
events,
|
||||
imagesMap,
|
||||
closeableReferenceLeaks: [],
|
||||
isLeakTrackingEnabled: true,
|
||||
};
|
||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
||||
expect(metricsReducer).toBeDefined();
|
||||
@@ -255,7 +265,7 @@ test('the metric reducer with the multiple events', () => {
|
||||
test('closeable reference metrics on empty state', () => {
|
||||
const metricsReducer: (
|
||||
persistedState: PersistedState,
|
||||
) => Promise<MetricType> = (FrescoPlugin.metricsReducer: any);
|
||||
) => Promise<MetricType> = FrescoPlugin.metricsReducer;
|
||||
const persistedState = mockPersistedState();
|
||||
const metrics = metricsReducer(persistedState);
|
||||
return expect(metrics).resolves.toMatchObject({CLOSEABLE_REFERENCE_LEAKS: 0});
|
||||
@@ -264,7 +274,7 @@ test('closeable reference metrics on empty state', () => {
|
||||
test('closeable reference metrics on input', () => {
|
||||
const metricsReducer: (
|
||||
persistedState: PersistedState,
|
||||
) => Promise<MetricType> = (FrescoPlugin.metricsReducer: any);
|
||||
) => Promise<MetricType> = FrescoPlugin.metricsReducer;
|
||||
const closeableReferenceLeaks: Array<AndroidCloseableReferenceLeakEvent> = [
|
||||
{
|
||||
identityHashCode: 'deadbeef',
|
||||
@@ -288,7 +298,7 @@ test('closeable reference metrics on input', () => {
|
||||
test('notifications for leaks', () => {
|
||||
const notificationReducer: (
|
||||
persistedState: PersistedState,
|
||||
) => Array<Notification> = (FrescoPlugin.getActiveNotifications: any);
|
||||
) => Array<Notification> = FrescoPlugin.getActiveNotifications;
|
||||
const closeableReferenceLeaks: Array<AndroidCloseableReferenceLeakEvent> = [
|
||||
{
|
||||
identityHashCode: 'deadbeef',
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
export type ImageId = string;
|
||||
|
||||
// Listing images
|
||||
|
||||
export type CacheInfo = {|
|
||||
cacheType: string,
|
||||
clearKey?: null, // set this if this cache level supports clear(<key>)
|
||||
sizeBytes: number,
|
||||
maxSizeBytes?: number,
|
||||
imageIds: Array<ImageId>,
|
||||
|};
|
||||
|
||||
export type ImagesList = Array<CacheInfo>;
|
||||
|
||||
// The iOS Flipper api does not support a top-level array, so we wrap it in an object
|
||||
export type ImagesListResponse = {|
|
||||
levels: ImagesList,
|
||||
|};
|
||||
|
||||
// listImages() -> ImagesListResponse
|
||||
|
||||
// Getting details on a specific image
|
||||
|
||||
export type ImageBytes = string;
|
||||
|
||||
export type ImageData = {|
|
||||
imageId: ImageId,
|
||||
uri?: string,
|
||||
width: number,
|
||||
height: number,
|
||||
sizeBytes: number,
|
||||
data: ImageBytes,
|
||||
surface?: string,
|
||||
|};
|
||||
|
||||
// getImage({imageId: string}) -> ImageData
|
||||
|
||||
// Subscribing to image events (requests and prefetches)
|
||||
|
||||
export type Timestamp = number;
|
||||
|
||||
export type ViewportData = {|
|
||||
width: number,
|
||||
height: number,
|
||||
scanDisplayTime: {[scan_number: number]: Timestamp},
|
||||
|};
|
||||
|
||||
export type ImageEvent = {
|
||||
imageIds: Array<ImageId>,
|
||||
attribution: Array<string>,
|
||||
startTime: Timestamp,
|
||||
endTime: Timestamp,
|
||||
source: string,
|
||||
coldStart: boolean,
|
||||
viewport?: ViewportData, // not set for prefetches
|
||||
};
|
||||
|
||||
// Misc
|
||||
|
||||
export type FrescoDebugOverlayEvent = {|
|
||||
enabled: boolean,
|
||||
|};
|
||||
|
||||
export type AndroidCloseableReferenceLeakEvent = {|
|
||||
identityHashCode: string,
|
||||
className: string,
|
||||
stacktrace: ?string,
|
||||
|};
|
||||
77
src/plugins/fresco/api.tsx
Normal file
77
src/plugins/fresco/api.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
*/
|
||||
|
||||
export type ImageId = string;
|
||||
|
||||
// Listing images
|
||||
|
||||
export type CacheInfo = {
|
||||
cacheType: string;
|
||||
clearKey?: string; // set this if this cache level supports clear(<key>)
|
||||
sizeBytes: number;
|
||||
maxSizeBytes?: number;
|
||||
imageIds: Array<ImageId>;
|
||||
};
|
||||
|
||||
export type ImagesList = Array<CacheInfo>;
|
||||
|
||||
// The iOS Flipper api does not support a top-level array, so we wrap it in an object
|
||||
export type ImagesListResponse = {
|
||||
levels: ImagesList;
|
||||
};
|
||||
|
||||
// listImages() -> ImagesListResponse
|
||||
|
||||
// Getting details on a specific image
|
||||
|
||||
export type ImageBytes = string;
|
||||
|
||||
export type ImageData = {
|
||||
imageId: ImageId;
|
||||
uri?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
sizeBytes: number;
|
||||
data: ImageBytes;
|
||||
surface?: string;
|
||||
};
|
||||
|
||||
// getImage({imageId: string}) -> ImageData
|
||||
|
||||
// Subscribing to image events (requests and prefetches)
|
||||
|
||||
export type Timestamp = number;
|
||||
|
||||
export type ViewportData = {
|
||||
width: number;
|
||||
height: number;
|
||||
scanDisplayTime: {[scan_number: number]: Timestamp};
|
||||
};
|
||||
|
||||
export type ImageEvent = {
|
||||
imageIds: Array<ImageId>;
|
||||
attribution: Array<string>;
|
||||
startTime: Timestamp;
|
||||
endTime: Timestamp;
|
||||
source: string;
|
||||
coldStart: boolean;
|
||||
viewport?: ViewportData; // not set for prefetches
|
||||
};
|
||||
|
||||
// Misc
|
||||
|
||||
export type FrescoDebugOverlayEvent = {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type AndroidCloseableReferenceLeakEvent = {
|
||||
identityHashCode: string;
|
||||
className: string;
|
||||
stacktrace: string | null;
|
||||
};
|
||||
@@ -7,7 +7,7 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import type {
|
||||
import {
|
||||
ImageId,
|
||||
ImageData,
|
||||
ImagesList,
|
||||
@@ -16,12 +16,12 @@ import type {
|
||||
FrescoDebugOverlayEvent,
|
||||
AndroidCloseableReferenceLeakEvent,
|
||||
CacheInfo,
|
||||
} from './api.js';
|
||||
} from './api';
|
||||
import {Fragment} from 'react';
|
||||
import type {ImagesMap} from './ImagePool.js';
|
||||
import type {MetricType, MiddlewareAPI} from 'flipper';
|
||||
import {ImagesMap} from './ImagePool';
|
||||
import {MetricType, MiddlewareAPI} from 'flipper';
|
||||
import React from 'react';
|
||||
import ImagesCacheOverview from './ImagesCacheOverview.js';
|
||||
import ImagesCacheOverview from './ImagesCacheOverview';
|
||||
import {
|
||||
FlipperPlugin,
|
||||
FlexRow,
|
||||
@@ -31,29 +31,29 @@ import {
|
||||
styled,
|
||||
isProduction,
|
||||
} from 'flipper';
|
||||
import ImagesSidebar from './ImagesSidebar.js';
|
||||
import ImagePool from './ImagePool.js';
|
||||
import type {Notification} from '../../plugin.tsx';
|
||||
import ImagesSidebar from './ImagesSidebar';
|
||||
import ImagePool from './ImagePool';
|
||||
import {Notification, BaseAction} from '../../plugin';
|
||||
|
||||
export type ImageEventWithId = ImageEvent & {eventId: number};
|
||||
|
||||
export type PersistedState = {
|
||||
surfaceList: Set<string>,
|
||||
images: ImagesList,
|
||||
events: Array<ImageEventWithId>,
|
||||
imagesMap: ImagesMap,
|
||||
closeableReferenceLeaks: Array<AndroidCloseableReferenceLeakEvent>,
|
||||
isLeakTrackingEnabled: boolean,
|
||||
nextEventId: number,
|
||||
surfaceList: Set<string>;
|
||||
images: ImagesList;
|
||||
events: Array<ImageEventWithId>;
|
||||
imagesMap: ImagesMap;
|
||||
closeableReferenceLeaks: Array<AndroidCloseableReferenceLeakEvent>;
|
||||
isLeakTrackingEnabled: boolean;
|
||||
nextEventId: number;
|
||||
};
|
||||
|
||||
type PluginState = {
|
||||
selectedSurfaces: Set<string>,
|
||||
selectedImage: ?ImageId,
|
||||
isDebugOverlayEnabled: boolean,
|
||||
isAutoRefreshEnabled: boolean,
|
||||
images: ImagesList,
|
||||
coldStartFilter: boolean,
|
||||
selectedSurfaces: Set<string>;
|
||||
selectedImage: ImageId | null;
|
||||
isDebugOverlayEnabled: boolean;
|
||||
isAutoRefreshEnabled: boolean;
|
||||
images: ImagesList;
|
||||
coldStartFilter: boolean;
|
||||
};
|
||||
|
||||
const EmptySidebar = styled(FlexRow)({
|
||||
@@ -70,23 +70,23 @@ export const InlineFlexRow = styled(FlexRow)({
|
||||
|
||||
const surfaceDefaultText = 'SELECT ALL SURFACES';
|
||||
|
||||
const debugLog = (...args) => {
|
||||
const debugLog = (...args: any[]) => {
|
||||
if (!isProduction()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(...args);
|
||||
}
|
||||
};
|
||||
|
||||
type ImagesMetaData = {|
|
||||
levels: ImagesListResponse,
|
||||
events: Array<ImageEventWithId>,
|
||||
imageDataList: Array<ImageData>,
|
||||
|};
|
||||
type ImagesMetaData = {
|
||||
levels: ImagesListResponse;
|
||||
events: Array<ImageEventWithId>;
|
||||
imageDataList: Array<ImageData>;
|
||||
};
|
||||
|
||||
export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
PluginState,
|
||||
*,
|
||||
PersistedState,
|
||||
BaseAction,
|
||||
PersistedState
|
||||
> {
|
||||
static defaultPersistedState: PersistedState = {
|
||||
images: [],
|
||||
@@ -99,10 +99,10 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
};
|
||||
|
||||
static exportPersistedState = (
|
||||
callClient: (string, ?Object) => Promise<Object>,
|
||||
persistedState: ?PersistedState,
|
||||
store: ?MiddlewareAPI,
|
||||
): Promise<?PersistedState> => {
|
||||
callClient: (method: string, params?: any) => Promise<any>,
|
||||
persistedState: PersistedState,
|
||||
store?: MiddlewareAPI,
|
||||
): Promise<PersistedState> => {
|
||||
const defaultPromise = Promise.resolve(persistedState);
|
||||
if (!persistedState) {
|
||||
persistedState = FlipperImagesPlugin.defaultPersistedState;
|
||||
@@ -149,9 +149,9 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
acc.add(id);
|
||||
});
|
||||
return acc;
|
||||
}, new Set());
|
||||
}, new Set<string>());
|
||||
const imageDataList: Array<ImageData> = [];
|
||||
for (const id: string of idSet) {
|
||||
for (const id of idSet) {
|
||||
try {
|
||||
const imageData: ImageData = await callClient('getImage', {
|
||||
imageId: id,
|
||||
@@ -173,10 +173,10 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
static persistedStateReducer = (
|
||||
persistedState: PersistedState,
|
||||
method: string,
|
||||
data: Object,
|
||||
data: AndroidCloseableReferenceLeakEvent | ImageEvent,
|
||||
): PersistedState => {
|
||||
if (method == 'closeable_reference_leak_event') {
|
||||
const event: AndroidCloseableReferenceLeakEvent = data;
|
||||
const event: AndroidCloseableReferenceLeakEvent = data as AndroidCloseableReferenceLeakEvent;
|
||||
return {
|
||||
...persistedState,
|
||||
closeableReferenceLeaks: persistedState.closeableReferenceLeaks.concat(
|
||||
@@ -184,8 +184,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
),
|
||||
};
|
||||
} else if (method == 'events') {
|
||||
const event: ImageEvent = data;
|
||||
|
||||
const event: ImageEvent = data as ImageEvent;
|
||||
debugLog('Received events', event);
|
||||
const {surfaceList} = persistedState;
|
||||
const {attribution} = event;
|
||||
@@ -267,11 +266,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
category: 'closeablereference_leak',
|
||||
}));
|
||||
|
||||
state: PluginState;
|
||||
imagePool: ImagePool;
|
||||
nextEventId: number = 1;
|
||||
|
||||
state = {
|
||||
state: PluginState = {
|
||||
selectedSurfaces: new Set([surfaceDefaultText]),
|
||||
selectedImage: null,
|
||||
isDebugOverlayEnabled: false,
|
||||
@@ -279,6 +274,8 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
images: [],
|
||||
coldStartFilter: false,
|
||||
};
|
||||
imagePool: ImagePool | undefined;
|
||||
nextEventId: number = 1;
|
||||
|
||||
filterImages = (
|
||||
images: ImagesList,
|
||||
@@ -340,7 +337,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
}
|
||||
|
||||
teardown() {
|
||||
this.imagePool.clear();
|
||||
this.imagePool ? this.imagePool.clear() : undefined;
|
||||
}
|
||||
|
||||
updateImagesOnUI = (
|
||||
@@ -365,7 +362,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
debugLog('Requesting images list (reason=' + reason + ')');
|
||||
this.client.call('listImages').then((response: ImagesListResponse) => {
|
||||
response.levels.forEach(data =>
|
||||
this.imagePool.fetchImages(data.imageIds),
|
||||
this.imagePool ? this.imagePool.fetchImages(data.imageIds) : undefined,
|
||||
);
|
||||
this.props.setPersistedState({images: response.levels});
|
||||
this.updateImagesOnUI(
|
||||
@@ -409,7 +406,7 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
||||
debugLog('<- getImage requested for ' + imageId);
|
||||
this.client.call('getImage', {imageId}).then((image: ImageData) => {
|
||||
debugLog('-> getImage ' + imageId + ' returned');
|
||||
this.imagePool._fetchCompleted(image);
|
||||
this.imagePool ? this.imagePool._fetchCompleted(image) : undefined;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Fresco",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"main": "index.tsx",
|
||||
"license": "MIT",
|
||||
"keywords": ["flipper-plugin"],
|
||||
"title": "Images",
|
||||
|
||||
Reference in New Issue
Block a user