From cd2bc321cd9254205345b5723613e1b0b7a2b6da Mon Sep 17 00:00:00 2001 From: jane400 Date: Thu, 22 Aug 2024 13:19:04 +0200 Subject: [PATCH] feat: paket: sendungsverfolgung bringup --- paket/src/main.rs | 2 + paket/src/ready.rs | 172 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 169 insertions(+), 5 deletions(-) diff --git a/paket/src/main.rs b/paket/src/main.rs index 2041ce8..a6b8e50 100644 --- a/paket/src/main.rs +++ b/paket/src/main.rs @@ -13,6 +13,7 @@ mod advices; mod constants; mod login; mod ready; +mod tracking; #[derive(Debug, PartialEq)] enum AppState { @@ -282,6 +283,7 @@ fn convert_ready_response(response: ReadyOutput) -> AppInput { long: "There is no feature on your account which is supported by this app. You need the offical app and register for one or more of:\n\"Briefasnkündigung\"".to_string(), }), ReadyOutput::Error(err) => AppInput::ErrorOccoured(AppError { short: "meow".to_string(), long: err.to_string() }), + ReadyOutput::Notification(value) => AppInput::Notification(value, 60), ReadyOutput::Ready => AppInput::SwitchToReady, } } diff --git a/paket/src/ready.rs b/paket/src/ready.rs index 5717b75..f5d4377 100644 --- a/paket/src/ready.rs +++ b/paket/src/ready.rs @@ -1,14 +1,21 @@ // managed the various pages... +use std::hash::DefaultHasher; +use std::string; use std::time::Duration; use adw::prelude::*; use libpaket::{ self, advices::{AdvicesList, UatToken}, + tracking::{Shipment, TrackingParams}, LibraryError, LibraryResult, }; -use relm4::{adw, factory::FactoryVecDeque, prelude::*}; +use relm4::{ + adw, + factory::{FactoryHashMap, FactorySender, FactoryVecDeque}, + prelude::*, +}; use crate::advices::AppAdviceMetadata; @@ -25,9 +32,13 @@ pub struct Ready { login: crate::LoginSharedState, activate: bool, have_service_advices: bool, + #[do_not_track] advices_factory: FactoryVecDeque, advices_state: ReadyAdvicesState, + + #[do_not_track] + tracking_factory: FactoryHashMap, } #[derive(Debug)] @@ -35,6 +46,7 @@ pub enum ReadyOutput { Ready, Error(LibraryError), FatalError(LibraryError), + Notification(String), NoServicesEnabled, } @@ -45,6 +57,7 @@ pub enum ReadyCmds { GotCustomerDataFull(LibraryResult), RetryAdvices, GotAdvices((LibraryResult>, Option)), + GotTracking(LibraryResult>), } #[derive(Debug)] @@ -52,6 +65,8 @@ pub enum ReadyInput { Activate, Deactivate, HaveAdvicesService, + HavePaketankuendigungService, + SearchTracking(String), } #[relm4::component(pub)] @@ -112,6 +127,40 @@ impl Component for Ready { /*#[track(model.changed_have_service_advices())] set_visible: model.have_service_advices,*/ }, + + add = &adw::Bin { + #[wrap(Some)] + set_child = >k::ScrolledWindow { + #[wrap(Some)] + set_child = >k::Box { + set_orientation: gtk::Orientation::Vertical, + + gtk::Box { + set_orientation: gtk::Orientation::Horizontal, + set_margin_all: 8, + add_css_class: relm4::css::TOOLBAR, + + #[name = "tracking_entry"] + gtk::Entry { + set_input_hints: gtk::InputHints::PRIVATE, + set_hexpand: true, + }, + + #[name = "tracking_entry_button"] + gtk::Button {} + }, + + + #[local_ref] + tracking_box -> gtk::Box { + set_spacing: 8 + }, + } + } + } -> /*page_tracking: adw::ViewStackPage*/ { + set_title: Some("Shipment tracking"), + set_name: Some("page_tracking"), + }, } } @@ -120,9 +169,9 @@ impl Component for Ready { root: Self::Root, sender: ComponentSender, ) -> ComponentParts { - let advices_factory = FactoryVecDeque::builder() - .launch(adw::Carousel::new()) - .detach(); + let advices_factory = FactoryVecDeque::builder().launch_default().detach(); + + let tracking_factory = FactoryHashMap::builder().launch_default().detach(); let model = Ready { have_service_advices: false, @@ -130,10 +179,12 @@ impl Component for Ready { advices_state: ReadyAdvicesState::Loading, login: init.clone(), activate: false, + tracking_factory, tracker: 0, }; let advices_carousel = model.advices_factory.widget(); + let tracking_box = model.tracking_factory.widget(); let widgets = view_output!(); { @@ -154,6 +205,21 @@ impl Component for Ready { .drop_on_shutdown() }); } + { + let sender = sender.clone(); + widgets.tracking_entry.connect_activate(move |entry| { + sender.input(ReadyInput::SearchTracking(entry.text().into())); + entry.set_text(""); + }); + } + { + let sender = sender.clone(); + let entry = widgets.tracking_entry.clone(); + widgets.tracking_entry_button.connect_clicked(move |_| { + sender.input(ReadyInput::SearchTracking(entry.text().into())); + entry.set_text(""); + }); + } ComponentParts { model, widgets } } @@ -173,9 +239,46 @@ impl Component for Ready { }); } } + ReadyInput::SearchTracking(value) => { + sender.oneshot_command(async move { + let client = libpaket::WebClient::new(); + let mut vec = Vec::new(); + vec.push(value); + ReadyCmds::GotTracking( + client + .tracking_search( + TrackingParams { + language: Some("de".to_string()), + }, + vec, + None, + ) + .await, + ) + }); + } ReadyInput::Deactivate => { self.set_activate(false); } + ReadyInput::HavePaketankuendigungService => { + let token = self.login.clone(); + sender.oneshot_command(async move { + // fetching advices + let dhli_token = crate::login::get_id_token(&token).await.unwrap(); + let client = libpaket::WebClient::new(); + ReadyCmds::GotTracking( + client + .tracking_search( + TrackingParams { + language: Some("de".to_string()), + }, + Vec::new(), + Some(&dhli_token), + ) + .await, + ) + }); + } ReadyInput::HaveAdvicesService => { let token = self.login.clone(); sender.oneshot_command(async move { @@ -227,7 +330,9 @@ impl Component for Ready { for service in &res.common.services { match service { libpaket::stammdaten::CustomerDataService::Packstation => (), - libpaket::stammdaten::CustomerDataService::Paketankuendigung => (), + libpaket::stammdaten::CustomerDataService::Paketankuendigung => { + sender.input(ReadyInput::HavePaketankuendigungService); + } libpaket::stammdaten::CustomerDataService::PostfilialeDirekt => (), libpaket::stammdaten::CustomerDataService::Digiben => (), libpaket::stammdaten::CustomerDataService::GeraetAktiviert => (), @@ -241,6 +346,63 @@ impl Component for Ready { } Err(err) => sender.output(ReadyOutput::FatalError(err)).unwrap(), }, + ReadyCmds::GotTracking(res) => match res { + Ok(shipment_vec) => { + for item in shipment_vec { + if let Some(err) = item.error { + // TODO: gettext + if err.id_invalid { + sender + .output(ReadyOutput::Notification(format!( + "The id is invalid ({})", + item.id + ))) + .unwrap(); + } else if err.letter_not_found { + sender + .output(ReadyOutput::Notification(format!( + "The letter wasn't found ({})", + item.id + ))) + .unwrap(); + } else if err.id_not_searchable { + sender + .output(ReadyOutput::Notification(format!( + "The id is not searchable ({})", + item.id + ))) + .unwrap(); + } else if err.data_to_old { + sender + .output(ReadyOutput::Notification(format!( + "No data available with id ({}) (data expired)", + item.id + ))) + .unwrap(); + } else if err.not_from_dhl { + sender + .output(ReadyOutput::Notification(format!( + "The id is not from DHL ({})", + item.id + ))) + .unwrap(); + } else if err.no_data_available { + sender + .output(ReadyOutput::Notification(format!( + "No data available with id ({})", + item.id + ))) + .unwrap(); + } + } else { + self.tracking_factory.insert(item.id.clone(), item); + } + } + } + Err(err) => { + sender.output(ReadyOutput::Error(err)).unwrap(); + } + }, ReadyCmds::GotAdvices(res) => match res.0 { Ok(advices_vec) => { {