Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caching + supabase backend to snippet #67

Merged
merged 4 commits into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 59 additions & 24 deletions static/js/refinery.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,27 @@ refinery.RefineryAPI = function (config = {}) {

let defaultConfig = {
debugMode: false,
refineryHostUrl: "https://app.banditml.com/api/v2/",
refineryHostUrl: "https://utvcbgmjyqoututafrjy.supabase.co/rest/v1/rpc/",
supabasePublicApiKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYxODQxODU2NiwiZXhwIjoxOTMzOTk0NTY2fQ.uAM7wYbfMO8i8G5r-Mi7CFAS56ZEVicp4de6GJg_Gt0",
};
this.config = Object.assign(defaultConfig, config);

this.refineryLiveRefinementEndpoint = `${this.config.refineryHostUrl}changesets`;
this.refineryLiveRefinementEndpoint = `${this.config.refineryHostUrl}changes_by_app_token`;
};

refinery.RefineryAPI.prototype.setItemInStorage = function (key, obj) {
this.storage.setItem(key, JSON.stringify(obj));
};

refinery.RefineryAPI.prototype.getItemFromStorage = function (key) {
return JSON.parse(this.storage.getItem(key));
};

refinery.RefineryAPI.prototype.getAppTokenFromScriptTag = function () {
return document.currentScript.getAttribute('appToken');
};

refinery.RefineryAPI.prototype.asyncGetRequest = async function(
refinery.RefineryAPI.prototype.asyncGetRequest = async function (
url,
params = {},
headers = {}
Expand Down Expand Up @@ -47,52 +56,78 @@ refinery.RefineryAPI.prototype.asyncGetRequest = async function(
return await response.json();
};

refinery.RefineryAPI.prototype.getLiveRefinement = function() {
refinery.RefineryAPI.prototype.getLiveRefinement = function () {
const self = this;
const changesPromise = self.asyncGetRequest(
url = self.refineryLiveRefinementEndpoint,
url = `${self.refineryLiveRefinementEndpoint}?app_token=${self.refineryAppToken}`,
params = {},
headers = {
"x-app-token": `${self.refineryAppToken}`
"Content-Type": "application/json",
"apikey": `${self.config.supabasePublicApiKey}`,
"Authorization": `Bearer ${self.config.supabasePublicApiKey}`
}
);
return changesPromise.then(response => {
return response.results;
return response;
}).catch(e => {
console.error("Failed to fetch changes", e);
});
};

refinery.RefineryAPI.prototype.applyChange = function(change) {
refinery.RefineryAPI.prototype.applyChange = function (change) {
if (window.location.href === change.href) {
let elem = document.querySelector(change.domPath);
if (elem.innerHTML.trim() !== change.beforeHtml.trim()) {
console.warn(`Can't apply change on '${change.beforeHtml}' because copy doesn't match.\
'${change.beforeHtml.trim()}' vs. '${elem.innerHTML.trim()}'.`);
let elem = document.querySelector(change.dom_path);
if (elem.innerHTML.trim() !== change.before_html.trim()) {
console.warn(`Can't apply change on '${change.before_html}' because copy doesn't match.\
'${change.before_html.trim()}' vs. '${elem.innerHTML.trim()}'.`);
} else {
console.log(change.afterHtml);
elem.innerHTML = change.afterHtml;
elem.innerHTML = change.after_html;
return true;
}
} else {
console.warn(`
Can't apply change on '${change.beforeHtml}' because href's don't match.\
Can't apply change on '${change.before_html}' because href's don't match.\
'${change.href}' vs. '${window.location.href}'.`);
}
return false;
};

refinery.RefineryAPI.prototype.cacheChanges = function (changes) {
const self = this;
self.setItemInStorage(self.refineryAppToken, changes)
};

refinery.RefineryAPI.prototype.applyChanges = function() {
refinery.RefineryAPI.prototype.applyChangesFromCache = function () {
const self = this;
const changes = this.getItemFromStorage(self.refineryAppToken) || [];
const changesAppliedFromCache = new Set();
changes.filter(change => !change.deleted).forEach(change => {
const changeApplied = self.applyChange(change);
if (changeApplied) changesAppliedFromCache.add(change.id);
});
return changesAppliedFromCache;
};

refinery.RefineryAPI.prototype.applyChangesFromProd = function (changesAppliedFromCache) {
const self = this;
const changesToCache = [];
self.getLiveRefinement().then(response => {
// right now we assume 1 changeset only, so grab the first i.e. [0]
let changeset = response[0];
let changes;
(changeset.publish) ? changes = changeset.changes : changes = [];
let changes = response;
changes.filter(change => !change.deleted).forEach(change => {
self.applyChange(change);

if (!(changesAppliedFromCache.has(change.id))) {
// the change hasn't been applied from the cache so we must apply it
// this works as long as we don't allow edits to changes (we currently don't)
// once we do, then we will need to make a check if the change has changed -_-
self.applyChange(change);
}
Comment on lines +117 to +122
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we could always apply the change no matter what - if they match, nothing happens. simpler code and handles both scenarios

Copy link
Collaborator Author

@econti econti Apr 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would nothing happen though? I was trying to guard against a flicker (even if the same text will be filled, just the filling itself may cause a flicker, but not entirely sure)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, a very, very, very small benefit of doing it this way is that the prod changes will get applied faster.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would nothing happen though?
pretty sure, at no time does innerHTML = null.
will get applied faster.
not noticably.

its not a big deal if you dont think its worth it.

changesToCache.push(change);
});
self.cacheChanges(changesToCache);
});
}
};

let refineryAPI = new refinery.RefineryAPI();
refineryAPI.applyChanges();
document.addEventListener('DOMContentLoaded', (event) => {
const changesAppliedFromCache = refineryAPI.applyChangesFromCache();
refineryAPI.applyChangesFromProd(changesAppliedFromCache);
})
Comment on lines +130 to +133
Copy link
Collaborator Author

@econti econti Apr 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we can do something smarter here. Document.readyState seemed to be slower from some local tests.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea i think both would have to be done for any potential benefit, e.g.

const changesAppliedFromCache = refineryAPI.applyChangesFromCache()
if (document.readyState === "complete" || document.readyState === "interactive") {
  refineryAPI.applyChangesFromProd(changesAppliedFromCache)
} else {
  document.addEventListener("DOMContentLoaded", () => refineryAPI.applyChangesFromProd(changesAppliedFromCache))
}

might be slightly faster if user ends up putting our script at the end instead of beginning and document is already loaded? not sure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh smart! that's a good idea. i'll add it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, doesn't seem to work.

can't do this first because the DOM isn't ready yet:

const changesAppliedFromCache = refineryAPI.applyChangesFromCache()

and this doesn't apply any changes for some reason

let refineryAPI = new refinery.RefineryAPI();
if (document.readyState === "complete" || document.readyState === "interactive") {
  refineryAPI.applyChangesFromCache()
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh got it, nm!

2 changes: 1 addition & 1 deletion static/minified/refinery.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.