Skip to content

Commit

Permalink
web: Download the file on save dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
kjarosh committed Apr 30, 2024
1 parent cca6c13 commit c65bd0f
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 22 deletions.
3 changes: 2 additions & 1 deletion web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ features = [
"ChannelMergerNode", "ChannelSplitterNode", "ClipboardEvent", "DataTransfer", "Element", "Event",
"EventTarget", "GainNode", "Headers", "HtmlCanvasElement", "HtmlDocument", "HtmlElement", "HtmlFormElement",
"HtmlInputElement", "HtmlTextAreaElement", "KeyboardEvent", "Location", "PointerEvent",
"Request", "RequestInit", "Response", "Storage", "WheelEvent", "Window", "ReadableStream", "RequestCredentials"
"Request", "RequestInit", "Response", "Storage", "WheelEvent", "Window", "ReadableStream", "RequestCredentials",
"Url",
]

[package.metadata.cargo-machete]
Expand Down
114 changes: 93 additions & 21 deletions web/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,15 @@ use ruffle_core::backend::ui::{
};
use ruffle_web_common::JsResult;
use std::borrow::Cow;
use std::sync::{Mutex, MutexGuard};
use url::Url;
use wasm_bindgen::JsCast;
use web_sys::{HtmlCanvasElement, HtmlDocument, HtmlTextAreaElement};
use web_sys::{
Blob, HtmlCanvasElement, HtmlDocument, HtmlElement, HtmlTextAreaElement, Url as JsUrl,
};

use chrono::{DateTime, Utc};
use js_sys::Uint8Array;

#[allow(dead_code)]
#[derive(Debug)]
Expand Down Expand Up @@ -116,6 +120,91 @@ impl FileDialogResult for WebFileDialogResult {
fn refresh(&mut self) {}
}

pub struct WebDownloadFileData {
file_name: String,
contents: Vec<u8>,
downloaded: bool,
}

pub struct WebDownloadFileDialogResult {
data: Mutex<WebDownloadFileData>,
}

impl WebDownloadFileDialogResult {
fn new(file_name: String) -> WebDownloadFileDialogResult {
Self {
data: Mutex::new(WebDownloadFileData {
file_name,
contents: Vec::new(),
downloaded: false,
}),
}
}

fn data(&self) -> MutexGuard<'_, WebDownloadFileData> {
self.data.lock().expect("non-poisoned data")
}

fn download(mut data: MutexGuard<WebDownloadFileData>) -> Option<()> {
let array = Uint8Array::from(&data.contents[..]);
let blob = Blob::new_with_u8_array_sequence(&array).ok()?;
let window = web_sys::window()?;
let document = window.document()?;
let a = document.create_element("a").ok()?;

let url = JsUrl::create_object_url_with_blob(&blob).ok()?;
a.set_attribute("href", &url).ok()?;
a.set_attribute("download", &data.file_name).ok()?;
a.dyn_into::<HtmlElement>().ok()?.click();
data.downloaded = true;
Some(())
}
}

impl FileDialogResult for WebDownloadFileDialogResult {
fn is_cancelled(&self) -> bool {
self.data().downloaded
}

fn creation_time(&self) -> Option<DateTime<Utc>> {
None
}

fn modification_time(&self) -> Option<DateTime<Utc>> {
None
}

fn file_name(&self) -> Option<String> {
Some(self.data().file_name.clone())
}

fn size(&self) -> Option<u64> {
Some(self.data().contents.len() as u64)
}

fn file_type(&self) -> Option<String> {
None
}

fn creator(&self) -> Option<String> {
None
}

fn contents(&self) -> &[u8] {
tracing::warn!("Reading downloaded file not implemented");
&[]
}

fn write(&self, to_write: &[u8]) {
let mut data = self.data();
data.contents = to_write.to_vec();

Self::download(data);
}

fn refresh(&mut self) {}
}

/// An implementation of `UiBackend` utilizing `web_sys` bindings to input APIs.
pub struct WebUiBackend {
js_player: JavascriptPlayer,
Expand Down Expand Up @@ -312,30 +401,13 @@ impl UiBackend for WebUiBackend {

fn display_file_save_dialog(
&mut self,
_file_name: String,
file_name: String,
_title: String,
) -> Option<DialogResultFuture> {
None
/* Temporary disabled while #14949 is being fixed
// Prevent opening multiple dialogs at the same time
if self.dialog_open {
return None;
}
self.dialog_open = true;
// Create the dialog future
Some(Box::pin(async move {
// Select the location to save the file to
let dialog = AsyncFileDialog::new()
.set_title(&title)
.set_file_name(&file_name);
let result: Result<Box<dyn FileDialogResult>, DialogLoaderError> = Ok(Box::new(
WebFileDialogResult::new(dialog.save_file().await).await,
));
let result: Result<Box<dyn FileDialogResult>, DialogLoaderError> =
Ok(Box::new(WebDownloadFileDialogResult::new(file_name)));
result
}))
*/
}
}

0 comments on commit c65bd0f

Please sign in to comment.