-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Navigation #239
Merged
mobile-bungalow
merged 32 commits into
liveview-native:main
from
mobile-bungalow:navigation
Nov 26, 2024
Merged
Navigation #239
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
81329ff
Cargo.lock
mobile-bungalow 9c5c451
Merge branch 'main' of https://github.com/liveview-native/liveview-na…
mobile-bungalow b564457
Merge branch 'main' of https://github.com/liveview-native/liveview-na…
mobile-bungalow f242756
8 failures, removed pointer chasing
mobile-bungalow cb8fdfc
lints
mobile-bungalow 97b60eb
Merge branch 'main' of https://github.com/liveview-native/liveview-na…
mobile-bungalow ef4094f
7 failing, do not newrender in conversion
mobile-bungalow 8c653ca
clippy
mobile-bungalow cbac04c
Merge branch 'main' of https://github.com/liveview-native/liveview-na…
mobile-bungalow bac2faa
first failing navigation test
mobile-bungalow 320308f
nav files
mobile-bungalow 0be4f69
first test passing
mobile-bungalow 5a95faf
tests passing
mobile-bungalow 0671dfd
remove unnecessary state object
mobile-bungalow 63b53d4
all traversal logic implemented
mobile-bungalow 123caed
tests passing
mobile-bungalow df9df11
more thorough tests
mobile-bungalow 039c008
typo
mobile-bungalow cd8bede
clippy
mobile-bungalow d345c6f
lints
mobile-bungalow 64b9556
clippy passing, api's wrapped
mobile-bungalow 6b71560
start rollback state
mobile-bungalow cedaec0
rollback
mobile-bungalow 6e2f3e0
state rollback works, add the begginning of the actual network code
mobile-bungalow 8bf4464
actually changes page
mobile-bungalow 4260c7c
add session data
mobile-bungalow cb70a14
swift tests
mobile-bungalow 994292c
simple use case kotlin and swift
mobile-bungalow 0349d25
remove generated files
mobile-bungalow 10dc96f
remove short circuiting nav on failure
mobile-bungalow 571b3d2
code review, remove excessive indirection, bubble errors, try lock
mobile-bungalow 991340b
add lock macro
mobile-bungalow File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
//! # FFI Navigation Types | ||
//! | ||
//! Types and utilities for interacting with the navigation API for the FFI api consumers. | ||
use reqwest::Url; | ||
|
||
pub type HistoryId = u64; | ||
|
||
#[uniffi::export(callback_interface)] | ||
pub trait NavEventHandler: Send + Sync { | ||
/// This callback instruments events that occur when your user navigates to a | ||
/// new view. You can add serialized metadata to these events as a byte buffer | ||
/// through the [NavOptions] object. | ||
fn handle_event(&self, event: NavEvent) -> HandlerResponse; | ||
} | ||
|
||
#[derive(uniffi::Enum, Clone, Debug, PartialEq, Default)] | ||
pub enum HandlerResponse { | ||
#[default] | ||
Default, | ||
PreventDefault, | ||
} | ||
|
||
#[derive(uniffi::Enum, Clone, Debug, PartialEq)] | ||
pub enum NavEventType { | ||
Push, | ||
Replace, | ||
Reload, | ||
Traverse, | ||
} | ||
|
||
#[derive(uniffi::Record, Clone, Debug, PartialEq)] | ||
pub struct NavHistoryEntry { | ||
/// The target url. | ||
pub url: String, | ||
/// Unique id for this piece of nav entry state. | ||
pub id: HistoryId, | ||
/// state passed in by the user, to be passed in to the navigation event callback. | ||
pub state: Option<Vec<u8>>, | ||
} | ||
|
||
/// An event emitted when the user navigates between views. | ||
#[derive(uniffi::Record, Clone, Debug, PartialEq)] | ||
pub struct NavEvent { | ||
pub event: NavEventType, | ||
pub same_document: bool, | ||
/// The previous location of the page, if there was one | ||
pub from: Option<NavHistoryEntry>, | ||
/// Destination URL | ||
pub to: NavHistoryEntry, | ||
/// Additional user provided metadata handed to the event handler. | ||
pub info: Option<Vec<u8>>, | ||
} | ||
|
||
// An action taken with respect to the history stack | ||
// when [NavCtx::navigate] is executed. | ||
#[derive(uniffi::Enum, Default, Clone)] | ||
pub enum NavAction { | ||
/// Push the navigation event onto the history stack. | ||
#[default] | ||
Push, | ||
/// Replace the current top of the history stack with this navigation event. | ||
Replace, | ||
} | ||
|
||
/// Options for calls to [NavCtx::navigate] | ||
#[derive(Default, uniffi::Record)] | ||
pub struct NavOptions { | ||
pub action: NavAction, | ||
pub extra_event_info: Option<Vec<u8>>, | ||
pub state: Option<Vec<u8>>, | ||
} | ||
|
||
impl NavEvent { | ||
pub fn new( | ||
event: NavEventType, | ||
to: NavHistoryEntry, | ||
from: Option<NavHistoryEntry>, | ||
info: Option<Vec<u8>>, | ||
) -> Self { | ||
let new_url = Url::parse(&to.url).ok(); | ||
let old_url = from.as_ref().and_then(|dest| Url::parse(&dest.url).ok()); | ||
|
||
let same_document = old_url | ||
.zip(new_url) | ||
.is_some_and(|(old, new)| old.path() == new.path()); | ||
|
||
NavEvent { | ||
event, | ||
same_document, | ||
from, | ||
to, | ||
info, | ||
} | ||
} | ||
|
||
pub fn new_from_reload(dest: NavHistoryEntry, info: Option<Vec<u8>>) -> NavEvent { | ||
NavEvent::new(NavEventType::Reload, dest.clone(), dest.into(), info) | ||
} | ||
|
||
/// Create a new nav event from the details of a [NavCtx::traverse_to] event | ||
pub fn new_from_traverse( | ||
new_dest: NavHistoryEntry, | ||
old_dest: Option<NavHistoryEntry>, | ||
info: Option<Vec<u8>>, | ||
) -> NavEvent { | ||
NavEvent::new(NavEventType::Traverse, new_dest, old_dest, info) | ||
} | ||
|
||
/// Create a new nav event from the details of a [NavCtx::navigate] event | ||
pub fn new_from_navigate( | ||
new_dest: NavHistoryEntry, | ||
old_dest: Option<NavHistoryEntry>, | ||
opts: NavOptions, | ||
) -> NavEvent { | ||
let event = match opts.action { | ||
NavAction::Push => NavEventType::Push, | ||
NavAction::Replace => NavEventType::Replace, | ||
}; | ||
|
||
NavEvent::new(event, new_dest, old_dest, opts.extra_event_info) | ||
} | ||
|
||
/// Create a new nav event from the details of a [NavCtx::back] event, | ||
/// passing info into the event handler closure. | ||
pub fn new_from_forward( | ||
new_dest: NavHistoryEntry, | ||
old_dest: Option<NavHistoryEntry>, | ||
info: Option<Vec<u8>>, | ||
) -> NavEvent { | ||
NavEvent::new(NavEventType::Push, new_dest, old_dest, info) | ||
} | ||
|
||
/// Create a new nav event from the details of a [NavCtx::back] event, | ||
/// passing info into the event handler closure. | ||
pub fn new_from_back( | ||
new_dest: NavHistoryEntry, | ||
old_dest: NavHistoryEntry, | ||
info: Option<Vec<u8>>, | ||
) -> NavEvent { | ||
NavEvent::new(NavEventType::Push, new_dest, Some(old_dest), info) | ||
} | ||
} | ||
|
||
use super::{super::error::LiveSocketError, LiveSocket}; | ||
|
||
#[cfg_attr(not(target_family = "wasm"), uniffi::export(async_runtime = "tokio"))] | ||
impl LiveSocket { | ||
pub async fn navigate( | ||
&self, | ||
url: String, | ||
opts: NavOptions, | ||
) -> Result<Option<HistoryId>, LiveSocketError> { | ||
let url = Url::parse(&url)?; | ||
|
||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
let res = nav_ctx.navigate(url, opts); | ||
|
||
if res.is_some() { | ||
//let _ = nav_ctx.current(); | ||
//todo!("connect logic") | ||
} | ||
|
||
Ok(res) | ||
} | ||
|
||
pub async fn reload( | ||
&self, | ||
info: Option<Vec<u8>>, | ||
) -> Result<Option<HistoryId>, LiveSocketError> { | ||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
let res = nav_ctx.reload(info); | ||
if res.is_some() { | ||
if let Some(_current) = nav_ctx.current() {} | ||
} | ||
Ok(res) | ||
} | ||
|
||
pub async fn back(&self, info: Option<Vec<u8>>) -> Result<Option<HistoryId>, LiveSocketError> { | ||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
let res = nav_ctx.back(info); | ||
if res.is_some() { | ||
if let Some(_current) = nav_ctx.current() {} | ||
} | ||
Ok(res) | ||
} | ||
|
||
pub async fn forward( | ||
&self, | ||
info: Option<Vec<u8>>, | ||
) -> Result<Option<HistoryId>, LiveSocketError> { | ||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
let res = nav_ctx.forward(info); | ||
if res.is_some() { | ||
if let Some(_current) = nav_ctx.current() {} | ||
} | ||
Ok(res) | ||
} | ||
|
||
pub async fn traverse_to( | ||
&self, | ||
id: HistoryId, | ||
info: Option<Vec<u8>>, | ||
) -> Result<Option<HistoryId>, LiveSocketError> { | ||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
let res = nav_ctx.traverse_to(id, info); | ||
if res.is_some() { | ||
if let Some(_current) = nav_ctx.current() {} | ||
} | ||
Ok(res) | ||
} | ||
|
||
pub fn can_go_back(&self) -> bool { | ||
let nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.can_go_back() | ||
} | ||
|
||
pub fn can_go_forward(&self) -> bool { | ||
let nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.can_go_forward() | ||
} | ||
|
||
pub fn can_traverse_to(&self, id: HistoryId) -> bool { | ||
let nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.can_traverse_to(id) | ||
} | ||
|
||
pub fn get_entries(&self) -> Vec<NavHistoryEntry> { | ||
let nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.entries() | ||
} | ||
|
||
pub fn current(&self) -> Option<NavHistoryEntry> { | ||
let nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.current() | ||
} | ||
|
||
pub fn set_event_handler(&self, handler: Box<dyn NavEventHandler>) { | ||
let mut nav_ctx = self.navigation_ctx.lock().expect("lock poison"); | ||
nav_ctx.set_event_handler(handler.into()) | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this match the name
state
in theNavHistoryEntry
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info
andstate
are two different things, the history entry contains persistent state which will always be included whenever it shows up in a navigation event, or whenNavCtx::current
accquires it.info
is ephemeral and is passed to the event once.I based this off of the web navigation api's naming scheme which is honestly not great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
event_specific_info
might be more descriptive.