Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## New features

* Added support for bookmarking Shiny applications. Bookmarking allows users to save the current state of an application and return to it later. This feature is available in both Shiny Core and Shiny Express. (#1870, #1915, #1919, #1920, #1922, #1934, #1938, #1945)
* Added support for bookmarking Shiny applications. Bookmarking allows users to save the current state of an application and return to it later. This feature is available in both Shiny Core and Shiny Express. (#1870, #1915, #1919, #1920, #1922, #1934, #1938, #1945, #1955)
* To enable bookmarking in Express mode, set `shiny.express.app_opts(bookmark_store=)` during the app's initial construction.
* To enable bookmarking in Core mode, set `shiny.App(bookmark_store=)` when constructing the `app` object.

Expand Down
10 changes: 5 additions & 5 deletions shiny/_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from ._error import ErrorMiddleware
from ._shinyenv import is_pyodide
from ._utils import guess_mime_type, is_async_callable, sort_keys_length
from .bookmark import _global as bookmark_global_state
from .bookmark._global import as_bookmark_dir_fn
from .bookmark._restore_state import RestoreContext, restore_context
from .bookmark._types import (
Expand All @@ -42,6 +41,7 @@
from .html_dependencies import jquery_deps, require_deps, shiny_deps
from .http_staticfiles import FileResponse, StaticFiles
from .session._session import AppSession, Inputs, Outputs, Session, session_context
from .types import MISSING, MISSING_TYPE

T = TypeVar("T")

Expand Down Expand Up @@ -115,8 +115,8 @@ def server(input: Inputs, output: Outputs, session: Session):
ui: RenderedHTML | Callable[[Request], Tag | TagList]
server: Callable[[Inputs, Outputs, Session], None]

_bookmark_save_dir_fn: BookmarkSaveDirFn | None
_bookmark_restore_dir_fn: BookmarkRestoreDirFn | None
_bookmark_save_dir_fn: BookmarkSaveDirFn | None | MISSING_TYPE
_bookmark_restore_dir_fn: BookmarkRestoreDirFn | None | MISSING_TYPE
_bookmark_store: BookmarkStore

def __init__(
Expand Down Expand Up @@ -498,8 +498,8 @@ def _render_page_from_file(self, file: Path, lib_prefix: str) -> RenderedHTML:
# ==========================================================================

def _init_bookmarking(self, *, bookmark_store: BookmarkStore, ui: Any) -> None:
self._bookmark_save_dir_fn = bookmark_global_state.bookmark_save_dir
self._bookmark_restore_dir_fn = bookmark_global_state.bookmark_restore_dir
self._bookmark_save_dir_fn = MISSING
self._bookmark_restore_dir_fn = MISSING
self._bookmark_store = bookmark_store

if bookmark_store != "disable" and not callable(ui):
Expand Down
38 changes: 31 additions & 7 deletions shiny/bookmark/_global.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,44 @@
from typing import overload

from .._utils import wrap_async
from ..types import MISSING_TYPE
from ._types import (
BookmarkDirFn,
BookmarkDirFnAsync,
BookmarkRestoreDirFn,
BookmarkSaveDirFn,
)

# WARNING! This file contains global state!
# WARNING! This file contains global default values!


# During App initialization, the save_dir and restore_dir functions are conventionally set
# to read-only on the App.
# If nothing is set on the `app` object, the global default bookmark functions are found and used during every save/restore.
_default_bookmark_save_dir_fn: BookmarkSaveDirFn | None = None
_default_bookmark_restore_dir_fn: BookmarkRestoreDirFn | None = None


def get_bookmark_save_dir_fn(
save_dir_fn: BookmarkSaveDirFn | None | MISSING_TYPE,
) -> BookmarkSaveDirFn | None:
if isinstance(save_dir_fn, MISSING_TYPE):
# Allow for default bookmark function to be utilized after app initialization.
# Sometimes the app is created before hooks are registered.
return _default_bookmark_save_dir_fn
else:
return save_dir_fn


bookmark_save_dir: BookmarkSaveDirFn | None = None
bookmark_restore_dir: BookmarkRestoreDirFn | None = None
def get_bookmark_restore_dir_fn(
restore_dir_fn: BookmarkRestoreDirFn | None | MISSING_TYPE,
) -> BookmarkRestoreDirFn | None:
if isinstance(restore_dir_fn, MISSING_TYPE):
# Allow for default bookmark function to be utilized after app initialization.
# Sometimes the app is created before hooks are registered.
return _default_bookmark_restore_dir_fn
else:
return restore_dir_fn


@overload
Expand Down Expand Up @@ -74,9 +98,9 @@ def restore_bookmark_dir(id: str) -> Path:
--------
* `~shiny.bookmark.set_global_restore_dir_fn` : Set the global bookmark restore directory function
"""
global bookmark_save_dir
global _default_bookmark_save_dir_fn

bookmark_save_dir = as_bookmark_dir_fn(fn)
_default_bookmark_save_dir_fn = as_bookmark_dir_fn(fn)
return fn


Expand Down Expand Up @@ -118,7 +142,7 @@ def restore_bookmark_dir(id: str) -> Path:
--------
* `~shiny.bookmark.set_global_save_dir_fn` : Set the global bookmark save directory function.
"""
global bookmark_restore_dir
global _default_bookmark_restore_dir_fn

bookmark_restore_dir = as_bookmark_dir_fn(fn)
_default_bookmark_restore_dir_fn = as_bookmark_dir_fn(fn)
return fn
5 changes: 4 additions & 1 deletion shiny/bookmark/_restore_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .._docstring import add_example
from ..module import ResolvedId
from ._bookmark_state import local_restore_dir
from ._global import get_bookmark_restore_dir_fn
from ._types import BookmarkRestoreDirFn
from ._utils import from_json_file, from_json_str, in_shiny_server

Expand Down Expand Up @@ -189,7 +190,9 @@ async def _load_state_qs(self, query_string: str, *, app: App) -> None:

id = id[0]

load_bookmark_fn: BookmarkRestoreDirFn | None = app._bookmark_restore_dir_fn
load_bookmark_fn: BookmarkRestoreDirFn | None = get_bookmark_restore_dir_fn(
app._bookmark_restore_dir_fn
)

if load_bookmark_fn is None:
if in_shiny_server():
Expand Down
5 changes: 4 additions & 1 deletion shiny/bookmark/_save_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from .._utils import private_random_id
from ..reactive import isolate
from ._bookmark_state import local_save_dir
from ._global import get_bookmark_save_dir_fn
from ._types import BookmarkSaveDirFn
from ._utils import in_shiny_server, to_json_file, to_json_str

Expand Down Expand Up @@ -66,7 +67,9 @@ async def _save_state(self, *, app: App) -> str:
# to `self.dir`.

# This will be defined by the hosting environment if it supports bookmarking.
save_bookmark_fn: BookmarkSaveDirFn | None = app._bookmark_save_dir_fn
save_bookmark_fn: BookmarkSaveDirFn | None = get_bookmark_save_dir_fn(
app._bookmark_save_dir_fn
)

if save_bookmark_fn is None:
if in_shiny_server():
Expand Down
Loading