Keep state of the Rust Playgrounds when navigating between slides
Today, the embedded Playgrounds are reset when you navigate between slides. This has caused problems: if people navigate away from a slide to look something up, they've suddenly lost their work.
This is actually the reason why we don't make the exercises editable directly in the slides: too many people have lost work because of this.
We should be able to store the current state in the browser's local storage.
I would like to help with it.
I would like to help with it.
Hi @jnovikov, that would be awesome! I hope the idea is somewhat clear, otherwise please let me know :smile: The problem right now is that using Arrow-Left or Arrow-Right (or clicking links) navigates away from the current slide: when you navigate back, the playground is back at its initial state. The idea is to prevent this by storing the state in local storage.
I don't know much about the editor here, except that mdbook uses this one: https://ace.c9.io/.
Hey @mgeisler , I would like to give a try .If it Is still open.
It's all yours! I'm not sure anyone has in mind a good way to do this, so the first step is coming up with an idea..
It's all yours! I'm not sure anyone has in mind a good way to do this, so the first step is coming up with an idea..
I looked at it there is a div which as has some child html elements in them all our code lies as innerHTML/innerText.
I will store all children's of that parent div
in an array using Arraysfrom(document.getElementsByClassName('{that parent div class name}')[0].children).
Then I will make a object with the URL as key and array of html elements as value.
I will store that array of objects (Assuming user make changes in all playgrounds) in browser local storage.
Every time loads the page, I will check the object is that url present in that key.
If present then I will display object value code.
else I will start assigning that code to new key in the object.
Process of displaying code : Delete all children of parent div add all childrens in object one by one using loop.
That sounds like it will work -- try it out!
It's all yours! I'm not sure anyone has in mind a good way to do this, so the first step is coming up with an idea..
I looked at it there is a
divwhich as has some child html elements in them all our code lies as innerHTML/innerText.I will store all children's of that
parent divin an array usingArraysfrom(document.getElementsByClassName('{that parent div class name}')[0].children).Then I will make a object with the URL as key and array of html elements as value.
I will store that array of objects (Assuming user make changes in all playgrounds) in browser local storage.
Every time loads the page, I will check the object is that url present in that key.
If present then I will display object value code.
else I will start assigning that code to new key in the object.
Process of displaying code : Delete all children of parent div add all childrens in object one by one using loop.
May be few steps will change. I will come up with code tommarow.
It's all yours! I'm not sure anyone has in mind a good way to do this, so the first step is coming up with an idea..
I looked at it there is a
divwhich as has some child html elements in them all our code lies as innerHTML/innerText.I will store all children's of that
parent divin an array usingArraysfrom(document.getElementsByClassName('{that parent div class name}')[0].children).Then I will make a object with the URL as key and array of html elements as value.
I will store that array of objects (Assuming user make changes in all playgrounds) in browser local storage.
Every time loads the page, I will check the object is that url present in that key.
If present then I will display object value code.
else I will start assigning that code to new key in the object.
Process of displaying code : Delete all children of parent div add all childrens in object one by one using loop.
Is it okay to use indexedDb(Browser storage) to store data.
Because , I cannot store HTML tags in local storage.
I think either IndexdDB or LocalStorage is fine. I suspect it only needs to store text (the code in the playground) and not HTML, right?
I think either IndexdDB or LocalStorage is fine. I suspect it only needs to store text (the code in the playground) and not HTML, right?
Yes, now, I am storing text instead of html elements.
function setCodeToPlayground(){
const code = JSON.parse(localStorage.getItem(window.location.href));
console.log(code)
if(code){
const playground = document.getElementsByClassName('ace_text-layer')[0]
while (playground.lastElementChild) {
console.log(playground.lastElementChild.innerHTML.replace('<span>','^').replace('</span>',"^").replace(/\s+/g, '').split('^'))
playground.removeChild(playground.lastElementChild);
}
console.log(playground,"after removal")
for(let i = 0; i < code.length; i++){
let parentDiv = code[i][0]
let spanChild = code[i][1]
let div = document.createElement(parentDiv.tag)
div.style.height = "17.5938px"
div.style.top = `${17.5938*i}px`
for(let cls in parentDiv.classes){
div.classList.add(parentDiv.classes[cls])
}
for(let j = 0;j<spanChild.length;j++){
//console.log(spanChild[j].styles,typeof(spanChild[j].styles))
let span = document.createElement(spanChild[j].tag)
//span.classList = spanChild[j].classes
for(let cls in spanChild[j].classes){
span.classList.add(spanChild[j].classes[cls])
}
span.innerText = spanChild[j].text
div.insertBefore(span,div.lastChild)
}
playground.insertBefore(div,playground.lastChild)
}
}
localStorage.removeItem(window.location.href)
}
window.onunload = setTimeout(setCodeToPlayground,5000)
function getCodeFromPlayground() {
console.log("getCodeFromPlayground")
const playground = document.getElementsByClassName('ace_text-layer')[0].children
var code = []
for (let i = 0; i < playground.length; i++) {
let parentCodeList = {
tag : playground[i].tagName,
classes : playground[i].classList,
styles : playground[i].style
}
var line = []
for(let j = 0; j < playground[i].children.length; j++) {
console.log(playground[i].innerHTML)
let codeList = {
tag : playground[i].children[j].tagName,
text: playground[i].children[j].innerText,
classes : playground[i].children[j].classList,
styles : playground[i].children[j].style,
}
line.push(codeList)
}
code.push([parentCodeList,line])
}
console.log(code)
//localStorage.removeItem(window.location.href)
localStorage.setItem(window.location.href,JSON.stringify(code))
}
addEventListener('beforeunload',getCodeFromPlayground())
Need some help with this code . If any one can.
function setCodeToPlayground(){ const code = JSON.parse(localStorage.getItem(window.location.href)); console.log(code) if(code){ const playground = document.getElementsByClassName('ace_text-layer')[0] while (playground.lastElementChild) { console.log(playground.lastElementChild.innerHTML.replace('<span>','^').replace('</span>',"^").replace(/\s+/g, '').split('^')) playground.removeChild(playground.lastElementChild); } console.log(playground,"after removal") for(let i = 0; i < code.length; i++){ let parentDiv = code[i][0] let spanChild = code[i][1] let div = document.createElement(parentDiv.tag) div.style.height = "17.5938px" div.style.top = `${17.5938*i}px` for(let cls in parentDiv.classes){ div.classList.add(parentDiv.classes[cls]) } for(let j = 0;j<spanChild.length;j++){ //console.log(spanChild[j].styles,typeof(spanChild[j].styles)) let span = document.createElement(spanChild[j].tag) //span.classList = spanChild[j].classes for(let cls in spanChild[j].classes){ span.classList.add(spanChild[j].classes[cls]) } span.innerText = spanChild[j].text div.insertBefore(span,div.lastChild) } playground.insertBefore(div,playground.lastChild) } } localStorage.removeItem(window.location.href) } window.onunload = setTimeout(setCodeToPlayground,5000) function getCodeFromPlayground() { console.log("getCodeFromPlayground") const playground = document.getElementsByClassName('ace_text-layer')[0].children var code = [] for (let i = 0; i < playground.length; i++) { let parentCodeList = { tag : playground[i].tagName, classes : playground[i].classList, styles : playground[i].style } var line = [] for(let j = 0; j < playground[i].children.length; j++) { console.log(playground[i].innerHTML) let codeList = { tag : playground[i].children[j].tagName, text: playground[i].children[j].innerText, classes : playground[i].children[j].classList, styles : playground[i].children[j].style, } line.push(codeList) } code.push([parentCodeList,line]) } console.log(code) //localStorage.removeItem(window.location.href) localStorage.setItem(window.location.href,JSON.stringify(code)) } addEventListener('beforeunload',getCodeFromPlayground())Need some help with this code . If any one can.
Updated code.
Getting problem with word without span tag. every word as a specific span tag except variables and print keyword.Missing those words without span tag.
Can you push a draft PR that we could experiment with?
Sure on it.
It seems like the text editor widget (ace_text) should have some API for getting/setting text contents, rather than digging around in its internal markup.
From the docs, it looks like these suffice: editors[0].getValue();/editors[0].setValue("fn main(){\n\n}", -1);.
While teaching I rely on being able to reset the code samples to their original contents (both while discussing a slide and before teaching the course a second time), so I'd like to make sure there's still a straightforward way to do so before this lands.
(moving this conversation to the PR, #1917)
Hello @djmitche , still any thing needed to add to close this issue.
I don't think so!
Cc @djmitche, @fw-immunant, and @mani-chand. I merged this now so I can ensure the course works for the class I'm teaching in ~12 hours.
I only saw this after browsing around on the site for 30 minutes or so. I don't really know why it would happen, but I guess the event that saves the playgrounds is unreliable. I would suggest looking into saving the state on changes to the playgrounds instead of trying to detect navigating away from the pages.
I've seen problems with detecting the pagehide event in the past: the event is not reliably fired because it has been misused in the past.
I ran into another problem: clearing saved playground data doesn't fully work. Navigating away from the current page will save the playgrounds on the page again.
Originally posted by @mgeisler in https://github.com/google/comprehensive-rust/issues/1935#issuecomment-2016969529
LocalState is limited in the amount of storage a site is allowed. I think that's 5MB? That might be part of the issue.
Docs on pagehide agree that it's unreliable, but I think it's better than onunload?
on 2nd point.
saving the current page after reset of playground.
When you reset the playground it will clear all including the current page but when you leave the current page pagehide event triggers and saves the state even there is no change in the code.
Cc @djmitche, @fw-immunant, and @mani-chand. I merged this now so I can ensure the course works for the class I'm teaching in ~12 hours.
I only saw this after browsing around on the site for 30 minutes or so. I don't really know why it would happen, but I guess the event that saves the playgrounds is unreliable. I would suggest looking into saving the state on changes to the playgrounds instead of trying to detect navigating away from the pages.
I've seen problems with detecting the
pagehideevent in the past: the event is not reliably fired because it has been misused in the past.I ran into another problem: clearing saved playground data doesn't fully work. Navigating away from the current page will save the playgrounds on the page again.
Originally posted by @mgeisler in https://github.com/google/comprehensive-rust/issues/1935#issuecomment-2016969529
1st point
I think pagehide event doesn't trigger on closing the window directly.
Cc @djmitche, @fw-immunant, and @mani-chand. I merged this now so I can ensure the course works for the class I'm teaching in ~12 hours.
I only saw this after browsing around on the site for 30 minutes or so. I don't really know why it would happen, but I guess the event that saves the playgrounds is unreliable. I would suggest looking into saving the state on changes to the playgrounds instead of trying to detect navigating away from the pages.
I've seen problems with detecting the
pagehideevent in the past: the event is not reliably fired because it has been misused in the past.I ran into another problem: clearing saved playground data doesn't fully work. Navigating away from the current page will save the playgrounds on the page again.
Originally posted by @mgeisler in https://github.com/google/comprehensive-rust/issues/1935#issuecomment-2016969529
For onchange of innerText https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver. It looks good. What do you say? @djmitche @mgeisler
LocalState is limited in the amount of storage a site is allowed. I think that's 5MB? That might be part of the issue.
Docs on pagehide agree that it's unreliable, but I think it's better than
onunload?
I think all our codes doesn't consume even 3mb because they are only strings.
Yeah, my quic mental math also suggested we weren't using 5MB.
I think the ACE editor has its own events which would probably be better to listen to instead of the underlying DOM events. Will updating LocalStorage on every keypress be too slow?
Yeah, my quic mental math also suggested we weren't using 5MB.
I think the ACE editor has its own events which would probably be better to listen to instead of the underlying DOM events. Will updating LocalStorage on every keypress be too slow?
This is great. I think we can trust it.
Yeah, my quic mental math also suggested we weren't using 5MB.
I think the ACE editor has its own events which would probably be better to listen to instead of the underlying DOM events. Will updating LocalStorage on every keypress be too slow?
You really think my brain will know or understand quic 🤔. I went to Google for 🧐and understood what it is . My brain is still in completing bachelor of technology in computer science. Even I am doing an internship at a startup.
I ran into another problem: clearing saved playground data doesn't fully work. Navigating away from the current page will save the playgrounds on the page again.
I think this problem will also gets solved. If someone clears the playground and navigate to another page without changing playground code then it will won't save.
Sorry! I've been working on QUIC things for a few months now and my fingers type that instead of "quick". It is, indeed, kind of 🤯!