Tychobra / polished

Authentication and Administration for Shiny apps
https://polished.tech
Other
234 stars 36 forks source link

custom UI not rendering on shinyapps.io #108

Closed josho88 closed 3 years ago

josho88 commented 3 years ago

Hi

I've successfully deployed an app to shinyapps.io using the default UI provided. This works great. I followed your example docs here:

https://polished.tech/docs/get-started

However, when I tried to deploy using the custom UI example (https://polished.tech/docs/custom-branding) the sign in page doesn't render properly. All works as expected locally, but when the custom version is pushed to shinyapps.io none of the inputs appear in the modal (only see the sign in header). I wondered if some javascript dependencies aren't being loaded for some reason?

Thanks

merlinoa commented 3 years ago

Hi @josho88

It should work on shinyapps.io. Is the custom version working locally? Can you open your browser's JavaScript console and check for errors?

If you can provide your code I'd be happy to take a look.

josho88 commented 3 years ago

Hi @merlinoa

Ok so here is a basic app that i've just tried to deploy and i get the same error. I'm also using the recently added argument to bypass email verification. In the console i can see some elements set to display: none !important. For example:

library(shiny)
library(polished)

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),
        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot"),
           actionButton(
             "sign_out",
             "Sign Out",
             icon = icon("sign-out-alt"),
             class = "pull-right"
           )
        )
    )
)

# -------------------------
# custom sign in page
# -------------------------
my_custom_sign_in_page <- sign_in_ui_default(
  color = "#006CB5",
  company_name = "My Company"
)
# -------------------------
# secure UI
# -------------------------
# secure your UI behind your custom sign in page
ui <- polished::secure_ui(
  ui,
  sign_in_page_ui = my_custom_sign_in_page  
)

# Define server logic required to draw a histogram
server <- polished::secure_server(function(input, output, session) {

    observeEvent(input$sign_out, {
      sign_out_from_shiny()
      session$reload()
    })

    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
})

# run the app
shinyApp(ui = ui, server = server, onStart = function(){
  library(polished)

  # configure the global sessions when the app initially starts up.
  polished::global_sessions_config(
    app_name = "test",
    api_key = ****,
    is_email_verification_required = FALSE # overwrite email verification step
  )
})
merlinoa commented 3 years ago

Thanks @josho88 . This is an issue with the R6 object initialized by global_sessions_config() not being initialized until after the UI is defined. I had assumed that calling the global_sessions_config() from the shinyApp()'s onStart argument would work correctly, but it doesn't.

To avoid this issue, I recommend separating your Shiny app into 3 primary files:

e.g.:

# global.R

library(shiny)
library(polished)

# configure the global sessions when the app initially starts up.
polished::global_sessions_config(
   app_name = "test",
   api_key = ****,
   is_email_verification_required = FALSE # overwrite email verification step
)
# ui.R

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),
        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot"),
           actionButton(
             "sign_out",
             "Sign Out",
             icon = icon("sign-out-alt"),
             class = "pull-right"
           )
        )
    )
)

# -------------------------
# custom sign in page
# -------------------------
my_custom_sign_in_page <- sign_in_ui_default(
  color = "#006CB5",
  company_name = "My Company"
)
# -------------------------
# secure UI
# -------------------------
# secure your UI behind your custom sign in page
polished::secure_ui(
  ui,
  sign_in_page_ui = my_custom_sign_in_page  
)
# server.R

# Define server logic required to draw a histogram
polished::secure_server(function(input, output, session) {

    observeEvent(input$sign_out, {
      sign_out_from_shiny()
      session$reload()
    })

    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
})

I always set up my apps using the above 3 primary files, so this set up is much better tested than the single file shiny app set up. I am going to update the docs to no longer provide examples using single file shiny apps. Thanks for opening this issue. It is a bug in my example apps. Please let me know if switching to the 3 file setup solves the problem.

josho88 commented 3 years ago

Yes that setup has fixed it, thanks for looking into that. Looks like the following also works if we want to keep the UI and server functions in the same file.

# global.R

library(shiny)
library(polished)

# configure the global sessions when the app initially starts up.
polished::global_sessions_config(
    app_name = "test",
    api_key = ***,
    is_email_verification_required = FALSE
)
# app.R

source('global.R')

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Old Faithful Geyser Data"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
            sliderInput("bins",
                        "Number of bins:",
                        min = 1,
                        max = 50,
                        value = 30)
        ),
        # Show a plot of the generated distribution
        mainPanel(
           plotOutput("distPlot"),
           actionButton(
             "sign_out",
             "Sign Out",
             icon = icon("sign-out-alt"),
             class = "pull-right"
           )
        )
    )
)

# -------------------------
# custom sign in page
# -------------------------
my_custom_sign_in_page <- sign_in_ui_default(
  color = "#006CB5",
  company_name = "My Company"
)
# -------------------------
# secure UI
# -------------------------
# secure your UI behind your custom sign in page
ui <- polished::secure_ui(
  ui,
  sign_in_page_ui = my_custom_sign_in_page
)

# Define server logic required to draw a histogram
server <- polished::secure_server(function(input, output, session) {

    observeEvent(input$sign_out, {
      sign_out_from_shiny()
      session$reload()
    })

    output$distPlot <- renderPlot({
        # generate bins based on input$bins from ui.R
        x    <- faithful[, 2]
        bins <- seq(min(x), max(x), length.out = input$bins + 1)

        # draw the histogram with the specified number of bins
        hist(x, breaks = bins, col = 'darkgray', border = 'white')
    })
})

# run the app
shinyApp(ui = ui, server = server)
merlinoa commented 3 years ago

I think scoping/script execution is a little different with the "app.R" and sourcing a "global.R" script than when using global.R, server.R, and ui.R. i.e. with the 3 files, global.R only runs once when the app starts, but when sourcing global.R into app.R, I think it runs every time that a session starts. Both ways might work fine, but I'd recommend the 3 file setup with global.R, server.R, and app.R.