rx.upload must include _var_data from props (#4463)
* rx.upload must include _var_data from props str-casting the dropzone arguments removed any VarData they depended on, like the state context. update test_upload to include passing a prop from a state var * Handle large payload delta from upload event handler Fix update chunk chaining logic; try/catch wasn't catching errors from the async inner function.
This commit is contained in:
parent
a320d062fb
commit
99d1b5fbdf
@ -457,7 +457,7 @@ export const connect = async (
|
|||||||
socket.current.on("reload", async (event) => {
|
socket.current.on("reload", async (event) => {
|
||||||
event_processing = false;
|
event_processing = false;
|
||||||
queueEvents([...initialEvents(), JSON5.parse(event)], socket);
|
queueEvents([...initialEvents(), JSON5.parse(event)], socket);
|
||||||
})
|
});
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", checkVisibility);
|
document.addEventListener("visibilitychange", checkVisibility);
|
||||||
};
|
};
|
||||||
@ -490,23 +490,30 @@ export const uploadFiles = async (
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track how many partial updates have been processed for this upload.
|
||||||
let resp_idx = 0;
|
let resp_idx = 0;
|
||||||
const eventHandler = (progressEvent) => {
|
const eventHandler = (progressEvent) => {
|
||||||
// handle any delta / event streamed from the upload event handler
|
const event_callbacks = socket._callbacks.$event;
|
||||||
|
// Whenever called, responseText will contain the entire response so far.
|
||||||
const chunks = progressEvent.event.target.responseText.trim().split("\n");
|
const chunks = progressEvent.event.target.responseText.trim().split("\n");
|
||||||
|
// So only process _new_ chunks beyond resp_idx.
|
||||||
chunks.slice(resp_idx).map((chunk) => {
|
chunks.slice(resp_idx).map((chunk) => {
|
||||||
try {
|
event_callbacks.map((f, ix) => {
|
||||||
socket._callbacks.$event.map((f) => {
|
f(chunk)
|
||||||
f(chunk);
|
.then(() => {
|
||||||
});
|
if (ix === event_callbacks.length - 1) {
|
||||||
resp_idx += 1;
|
// Mark this chunk as processed.
|
||||||
} catch (e) {
|
resp_idx += 1;
|
||||||
if (progressEvent.progress === 1) {
|
}
|
||||||
// Chunk may be incomplete, so only report errors when full response is available.
|
})
|
||||||
console.log("Error parsing chunk", chunk, e);
|
.catch((e) => {
|
||||||
}
|
if (progressEvent.progress === 1) {
|
||||||
return;
|
// Chunk may be incomplete, so only report errors when full response is available.
|
||||||
}
|
console.log("Error parsing chunk", chunk, e);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -711,7 +718,7 @@ export const useEventLoop = (
|
|||||||
const combined_name = events.map((e) => e.name).join("+++");
|
const combined_name = events.map((e) => e.name).join("+++");
|
||||||
if (event_actions?.temporal) {
|
if (event_actions?.temporal) {
|
||||||
if (!socket.current || !socket.current.connected) {
|
if (!socket.current || !socket.current.connected) {
|
||||||
return; // don't queue when the backend is not connected
|
return; // don't queue when the backend is not connected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (event_actions?.throttle) {
|
if (event_actions?.throttle) {
|
||||||
@ -852,7 +859,7 @@ export const useEventLoop = (
|
|||||||
if (router.components[router.pathname].error) {
|
if (router.components[router.pathname].error) {
|
||||||
delete router.components[router.pathname].error;
|
delete router.components[router.pathname].error;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
router.events.on("routeChangeStart", change_start);
|
router.events.on("routeChangeStart", change_start);
|
||||||
router.events.on("routeChangeComplete", change_complete);
|
router.events.on("routeChangeComplete", change_complete);
|
||||||
router.events.on("routeChangeError", change_error);
|
router.events.on("routeChangeError", change_error);
|
||||||
|
@ -293,13 +293,15 @@ class Upload(MemoizationLeaf):
|
|||||||
format.to_camel_case(key): value for key, value in upload_props.items()
|
format.to_camel_case(key): value for key, value in upload_props.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
use_dropzone_arguments = {
|
use_dropzone_arguments = Var.create(
|
||||||
"onDrop": event_var,
|
{
|
||||||
**upload_props,
|
"onDrop": event_var,
|
||||||
}
|
**upload_props,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
|
left_side = f"const {{getRootProps: {root_props_unique_name}, getInputProps: {input_props_unique_name}}} "
|
||||||
right_side = f"useDropzone({str(Var.create(use_dropzone_arguments))})"
|
right_side = f"useDropzone({str(use_dropzone_arguments)})"
|
||||||
|
|
||||||
var_data = VarData.merge(
|
var_data = VarData.merge(
|
||||||
VarData(
|
VarData(
|
||||||
@ -307,6 +309,7 @@ class Upload(MemoizationLeaf):
|
|||||||
hooks={Hooks.EVENTS: None},
|
hooks={Hooks.EVENTS: None},
|
||||||
),
|
),
|
||||||
event_var._get_all_var_data(),
|
event_var._get_all_var_data(),
|
||||||
|
use_dropzone_arguments._get_all_var_data(),
|
||||||
VarData(
|
VarData(
|
||||||
hooks={
|
hooks={
|
||||||
callback_str: None,
|
callback_str: None,
|
||||||
|
@ -19,10 +19,14 @@ def UploadFile():
|
|||||||
|
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
|
|
||||||
|
LARGE_DATA = "DUMMY" * 1024 * 512
|
||||||
|
|
||||||
class UploadState(rx.State):
|
class UploadState(rx.State):
|
||||||
_file_data: Dict[str, str] = {}
|
_file_data: Dict[str, str] = {}
|
||||||
event_order: List[str] = []
|
event_order: List[str] = []
|
||||||
progress_dicts: List[dict] = []
|
progress_dicts: List[dict] = []
|
||||||
|
disabled: bool = False
|
||||||
|
large_data: str = ""
|
||||||
|
|
||||||
async def handle_upload(self, files: List[rx.UploadFile]):
|
async def handle_upload(self, files: List[rx.UploadFile]):
|
||||||
for file in files:
|
for file in files:
|
||||||
@ -33,6 +37,7 @@ def UploadFile():
|
|||||||
for file in files:
|
for file in files:
|
||||||
upload_data = await file.read()
|
upload_data = await file.read()
|
||||||
self._file_data[file.filename or ""] = upload_data.decode("utf-8")
|
self._file_data[file.filename or ""] = upload_data.decode("utf-8")
|
||||||
|
self.large_data = LARGE_DATA
|
||||||
yield UploadState.chain_event
|
yield UploadState.chain_event
|
||||||
|
|
||||||
def upload_progress(self, progress):
|
def upload_progress(self, progress):
|
||||||
@ -41,13 +46,15 @@ def UploadFile():
|
|||||||
self.progress_dicts.append(progress)
|
self.progress_dicts.append(progress)
|
||||||
|
|
||||||
def chain_event(self):
|
def chain_event(self):
|
||||||
|
assert self.large_data == LARGE_DATA
|
||||||
|
self.large_data = ""
|
||||||
self.event_order.append("chain_event")
|
self.event_order.append("chain_event")
|
||||||
|
|
||||||
def index():
|
def index():
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
rx.input(
|
rx.input(
|
||||||
value=UploadState.router.session.client_token,
|
value=UploadState.router.session.client_token,
|
||||||
is_read_only=True,
|
read_only=True,
|
||||||
id="token",
|
id="token",
|
||||||
),
|
),
|
||||||
rx.heading("Default Upload"),
|
rx.heading("Default Upload"),
|
||||||
@ -56,6 +63,7 @@ def UploadFile():
|
|||||||
rx.button("Select File"),
|
rx.button("Select File"),
|
||||||
rx.text("Drag and drop files here or click to select files"),
|
rx.text("Drag and drop files here or click to select files"),
|
||||||
),
|
),
|
||||||
|
disabled=UploadState.disabled,
|
||||||
),
|
),
|
||||||
rx.button(
|
rx.button(
|
||||||
"Upload",
|
"Upload",
|
||||||
|
Loading…
Reference in New Issue
Block a user