I'm creating an extension that is a sticky button on the browser and when clicked it loads the next article you've saved. These articles are stored in a firebase DB and are being fetched on page load.
I've added a pointer variable to index the array and storing the value of the pointer in local storage so I have it as the pages refresh. I'm able to subtract the value of the pointer correctly and when I try to load the next URL upon click for some reason it loads an entirely different URL.
the shape of fetched data:
data = [
{
book: "the matrix,
url: 'https://thisurl1.com
},
{
book: "the matrix 2,
url: 'https://thisurl2.com
},
{
book: "the matrix 3,
url: 'https://thisurl3.com
}
]
here's the code:
// check if local storage is available
const storageAvailable = (type) => {
}
// fetches articles from article endpoint => [data]
const fetchArticles = async () => {
try {
const response = await fetch("url_endpoint");
const data = await response.json();
articleStorage = Object.values(data);
localStorage.setItem("articles", JSON.stringify(articleStorage))
const pointer = Number(localStorage.getItem("pointer"));
if (pointer === null || pointer < 0 || pointer > articleStorage.length - 1) {
localStorage.setItem("pointer", articleStorage.length - 1);
}
return;
} catch (err) {
console.log(err);
}
};
// create the next button
const nextButton = () => {
// creating tags and buttons
// styling the button
// loads next article on click
button.addEventListener("click", loadNextArticle);
// appending to dom
};
// loads next article in array
const loadNextArticle = () => {
const pointer = Number(localStorage.getItem("pointer"));
const newPointer = pointer - 1;
const articleStorage = JSON.parse(localStorage.getItem('articles'));
if (pointer < 0 || pointer > articleStorage.length - 1) {
alert('nothing else to show');
} else {
localStorage.setItem('pointer', newPointer);
window.location.href = articleStorage[newPointer].link;
}
};
window.onload = () => {
if (storageAvailable('localStorage')) {
if (localStorage.getItem("articles") === null) fetchArticles();
nextButton();
} else {
console.log('local storage not available');
}
};
You never update pointer
console.log(pointer); // <-- read pointer
localStorage.setItem("pointer", pointer - 1); // <-- update local storage
if (pointer < 0 || pointer > articleStorage.length - 1) { // <-- still the same pointer value
You need to update the variable since it will not update on its own
pointer--; // update the variable
localStorage.setItem("pointer", pointer);
if (pointer < 0 || pointer > articleStorage.length - 1) {
Related
I'm trying to create a chrome extension, but I am having some trouble updating my DB.
In the code below I am using index.get to the the object that contains a certain value. If such an object doesn't exist I will create a new one, which works just fine.
But if the DB contains an object with the specified value, I want to append a new object to an array (allMessages) that is inside the object I searched for. The details doesn't really matter in this case.
What is important is to find out if the way I'm adding this new obj to the array (allMessages) is a valid way of updating the database.
records.forEach((person) => {
console.log("here1");
const index = objectStore.index("urlKeyValue");
let search = index.get(person.urlKeyValue);
search.onsuccess = function (event) {
if (search.result === undefined) {
// no record with that key
let request = objectStore.add(person);
request.onsuccess = function () {
console.log("Added: ", person);
};
} else {
// here I'm iterating an array that is inside the obj I searched for,
// and then checking if the key for that array matches **theUserId**
for (userObj of event.target.result.allMessages) {
if (theUserId == Object.keys(userObj)) {
// is this part correct. Is it possible to update the DB this way?
let objToAdd1 = {
time: person.allMessages[0][theUserId][0].time,
msg: person.allMessages[0][theUserId][0].msg,
};
let currentObj = userObj[theUserId];
let updatedObj = currentObj.push(objToAdd1);
}
}
)}
Using objectStore.openCursor you can update only part of the record.
The following updates only book prices.
const transaction = db.transaction("books", "readwrite");
const objectStore = transaction.objectStore("books");
records = [{ id: "kimetu", price: 600 }];
records.forEach((book) => {
const index = objectStore.index("id");
const search = index.get(book.id);
search.onsuccess = () => {
if (search.result === undefined) {
const request = objectStore.add(book);
request.onsuccess = () => {
console.log("Added: ", book);
};
} else {
const request = objectStore.openCursor(IDBKeyRange.only(book.id));
request.onsuccess = () => {
const cursor = request.result;
if (cursor) {
cursor.value.price = 1000;
const updateRequest = cursor.update(cursor.value);
updateRequest.onsuccess = () => {
console.log("Updated: ", cursor.value.price);
};
cursor.continue();
}
};
}
}
});
I am using chai and javascript to run some automated tests against API endpoints.
In one of my tests, I am pushing results into an array (shown below, the array is named acceptanceCriteriaHashes).
If I put a breakpoint in the before code then I can see the array, and all the elements within the array, are fully populated with the object returned from the db call.
However when I actually come to verify the contents of the array later in the test, the array, although it has the correct number of elements, contains only undefined?
Have tried to push directly with the results of the db call and that doesn't work either?
const getAcceptanceCriteria = (testCase) => {
let getAcceptanceCriteriaResponse;
let myLicenceChecksPassed = false;
const acceptanceCriteriaHashes = new Array();
describeTest(`${testCase.testCaseId} - ${testCase.testDescription}`, async () => {
before(async () => {
if (testCase.myLicenceChecksPassed !== undefined) {
myLicenceChecksPassed = testCase.myLicenceChecksPassed;
}
getAcceptanceCriteriaResponse = await getAcceptanceCriteriaAPICall(testCase.productId, testCase.assetType, testCase.underwriter, myLicenceChecksPassed);
if (getAcceptanceCriteriaResponse != undefined && getAcceptanceCriteriaResponse.body.Detail.length > 0) {
for (let i = 0; i < getAcceptanceCriteriaResponse.body.Detail.length; i++) {
// eslint-disable-next-line prefer-const
let response = await readAcceptanceCriteriaHashes(getAcceptanceCriteriaResponse.body.Detail[i].ContentHash);
if (response != undefined) {
let record = { Hash: response.Hash, Criteria: response.Criteria };
acceptanceCriteriaHashes.push(record);
}
}
console.log(acceptanceCriteriaHashes);
}
});
// other tests successfully run here
it(`${testCase.testCaseId} - Record for contentHash exists in the AcceptanceCriteriaHashes table`, async () => {
expect(acceptanceCriteriaHashes).to.not.be.empty;
expect(getAcceptanceCriteriaResponse.body.Detail.length).to.eql(acceptanceCriteriaHashes.length);
for (let i = 0; i < acceptanceCriteriaHashes.length; i++) {
expect(acceptanceCriteriaHashes[i].to.not.be('undefined', `Returned undefined for ContentHash: ${getAcceptanceCriteriaResponse.body.Detail[i].ContentHash}`));
expect(acceptanceCriteriaHashes[i].Criteria.to.not.be.empty);
}
});
});
};
The issue was I needed to declare the record variable outside of the before code block otherwise, when we exited the before function, it ceased to exist by the time I came to query it in the subsequent test..
Declaration goes just before the before function:
const getAcceptanceCriteria = (testCase) => {
let getAcceptanceCriteriaResponse;
let myLicenceChecksPassed = false;
// eslint-disable-next-line prefer-const
// eslint-disable-next-line no-array-constructor
const acceptanceCriteriaHashes = new Array();
let record;
describeTest(`${testCase.testCaseId} - ${testCase.testDescription}`, async () => {
before(async () => {
if (testCase.myLicenceChecksPassed !== undefined) {
myLicenceChecksPassed = testCase.myLicenceChecksPassed;
}
getAcceptanceCriteriaResponse = await getAcceptanceCriteriaAPICall(testCase.productId, testCase.assetType, testCase.underwriter, myLicenceChecksPassed);
if (getAcceptanceCriteriaResponse != undefined && getAcceptanceCriteriaResponse.body.Detail.length > 0) {
for (let i = 0; i < getAcceptanceCriteriaResponse.body.Detail.length; i++) {
// eslint-disable-next-line prefer-const
let response = await readAcceptanceCriteriaHashes(getAcceptanceCriteriaResponse.body.Detail[i].ContentHash);
if (response != undefined) {
record = { Hash: response.Hash, Criteria: response.Criteria };
acceptanceCriteriaHashes.push(record);
}
}
console.log(acceptanceCriteriaHashes);
}
});
the parent component makes a call to the api and pushes data to the sharedData service and subscribes to it. So everytime a change has been made to the date set its automatically updated in the parent component.
ngOnInit() {
this.initialise();
}
initialise() {
this.dataShareService.setDataFilter(null);
this.loading = true;
this.assetService.table_data().subscribe(res => {
this.dataShareService.setEnviromentData(res);
this.dataShareService.enviromeData.subscribe(r => {
this.loading = false;
this.data = r;
let i = 0;
console.log('just a ocunt', i++)
this.searchValue = r['results'].map((val: any) => {
return val.name;
});
this.totalItems = this.data['results'].length;
this.loading = false;
}, err => {
console.log(err);
});
}
this data is then passed down to the child component - which is the data table
<data-table
*ngIf="!loading"
class="flex_display_centre"
[showTable]="showTable"
[columns]="columns"
[limit]="limit"
[data]="data">
</data-table>
I am the onScroll function to to get the why coordinates.
<ngx-datatable
[scrollbarV]="true"
[loadingIndicator]="isLoading"
[limit]="limit"
(scroll)='onScroll($event.offsetY)'>
</ngx-datatable>
onScroll(offsetY: number) {
this.isLoading = true;
let numberViewRows = 12;
const viewHeight = this.el.nativeElement.getBoundingClientRect().height - this.headerHeight;
if (!this.isLoading && offsetY >= this.rowHeight * numberViewRows ) {
this.loadPageData.emit(this.data['next']);
this.isLoading = false;
}
}
Once the condition is true an emitter is sent to the parent component to get the next page/data
nextPage(pageNum: any) {
this.isLoading = true;
this.assetService.reloadDataTable(pageNum).subscribe(results => {
this.dataShareService.setEnviromentData(results);
this.isLoading = false;
}, err => {
console.log('err', err)
});
}
data share service:
this first time the service get data it assigns the to previousEnviromentData variable. everytime the api is called the next/prev and count is updated and the results array is appended to the previous data
private enviromeInfoData = new BehaviorSubject([{}]);
enviromeData = this.enviromeInfoData.asObservable();
private previousEnviromentData;
setEnviromentData(data: any) {
if (data['results'] !== this.previousEnviromentData && this.previousEnviromentData !== undefined) {
this.previousEnviromentData["count"] = data.count,
this.previousEnviromentData["next"] = data.next,
this.previousEnviromentData["page_count"] = data.page_count,
this.previousEnviromentData["previous"] = data.previous,
this.previousEnviromentData['results'].push(...data['results']);
this.enviromeInfoData.next(this.previousEnviromentData);
} else {
this.previousEnviromentData = data;
this.enviromeInfoData.next(data);
}
}
However when I scroll to the bottom of the table the data is updated in the parent components and data share servuce but never updates in the table.
What am I doing wrong/missing?
I am trying to build a profile scroller-type app, and I am currently stuck on how I'm supposed to go back to the previous profile. See the code below:
let profiles = profileIterator(profiles); // array of objects stored in a separate .js file
// Next Event
next.addEventListener("click", nextProfile);
// Next Profile Display
function nextProfile() {
const currentProfile = profiles.next().value;
if(currentProfile !== undefined) {
name.innerHTML = `<h4>${currentProfile.name}</h4>`; // content on HTML page that will be changed
text.innerHTML = `<p>${currentProfile.info}</p>`
img.innerHTML = `<img src="${currentProfile.img}">`
next.innerHTML = 'Next';
previous.style.display = "inline-block";
previous.innerHTML = 'PREVIOUS';
} else {
window.location.reload();
}
}
function previousProfile() {
const currentProfile = profiles.previous().value;
if(currentProfile !== undefined) {
name.innerHTML = `<h4>${currentProfile.name}</h4>`;
text.innerHTML = `<p>${currentProfile.info}</p>`
img.innerHTML = `<img src="${currentProfile.img}">`
} else {
window.location.reload();
}
}
// Profile Iterator
function profileIterator(profiles) {
let nextIndex = 0;
return {
next: function() {
return nextIndex < profiles.length
? { value: profiles[nextIndex++], done: false }
: { done: true }
},
previous: function() {
return nextIndex < profiles.length
? { value: profiles[nextIndex--], done: false }
: { done: true}
}
};
}
It seemed pretty straightforward to me at first, but apparently, I am wrong as whenever I click the Previous button, instead of going through the previous profiles, it instead jumbles them up in no particular order, missing one of them entirely as well.
Any advice? Is there a way to set an HTML5 video element's attributes from Batch?
are you sure that when returning .previous() you should check if nextIndex < profiles.length? For my first glance, there is no way for that to be false and maybe it's the source of your problems.
I'm using Firebase cloud functions in my app to count likes of users.
I have node of likes that when user like a video it saves his ID and a boolean parameter (true).
Here example
On the Firebase cloud functions i listen to that node, when new like added it count the likes.
as you can see "likes:3".
Cloud function code - update the counter
exports.countlikechange = functions.database.ref('/likes/{postid}/{userUID}').onWrite(event => {
const collectionRef = event.data.ref.parent;
console.log(collectionRef);
const countRef = collectionRef.child('likes');
// Return the promise from countRef.transaction() so our function
// waits for this async event to complete before it exits.
return countRef.transaction(current => {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
}
else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
}).then(() => {
console.log('Counter updated.');
});
});
That method listens to the "likes" node and when a child added it trigger that method and update the "likes:.." on each videoID.
What im trying to do is first i want to update the counter in other node
On that node i also want to update the counter.
My problem is that i dont know how to get the reference to that node.
On the "HipHop" node, videos are saved, each video saved under his ID.
How can i reference from the cloud functions to that node and update the "likes"??
EDIT
Also how can i retrive the data from the node that i'm listening.
for example im listening to the "likes" node, i want to retrive the data that just update in that node.
You could write something like this:
exports.countlikechange = functions.database.ref('/likes/{postid}/{userUID}').onWrite(event => {
const collectionRef = event.data.ref.parent;
const countRef = collectionRef.child('likes');
const promises = [];
if (event.data.exists() && !event.data.previous.exists()) {
const promisseadd1 = countRef.transaction(current => {
return (current || 0) + 1;
});
const promisseadd2 = admin.database().ref(`/enter/here/new/path/likes`).transaction(current => {
return (current || 0) + 1;
});
return Promise.all([promisseadd1, promisseadd2]);
} else if (!event.data.exists() && event.data.previous.exists()) {
const promissesubs1 = countRef.transaction(current => {
return (current || 0) - 1;
});
const promissesubs2 = admin.database().ref(`/enter/here/new/path/likes`).transaction(current => {
return (current || 0) - 1;
});
return Promise.all([promissesubs1, promissesubs2]);
}
});