feat: allow reactive updates of UI with inserted file names
This commit is contained in:
1 parent
1bccecdc72
commit
6f16bbe311
2 files changed
+31
-30
No files matched your search
+13
-20
@@ -18,12 +18,12 @@ pub fn FileDrop(#[prop(into)] name: String, #[prop(into)] form_id: String) -> im
|
|||||||
let form_id = StoredValue::new(form_id);
|
let form_id = StoredValue::new(form_id);
|
||||||
let auth = expect_context::<AuthSignal>();
|
let auth = expect_context::<AuthSignal>();
|
||||||
|
|
||||||
let upload_action = StoredValue::new(Action::new(|form_data: &FormData| {
|
let upload_action = Action::new(|form_data: &FormData| {
|
||||||
let form_data = form_data.clone();
|
let form_data = form_data.clone();
|
||||||
async move { upload_file(form_data.into()).await }
|
async move { upload_file(form_data.into()).await }
|
||||||
}));
|
});
|
||||||
|
|
||||||
let uploading = upload_action.get_value().pending();
|
let uploading = upload_action.pending();
|
||||||
|
|
||||||
let upload_files = Callback::new(move |files: Vec<File>| {
|
let upload_files = Callback::new(move |files: Vec<File>| {
|
||||||
let form_data = FormData::new().unwrap();
|
let form_data = FormData::new().unwrap();
|
||||||
@@ -52,7 +52,7 @@ pub fn FileDrop(#[prop(into)] name: String, #[prop(into)] form_id: String) -> im
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upload file to server.
|
// Upload file to server.
|
||||||
upload_action.get_value().dispatch(form_data);
|
upload_action.dispatch(form_data);
|
||||||
});
|
});
|
||||||
|
|
||||||
let UseDropZoneReturn {
|
let UseDropZoneReturn {
|
||||||
@@ -64,11 +64,6 @@ pub fn FileDrop(#[prop(into)] name: String, #[prop(into)] form_id: String) -> im
|
|||||||
.on_drop(move |ev| {
|
.on_drop(move |ev| {
|
||||||
let file = ev.files.first().unwrap();
|
let file = ev.files.first().unwrap();
|
||||||
|
|
||||||
// Update file list
|
|
||||||
file_name_list.update(|list| {
|
|
||||||
list.push(file.name());
|
|
||||||
});
|
|
||||||
|
|
||||||
upload_files.run(vec![file.to_owned()]);
|
upload_files.run(vec![file.to_owned()]);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -88,20 +83,18 @@ pub fn FileDrop(#[prop(into)] name: String, #[prop(into)] form_id: String) -> im
|
|||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
file_name_list.update({
|
|
||||||
let files = files.clone();
|
|
||||||
|list| {
|
|
||||||
for file in files {
|
|
||||||
if !list.iter().any(|f| *f == file.name()) {
|
|
||||||
list.push(file.name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
upload_files.run(files);
|
upload_files.run(files);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add file names to the list when upload_action is successful.
|
||||||
|
Effect::new(move || {
|
||||||
|
if let Some(Ok(uploaded_file_name)) = upload_action.value().get() {
|
||||||
|
file_name_list.update(|list| {
|
||||||
|
list.push(uploaded_file_name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// let on_click_file_delete = move |file_name, form_id| {
|
// let on_click_file_delete = move |file_name, form_id| {
|
||||||
//
|
//
|
||||||
// };
|
// };
|
||||||
|
|||||||
+18
-10
@@ -3,7 +3,7 @@ use leptos::prelude::*;
|
|||||||
use server_fn::codec::{MultipartData, MultipartFormData};
|
use server_fn::codec::{MultipartData, MultipartFormData};
|
||||||
|
|
||||||
#[server(input = MultipartFormData)]
|
#[server(input = MultipartFormData)]
|
||||||
pub async fn upload_file(data: MultipartData) -> Result<(), ServerFnError> {
|
pub async fn upload_file(data: MultipartData) -> Result<String, ServerFnError> {
|
||||||
use super::{MAIN_TABLE_NAME, S3_BUCKET_NAME};
|
use super::{MAIN_TABLE_NAME, S3_BUCKET_NAME};
|
||||||
use crate::pages::api::tokens::validate_and_get_token_info;
|
use crate::pages::api::tokens::validate_and_get_token_info;
|
||||||
use crate::utils::server::create_aws_config;
|
use crate::utils::server::create_aws_config;
|
||||||
@@ -89,31 +89,39 @@ pub async fn upload_file(data: MultipartData) -> Result<(), ServerFnError> {
|
|||||||
"SK".to_string(),
|
"SK".to_string(),
|
||||||
AttributeValue::S(format!("FILE#{form_id}#{input_name}")),
|
AttributeValue::S(format!("FILE#{form_id}#{input_name}")),
|
||||||
)
|
)
|
||||||
.item("file_name".to_string(), AttributeValue::S(file_name))
|
.item(
|
||||||
|
"file_name".to_string(),
|
||||||
|
AttributeValue::S(file_name.clone()),
|
||||||
|
)
|
||||||
.item("file_key".to_string(), AttributeValue::S(key.clone()))
|
.item("file_key".to_string(), AttributeValue::S(key.clone()))
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
.map_err(|e| {
|
||||||
.map_err(|e| ServerFnError::new(format!("Couldn't put file to Dynamo: {}", e.to_string())));
|
ServerFnError::new(format!(
|
||||||
|
"Couldn't put file to Dynamo, file rolled back: {}",
|
||||||
|
e.to_string()
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
if let Err(_) = dynamo_result {
|
// If Dynamo fails, we want to handle the error by rolling back the file. Then, we return a failure.
|
||||||
// Roll back file insertion to S3. Only attempted on Dynamo insertion failure.
|
if let Err(err) = dynamo_result {
|
||||||
s3_client
|
s3_client
|
||||||
.delete_object()
|
.delete_object()
|
||||||
.bucket("leptos-scholarships")
|
.bucket("leptos-scholarships")
|
||||||
.key(&key)
|
.key(&key)
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map(|_| ())
|
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ServerFnError::new(format!(
|
ServerFnError::new(format!(
|
||||||
"Couldn't roll back S3 operation: {}",
|
"Couldn't roll back S3 operation: {}",
|
||||||
e.to_string()
|
e.to_string()
|
||||||
))
|
))
|
||||||
})
|
})?;
|
||||||
} else {
|
|
||||||
dynamo_result
|
return Err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(file_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
|
|||||||
Reference in new issue
Block a user