@@ -180,6 +180,26 @@ async def update_query_string(
180180 """
181181 ...
182182
183+ @abstractmethod
184+ async def get_bookmark_url (self ) -> str | None :
185+ """
186+ Get the URL of the current bookmark state
187+
188+ This method should return the full URL, including the query string.
189+
190+ As a side effect, all `on_bookmark` callbacks will be invoked to allow the
191+ bookmark state to be modified before it is serialized. This will cause the
192+ bookmark state to be serialized to disk when `session.bookmark.store ==
193+ "server"`.
194+
195+ Returns
196+ -------
197+ :
198+ The full URL of the current bookmark state, including the query string.
199+ `None` if bookmarking is not enabled.
200+ """
201+ ...
202+
183203 @abstractmethod
184204 async def do_bookmark (self ) -> None :
185205 """
@@ -385,66 +405,76 @@ def _get_bookmark_exclude(self) -> list[str]:
385405 # Remove duplicates
386406 return list (set ([* self .exclude , * scoped_excludes ]))
387407
408+ async def get_bookmark_url (self ) -> str | None :
409+ if self .store == "disable" :
410+ return None
411+
412+ # ?withLogErrors
413+ from ..bookmark ._bookmark import BookmarkState
414+ from ..session import session_context
415+
416+ async def root_state_on_save (state : BookmarkState ) -> None :
417+ with session_context (self ._session ):
418+ await self ._on_bookmark_callbacks .invoke (state )
419+
420+ root_state = BookmarkState (
421+ input = self ._session .input ,
422+ exclude = self ._get_bookmark_exclude (),
423+ on_save = root_state_on_save ,
424+ )
425+
426+ if self .store == "server" :
427+ query_string = await root_state ._save_state (app = self ._session .app )
428+ elif self .store == "url" :
429+ query_string = await root_state ._encode_state ()
430+ # # Can we have browser storage?
431+ # elif self.store == "browser":
432+ # get_json object
433+ # get consistent storage value (not session id)
434+ # send object to browser storage
435+ # return server-like-id url value
436+ else :
437+ raise ValueError ("Unknown bookmark store: " + self .store )
438+
439+ clientdata = self ._session .clientdata
440+
441+ port = str (clientdata .url_port ())
442+ full_url = "" .join (
443+ [
444+ clientdata .url_protocol (),
445+ "//" ,
446+ clientdata .url_hostname (),
447+ ":" if port else "" ,
448+ port ,
449+ clientdata .url_pathname (),
450+ "?" ,
451+ query_string ,
452+ ]
453+ )
454+
455+ return full_url
456+
388457 async def do_bookmark (self ) -> None :
389458
390- if self .store == "disable" :
391- # If you have a bookmark button or request a bookmark to be saved,
392- # then it should be saved. (Present a warning telling author how to fix it)
393- warnings .warn (
394- "Saving the bookmark state has been requested. "
395- 'However, bookmarking is current set to `"disable"`. '
396- "Please enable bookmarking by setting "
397- "`shiny.App(bookmark_store=)` or "
398- "`shiny.express.app_opts(bookmark_store=)`" ,
399- stacklevel = 2 ,
400- )
401- return
459+ from ..session import session_context
402460
403461 try :
404- # ?withLogErrors
405- from ..bookmark ._bookmark import BookmarkState
406- from ..session import session_context
462+ full_url = await self .get_bookmark_url ()
463+
464+ if full_url is None :
465+ # If you have a bookmark button or request a bookmark to be saved,
466+ # then it should be saved. (Present a warning telling author how to fix it)
467+ warnings .warn (
468+ "Saving the bookmark state has been requested. "
469+ 'However, bookmarking is current set to `"disable"`. '
470+ "Please enable bookmarking by setting "
471+ "`shiny.App(bookmark_store=)` or "
472+ "`shiny.express.app_opts(bookmark_store=)`" ,
473+ stacklevel = 2 ,
474+ )
475+ return
407476
408- async def root_state_on_save (state : BookmarkState ) -> None :
409- with session_context (self ._session ):
410- await self ._on_bookmark_callbacks .invoke (state )
411-
412- root_state = BookmarkState (
413- input = self ._session .input ,
414- exclude = self ._get_bookmark_exclude (),
415- on_save = root_state_on_save ,
416- )
417-
418- if self .store == "server" :
419- query_string = await root_state ._save_state (app = self ._session .app )
420- elif self .store == "url" :
421- query_string = await root_state ._encode_state ()
422- # # Can we have browser storage?
423- # elif self.store == "browser":
424- # get_json object
425- # get consistent storage value (not session id)
426- # send object to browser storage
427- # return server-like-id url value
428- else :
429- raise ValueError ("Unknown bookmark store: " + self .store )
430-
431- clientdata = self ._session .clientdata
432-
433- port = str (clientdata .url_port ())
434- full_url = "" .join (
435- [
436- clientdata .url_protocol (),
437- "//" ,
438- clientdata .url_hostname (),
439- ":" if port else "" ,
440- port ,
441- clientdata .url_pathname (),
442- "?" ,
443- query_string ,
444- ]
445- )
446-
447- # If onBookmarked callback was provided, invoke it; if not call
477+ # If on_bookmarked callback was provided, invoke it; if not call
448478 # the default.
449479 if self ._on_bookmarked_callbacks .count () > 0 :
450480 with session_context (self ._session ):
@@ -573,6 +603,9 @@ async def update_query_string(
573603 ) -> None :
574604 await self ._session ._parent .bookmark .update_query_string (query_string , mode )
575605
606+ async def get_bookmark_url (self ) -> str | None :
607+ return await self ._session ._parent .bookmark .get_bookmark_url ()
608+
576609 async def do_bookmark (self ) -> None :
577610 await self ._session ._parent .bookmark .do_bookmark ()
578611
@@ -617,6 +650,9 @@ async def update_query_string(
617650 # no-op within ExpressStub
618651 return None
619652
653+ async def get_bookmark_url (self ) -> str | None :
654+ return None
655+
620656 async def do_bookmark (self ) -> None :
621657 # no-op within ExpressStub
622658 return None
0 commit comments