Display spinner animation during loading

This commit is contained in:
Zaid Abdulla committed 2021-02-16 00:09:24 -08:00
1 parent 4871613c67
commit 688811317c
23 files changed
+447 -307

No files matched your search

+141 -171
View File
@@ -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[]