@@ -31,6 +31,7 @@ import { GraphVisualiser } from './GraphVisualiser';
3131import { usePgAdmin } from '../../../../../../static/js/PgAdminProvider' ;
3232import pgAdmin from 'sources/pgadmin' ;
3333import { connectServer , connectServerModal } from '../connectServer' ;
34+ import { useLatestFunc } from '../../../../../../static/js/custom_hooks' ;
3435
3536const StyledBox = styled ( Box ) ( ( { theme} ) => ( {
3637 display : 'flex' ,
@@ -846,7 +847,7 @@ export function ResultSet() {
846847 const modalId = MODAL_DIALOGS . QT_CONFIRMATIONS ;
847848 // We'll use this track if any changes were saved.
848849 // It will help to decide whether results refresh is required or not on page change.
849- const pageDataDirty = useRef ( false ) ;
850+ const pageDataOutOfSync = useRef ( false ) ;
850851
851852 const selectedCell = useRef ( [ ] ) ;
852853 const selectedRange = useRef ( null ) ;
@@ -873,9 +874,10 @@ export function ResultSet() {
873874 // To use setLoaderText to the ResultSetUtils.
874875 rsu . current . setLoaderText = setLoaderText ;
875876
876- const isDataChanged = ( ) => {
877- return Boolean ( _ . size ( dataChangeStore . updated ) || _ . size ( dataChangeStore . added ) || _ . size ( dataChangeStore . deleted ) ) ;
878- } ;
877+ const isDataChangedRef = useRef ( false ) ;
878+ useEffect ( ( ) => {
879+ isDataChangedRef . current = Boolean ( _ . size ( dataChangeStore . updated ) || _ . size ( dataChangeStore . added ) || _ . size ( dataChangeStore . deleted ) ) ;
880+ } , [ dataChangeStore ] ) ;
879881
880882 const fireRowsColsCellChanged = ( ) => {
881883 eventBus . fireEvent ( QUERY_TOOL_EVENTS . SELECTED_ROWS_COLS_CELL_CHANGED , selectedRows . size , selectedColumns . size , selectedRange . current , selectedCell . current ?. length ) ;
@@ -948,7 +950,7 @@ export function ResultSet() {
948950 } ) ;
949951 } ;
950952
951- if ( isDataChanged ( ) && ! refreshData ) {
953+ if ( isDataChangedRef . current && ! refreshData ) {
952954 queryToolCtx . modal . confirm (
953955 gettext ( 'Unsaved changes' ) ,
954956 gettext ( 'The data has been modified, but not saved. Are you sure you wish to discard the changes?' ) ,
@@ -1093,6 +1095,7 @@ export function ResultSet() {
10931095 setLoaderText ( gettext ( 'Fetching rows...' ) ) ;
10941096 try {
10951097 res = await rsu . current . getWindowRows ( fromRownum , toRownum ) ;
1098+ resetSelectionAndChanges ( ) ;
10961099 const newRows = rsu . current . processRows ( res . data . data . result , columns ) ;
10971100 setRows ( [ ...newRows ] ) ;
10981101 setQueryData ( ( prev ) => ( {
@@ -1118,23 +1121,48 @@ export function ResultSet() {
11181121
11191122 useEffect ( ( ) => {
11201123 let deregExecEnd ;
1124+ let deregSaveDataDone ;
11211125 const deregFetch = eventBus . registerListener ( QUERY_TOOL_EVENTS . FETCH_WINDOW , ( ...args ) => {
1122- if ( pageDataDirty . current ) {
1123- deregExecEnd = eventBus . registerListener ( QUERY_TOOL_EVENTS . EXECUTION_END , ( success ) => {
1124- if ( ! success ) return ;
1125- pageDataDirty . current = false ;
1126+ const impl = ( ) => {
1127+ if ( pageDataOutOfSync . current ) {
1128+ deregExecEnd = eventBus . registerListener ( QUERY_TOOL_EVENTS . EXECUTION_END , ( success ) => {
1129+ if ( ! success ) return ;
1130+ pageDataOutOfSync . current = false ;
1131+ fetchWindow ( ...args ) ;
1132+ } , true ) ;
1133+ eventBus . fireEvent ( QUERY_TOOL_EVENTS . EXECUTION_START , rsu . current . query , { refreshData : true } ) ;
1134+ // executionStartCallback(rsu.current.query, {refreshData: true});
1135+ } else {
1136+ pageDataOutOfSync . current = false ;
11261137 fetchWindow ( ...args ) ;
1127- } , true ) ;
1128- executionStartCallback ( rsu . current . query , { refreshData : true } ) ;
1129- } else {
1130- pageDataDirty . current = false ;
1131- fetchWindow ( ...args ) ;
1138+ }
1139+ } ;
1140+
1141+ if ( ! isDataChangedRef . current ) {
1142+ impl ( ) ;
1143+ return ;
11321144 }
1133- resetSelectionAndChanges ( ) ;
1145+ queryToolCtx . modal . showModal ( gettext ( 'Save data changes?' ) , ( closeModal ) => (
1146+ < ConfirmSaveContent
1147+ closeModal = { closeModal }
1148+ text = { gettext ( 'The data has changed. Do you want to save changes before moving to next page?' ) }
1149+ onDontSave = { ( ) => {
1150+ impl ( ) ;
1151+ } }
1152+ onSave = { async ( ) => {
1153+ deregSaveDataDone = eventBus . registerListener ( QUERY_TOOL_EVENTS . SAVE_DATA_END , ( success ) => {
1154+ if ( ! success ) return ;
1155+ impl ( ) ;
1156+ } , true ) ;
1157+ await triggerSaveData ( ) ;
1158+ } }
1159+ />
1160+ ) , { id : modalId } ) ;
11341161 } ) ;
11351162 return ( ) => {
11361163 deregFetch ( ) ;
11371164 deregExecEnd ?. ( ) ;
1165+ deregSaveDataDone ?. ( ) ;
11381166 } ;
11391167 } , [ columns ] ) ;
11401168
@@ -1144,7 +1172,7 @@ export function ResultSet() {
11441172
11451173 const warnSaveDataClose = ( ) => {
11461174 // No changes.
1147- if ( ! isDataChanged ( ) || ! queryToolCtx . preferences ?. sqleditor . prompt_save_data_changes ) {
1175+ if ( ! isDataChangedRef . current || ! queryToolCtx . preferences ?. sqleditor . prompt_save_data_changes ) {
11481176 eventBus . fireEvent ( QUERY_TOOL_EVENTS . WARN_SAVE_TEXT_CLOSE ) ;
11491177 return ;
11501178 }
@@ -1172,8 +1200,9 @@ export function ResultSet() {
11721200 } ;
11731201 } , [ dataChangeStore ] ) ;
11741202
1175- const triggerSaveData = async ( ) => {
1203+ const triggerSaveData = useLatestFunc ( async ( ) => {
11761204 if ( ! _ . size ( dataChangeStore . updated ) && ! _ . size ( dataChangeStore . added ) && ! _ . size ( dataChangeStore . deleted ) ) {
1205+ eventBus . fireEvent ( QUERY_TOOL_EVENTS . SAVE_DATA_END , false ) ;
11771206 return ;
11781207 }
11791208 rsu . current . historyQuerySource = QuerySources . SAVE_DATA ;
@@ -1210,7 +1239,7 @@ export function ResultSet() {
12101239 } catch { /* History errors should not bother others */ }
12111240
12121241 if ( ! respData . data . status ) {
1213- pageDataDirty . current = false ;
1242+ pageDataOutOfSync . current = false ;
12141243 eventBus . fireEvent ( QUERY_TOOL_EVENTS . SET_MESSAGE , respData . data . result ) ;
12151244 pgAdmin . Browser . notifier . error ( respData . data . result , 20000 ) ;
12161245 // If the transaction is not idle, notify the user that previous queries are not rolled back,
@@ -1220,10 +1249,11 @@ export function ResultSet() {
12201249 'still active; previous queries are unaffected.' ) ) ;
12211250 }
12221251 setLoaderText ( null ) ;
1252+ eventBus . fireEvent ( QUERY_TOOL_EVENTS . SAVE_DATA_END , false ) ;
12231253 return ;
12241254 }
12251255
1226- pageDataDirty . current = true ;
1256+ pageDataOutOfSync . current = true ;
12271257 if ( _ . size ( dataChangeStore . added ) ) {
12281258 // Update the rows in a grid after addition
12291259 respData . data . query_results . forEach ( ( qr ) => {
@@ -1258,18 +1288,21 @@ export function ResultSet() {
12581288 resetSelectionAndChanges ( ) ;
12591289 eventBus . fireEvent ( QUERY_TOOL_EVENTS . SET_CONNECTION_STATUS , respData . data . transaction_status ) ;
12601290 eventBus . fireEvent ( QUERY_TOOL_EVENTS . SET_MESSAGE , '' ) ;
1291+ setLoaderText ( null ) ;
1292+ eventBus . fireEvent ( QUERY_TOOL_EVENTS . SAVE_DATA_END , true ) ;
12611293 pgAdmin . Browser . notifier . success ( gettext ( 'Data saved successfully.' ) ) ;
12621294 if ( respData . data . transaction_status > CONNECTION_STATUS . TRANSACTION_STATUS_IDLE ) {
12631295 pgAdmin . Browser . notifier . info ( gettext ( 'Auto-commit is off. You still need to commit changes to the database.' ) ) ;
12641296 }
12651297 } catch ( error ) {
1266- pageDataDirty . current = false ;
1298+ pageDataOutOfSync . current = false ;
12671299 eventBus . fireEvent ( QUERY_TOOL_EVENTS . HANDLE_API_ERROR , error , {
12681300 checkTransaction : true ,
12691301 } ) ;
1302+ setLoaderText ( null ) ;
1303+ eventBus . fireEvent ( QUERY_TOOL_EVENTS . SAVE_DATA_END , false ) ;
12701304 }
1271- setLoaderText ( null ) ;
1272- } ;
1305+ } ) ;
12731306
12741307 useEffect ( ( ) => {
12751308 eventBus . registerListener ( QUERY_TOOL_EVENTS . TRIGGER_SAVE_DATA , triggerSaveData ) ;
0 commit comments