Skip to content

Commit b00d2a8

Browse files
committed
Require that a ResolvedId is supplied to restore_input()
1 parent 8b0a85a commit b00d2a8

File tree

2 files changed

+31
-28
lines changed

2 files changed

+31
-28
lines changed
Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from htmltools import tags
1+
from htmltools import css, tags
22
from starlette.requests import Request
33

44
from shiny import App, Inputs, Outputs, Session, ui
@@ -13,32 +13,26 @@ def custom_input_text(
1313

1414
resolved_id = resolve_id(id)
1515
return tags.div(
16-
"Custom input text:",
17-
tags.input(
16+
tags.label(tags.strong("Custom input text:")),
17+
tags.textarea(
18+
restore_input(resolved_id, value),
1819
id=resolved_id,
1920
type="text",
20-
value=restore_input(resolved_id, value),
2121
placeholder="Type here...",
22+
style=css(width="400px", height="3hr"),
2223
),
2324
class_="shiny-input-container",
24-
style=ui.css(width="400px"),
2525
)
2626

2727

2828
# App UI **must** be a function to ensure that each user restores their own UI values.
2929
def app_ui(request: Request):
3030
return ui.page_fluid(
31-
custom_input_text("myid", value="Default value - Hello, world!"),
31+
custom_input_text(
32+
"myid",
33+
value="Change this value, then click bookmark and refresh the page.",
34+
),
3235
ui.input_bookmark_button(),
33-
# ui.markdown(
34-
# "Directions: "
35-
# "\n1. Change the radio button selection below"
36-
# "\n2. Save the bookmark."
37-
# "\n3. Then, refresh your browser page to see the radio button selection has been restored."
38-
# ),
39-
# ui.hr(),
40-
# ui.input_radio_buttons("letter", "Choose a letter", choices=["A", "B", "C"]),
41-
# ui.input_bookmark_button(label="Save bookmark!"),
4236
)
4337

4438

@@ -49,4 +43,5 @@ async def _(url: str):
4943
await session.bookmark.update_query_string(url)
5044

5145

46+
# `bookmark_store` (`"url"` or `"server"`) must be passed to the `App` constructor to enable bookmarking.
5247
app = App(app_ui, server, bookmark_store="url")

shiny/bookmark/_restore_state.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from typing import TYPE_CHECKING, Any, Literal, Optional
99
from urllib.parse import parse_qs, parse_qsl
1010

11+
from ..module import ResolvedId
1112
from ._bookmark_state import local_restore_dir
1213
from ._types import BookmarkRestoreDirFn
1314
from ._utils import from_json_str, in_shiny_server
@@ -283,33 +284,36 @@ class RestoreInputSet:
283284
could completely prevent any other (non- restored) kvalue from being used.
284285
"""
285286

286-
_values: dict[str, Any]
287-
_pending: set[str]
287+
_values: dict[ResolvedId, Any]
288+
_pending: set[ResolvedId]
288289
"""Names of values which have been marked as pending"""
289-
_used: set[str]
290+
_used: set[ResolvedId]
290291
"""Names of values which have been used"""
291292

292293
def __init__(self, values: Optional[dict[str, Any]] = None):
293294

294-
self._values = {} if values is None else values
295+
if values is None:
296+
self._values = {}
297+
else:
298+
self._values = {ResolvedId(key): value for key, value in values.items()}
295299
self._pending = set()
296300
self._used = set()
297301

298-
def exists(self, name: str) -> bool:
302+
def exists(self, name: ResolvedId) -> bool:
299303
return name in self._values
300304

301-
def available(self, name: str) -> bool:
305+
def available(self, name: ResolvedId) -> bool:
302306
return self.exists(name) and not self.is_used(name)
303307

304-
def is_pending(self, name: str) -> bool:
308+
def is_pending(self, name: ResolvedId) -> bool:
305309
return name in self._pending
306310

307-
def is_used(self, name: str) -> bool:
311+
def is_used(self, name: ResolvedId) -> bool:
308312
return name in self._used
309313

310314
# Get a value. If `force` is TRUE, get the value without checking whether
311315
# has been used, and without marking it as pending.
312-
def get(self, name: str, force: bool = False) -> Any:
316+
def get(self, name: ResolvedId, force: bool = False) -> Any:
313317
if force:
314318
return self._values[name]
315319

@@ -325,7 +329,7 @@ def flush_pending(self) -> None:
325329
self._pending.clear()
326330

327331
def as_dict(self) -> dict[str, Any]:
328-
return self._values
332+
return {str(key): value for key, value in self._values.items()}
329333

330334

331335
# #############################################################################
@@ -386,7 +390,7 @@ def get_current_restore_context() -> RestoreContext | None:
386390
return ctx
387391

388392

389-
def restore_input(id: str, default: Any) -> Any:
393+
def restore_input(resolved_id: ResolvedId, default: Any) -> Any:
390394
"""
391395
Restore an input value
392396
@@ -400,6 +404,10 @@ def restore_input(id: str, default: Any) -> Any:
400404
default
401405
A default value to use, if there's no value to restore.
402406
"""
407+
if not isinstance(resolved_id, ResolvedId):
408+
raise TypeError(
409+
"Expected `resolved_id` to be of type `ResolvedId` which is returned from `shiny.module.resolve_id(id)`."
410+
)
403411
# Will run even if the domain is missing
404412
if not has_current_restore_context():
405413
return default
@@ -408,7 +416,7 @@ def restore_input(id: str, default: Any) -> Any:
408416
ctx = get_current_restore_context()
409417
if isinstance(ctx, RestoreContext):
410418
old_inputs = ctx.input
411-
if old_inputs.available(id):
412-
return old_inputs.get(id)
419+
if old_inputs.available(resolved_id):
420+
return old_inputs.get(resolved_id)
413421

414422
return default

0 commit comments

Comments
 (0)