diff --git a/packages/bruno-app/src/components/CodeEditor/index.js b/packages/bruno-app/src/components/CodeEditor/index.js
index a83535300..4a8f0591a 100644
--- a/packages/bruno-app/src/components/CodeEditor/index.js
+++ b/packages/bruno-app/src/components/CodeEditor/index.js
@@ -186,6 +186,8 @@ export default class CodeEditor extends React.Component {
if (editor) {
editor.setOption('lint', this.props.mode && editor.getValue().trim().length > 0 ? this.lintOptions : false);
editor.on('change', this._onEdit);
+ editor.on('scroll', this.onScroll);
+ editor.scrollTo(null, this.props.initialScroll);
this.addOverlay();
const getAllVariablesHandler = () => getAllVariables(this.props.collection, this.props.item);
@@ -230,12 +232,18 @@ export default class CodeEditor extends React.Component {
if (this.props.theme !== prevProps.theme && this.editor) {
this.editor.setOption('theme', this.props.theme === 'dark' ? 'monokai' : 'default');
}
+
+ if (this.props.initialScroll !== prevProps.initialScroll) {
+ this.editor.scrollTo(null, this.props.initialScroll);
+ }
+
this.ignoreChangeEvent = false;
}
componentWillUnmount() {
if (this.editor) {
this.editor.off('change', this._onEdit);
+ this.editor.off('scroll', this.onScroll);
this.editor = null;
}
@@ -271,6 +279,8 @@ export default class CodeEditor extends React.Component {
this.editor.setOption('mode', 'brunovariables');
};
+ onScroll = (event) => this.props.onScroll?.(event);
+
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {
this.editor.setOption('lint', this.editor.getValue().trim().length > 0 ? this.lintOptions : false);
diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js
index d4fbee07e..595c170e2 100644
--- a/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/QueryResultPreview/index.js
@@ -1,7 +1,9 @@
import React, { useState, useEffect } from 'react';
import CodeEditor from 'components/CodeEditor/index';
import { get } from 'lodash';
+import find from 'lodash/find';
import { useDispatch, useSelector } from 'react-redux';
+import { updateResponsePaneScrollPosition } from 'providers/ReduxStore/slices/tabs';
import { sendRequest } from 'providers/ReduxStore/slices/collections/actions';
import { Document, Page } from 'react-pdf';
import 'pdfjs-dist/build/pdf.worker';
@@ -51,6 +53,10 @@ const QueryResultPreview = ({
displayedTheme
}) => {
const preferences = useSelector((state) => state.app.preferences);
+ const tabs = useSelector((state) => state.tabs.tabs);
+ const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
+ const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
+
const dispatch = useDispatch();
const [numPages, setNumPages] = useState(null);
@@ -66,9 +72,19 @@ const QueryResultPreview = ({
if (disableRunEventListener) {
return;
}
+
dispatch(sendRequest(item, collection.uid));
};
+ const onScroll = (event) => {
+ dispatch(
+ updateResponsePaneScrollPosition({
+ uid: focusedTab.uid,
+ scrollY: event.doc.scrollTop
+ })
+ );
+ };
+
switch (previewTab?.mode) {
case 'preview-web': {
const webViewSrc = data.replace('
', ``);
@@ -111,8 +127,10 @@ const QueryResultPreview = ({
fontSize={get(preferences, 'font.codeFontSize')}
theme={displayedTheme}
onRun={onRun}
+ onScroll={onScroll}
value={formattedData}
mode={mode}
+ initialScroll={focusedTab.responsePaneScrollPosition || 0}
readOnly
/>
);
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
index 75b5ecc55..0c490df80 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
@@ -45,7 +45,7 @@ import {
} from './index';
import { each } from 'lodash';
-import { closeAllCollectionTabs } from 'providers/ReduxStore/slices/tabs';
+import { closeAllCollectionTabs, updateResponsePaneScrollPosition } from 'providers/ReduxStore/slices/tabs';
import { resolveRequestFilename } from 'utils/common/platform';
import { parsePathParams, splitOnFirst } from 'utils/url/index';
import { sendCollectionOauth2Request as _sendCollectionOauth2Request } from 'utils/network/index';
@@ -258,6 +258,13 @@ export const sendRequest = (item, collectionUid) => (dispatch, getState) => {
const requestUid = uuid();
itemCopy.requestUid = requestUid;
+ await dispatch(
+ updateResponsePaneScrollPosition({
+ uid: state.tabs.activeTabUid,
+ scrollY: 0
+ })
+ );
+
await dispatch(
initRunRequestEvent({
requestUid,
@@ -1627,27 +1634,33 @@ export const clearOauth2Cache = (payload) => async (dispatch, getState) => {
};
// todo: could be removed
-export const loadRequestViaWorker = ({ collectionUid, pathname }) => (dispatch, getState) => {
- return new Promise(async (resolve, reject) => {
- const { ipcRenderer } = window;
- ipcRenderer.invoke('renderer:load-request-via-worker', { collectionUid, pathname }).then(resolve).catch(reject);
- });
-};
+export const loadRequestViaWorker =
+ ({ collectionUid, pathname }) =>
+ (dispatch, getState) => {
+ return new Promise(async (resolve, reject) => {
+ const { ipcRenderer } = window;
+ ipcRenderer.invoke('renderer:load-request-via-worker', { collectionUid, pathname }).then(resolve).catch(reject);
+ });
+ };
// todo: could be removed
-export const loadRequest = ({ collectionUid, pathname }) => (dispatch, getState) => {
- return new Promise(async (resolve, reject) => {
- const { ipcRenderer } = window;
- ipcRenderer.invoke('renderer:load-request', { collectionUid, pathname }).then(resolve).catch(reject);
- });
-};
+export const loadRequest =
+ ({ collectionUid, pathname }) =>
+ (dispatch, getState) => {
+ return new Promise(async (resolve, reject) => {
+ const { ipcRenderer } = window;
+ ipcRenderer.invoke('renderer:load-request', { collectionUid, pathname }).then(resolve).catch(reject);
+ });
+ };
-export const loadLargeRequest = ({ collectionUid, pathname }) => (dispatch, getState) => {
- return new Promise(async (resolve, reject) => {
- const { ipcRenderer } = window;
- ipcRenderer.invoke('renderer:load-large-request', { collectionUid, pathname }).then(resolve).catch(reject);
- });
-};
+export const loadLargeRequest =
+ ({ collectionUid, pathname }) =>
+ (dispatch, getState) => {
+ return new Promise(async (resolve, reject) => {
+ const { ipcRenderer } = window;
+ ipcRenderer.invoke('renderer:load-large-request', { collectionUid, pathname }).then(resolve).catch(reject);
+ });
+ };
export const mountCollection =
({ collectionUid, collectionPathname, brunoConfig }) =>
@@ -1671,16 +1684,17 @@ export const showInFolder = (collectionPath) => () => {
});
};
-export const updateRunnerConfiguration = (collectionUid, selectedRequestItems, requestItemsOrder, delay) => (dispatch) => {
- dispatch(
- _updateRunnerConfiguration({
- collectionUid,
- selectedRequestItems,
- requestItemsOrder,
- delay
- })
- );
-};
+export const updateRunnerConfiguration =
+ (collectionUid, selectedRequestItems, requestItemsOrder, delay) => (dispatch) => {
+ dispatch(
+ _updateRunnerConfiguration({
+ collectionUid,
+ selectedRequestItems,
+ requestItemsOrder,
+ delay
+ })
+ );
+ };
export const updateActiveConnectionsInStore = (activeConnectionIds) => (dispatch, getState) => {
dispatch(updateActiveConnections(activeConnectionIds));
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/tabs.js b/packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
index 0225417ec..694239da6 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/tabs.js
@@ -62,10 +62,10 @@ export const tabsSlice = createSlice({
? preview
: !nonReplaceableTabTypes.includes(type),
...(uid ? { folderUid: uid } : {})
- }
+ };
state.activeTabUid = uid;
- return
+ return;
}
state.tabs.push({
@@ -74,6 +74,7 @@ export const tabsSlice = createSlice({
requestPaneWidth: null,
requestPaneTab: requestPaneTab || defaultRequestPaneTab,
responsePaneTab: 'response',
+ responsePaneScrollPosition: null,
type: type || 'request',
...(uid ? { folderUid: uid } : {}),
preview: preview !== undefined
@@ -126,6 +127,13 @@ export const tabsSlice = createSlice({
tab.responsePaneTab = action.payload.responsePaneTab;
}
},
+ updateResponsePaneScrollPosition: (state, action) => {
+ const tab = find(state.tabs, (t) => t.uid === action.payload.uid);
+
+ if (tab) {
+ tab.responsePaneScrollPosition = action.payload.scrollY;
+ }
+ },
closeTabs: (state, action) => {
const activeTab = find(state.tabs, (t) => t.uid === state.activeTabUid);
const tabUids = action.payload.tabUids || [];
@@ -167,8 +175,8 @@ export const tabsSlice = createSlice({
const tab = find(state.tabs, (t) => t.uid === uid);
if (tab) {
tab.preview = false;
- } else{
- console.error("Tab not found!")
+ } else {
+ console.error('Tab not found!');
}
}
}
@@ -181,6 +189,7 @@ export const {
updateRequestPaneTabWidth,
updateRequestPaneTab,
updateResponsePaneTab,
+ updateResponsePaneScrollPosition,
closeTabs,
closeAllCollectionTabs,
makeTabPermanent