Display spinner animation during loading
This commit is contained in:
1 parent
4871613c67
commit
688811317c
23 files changed
+447
-307
No files matched your search
+141
-171
@@ -76,14 +76,14 @@ for (const r of [
|
||||
}
|
||||
|
||||
class QueryResponse {
|
||||
public get fulfilled(): boolean {
|
||||
public get fulfilled(): number {
|
||||
queries.accessed.set(this, Date.now());
|
||||
return !!queries.fulfilled.get(this);
|
||||
return queries.fulfilled.get(this) || 0;
|
||||
}
|
||||
|
||||
public get fulfilling(): boolean {
|
||||
queries.accessed.set(this, Date.now());
|
||||
return queries.fulfilling.has(this);
|
||||
return !(queries.fulfilled.get(this) >= fulfillTimestamp);
|
||||
}
|
||||
|
||||
public get value(): any {
|
||||
@@ -348,18 +348,7 @@ export function fetch(
|
||||
return queryResponse;
|
||||
}
|
||||
|
||||
function _fulfill(
|
||||
accessTimestamp: number,
|
||||
_fulfillTimestamp: number
|
||||
): Promise<boolean> {
|
||||
let updated = false;
|
||||
|
||||
if (_fulfillTimestamp > fulfillTimestamp) {
|
||||
for (const resource of Object.values(resources))
|
||||
resource.combinedFilter = null;
|
||||
fulfillTimestamp = _fulfillTimestamp;
|
||||
}
|
||||
|
||||
export function fulfill(accessTimestamp: number): void {
|
||||
const allPromises = [];
|
||||
|
||||
for (const [resourceType, resource] of Object.entries(resources)) {
|
||||
@@ -373,38 +362,32 @@ function _fulfill(
|
||||
|
||||
if (!(fulfillTimestamp <= queries.fulfilled.get(queryResponse))) {
|
||||
queries.fulfilling.add(queryResponse);
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = unpackExpression(filter);
|
||||
allPromises.push(
|
||||
new Promise((resolve, reject) => {
|
||||
updated = true;
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = unpackExpression(filter);
|
||||
xhrRequest({
|
||||
method: "HEAD",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(filter),
|
||||
}),
|
||||
extract: (xhr) => {
|
||||
if (xhr.status === 403) throw new Error("Not authorized");
|
||||
if (!xhr.status) {
|
||||
throw new Error("Server is unreachable");
|
||||
} else if (xhr.status !== 200) {
|
||||
throw new Error(
|
||||
`Unexpected response status code ${xhr.status}`
|
||||
);
|
||||
}
|
||||
return +xhr.getResponseHeader("x-total-count");
|
||||
},
|
||||
background: true,
|
||||
})
|
||||
.then((c) => {
|
||||
queries.value.set(queryResponse, c);
|
||||
queries.fulfilled.set(queryResponse, fulfillTimestamp);
|
||||
queries.fulfilling.delete(queryResponse);
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => reject(err));
|
||||
xhrRequest({
|
||||
method: "HEAD",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(filter),
|
||||
}),
|
||||
extract: (xhr) => {
|
||||
if (xhr.status === 403) throw new Error("Not authorized");
|
||||
if (!xhr.status) {
|
||||
throw new Error("Server is unreachable");
|
||||
} else if (xhr.status !== 200) {
|
||||
throw new Error(
|
||||
`Unexpected response status code ${xhr.status}`
|
||||
);
|
||||
}
|
||||
return +xhr.getResponseHeader("x-total-count");
|
||||
},
|
||||
background: false,
|
||||
}).then((c) => {
|
||||
queries.value.set(queryResponse, c);
|
||||
queries.fulfilled.set(queryResponse, fulfillTimestamp);
|
||||
queries.fulfilling.delete(queryResponse);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -429,45 +412,39 @@ function _fulfill(
|
||||
const limit = queries.limit.get(queryResponse);
|
||||
const sort = queries.sort.get(queryResponse);
|
||||
if (limit) {
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = unpackExpression(filter);
|
||||
allPromises.push(
|
||||
new Promise((resolve, reject) => {
|
||||
updated = true;
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = unpackExpression(filter);
|
||||
xhrRequest({
|
||||
method: "GET",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(filter),
|
||||
limit: 1,
|
||||
skip: limit - 1,
|
||||
sort: JSON.stringify(sort),
|
||||
projection: Object.keys(sort).join(","),
|
||||
}),
|
||||
})
|
||||
.then((res) => {
|
||||
if ((res as any[]).length) {
|
||||
// Generate bookmark object
|
||||
const bm = Object.keys(sort).reduce((b, k) => {
|
||||
if (res[0][k] != null) {
|
||||
if (typeof res[0][k] === "object") {
|
||||
if (res[0][k].value != null)
|
||||
b[k] = res[0][k].value[0];
|
||||
} else {
|
||||
b[k] = res[0][k];
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}, {});
|
||||
queries.bookmark.set(queryResponse, bm);
|
||||
} else {
|
||||
queries.bookmark.delete(queryResponse);
|
||||
xhrRequest({
|
||||
method: "GET",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(filter),
|
||||
limit: 1,
|
||||
skip: limit - 1,
|
||||
sort: JSON.stringify(sort),
|
||||
projection: Object.keys(sort).join(","),
|
||||
}),
|
||||
background: true,
|
||||
}).then((res) => {
|
||||
if ((res as any[]).length) {
|
||||
// Generate bookmark object
|
||||
const bm = Object.keys(sort).reduce((b, k) => {
|
||||
if (res[0][k] != null) {
|
||||
if (typeof res[0][k] === "object") {
|
||||
if (res[0][k].value != null) b[k] = res[0][k].value[0];
|
||||
} else {
|
||||
b[k] = res[0][k];
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
.catch(reject);
|
||||
|
||||
return b;
|
||||
}, {});
|
||||
queries.bookmark.set(queryResponse, bm);
|
||||
} else {
|
||||
queries.bookmark.delete(queryResponse);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -475,31 +452,82 @@ function _fulfill(
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.all(allPromises)
|
||||
.then(() => {
|
||||
const allPromises2 = [];
|
||||
for (const [resourceType, toFetch] of Object.entries(toFetchAll)) {
|
||||
let combinedFilter = null;
|
||||
Promise.all(allPromises)
|
||||
.then(() => {
|
||||
let updated = false;
|
||||
const allPromises2 = [];
|
||||
for (const [resourceType, toFetch] of Object.entries(toFetchAll)) {
|
||||
let combinedFilter = null;
|
||||
|
||||
for (const queryResponse of toFetch) {
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = memoizedEvaluate(filter, null, fulfillTimestamp);
|
||||
const bookmark = queries.bookmark.get(queryResponse);
|
||||
const sort = queries.sort.get(queryResponse);
|
||||
if (bookmark) filter = limitFilter(filter, sort, bookmark);
|
||||
combinedFilter = or(combinedFilter, filter);
|
||||
}
|
||||
|
||||
const [union, diff] = unionDiff(
|
||||
resources[resourceType].combinedFilter,
|
||||
combinedFilter
|
||||
);
|
||||
|
||||
if (!diff) {
|
||||
for (const queryResponse of toFetch) {
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = memoizedEvaluate(filter, null, fulfillTimestamp);
|
||||
const limit = queries.limit.get(queryResponse);
|
||||
const bookmark = queries.bookmark.get(queryResponse);
|
||||
const sort = queries.sort.get(queryResponse);
|
||||
if (bookmark) filter = limitFilter(filter, sort, bookmark);
|
||||
combinedFilter = or(combinedFilter, filter);
|
||||
|
||||
queries.value.set(
|
||||
queryResponse,
|
||||
findMatches(resourceType, filter, sort, limit)
|
||||
);
|
||||
queries.fulfilled.set(queryResponse, fulfillTimestamp);
|
||||
queries.fulfilling.delete(queryResponse);
|
||||
updated = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const [union, diff] = unionDiff(
|
||||
resources[resourceType].combinedFilter,
|
||||
combinedFilter
|
||||
);
|
||||
let deleted = new Set<string>();
|
||||
if (!resources[resourceType].combinedFilter)
|
||||
deleted = new Set(resources[resourceType].objects.keys());
|
||||
|
||||
const combinedFilterDiff = diff;
|
||||
resources[resourceType].combinedFilter = union;
|
||||
|
||||
allPromises2.push(
|
||||
xhrRequest({
|
||||
method: "GET",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(combinedFilterDiff),
|
||||
}),
|
||||
background: false,
|
||||
}).then((res) => {
|
||||
for (const r of res as any[]) {
|
||||
const id =
|
||||
resourceType === "devices"
|
||||
? r["DeviceID.ID"].value[0]
|
||||
: r["_id"];
|
||||
resources[resourceType].objects.set(id, r);
|
||||
deleted.delete(id);
|
||||
}
|
||||
|
||||
for (const d of deleted) {
|
||||
const obj = resources[resourceType].objects.get(d);
|
||||
if (evaluate(combinedFilterDiff, obj, fulfillTimestamp))
|
||||
resources[resourceType].objects.delete(d);
|
||||
}
|
||||
|
||||
if (!diff) {
|
||||
for (const queryResponse of toFetch) {
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = memoizedEvaluate(filter, null, fulfillTimestamp);
|
||||
filter = unpackExpression(filter);
|
||||
const limit = queries.limit.get(queryResponse);
|
||||
const bookmark = queries.bookmark.get(queryResponse);
|
||||
const sort = queries.sort.get(queryResponse);
|
||||
@@ -512,87 +540,29 @@ function _fulfill(
|
||||
queries.fulfilled.set(queryResponse, fulfillTimestamp);
|
||||
queries.fulfilling.delete(queryResponse);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
updated = true;
|
||||
let deleted = new Set<string>();
|
||||
if (!resources[resourceType].combinedFilter)
|
||||
deleted = new Set(resources[resourceType].objects.keys());
|
||||
|
||||
const combinedFilterDiff = diff;
|
||||
resources[resourceType].combinedFilter = union;
|
||||
|
||||
allPromises2.push(
|
||||
new Promise((resolve2, reject2) => {
|
||||
xhrRequest({
|
||||
method: "GET",
|
||||
url:
|
||||
`api/${resourceType}/?` +
|
||||
m.buildQueryString({
|
||||
filter: memoizedStringify(combinedFilterDiff),
|
||||
}),
|
||||
})
|
||||
.then((res) => {
|
||||
for (const r of res as any[]) {
|
||||
const id =
|
||||
resourceType === "devices"
|
||||
? r["DeviceID.ID"].value[0]
|
||||
: r["_id"];
|
||||
resources[resourceType].objects.set(id, r);
|
||||
deleted.delete(id);
|
||||
}
|
||||
|
||||
for (const d of deleted) {
|
||||
const obj = resources[resourceType].objects.get(d);
|
||||
if (evaluate(combinedFilterDiff, obj, fulfillTimestamp))
|
||||
resources[resourceType].objects.delete(d);
|
||||
}
|
||||
|
||||
for (const queryResponse of toFetch) {
|
||||
let filter = queries.filter.get(queryResponse);
|
||||
filter = unpackExpression(filter);
|
||||
const limit = queries.limit.get(queryResponse);
|
||||
const bookmark = queries.bookmark.get(queryResponse);
|
||||
const sort = queries.sort.get(queryResponse);
|
||||
if (bookmark) filter = limitFilter(filter, sort, bookmark);
|
||||
|
||||
queries.value.set(
|
||||
queryResponse,
|
||||
findMatches(resourceType, filter, sort, limit)
|
||||
);
|
||||
queries.fulfilled.set(queryResponse, fulfillTimestamp);
|
||||
queries.fulfilling.delete(queryResponse);
|
||||
}
|
||||
resolve2();
|
||||
})
|
||||
.catch(reject2);
|
||||
})
|
||||
);
|
||||
}
|
||||
Promise.all(allPromises2)
|
||||
.then(() => resolve(updated))
|
||||
.catch(reject);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
export function fulfill(
|
||||
accessTimestamp: number,
|
||||
_fulfillTimestamp: number,
|
||||
callback?: (updated: boolean) => void
|
||||
): void {
|
||||
_fulfill(accessTimestamp, _fulfillTimestamp).then(callback, (err) => {
|
||||
notifications.push("error", err.message);
|
||||
if (callback) callback(false);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
if (updated) m.redraw();
|
||||
return Promise.all(allPromises2);
|
||||
})
|
||||
.catch((err) => {
|
||||
notifications.push("error", err.message);
|
||||
});
|
||||
}
|
||||
|
||||
export function getTimestamp(): number {
|
||||
return fulfillTimestamp;
|
||||
}
|
||||
|
||||
export function setTimestamp(t: number): void {
|
||||
if (t > fulfillTimestamp) {
|
||||
fulfillTimestamp = t;
|
||||
for (const resource of Object.values(resources))
|
||||
resource.combinedFilter = null;
|
||||
}
|
||||
}
|
||||
|
||||
export function postTasks(
|
||||
deviceId: string,
|
||||
tasks: QueueTask[]
|
||||
|
||||
Reference in new issue
Block a user