Updates to cookie editor

This commit is contained in:
Gregory Schier
2017-08-22 15:30:57 -07:00
parent 21aadc36c8
commit d654feca82
16 changed files with 279 additions and 191 deletions

View File

@@ -7,23 +7,14 @@ import * as models from '../models';
import {setDefaultProtocol} from './misc';
import * as db from './database';
import * as templating from '../templating';
import type {CookieJar} from '../models/cookie-jar';
export const KEEP_ON_ERROR = 'keep';
export const THROW_ON_ERROR = 'throw';
type Cookie = {
domain: string,
path: string,
key: string,
value: string,
expires: number
}
export type RenderedRequest = Request & {
cookies: Array<{name: string, value: string, disabled?: boolean}>,
cookieJar: {
cookies: Array<Cookie>
}
cookieJar: CookieJar
};
export async function buildRenderContext (
@@ -227,7 +218,8 @@ export async function getRenderedRequest (
models.workspace.type
]);
const workspace = ancestors.find(doc => doc.type === models.workspace.type);
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
const parentId = workspace ? workspace._id : 'n/a';
const cookieJar = await models.cookieJar.getOrCreateForParentId(parentId);
const renderContext = await getRenderContext(request, environmentId, ancestors);

View File

@@ -1,9 +1,27 @@
// @flow
import * as db from '../common/database';
import type {BaseModel} from './index';
export const name = 'Cookie Jar';
export const type = 'CookieJar';
export const prefix = 'jar';
export const canDuplicate = true;
export type Cookie = {
domain: string,
path: string,
key: string,
value: string,
expires: number
}
type BaseCookieJar = {
name: string,
cookies: Array<Cookie>
};
export type CookieJar = BaseModel & BaseCookieJar;
export function init () {
return {
name: 'Default Jar',
@@ -11,16 +29,15 @@ export function init () {
};
}
export function migrate (doc) {
export function migrate (doc: CookieJar): CookieJar {
return doc;
}
export function create (patch = {}) {
export function create (patch: Object = {}) {
return db.docCreate(type, patch);
}
export async function getOrCreateForWorkspace (workspace) {
const parentId = workspace._id;
export async function getOrCreateForParentId (parentId: string) {
const cookieJars = await db.find(type, {parentId});
if (cookieJars.length === 0) {
return await create({parentId});
@@ -33,10 +50,10 @@ export function all () {
return db.all(type);
}
export function getById (id) {
export function getById (id: string) {
return db.get(type, id);
}
export function update (cookieJar, patch) {
export function update (cookieJar: CookieJar, patch: Object = {}) {
return db.docUpdate(cookieJar, patch);
}

View File

@@ -37,24 +37,22 @@ export const requestMeta = _requestMeta;
export const response = _response;
export const oAuth2Token = _oAuth2Token;
const _models = {
[stats.type]: stats,
[settings.type]: settings,
[workspace.type]: workspace,
[workspaceMeta.type]: workspaceMeta,
[environment.type]: environment,
[cookieJar.type]: cookieJar,
[requestGroup.type]: requestGroup,
[requestGroupMeta.type]: requestGroupMeta,
[request.type]: request,
[requestVersion.type]: requestVersion,
[requestMeta.type]: requestMeta,
[response.type]: response,
[oAuth2Token.type]: oAuth2Token
};
export function all () {
return Object.keys(_models).map(type => _models[type]);
return [
stats,
settings,
workspace,
workspaceMeta,
environment,
cookieJar,
requestGroup,
requestGroupMeta,
request,
requestVersion,
requestMeta,
response,
oAuth2Token
];
}
export function types () {
@@ -62,7 +60,7 @@ export function types () {
}
export function getModel (type: string) {
return _models[type] || null;
return all().find(m => m.type === type) || null;
}
export function canDuplicate (type: string) {
@@ -88,12 +86,12 @@ export function initModel (type: string, ...sources: Array<Object>) {
const model = getModel(type);
if (!model) {
const choices = Object.keys(_models).join(', ');
const choices = all().map(m => m.type).join(', ');
throw new Error(`Tried to init invalid model "${type}". Choices are ${choices}`);
}
// Define global default fields
const objectDefaults = Object.assign({
const objectDefaults = Object.assign({}, {
_id: null,
type: type,
parentId: null,

View File

@@ -13,7 +13,7 @@ describe('RequestExtension cookie', async () => {
parentId: workspace._id,
url: 'https://insomnia.rest/foo/bar'
});
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
const cookieJar = await models.cookieJar.getOrCreateForParentId(workspace._id);
const jar = jarFromCookies(cookieJar.cookies);
jar.setCookieSync([
'foo=bar',

View File

@@ -54,7 +54,7 @@ export default {
case 'url':
return getRequestUrl(context, request);
case 'cookie':
const cookieJar = await context.util.models.cookieJar.getOrCreateForWorkspace(workspace);
const cookieJar = await context.util.models.cookieJar.getOrCreateForParentId(workspace._id);
const url = await getRequestUrl(context, request);
const value = await getCookieValue(cookieJar, url, name);
return value;

View File

@@ -5,13 +5,10 @@ import {Cookie} from 'tough-cookie';
import {cookieToString} from '../../common/cookies';
import PromptButton from './base/prompt-button';
import RenderedText from './rendered-text';
@autobind
class CookieList extends PureComponent {
shouldComponentUpdate (nextProps, nextState) {
return nextProps.cookies !== this.props.cookies;
}
_handleCookieAdd () {
const newCookie = new Cookie({
key: 'foo',
@@ -32,7 +29,8 @@ class CookieList extends PureComponent {
render () {
const {
cookies,
handleShowModifyCookieModal
handleShowModifyCookieModal,
handleRender
} = this.props;
return (
@@ -43,10 +41,10 @@ class CookieList extends PureComponent {
<th style={{minWidth: '10rem'}}>Domain</th>
<th style={{width: '90%'}}>Cookie</th>
<th style={{width: '2rem'}} className="text-right">
<button className="btn btn--super-compact"
<button className="btn btn--super-duper-compact btn--outlined txt-md"
onClick={this._handleCookieAdd}
title="Add cookie">
<i className="fa fa-plus-circle"/>
Add Cookie
</button>
</th>
</tr>
@@ -57,18 +55,20 @@ class CookieList extends PureComponent {
return (
<tr className="selectable" key={i}>
<td
onClick={() => handleShowModifyCookieModal(cookie)}>
<RenderedText render={handleRender} component="td">
{cookie.domain}
</td>
<td
onClick={() => handleShowModifyCookieModal(cookie)}>
</RenderedText>
<RenderedText render={handleRender} component="td">
{cookieString}
</td>
<td
onClick={null}
className="text-right">
<PromptButton className="btn btn--super-compact"
</RenderedText>
<td onClick={null} className="text-right no-wrap">
<button className="btn btn--super-compact btn--outlined"
onClick={e => handleShowModifyCookieModal(cookie)}
title="Edit cookie properties">
Edit
</button>
{' '}
<PromptButton className="btn btn--super-compact btn--outlined"
addIcon
confirmMessage=" "
onClick={e => this._handleDeleteCookie(cookie)}
@@ -87,8 +87,7 @@ class CookieList extends PureComponent {
I couldn't find any cookies for you.
</p>
<p>
<button className="btn btn--clicky"
onClick={e => this._handleCookieAdd()}>
<button className="btn btn--clicky" onClick={e => this._handleCookieAdd()}>
Add Cookie <i className="fa fa-plus-circle"/>
</button>
</p>
@@ -104,7 +103,8 @@ CookieList.propTypes = {
onCookieDelete: PropTypes.func.isRequired,
cookies: PropTypes.array.isRequired,
newCookieDomainName: PropTypes.string.isRequired,
handleShowModifyCookieModal: PropTypes.func.isRequired
handleShowModifyCookieModal: PropTypes.func.isRequired,
handleRender: PropTypes.func.isRequired
};
export default CookieList;

View File

@@ -1,6 +1,8 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Tabs, TabList, Tab, TabPanel} from 'react-tabs';
import autobind from 'autobind-decorator';
import {Cookie} from 'tough-cookie';
import * as models from '../../../models';
import {DEBOUNCE_MILLIS} from '../../../common/constants';
import {trackEvent} from '../../../analytics/index';
@@ -9,6 +11,7 @@ import ModalBody from '../base/modal-body';
import ModalHeader from '../base/modal-header';
import ModalFooter from '../base/modal-footer';
import OneLineEditor from '../codemirror/one-line-editor';
import {cookieToString} from '../../../common/cookies';
@autobind
class CookieModifyModal extends PureComponent {
@@ -17,16 +20,8 @@ class CookieModifyModal extends PureComponent {
this.state = {
cookieJar: null,
defaultCookie: {
key: 'foo',
value: 'bar',
domain: 'domain.tld',
path: '/',
expires: Infinity,
secure: false,
httpOnly: false
},
cookie: null,
rawValue: '',
isValid: {
key: true,
value: true,
@@ -43,7 +38,7 @@ class CookieModifyModal extends PureComponent {
async _load () {
const {workspace} = this.props;
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
const cookieJar = await models.cookieJar.getOrCreateForParentId(workspace._id);
this.setState({cookieJar});
}
@@ -66,11 +61,20 @@ class CookieModifyModal extends PureComponent {
async _saveChanges () {
const {cookieJar} = this.state;
await models.cookieJar.update(cookieJar);
this.props.reloadCookiesModal();
this._load();
await this._load();
}
_handleCookieUpdate (oldCookie, cookie) {
_handleChangeRawValue (e) {
const value = e.target.value;
clearTimeout(this._rawTimeout);
this._rawTimeout = setTimeout(async () => {
const cookie = Cookie.parse(value);
await this._handleCookieUpdate(this.state.cookie, cookie);
}, DEBOUNCE_MILLIS);
}
async _handleCookieUpdate (oldCookie, cookie) {
const {cookieJar} = this.state;
const {cookies} = cookieJar;
const index = cookies.findIndex(c => c.domain === oldCookie.domain && c.key === oldCookie.key);
@@ -81,7 +85,9 @@ class CookieModifyModal extends PureComponent {
...cookies.slice(index + 1)
];
this._saveChanges(cookieJar);
this.setState({cookie});
await this._saveChanges(cookieJar);
trackEvent('Cookie', 'Update');
}
@@ -120,61 +126,92 @@ class CookieModifyModal extends PureComponent {
handleRender,
handleGetRenderContext
} = this.props;
const {
isValid,
cookie,
defaultCookie
cookieJar
} = this.state;
if (!cookie || !cookieJar) {
return null;
}
const textFields = ['key', 'value', 'domain', 'path', 'expires'];
const checkFields = ['secure', 'httpOnly'];
let rawCookieString = '';
try {
rawCookieString = cookieToString(Cookie.fromJSON(JSON.stringify(cookie)));
} catch (err) {
console.warn('Failed to parse cookie', err);
}
return (
<Modal ref={this._setModalRef} {...this.props}>
<ModalHeader>Modify Cookie</ModalHeader>
<ModalHeader>Edit Cookie</ModalHeader>
<ModalBody className="cookie-modify">
<div className="pad">
{textFields.map((field, i) => {
const val = Object.assign({}, defaultCookie, cookie)[field].toString();
<Tabs>
<TabList>
<Tab>
<button>Friendly</button>
</Tab>
<Tab>
<button>Raw</button>
</Tab>
</TabList>
<TabPanel>
<div className="pad">
{textFields.map((field, i) => {
const val = (cookie[field] || '').toString();
return (
<div className="form-control form-control--outlined" key={i}>
<label>{this._capitalize(field)}
<OneLineEditor
className={isValid[field] ? '' : 'input--error'}
ref={this._setInputRef}
forceEditor
type="text"
render={handleRender}
getRenderContext={handleGetRenderContext}
defaultValue={val || ''}
onChange={(i) => this._handleChange(i, field)}/>
</label>
</div>
);
})}
</div>
<div className="pad cookie-modify__checkboxes">
{checkFields.map((field, i) => {
const checked = Object.assign({}, defaultCookie, cookie)[field];
return (
<div className="form-control form-control--outlined" key={i}>
<label>{this._capitalize(field)}
<OneLineEditor
className={isValid[field] ? '' : 'input--error'}
ref={this._setInputRef}
forceEditor
type="text"
render={handleRender}
getRenderContext={handleGetRenderContext}
defaultValue={val || ''}
onChange={(i) => this._handleChange(i, field)}/>
</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
type="checkbox"
name={field}
defaultChecked={checked || false}
onChange={(e) => this._handleChange(e, field)}
/>
return (
<label key={i}>{this._capitalize(field)}
<input
className="space-left"
type="checkbox"
name={field}
defaultChecked={checked || false}
onChange={(e) => this._handleChange(e, field)}
/>
</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>
</div>
</TabPanel>
</Tabs>
</ModalBody>
<ModalFooter>
<div className="margin-left faint italic txt-sm tall">
* cookies are automatically sent with relevant requests
</div>
<button className="btn" onClick={this.hide}>
Done
</button>
@@ -185,7 +222,6 @@ class CookieModifyModal extends PureComponent {
}
CookieModifyModal.propTypes = {
reloadCookiesModal: PropTypes.func.isRequired,
handleRender: PropTypes.func.isRequired,
handleGetRenderContext: PropTypes.func.isRequired,
workspace: PropTypes.object.isRequired

View File

@@ -1,5 +1,5 @@
// @flow
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import autobind from 'autobind-decorator';
import Modal from '../base/modal';
import ModalBody from '../base/modal-body';
@@ -8,63 +8,74 @@ import ModalFooter from '../base/modal-footer';
import CookieList from '../cookie-list';
import * as models from '../../../models';
import {trackEvent} from '../../../analytics/index';
import type {Cookie, CookieJar} from '../../../models/cookie-jar';
import type {Workspace} from '../../../models/workspace';
@autobind
class CookiesModal extends PureComponent {
constructor (props) {
props: {
handleShowModifyCookieModal: Function,
handleRender: Function,
cookieJar: CookieJar,
workspace: Workspace
};
state: {
filter: string
};
modal: Modal | null;
filterInput: HTMLInputElement | null;
constructor (props: any) {
super(props);
this.state = {
cookieJar: null,
filter: ''
};
}
_setModalRef (n) {
_setModalRef (n: React.Element<*> | null) {
this.modal = n;
}
_setFilterInputRef (n) {
_setFilterInputRef (n: HTMLInputElement | null) {
this.filterInput = n;
}
async _saveChanges () {
const {cookieJar} = this.state;
const {cookieJar} = this.props;
await models.cookieJar.update(cookieJar);
this._load();
}
_handleCookieAdd (cookie) {
const {cookieJar} = this.state;
async _handleCookieAdd (cookie: Cookie) {
const {cookieJar} = this.props;
const {cookies} = cookieJar;
cookieJar.cookies = [cookie, ...cookies];
this._saveChanges(cookieJar);
await this._saveChanges();
trackEvent('Cookie', 'Create');
}
_handleCookieDelete (cookie) {
const {cookieJar} = this.state;
async _handleCookieDelete (cookie: Cookie) {
const {cookieJar} = this.props;
const {cookies} = cookieJar;
// NOTE: This is sketchy because it relies on the same reference
cookieJar.cookies = cookies.filter(c => c !== cookie);
this._saveChanges(cookieJar);
await this._saveChanges();
trackEvent('Cookie', 'Delete');
}
_handleFilterChange (e) {
_handleFilterChange (e: Event & {target: HTMLInputElement}) {
const filter = e.target.value;
this.setState({filter});
trackEvent('Cookie Editor', 'Filter Change');
}
_getFilteredSortedCookies () {
const {cookieJar, filter} = this.state;
if (!cookieJar) {
// Nothing to do yet.
return [];
}
const {cookieJar} = this.props;
const {filter} = this.state;
const {cookies} = cookieJar;
return cookies.filter(c => {
@@ -73,39 +84,29 @@ class CookiesModal extends PureComponent {
});
}
async _load () {
const {workspace} = this.props;
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
this.setState({cookieJar});
}
async show () {
await this._load();
this.modal && this.modal.show();
this.modal.show();
setTimeout(() => this.filterInput.focus(), 100);
setTimeout(() => {
this.filterInput && this.filterInput.focus();
}, 100);
trackEvent('Cookie Manager', 'Show');
}
hide () {
this.modal.hide();
}
toggle () {
if (this.modal.isOpen()) {
this.hide();
} else {
this.show();
}
this.modal && this.modal.hide();
}
render () {
const filteredCookies = this._getFilteredSortedCookies();
const {
handleShowModifyCookieModal
handleShowModifyCookieModal,
handleRender
} = this.props;
const {filter} = this.state;
const {
filter
} = this.state;
return (
<Modal ref={this._setModalRef} wide tall {...this.props}>
@@ -126,6 +127,7 @@ class CookiesModal extends PureComponent {
<div className="pad-top">
<CookieList
handleShowModifyCookieModal={handleShowModifyCookieModal}
handleRender={handleRender}
cookies={filteredCookies}
onCookieAdd={this._handleCookieAdd}
onCookieDelete={this._handleCookieDelete}
@@ -137,7 +139,7 @@ class CookiesModal extends PureComponent {
</ModalBody>
<ModalFooter>
<div className="margin-left faint italic txt-sm tall">
* click a cookie to modify it
* cookies are automatically sent with relevant requests
</div>
<button className="btn" onClick={this.hide}>
Done
@@ -148,10 +150,5 @@ class CookiesModal extends PureComponent {
}
}
CookiesModal.propTypes = {
handleShowModifyCookieModal: PropTypes.func.isRequired,
workspace: PropTypes.object.isRequired
};
// export CookiesModal;
export default CookiesModal;

View File

@@ -31,10 +31,6 @@ export function hideModal (modalCls) {
return _getModal(modalCls).hide();
}
export function reloadModal (modalCls) {
return _getModal(modalCls)._load();
}
export function hideAllModals () {
for (const key of Object.keys(modals)) {
const modal = modals[key];

View File

@@ -0,0 +1,42 @@
// @flow
import React from 'react';
class RenderedText extends React.PureComponent {
props: {
component: string,
children: string,
render: Function
};
state: {
renderedText: string
};
constructor (props: any) {
super(props);
this.state = {
renderedText: ''
};
}
async _render () {
const {render, children} = this.props;
const renderedText = await render(children);
this.setState({renderedText});
}
componentDidMount () {
this._render();
}
componentDidUpdate () {
this._render();
}
render () {
const {component} = this.props;
return React.createElement(component, {}, this.state.renderedText);
}
}
export default RenderedText;

View File

@@ -4,11 +4,12 @@ import type {Response} from '../../models/response';
import type {OAuth2Token} from '../../models/o-auth-2-token';
import type {Workspace} from '../../models/workspace';
import type {Request, RequestAuthentication, RequestBody, RequestHeader, RequestParameter} from '../../models/request';
import {updateMimeType} from '../../models/request';
import React from 'react';
import autobind from 'autobind-decorator';
import classnames from 'classnames';
import {registerModal, showModal, reloadModal} from './modals/index';
import {registerModal, reloadModal, showModal} from './modals/index';
import AlertModal from './modals/alert-modal';
import ChangelogModal from './modals/changelog-modal';
import CookiesModal from './modals/cookies-modal';
@@ -35,9 +36,9 @@ import WorkspaceSettingsModal from './modals/workspace-settings-modal';
import WorkspaceShareSettingsModal from './modals/workspace-share-settings-modal';
import CodePromptModal from './modals/code-prompt-modal';
import * as models from '../../models/index';
import {updateMimeType} from '../../models/request';
import {trackEvent} from '../../analytics/index';
import * as importers from 'insomnia-importers';
import type {CookieJar} from '../../models/cookie-jar';
const rUpdate = (request, ...args) => {
if (!request) {
@@ -108,6 +109,7 @@ class Wrapper extends React.PureComponent {
environments: Array<Object>,
activeRequestResponses: Array<Response>,
activeWorkspace: Workspace,
activeCookieJar: CookieJar,
// Optional
oAuth2Token: ?OAuth2Token,
@@ -250,12 +252,8 @@ class Wrapper extends React.PureComponent {
showModal(CookiesModal, this.props.activeWorkspace);
}
_handleReloadCookiesModal (): void {
reloadModal(CookiesModal);
}
_handleShowModifyCookieModal (cookie: Object): void {
showModal(CookieModifyModal, cookie, this.props.activeWorkspace);
showModal(CookieModifyModal, cookie);
}
_handleShowRequestSettingsModal (): void {
@@ -335,6 +333,7 @@ class Wrapper extends React.PureComponent {
activeEnvironment,
activeRequest,
activeWorkspace,
activeCookieJar,
activeRequestResponses,
activeResponse,
environments,
@@ -532,11 +531,12 @@ class Wrapper extends React.PureComponent {
<CookiesModal
handleShowModifyCookieModal={this._handleShowModifyCookieModal}
handleRender={handleRender}
ref={registerModal}
workspace={activeWorkspace}
cookieJar={activeCookieJar}
/>
<CookieModifyModal
reloadCookiesModal={this._handleReloadCookiesModal}
handleRender={handleRender}
handleGetRenderContext={handleGetRenderContext}
ref={registerModal}

View File

@@ -21,7 +21,7 @@ import * as globalActions from '../redux/modules/global';
import * as db from '../../common/database';
import * as models from '../../models';
import {trackEvent} from '../../analytics';
import {selectActiveOAuth2Token, selectActiveRequest, selectActiveRequestMeta, selectActiveRequestResponses, selectActiveResponse, selectActiveWorkspace, selectActiveWorkspaceMeta, selectEntitiesLists, selectSidebarChildren, selectUnseenWorkspaces, selectWorkspaceRequestsAndRequestGroups} from '../redux/selectors';
import {selectActiveCookieJar, selectActiveOAuth2Token, selectActiveRequest, selectActiveRequestMeta, selectActiveRequestResponses, selectActiveResponse, selectActiveWorkspace, selectActiveWorkspaceMeta, selectEntitiesLists, selectSidebarChildren, selectUnseenWorkspaces, selectWorkspaceRequestsAndRequestGroups} from '../redux/selectors';
import RequestCreateModal from '../components/modals/request-create-modal';
import GenerateCodeModal from '../components/modals/generate-code-modal';
import WorkspaceSettingsModal from '../components/modals/workspace-settings-modal';
@@ -903,6 +903,7 @@ function mapStateToProps (state, props) {
// Request stuff
const requestMeta = selectActiveRequestMeta(state, props) || {};
const activeRequest = selectActiveRequest(state, props);
const activeCookieJar = selectActiveCookieJar(state, props);
const responsePreviewMode = requestMeta.previewMode || PREVIEW_MODE_SOURCE;
const responseFilter = requestMeta.responseFilter || '';
const responseFilterHistory = requestMeta.responseFilterHistory || [];
@@ -937,6 +938,7 @@ function mapStateToProps (state, props) {
activeRequest,
activeRequestResponses,
activeResponse,
activeCookieJar,
sidebarHidden,
sidebarFilter,
sidebarWidth,

View File

@@ -7,10 +7,6 @@
grid-template-rows: auto minmax(0, 1fr);
table {
td {
cursor: pointer;
}
input:not([type="checkbox"]) {
padding: @padding-xs @padding-xxs;
width: 100%;

View File

@@ -27,17 +27,11 @@
}
.cookie-modify__checkboxes {
padding-top: 0;
position: relative;
display: flex;
justify-content: space-around;
font-size: medium;
label > input {
vertical-align: middle;
margin-left: 5px;
height: 20px;
width: 20px;
}
//label > input {
// vertical-align: middle;
// margin-left: 5px;
// height: 20px;
// width: 20px;
//}
}
}

View File

@@ -214,7 +214,7 @@ p.notice {
}
.text-right {
text-align: right;
text-align: right !important;
}
.text-left {
@@ -312,6 +312,15 @@ blockquote {
}
}
.row-around {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around !important;
width: 100%;
box-sizing: border-box;
}
.row-spaced {
display: flex;
flex-direction: row;

View File

@@ -163,6 +163,15 @@ export const selectActiveRequest = createSelector(
}
);
export const selectActiveCookieJar = createSelector(
selectEntitiesLists,
selectActiveWorkspace,
(entities, workspace) => {
const cookieJar = entities.cookieJars.find(cj => cj.parentId === workspace._id);
return cookieJar || null;
}
);
export const selectActiveOAuth2Token = createSelector(
selectEntitiesLists,
selectActiveWorkspaceMeta,