diff --git a/app/analytics/__tests__/index.test.js b/app/analytics/__tests__/index.test.js index 6bb9aac0e0..5cd290792a 100644 --- a/app/analytics/__tests__/index.test.js +++ b/app/analytics/__tests__/index.test.js @@ -1,5 +1,5 @@ import * as analytics from '../index'; -import {GA_HOST} from '../../common/constants'; +import {GA_HOST, getAppVersion, getAppPlatform} from '../../common/constants'; import * as db from '../../common/database'; import * as models from '../../models'; @@ -30,7 +30,7 @@ describe('init()', () => { await analytics.init('acct_123'); // Verify that Google Analytics works - expect(window.ga.mock.calls.length).toBe(5); + expect(window.ga.mock.calls.length).toBe(7); expect(window.ga.mock.calls[0][0]).toBe('create'); expect(window.ga.mock.calls[0][1]).toBe('UA-86416787-1'); expect(window.ga.mock.calls[0][2].storage).toBe('none'); @@ -39,20 +39,22 @@ describe('init()', () => { expect(window.ga.mock.calls[1].slice(0, 2)).toEqual(['set', 'checkProtocolTask']); expect(window.ga.mock.calls[1][2]()).toBeNull(); expect(window.ga.mock.calls[2]).toEqual(['set', 'location', `https://${GA_HOST}/`]); - expect(window.ga.mock.calls[3]).toEqual(['send', 'pageview']); - expect(window.ga.mock.calls[4]).toEqual(['set', 'userId', 'acct_123']); + expect(window.ga.mock.calls[3]).toEqual(['set', 'userId', 'acct_123']); + expect(window.ga.mock.calls[4]).toEqual(['set', 'dimension1', getAppPlatform()]); + expect(window.ga.mock.calls[5]).toEqual(['set', 'dimension2', getAppVersion()]); + expect(window.ga.mock.calls[6]).toEqual(['send', 'pageview']); analytics.trackEvent('foo', 'bar', 'baz'); - expect(window.ga.mock.calls.length).toBe(6); - expect(window.ga.mock.calls[5]).toEqual(['send', 'event', 'foo', 'bar', 'baz']); + expect(window.ga.mock.calls.length).toBe(8); + expect(window.ga.mock.calls[7]).toEqual(['send', 'event', 'foo', 'bar', 'baz']); analytics.setAccountId('acct_456'); - expect(window.ga.mock.calls.length).toBe(7); - expect(window.ga.mock.calls[6]).toEqual(['set', 'userId', 'acct_456']); + expect(window.ga.mock.calls.length).toBe(9); + expect(window.ga.mock.calls[8]).toEqual(['set', 'userId', 'acct_456']); // Try reinitializing analytics.init(); - expect(window.ga.mock.calls.length).toBe(7); + expect(window.ga.mock.calls.length).toBe(9); // TODO: Verify that Segment works (although it's not that important) }); diff --git a/app/analytics/google.js b/app/analytics/google.js index 186cb0fa9f..fba3550dd6 100644 --- a/app/analytics/google.js +++ b/app/analytics/google.js @@ -2,7 +2,10 @@ import * as constants from '../common/constants'; let _sessionId = null; -export function init (userId = null) { +const DIMENSION_PLATFORM = 'dimension1'; +const DIMENSION_VERSION = 'dimension2'; + +export function init (userId, platform, version) { if (constants.isDevelopment()) { console.log(`[ga] Not initializing for dev`); return; @@ -29,18 +32,40 @@ export function init (userId = null) { // Set a fake location window.ga('set', 'location', `https://${constants.GA_HOST}/`); + setUserId(userId); + setPlatform(platform); + setVersion(version); + // Track the initial page view window.ga('send', 'pageview'); - if (userId) { - setUserId(userId); - } - console.log(`[ga] Initialized for ${_sessionId}`); } +export function setPlatform (platform) { + if (!window.ga || !platform) { + return; + } + + ga('set', DIMENSION_PLATFORM, platform); + console.log(`[ga] Set platform ${platform}`); +} + +export function setVersion (version) { + if (!window.ga || !version) { + return; + } + + ga('set', DIMENSION_VERSION, version); + console.log(`[ga] Set version ${version}`); +} + export function setUserId (userId) { - window.ga && window.ga('set', 'userId', userId); + if (!window.ga || !userId) { + return; + } + + window.ga('set', 'userId', userId); console.log(`[ga] Set userId ${userId}`); } diff --git a/app/analytics/index.js b/app/analytics/index.js index 1a47348ec9..adb4de70c7 100644 --- a/app/analytics/index.js +++ b/app/analytics/index.js @@ -1,6 +1,7 @@ import * as segment from './segment'; import * as google from './google'; import {ipcRenderer} from 'electron'; +import {getAppVersion, getAppPlatform} from '../common/constants'; let initialized = false; export async function init (accountId) { @@ -10,7 +11,7 @@ export async function init (accountId) { try { await segment.init(); - await google.init(accountId); + await google.init(accountId, getAppPlatform(), getAppVersion()); initialized = true; } catch (e) { diff --git a/app/icons/icon.png b/app/icons/icon.png new file mode 100644 index 0000000000..f5d03f5550 Binary files /dev/null and b/app/icons/icon.png differ diff --git a/app/main.development.js b/app/main.development.js index e836c8ccf6..0aaab2f778 100644 --- a/app/main.development.js +++ b/app/main.development.js @@ -280,6 +280,7 @@ function createWindow () { minHeight: 500, minWidth: 500, acceptFirstMouse: true, + icon: path.resolve(__dirname, 'icons/icon.png'), webPreferences: { zoomFactor: zoomFactor } @@ -302,7 +303,7 @@ function createWindow () { clearTimeout(_moveTimeout); _moveTimeout = setTimeout(() => { trackEvent('Window', 'Move'); - }); + }, 1000); }); mainWindow.on('unresponsive', e => { showUnresponsiveModal(); diff --git a/app/package.json b/app/package.json index 9ef502d0c7..ad8665bb6d 100644 --- a/app/package.json +++ b/app/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "insomnia", - "version": "4.0.2", + "version": "4.0.4", "productName": "Insomnia", "longName": "Insomnia REST Client", "description": "A simple and beautiful REST API client", @@ -16,7 +16,7 @@ "electron-squirrel-startup": "^1.0.0", "hkdf": "0.0.2", "httpsnippet": "git@github.com:getinsomnia/httpsnippet.git#a3a2c0a0167fa844bf92df52a1442fa1d68a9053", - "insomnia-importers": "^1.2.0", + "insomnia-importers": "^1.2.5", "json-lint": "^0.1.0", "jsonpath-plus": "^0.15.0", "mime-types": "^2.1.12", diff --git a/app/ui/components/base/KeyValueEditor.js b/app/ui/components/base/KeyValueEditor.js index 80ba7b1138..f7c3a38ac3 100644 --- a/app/ui/components/base/KeyValueEditor.js +++ b/app/ui/components/base/KeyValueEditor.js @@ -3,6 +3,7 @@ import classnames from 'classnames'; import {DEBOUNCE_MILLIS} from '../../../common/constants'; import FileInputButton from '../base/FileInputButton'; import {Dropdown, DropdownItem, DropdownButton} from './dropdown/index'; +import PromptButton from '../base/PromptButton'; const NAME = 'name'; const VALUE = 'value'; @@ -202,7 +203,7 @@ class KeyValueEditor extends Component { const {maxPairs, className, valueInputType, multipart} = this.props; return ( -