mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 14:19:58 +00:00
Better cookie handling/editing
This commit is contained in:
parent
2887282032
commit
c69c8d2dd0
@ -3,4 +3,5 @@ charset = utf-8
|
|||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
end_of_line = lf
|
@ -2,8 +2,10 @@
|
|||||||
import React, {PureComponent} from 'react';
|
import React, {PureComponent} from 'react';
|
||||||
import {Tabs, TabList, Tab, TabPanel} from 'react-tabs';
|
import {Tabs, TabList, Tab, TabPanel} from 'react-tabs';
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
|
import deepEqual from 'deep-equal';
|
||||||
import * as toughCookie from 'tough-cookie';
|
import * as toughCookie from 'tough-cookie';
|
||||||
import * as models from '../../../models';
|
import * as models from '../../../models';
|
||||||
|
import clone from 'clone';
|
||||||
import {DEBOUNCE_MILLIS} from '../../../common/constants';
|
import {DEBOUNCE_MILLIS} from '../../../common/constants';
|
||||||
import {trackEvent} from '../../../analytics/index';
|
import {trackEvent} from '../../../analytics/index';
|
||||||
import Modal from '../base/modal';
|
import Modal from '../base/modal';
|
||||||
@ -96,9 +98,11 @@ class CookieModifyModal extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handleCookieUpdate (oldCookie: Cookie, cookie: Cookie) {
|
async _handleCookieUpdate (oldCookie: Cookie, cookie: Cookie) {
|
||||||
const {cookieJar} = this.props;
|
// Clone so we don't modify the original
|
||||||
|
const cookieJar = clone(this.props.cookieJar);
|
||||||
|
|
||||||
const {cookies} = cookieJar;
|
const {cookies} = cookieJar;
|
||||||
const index = cookies.findIndex(c => c.domain === oldCookie.domain && c.key === oldCookie.key);
|
const index = cookies.findIndex(c => deepEqual(c, oldCookie));
|
||||||
|
|
||||||
cookieJar.cookies = [
|
cookieJar.cookies = [
|
||||||
...cookies.slice(0, index),
|
...cookies.slice(0, index),
|
||||||
@ -144,6 +148,21 @@ class CookieModifyModal extends PureComponent {
|
|||||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getRawCookieString () {
|
||||||
|
const {cookie} = this.state;
|
||||||
|
|
||||||
|
if (!cookie) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return cookieToString(toughCookie.Cookie.fromJSON(JSON.stringify(cookie)));
|
||||||
|
} catch (err) {
|
||||||
|
console.warn('Failed to parse cookie string', err);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
cookieJar,
|
cookieJar,
|
||||||
@ -151,91 +170,78 @@ class CookieModifyModal extends PureComponent {
|
|||||||
handleGetRenderContext
|
handleGetRenderContext
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!cookieJar) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isValid,
|
isValid,
|
||||||
cookie
|
cookie
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
if (!cookie || !cookieJar) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const textFields = ['key', 'value', 'domain', 'path', 'expires'];
|
const textFields = ['key', 'value', 'domain', 'path', 'expires'];
|
||||||
const checkFields = ['secure', 'httpOnly'];
|
const checkFields = ['secure', 'httpOnly'];
|
||||||
|
|
||||||
let rawCookieString = '';
|
|
||||||
try {
|
|
||||||
rawCookieString = cookieToString(toughCookie.Cookie.fromJSON(JSON.stringify(cookie)));
|
|
||||||
} catch (err) {
|
|
||||||
console.warn('Failed to parse cookie', err);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal ref={this._setModalRef} {...this.props}>
|
<Modal ref={this._setModalRef} {...this.props}>
|
||||||
<ModalHeader>Edit Cookie</ModalHeader>
|
<ModalHeader>Edit Cookie</ModalHeader>
|
||||||
<ModalBody className="cookie-modify">
|
<ModalBody className="cookie-modify">
|
||||||
<Tabs>
|
{cookieJar && cookie && (
|
||||||
<TabList>
|
<Tabs>
|
||||||
<Tab>
|
<TabList>
|
||||||
<button>Friendly</button>
|
<Tab>
|
||||||
</Tab>
|
<button>Friendly</button>
|
||||||
<Tab>
|
</Tab>
|
||||||
<button>Raw</button>
|
<Tab>
|
||||||
</Tab>
|
<button>Raw</button>
|
||||||
</TabList>
|
</Tab>
|
||||||
<TabPanel>
|
</TabList>
|
||||||
<div className="pad">
|
<TabPanel>
|
||||||
{textFields.map((field, i) => {
|
<div className="pad">
|
||||||
const val = (cookie[field] || '').toString();
|
{textFields.map((field, i) => {
|
||||||
|
const val = (cookie[field] || '').toString();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-control form-control--outlined" key={i}>
|
<div className="form-control form-control--outlined" key={i}>
|
||||||
<label>{this._capitalize(field)}
|
<label>{this._capitalize(field)}
|
||||||
<OneLineEditor
|
<OneLineEditor
|
||||||
className={isValid[field] ? '' : 'input--error'}
|
className={isValid[field] ? '' : 'input--error'}
|
||||||
forceEditor
|
forceEditor
|
||||||
type="text"
|
type="text"
|
||||||
render={handleRender}
|
render={handleRender}
|
||||||
getRenderContext={handleGetRenderContext}
|
getRenderContext={handleGetRenderContext}
|
||||||
defaultValue={val || ''}
|
defaultValue={val || ''}
|
||||||
onChange={value => this._handleChange(field, value)}/>
|
onChange={value => this._handleChange(field, value)}/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="pad no-pad-top cookie-modify__checkboxes row-around txt-lg">
|
||||||
|
{checkFields.map((field, i) => {
|
||||||
|
const checked = !!cookie[field];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label key={i}>{this._capitalize(field)}
|
||||||
|
<input
|
||||||
|
className="space-left"
|
||||||
|
type="checkbox"
|
||||||
|
name={field}
|
||||||
|
defaultChecked={checked || false}
|
||||||
|
onChange={e => this._handleChange(field, e)}
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
);
|
||||||
);
|
})}
|
||||||
})}
|
</div>
|
||||||
</div>
|
</TabPanel>
|
||||||
<div className="pad no-pad-top cookie-modify__checkboxes row-around txt-lg">
|
<TabPanel className="react-tabs__tab-panel pad">
|
||||||
{checkFields.map((field, i) => {
|
<div className="form-control form-control--outlined">
|
||||||
const checked = !!cookie[field];
|
<label>Raw Cookie String
|
||||||
|
<input type="text"
|
||||||
return (
|
onChange={this._handleChangeRawValue}
|
||||||
<label key={i}>{this._capitalize(field)}
|
defaultValue={this._getRawCookieString()}/>
|
||||||
<input
|
</label>
|
||||||
className="space-left"
|
</div>
|
||||||
type="checkbox"
|
</TabPanel>
|
||||||
name={field}
|
</Tabs>
|
||||||
defaultChecked={checked || false}
|
)}
|
||||||
onChange={e => this._handleChange(field, e)}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel className="react-tabs__tab-panel pad">
|
|
||||||
<div className="form-control form-control--outlined">
|
|
||||||
<label>Raw Cookie String
|
|
||||||
<input type="text"
|
|
||||||
onChange={this._handleChangeRawValue}
|
|
||||||
defaultValue={rawCookieString}/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</TabPanel>
|
|
||||||
</Tabs>
|
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<button className="btn" onClick={this.hide}>
|
<button className="btn" onClick={this.hide}>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React, {PureComponent} from 'react';
|
import React, {PureComponent} from 'react';
|
||||||
|
import deepEqual from 'deep-equal';
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
import Modal from '../base/modal';
|
import Modal from '../base/modal';
|
||||||
import ModalBody from '../base/modal-body';
|
import ModalBody from '../base/modal-body';
|
||||||
@ -10,6 +11,7 @@ import * as models from '../../../models';
|
|||||||
import {trackEvent} from '../../../analytics/index';
|
import {trackEvent} from '../../../analytics/index';
|
||||||
import type {Cookie, CookieJar} from '../../../models/cookie-jar';
|
import type {Cookie, CookieJar} from '../../../models/cookie-jar';
|
||||||
import type {Workspace} from '../../../models/workspace';
|
import type {Workspace} from '../../../models/workspace';
|
||||||
|
import {fuzzyMatch} from '../../../common/misc';
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
class CookiesModal extends PureComponent {
|
class CookiesModal extends PureComponent {
|
||||||
@ -21,7 +23,8 @@ class CookiesModal extends PureComponent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state: {
|
state: {
|
||||||
filter: string
|
filter: string,
|
||||||
|
visibleCookieIndexes: Array<number> | null
|
||||||
};
|
};
|
||||||
|
|
||||||
modal: Modal | null;
|
modal: Modal | null;
|
||||||
@ -29,8 +32,10 @@ class CookiesModal extends PureComponent {
|
|||||||
|
|
||||||
constructor (props: any) {
|
constructor (props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: ''
|
filter: '',
|
||||||
|
visibleCookieIndexes: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,21 +79,56 @@ class CookiesModal extends PureComponent {
|
|||||||
trackEvent('Cookie', 'Delete');
|
trackEvent('Cookie', 'Delete');
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleFilterChange (e: Event & {target: HTMLInputElement}) {
|
async _handleFilterChange (e: Event & {target: HTMLInputElement}) {
|
||||||
const filter = e.target.value;
|
const filter = e.target.value;
|
||||||
this.setState({filter});
|
this._applyFilter(filter, this.props.cookieJar.cookies);
|
||||||
trackEvent('Cookie Editor', 'Filter Change');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_getFilteredSortedCookies () {
|
componentWillReceiveProps (nextProps: any) {
|
||||||
const {cookieJar} = this.props;
|
// Re-filter if we received new cookies
|
||||||
const {filter} = this.state;
|
// Compare cookies with Dates cast to strings
|
||||||
|
const sameCookies = deepEqual(
|
||||||
|
this.props.cookieJar.cookies,
|
||||||
|
nextProps.cookieJar.cookies
|
||||||
|
);
|
||||||
|
|
||||||
const {cookies} = cookieJar;
|
if (!sameCookies) {
|
||||||
return cookies.filter(c => {
|
this._applyFilter(this.state.filter, nextProps.cookieJar.cookies);
|
||||||
const toSearch = JSON.stringify(c).toLowerCase();
|
}
|
||||||
return toSearch.indexOf(filter.toLowerCase()) !== -1;
|
}
|
||||||
});
|
|
||||||
|
async _applyFilter (filter: string, cookies: Array<Cookie>) {
|
||||||
|
const renderedCookies = await this.props.handleRender(cookies);
|
||||||
|
|
||||||
|
let visibleCookieIndexes;
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
visibleCookieIndexes = [];
|
||||||
|
for (let i = 0; i < renderedCookies.length; i++) {
|
||||||
|
const toSearch = JSON.stringify(renderedCookies[i]);
|
||||||
|
const matched = fuzzyMatch(filter, toSearch);
|
||||||
|
if (matched) {
|
||||||
|
visibleCookieIndexes.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visibleCookieIndexes = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('APPLIED FILTER', filter, visibleCookieIndexes);
|
||||||
|
|
||||||
|
this.setState({filter, visibleCookieIndexes});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getVisibleCookies () {
|
||||||
|
const {cookieJar} = this.props;
|
||||||
|
const {visibleCookieIndexes} = this.state;
|
||||||
|
|
||||||
|
if (visibleCookieIndexes === null) {
|
||||||
|
return cookieJar.cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieJar.cookies.filter((c, i) => visibleCookieIndexes.includes(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
async show () {
|
async show () {
|
||||||
@ -98,6 +138,9 @@ class CookiesModal extends PureComponent {
|
|||||||
this.filterInput && this.filterInput.focus();
|
this.filterInput && this.filterInput.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
||||||
|
// make sure the filter is up to date
|
||||||
|
await this._applyFilter(this.state.filter, this.props.cookieJar.cookies);
|
||||||
|
|
||||||
this.modal && this.modal.show();
|
this.modal && this.modal.show();
|
||||||
trackEvent('Cookie Manager', 'Show');
|
trackEvent('Cookie Manager', 'Show');
|
||||||
}
|
}
|
||||||
@ -113,44 +156,42 @@ class CookiesModal extends PureComponent {
|
|||||||
cookieJar
|
cookieJar
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (!cookieJar) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
filter
|
filter
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const filteredCookies = this._getFilteredSortedCookies();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal ref={this._setModalRef} wide tall {...this.props}>
|
<Modal ref={this._setModalRef} wide tall {...this.props}>
|
||||||
<ModalHeader>Manage Cookies</ModalHeader>
|
<ModalHeader>Manage Cookies</ModalHeader>
|
||||||
<ModalBody className="cookie-list" noScroll>
|
<ModalBody className="cookie-list" noScroll>
|
||||||
<div className="pad">
|
{cookieJar && (
|
||||||
<div className="form-control form-control--outlined">
|
<div>
|
||||||
<label>Filter Cookies
|
<div className="pad">
|
||||||
<input ref={this._setFilterInputRef}
|
<div className="form-control form-control--outlined">
|
||||||
onChange={this._handleFilterChange}
|
<label>Filter Cookies
|
||||||
type="text"
|
<input ref={this._setFilterInputRef}
|
||||||
placeholder="twitter.com"
|
onChange={this._handleFilterChange}
|
||||||
defaultValue=""/>
|
type="text"
|
||||||
</label>
|
placeholder="twitter.com"
|
||||||
|
defaultValue=""/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="cookie-list__list border-top">
|
||||||
|
<div className="pad-top">
|
||||||
|
<CookieList
|
||||||
|
handleShowModifyCookieModal={handleShowModifyCookieModal}
|
||||||
|
handleRender={handleRender}
|
||||||
|
cookies={this._getVisibleCookies()}
|
||||||
|
onCookieAdd={this._handleCookieAdd}
|
||||||
|
onCookieDelete={this._handleCookieDelete}
|
||||||
|
// Set the domain to the filter so that it shows up if we're filtering
|
||||||
|
newCookieDomainName={filter || 'domain.com'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="cookie-list__list border-top">
|
|
||||||
<div className="pad-top">
|
|
||||||
<CookieList
|
|
||||||
handleShowModifyCookieModal={handleShowModifyCookieModal}
|
|
||||||
handleRender={handleRender}
|
|
||||||
cookies={filteredCookies}
|
|
||||||
onCookieAdd={this._handleCookieAdd}
|
|
||||||
onCookieDelete={this._handleCookieDelete}
|
|
||||||
// Set the domain to the filter so that it shows up if we're filtering
|
|
||||||
newCookieDomainName={filter || 'domain.com'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<div className="margin-left faint italic txt-sm tall">
|
<div className="margin-left faint italic txt-sm tall">
|
||||||
|
3
flow-typed/deep-equal.js
vendored
Normal file
3
flow-typed/deep-equal.js
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
declare module 'deep-equal' {
|
||||||
|
declare module.exports: Function
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user