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 (
-
+
{pairs.map((pair, i) => (
-
-
+
))}
{!maxPairs || pairs.length < maxPairs ? (
diff --git a/app/ui/components/base/Link.js b/app/ui/components/base/Link.js
index 078b1d5e0a..b0211546ff 100644
--- a/app/ui/components/base/Link.js
+++ b/app/ui/components/base/Link.js
@@ -15,7 +15,7 @@ class Link extends Component {
if (href.match(/^http/i)) {
const appName = isDevelopment() ? 'Insomnia Dev' : 'Insomnia';
- const qs = `utm_source=${appName}&utm_medium=App&utm_campaign=v${getAppVersion()}`;
+ const qs = `utm_source=${appName}&utm_medium=app&utm_campaign=v${getAppVersion()}`;
const attributedHref = querystring.joinUrl(href, qs);
shell.openExternal(attributedHref);
} else {
diff --git a/app/ui/components/dropdowns/ResponseHistoryDropdown.js b/app/ui/components/dropdowns/ResponseHistoryDropdown.js
index a38b71141f..ecc823bed8 100644
--- a/app/ui/components/dropdowns/ResponseHistoryDropdown.js
+++ b/app/ui/components/dropdowns/ResponseHistoryDropdown.js
@@ -5,6 +5,7 @@ import StatusTag from '../tags/StatusTag';
import TimeTag from '../tags/TimeTag';
import * as models from '../../../models/index';
import PromptButton from '../base/PromptButton';
+import {trackEvent} from '../../../analytics/index';
class ResponseHistoryDropdown extends Component {
state = {
@@ -12,9 +13,15 @@ class ResponseHistoryDropdown extends Component {
};
_handleDeleteResponses = () => {
+ trackEvent('History', 'Delete Responses');
this.props.handleDeleteResponses(this.props.requestId);
};
+ _handleSetActiveResponse = responseId => {
+ trackEvent('History', 'Activate Response');
+ this.props.handleSetActiveResponse(responseId);
+ };
+
async _load (requestId) {
const responses = await models.response.findRecentForRequest(requestId);
@@ -40,13 +47,13 @@ class ResponseHistoryDropdown extends Component {
}
renderDropdownItem = (response, i) => {
- const {activeResponseId, handleSetActiveResponse} = this.props;
+ const {activeResponseId} = this.props;
const active = response._id === activeResponseId;
return (
+ onClick={this._handleSetActiveResponse}>
{active ? : }
{" "}
{
const {workspace} = this.props;
const certificates = workspace.certificates.filter(c => c.host !== certificate.host);
models.workspace.update(workspace, {certificates});
+ trackEvent('Certificates', 'Delete');
};
_handleToggleCertificate = certificate => {
@@ -71,6 +74,7 @@ class WorkspaceSettingsModal extends Component {
c => c === certificate ? Object.assign({}, c, {disabled: !c.disabled}) : c
);
models.workspace.update(workspace, {certificates});
+ trackEvent('Certificates', 'Toggle');
};
toggle (workspace) {
@@ -303,7 +307,7 @@ class WorkspaceSettingsModal extends Component {
{" "}
@@ -320,11 +324,6 @@ class WorkspaceSettingsModal extends Component {
{workspace ? this.renderModalHeader() : null}
{workspace ? this.renderModalBody() : null}
- {/**/}
- {/**/}
- {/**/}
)
}
diff --git a/app/ui/containers/App.js b/app/ui/containers/App.js
index 0b54631475..0dd1dbc127 100644
--- a/app/ui/containers/App.js
+++ b/app/ui/containers/App.js
@@ -217,10 +217,21 @@ class App extends Component {
};
_handleSendRequestWithEnvironment = async (requestId, environmentId) => {
- this.props.handleStartLoading(requestId);
+ const request = await models.request.getById(requestId);
+ if (!request) {
+ return;
+ }
- trackEvent('Request', 'Send');
- trackLegacyEvent('Request Send');
+ // NOTE: Since request is by far the most popular event, we will throttle
+ // it so that we only track it if the request has changed since the last noe
+ const key = `${request._id}::${request.modified}`;
+ if (this._sendRequestTrackingKey !== key) {
+ trackEvent('Request', 'Send');
+ trackLegacyEvent('Request Send');
+ this._sendRequestTrackingKey = key;
+ }
+
+ this.props.handleStartLoading(requestId);
try {
await network.send(requestId, environmentId);
@@ -244,12 +255,12 @@ class App extends Component {
};
_startDragSidebar = () => {
- trackEvent('Sidebar', 'Drag Start');
+ trackEvent('Sidebar', 'Drag');
this.setState({draggingSidebar: true})
};
_resetDragSidebar = () => {
- trackEvent('Sidebar', 'Drag Reset');
+ trackEvent('Sidebar', 'Drag');
// TODO: Remove setTimeout need be not triggering drag on double click
setTimeout(() => this._handleSetSidebarWidth(DEFAULT_SIDEBAR_WIDTH), 50);
};
@@ -260,7 +271,7 @@ class App extends Component {
};
_resetDragPane = () => {
- trackEvent('App Pane', 'Reset');
+ trackEvent('App Pane', 'Drag Reset');
// TODO: Remove setTimeout need be not triggering drag on double click
setTimeout(() => this._handleSetPaneWidth(DEFAULT_PANE_WIDTH), 50);
};
@@ -329,6 +340,8 @@ class App extends Component {
} else if (lastVersion !== getAppVersion()) {
trackEvent('General', 'Updated', getAppVersion());
showModal(ChangelogModal);
+ } else {
+ trackEvent('General', 'Launched', getAppVersion());
}
db.onChange(changes => {
diff --git a/app/ui/redux/create.js b/app/ui/redux/create.js
index 42614b6d00..5ce53c7f9f 100644
--- a/app/ui/redux/create.js
+++ b/app/ui/redux/create.js
@@ -6,8 +6,8 @@ export default function configureStore () {
const middleware = [thunkMiddleware];
if (__DEV__) {
- const createLogger = require('redux-logger');
- middleware.push(createLogger({collapsed: true}));
+ // const createLogger = require('redux-logger');
+ // middleware.push(createLogger({collapsed: true}));
}
const store = createStore(reducer, applyMiddleware(...middleware));
diff --git a/package.json b/package.json
index 5f0e55647c..c1f8ec09b7 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,8 @@
"build:copy": "cp -r ./app/package.json ./app/static ./app/icons/* ./build/",
"build:install": "cd build && npm install",
"build": "npm run build:clean && npm run build:renderer && npm run build:main && npm run build:copy && npm run build:install",
- "package:mac": "rm -rf dist/mac && node node_modules/electron-builder/out/build-cli.js --publish=never --x64 --mac",
- "package:win": "rm -rf dist/win* && node node_modules/electron-builder/out/build-cli.js --publish=never --x64 --win",
+ "package:mac": "rm -rf dist/mac && node node_modules/electron-builder/out/cli/build-cli.js --publish=never --x64 --mac",
+ "package:win": "rm -rf dist/win* && node node_modules/electron-builder/out/cli/build-cli.js --publish=never --x64 --win",
"package:linux": "rm -rf dist/linux && rm -f dist/*.AppImage && rm -f dist/*.deb && node node_modules/electron-builder/out/build-cli.js --publish=never --x64 --linux",
"package": "npm test && rm -rf dist && npm run package:mac && npm run package:win && npm run package:linux",
"build-n-package": "npm run build && npm run package",
@@ -101,7 +101,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",
@@ -149,8 +149,8 @@
"concurrently": "^2.0.0",
"cross-env": "^2.0.0",
"css-loader": "^0.23.1",
- "electron": "^1.4.7",
- "electron-builder": "^8.6.0",
+ "electron": "^1.4.10",
+ "electron-builder": "^10.5.0",
"electron-devtools-installer": "^2.0.1",
"express": "^4.14.0",
"file-loader": "^0.9.0",