[#7694] don't reset the records list pagination on record update

This commit is contained in:
Gani Georgiev
2026-05-18 19:22:51 +03:00
parent b9b0e5ae80
commit d0b2551e78
9 changed files with 73 additions and 17 deletions
+2
View File
@@ -5,6 +5,8 @@
- Added extra checks for the connected user IP in the realtime APIs to prevent bruteforce guest subscription update attempts and to serve as an extra XSRF protection for the "all-in-one" OAuth2 realtime handler. - Added extra checks for the connected user IP in the realtime APIs to prevent bruteforce guest subscription update attempts and to serve as an extra XSRF protection for the "all-in-one" OAuth2 realtime handler.
- Don't reset the records list pagination on record update ([#7694](https://github.com/pocketbase/pocketbase/issues/7694)).
- (@todo) Updated all `golang.org/x/` packages containing several [security fixes](https://groups.google.com/g/golang-announce/c/PdiGK3xulk4). - (@todo) Updated all `golang.org/x/` packages containing several [security fixes](https://groups.google.com/g/golang-announce/c/PdiGK3xulk4).
+1 -1
View File
@@ -12,4 +12,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs"
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk" PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk" PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases" PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
PB_VERSION = "v0.38.1" PB_VERSION = "v0.38.2-dev"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -13,9 +13,9 @@
<!-- prism --> <!-- prism -->
<script src="./libs/prism/prism.js" data-manual></script> <script src="./libs/prism/prism.js" data-manual></script>
<script type="module" crossorigin src="./assets/index-_S-h6ORT.js"></script> <script type="module" crossorigin src="./assets/index-qP-3lJAQ.js"></script>
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js"> <link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
<link rel="stylesheet" crossorigin href="./assets/index-DRWtZ-mO.css"> <link rel="stylesheet" crossorigin href="./assets/index-Cbzik3Gc.css">
</head> </head>
<body> <body>
</body> </body>
+3 -3
View File
@@ -51,16 +51,16 @@ window.app.components.refreshButton = function(propsArg = {}) {
props.onclick(e); props.onclick(e);
} }
btn.classList.add("rotate"); btn.dataset.rotate = true;
btn.addEventListener("animationend", () => { btn.addEventListener("animationend", () => {
btn.classList.remove("rotate"); btn.dataset.rotate = false;
}); });
// fallback // fallback
clearTimeout(refreshTimeoutId); clearTimeout(refreshTimeoutId);
refreshTimeoutId = setTimeout(() => { refreshTimeoutId = setTimeout(() => {
clearTimeout(refreshTimeoutId); clearTimeout(refreshTimeoutId);
btn.classList.remove("rotate"); btn.dataset.rotate = false;
}, 500); }, 500);
}, },
}, },
+9 -1
View File
@@ -14,6 +14,7 @@ export function pageCollections(route) {
const pageData = store({ const pageData = store({
reset: null, reset: null,
suggestReset: false,
activeRecordIdOrModel: route.query[RECORD_QUERY_KEY]?.[0] || "", activeRecordIdOrModel: route.query[RECORD_QUERY_KEY]?.[0] || "",
sort: route.query[SORT_QUERY_KEY]?.[0] || "", sort: route.query[SORT_QUERY_KEY]?.[0] || "",
filter: route.query[FILTER_QUERY_KEY]?.[0] || "", filter: route.query[FILTER_QUERY_KEY]?.[0] || "",
@@ -148,7 +149,7 @@ export function pageCollections(route) {
]; ];
const documentEvents = { const documentEvents = {
"record:save": (e) => { "record:create": (e) => {
if (e.detail.collectionId != app.store.activeCollection?.id) { if (e.detail.collectionId != app.store.activeCollection?.id) {
return; return;
} }
@@ -238,6 +239,9 @@ export function pageCollections(route) {
), ),
app.components.refreshButton({ app.components.refreshButton({
onclick: () => refreshRecordsList(), onclick: () => refreshRecordsList(),
className: () =>
`btn transparent circle rotate-btn ${pageData.suggestReset ? "warning" : "secondary"}`,
tooltip: () => `Refresh${pageData.suggestReset ? "\n(detected change)" : ""}`,
}), }),
), ),
t.div( t.div(
@@ -317,6 +321,10 @@ export function pageCollections(route) {
pageData.filter = newFilter; pageData.filter = newFilter;
pageData.sort = newSort; pageData.sort = newSort;
}, },
suggestReset: () => pageData.suggestReset,
onSuggestResetChange: (suggestReset) => {
pageData.suggestReset = !!suggestReset;
},
}), }),
t.footer( t.footer(
{ className: "page-footer" }, { className: "page-footer" },
+2 -1
View File
@@ -161,7 +161,6 @@ button {
&.danger { &.danger {
color: var(--dangerColor); color: var(--dangerColor);
} }
&.accent,
&.secondary, &.secondary,
&.info, &.info,
&.success, &.success,
@@ -283,12 +282,14 @@ button {
} }
} }
&[data-rotate="true"],
&.rotate { &.rotate {
i { i {
animation: halfRotate 300ms; animation: halfRotate 300ms;
} }
} }
&[data-loading="true"],
&.loading { &.loading {
cursor: default; cursor: default;
&::after { &::after {
+48 -4
View File
@@ -31,6 +31,7 @@ window.app.components.recordsList = function(propsArg = {}) {
filter: "", filter: "",
sort: "", sort: "",
reset: undefined, reset: undefined,
suggestReset: false,
// --- // ---
rid: undefined, rid: undefined,
id: undefined, id: undefined,
@@ -38,6 +39,7 @@ window.app.components.recordsList = function(propsArg = {}) {
className: "", className: "",
onchange: (newFilter, newSort) => {}, onchange: (newFilter, newSort) => {},
onselect: (record) => {}, onselect: (record) => {},
onSuggestResetChange: (suggestReset) => {},
}); });
const watchers = app.utils.extendStore(props, propsArg); const watchers = app.utils.extendStore(props, propsArg);
@@ -84,6 +86,10 @@ window.app.components.recordsList = function(propsArg = {}) {
data.isLoading = true; data.isLoading = true;
if (reset) {
props.suggestReset = false;
}
try { try {
// (note if changed update the related counter query too!) // (note if changed update the related counter query too!)
const normalizedFilter = app.utils.normalizeSearchFilter( const normalizedFilter = app.utils.normalizeSearchFilter(
@@ -240,6 +246,19 @@ window.app.components.recordsList = function(propsArg = {}) {
return field.hidden; return field.hidden;
} }
// note: for now always assuming single field sort
function getActiveSortKey() {
if (!props.sort) {
return "";
}
if (props.sort.startsWith("-") || props.sort.startsWith("+")) {
return props.sort.substring(1);
}
return props.sort;
}
let deleteRefreshTimeoutId; let deleteRefreshTimeoutId;
const documentEvents = { const documentEvents = {
@@ -248,13 +267,25 @@ window.app.components.recordsList = function(propsArg = {}) {
return; return;
} }
// optimistically merge with existing to minimize flickering
const found = data.records.find((r) => r.id == e.detail.id); const found = data.records.find((r) => r.id == e.detail.id);
if (found) { if (!found) {
Object.assign(found, JSON.parse(JSON.stringify(e.detail))); return loadRecords(true);
} }
loadRecords(true); const sortKey = getActiveSortKey();
// loosely check if reload is needed
if (
// active server-side filter
props.filter?.length
// sorted value has changed
|| (sortKey && found[sortKey] != e.detail[sortKey])
) {
props.suggestReset = true;
}
// merge with existing
Object.assign(found, JSON.parse(JSON.stringify(e.detail)));
}, },
"record:delete": (e) => { "record:delete": (e) => {
if ( if (
@@ -331,6 +362,7 @@ window.app.components.recordsList = function(propsArg = {}) {
), ),
); );
// persist columns prefereces
watchers.push( watchers.push(
watch( watch(
() => JSON.stringify(data.columnsPreferences), () => JSON.stringify(data.columnsPreferences),
@@ -344,6 +376,18 @@ window.app.components.recordsList = function(propsArg = {}) {
}, },
), ),
); );
// sync suggestReset
watchers.push(
watch(
() => props.suggestReset,
(newVal, oldVal) => {
if (typeof oldVal != "undefined") {
props.onSuggestResetChange?.(newVal);
}
},
),
);
}, },
onunmount: () => { onunmount: () => {
app.pb.cancelRequest(uniqueId); app.pb.cancelRequest(uniqueId);