mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
chore: move electron out of the renderer (#4406)
* fix lint * fix types * remove flush db.change event send * guard against tests running electron functions * copy preload to build output * fix webview context menu * fix context menu and plugin install * move installPlugin to main context
This commit is contained in:
parent
837342ddab
commit
3947bdc4aa
@ -247,12 +247,14 @@ export const database = {
|
|||||||
for (const fn of changeListeners) {
|
for (const fn of changeListeners) {
|
||||||
await fn(changes);
|
await fn(changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify remote listeners
|
// Notify remote listeners
|
||||||
const windows = electron.BrowserWindow.getAllWindows();
|
const isMainContext = process.type === 'browser';
|
||||||
|
if (isMainContext){
|
||||||
|
const windows = electron.BrowserWindow.getAllWindows();
|
||||||
|
|
||||||
for (const window of windows) {
|
for (const window of windows) {
|
||||||
window.webContents.send('db.changes', changes);
|
window.webContents.send('db.changes', changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -19,66 +19,18 @@ export function clickLink(href: string) {
|
|||||||
export const getPortableExecutableDir = () => process.env.PORTABLE_EXECUTABLE_DIR;
|
export const getPortableExecutableDir = () => process.env.PORTABLE_EXECUTABLE_DIR;
|
||||||
|
|
||||||
export function getDataDirectory() {
|
export function getDataDirectory() {
|
||||||
const { app } = electron.remote || electron;
|
const { app } = process.type === 'renderer' ? window : electron;
|
||||||
return process.env.INSOMNIA_DATA_PATH || app.getPath('userData');
|
return process.env.INSOMNIA_DATA_PATH || app.getPath('userData');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getViewportSize(): string | null {
|
|
||||||
const { BrowserWindow } = electron.remote || electron;
|
|
||||||
const browserWindow = BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0];
|
|
||||||
|
|
||||||
if (browserWindow) {
|
|
||||||
const { width, height } = browserWindow.getContentBounds();
|
|
||||||
return `${width}x${height}`;
|
|
||||||
} else {
|
|
||||||
// No windows open
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getScreenResolution() {
|
|
||||||
const { screen } = electron.remote || electron;
|
|
||||||
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
|
||||||
return `${width}x${height}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserLanguage() {
|
|
||||||
const { app } = electron.remote || electron;
|
|
||||||
return app.getLocale();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getTempDir() {
|
export function getTempDir() {
|
||||||
// NOTE: Using a fairly unique name here because "insomnia" is a common word
|
// NOTE: Using a fairly unique name here because "insomnia" is a common word
|
||||||
const { app } = electron.remote || electron;
|
const { app } = process.type === 'renderer' ? window : electron;
|
||||||
const dir = join(app.getPath('temp'), `insomnia_${appConfig.version}`);
|
const dir = join(app.getPath('temp'), `insomnia_${appConfig.version}`);
|
||||||
mkdirp.sync(dir);
|
mkdirp.sync(dir);
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function restartApp() {
|
|
||||||
const { app } = electron.remote || electron;
|
|
||||||
app.relaunch();
|
|
||||||
app.exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
export const exitAppFailure = () => {
|
|
||||||
const { app } = electron.remote || electron;
|
|
||||||
app.exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setMenuBarVisibility = (visible: boolean) => {
|
|
||||||
const { BrowserWindow } = electron.remote || electron;
|
|
||||||
BrowserWindow.getAllWindows()
|
|
||||||
.forEach(window => {
|
|
||||||
// the `setMenuBarVisibility` signature uses `visible` semantics
|
|
||||||
window.setMenuBarVisibility(visible);
|
|
||||||
|
|
||||||
// the `setAutoHideMenu` signature uses `hide` semantics
|
|
||||||
const hide = !visible;
|
|
||||||
window.setAutoHideMenuBar(hide);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* There's no option that prevents Electron from fetching spellcheck dictionaries from Chromium's CDN and passing a non-resolving URL is the only known way to prevent it from fetching.
|
* There's no option that prevents Electron from fetching spellcheck dictionaries from Chromium's CDN and passing a non-resolving URL is the only known way to prevent it from fetching.
|
||||||
* see: https://github.com/electron/electron/issues/22995
|
* see: https://github.com/electron/electron/issues/22995
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { OpenDialogOptions, remote } from 'electron';
|
import { OpenDialogOptions } from 'electron';
|
||||||
import { unreachableCase } from 'ts-assert-unreachable';
|
import { unreachableCase } from 'ts-assert-unreachable';
|
||||||
|
|
||||||
interface Options {
|
interface Options {
|
||||||
@ -49,7 +49,8 @@ export const selectFileOrFolder = async ({ itemTypes, extensions }: Options) =>
|
|||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(options);
|
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||||
|
|
||||||
const fileSelection: FileSelection = {
|
const fileSelection: FileSelection = {
|
||||||
filePath: filePaths[0],
|
filePath: filePaths[0],
|
||||||
canceled,
|
canceled,
|
||||||
|
17
packages/insomnia-app/app/global.d.ts
vendored
17
packages/insomnia-app/app/global.d.ts
vendored
@ -22,6 +22,23 @@ declare namespace NodeJS {
|
|||||||
|
|
||||||
interface Window {
|
interface Window {
|
||||||
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function;
|
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: Function;
|
||||||
|
main: {
|
||||||
|
restart: () => void;
|
||||||
|
authorizeUserInWindow: (options: { url: string; urlSuccessRegex?: RegExp; urlFailureRegex?: RegExp; sessionId: string }) => Promise<string>;
|
||||||
|
setMenuBarVisibility: (visible: boolean) => void;
|
||||||
|
installPlugin: (url: string) => void;
|
||||||
|
};
|
||||||
|
dialog: {
|
||||||
|
showOpenDialog: (options: Electron.OpenDialogOptions) => Promise<Electron.OpenDialogReturnValue>;
|
||||||
|
showSaveDialog: (options: Electron.SaveDialogOptions) => Promise<Electron.SaveDialogReturnValue>;
|
||||||
|
};
|
||||||
|
app: {
|
||||||
|
getPath: (name: string) => string;
|
||||||
|
getAppPath: () => string;
|
||||||
|
};
|
||||||
|
shell: {
|
||||||
|
showItemInFolder: (fullPath: string) => void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed for @hot-loader/react-dom in order for TypeScript to build
|
// needed for @hot-loader/react-dom in order for TypeScript to build
|
||||||
|
@ -7,7 +7,7 @@ import appConfig from '../config/config.json';
|
|||||||
import { SegmentEvent, trackSegmentEvent } from './common/analytics';
|
import { SegmentEvent, trackSegmentEvent } from './common/analytics';
|
||||||
import { changelogUrl, getAppVersion, isDevelopment, isMac } from './common/constants';
|
import { changelogUrl, getAppVersion, isDevelopment, isMac } from './common/constants';
|
||||||
import { database } from './common/database';
|
import { database } from './common/database';
|
||||||
import { disableSpellcheckerDownload, exitAppFailure } from './common/electron-helpers';
|
import { disableSpellcheckerDownload } from './common/electron-helpers';
|
||||||
import log, { initializeLogging } from './common/log';
|
import log, { initializeLogging } from './common/log';
|
||||||
import { validateInsomniaConfig } from './common/validate-insomnia-config';
|
import { validateInsomniaConfig } from './common/validate-insomnia-config';
|
||||||
import * as errorHandling from './main/error-handling';
|
import * as errorHandling from './main/error-handling';
|
||||||
@ -17,6 +17,8 @@ import * as updates from './main/updates';
|
|||||||
import * as windowUtils from './main/window-utils';
|
import * as windowUtils from './main/window-utils';
|
||||||
import * as models from './models/index';
|
import * as models from './models/index';
|
||||||
import type { Stats } from './models/stats';
|
import type { Stats } from './models/stats';
|
||||||
|
import { authorizeUserInWindow } from './network/o-auth-2/misc';
|
||||||
|
import installPlugin from './plugins/install';
|
||||||
import type { ToastNotification } from './ui/components/toast';
|
import type { ToastNotification } from './ui/components/toast';
|
||||||
|
|
||||||
// Handle potential auto-update
|
// Handle potential auto-update
|
||||||
@ -41,7 +43,14 @@ if (!isDevelopment()) {
|
|||||||
// So if (window) checks don't throw
|
// So if (window) checks don't throw
|
||||||
global.window = global.window || undefined;
|
global.window = global.window || undefined;
|
||||||
|
|
||||||
contextMenu();
|
// setup right click menu
|
||||||
|
app.on('web-contents-created', (_, contents) => {
|
||||||
|
if (contents.getType() === 'webview') {
|
||||||
|
contextMenu({ window: contents });
|
||||||
|
} else {
|
||||||
|
contextMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// When the app is first launched
|
// When the app is first launched
|
||||||
app.on('ready', async () => {
|
app.on('ready', async () => {
|
||||||
@ -50,7 +59,7 @@ app.on('ready', async () => {
|
|||||||
if (error) {
|
if (error) {
|
||||||
electron.dialog.showErrorBox(error.title, error.message);
|
electron.dialog.showErrorBox(error.title, error.message);
|
||||||
console.log('[config] Insomnia config is invalid, preventing app initialization');
|
console.log('[config] Insomnia config is invalid, preventing app initialization');
|
||||||
exitAppFailure();
|
app.exit(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +212,53 @@ async function _trackStats() {
|
|||||||
|
|
||||||
trackSegmentEvent(SegmentEvent.appStarted, {}, { queueable: true });
|
trackSegmentEvent(SegmentEvent.appStarted, {}, { queueable: true });
|
||||||
|
|
||||||
|
ipcMain.handle('showOpenDialog', async (_, options: Electron.OpenDialogOptions) => {
|
||||||
|
const { filePaths, canceled } = await electron.dialog.showOpenDialog(options);
|
||||||
|
return { filePaths, canceled };
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('showSaveDialog', async (_, options: Electron.SaveDialogOptions) => {
|
||||||
|
const { filePath, canceled } = await electron.dialog.showSaveDialog(options);
|
||||||
|
return { filePath, canceled };
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('installPlugin', async (_, options) => {
|
||||||
|
return installPlugin(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('showItemInFolder', (_, name) => {
|
||||||
|
electron.shell.showItemInFolder(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('restart', () => {
|
||||||
|
app.relaunch();
|
||||||
|
app.exit();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('setMenuBarVisibility', (_, visible) => {
|
||||||
|
electron.BrowserWindow.getAllWindows()
|
||||||
|
.forEach(window => {
|
||||||
|
// the `setMenuBarVisibility` signature uses `visible` semantics
|
||||||
|
window.setMenuBarVisibility(visible);
|
||||||
|
// the `setAutoHideMenu` signature uses `hide` semantics
|
||||||
|
const hide = !visible;
|
||||||
|
window.setAutoHideMenuBar(hide);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('getPath', (event, name) => {
|
||||||
|
event.returnValue = electron.app.getPath(name);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on('getAppPath', event => {
|
||||||
|
event.returnValue = electron.app.getAppPath();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('authorizeUserInWindow', (_, options) => {
|
||||||
|
const { url, urlSuccessRegex, urlFailureRegex, sessionId } = options;
|
||||||
|
return authorizeUserInWindow({ url, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.once('window-ready', () => {
|
ipcMain.once('window-ready', () => {
|
||||||
const { currentVersion, launches, lastVersion } = stats;
|
const { currentVersion, launches, lastVersion } = stats;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
MNEMONIC_SYM,
|
MNEMONIC_SYM,
|
||||||
} from '../common/constants';
|
} from '../common/constants';
|
||||||
import { docsBase } from '../common/documentation';
|
import { docsBase } from '../common/documentation';
|
||||||
import { clickLink, getDataDirectory, restartApp } from '../common/electron-helpers';
|
import { clickLink, getDataDirectory } from '../common/electron-helpers';
|
||||||
import * as log from '../common/log';
|
import * as log from '../common/log';
|
||||||
import LocalStorage from './local-storage';
|
import LocalStorage from './local-storage';
|
||||||
|
|
||||||
@ -85,10 +85,10 @@ export function createWindow() {
|
|||||||
acceptFirstMouse: true,
|
acceptFirstMouse: true,
|
||||||
icon: path.resolve(__dirname, appLogo),
|
icon: path.resolve(__dirname, appLogo),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
zoomFactor: zoomFactor,
|
zoomFactor: zoomFactor,
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
webviewTag: true,
|
webviewTag: true,
|
||||||
enableRemoteModule: true,
|
|
||||||
// TODO: enable context isolation
|
// TODO: enable context isolation
|
||||||
contextIsolation: false,
|
contextIsolation: false,
|
||||||
disableBlinkFeatures: 'Auxclick',
|
disableBlinkFeatures: 'Auxclick',
|
||||||
@ -482,7 +482,7 @@ export function createWindow() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `R${MNEMONIC_SYM}estart`,
|
label: `R${MNEMONIC_SYM}estart`,
|
||||||
click: restartApp,
|
click: window?.main.restart,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
import electron from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { globalBeforeEach } from '../../__jest__/before-each';
|
import { globalBeforeEach } from '../../__jest__/before-each';
|
||||||
import { buildMultipart, DEFAULT_BOUNDARY } from '../multipart';
|
import { buildMultipart, DEFAULT_BOUNDARY } from '../multipart';
|
||||||
|
window.app = electron.app;
|
||||||
describe('buildMultipart()', () => {
|
describe('buildMultipart()', () => {
|
||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CurlHttpVersion } from '@getinsomnia/node-libcurl';
|
import { CurlHttpVersion } from '@getinsomnia/node-libcurl';
|
||||||
|
import electron from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { HttpVersions } from 'insomnia-common';
|
import { HttpVersions } from 'insomnia-common';
|
||||||
import { join as pathJoin, resolve as pathResolve } from 'path';
|
import { join as pathJoin, resolve as pathResolve } from 'path';
|
||||||
@ -18,6 +19,7 @@ import { getRenderedRequestAndContext } from '../../common/render';
|
|||||||
import * as models from '../../models';
|
import * as models from '../../models';
|
||||||
import { DEFAULT_BOUNDARY } from '../multipart';
|
import { DEFAULT_BOUNDARY } from '../multipart';
|
||||||
import * as networkUtils from '../network';
|
import * as networkUtils from '../network';
|
||||||
|
window.app = electron.app;
|
||||||
|
|
||||||
const getRenderedRequest = async (args: Parameters<typeof getRenderedRequestAndContext>[0]) => (await getRenderedRequestAndContext(args)).request;
|
const getRenderedRequest = async (args: Parameters<typeof getRenderedRequestAndContext>[0]) => (await getRenderedRequestAndContext(args)).request;
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as electron from 'electron';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import mimes from 'mime-types';
|
import mimes from 'mime-types';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -15,7 +14,7 @@ interface Multipart {
|
|||||||
|
|
||||||
export async function buildMultipart(params: RequestBodyParameter[]) {
|
export async function buildMultipart(params: RequestBodyParameter[]) {
|
||||||
return new Promise<Multipart>(async (resolve, reject) => {
|
return new Promise<Multipart>(async (resolve, reject) => {
|
||||||
const filePath = path.join(electron.remote.app.getPath('temp'), Math.random() + '.body');
|
const filePath = path.join(window.app.getPath('temp'), Math.random() + '.body');
|
||||||
const writeStream = fs.createWriteStream(filePath);
|
const writeStream = fs.createWriteStream(filePath);
|
||||||
const lineBreak = '\r\n';
|
const lineBreak = '\r\n';
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
@ -5,7 +5,6 @@ import { globalBeforeEach } from '../../../__jest__/before-each';
|
|||||||
import { getTempDir } from '../../../common/electron-helpers';
|
import { getTempDir } from '../../../common/electron-helpers';
|
||||||
import * as network from '../../network';
|
import * as network from '../../network';
|
||||||
import getToken from '../grant-authorization-code';
|
import getToken from '../grant-authorization-code';
|
||||||
import { createBWRedirectMock } from './helpers';
|
|
||||||
|
|
||||||
// Mock some test things
|
// Mock some test things
|
||||||
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
||||||
@ -22,7 +21,7 @@ describe('authorization_code', () => {
|
|||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
|
|
||||||
it('gets token with JSON and basic auth', async () => {
|
it('gets token with JSON and basic auth', async () => {
|
||||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
bodyPath,
|
bodyPath,
|
||||||
@ -130,7 +129,7 @@ describe('authorization_code', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('gets token with urlencoded and body auth', async () => {
|
it('gets token with urlencoded and body auth', async () => {
|
||||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
bodyPath,
|
bodyPath,
|
||||||
@ -242,7 +241,7 @@ describe('authorization_code', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses PKCE', async () => {
|
it('uses PKCE', async () => {
|
||||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}?code=code_123&state=${STATE}` });
|
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}?code=code_123&state=${STATE}`) };
|
||||||
const bodyPath = path.join(getTempDir(), 'foo.response');
|
const bodyPath = path.join(getTempDir(), 'foo.response');
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
bodyPath,
|
bodyPath,
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { globalBeforeEach } from '../../../__jest__/before-each';
|
import { globalBeforeEach } from '../../../__jest__/before-each';
|
||||||
import getToken from '../grant-implicit';
|
import getToken from '../grant-implicit';
|
||||||
import { createBWRedirectMock } from './helpers';
|
|
||||||
// Mock some test things
|
// Mock some test things
|
||||||
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
const AUTHORIZE_URL = 'https://foo.com/authorizeAuthCode';
|
||||||
const CLIENT_ID = 'client_123';
|
const CLIENT_ID = 'client_123';
|
||||||
@ -13,7 +12,8 @@ describe('implicit', () => {
|
|||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
|
|
||||||
it('works in default case', async () => {
|
it('works in default case', async () => {
|
||||||
createBWRedirectMock({ redirectTo: `${REDIRECT_URI}#access_token=token_123&state=${STATE}&foo=bar` });
|
window.main = { authorizeUserInWindow: () => Promise.resolve(`${REDIRECT_URI}#access_token=token_123&state=${STATE}&foo=bar`) };
|
||||||
|
|
||||||
const result = await getToken(AUTHORIZE_URL, CLIENT_ID, REDIRECT_URI, SCOPE, STATE, AUDIENCE);
|
const result = await getToken(AUTHORIZE_URL, CLIENT_ID, REDIRECT_URI, SCOPE, STATE, AUDIENCE);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
access_token: 'token_123',
|
access_token: 'token_123',
|
||||||
|
@ -10,7 +10,7 @@ export function createBWRedirectMock({
|
|||||||
redirectTo,
|
redirectTo,
|
||||||
setCertificateVerifyProc = () => {},
|
setCertificateVerifyProc = () => {},
|
||||||
}: Options) {
|
}: Options) {
|
||||||
electron.remote.BrowserWindow = jest.fn(function() {
|
electron.BrowserWindow = jest.fn(function() {
|
||||||
this._emitter = new EventEmitter();
|
this._emitter = new EventEmitter();
|
||||||
|
|
||||||
this.loadURL = () => this.webContents.emit('did-navigate');
|
this.loadURL = () => this.webContents.emit('did-navigate');
|
||||||
|
@ -64,6 +64,8 @@ describe('authorizeUserInWindow()', () => {
|
|||||||
|
|
||||||
const getCertificateVerifyCallbackMock = () => {
|
const getCertificateVerifyCallbackMock = () => {
|
||||||
const mockCallback = mocked<(verificationResult: number) => void>(jest.fn());
|
const mockCallback = mocked<(verificationResult: number) => void>(jest.fn());
|
||||||
|
window.main = { authorizeUserInWindow: () => Promise.resolve(MOCK_AUTHORIZATION_URL) };
|
||||||
|
|
||||||
createBWRedirectMock({
|
createBWRedirectMock({
|
||||||
redirectTo: MOCK_AUTHORIZATION_URL,
|
redirectTo: MOCK_AUTHORIZATION_URL,
|
||||||
setCertificateVerifyProc: proc => {
|
setCertificateVerifyProc: proc => {
|
||||||
@ -86,7 +88,7 @@ describe('authorizeUserInWindow()', () => {
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
// We don't really care about the result here, since we're only testing an event handler.
|
// We don't really care about the result here, since we're only testing an event handler.
|
||||||
await authorizeUserInWindow(MOCK_AUTHORIZATION_URL, /.*/);
|
await authorizeUserInWindow({ url: MOCK_AUTHORIZATION_URL, urlSuccessRegex: /.*/ });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.USE_CHROMIUM_RESULT);
|
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.USE_CHROMIUM_RESULT);
|
||||||
@ -103,7 +105,7 @@ describe('authorizeUserInWindow()', () => {
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
// We don't really care about the result here, since we're only testing an event handler.
|
// We don't really care about the result here, since we're only testing an event handler.
|
||||||
await authorizeUserInWindow(MOCK_AUTHORIZATION_URL, /.*/);
|
await authorizeUserInWindow({ url: MOCK_AUTHORIZATION_URL, urlSuccessRegex: /.*/ });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.BLIND_TRUST);
|
expect(mockCallback).toHaveBeenCalledWith(ChromiumVerificationResult.BLIND_TRUST);
|
||||||
|
@ -7,8 +7,7 @@ import * as models from '../../models/index';
|
|||||||
import { getBasicAuthHeader } from '../basic-auth/get-header';
|
import { getBasicAuthHeader } from '../basic-auth/get-header';
|
||||||
import { sendWithSettings } from '../network';
|
import { sendWithSettings } from '../network';
|
||||||
import * as c from './constants';
|
import * as c from './constants';
|
||||||
import { authorizeUserInWindow, responseToObject } from './misc';
|
import { getOAuthSession, responseToObject } from './misc';
|
||||||
|
|
||||||
export default async function(
|
export default async function(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
authorizeUrl: string,
|
authorizeUrl: string,
|
||||||
@ -147,9 +146,11 @@ async function _authorize(
|
|||||||
// Add query params to URL
|
// Add query params to URL
|
||||||
const qs = buildQueryStringFromParams(params);
|
const qs = buildQueryStringFromParams(params);
|
||||||
const finalUrl = joinUrlAndQueryString(url, qs);
|
const finalUrl = joinUrlAndQueryString(url, qs);
|
||||||
const successRegex = new RegExp(`${escapeRegex(redirectUri)}.*(code=)`, 'i');
|
const urlSuccessRegex = new RegExp(`${escapeRegex(redirectUri)}.*(code=)`, 'i');
|
||||||
const failureRegex = new RegExp(`${escapeRegex(redirectUri)}.*(error=)`, 'i');
|
const urlFailureRegex = new RegExp(`${escapeRegex(redirectUri)}.*(error=)`, 'i');
|
||||||
const redirectedTo = await authorizeUserInWindow(finalUrl, successRegex, failureRegex);
|
const sessionId = getOAuthSession();
|
||||||
|
|
||||||
|
const redirectedTo = await window.main.authorizeUserInWindow({ url: finalUrl, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||||
console.log('[oauth2] Detected redirect ' + redirectedTo);
|
console.log('[oauth2] Detected redirect ' + redirectedTo);
|
||||||
const { query } = urlParse(redirectedTo);
|
const { query } = urlParse(redirectedTo);
|
||||||
return responseToObject(query, [
|
return responseToObject(query, [
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { buildQueryStringFromParams, joinUrlAndQueryString } from 'insomnia-url';
|
import { buildQueryStringFromParams, joinUrlAndQueryString } from 'insomnia-url';
|
||||||
|
|
||||||
import * as c from './constants';
|
import * as c from './constants';
|
||||||
import { authorizeUserInWindow, responseToObject } from './misc';
|
import { getOAuthSession, responseToObject } from './misc';
|
||||||
|
|
||||||
export default async function(
|
export default async function(
|
||||||
_requestId: string,
|
_requestId: string,
|
||||||
@ -60,11 +60,11 @@ export default async function(
|
|||||||
// Add query params to URL
|
// Add query params to URL
|
||||||
const qs = buildQueryStringFromParams(params);
|
const qs = buildQueryStringFromParams(params);
|
||||||
const finalUrl = joinUrlAndQueryString(authorizationUrl, qs);
|
const finalUrl = joinUrlAndQueryString(authorizationUrl, qs);
|
||||||
const redirectedTo = await authorizeUserInWindow(
|
const urlSuccessRegex = /(access_token=|id_token=)/;
|
||||||
finalUrl,
|
const urlFailureRegex = /(error=)/;
|
||||||
/(access_token=|id_token=)/,
|
const sessionId = getOAuthSession();
|
||||||
/(error=)/,
|
const redirectedTo = await window.main.authorizeUserInWindow({ url: finalUrl, urlSuccessRegex, urlFailureRegex, sessionId });
|
||||||
);
|
|
||||||
const fragment = redirectedTo.split('#')[1];
|
const fragment = redirectedTo.split('#')[1];
|
||||||
|
|
||||||
if (fragment) {
|
if (fragment) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import electron from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
import * as uuid from 'uuid';
|
import * as uuid from 'uuid';
|
||||||
|
|
||||||
@ -9,20 +9,17 @@ export enum ChromiumVerificationResult {
|
|||||||
USE_CHROMIUM_RESULT = -3
|
USE_CHROMIUM_RESULT = -3
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOCALSTORAGE_KEY_SESSION_ID = 'insomnia::current-oauth-session-id';
|
export const LOCALSTORAGE_KEY_SESSION_ID = 'insomnia::current-oauth-session-id';
|
||||||
let authWindowSessionId;
|
export function getOAuthSession(): string {
|
||||||
|
const token = window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID);
|
||||||
if (window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID)) {
|
return token || initNewOAuthSession();
|
||||||
authWindowSessionId = window.localStorage.getItem(LOCALSTORAGE_KEY_SESSION_ID);
|
|
||||||
} else {
|
|
||||||
initNewOAuthSession();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initNewOAuthSession() {
|
export function initNewOAuthSession() {
|
||||||
// the value of this variable needs to start with 'persist:'
|
// the value of this variable needs to start with 'persist:'
|
||||||
// otherwise sessions won't be persisted over application-restarts
|
// otherwise sessions won't be persisted over application-restarts
|
||||||
authWindowSessionId = `persist:oauth2_${uuid.v4()}`;
|
const authWindowSessionId = `persist:oauth2_${uuid.v4()}`;
|
||||||
window.localStorage.setItem(LOCALSTORAGE_KEY_SESSION_ID, authWindowSessionId);
|
window.localStorage.setItem(LOCALSTORAGE_KEY_SESSION_ID, authWindowSessionId);
|
||||||
|
return authWindowSessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function responseToObject(body, keys, defaults = {}) {
|
export function responseToObject(body, keys, defaults = {}) {
|
||||||
@ -60,11 +57,12 @@ export function responseToObject(body, keys, defaults = {}) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function authorizeUserInWindow(
|
export function authorizeUserInWindow({
|
||||||
url,
|
url,
|
||||||
urlSuccessRegex = /(code=).*/,
|
urlSuccessRegex = /(code=).*/,
|
||||||
urlFailureRegex = /(error=).*/,
|
urlFailureRegex = /(error=).*/,
|
||||||
) {
|
sessionId,
|
||||||
|
}) {
|
||||||
return new Promise<string>(async (resolve, reject) => {
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
let finalUrl: string | null = null;
|
let finalUrl: string | null = null;
|
||||||
|
|
||||||
@ -74,10 +72,10 @@ export function authorizeUserInWindow(
|
|||||||
} = await models.settings.getOrCreate();
|
} = await models.settings.getOrCreate();
|
||||||
|
|
||||||
// Create a child window
|
// Create a child window
|
||||||
const child = new electron.remote.BrowserWindow({
|
const child = new BrowserWindow({
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
partition: authWindowSessionId,
|
partition: sessionId,
|
||||||
},
|
},
|
||||||
show: false,
|
show: false,
|
||||||
});
|
});
|
||||||
|
@ -146,7 +146,7 @@ export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): {
|
|||||||
getPath(name: string) {
|
getPath(name: string) {
|
||||||
switch (name.toLowerCase()) {
|
switch (name.toLowerCase()) {
|
||||||
case 'desktop':
|
case 'desktop':
|
||||||
return electron.remote.app.getPath('desktop');
|
return window.app.getPath('desktop');
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown path name ${name}`);
|
throw new Error(`Unknown path name ${name}`);
|
||||||
@ -172,7 +172,7 @@ export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): {
|
|||||||
buttonLabel: 'Save',
|
buttonLabel: 'Save',
|
||||||
defaultPath: options.defaultPath,
|
defaultPath: options.defaultPath,
|
||||||
};
|
};
|
||||||
const { filePath } = await electron.remote.dialog.showSaveDialog(
|
const { filePath } = await window.dialog.showSaveDialog(
|
||||||
saveOptions
|
saveOptions
|
||||||
);
|
);
|
||||||
return filePath || null;
|
return filePath || null;
|
||||||
|
@ -56,10 +56,11 @@ export default async function(lookupName: string) {
|
|||||||
mkdirp.sync(pluginDir);
|
mkdirp.sync(pluginDir);
|
||||||
|
|
||||||
// Download the module
|
// Download the module
|
||||||
const request = electron.remote.net.request(info.dist.tarball);
|
const request = electron.net.request(info.dist.tarball);
|
||||||
request.on('error', err => {
|
request.on('error', err => {
|
||||||
reject(new Error(`Failed to make plugin request ${info?.dist.tarball}: ${err.message}`));
|
reject(new Error(`Failed to make plugin request ${info?.dist.tarball}: ${err.message}`));
|
||||||
});
|
});
|
||||||
|
|
||||||
const { tmpDir } = await _installPluginToTmpDir(lookupName);
|
const { tmpDir } = await _installPluginToTmpDir(lookupName);
|
||||||
console.log(`[plugins] Moving plugin from ${tmpDir} to ${pluginDir}`);
|
console.log(`[plugins] Moving plugin from ${tmpDir} to ${pluginDir}`);
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ export function isDeprecatedDependencies(str: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _getYarnPath() {
|
function _getYarnPath() {
|
||||||
const { app } = electron.remote || electron;
|
const { app } = process.type === 'renderer' ? window : electron;
|
||||||
|
|
||||||
// TODO: This is brittle. Make finding this more robust.
|
// TODO: This is brittle. Make finding this more robust.
|
||||||
if (isDevelopment()) {
|
if (isDevelopment()) {
|
||||||
|
32
packages/insomnia-app/app/preload.js
Normal file
32
packages/insomnia-app/app/preload.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
|
const main = {
|
||||||
|
restart: () => ipcRenderer.send('restart'),
|
||||||
|
authorizeUserInWindow: options => ipcRenderer.invoke('authorizeUserInWindow', options),
|
||||||
|
setMenuBarVisibility: options => ipcRenderer.send('setMenuBarVisibility', options),
|
||||||
|
installPlugin: options => ipcRenderer.invoke('installPlugin', options),
|
||||||
|
};
|
||||||
|
const dialog = {
|
||||||
|
showOpenDialog: options => ipcRenderer.invoke('showOpenDialog', options),
|
||||||
|
showSaveDialog: options => ipcRenderer.invoke('showSaveDialog', options),
|
||||||
|
};
|
||||||
|
const app = {
|
||||||
|
getPath: options => ipcRenderer.sendSync('getPath', options),
|
||||||
|
getAppPath: options => ipcRenderer.sendSync('getAppPath', options),
|
||||||
|
};
|
||||||
|
const shell = {
|
||||||
|
showItemInFolder: options => ipcRenderer.send('showItemInFolder', options),
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (process.contextIsolated) { TODO: use if rather than try after upgrading to electron 13
|
||||||
|
try {
|
||||||
|
contextBridge.exposeInMainWorld('main', main);
|
||||||
|
contextBridge.exposeInMainWorld('dialog', dialog);
|
||||||
|
contextBridge.exposeInMainWorld('app', app);
|
||||||
|
contextBridge.exposeInMainWorld('shell', shell);
|
||||||
|
} catch {
|
||||||
|
window.main = main;
|
||||||
|
window.dialog = dialog;
|
||||||
|
window.app = app;
|
||||||
|
window.shell = shell;
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import electron from 'electron';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
@ -26,7 +25,7 @@ export class FileEditor extends PureComponent<Props> {
|
|||||||
render() {
|
render() {
|
||||||
const { path } = this.props;
|
const { path } = this.props;
|
||||||
// Replace home path with ~/ to make the path shorter
|
// Replace home path with ~/ to make the path shorter
|
||||||
const homeDirectory = electron.remote.app.getPath('home');
|
const homeDirectory = window.app.getPath('home');
|
||||||
const pathDescription = path.replace(homeDirectory, '~');
|
const pathDescription = path.replace(homeDirectory, '~');
|
||||||
let sizeDescription = '';
|
let sizeDescription = '';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import classnames from 'classnames';
|
|||||||
import { EditorFromTextArea, LintOptions, ShowHintOptions, TextMarker } from 'codemirror';
|
import { EditorFromTextArea, LintOptions, ShowHintOptions, TextMarker } from 'codemirror';
|
||||||
import { GraphQLInfoOptions } from 'codemirror-graphql/info';
|
import { GraphQLInfoOptions } from 'codemirror-graphql/info';
|
||||||
import { ModifiedGraphQLJumpOptions } from 'codemirror-graphql/jump';
|
import { ModifiedGraphQLJumpOptions } from 'codemirror-graphql/jump';
|
||||||
import electron, { OpenDialogOptions } from 'electron';
|
import { OpenDialogOptions } from 'electron';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { DefinitionNode, DocumentNode, GraphQLNonNull, GraphQLSchema, NonNullTypeNode, OperationDefinitionNode } from 'graphql';
|
import { DefinitionNode, DocumentNode, GraphQLNonNull, GraphQLSchema, NonNullTypeNode, OperationDefinitionNode } from 'graphql';
|
||||||
import { parse, typeFromAST } from 'graphql';
|
import { parse, typeFromAST } from 'graphql';
|
||||||
@ -362,7 +362,7 @@ export class GraphQLEditor extends PureComponent<Props, State> {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { canceled, filePaths } = await electron.remote.dialog.showOpenDialog(options);
|
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||||
|
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { clipboard, remote } from 'electron';
|
import { clipboard } from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { HotKeyRegistry } from 'insomnia-common';
|
import { HotKeyRegistry } from 'insomnia-common';
|
||||||
import { json as jsonPrettify } from 'insomnia-prettify';
|
import { json as jsonPrettify } from 'insomnia-prettify';
|
||||||
@ -85,7 +85,7 @@ export class ResponsePane extends PureComponent<Props> {
|
|||||||
|
|
||||||
const { contentType } = response;
|
const { contentType } = response;
|
||||||
const extension = mime.extension(contentType) || 'unknown';
|
const extension = mime.extension(contentType) || 'unknown';
|
||||||
const { canceled, filePath: outputPath } = await remote.dialog.showSaveDialog({
|
const { canceled, filePath: outputPath } = await window.dialog.showSaveDialog({
|
||||||
title: 'Save Response Body',
|
title: 'Save Response Body',
|
||||||
buttonLabel: 'Save',
|
buttonLabel: 'Save',
|
||||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.${extension}`,
|
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.${extension}`,
|
||||||
@ -140,7 +140,7 @@ export class ResponsePane extends PureComponent<Props> {
|
|||||||
.map(v => v.value)
|
.map(v => v.value)
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
const { canceled, filePath } = await remote.dialog.showSaveDialog({
|
const { canceled, filePath } = await window.dialog.showSaveDialog({
|
||||||
title: 'Save Full Response',
|
title: 'Save Full Response',
|
||||||
buttonLabel: 'Save',
|
buttonLabel: 'Save',
|
||||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.txt`,
|
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.txt`,
|
||||||
@ -192,7 +192,7 @@ export class ResponsePane extends PureComponent<Props> {
|
|||||||
const data = await exportHarCurrentRequest(request, response);
|
const data = await exportHarCurrentRequest(request, response);
|
||||||
const har = JSON.stringify(data, null, '\t');
|
const har = JSON.stringify(data, null, '\t');
|
||||||
|
|
||||||
const { filePath } = await remote.dialog.showSaveDialog({
|
const { filePath } = await window.dialog.showSaveDialog({
|
||||||
title: 'Export As HAR',
|
title: 'Export As HAR',
|
||||||
buttonLabel: 'Save',
|
buttonLabel: 'Save',
|
||||||
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.har`,
|
defaultPath: `${request.name.replace(/ +/g, '_')}-${Date.now()}.har`,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import { OpenDialogOptions, remote } from 'electron';
|
import { OpenDialogOptions } from 'electron';
|
||||||
import { HotKeyRegistry } from 'insomnia-common';
|
import { HotKeyRegistry } from 'insomnia-common';
|
||||||
import React, { PureComponent, ReactNode } from 'react';
|
import React, { PureComponent, ReactNode } from 'react';
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ export class RequestUrlBar extends PureComponent<Props, State> {
|
|||||||
buttonLabel: 'Select',
|
buttonLabel: 'Select',
|
||||||
properties: ['openDirectory'],
|
properties: ['openDirectory'],
|
||||||
};
|
};
|
||||||
const { canceled, filePaths } = await remote.dialog.showOpenDialog(options);
|
const { canceled, filePaths } = await window.dialog.showOpenDialog(options);
|
||||||
|
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import * as electron from 'electron';
|
|
||||||
import { PluginConfig } from 'insomnia-common';
|
import { PluginConfig } from 'insomnia-common';
|
||||||
import { Button, ToggleSwitch } from 'insomnia-components';
|
import { Button, ToggleSwitch } from 'insomnia-components';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@ -18,7 +17,6 @@ import type { Settings } from '../../../models/settings';
|
|||||||
import { createPlugin } from '../../../plugins/create';
|
import { createPlugin } from '../../../plugins/create';
|
||||||
import type { Plugin } from '../../../plugins/index';
|
import type { Plugin } from '../../../plugins/index';
|
||||||
import { getPlugins } from '../../../plugins/index';
|
import { getPlugins } from '../../../plugins/index';
|
||||||
import installPlugin from '../../../plugins/install';
|
|
||||||
import { reload } from '../../../templating/index';
|
import { reload } from '../../../templating/index';
|
||||||
import { CopyButton } from '../base/copy-button';
|
import { CopyButton } from '../base/copy-button';
|
||||||
import { Link } from '../base/link';
|
import { Link } from '../base/link';
|
||||||
@ -76,7 +74,7 @@ export class Plugins extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await installPlugin(this.state.npmPluginValue.trim());
|
await window.main.installPlugin(this.state.npmPluginValue.trim());
|
||||||
await this._handleRefreshPlugins();
|
await this._handleRefreshPlugins();
|
||||||
newState.npmPluginValue = ''; // Clear input if successful install
|
newState.npmPluginValue = ''; // Clear input if successful install
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -88,7 +86,7 @@ export class Plugins extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static _handleOpenDirectory(directory: string) {
|
static _handleOpenDirectory(directory: string) {
|
||||||
electron.remote.shell.showItemInFolder(directory);
|
window.shell.showItemInFolder(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleRefreshPlugins() {
|
async _handleRefreshPlugins() {
|
||||||
@ -116,7 +114,7 @@ export class Plugins extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static _handleClickShowPluginsFolder() {
|
static _handleClickShowPluginsFolder() {
|
||||||
electron.remote.shell.showItemInFolder(PLUGIN_PATH);
|
window.shell.showItemInFolder(PLUGIN_PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleCreatePlugin() {
|
_handleCreatePlugin() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import electron, { SaveDialogOptions } from 'electron';
|
import { SaveDialogOptions } from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import mimes from 'mime-types';
|
import mimes from 'mime-types';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
@ -132,7 +132,7 @@ export class ResponseMultipartViewer extends PureComponent<Props, State> {
|
|||||||
const contentType = getContentTypeFromHeaders(part.headers, 'text/plain');
|
const contentType = getContentTypeFromHeaders(part.headers, 'text/plain');
|
||||||
const extension = mimes.extension(contentType) || '.txt';
|
const extension = mimes.extension(contentType) || '.txt';
|
||||||
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
||||||
const dir = lastDir || electron.remote.app.getPath('desktop');
|
const dir = lastDir || window.app.getPath('desktop');
|
||||||
const date = moment().format('YYYY-MM-DD');
|
const date = moment().format('YYYY-MM-DD');
|
||||||
const filename = part.filename || `${part.name}_${date}`;
|
const filename = part.filename || `${part.name}_${date}`;
|
||||||
const options: SaveDialogOptions = {
|
const options: SaveDialogOptions = {
|
||||||
@ -146,7 +146,7 @@ export class ResponseMultipartViewer extends PureComponent<Props, State> {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const { canceled, filePath } = await electron.remote.dialog.showSaveDialog(options);
|
const { canceled, filePath } = await window.dialog.showSaveDialog(options);
|
||||||
|
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
return;
|
return;
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import contextMenu from 'electron-context-menu';
|
|
||||||
import { EventEmitter } from 'events';
|
|
||||||
import React, { createRef, PureComponent } from 'react';
|
import React, { createRef, PureComponent } from 'react';
|
||||||
|
|
||||||
import { AUTOBIND_CFG } from '../../../common/constants';
|
import { AUTOBIND_CFG } from '../../../common/constants';
|
||||||
@ -37,11 +35,6 @@ export class ResponseWebView extends PureComponent<Props> {
|
|||||||
|
|
||||||
this.webview.current.removeEventListener('dom-ready', this._handleDOMReady);
|
this.webview.current.removeEventListener('dom-ready', this._handleDOMReady);
|
||||||
|
|
||||||
contextMenu({
|
|
||||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
|
||||||
window: this.webview.current,
|
|
||||||
});
|
|
||||||
|
|
||||||
this._setBody();
|
this._setBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,17 +52,11 @@ export class ResponseWebView extends PureComponent<Props> {
|
|||||||
//
|
//
|
||||||
// https://github.com/electron/electron/issues/20700
|
// https://github.com/electron/electron/issues/20700
|
||||||
//
|
//
|
||||||
// webview.loadURL(`data:${contentType},${encodeURIComponent(body)}`, {
|
// this.webview.current.loadURL(`data:${contentType},${encodeURIComponent(body)}`, {
|
||||||
// baseURLForDataURL: url,
|
// baseURLForDataURL: url,
|
||||||
// });
|
// });
|
||||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
// @ts-expect-error -- TSCONVERSION type mismatch
|
||||||
this.webview.current.loadURL(`data:${contentType},${encodeURIComponent(bodyWithBase)}`);
|
this.webview.current.loadURL(`data:${contentType},${encodeURIComponent(bodyWithBase)}`);
|
||||||
|
|
||||||
// This is kind of hacky but electron-context-menu fails to save images if this isn't here.
|
|
||||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
|
||||||
this.webview.current.webContents = this.webview.current;
|
|
||||||
// @ts-expect-error -- TSCONVERSION type mismatch
|
|
||||||
this.webview.current.webContents.session = new EventEmitter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
import { autoBindMethodsForReact } from 'class-autobind-decorator';
|
||||||
import { clipboard, ipcRenderer, remote, SaveDialogOptions } from 'electron';
|
import { clipboard, ipcRenderer, SaveDialogOptions } from 'electron';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import HTTPSnippet from 'httpsnippet';
|
import HTTPSnippet from 'httpsnippet';
|
||||||
import * as mime from 'mime-types';
|
import * as mime from 'mime-types';
|
||||||
@ -681,7 +681,7 @@ class App extends PureComponent<AppProps, State> {
|
|||||||
options.defaultPath = defaultPath;
|
options.defaultPath = defaultPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { filePath } = await remote.dialog.showSaveDialog(options);
|
const { filePath } = await window.dialog.showSaveDialog(options);
|
||||||
// @ts-expect-error -- TSCONVERSION don't set item if filePath is undefined
|
// @ts-expect-error -- TSCONVERSION don't set item if filePath is undefined
|
||||||
window.localStorage.setItem('insomnia.sendAndDownloadLocation', filePath);
|
window.localStorage.setItem('insomnia.sendAndDownloadLocation', filePath);
|
||||||
return filePath || null;
|
return filePath || null;
|
||||||
|
@ -2,7 +2,6 @@ import { useEffect, useLayoutEffect } from 'react';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { usePrevious } from 'react-use';
|
import { usePrevious } from 'react-use';
|
||||||
|
|
||||||
import { restartApp, setMenuBarVisibility } from '../../common/electron-helpers';
|
|
||||||
import { Settings } from '../../models/settings';
|
import { Settings } from '../../models/settings';
|
||||||
import { selectSettings } from '../redux/selectors';
|
import { selectSettings } from '../redux/selectors';
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ const useRestartSetting = (setting: keyof Settings) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
restartApp();
|
window.main.restart();
|
||||||
}, [nextValue, previousValue]);
|
}, [nextValue, previousValue]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ export const useSettingsSideEffects = () => {
|
|||||||
}, [settings.fontSize]);
|
}, [settings.fontSize]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMenuBarVisibility(!settings.autoHideMenuBar);
|
window.main.setMenuBarVisibility(!settings.autoHideMenuBar);
|
||||||
}, [settings.autoHideMenuBar]);
|
}, [settings.autoHideMenuBar]);
|
||||||
|
|
||||||
useRestartSetting('nunjucksPowerUserMode');
|
useRestartSetting('nunjucksPowerUserMode');
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import electron from 'electron';
|
|
||||||
import fs, { NoParamCallback } from 'fs';
|
import fs, { NoParamCallback } from 'fs';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@ -30,7 +29,6 @@ import { Request } from '../../../models/request';
|
|||||||
import { isWorkspace } from '../../../models/workspace';
|
import { isWorkspace } from '../../../models/workspace';
|
||||||
import { reloadPlugins } from '../../../plugins';
|
import { reloadPlugins } from '../../../plugins';
|
||||||
import { createPlugin } from '../../../plugins/create';
|
import { createPlugin } from '../../../plugins/create';
|
||||||
import install from '../../../plugins/install';
|
|
||||||
import { setTheme } from '../../../plugins/misc';
|
import { setTheme } from '../../../plugins/misc';
|
||||||
import { AskModal } from '../../../ui/components/modals/ask-modal';
|
import { AskModal } from '../../../ui/components/modals/ask-modal';
|
||||||
import { AlertModal } from '../../components/modals/alert-modal';
|
import { AlertModal } from '../../components/modals/alert-modal';
|
||||||
@ -240,7 +238,7 @@ export const newCommand = (command: string, args: any) => async (dispatch: Dispa
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await install(args.name);
|
await window.main.installPlugin(args.name);
|
||||||
showModal(SettingsModal, TAB_INDEX_PLUGINS);
|
showModal(SettingsModal, TAB_INDEX_PLUGINS);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showError({
|
showError({
|
||||||
@ -416,13 +414,13 @@ const showSaveExportedFileDialog = async ({
|
|||||||
const date = moment().format('YYYY-MM-DD');
|
const date = moment().format('YYYY-MM-DD');
|
||||||
const name = exportedFileNamePrefix.replace(/ /g, '-');
|
const name = exportedFileNamePrefix.replace(/ /g, '-');
|
||||||
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
const lastDir = window.localStorage.getItem('insomnia.lastExportPath');
|
||||||
const dir = lastDir || electron.remote.app.getPath('desktop');
|
const dir = lastDir || window.app.getPath('desktop');
|
||||||
const options = {
|
const options = {
|
||||||
title: 'Export Insomnia Data',
|
title: 'Export Insomnia Data',
|
||||||
buttonLabel: 'Export',
|
buttonLabel: 'Export',
|
||||||
defaultPath: `${path.join(dir, `${name}_${date}`)}.${selectedFormat}`,
|
defaultPath: `${path.join(dir, `${name}_${date}`)}.${selectedFormat}`,
|
||||||
};
|
};
|
||||||
const { filePath } = await electron.remote.dialog.showSaveDialog(options);
|
const { filePath } = await window.dialog.showSaveDialog(options);
|
||||||
return filePath || null;
|
return filePath || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ export const importFile = (
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
const { canceled, filePaths } = await electron.remote.dialog.showOpenDialog(openDialogOptions);
|
const { canceled, filePaths } = await window.dialog.showOpenDialog(openDialogOptions);
|
||||||
|
|
||||||
if (canceled) {
|
if (canceled) {
|
||||||
// It was cancelled, so let's bail out
|
// It was cancelled, so let's bail out
|
||||||
|
16
packages/insomnia-app/package-lock.json
generated
16
packages/insomnia-app/package-lock.json
generated
@ -11244,13 +11244,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-context-menu": {
|
"electron-context-menu": {
|
||||||
"version": "2.5.2",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/electron-context-menu/-/electron-context-menu-3.1.1.tgz",
|
||||||
"integrity": "sha512-1cEQR6fA9ktFsRBc+eXPwvrOgAPytUD7rUV4iBAA5zTrLAPKokJ23xeMjcK2fjrDPrlFRBxcLz0KP+GUhMrSCQ==",
|
"integrity": "sha512-LJhwaKf6XHwk2LQ5SdwoGNODoA8lRwks9bbEeAqqMf4e3hsrT7pZtX6MaHKYNFZKxF14JjI/VR+VRjGvxmaQoA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cli-truncate": "^2.1.0",
|
"cli-truncate": "^2.1.0",
|
||||||
"electron-dl": "^3.1.0",
|
"electron-dl": "^3.2.1",
|
||||||
"electron-is-dev": "^1.2.0"
|
"electron-is-dev": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-devtools-installer": {
|
"electron-devtools-installer": {
|
||||||
@ -11293,9 +11293,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron-is-dev": {
|
"electron-is-dev": {
|
||||||
"version": "1.2.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-2.0.0.tgz",
|
||||||
"integrity": "sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw=="
|
"integrity": "sha512-3X99K852Yoqu9AcW50qz3ibYBWY79/pBhlMCab8ToEWS48R0T9tyxRiQhwylE7zQdXrMnx2JKqUJyMPmt5FBqA=="
|
||||||
},
|
},
|
||||||
"electron-log": {
|
"electron-log": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
"codemirror": "^5.62.3",
|
"codemirror": "^5.62.3",
|
||||||
"codemirror-graphql": "^1.0.2",
|
"codemirror-graphql": "^1.0.2",
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"electron-context-menu": "^2.5.2",
|
"electron-context-menu": "^3.1.1",
|
||||||
"electron-log": "^4.4.3",
|
"electron-log": "^4.4.3",
|
||||||
"electron-updater": "^4.6.1",
|
"electron-updater": "^4.6.1",
|
||||||
"framer-motion": "^1.11.1",
|
"framer-motion": "^1.11.1",
|
||||||
|
@ -30,7 +30,7 @@ if (process.env.NODE_ENV === 'development') {
|
|||||||
plugins = productionConfig.plugins;
|
plugins = productionConfig.plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configuration: Configuration = {
|
const configuration: Configuration[] = [{
|
||||||
...productionConfig,
|
...productionConfig,
|
||||||
devtool,
|
devtool,
|
||||||
entry: ['./main.development.ts'],
|
entry: ['./main.development.ts'],
|
||||||
@ -40,6 +40,14 @@ const configuration: Configuration = {
|
|||||||
},
|
},
|
||||||
target: 'electron-main',
|
target: 'electron-main',
|
||||||
plugins,
|
plugins,
|
||||||
};
|
},
|
||||||
|
{
|
||||||
|
entry: './app/preload.js',
|
||||||
|
target: 'electron-preload',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, '../build'),
|
||||||
|
filename: 'preload.js',
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
|
||||||
export default configuration;
|
export default configuration;
|
||||||
|
Loading…
Reference in New Issue
Block a user