From ca3002cb4aeaa6c7af3aab75afffa28e840d45a3 Mon Sep 17 00:00:00 2001 From: William Neto Date: Sun, 2 Jul 2023 18:39:33 -0300 Subject: [PATCH 1/7] Implementing localStorage integration --- .../@reactpy/client/src/components.tsx | 12 ++++++ .../packages/@reactpy/client/src/messages.ts | 9 +++- .../@reactpy/client/src/reactpy-client.ts | 17 ++++++++ src/py/reactpy/reactpy/backend/hooks.py | 5 +++ src/py/reactpy/reactpy/backend/starlette.py | 7 +++- src/py/reactpy/reactpy/backend/types.py | 4 +- src/py/reactpy/reactpy/core/serve.py | 20 ++++++--- src/py/reactpy/reactpy/core/types.py | 42 ++++++++++++++++++- 8 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/js/packages/@reactpy/client/src/components.tsx b/src/js/packages/@reactpy/client/src/components.tsx index 728c4cec7..d94f49a10 100644 --- a/src/js/packages/@reactpy/client/src/components.tsx +++ b/src/js/packages/@reactpy/client/src/components.tsx @@ -40,6 +40,18 @@ export function Layout(props: { client: ReactPyClient }): JSX.Element { [currentModel, props.client], ); + useEffect( + () => + props.client.onMessage("sync-local-storage", ({ type, storage}) => { + for (let itemKey in storage) { + window.localStorage.setItem( + itemKey, + storage[itemKey] + ) + } + }) + ) + return ( diff --git a/src/js/packages/@reactpy/client/src/messages.ts b/src/js/packages/@reactpy/client/src/messages.ts index 34001dcb0..1227a362c 100644 --- a/src/js/packages/@reactpy/client/src/messages.ts +++ b/src/js/packages/@reactpy/client/src/messages.ts @@ -12,6 +12,11 @@ export type LayoutEventMessage = { data: any; }; -export type IncomingMessage = LayoutUpdateMessage; -export type OutgoingMessage = LayoutEventMessage; +export type LocalStorageUpdateMessage = { + type: "sync-local-storage", + storage: any; +} + +export type IncomingMessage = LayoutUpdateMessage | LocalStorageUpdateMessage; +export type OutgoingMessage = LayoutEventMessage | LocalStorageUpdateMessage; export type Message = IncomingMessage | OutgoingMessage; diff --git a/src/js/packages/@reactpy/client/src/reactpy-client.ts b/src/js/packages/@reactpy/client/src/reactpy-client.ts index 6f37b55a1..ab2f121f6 100644 --- a/src/js/packages/@reactpy/client/src/reactpy-client.ts +++ b/src/js/packages/@reactpy/client/src/reactpy-client.ts @@ -204,6 +204,23 @@ function createReconnectingWebSocket( socket.current.onopen = () => { everConnected = true; logger.log("client connected"); + + let storage : any = {} + for (let i = 0; i <= window.localStorage.length; i++) { + let storage_key = window.localStorage.key(i) + if (storage_key){ + storage[storage_key] = window.localStorage.getItem(storage_key) + } + socket.current?.send( + JSON.stringify( + { + "type": "sync-local-storage", + "storage": storage + } + ) + ) + } + interval = startInterval; retries = 0; if (props.onOpen) { diff --git a/src/py/reactpy/reactpy/backend/hooks.py b/src/py/reactpy/reactpy/backend/hooks.py index 19ad114ed..d6f91315e 100644 --- a/src/py/reactpy/reactpy/backend/hooks.py +++ b/src/py/reactpy/reactpy/backend/hooks.py @@ -5,6 +5,7 @@ from reactpy.backend.types import Connection, Location from reactpy.core.hooks import Context, create_context, use_context +from reactpy.core.types import LocalStorage # backend implementations should establish this context at the root of an app ConnectionContext: Context[Connection[Any] | None] = create_context(None) @@ -27,3 +28,7 @@ def use_scope() -> MutableMapping[str, Any]: def use_location() -> Location: """Get the current :class:`~reactpy.backend.types.Connection`'s location.""" return use_connection().location + +def use_local_storage() -> LocalStorage: + """Get the storage object for the connection""" + return use_connection().storage \ No newline at end of file diff --git a/src/py/reactpy/reactpy/backend/starlette.py b/src/py/reactpy/reactpy/backend/starlette.py index 3a9695b33..44f9b152c 100644 --- a/src/py/reactpy/reactpy/backend/starlette.py +++ b/src/py/reactpy/reactpy/backend/starlette.py @@ -29,7 +29,7 @@ from reactpy.config import REACTPY_WEB_MODULES_DIR from reactpy.core.layout import Layout from reactpy.core.serve import RecvCoroutine, SendCoroutine, serve_layout -from reactpy.core.types import RootComponentConstructor +from reactpy.core.types import RootComponentConstructor, LocalStorage logger = logging.getLogger(__name__) @@ -79,7 +79,7 @@ def use_connection() -> Connection[WebSocket]: if not isinstance(conn.carrier, WebSocket): # nocov msg = f"Connection has unexpected carrier {conn.carrier}. Are you running with a Flask server?" raise TypeError(msg) - return conn + return @dataclass @@ -140,6 +140,7 @@ async def model_stream(socket: WebSocket) -> None: pathname = "/" + socket.scope["path_params"].get("path", "") pathname = pathname[len(options.url_prefix) :] or "/" search = socket.scope["query_string"].decode() + storage_obj = LocalStorage(sock=socket) try: await serve_layout( @@ -149,10 +150,12 @@ async def model_stream(socket: WebSocket) -> None: value=Connection( scope=socket.scope, location=Location(pathname, f"?{search}" if search else ""), + storage=storage_obj, carrier=socket, ), ) ), + storage_obj, send, recv, ) diff --git a/src/py/reactpy/reactpy/backend/types.py b/src/py/reactpy/reactpy/backend/types.py index fbc4addc0..614c9cb44 100644 --- a/src/py/reactpy/reactpy/backend/types.py +++ b/src/py/reactpy/reactpy/backend/types.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import Any, Callable, Generic, Protocol, TypeVar, runtime_checkable -from reactpy.core.types import RootComponentConstructor +from reactpy.core.types import RootComponentConstructor, LocalStorage _App = TypeVar("_App") @@ -51,6 +51,8 @@ class Connection(Generic[_Carrier]): location: Location """The current location (URL)""" + storage: LocalStorage + carrier: _Carrier """How the connection is mediated. For example, a request or websocket. diff --git a/src/py/reactpy/reactpy/core/serve.py b/src/py/reactpy/reactpy/core/serve.py index 3a530e854..a5c5d2769 100644 --- a/src/py/reactpy/reactpy/core/serve.py +++ b/src/py/reactpy/reactpy/core/serve.py @@ -8,8 +8,8 @@ from anyio.abc import TaskGroup from reactpy.config import REACTPY_DEBUG_MODE -from reactpy.core.types import LayoutEventMessage, LayoutType, LayoutUpdateMessage - +from reactpy.core.types import StorageEventMessage, LocalStorage, LayoutEventMessage, LayoutType, LayoutUpdateMessage +from reactpy.core.layout import Layout logger = getLogger(__name__) @@ -33,6 +33,7 @@ class Stop(BaseException): async def serve_layout( layout: LayoutType[LayoutUpdateMessage, LayoutEventMessage], + storage: LocalStorage, send: SendCoroutine, recv: RecvCoroutine, ) -> None: @@ -41,11 +42,10 @@ async def serve_layout( try: async with create_task_group() as task_group: task_group.start_soon(_single_outgoing_loop, layout, send) - task_group.start_soon(_single_incoming_loop, task_group, layout, recv) + task_group.start_soon(_single_incoming_loop, task_group, layout, storage, recv) except Stop: logger.info(f"Stopped serving {layout}") - async def _single_outgoing_loop( layout: LayoutType[LayoutUpdateMessage, LayoutEventMessage], send: SendCoroutine ) -> None: @@ -63,13 +63,23 @@ async def _single_outgoing_loop( logger.error(msg) raise +async def incoming_router( + layout: Layout, + storage: LocalStorage, + event: LayoutEventMessage or StorageEventMessage, +): + if event["type"] == "sync-local-storage": + storage._sync(event["storage"]) + else: + await layout.deliver(event) async def _single_incoming_loop( task_group: TaskGroup, layout: LayoutType[LayoutUpdateMessage, LayoutEventMessage], + storage: LocalStorage, recv: RecvCoroutine, ) -> None: while True: # We need to fire and forget here so that we avoid waiting on the completion # of this event handler before receiving and running the next one. - task_group.start_soon(layout.deliver, await recv()) + task_group.start_soon(incoming_router, layout, storage, await recv()) diff --git a/src/py/reactpy/reactpy/core/types.py b/src/py/reactpy/reactpy/core/types.py index 45f300f4f..05ccfaf59 100644 --- a/src/py/reactpy/reactpy/core/types.py +++ b/src/py/reactpy/reactpy/core/types.py @@ -1,6 +1,7 @@ from __future__ import annotations -import sys +import sys, json +from starlette.websockets import WebSocket from collections import namedtuple from collections.abc import Mapping, Sequence from types import TracebackType @@ -233,3 +234,42 @@ class LayoutEventMessage(TypedDict): """The ID of the event handler.""" data: Sequence[Any] """A list of event data passed to the event handler.""" + +class StorageEventMessage(TypedDict): + type: Literal["sync-local-storage"] + storage: dict + +class LocalStorage(): + _socket: WebSocket + storage: dict + + def __init__(self, sock): + self._socket = sock + self.storage = {} + + def _sync(self, sto): + self.storage = sto + + async def _sync_client(self): + await self._socket.send_text( + json.dumps( + { + "type": "sync-local-storage", + "storage": self.storage + } + ) + ) + + def get_item( + self, + key: str + ): + return self.storage.get(key) + + async def set_item( + self, + key: str, + value: str + ): + self.storage[key] = value + await self._sync_client() \ No newline at end of file From e4be3e5419d0de7c99ee175f003c54f870d59c84 Mon Sep 17 00:00:00 2001 From: William Neto Date: Sun, 2 Jul 2023 18:40:19 -0300 Subject: [PATCH 2/7] use_local_storage sample --- .../reactpy/samples/use_local_storage.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/py/reactpy/reactpy/samples/use_local_storage.py diff --git a/src/py/reactpy/reactpy/samples/use_local_storage.py b/src/py/reactpy/reactpy/samples/use_local_storage.py new file mode 100644 index 000000000..1d493f95f --- /dev/null +++ b/src/py/reactpy/reactpy/samples/use_local_storage.py @@ -0,0 +1,57 @@ +from reactpy import component, html, run +from reactpy.core.hooks import use_state +from reactpy.backend.hooks import use_local_storage +from reactpy.core.types import LocalStorage + +@component +def App(): + storage = use_local_storage() + key_input, set_key_input = use_state("") + val_input, set_val_input = use_state("") + + def handle_get(e): + set_val_input( + storage.get_item( + key_input + ) + ) + + async def handle_set(e): + await storage.set_item( + key_input, + val_input + ) + + return html.div( + html.h1("Local Storage"), + html.input( + { + "type": "text", + "placeholder": "Key", + "value": key_input, + "on_change": lambda e: set_key_input(e["target"]["value"]) + } + ), + html.textarea( + { + "placeholder": "Value", + "value": val_input, + "on_change": lambda e: set_val_input(e["target"]["value"]) + } + ), + html.button( + { + "on_click": handle_get + }, + "Get" + ), + html.button( + { + "on_click": handle_set + }, + "Set" + ) + ) + + +run(App) From 6eda41d2bd8877ef42fc073d5de106b1c20576ec Mon Sep 17 00:00:00 2001 From: William Neto Date: Tue, 4 Jul 2023 21:10:44 -0300 Subject: [PATCH 3/7] Ajusting sample --- src/py/reactpy/reactpy/samples/use_local_storage.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/py/reactpy/reactpy/samples/use_local_storage.py b/src/py/reactpy/reactpy/samples/use_local_storage.py index 1d493f95f..1b6345695 100644 --- a/src/py/reactpy/reactpy/samples/use_local_storage.py +++ b/src/py/reactpy/reactpy/samples/use_local_storage.py @@ -1,5 +1,5 @@ from reactpy import component, html, run -from reactpy.core.hooks import use_state +from reactpy.core.hooks import use_state, use_effect from reactpy.backend.hooks import use_local_storage from reactpy.core.types import LocalStorage @@ -9,14 +9,16 @@ def App(): key_input, set_key_input = use_state("") val_input, set_val_input = use_state("") - def handle_get(e): + @use_effect + def handle_get(): set_val_input( storage.get_item( key_input ) ) - async def handle_set(e): + @use_effect + async def handle_set(): await storage.set_item( key_input, val_input From aa5aa65a20f52b405d8b1f015c7c96013e792388 Mon Sep 17 00:00:00 2001 From: William Neto Date: Thu, 6 Jul 2023 01:18:15 -0300 Subject: [PATCH 4/7] Implementing access to sessionStorage --- .../@reactpy/client/src/components.tsx | 12 +++++ .../packages/@reactpy/client/src/messages.ts | 9 +++- .../@reactpy/client/src/reactpy-client.ts | 34 +++++++++---- src/py/reactpy/reactpy/backend/hooks.py | 10 ++-- src/py/reactpy/reactpy/backend/starlette.py | 13 +++-- src/py/reactpy/reactpy/backend/types.py | 8 ++- src/py/reactpy/reactpy/core/serve.py | 31 ++++++++---- src/py/reactpy/reactpy/core/types.py | 49 ++++++++++++++++++- 8 files changed, 135 insertions(+), 31 deletions(-) diff --git a/src/js/packages/@reactpy/client/src/components.tsx b/src/js/packages/@reactpy/client/src/components.tsx index d94f49a10..8fb734175 100644 --- a/src/js/packages/@reactpy/client/src/components.tsx +++ b/src/js/packages/@reactpy/client/src/components.tsx @@ -52,6 +52,18 @@ export function Layout(props: { client: ReactPyClient }): JSX.Element { }) ) + useEffect( + () => + props.client.onMessage("sync-session-storage", ({ type, storage}) => { + for (let itemKey in storage) { + window.sessionStorage.setItem( + itemKey, + storage[itemKey] + ) + } + }) + ) + return ( diff --git a/src/js/packages/@reactpy/client/src/messages.ts b/src/js/packages/@reactpy/client/src/messages.ts index 1227a362c..b8073391c 100644 --- a/src/js/packages/@reactpy/client/src/messages.ts +++ b/src/js/packages/@reactpy/client/src/messages.ts @@ -17,6 +17,11 @@ export type LocalStorageUpdateMessage = { storage: any; } -export type IncomingMessage = LayoutUpdateMessage | LocalStorageUpdateMessage; -export type OutgoingMessage = LayoutEventMessage | LocalStorageUpdateMessage; +export type SessionStorageUpdateMessage = { + type: "sync-session-storage", + storage: any; +} + +export type IncomingMessage = LayoutUpdateMessage | LocalStorageUpdateMessage | SessionStorageUpdateMessage; +export type OutgoingMessage = LayoutEventMessage | LocalStorageUpdateMessage | SessionStorageUpdateMessage; export type Message = IncomingMessage | OutgoingMessage; diff --git a/src/js/packages/@reactpy/client/src/reactpy-client.ts b/src/js/packages/@reactpy/client/src/reactpy-client.ts index ab2f121f6..1c05721a5 100644 --- a/src/js/packages/@reactpy/client/src/reactpy-client.ts +++ b/src/js/packages/@reactpy/client/src/reactpy-client.ts @@ -205,21 +205,37 @@ function createReconnectingWebSocket( everConnected = true; logger.log("client connected"); - let storage : any = {} + let _localStorage : any = {} for (let i = 0; i <= window.localStorage.length; i++) { let storage_key = window.localStorage.key(i) if (storage_key){ - storage[storage_key] = window.localStorage.getItem(storage_key) + _localStorage[storage_key] = window.localStorage.getItem(storage_key) } - socket.current?.send( - JSON.stringify( - { - "type": "sync-local-storage", - "storage": storage - } - ) + } + socket.current?.send( + JSON.stringify( + { + "type": "sync-local-storage", + "storage": _localStorage + } ) + ) + + let _sessionStorage : any = {} + for (let i = 0; i <= window.sessionStorage.length; i++) { + let storage_key = window.sessionStorage.key(i) + if (storage_key){ + _sessionStorage[storage_key] = window.sessionStorage.getItem(storage_key) + } } + socket.current?.send( + JSON.stringify( + { + "type": "sync-local-storage", + "storage": _sessionStorage + } + ) + ) interval = startInterval; retries = 0; diff --git a/src/py/reactpy/reactpy/backend/hooks.py b/src/py/reactpy/reactpy/backend/hooks.py index d6f91315e..7578ae982 100644 --- a/src/py/reactpy/reactpy/backend/hooks.py +++ b/src/py/reactpy/reactpy/backend/hooks.py @@ -5,7 +5,7 @@ from reactpy.backend.types import Connection, Location from reactpy.core.hooks import Context, create_context, use_context -from reactpy.core.types import LocalStorage +from reactpy.core.types import LocalStorage, SessionStorage # backend implementations should establish this context at the root of an app ConnectionContext: Context[Connection[Any] | None] = create_context(None) @@ -30,5 +30,9 @@ def use_location() -> Location: return use_connection().location def use_local_storage() -> LocalStorage: - """Get the storage object for the connection""" - return use_connection().storage \ No newline at end of file + """Get the localStorage object for the connection""" + return use_connection().local_storage + +def use_session_storage() -> SessionStorage: + """Get the sessionStorage object for the connection""" + return use_connection().session_storage \ No newline at end of file diff --git a/src/py/reactpy/reactpy/backend/starlette.py b/src/py/reactpy/reactpy/backend/starlette.py index 44f9b152c..4044c89b0 100644 --- a/src/py/reactpy/reactpy/backend/starlette.py +++ b/src/py/reactpy/reactpy/backend/starlette.py @@ -29,7 +29,7 @@ from reactpy.config import REACTPY_WEB_MODULES_DIR from reactpy.core.layout import Layout from reactpy.core.serve import RecvCoroutine, SendCoroutine, serve_layout -from reactpy.core.types import RootComponentConstructor, LocalStorage +from reactpy.core.types import RootComponentConstructor, LocalStorage, SessionStorage logger = logging.getLogger(__name__) @@ -79,7 +79,7 @@ def use_connection() -> Connection[WebSocket]: if not isinstance(conn.carrier, WebSocket): # nocov msg = f"Connection has unexpected carrier {conn.carrier}. Are you running with a Flask server?" raise TypeError(msg) - return + return conn @dataclass @@ -140,7 +140,8 @@ async def model_stream(socket: WebSocket) -> None: pathname = "/" + socket.scope["path_params"].get("path", "") pathname = pathname[len(options.url_prefix) :] or "/" search = socket.scope["query_string"].decode() - storage_obj = LocalStorage(sock=socket) + local_storage_obj = LocalStorage(sock=socket) + session_storage_obj = SessionStorage(sock=socket) try: await serve_layout( @@ -150,12 +151,14 @@ async def model_stream(socket: WebSocket) -> None: value=Connection( scope=socket.scope, location=Location(pathname, f"?{search}" if search else ""), - storage=storage_obj, + local_storage=local_storage_obj, + session_storage=session_storage_obj, carrier=socket, ), ) ), - storage_obj, + local_storage_obj, + session_storage_obj, send, recv, ) diff --git a/src/py/reactpy/reactpy/backend/types.py b/src/py/reactpy/reactpy/backend/types.py index 614c9cb44..5a8831db5 100644 --- a/src/py/reactpy/reactpy/backend/types.py +++ b/src/py/reactpy/reactpy/backend/types.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import Any, Callable, Generic, Protocol, TypeVar, runtime_checkable -from reactpy.core.types import RootComponentConstructor, LocalStorage +from reactpy.core.types import RootComponentConstructor, LocalStorage, SessionStorage _App = TypeVar("_App") @@ -51,7 +51,11 @@ class Connection(Generic[_Carrier]): location: Location """The current location (URL)""" - storage: LocalStorage + local_storage: LocalStorage + """An object to obtain client localStorage""" + + session_storage: SessionStorage + """An object to obtain client sessionStorage""" carrier: _Carrier """How the connection is mediated. For example, a request or websocket. diff --git a/src/py/reactpy/reactpy/core/serve.py b/src/py/reactpy/reactpy/core/serve.py index a5c5d2769..c2f2bc6aa 100644 --- a/src/py/reactpy/reactpy/core/serve.py +++ b/src/py/reactpy/reactpy/core/serve.py @@ -8,7 +8,15 @@ from anyio.abc import TaskGroup from reactpy.config import REACTPY_DEBUG_MODE -from reactpy.core.types import StorageEventMessage, LocalStorage, LayoutEventMessage, LayoutType, LayoutUpdateMessage +from reactpy.core.types import ( + LocalStorageEventMessage, + SessionStorageEventMessage, + LocalStorage, + SessionStorage, + LayoutEventMessage, + LayoutType, + LayoutUpdateMessage +) from reactpy.core.layout import Layout logger = getLogger(__name__) @@ -33,7 +41,8 @@ class Stop(BaseException): async def serve_layout( layout: LayoutType[LayoutUpdateMessage, LayoutEventMessage], - storage: LocalStorage, + local_storage: LocalStorage, + session_storage: SessionStorage, send: SendCoroutine, recv: RecvCoroutine, ) -> None: @@ -42,7 +51,7 @@ async def serve_layout( try: async with create_task_group() as task_group: task_group.start_soon(_single_outgoing_loop, layout, send) - task_group.start_soon(_single_incoming_loop, task_group, layout, storage, recv) + task_group.start_soon(_single_incoming_loop, task_group, layout, local_storage, session_storage,recv) except Stop: logger.info(f"Stopped serving {layout}") @@ -65,21 +74,25 @@ async def _single_outgoing_loop( async def incoming_router( layout: Layout, - storage: LocalStorage, - event: LayoutEventMessage or StorageEventMessage, + local_storage: LocalStorage, + session_storage: SessionStorage, + event: LayoutEventMessage or LocalStorageEventMessage or SessionStorageEventMessage, ): if event["type"] == "sync-local-storage": - storage._sync(event["storage"]) - else: + local_storage._sync(event["storage"]) + elif event["type"] == "sync-session-storage": + session_storage._sync(event["storage"]) + elif event["type"] == "layout-event": await layout.deliver(event) async def _single_incoming_loop( task_group: TaskGroup, layout: LayoutType[LayoutUpdateMessage, LayoutEventMessage], - storage: LocalStorage, + local_storage: LocalStorage, + session_storage: SessionStorage, recv: RecvCoroutine, ) -> None: while True: # We need to fire and forget here so that we avoid waiting on the completion # of this event handler before receiving and running the next one. - task_group.start_soon(incoming_router, layout, storage, await recv()) + task_group.start_soon(incoming_router, layout, local_storage, session_storage, await recv()) diff --git a/src/py/reactpy/reactpy/core/types.py b/src/py/reactpy/reactpy/core/types.py index 05ccfaf59..59417017f 100644 --- a/src/py/reactpy/reactpy/core/types.py +++ b/src/py/reactpy/reactpy/core/types.py @@ -235,9 +235,21 @@ class LayoutEventMessage(TypedDict): data: Sequence[Any] """A list of event data passed to the event handler.""" -class StorageEventMessage(TypedDict): +class LocalStorageEventMessage(TypedDict): + """Message describing an event containing localStorage""" + type: Literal["sync-local-storage"] + """The type of message""" + storage: dict + """A dictionary containing localStorage items""" + +class SessionStorageEventMessage(TypedDict): + """Message describing an event containing sessionStorage""" + + type: Literal["sync-session-storage"] + """Message describing an event containing localStorage""" storage: dict + """A dictionary containing localStorage items""" class LocalStorage(): _socket: WebSocket @@ -266,6 +278,41 @@ def get_item( ): return self.storage.get(key) + async def set_item( + self, + key: str, + value: str + ): + self.storage[key] = value + await self._sync_client() + +class SessionStorage(): + _socket: WebSocket + storage: dict + + def __init__(self, sock): + self._socket = sock + self.storage = {} + + def _sync(self, sto): + self.storage = sto + + async def _sync_client(self): + await self._socket.send_text( + json.dumps( + { + "type": "sync-session-storage", + "storage": self.storage + } + ) + ) + + def get_item( + self, + key: str + ): + return self.storage.get(key) + async def set_item( self, key: str, From fd0cfcb76c11701668f0b9db3f6d0e5e691d8cf3 Mon Sep 17 00:00:00 2001 From: William Neto Date: Thu, 6 Jul 2023 01:18:52 -0300 Subject: [PATCH 5/7] Updating use_local_storage and use_session_storage samples --- .../reactpy/samples/use_local_storage.py | 2 - .../reactpy/samples/use_session_storage.py | 57 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/py/reactpy/reactpy/samples/use_session_storage.py diff --git a/src/py/reactpy/reactpy/samples/use_local_storage.py b/src/py/reactpy/reactpy/samples/use_local_storage.py index 1b6345695..d161593bd 100644 --- a/src/py/reactpy/reactpy/samples/use_local_storage.py +++ b/src/py/reactpy/reactpy/samples/use_local_storage.py @@ -1,7 +1,6 @@ from reactpy import component, html, run from reactpy.core.hooks import use_state, use_effect from reactpy.backend.hooks import use_local_storage -from reactpy.core.types import LocalStorage @component def App(): @@ -17,7 +16,6 @@ def handle_get(): ) ) - @use_effect async def handle_set(): await storage.set_item( key_input, diff --git a/src/py/reactpy/reactpy/samples/use_session_storage.py b/src/py/reactpy/reactpy/samples/use_session_storage.py new file mode 100644 index 000000000..b1e6f9089 --- /dev/null +++ b/src/py/reactpy/reactpy/samples/use_session_storage.py @@ -0,0 +1,57 @@ +from reactpy import component, html, run +from reactpy.core.hooks import use_state, use_effect +from reactpy.backend.hooks import use_session_storage + +@component +def App(): + storage = use_session_storage() + key_input, set_key_input = use_state("") + val_input, set_val_input = use_state("") + + @use_effect + def handle_get(): + set_val_input( + storage.get_item( + key_input + ) + ) + + async def handle_set(e): + await storage.set_item( + key_input, + val_input + ) + + return html.div( + html.h1("Session Storage"), + html.input( + { + "type": "text", + "placeholder": "Key", + "value": key_input, + "on_change": lambda e: set_key_input(e["target"]["value"]) + } + ), + html.textarea( + { + "placeholder": "Value", + "value": val_input, + "on_change": lambda e: set_val_input(e["target"]["value"]) + } + ), + html.button( + { + "on_click": handle_get + }, + "Get" + ), + html.button( + { + "on_click": handle_set + }, + "Set" + ) + ) + + +run(App) From 89dbe5af076d07f9216e26de734244f7cc269689 Mon Sep 17 00:00:00 2001 From: William Neto Date: Thu, 6 Jul 2023 01:42:31 -0300 Subject: [PATCH 6/7] Updating changelog --- docs/source/about/changelog.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/about/changelog.rst b/docs/source/about/changelog.rst index 30d595b94..3df32b84f 100644 --- a/docs/source/about/changelog.rst +++ b/docs/source/about/changelog.rst @@ -23,7 +23,8 @@ more info, see the :ref:`Contributor Guide `. Unreleased ---------- -Nothing yet... +**Added** +- :issue:`1075` - Add support to localStorage and sessionStorage access v1.0.2 From 1ac5bbc25e4ea5f613e4ea0c3313e33fd571b294 Mon Sep 17 00:00:00 2001 From: William Neto Date: Thu, 6 Jul 2023 01:52:19 -0300 Subject: [PATCH 7/7] Adjusting use_local_storage sample --- src/py/reactpy/reactpy/samples/use_local_storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py/reactpy/reactpy/samples/use_local_storage.py b/src/py/reactpy/reactpy/samples/use_local_storage.py index d161593bd..65f63bab3 100644 --- a/src/py/reactpy/reactpy/samples/use_local_storage.py +++ b/src/py/reactpy/reactpy/samples/use_local_storage.py @@ -16,7 +16,7 @@ def handle_get(): ) ) - async def handle_set(): + async def handle_set(e): await storage.set_item( key_input, val_input