diff --git a/app/__mocks__/node-forge.js b/app/__mocks__/node-forge.js new file mode 100644 index 000000000..32a349b53 --- /dev/null +++ b/app/__mocks__/node-forge.js @@ -0,0 +1,65 @@ +/* + * This is a stupid little mock that basically disabled encryption. + * The reason it is needed is because the Forge module loader doesn't + * play along with Jest. + */ + +module.exports = function (options) { + const forge = require('../../node_modules/node-forge/js/forge')(options); + + forge.util = { + hexToBytes: forge.hexToBytes, + bytesToHex: forge.bytesToHex, + createBuffer (text) { + return new Buffer(text); + } + }; + + forge.pkcs5 = { + pbkdf2 () { + + } + }; + + forge.random = { + getBytesSync(n) { + let s = ''; + for (let i = 0; i < n; i++) { + s += 'a'; + } + return s; + } + }; + + forge.cipher = { + createCipher(alg, key) { + return { + start(config) { + this._config = config; + }, + update(buffer) { + this._data = buffer; + }, + finish() { + this.mode = {tag: 'tag'}; + this.output = this._data; + } + }; + }, + createDecipher(alg, key) { + return { + start (config) { + this._config = config; + }, + update (buffer) { + this.output = buffer; + }, + finish () { + return true; + } + }; + } + }; + + return forge; +}; diff --git a/app/analytics/index.js b/app/analytics/index.js index e293ed584..9b407be0e 100644 --- a/app/analytics/index.js +++ b/app/analytics/index.js @@ -9,12 +9,10 @@ export async function init (accountId) { return; } - process.nextTick(() => { - google.init(accountId, getAppPlatform(), getAppVersion()); - segment.init(); + await segment.init(); + await google.init(accountId, getAppPlatform(), getAppVersion()); - initialized = true; - }); + initialized = true; ipcRenderer.on('analytics-track-event', (_, args) => { trackEvent(...args); diff --git a/app/common/constants.js b/app/common/constants.js index 463b2ffc6..eea591b8a 100644 --- a/app/common/constants.js +++ b/app/common/constants.js @@ -121,7 +121,7 @@ export const CONTENT_TYPE_OTHER = ''; export const contentTypesMap = { [CONTENT_TYPE_JSON]: 'JSON', [CONTENT_TYPE_XML]: 'XML', - [CONTENT_TYPE_FORM_DATA]: 'Form Data', + [CONTENT_TYPE_FORM_DATA]: 'Multipart Form', [CONTENT_TYPE_FORM_URLENCODED]: 'Url Encoded', [CONTENT_TYPE_FILE]: 'Binary File', [CONTENT_TYPE_OTHER]: 'Other' diff --git a/app/models/request.js b/app/models/request.js index bec500f29..426a0cd43 100644 --- a/app/models/request.js +++ b/app/models/request.js @@ -107,6 +107,8 @@ export function updateMimeType (request, mimeType, doCreate = false) { body = newBodyForm(request.body.params || []); } else if (mimeType === CONTENT_TYPE_FILE) { body = newBodyFile(''); + } else if (typeof mimeType !== 'string') { + body = newBodyRaw(''); } else { body = newBodyRaw(request.body.text || '', mimeType); } diff --git a/app/package.json b/app/package.json index 07be48b2a..c69d0d355 100644 --- a/app/package.json +++ b/app/package.json @@ -22,11 +22,10 @@ "mime-types": "^2.1.12", "mkdirp": "^0.5.1", "nedb": "^1.8.0", - "node-forge": "^0.6.43", + "node-forge": "^0.6.46", "nunjucks": "^3.0.0", "raven": "^0.12.1", "request": "^2.71.0", - "sjcl": "^1.0.6", "srp": "git@github.com:getinsomnia/srp-js.git#6ebd8c3acfbcf69645e4c6e6f8f6b55138ff22c3", "tough-cookie": "^2.3.1", "traverse": "^0.6.6", diff --git a/app/renderer.html b/app/renderer.html index 5c99e98b9..74d7c677c 100644 --- a/app/renderer.html +++ b/app/renderer.html @@ -117,16 +117,5 @@ // SOME HELPERS document.body.setAttribute('data-platform', process.platform); - - - - diff --git a/app/sync/__tests__/crypt.test.js b/app/sync/__tests__/crypt.test.js new file mode 100644 index 000000000..6b2b9605a --- /dev/null +++ b/app/sync/__tests__/crypt.test.js @@ -0,0 +1,30 @@ +import * as crypt from '../crypt'; + +describe('deriveKey()', () => { + it('derives a key properly', async () => { + const result = await crypt.deriveKey('Password', 'email', 'salt'); + + const expected = ''; + + expect(result).toBe(expected); + }) +}); + +describe('encryptRSA', () => { + it('encrypts and decrypts', () => { + const resultEncrypted = crypt.encryptAES('rawkey', 'Hello World!', 'additional data'); + const resultDecrypted = crypt.decryptAES('rawkey', resultEncrypted); + + const expectedEncrypted = { + ad: '6164646974696f6e616c2064617461', + d: '48656c6c6f253230576f726c6421', + iv: '616161616161616161616161', + t: '746167' + }; + + const expectedDecrypted = 'Hello World!'; + + expect(resultEncrypted).toEqual(expectedEncrypted); + expect(resultDecrypted).toEqual(expectedDecrypted); + }) +}); diff --git a/app/sync/crypt.js b/app/sync/crypt.js index be88a0766..820caa5b4 100644 --- a/app/sync/crypt.js +++ b/app/sync/crypt.js @@ -1,7 +1,9 @@ import HKDF from 'hkdf'; -import sjcl from 'sjcl'; import srp from 'srp'; -import forge from 'node-forge'; +import initForge from 'node-forge'; + +// Initialize Forge +const forge = initForge(); const DEFAULT_BYTE_LENGTH = 32; const DEFAULT_PBKDF2_ITERATIONS = 1E5; // 100,000 @@ -182,7 +184,7 @@ export async function generateAES256Key () { ); return subtle.exportKey('jwk', key); } else { - console.log('-- Using Falback Forge AES Key Generation --'); + console.log('-- Using Fallback Forge AES Key Generation --'); const key = forge.util.bytesToHex(forge.random.getBytesSync(32)); return { kty: 'oct', @@ -200,7 +202,7 @@ export async function generateAES256Key () { * @returns Object */ export async function generateKeyPairJWK () { - // NOTE: Safari has crypto.webkitSubtle, but does not support RSA-OAEP-SHA256 + // NOTE: Safari has crypto.webkitSubtle, but it does not support RSA-OAEP-SHA256 const subtle = window.crypto && window.crypto.subtle; if (subtle) { @@ -298,9 +300,6 @@ function _b64UrlToHex (s) { return forge.util.bytesToHex(atob(b64)); } -window.b64urltohex = _b64UrlToHex; -window.forge = forge; - /** * Derive key from password * @@ -329,26 +328,15 @@ async function _pbkdf2Passphrase (passphrase, salt) { const derivedKeyRaw = await window.crypto.subtle.deriveBits(algo, k, DEFAULT_BYTE_LENGTH * 8); return new Buffer(derivedKeyRaw).toString('hex'); } else { - console.log('-- Using SJCL PBKDF2 --'); - - const derivedKeyRaw = sjcl.misc.pbkdf2( + console.log('-- Using Forge PBKDF2 --'); + const derivedKeyRaw = forge.pkcs5.pbkdf2( passphrase, - sjcl.codec.hex.toBits(salt), + forge.util.hexToBytes(salt), DEFAULT_PBKDF2_ITERATIONS, - DEFAULT_BYTE_LENGTH * 8, - sjcl.hash.sha1 + DEFAULT_BYTE_LENGTH, + forge.md.sha256.create() ); - return sjcl.codec.hex.fromBits(derivedKeyRaw); - - // NOTE: SJCL (above) is about 10x faster than Forge - // const derivedKeyRaw = forge.pkcs5.pbkdf2( - // passphrase, - // forge.util.hexToBytes(salt), - // DEFAULT_PBKDF2_ITERATIONS, - // DEFAULT_BYTE_LENGTH, - // forge.md.sha256.create() - // ); - // const derivedKey = forge.util.bytesToHex(derivedKeyRaw); + return forge.util.bytesToHex(derivedKeyRaw); } } diff --git a/app/sync/session.js b/app/sync/session.js index 957668a7e..825f6f851 100644 --- a/app/sync/session.js +++ b/app/sync/session.js @@ -233,7 +233,10 @@ export async function logout () { trackEvent('Session', 'Logout'); } -/** Cancel the user's subscription Account */ +export async function listTeams () { + return util.get('/api/teams'); +} + export async function cancelAccount () { await util.del('/api/billing/subscriptions'); trackEvent('Session', 'Cancel Account'); diff --git a/app/ui/components/RequestUrlBar.js b/app/ui/components/RequestUrlBar.js index 69ecd11b0..55cb7d822 100644 --- a/app/ui/components/RequestUrlBar.js +++ b/app/ui/components/RequestUrlBar.js @@ -17,6 +17,7 @@ class RequestUrlBar extends Component { _handleFormSubmit = e => { e.preventDefault(); + e.stopPropagation(); this.props.handleSend(); }; @@ -117,8 +118,8 @@ class RequestUrlBar extends Component { clearTimeout(this._sendInterval); if (this.state.currentInterval) { this.setState({currentInterval: null}); + trackEvent('Request', 'Stop Send Interval'); } - trackEvent('Request', 'Stop Send Interval'); }; _handleStopTimeout = () => { @@ -129,6 +130,20 @@ class RequestUrlBar extends Component { trackEvent('Request', 'Stop Send Timeout'); }; + _handleClickSend = e => { + const metaPressed = isMac() ? e.metaKey : e.ctrlKey; + + // If we're pressing a meta key, let the dropdown open + if (metaPressed) { + e.preventDefault(); // Don't submit the form + return; + } + + // If we're not pressing a meta key, cancel dropdown and send the request + e.stopPropagation(); // Don't trigger the dropdown + this._handleFormSubmit(e); + }; + componentDidMount () { document.body.addEventListener('keydown', this._handleKeyDown); document.body.addEventListener('keyup', this._handleKeyUp); @@ -140,7 +155,7 @@ class RequestUrlBar extends Component { } renderSendButton () { - const {showAdvanced, currentInterval, currentTimeout} = this.state; + const {currentInterval, currentTimeout} = this.state; let cancelButton = null; if (currentInterval) { @@ -164,22 +179,15 @@ class RequestUrlBar extends Component { } let sendButton; - if (!cancelButton && !showAdvanced) { - sendButton = ( - - ) - } - - let dropdown = null; if (!cancelButton) { - dropdown = ( + sendButton = ( - - + + Send - + Basic Send Now @@ -187,7 +195,7 @@ class RequestUrlBar extends Component { Generate Client Code - + Advanced Send After Delay @@ -201,7 +209,6 @@ class RequestUrlBar extends Component { return [ cancelButton, sendButton, - dropdown, ] } diff --git a/app/ui/components/Wrapper.js b/app/ui/components/Wrapper.js index ff1c502bd..fdf5a698d 100644 --- a/app/ui/components/Wrapper.js +++ b/app/ui/components/Wrapper.js @@ -7,7 +7,6 @@ import CookiesModal from '../components/modals/CookiesModal'; import EnvironmentEditModal from '../components/modals/EnvironmentEditModal'; import GenerateCodeModal from '../components/modals/GenerateCodeModal'; import LoginModal from '../components/modals/LoginModal'; -import PaymentModal from '../components/modals/PaymentModal'; import PaymentNotificationModal from '../components/modals/PaymentNotificationModal'; import PromptModal from '../components/modals/PromptModal'; import RequestCreateModal from '../components/modals/RequestCreateModal'; @@ -16,10 +15,10 @@ import RequestSwitcherModal from '../components/modals/RequestSwitcherModal'; import ResponsePane from './ResponsePane'; import SettingsModal from '../components/modals/SettingsModal'; import Sidebar from './sidebar/Sidebar'; -import SignupModal from '../components/modals/SignupModal'; import SyncLogsModal from '../components/modals/SyncLogsModal'; import WorkspaceEnvironmentsEditModal from '../components/modals/WorkspaceEnvironmentsEditModal'; import WorkspaceSettingsModal from '../components/modals/WorkspaceSettingsModal'; +import WorkspaceShareSettingsModal from '../components/modals/WorkspaceShareSettingsModal'; import * as models from '../../models/index'; import {updateMimeType} from '../../models/request'; import {trackEvent} from '../../analytics/index'; @@ -276,14 +275,15 @@ class Wrapper extends Component { - - + { + _handleEditEnd = () => { const value = this._input.value.trim(); if (!value) { @@ -37,8 +36,7 @@ class Editable extends Component { // This timeout prevents the UI from showing the old value after submit. // It should give the UI enough time to redraw the new value. - await misc.delay(100); - this.setState({editing: false}); + setTimeout(async () => this.setState({editing: false}), 100); }; _handleEditKeyDown = e => { diff --git a/app/ui/components/base/dropdown/Dropdown.js b/app/ui/components/base/dropdown/Dropdown.js index 456105b9f..dc8bcaaa1 100644 --- a/app/ui/components/base/dropdown/Dropdown.js +++ b/app/ui/components/base/dropdown/Dropdown.js @@ -102,7 +102,7 @@ class Dropdown extends Component { } render () { - const {right, outline, wide, className, style} = this.props; + const {right, outline, wide, className, style, children} = this.props; const {dropUp, open} = this.state; const classes = classnames( @@ -117,7 +117,9 @@ class Dropdown extends Component { const dropdownButtons = []; const dropdownItems = []; - for (const child of this._getFlattenedChildren(this.props.children)) { + + const allChildren = this._getFlattenedChildren(Array.isArray(children) ? children : [children]); + for (const child of allChildren) { if (child.type === DropdownButton) { dropdownButtons.push(child); } else if (child.type === DropdownItem) { @@ -127,13 +129,11 @@ class Dropdown extends Component { } } - let children = []; + let finalChildren = []; if (dropdownButtons.length !== 1) { console.error(`Dropdown needs exactly one DropdownButton! Got ${dropdownButtons.length}`, this.props); - } else if (dropdownItems.length === 0) { - children = dropdownButtons; } else { - children = [ + finalChildren = [ dropdownButtons[0],
    {dropdownItems} @@ -146,7 +146,7 @@ class Dropdown extends Component { className={classes} onClick={this._handleClick} onMouseDown={this._handleMouseDown}> - {children} + {finalChildren}
    ) diff --git a/app/ui/components/base/dropdown/DropdownDivider.js b/app/ui/components/base/dropdown/DropdownDivider.js index 85c9d02aa..4621a0870 100644 --- a/app/ui/components/base/dropdown/DropdownDivider.js +++ b/app/ui/components/base/dropdown/DropdownDivider.js @@ -1,23 +1,21 @@ -import React, {PropTypes} from 'react'; +import React from 'react'; import classnames from 'classnames'; -const DropdownDivider = ({name}) => { +const DropdownDivider = ({children}) => { const classes = classnames( 'dropdown__divider', - {'dropdown__divider--no-name': !name} + {'dropdown__divider--no-name': !children} ); return (
  • - {name} + {children}
  • ) }; -DropdownDivider.propTypes = { - name: PropTypes.any -}; +DropdownDivider.propTypes = {}; export default DropdownDivider; diff --git a/app/ui/components/dropdowns/ContentTypeDropdown.js b/app/ui/components/dropdowns/ContentTypeDropdown.js index dce2e655d..4532eb3d2 100644 --- a/app/ui/components/dropdowns/ContentTypeDropdown.js +++ b/app/ui/components/dropdowns/ContentTypeDropdown.js @@ -34,14 +34,14 @@ class ContentTypeDropdown extends Component { {children} - Form Data}/> + Form Data {this._renderDropdownItem(constants.CONTENT_TYPE_FORM_DATA)} {this._renderDropdownItem(constants.CONTENT_TYPE_FORM_URLENCODED)} - Raw Text}/> + Raw Text {this._renderDropdownItem(constants.CONTENT_TYPE_JSON)} {this._renderDropdownItem(constants.CONTENT_TYPE_XML)} {this._renderDropdownItem(constants.CONTENT_TYPE_OTHER)} - Other}/> + Other {this._renderDropdownItem(constants.CONTENT_TYPE_FILE)} {this._renderDropdownItem(EMPTY_MIME_TYPE, 'No Body')} diff --git a/app/ui/components/dropdowns/EnvironmentsDropdown.js b/app/ui/components/dropdowns/EnvironmentsDropdown.js index 1b3e2ee93..dd0f6b852 100644 --- a/app/ui/components/dropdowns/EnvironmentsDropdown.js +++ b/app/ui/components/dropdowns/EnvironmentsDropdown.js @@ -2,11 +2,10 @@ import React, {Component, PropTypes} from 'react'; import {ipcRenderer} from 'electron'; import classnames from 'classnames'; import EnvironmentsModal from '../modals/WorkspaceEnvironmentsEditModal'; -import {Dropdown, DropdownDivider, DropdownButton, DropdownItem} from '../base/dropdown'; +import {Dropdown, DropdownDivider, DropdownButton, DropdownItem, DropdownHint} from '../base/dropdown'; import {showModal} from '../modals/index'; import {trackEvent} from '../../../analytics/index'; - const EnvironmentsDropdown = ({ className, workspace, @@ -17,7 +16,9 @@ const EnvironmentsDropdown = ({ }) => { // NOTE: Base environment might not exist if the users hasn't managed environments yet. const baseEnvironment = environments.find(e => e.parentId === workspace._id); - const subEnvironments = environments.filter(e => e.parentId === (baseEnvironment && baseEnvironment._id)); + const subEnvironments = environments.filter( + e => e.parentId === (baseEnvironment && baseEnvironment._id) + ); let description; if (!activeEnvironment || activeEnvironment === baseEnvironment) { @@ -35,7 +36,7 @@ const EnvironmentsDropdown = ({ - + Switch Environment {subEnvironments.map(environment => ( { @@ -51,9 +52,10 @@ const EnvironmentsDropdown = ({ }}> No Environment - + General showModal(EnvironmentsModal, workspace)}> Manage Environments + ) diff --git a/app/ui/components/dropdowns/PreviewModeDropdown.js b/app/ui/components/dropdowns/PreviewModeDropdown.js index 84f070af6..f1e560c8a 100644 --- a/app/ui/components/dropdowns/PreviewModeDropdown.js +++ b/app/ui/components/dropdowns/PreviewModeDropdown.js @@ -16,14 +16,14 @@ class PreviewModeDropdown extends PureComponent { - + Preview Mode {PREVIEW_MODES.map(mode => ( {previewMode === mode ? : } {getPreviewModeName(mode)} ))} - + Actions Save to File diff --git a/app/ui/components/dropdowns/RequestActionsDropdown.js b/app/ui/components/dropdowns/RequestActionsDropdown.js index dc2f628bd..43564600f 100644 --- a/app/ui/components/dropdowns/RequestActionsDropdown.js +++ b/app/ui/components/dropdowns/RequestActionsDropdown.js @@ -5,6 +5,8 @@ import PromptModal from '../modals/PromptModal'; import * as models from '../../../models'; import {showModal} from '../modals/index'; import {trackEvent} from '../../../analytics/index'; +import AlertModal from '../modals/AlertModal'; +import {MOD_SYM} from '../../../common/constants'; class RequestActionsDropdown extends Component { @@ -19,6 +21,19 @@ class RequestActionsDropdown extends Component { trackEvent('Request', 'Generate Code', 'Request Action'); }; + _handleAdvancedSend = () => { + trackEvent('Request', 'Advanced Send Hint', 'Request Action'); + showModal(AlertModal, { + title: 'Advanced Sending', + message: ( +
    + For advanced sending options, hold {MOD_SYM} while + clicking the send button next to the Url. +
    + ) + }); + }; + _handlePromptUpdateName = async () => { const {request} = this.props; @@ -57,6 +72,9 @@ class RequestActionsDropdown extends Component { Generate Code + + Advanced Sending + Delete diff --git a/app/ui/components/dropdowns/ResponseHistoryDropdown.js b/app/ui/components/dropdowns/ResponseHistoryDropdown.js index ecc823bed..d34584d78 100644 --- a/app/ui/components/dropdowns/ResponseHistoryDropdown.js +++ b/app/ui/components/dropdowns/ResponseHistoryDropdown.js @@ -82,14 +82,14 @@ class ResponseHistoryDropdown extends Component { : } - + Response History Clear History - + Past Responses {responses.map(this.renderDropdownItem)} ) diff --git a/app/ui/components/dropdowns/SyncDropdown.js b/app/ui/components/dropdowns/SyncDropdown.js index ede0c60a7..7c17f52fc 100644 --- a/app/ui/components/dropdowns/SyncDropdown.js +++ b/app/ui/components/dropdowns/SyncDropdown.js @@ -6,24 +6,21 @@ import * as syncStorage from '../../../sync/storage'; import * as session from '../../../sync/session'; import * as sync from '../../../sync'; import {trackEvent} from '../../../analytics'; -import SettingsModal, {TAB_PLUS} from '../modals/SettingsModal'; -import LoginModal from '../modals/LoginModal'; -import PromptButton from '../base/PromptButton'; class SyncDropdown extends Component { state = { loggedIn: null, syncData: null, loading: false, - hide: false, }; - _handleHideMenu () { - this.setState({hide: true}); - trackEvent('Sync', 'Hide Menu') - } + _trackShowMenu = () => trackEvent('Sync', 'Show Menu', 'Authenticated'); + _handleShowLogs = () => showModal(SyncLogsModal); + + _handleToggleSyncMode = async () => { + const {syncData} = this.state; + const resourceGroupId = syncData.resourceGroupId; - async _handleToggleSyncMode (resourceGroupId) { const config = await sync.getOrCreateConfig(resourceGroupId); let syncMode = config.syncMode === syncStorage.SYNC_MODE_OFF ? @@ -35,13 +32,16 @@ class SyncDropdown extends Component { // Trigger a sync right away if we're turning it on if (syncMode === syncStorage.SYNC_MODE_ON) { - await this._handleSyncResourceGroupId(resourceGroupId); + await this._handleSyncResourceGroupId(); } trackEvent('Sync', 'Change Mode', syncMode); - } + }; + + _handleSyncResourceGroupId = async () => { + const {syncData} = this.state; + const resourceGroupId = syncData.resourceGroupId; - async _handleSyncResourceGroupId (resourceGroupId) { // Set loading state this.setState({loading: true}); @@ -55,7 +55,7 @@ class SyncDropdown extends Component { this.setState({loading: false}); trackEvent('Sync', 'Manual Sync'); - } + }; async _reloadData () { const loggedIn = session.isLoggedIn(); @@ -108,45 +108,11 @@ class SyncDropdown extends Component { render () { const {className} = this.props; - const {syncData, loading, loggedIn, hide} = this.state; - - if (hide) { - return null; - } + const {syncData, loading, loggedIn} = this.state; + // Don't show the sync menu unless we're logged in if (!loggedIn) { - return ( -
    - - trackEvent('Sync', 'Show Menu', 'Guest')}> - Sync Settings - - - { - showModal(SettingsModal, TAB_PLUS); - trackEvent('Sync', 'Create Account'); - }}> - - Create Account - - { - showModal(LoginModal); - trackEvent('Sync', 'Login'); - }}> - - Login - - - this._handleHideMenu()}> - - Hide This Menu - - -
    - ) + return null; } if (!syncData) { @@ -158,25 +124,24 @@ class SyncDropdown extends Component { ) } else { - const {resourceGroupId, syncMode, syncPercent} = syncData; + const {syncMode, syncPercent} = syncData; const description = this._getSyncDescription(syncMode, syncPercent); return (
    - trackEvent('Sync', 'Show Menu', 'Authenticated')}> + {description} - - this._handleToggleSyncMode(resourceGroupId)} + Workspace Synced {syncPercent}% + {syncMode === syncStorage.SYNC_MODE_OFF ? : } Automatic Sync - this._handleSyncResourceGroupId(resourceGroupId)} + {loading ? @@ -185,13 +150,8 @@ class SyncDropdown extends Component { Sync Now {syncPercent === 100 ? '(up to date)' : ''} - - - showModal(SettingsModal, TAB_PLUS)}> - - Manage Account - - showModal(SyncLogsModal)}> + Other + Show Debug Logs diff --git a/app/ui/components/dropdowns/WorkspaceDropdown.js b/app/ui/components/dropdowns/WorkspaceDropdown.js index 85279e230..1db8f62a0 100644 --- a/app/ui/components/dropdowns/WorkspaceDropdown.js +++ b/app/ui/components/dropdowns/WorkspaceDropdown.js @@ -10,6 +10,7 @@ import {showModal} from '../modals/index'; import {trackEvent} from '../../../analytics/index'; import Link from '../base/Link'; import WorkspaceSettingsModal from '../modals/WorkspaceSettingsModal'; +import WorkspaceShareSettingsModal from '../modals/WorkspaceShareSettingsModal'; class WorkspaceDropdown extends Component { _handleShowExport = () => showModal(SettingsModal, TAB_INDEX_EXPORT); @@ -19,6 +20,11 @@ class WorkspaceDropdown extends Component { workspace: this.props.activeWorkspace, }); }; + _handleShowShareSettings = () => { + showModal(WorkspaceShareSettingsModal, { + workspace: this.props.activeWorkspace, + }); + }; _handleSwitchWorkspace = workspaceId => { this.props.handleSetActiveWorkspace(workspaceId); @@ -64,13 +70,16 @@ class WorkspaceDropdown extends Component { {activeWorkspace.name} - + {activeWorkspace.name} Workspace Settings + {/**/} + {/* Share {activeWorkspace.name}*/} + {/**/} - + Switch Workspace {nonActiveWorkspaces.map(w => ( @@ -81,10 +90,10 @@ class WorkspaceDropdown extends Component { New Workspace - + Insomnia Version {getAppVersion()} - Preferences + App Settings diff --git a/app/ui/components/modals/CookiesModal.js b/app/ui/components/modals/CookiesModal.js index 41320d744..9f1c3e64f 100644 --- a/app/ui/components/modals/CookiesModal.js +++ b/app/ui/components/modals/CookiesModal.js @@ -97,23 +97,18 @@ class CookiesModal extends Component { const {filter} = this.state; return ( - this.modal = m} wide={true} top={true} - tall={true} {...this.props}> - - Manage Cookies - + this.modal = m} wide={true} top={true} tall={true} {...this.props}> + Manage Cookies -
    -
    - - this.filterInput = n} - onChange={e => this._onFilterChange(e.target.value)} - type="text" - placeholder="twitter.com" - defaultValue="" - /> +
    +
    +
    diff --git a/app/ui/components/modals/LoginModal.js b/app/ui/components/modals/LoginModal.js index d9262ad77..c6ab636b8 100644 --- a/app/ui/components/modals/LoginModal.js +++ b/app/ui/components/modals/LoginModal.js @@ -5,10 +5,7 @@ import ModalBody from '../base/ModalBody'; import ModalHeader from '../base/ModalHeader'; import ModalFooter from '../base/ModalFooter'; import * as session from '../../../sync/session'; -import {showModal} from './index'; -import SignupModal from './SignupModal'; import * as sync from '../../../sync'; -import {trackEvent} from '../../../analytics'; class LoginModal extends Component { state = { @@ -41,14 +38,6 @@ class LoginModal extends Component { } }; - _handleSignup = e => { - e.preventDefault(); - - this.modal.hide(); - showModal(SignupModal); - trackEvent('Login', 'Switch to Signup'); - }; - show (options = {}) { const {title, message} = options; this.setState({step: 1, error: '', loading: false, title, message}); @@ -92,7 +81,7 @@ class LoginModal extends Component {
    Don't have an account yet? {" "} - Signup + Signup
    - ) : ( - - )} - - - - ) - } -} - -PaymentModal.propTypes = {}; - -export default PaymentModal; diff --git a/app/ui/components/modals/PaymentNotificationModal.js b/app/ui/components/modals/PaymentNotificationModal.js index df7774035..2cbfffca9 100644 --- a/app/ui/components/modals/PaymentNotificationModal.js +++ b/app/ui/components/modals/PaymentNotificationModal.js @@ -1,10 +1,9 @@ import React, {Component} from 'react'; +import Link from '../base/Link'; import Modal from '../base/Modal'; import ModalBody from '../base/ModalBody'; import ModalHeader from '../base/ModalHeader'; import ModalFooter from '../base/ModalFooter'; -import PaymentModal from './PaymentModal'; -import {showModal} from './index'; import * as session from '../../../sync/session'; import {trackEvent} from '../../../analytics'; @@ -16,12 +15,6 @@ class PaymentNotificationModal extends Component { this.hide(); }; - _handleProceedToPayment = () => { - this.hide(); - showModal(PaymentModal); - trackEvent('Billing', 'Trial Ended', 'Proceed') - }; - show () { // Don't trigger automatically if user has dismissed it already if (hidePaymentNotificationUntilNextLaunch) { @@ -52,10 +45,9 @@ class PaymentNotificationModal extends Component {


    - + + Enter Billing Info +

    @@ -63,7 +55,7 @@ class PaymentNotificationModal extends Component { -
    +   ) diff --git a/app/ui/components/modals/RequestCreateModal.js b/app/ui/components/modals/RequestCreateModal.js index 003ec3026..51283e98c 100644 --- a/app/ui/components/modals/RequestCreateModal.js +++ b/app/ui/components/modals/RequestCreateModal.js @@ -87,7 +87,7 @@ class RequestCreateModal extends Component { this.modal = m}> New Request -
    +
    - + +
    {!this._shouldNotHaveBody() ? (
    diff --git a/app/ui/components/modals/SettingsModal.js b/app/ui/components/modals/SettingsModal.js index 88f581a9e..fe06a9286 100644 --- a/app/ui/components/modals/SettingsModal.js +++ b/app/ui/components/modals/SettingsModal.js @@ -4,7 +4,6 @@ import {shell} from 'electron'; import Modal from '../base/Modal'; import ModalBody from '../base/ModalBody'; import ModalHeader from '../base/ModalHeader'; -import ModalFooter from '../base/ModalFooter'; import SettingsShortcuts from '../settings/SettingsShortcuts'; import SettingsAbout from '../settings/SettingsAbout'; import SettingsGeneral from '../settings/SettingsGeneral'; @@ -14,7 +13,6 @@ import * as models from '../../../models'; import {getAppVersion, getAppName} from '../../../common/constants'; import * as session from '../../../sync/session'; import {showModal} from './index'; -import SignupModal from './SignupModal'; import * as sync from '../../../sync'; import {trackEvent} from '../../../analytics/index'; @@ -82,7 +80,8 @@ class SettingsModal extends Component { - + @@ -126,9 +125,8 @@ class SettingsModal extends Component { this.modal.hide()} - handleUpdateSetting={(key, value) => models.settings.update(settings, {[key]: value})} - handleShowSignup={() => showModal(SignupModal)} + email={session.getEmail() || ''} + handleExit={this._handleClose} handleCancelAccount={sync.cancelAccount} handleReset={() => this._handleSyncReset()} handleLogout={sync.logout} @@ -139,11 +137,6 @@ class SettingsModal extends Component { - {/**/} - {/**/} - {/**/} ); } diff --git a/app/ui/components/modals/SignupModal.js b/app/ui/components/modals/SignupModal.js deleted file mode 100644 index b7be5141f..000000000 --- a/app/ui/components/modals/SignupModal.js +++ /dev/null @@ -1,195 +0,0 @@ -import React, {Component} from 'react'; -import classnames from 'classnames'; -import Link from '../base/Link'; -import Modal from '../base/Modal'; -import ModalBody from '../base/ModalBody'; -import ModalHeader from '../base/ModalHeader'; -import ModalFooter from '../base/ModalFooter'; -import * as session from '../../../sync/session'; -import {showModal} from './index'; -import LoginModal from './LoginModal'; -import * as sync from '../../../sync'; -import {trackEvent} from '../../../analytics'; - -const STEP_BASIC_INFO = 'basic'; -const STEP_CONFIRM_PASSWORD = 'confirm'; -const STEP_LOGIN_INFO = 'done'; - -class SignupModal extends Component { - state = { - step: STEP_BASIC_INFO, - error: '', - loading: false - }; - - async _handleSignup (e) { - e.preventDefault(); - - if (this.state.step === STEP_BASIC_INFO) { - this.setState({step: STEP_CONFIRM_PASSWORD}); - return; - } - - this.setState({error: '', loading: true}); - - const email = this._emailInput.value; - const password = this._passwordInput.value; - const firstName = this._nameFirstInput.value; - const lastName = this._nameLastInput.value; - - try { - await session.signup(firstName, lastName, email, password); - this.setState({step: STEP_LOGIN_INFO, loading: false}); - sync.init(); - } catch (e) { - this.setState({error: e.message, loading: false}); - } - } - - _handleLogin (e) { - e.preventDefault(); - - this.modal.hide(); - showModal(LoginModal, {}); - trackEvent('Signup', 'Switch to Login'); - } - - _checkPasswordsMatch () { - if (this._passwordInput.value !== this._passwordConfirmInput.value) { - this._passwordConfirmInput.setCustomValidity('Password didn\'t match') - } else { - this._passwordConfirmInput.setCustomValidity('') - } - } - - show () { - this.setState({step: STEP_BASIC_INFO, loading: false, error: ''}); - this.modal.show(); - setTimeout(() => this._nameFirstInput.focus(), 200); - } - - render () { - const {step} = this.state; - - let inner = null; - if (step === STEP_BASIC_INFO || step === STEP_CONFIRM_PASSWORD) { - inner = [ - Sign Up For a New Account, - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - {step === STEP_CONFIRM_PASSWORD ? ( -
    -

    - Keep your password safe because it cannot be recovered -
    - - Read More - {" "} - about how your password is used to encrypt your data - -

    -
    -
    - -
    - -
    -
    - ) : null} - {this.state.error ? ( -
    ** {this.state.error}
    - ) : null} -
    , - -
    - Already have an account? - {" "} - Login -
    - -
    - ] - } else { - inner = [ - Account Created, - -

    Thanks for signing up!

    -

    A verification email has been sent to your email address. You may now log in.

    -
    , - - - - ] - } - - return ( - - this.modal = m} {...this.props}> - {inner} - - - ) - } -} - -SignupModal.propTypes = {}; -export default SignupModal; diff --git a/app/ui/components/modals/WorkspaceSettingsModal.js b/app/ui/components/modals/WorkspaceSettingsModal.js index 3893e887f..9b9d35cf9 100644 --- a/app/ui/components/modals/WorkspaceSettingsModal.js +++ b/app/ui/components/modals/WorkspaceSettingsModal.js @@ -250,8 +250,8 @@ class WorkspaceSettingsModal extends Component { />
    -
    -
    +
    +
    -
    +


      Or  
    -
    +