Created
November 29, 2024 09:19
-
-
Save alper/90cdb323657752008da8614e378357d8 to your computer and use it in GitHub Desktop.
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
#![allow(non_snake_case)] | |
use anyhow::Result; | |
use dioxus::prelude::*; | |
use dioxus_logger::tracing::{error, info, Level}; | |
use dioxus_sdk::geolocation::core::Geocoordinates; | |
use dioxus_sdk::geolocation::{init_geolocator, use_geolocation, PowerMode}; | |
use futures::TryFutureExt; | |
use geoutils::Location; | |
use serde::{de, Deserialize, Serialize}; | |
use serde_json::Value; | |
use std::num::{NonZeroI16, NonZeroI32}; | |
use std::rc::Rc; | |
pub static CUPPINGS_URL: &str = "https://staging.cuppin.gs/venues/json/"; | |
pub async fn get_venues() -> Result<Vec<VenueItem>, reqwest::Error> { | |
reqwest::get(CUPPINGS_URL).await?.json().await | |
} | |
#[derive(Clone, Debug, Deserialize, Serialize)] | |
pub struct GoogleData { | |
pub formattedAddress: String, | |
} | |
fn google_formatted_address_deserializer<'de, D>(deserializer: D) -> Result<String, D::Error> | |
where | |
D: serde::Deserializer<'de>, | |
{ | |
let s = String::deserialize(deserializer); | |
match s { | |
Ok(s) => { | |
let v = serde_json::from_str::<Value>(&s); | |
match v { | |
Ok(v) => { | |
let addr = v | |
.get("formatted_address") | |
.unwrap_or(&Value::String("".to_string())) | |
.as_str() | |
.unwrap_or_default() | |
.to_string(); | |
return Ok(addr); | |
} | |
Err(e) => { | |
error!("Error deserializing google_data, {}", e); | |
return Err(de::Error::custom("Error deserializing google_data")); | |
} | |
} | |
} | |
Err(e) => { | |
error!("Nothing found in google_data field, {}", e); | |
return Err(e); | |
} | |
} | |
} | |
#[derive(Clone, Debug, Serialize, Deserialize)] | |
pub struct VenueItem { | |
// pub id: i64, | |
pub google_name: String, | |
pub state: String, | |
pub lat: f64, | |
pub lng: f64, | |
pub id: usize, | |
#[serde( | |
alias = "google_data", | |
deserialize_with = "google_formatted_address_deserializer" | |
)] | |
pub formatted_address: String, | |
} | |
impl PartialEq for VenueItem { | |
fn eq(&self, other: &Self) -> bool { | |
self.id == other.id | |
} | |
} | |
impl Eq for VenueItem {} | |
impl Ord for VenueItem { | |
fn cmp(&self, other: &Self) -> std::cmp::Ordering { | |
self.id.cmp(&other.id) | |
} | |
} | |
impl PartialOrd for VenueItem { | |
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | |
Some(self.cmp(other)) | |
} | |
} | |
#[derive(Clone, Routable, Debug)] | |
enum Route { | |
#[route("/")] | |
Home {}, | |
#[route("/map")] | |
Map {}, | |
} | |
fn main() { | |
info!("In main"); | |
dioxus_logger::init(Level::INFO).expect("failed to init logger"); | |
launch(|| { | |
rsx! { | |
Router::<Route> {} | |
} | |
}); | |
} | |
pub fn Map() -> Element { | |
rsx! { | |
p {"Map view coming soon"} | |
} | |
} | |
pub fn Home() -> Element { | |
info!("Home loaded"); | |
let geolocator = init_geolocator(PowerMode::High); | |
let coords = use_resource(move || async move { | |
// TODO collapsing all errors into None for now | |
match geolocator.read().as_ref() { | |
Ok(g) => match g.get_coordinates().await { | |
Ok(c) => Some(c), | |
Err(e) => { | |
error!("Could not get coordinates {}", e); | |
return None; | |
} | |
}, | |
Err(e) => { | |
error!("Could not get geocoder {}", e); | |
return None; | |
} | |
} | |
}); | |
let latest_coords = use_geolocation(); // TODO remove this | |
let venues_server = use_resource(move || get_venues().map_err(Rc::new)); | |
let sorted_venues = use_memo(move || { | |
info!("Sorting venues with respect to coord {:?}", coords()); | |
let venues = venues_server.read().clone(); | |
match venues { | |
Some(venues) => match venues { | |
Ok(mut venues) => { | |
info!("Venue count {}", venues.len()); | |
match coords() { | |
Some(Some(coord)) => { | |
venues.sort_by(|a, b| { | |
let loc = Location::new(coord.latitude, coord.longitude); | |
let a_dist = | |
loc.haversine_distance_to(&Location::new(a.lat, a.lng)); | |
let b_dist = | |
loc.haversine_distance_to(&Location::new(b.lat, b.lng)); | |
a_dist.meters().partial_cmp(&b_dist.meters()).unwrap() | |
}); | |
} | |
Some(None) => { | |
// Do nothing with the venues array | |
} | |
None => { | |
// Do nothing with the venues array | |
} | |
} | |
return venues; | |
} | |
Err(e) => { | |
info!("Error fetching venues {:?}", e); | |
return vec![]; | |
} | |
}, | |
None => { | |
info!("No venues found"); | |
return vec![]; | |
} | |
} | |
}); | |
// let current_location = match coords() { | |
// Some(Some(loc)) => Some(Location::new(loc.latitude, loc.longitude)), | |
// Some(None) => None, | |
// None => None, | |
// }; | |
// info!("Location {:?}", current_location); | |
rsx! { | |
head { | |
title { "Cuppings" } | |
script { src: "https://cdn.tailwindcss.com" } | |
} | |
// p { class: "text-3xl font-bold underline", if let Ok(coords) = latest_coords {"Latitude: {coords.latitude} | Longitude: {coords.longitude}"} } | |
div { class: "bg-black text-white", | |
for venue in sorted_venues() { | |
// { info!("{:?}", venue.clone()); } | |
VenueRow { venue: venue, location: coords.value() } | |
} | |
} | |
} | |
} | |
#[component] | |
fn VenueRow(venue: VenueItem, location: ReadOnlySignal<Option<Option<Geocoordinates>>>) -> Element { | |
let VenueItem { | |
google_name, | |
state, | |
lat, | |
lng, | |
id, | |
formatted_address, | |
} = venue; | |
let distance = match location() { | |
Some(Some(loc)) => { | |
let venue_loc = Location::new(lat, lng); | |
let distance = | |
venue_loc.haversine_distance_to(&Location::new(loc.latitude, loc.longitude)); | |
distance.meters() | |
} | |
Some(None) => 0.0, | |
None => 0.0, | |
}; | |
let navigator = use_navigator(); | |
rsx! { | |
div { onclick: move |event| { | |
info!("clicked"); | |
navigator.push(Route::Map {}); | |
}, padding: "0.5rem", position: "relative", | |
div { class: "border-b border-slate-700", font_size: "1.5rem", | |
if state == "PICK" { | |
p { class: "text-red-700", "STAFF PICK" } | |
} | |
p { "{google_name}" } | |
p { class:"whitespace-nowrap overflow-clip", "{distance} m {formatted_address}" } | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment