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

Storing state of the app as a bookmark #659

Closed
wants to merge 11 commits into from
Closed

Storing state of the app as a bookmark #659

wants to merge 11 commits into from

Conversation

usr110
Copy link
Member

@usr110 usr110 commented Mar 28, 2018

Note this is work in progress
This pertains to the issue I created yesterday - see #658
Allows user to save the state of the app on the server.

I've added a bookmark button in the input panel. Please see the screenshot below:

image

This button opens up a pop-up window with a URL to copy.

This might not work with the older version of shiny, so you may update it to the latest version (I am using shiny_1.0.5)

In terms of shortcomings:

  • It doesn't not save the state properly when:
    • Region changes
    • Map's bounding box (so doesn't capture any localized lines)

Would appreciate your comments @nikolai-b and @Robinlovelace

@usr110
Copy link
Member Author

usr110 commented Mar 29, 2018

sorted Region changes in 9494e03

@usr110
Copy link
Member Author

usr110 commented Mar 29, 2018

sorted Map's bounding box (so doesn't capture any localized lines) in 6da322f

@usr110 usr110 requested a review from nikolai-b March 29, 2018 15:35
@Robinlovelace
Copy link
Member

Robinlovelace commented Apr 3, 2018

This looks like great progress Ali, thanks. @nikolai-b let us know if this works on the test server so we can play with getting bookmarks for a range of locations / settings.

Copy link
Contributor

@nikolai-b nikolai-b left a comment

Choose a reason for hiding this comment

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

