Skip to content

Commit

Permalink
Go back to example page with browser Back-button (#5750)
Browse files Browse the repository at this point in the history
### What
* Closes #5398

This uses the [History
API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to
manipulate the web history.

When leaving the example page, two entries in the history are added:

* `?examples` (shows the examples)
* `?url=https://….rrd` (the url for the example)

This is then parsed on `popstate`. We thus support both going forward
and back in history.

This is not perfect, but a lot better than before.

Best reviewed commit by commit.

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using newly built examples:
[rerun.io/viewer](https://rerun.io/viewer/pr/5750)
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/5750?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/5750?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!

- [PR Build Summary](https://build.rerun.io/pr/5750)
- [Docs
preview](https://rerun.io/preview/d308ce076b28481d77fac64217df69a452cc54a9/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/d308ce076b28481d77fac64217df69a452cc54a9/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

---------

Co-authored-by: Jeremy Leibs <[email protected]>
  • Loading branch information
emilk and jleibs authored Apr 3, 2024
1 parent af7283e commit 027e16d
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 129 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 15 additions & 12 deletions crates/re_data_source/src/web_sockets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,25 @@ pub fn connect_to_ws_url(

re_log::info!("Connecting to WebSocket server at {url:?}…");

let callback = move |binary: Vec<u8>| match re_ws_comms::decode_log_msg(&binary) {
Ok(log_msg) => {
if tx.send(log_msg).is_ok() {
if let Some(on_msg) = &on_msg {
on_msg();
let callback = {
let url = url.to_owned();
move |binary: Vec<u8>| match re_ws_comms::decode_log_msg(&binary) {
Ok(log_msg) => {
if tx.send(log_msg).is_ok() {
if let Some(on_msg) = &on_msg {
on_msg();
}
std::ops::ControlFlow::Continue(())
} else {
re_log::info_once!("Closing connection to {url}");
std::ops::ControlFlow::Break(())
}
std::ops::ControlFlow::Continue(())
} else {
re_log::info!("Failed to send log message to viewer - closing");
}
Err(err) => {
re_log::error!("Failed to parse message: {err}");
std::ops::ControlFlow::Break(())
}
}
Err(err) => {
re_log::error!("Failed to parse message: {err}");
std::ops::ControlFlow::Break(())
}
};

re_ws_comms::viewer_to_server(url.to_owned(), callback)?;
Expand Down
8 changes: 4 additions & 4 deletions crates/re_log_encoding/src/stream_rrd_from_http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn stream_rrd_from_http_to_channel(
re_smart_channel::SmartChannelSource::RrdHttpStream { url: url.clone() },
);
stream_rrd_from_http(
url,
url.clone(),
Arc::new(move |msg| {
if let Some(on_msg) = &on_msg {
on_msg();
Expand All @@ -25,17 +25,17 @@ pub fn stream_rrd_from_http_to_channel(
if tx.send(msg).is_ok() {
ControlFlow::Continue(())
} else {
re_log::info!("Failed to send log message to viewer - closing");
re_log::info_once!("Closing connection to {url}");
ControlFlow::Break(())
}
}
HttpMessage::Success => {
tx.quit(None).warn_on_err_once("failed to send quit marker");
tx.quit(None).warn_on_err_once("Failed to send quit marker");
ControlFlow::Break(())
}
HttpMessage::Failure(err) => {
tx.quit(Some(err))
.warn_on_err_once("failed to send quit marker");
.warn_on_err_once("Failed to send quit marker");
ControlFlow::Break(())
}
}
Expand Down
9 changes: 9 additions & 0 deletions crates/re_smart_channel/src/receive_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ impl<T: Send> ReceiveSet<T> {
self.receivers.lock().retain(|r| r.source() != source);
}

pub fn retain(&self, mut f: impl FnMut(&Receiver<T>) -> bool) {
self.receivers.lock().retain(|r| f(r));
}

/// Remove all receivers.
pub fn clear(&self) {
self.receivers.lock().clear();
}

/// Disconnect from any channel with a source pointing at this `uri`.
#[cfg(target_arch = "wasm32")]
pub fn remove_by_uri(&self, uri: &str) {
Expand Down
6 changes: 6 additions & 0 deletions crates/re_ui/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum UICommand {
SaveRecordingSelection,
SaveBlueprint,
CloseCurrentRecording,
CloseAllRecordings,

#[cfg(not(target_arch = "wasm32"))]
Quit,

Expand Down Expand Up @@ -111,6 +113,9 @@ impl UICommand {
"Close the current recording (unsaved data will be lost)",
),

Self::CloseAllRecordings => ("Close all recordings",
"Close all open current recording (unsaved data will be lost)",),

#[cfg(not(target_arch = "wasm32"))]
Self::Quit => ("Quit", "Close the Rerun Viewer"),

Expand Down Expand Up @@ -258,6 +263,7 @@ impl UICommand {
Self::SaveBlueprint => None,
Self::Open => Some(cmd(Key::O)),
Self::CloseCurrentRecording => None,
Self::CloseAllRecordings => None,

#[cfg(all(not(target_arch = "wasm32"), target_os = "windows"))]
Self::Quit => Some(KeyboardShortcut::new(Modifiers::ALT, Key::F4)),
Expand Down
10 changes: 6 additions & 4 deletions crates/re_viewer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,16 @@ wgpu.workspace = true

# web dependencies:
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys.workspace = true
wasm-bindgen.workspace = true
wasm-bindgen-futures.workspace = true
web-sys = { workspace = true, features = [
'Location',
'Url',
'UrlSearchParams',
'Window',
"History",
"Location",
"Storage",
"Url",
"UrlSearchParams",
"Window",
] }


Expand Down
29 changes: 29 additions & 0 deletions crates/re_viewer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,10 +339,35 @@ impl App {
SystemCommand::ActivateRecording(store_id) => {
store_hub.set_activate_recording(store_id);
}

SystemCommand::CloseStore(store_id) => {
store_hub.remove(&store_id);
}

SystemCommand::CloseAllRecordings => {
store_hub.clear_recordings();

// Stop receiving into the old recordings.
// This is most important when going back to the example screen by using the "Back"
// button in the browser, and there is still a connection downloading an .rrd.
// That's the case of `SmartChannelSource::RrdHttpStream`.
// TODO(emilk): exactly what things get kept and what gets cleared?
self.rx.retain(|r| match r.source() {
SmartChannelSource::File(_) | SmartChannelSource::RrdHttpStream { .. } => false,

SmartChannelSource::WsClient { .. }
| SmartChannelSource::RrdWebEventListener
| SmartChannelSource::Sdk
| SmartChannelSource::TcpServer { .. }
| SmartChannelSource::Stdin => true,
});
}

SystemCommand::AddReceiver(rx) => {
re_log::debug!("Received AddReceiver");
self.add_receiver(rx);
}

SystemCommand::LoadDataSource(data_source) => {
let egui_ctx = self.re_ui.egui_ctx.clone();

Expand Down Expand Up @@ -487,6 +512,10 @@ impl App {
.send_system(SystemCommand::CloseStore(cur_rec.clone()));
}
}
UICommand::CloseAllRecordings => {
self.command_sender
.send_system(SystemCommand::CloseAllRecordings);
}

#[cfg(not(target_arch = "wasm32"))]
UICommand::Quit => {
Expand Down
29 changes: 22 additions & 7 deletions crates/re_viewer/src/ui/welcome_screen/example_section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use egui::{NumExt as _, Ui};
use ehttp::{fetch, Request};
use poll_promise::Promise;

use re_viewer_context::SystemCommandSender;
use re_viewer_context::{CommandSender, SystemCommandSender as _};

#[derive(Debug, serde::Deserialize)]
struct ExampleThumbnail {
Expand Down Expand Up @@ -269,7 +269,7 @@ impl ExampleSection {
&mut self,
ui: &mut egui::Ui,
re_ui: &re_ui::ReUi,
command_sender: &re_viewer_context::CommandSender,
command_sender: &CommandSender,
header_ui: &impl Fn(&mut Ui),
) {
let examples = self
Expand Down Expand Up @@ -414,18 +414,33 @@ impl ExampleSection {
// panel to quit auto-zoom mode.
ui.input_mut(|i| i.pointer = Default::default());

let data_source =
re_data_source::DataSource::RrdHttpUrl(example.desc.rrd_url.clone());
command_sender.send_system(
re_viewer_context::SystemCommand::LoadDataSource(data_source),
);
open_example_url(command_sender, &example.desc.rrd_url);
}
}
});
});
}
}

fn open_example_url(command_sender: &CommandSender, rrd_url: &str) {
let data_source = re_data_source::DataSource::RrdHttpUrl(rrd_url.to_owned());
command_sender.send_system(re_viewer_context::SystemCommand::LoadDataSource(
data_source,
));

#[cfg(target_arch = "wasm32")]
{
// Ensure that the user returns to the welcome page after navigating to an example.
use crate::web_tools;

// So we know where to return to
web_tools::push_history("?examples");

// Where we're going:
web_tools::push_history(&format!("?url={}", web_tools::percent_encode(rrd_url)));
}
}

fn example_thumbnail(ui: &mut Ui, example: &ExampleDesc, column_width: f32) {
// dimensions of the source image to use as thumbnail
let image_width = example.thumbnail.width as f32;
Expand Down
Loading

0 comments on commit 027e16d

Please sign in to comment.