Skip to contents

Overview

This guide covers the complete workflow for integrating modBrowse into a Shiny application, customizing its behavior, and extending its functionality. It assumes familiarity with Shiny modules and the Artalytics package ecosystem.


Integration Workflow

Step 1: Environment Setup

Before using modBrowse, ensure all required environment variables are set:

# Check database configuration
Sys.getenv("ART_PGHOST")
Sys.getenv("ART_PGPORT")
Sys.getenv("ART_PGUSER")
Sys.getenv("ART_PGPASS")

# Check CDN configuration
Sys.getenv("ART_BUCKETS_KEY_ID")
Sys.getenv("ART_BUCKETS_KEY_SECRET")

Missing variables will cause {artcore} connection failures at runtime.

Step 2: Initialize Reactive Context

The browse module requires a reactive context with artist data:

server <- function(input, output, session) {
  # Create reactive values container
  r <- reactiveValues(
    artist = NULL, # Will be set by artist selector
    artwork = NULL, # Updated by browse module on card click
    appdata = NULL # Populated by observer below
  )

  # Artist selection (from parent app logic)
  observeEvent(input$artist_selector, {
    r$artist <- input$artist_selector
  })

  # Load artist data when artist changes
  observe({
    req(r$artist)
    r$appdata <- artutils::get_appdata(r$artist, r$artwork)
  })

  # Initialize browse module
  modBrowseServer("browse", r)
}

Step 3: Wire Up Artwork Selection

When a user clicks an artwork card, modBrowse updates r$artwork. Use this to navigate to a detail view:

server <- function(input, output, session) {
  r <- reactiveValues(...)

  # React to artwork selection
  observeEvent(r$artwork, {
    req(r$artwork)

    # Option 1: Navigate to gallery module
    showModal(modalDialog(
      modGalleryUI("gallery"),
      size = "xl"
    ))
    modGalleryServer("gallery", r)

    # Option 2: Update URL for deep linking
    updateQueryString(paste0("?artist=", r$artist, "&artwork=", r$artwork))

    # Option 3: Switch tabs
    updateTabsetPanel(session, "main_tabs", selected = "gallery")
  })

  modBrowseServer("browse", r)
}

Filtering and Sorting

Available Filters

modBrowse provides four filter criteria:

Filter Column Description
NFT Backed Art is_nft Artworks with blockchain registration
Prints Available is_print Artworks with print sales enabled
Listed For Sale is_listed Artworks currently for sale
Variants Available has_variants Artworks with multiple versions (n_variants > 1)

Filters are applied as AND conditions - selecting multiple filters shows only artworks matching ALL selected criteria.

Sort Options

Sort Key Column Description
Creation Date created_utc When artwork was completed
Total Brush Strokes brush_strokes Number of strokes in artwork
Total Drawing Time drawing_hours Hours spent creating artwork

Sort direction toggles between ascending and descending with the arrow button.

Desktop vs Mobile Sync

Desktop uses checkbox buttons; mobile uses a multi-select dropdown. These are synchronized bidirectionally using a syncing flag to prevent infinite loops. See app-server.R lines 321-403 for implementation.


Customizing Cards

Using artDisplayCard Directly

For custom browse layouts, use artDisplayCard() directly:

library(modBrowse)

# In a render function
output$custom_grid <- renderUI({
  DT <- artwork_data()

  cards <- lapply(seq_len(nrow(DT)), function(i) {
    artDisplayCard(
      ns = session$ns,
      card_id = paste0("card_", i),
      art_title = DT[i, art_title],
      thumb_src = artutils::path_artwork_thumb(
        artist = DT[i, artist_uuid],
        artwork = DT[i, art_uuid]
      ),
      is_new = DT[i, is_recent],
      has_nft = DT[i, is_nft],
      n_variants = DT[i, n_variants]
    )
  })

  div(class = "row g-4", cards)
})

Card Click Handler

Cards trigger input$card_clicked_ with the numeric suffix from card_id:

observeEvent(input$card_clicked_, {
  card_num <- input$card_clicked_
  selected_uuid <- artwork_data()[card_num, art_uuid]
  # Handle selection...
})

Statistics Display

Enhanced Stats Boxes

Use enhancedStatsUI() / enhancedStatsServer() for custom statistics:

# UI
fluidRow(
  enhancedStatsUI(ns("metric1"),
    label = "Custom Metric",
    icon = "star",
    color = "primary",
    width = 4,
    tooltip = "Description of this metric"
  ),
  enhancedStatsUI(ns("metric2"),
    label = "Another Metric",
    icon = "chart-line",
    color = "secondary",
    width = 4
  )
)

# Server
enhancedStatsServer("metric1", reactive({
  scales::label_comma()(compute_metric_1())
}))

enhancedStatsServer("metric2", reactive({
  paste0(compute_percent(), "%")
}))

Collection Info Module

For a dedicated collection summary panel:

# UI
collectionInfoUI("collection_summary")

# Server
r_collect_name <- reactive({
  names(r$appdata$artist$collections)[input$collection_idx]
})
collectionInfoServer("collection_summary", r_collect_name, r)

Theming

Standalone Theme

When running run_app(), modBrowse applies its own theme via modBrowse_theme():

# View theme settings
modBrowse_theme()
# Returns bslib theme with:
# - Primary: #6366f1 (purple)
# - Secondary: #f59e0b (gold)
# - Rounded corners, subtle shadows

Parent App Integration

When embedded in appPlatform, the parent’s theme takes precedence. modBrowse CSS uses CSS variables (var(--bs-primary), etc.) for theme compatibility.

Custom CSS

Override styles by adding CSS after including modBrowse:

ui <- page_fluid(
  modBrowseUI("browse"),
  tags$style(HTML("
    .artwork-card { border-radius: 1rem; }
    .enhanced-stats-box { background: linear-gradient(...); }
  "))
)

Testing

Unit Tests

Run the test suite:

devtools::test()

Tests cover module structure and function signatures. Integration tests with mock reactive context are in tests/testthat/.

Manual Testing

Launch standalone app:

modBrowse::run_app(artist = "746b8207-72f5-4ab6-8d19-a91d03daec3d")

Verify: - Collection selector populates with collections - Statistics update when collection changes - Filters correctly narrow artwork grid - Sort toggles work in both directions - Cards are clickable (check console for input$card_clicked_ values) - Dark mode toggle functions

Package Checks

Before committing:

devtools::document()  # Regenerate docs
devtools::check()     # Target: 0 errors, 0 warnings, 0 notes
lintr::lint_package() # Code style

Troubleshooting

Common Issues

“Cannot connect to database” - Check ART_PG* environment variables - Verify network access to database host

Cards show placeholder image - CDN credentials may be missing (ART_BUCKETS_*) - Artwork thumbnails may not exist yet

Statistics show em-dashes (—) - Collection may be empty - artDT columns may have unexpected NULL/NA values

Filters not applying - Check that filter columns exist in artDT - Verify column types are logical

Debug Mode

Enable Shiny debugging:

options(shiny.reactlog = TRUE)
shiny::reactlogShow()  # After running app

Next Steps