Looks good, a few questions that are mainly style but overall seems pretty clean. Nice work!

} else {
"isle-of-wight"

if(!is.null(region$state_region) && !is.na(region$state_region)){
Copy link
Contributor

Choose a reason for hiding this comment

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

Why we need to do both na and null checks? I get confused which to use in R...

Copy link
Member Author

Choose a reason for hiding this comment

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

null when it's not initialized - ran before the initialization of the region variable, and na when there is no saved state.

Copy link
Contributor

Choose a reason for hiding this comment

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

what about

if (is.character(region$state_region)){
  region$current <- region$state_region
}


region$state_lng <<- state$input$map_center$lng
region$state_lat <<- state$input$map_center$lat
region$state_mzoom <<- state$input$map_zoom
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need <<-? I know we use them but we need to stop.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. No, I don't need it. Have now removed it. At some point, we should rid of all of them.

lng = region$state_lng,
lat = region$state_lat,
zoom = region$state_mzoom
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be done in onRestore?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it can't be done there. onRestore runs before any observe block is executed - since leaflet variable hasn't been initialized, this can't work.
I now have moved this block to the onRestored function which is run after all observe blocks are executed.

@nikolai-b nikolai-b mentioned this pull request Apr 10, 2018
@usr110
Copy link
Member Author

usr110 commented Apr 11, 2018

Thanks @nikolai-b for your feedback - have now replied to your comments.

Copy link
Contributor

@nikolai-b nikolai-b left a comment

Choose a reason for hiding this comment

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

After having another think sadly we need to use the url store unless you can think of another way of doing it.

Sorry I was slow at replying.

} else {
"isle-of-wight"

if(!is.null(region$state_region) && !is.na(region$state_region)){
Copy link
Contributor

Choose a reason for hiding this comment

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

what about

if (is.character(region$state_region)){
  region$current <- region$state_region
}

@@ -22,6 +22,9 @@
## Functions
source("pct_shiny_funs.R", local = T)

## Enable bookmarking at the server side
enableBookmarking(store = "server")
Copy link
Contributor

Choose a reason for hiding this comment

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

Right, sadly this won't work. We need to store the state in the URL. For one we have multiple servers so if you share a link from the London server and the request is handled by the Cambridge server it will fail. Also when we deploy we are likely to wipe these.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a fair point. The only big problem is (apart from a messy URL for a specific states), the URL keeps changing when someone uses the app and they are not even interested to save or restore the app's state. On the bright side, it's easier to manage, as the servers don't need to remember anything.
I still believe that server-side bookmarking is much neater. I can think of two solutions here:

  1. We don't care about app's state not saved on a specific server
  2. For the two servers, we routinely mirror their saved states. This requires writing a routine which runs periodically to manage.

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure what you mean with

the URL keeps changing when someone uses the app

I've switched to store = "url" and in my browser the URL doesn't change when I'm moving around. Does it in yours?

Does solution (1) mean we provide a bookmark system that works 50% of the time? Not sure I like that.

Solution (2) is possible but I've not got budgeted time to do it, also I think it is likely to be a hacky fix. Even per server we have multiple instances of R running which could well interact in surprising ways.

Server side results in a neater URL but a much messier code.

I've pushed fdd151b as a WIP but really I think it might be easier with the long messy URL sadly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Nice work @nikolai-b

I've switched to store = "url" and in my browser the URL doesn't change when I'm moving around. Does it in yours?

No, it doesn't. Sorry for the confusion. It keeps changing in another app, so I thought it will behave the same here.

You've convinced me to use the url bookmarking, so thanks (as long as we can provide a tiny URL). However I am unable to test your code in fdd151b

When I debug the state variable used in the function:

oldEncodeShinySaveState <- getFromNamespace("encodeShinySaveState", "shiny")
shortEncodeShinySaveState <- function(state){
res <- oldEncodeShinySaveState(state)
r <- httr::POST(
"https://www.googleapis.com/urlshortener/v1/url?key=AIzaSyBLGLnQojQm2WQu9Urri2mmVTQolrLuJBc",
body = list(longUrl = paste0("http://www.pct.bike/?", res)), encode = "json"
)
return(httr::content(r)$id)
}
assignInNamespace("encodeShinySaveState", shortEncodeShinySaveState, ns="shiny")

I see that it's NULL - see below:

image

Do I need to test it on the shiny-server?

# (including zoom from the saved state of the app)
onRestored(function(state) {

if(!is.null(region$state_region) && !is.na(region$state_region)){
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this is needed. For onRestored to be called then the state_region must be set, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, Nikolai. Have removed these checks.

shortEncodeShinySaveState <- function(state){
res <- oldEncodeShinySaveState(state)
r <- httr::POST(
"https://www.googleapis.com/urlshortener/v1/url?key=AIzaSyBLGLnQojQm2WQu9Urri2mmVTQolrLuJBc",
Copy link
Member

Choose a reason for hiding this comment

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

I generally suggest saving API keys in the .Renviron file, as described here: https://csgillespie.github.io/efficientR/set-up.html#renviron in this case. Is there no issue with rate limits? Maybe no issue sharing this with the world in that case... Another compromise, in this case between the + of short urls and the + of reducing dependencies. In this case I'd lean slightly towards reducing dependencies although can understand the desire to keep URLs short.

Copy link
Member

Choose a reason for hiding this comment

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

Nudge on this @nikolai-b - suggest not publicising the key in the code.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for spotting @Robinlovelace ! Very late response sorry I just saw this. You can see the commit message is

Dummy attempt at keeping URL short

Appears wrong on link, exposes our API key

This API key was locked down to only work from pct.bike and I disabled a few days after posting it here (was just so others could try it) but it just shows what we could do...
Since then the whole goo shortner has been deprecated so to be completely clear this is not the approach I'd suggest it was just to show it was possible to hack the URL to make it shorter, I think using assignInNamespace is probably a bad idea!

Copy link
Member

Choose a reason for hiding this comment

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

Never come across assignInMyNamespace. The standard way for users to handle API keys now is:

usethis::edit_r_environ()
# add new line like KEY=XYZ
# Restart R
Sys.getenv("KEY")

Would that approach work on the server (if we ever did want to use API keys)?

Late response to a late response!

@Robinlovelace
Copy link
Member

Any update on the status of this @usr110 and @nikolai-b ? Not a priority atm so I would suggest we close this and look to re-open after the schools layer (and maybe near market) features are added.

@Robinlovelace
Copy link
Member

Closing for now as it's gone stale.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants