SDA-2571 SDA-2721 Add annotate BI and fix window size on windows

This commit is contained in:
psjostrom 2020-11-27 17:07:46 +01:00
parent e18b431c98
commit 55f68fc225
6 changed files with 78 additions and 11 deletions

View File

@ -2,6 +2,7 @@ import { mount } from 'enzyme';
import * as React from 'react';
import AnnotateArea from '../src/renderer/components/annotate-area';
import { Tool } from '../src/renderer/components/snipping-tool';
import * as analyticsHandler from './../src/app/analytics-handler';
const defaultProps = {
paths: [],
@ -124,4 +125,14 @@ describe('<AnnotateArea/>', () => {
strokeWidth: 5,
}]);
});
it('should send capture_taken BI event on component mount', () => {
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
const expectedValue = { action_type: 'annotate_added', element: 'ScreenSnippet' };
const wrapper = mount(<AnnotateArea {...defaultProps} />);
const area = wrapper.find('[data-testid="annotate-area"]');
area.simulate('mousedown', { pageX: 2, pageY: 49 });
area.simulate('mouseup');
expect(spy).toBeCalledWith(expectedValue);
});
});

View File

@ -1,10 +1,15 @@
import { mount, shallow } from 'enzyme';
import * as React from 'react';
import SnippingTool from '../src/renderer/components/snipping-tool';
import * as analyticsHandler from './../src/app/analytics-handler';
import { ipcRenderer } from './__mocks__/electron';
const waitForPromisesToResolve = () => new Promise((resolve) => setTimeout(resolve));
afterEach(() => {
jest.clearAllMocks();
});
describe('Snipping Tool', () => {
it('should render correctly', () => {
const wrapper = shallow(React.createElement(SnippingTool));
@ -17,6 +22,23 @@ describe('Snipping Tool', () => {
expect(spy).toBeCalledWith('snipping-tool-data', expect.any(Function));
});
it('should send capture_taken BI event on component mount', () => {
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
const expectedValue = { action_type: 'capture_taken', element: 'ScreenSnippet' };
mount(React.createElement(SnippingTool));
expect(spy).toBeCalledWith(expectedValue);
});
it('should send capture_sent BI event when clicking done', async () => {
const spy = jest.spyOn(analyticsHandler.analytics, 'track');
const expectedValue = { action_type: 'capture_sent', element: 'ScreenSnippet' };
const wrapper = mount(React.createElement(SnippingTool));
wrapper.find('[data-testid="done-button"]').simulate('click');
wrapper.update();
await waitForPromisesToResolve();
expect(spy).toBeCalledWith(expectedValue);
});
it('should render pen color picker when clicked on pen', () => {
const wrapper = shallow(React.createElement(SnippingTool));
wrapper.find('[data-testid="pen-button"]').simulate('click');

View File

@ -1,7 +1,7 @@
export interface IAnalyticsData {
element: string;
action_type: MenuActionTypes;
action_result: string;
element: AnalyticsElements;
action_type: MenuActionTypes | ScreenSnippetActionTypes;
action_result?: AnalyticsActions;
}
export enum MenuActionTypes {
@ -13,6 +13,12 @@ export enum MenuActionTypes {
REFRESH_APP_IN_IDLE = 'refresh_app_in_idle',
}
export enum ScreenSnippetActionTypes {
CAPTURE_TAKEN = 'capture_taken',
ANNOTATE_ADDED = 'annotate_added',
CAPTURE_SENT = 'capture_sent',
}
export enum AnalyticsActions {
ENABLED = 'ON',
DISABLED = 'OFF',
@ -20,6 +26,7 @@ export enum AnalyticsActions {
export enum AnalyticsElements {
MENU = 'Menu',
SCREEN_SNIPPET = 'ScreenSnippet',
}
const MAX_EVENT_QUEUE_LENGTH = 50;
@ -61,7 +68,7 @@ class Analytics {
return;
}
this.analyticsEventQueue.push(eventData);
// don't store more than 100 msgs. keep most recent log msgs.
// don't store more than 50 msgs. keep most recent log msgs.
if (this.analyticsEventQueue.length > MAX_EVENT_QUEUE_LENGTH) {
this.analyticsEventQueue.shift();
}

View File

@ -991,8 +991,8 @@ export class WindowHandler {
const opts: ICustomBrowserWindowConstructorOpts = this.getWindowOpts(
{
width: annotateAreaWidth,
height: windowHeight,
width: annotateAreaWidth < MIN_WIDTH ? MIN_WIDTH : annotateAreaWidth,
height: windowHeight < MIN_HEIGHT ? MIN_HEIGHT : windowHeight,
minHeight: MIN_HEIGHT,
minWidth: MIN_WIDTH,
modal: false,

View File

@ -1,5 +1,6 @@
import { LazyBrush } from 'lazy-brush';
import * as React from 'react';
import { analytics, AnalyticsElements, ScreenSnippetActionTypes } from './../../app/analytics-handler';
import { IDimensions, IPath, IPoint, Tool } from './snipping-tool';
const { useState } = React;
@ -224,6 +225,14 @@ const AnnotateArea: React.FunctionComponent<IAnnotateAreaProps> = ({
addPathPoint(e);
};
const handleMouseUp = () => {
stopDrawing();
analytics.track({
element: AnalyticsElements.SCREEN_SNIPPET,
action_type: ScreenSnippetActionTypes.ANNOTATE_ADDED,
});
};
const getAnnotateWrapperStyle = () => {
const shouldShowScrollBars =
imageDimensions.height > annotateAreaDimensions.height ||
@ -246,7 +255,7 @@ const AnnotateArea: React.FunctionComponent<IAnnotateAreaProps> = ({
width={imageDimensions.width}
height={imageDimensions.height}
onMouseDown={handleMouseDown}
onMouseUp={stopDrawing}
onMouseUp={handleMouseUp}
onMouseMove={handleMouseMove}
onMouseLeave={stopDrawing}
>

View File

@ -1,6 +1,7 @@
import { ipcRenderer } from 'electron';
import * as React from 'react';
import { i18n } from '../../common/i18n-preload';
import { analytics, AnalyticsElements, ScreenSnippetActionTypes } from './../../app/analytics-handler';
import AnnotateArea from './annotate-area';
import ColorPickerPill, { IColor } from './color-picker-pill';
@ -89,9 +90,13 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
useLayoutEffect(() => {
ipcRenderer.once('snipping-tool-data', getSnipImageData);
return () => {
ipcRenderer.removeListener('snipping-tool-data', getSnipImageData);
};
analytics.track({
element: AnalyticsElements.SCREEN_SNIPPET,
action_type: ScreenSnippetActionTypes.CAPTURE_TAKEN,
});
return () => {
ipcRenderer.removeListener('snipping-tool-data', getSnipImageData);
};
}, []);
// Hook that alerts clicks outside of the passed refs
@ -176,6 +181,16 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
return 'NO CANVAS';
}
const backgroundImage = document.getElementById('backgroundImage') as HTMLImageElement;
// Fast lane in case there is no drawn SVG paths
if (paths.length === 0) {
ctx.drawImage(backgroundImage, 0, 0);
// Extracts base 64 png img data from the canvas
const data = canvas.toDataURL('image/png');
return data;
}
// Creates an in memory img without adding it to the DOM
const img = document.createElement('img');
@ -187,7 +202,6 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
'src',
'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgData))),
);
const backgroundImage = document.getElementById('backgroundImage') as HTMLImageElement;
return new Promise((resolve, reject) => {
// Listens to when the img is loaded in memory and adds the data from the SVG paths + screenSnippet to the canvas
@ -233,6 +247,10 @@ const SnippingTool: React.FunctionComponent<ISnippingToolProps> = ({ existingPat
const done = async () => {
const base64PngData = await getBase64PngData();
analytics.track({
element: AnalyticsElements.SCREEN_SNIPPET,
action_type: ScreenSnippetActionTypes.CAPTURE_SENT,
});
ipcRenderer.send('upload-snippet', { screenSnippetPath, base64PngData });
};