1
0
Thomas Brandého 1 жил өмнө
parent
commit
b378827b83

+ 15 - 6
reflex/.templates/web/utils/state.js

@@ -73,7 +73,7 @@ export const getToken = () => {
  * @param delta The delta to apply.
  * @param delta The delta to apply.
  */
  */
 export const applyDelta = (state, delta) => {
 export const applyDelta = (state, delta) => {
-  const new_state = {...state}
+  const new_state = { ...state }
   for (const substate in delta) {
   for (const substate in delta) {
     let s = new_state;
     let s = new_state;
     const path = substate.split(".").slice(1);
     const path = substate.split(".").slice(1);
@@ -153,6 +153,15 @@ export const applyEvent = async (event, socket) => {
     navigator.clipboard.writeText(content);
     navigator.clipboard.writeText(content);
     return false;
     return false;
   }
   }
+  if (event.name == "_download") {
+    const a = document.createElement('a');
+    a.hidden = true;
+    a.href = event.payload.url;
+    a.download = event.payload.filename;
+    a.click();
+    a.remove();
+    return false;
+  }
 
 
   if (event.name == "_alert") {
   if (event.name == "_alert") {
     alert(event.payload.message);
     alert(event.payload.message);
@@ -413,7 +422,7 @@ const applyClientStorageDelta = (client_storage, delta) => {
     for (const key in delta[substate]) {
     for (const key in delta[substate]) {
       const state_key = `${substate}.${key}`
       const state_key = `${substate}.${key}`
       if (client_storage.cookies && state_key in client_storage.cookies) {
       if (client_storage.cookies && state_key in client_storage.cookies) {
-        const cookie_options = {...client_storage.cookies[state_key]}
+        const cookie_options = { ...client_storage.cookies[state_key] }
         const cookie_name = cookie_options.name || state_key
         const cookie_name = cookie_options.name || state_key
         delete cookie_options.name  // name is not a valid cookie option
         delete cookie_options.name  // name is not a valid cookie option
         cookies.set(cookie_name, delta[substate][key], cookie_options);
         cookies.set(cookie_name, delta[substate][key], cookie_options);
@@ -445,18 +454,18 @@ export const useEventLoop = (
   const router = useRouter()
   const router = useRouter()
   const [state, dispatch] = useReducer(applyDelta, initial_state)
   const [state, dispatch] = useReducer(applyDelta, initial_state)
   const [connectError, setConnectError] = useState(null)
   const [connectError, setConnectError] = useState(null)
-  
+
   // Function to add new events to the event queue.
   // Function to add new events to the event queue.
   const Event = (events, _e) => {
   const Event = (events, _e) => {
-      preventDefault(_e);
-      queueEvents(events, socket)
+    preventDefault(_e);
+    queueEvents(events, socket)
   }
   }
 
 
   const sentHydrate = useRef(false);  // Avoid double-hydrate due to React strict-mode
   const sentHydrate = useRef(false);  // Avoid double-hydrate due to React strict-mode
   // initial state hydrate
   // initial state hydrate
   useEffect(() => {
   useEffect(() => {
     if (router.isReady && !sentHydrate.current) {
     if (router.isReady && !sentHydrate.current) {
-      Event(initial_events.map((e) => ({...e})))
+      Event(initial_events.map((e) => ({ ...e })))
       sentHydrate.current = true
       sentHydrate.current = true
     }
     }
   }, [router.isReady])
   }, [router.isReady])

+ 1 - 0
reflex/__init__.py

@@ -23,6 +23,7 @@ from .event import EventChain as EventChain
 from .event import FileUpload as upload_files
 from .event import FileUpload as upload_files
 from .event import clear_local_storage as clear_local_storage
 from .event import clear_local_storage as clear_local_storage
 from .event import console_log as console_log
 from .event import console_log as console_log
+from .event import download as download
 from .event import redirect as redirect
 from .event import redirect as redirect
 from .event import remove_cookie as remove_cookie
 from .event import remove_cookie as remove_cookie
 from .event import remove_local_storage as remove_local_storage
 from .event import remove_local_storage as remove_local_storage

+ 29 - 1
reflex/event.py

@@ -2,7 +2,7 @@
 from __future__ import annotations
 from __future__ import annotations
 
 
 import inspect
 import inspect
-from typing import Any, Callable, Dict, List, Tuple
+from typing import Any, Callable, Dict, List, Optional, Tuple
 
 
 from reflex import constants
 from reflex import constants
 from reflex.base import Base
 from reflex.base import Base
@@ -330,6 +330,34 @@ def set_clipboard(content: str) -> EventSpec:
     )
     )
 
 
 
 
+def download(url: str, filename: Optional[str] = None) -> EventSpec:
+    """Download the file at a given path.
+
+    Args:
+        url : The URL to the file to download.
+        filename : The name that the file should be saved as after download.
+
+    Raises:
+        ValueError: If the URL provided is invalid.
+
+    Returns:
+        EventSpec: An event to download the associated file.
+    """
+    if not url.startswith("/"):
+        raise ValueError("The URL argument should start with a /")
+
+    # if filename is not provided, infer it from url
+    if filename is None:
+        filename = url.rpartition("/")[-1]
+
+    return server_side(
+        "_download",
+        get_fn_signature(download),
+        url=url,
+        filename=filename,
+    )
+
+
 def get_event(state, event):
 def get_event(state, event):
     """Get the event from the given state.
     """Get the event from the given state.