seanbirchall / scrapeable

Infecting the web with R
GNU Affero General Public License v3.0
3 stars 1 forks source link

shinylive problem #1

Open timelyportfolio opened 1 month ago

timelyportfolio commented 1 month ago

See https://github.com/ramnathv/htmlwidgets/issues/487 for reference and background. @jcheng5 @seanbirchall

Debugging shinylive

Since MessageChannel replaces traditional Shiny websocket (easily tracked in browser debugging tools) in shinylive/webR, debugging can be a little more difficult. I discovered

1) options(shiny.trace = TRUE) in the app will log to browser console which is helpful 2) manually hacking shinylive-inject-socket.txt around line[https://github.com/posit-dev/shinylive/blob/main/src/assets/shinylive-inject-socket.txt#L62] by adding if(event.value && event.value.data) {console.log(JSON.parse(event.value.data))} after shinylive::export() in shinylive-sw.js provides the full stream

Potential Partial Solution (at least locally for me in shinylive)

While this helps in my local testing, I want to stress that the issue still eventually will appear if viewer is not cleared after first render. Before I was not able to get any rendered df_viewer, but it sounds like in the original issue that df_viewer would appear.

Moving lines to the end of mod_df_viewer.R seems to partially resolve what appears to be an asynchrony issue that happens in the shinylive context. Oddly, when I added a line before the req (even a commented one) without changing the order of the code, then the problem also went away at least locally in a shinylive context.

Sean, if working correctly, you should be able to remove the manually added dependencies in lines.

Problem

The shiny::req(ide$environment_selected, input$index, input$rows, input$columns) in line leads to a null data message sent. Then htmlwidgets line fails since data.deps does not exist.

image

seanbirchall commented 1 month ago

This helps, was cluelessly debugging shinylive apps previously.

I just finished a partial workaround, but I lose the ability to persist widgets in the viewer. Should be able to add some additional reactiveValues to fix it though.

Combination of debouncing my renderdfViewer and setting ide$viewer to NULL whenever the user swaps between viewer and environment pane.

mod_df_viewer.R

      output$spreadsheet <- renderdfViewer({
        shiny::req(ide$environment_selected, input$index, input$rows, input$columns)
        df <- ide$environment_selected
        # Logic for start index
        start_index <- as.integer(input$index)
        if(is.na(start_index) | start_index < 1){
          start_index <- 1
        }else if (start_index > nrow(df)){
          start_index <- nrow(df)
        }

        # Logic for rows
        row_count <- as.integer(input$rows)
        if(row_count > 0){
          # View first n rows
          end_index <- min(start_index + row_count - 1, nrow(df))
          df <- df[start_index:end_index, , drop = FALSE]
        }else{
          # View last n rows
          start_index <- max(1, nrow(df) + row_count + 1)
          df <- df[start_index:nrow(df), , drop = FALSE]
        }

        # Logic for columns
        col_count <- as.integer(input$columns)
        if (col_count > 0) {
          # View first n columns
          df <- df[, 1:min(col_count, ncol(df)), drop = FALSE]
        } else {
          # View last n columns
          col_start <- max(1, ncol(df) + col_count + 1)
          df <- df[, col_start:ncol(df), drop = FALSE]
        }

        if(is.null(df)){
          df <- data.frame()
        }

        df_viewer(
          data = df,
          colHeaders = names(df),
          width = "100%"
        )
      }) |>
        debounce(1000)

control.R

      shiny::observeEvent(input$tab_environment, {
        ide$viewer <- NULL
        ide$tab_control <- "environment"
      })

So I should get a working solution that keeps the app functioning as I want so long as I flag for when the user is trying to view data using the df_viewer widget. In other instances when the user runs code with an htmlwidget (reactable, gt, ggplot, listviewer... small list for now). Another issue for another day to programmatically detect widgets, wrap them in their render function, and then rerun. Need to look more into how George is doing widgets in his R repl app, as the rerun might be unnecessary at this point.

Thanks again @timelyportfolio!