[#7694] don't reset the records list pagination on record update
This commit is contained in:
@@ -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).
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
Vendored
+2
-2
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user