Compare commits
9 commits
b7ca6d9b27
...
02b129fb84
Author | SHA1 | Date | |
---|---|---|---|
|
02b129fb84 | ||
|
e2ff1594f8 | ||
|
0dc60250e6 | ||
|
6fbdc6193c | ||
|
ebde744290 | ||
|
ef63f18a77 | ||
|
b222695c16 | ||
|
5b205ce1e0 | ||
|
ec5896df52 |
9 changed files with 190 additions and 140 deletions
|
@ -1,3 +1,3 @@
|
||||||
app_id = "de.j4ne.Paket"
|
app_id = "de.j4ne.Paket"
|
||||||
|
|
||||||
icons = ["plus", "minus"]
|
icons = ["plus", "minus", "package-x-generic", "mail"]
|
|
@ -15,6 +15,14 @@ pub fn device_string() -> &'static str {
|
||||||
"OnePlus 6T Build/RQ3A.211001.001"
|
"OnePlus 6T Build/RQ3A.211001.001"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn okhttp_user_agent() -> String {
|
||||||
|
format!(
|
||||||
|
"okhttp/4.11.0 Post & DHL/{} ({})",
|
||||||
|
app_version(),
|
||||||
|
linux_android_version()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn web_user_agent() -> String {
|
pub fn web_user_agent() -> String {
|
||||||
format!("Mozilla/5.0 ({}; {}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/117.0.0.0 Mobile Safari/537.36",
|
format!("Mozilla/5.0 ({}; {}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/117.0.0.0 Mobile Safari/537.36",
|
||||||
linux_android_version(), device_string())
|
linux_android_version(), device_string())
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use reqwest::{header::HeaderMap, Request, RequestBuilder};
|
use reqwest::{header::HeaderMap, Request, RequestBuilder};
|
||||||
|
|
||||||
use crate::www::authorized_credentials;
|
|
||||||
use crate::constants::{app_version, linux_android_version};
|
|
||||||
use crate::common::APIResult;
|
use crate::common::APIResult;
|
||||||
|
use crate::constants::okhttp_user_agent;
|
||||||
|
use crate::www::authorized_credentials;
|
||||||
use crate::{login::DHLIdToken, LibraryResult};
|
use crate::{login::DHLIdToken, LibraryResult};
|
||||||
|
|
||||||
pub struct StammdatenClient {
|
pub struct StammdatenClient {
|
||||||
|
@ -15,18 +15,19 @@ impl StammdatenClient {
|
||||||
StammdatenClient {
|
StammdatenClient {
|
||||||
client: reqwest::ClientBuilder::new()
|
client: reqwest::ClientBuilder::new()
|
||||||
.default_headers(headers())
|
.default_headers(headers())
|
||||||
.user_agent(user_agent())
|
.user_agent(okhttp_user_agent())
|
||||||
.build()
|
.build()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn base_request(&self, request_builder: RequestBuilder, dhli: &DHLIdToken) -> Request {
|
pub(crate) fn base_request(
|
||||||
|
&self,
|
||||||
|
request_builder: RequestBuilder,
|
||||||
|
dhli: &DHLIdToken,
|
||||||
|
) -> Request {
|
||||||
request_builder
|
request_builder
|
||||||
.basic_auth(
|
.basic_auth(authorized_credentials().0, Some(authorized_credentials().1))
|
||||||
authorized_credentials().0,
|
|
||||||
Some(authorized_credentials().1),
|
|
||||||
)
|
|
||||||
.headers(headers())
|
.headers(headers())
|
||||||
.header("cookie", format!("dhli={}", dhli.as_str()))
|
.header("cookie", format!("dhli={}", dhli.as_str()))
|
||||||
.build()
|
.build()
|
||||||
|
@ -68,16 +69,6 @@ impl StammdatenClient {
|
||||||
Err(err) => Err(err.into()),
|
Err(err) => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn user_agent() -> String {
|
|
||||||
format!(
|
|
||||||
"okhttp/4.11.0 Post & DHL/{} ({})",
|
|
||||||
app_version(),
|
|
||||||
linux_android_version()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn headers() -> HeaderMap {
|
fn headers() -> HeaderMap {
|
||||||
|
@ -144,9 +135,13 @@ pub enum CustomerDataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug)]
|
#[derive(serde::Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CustomerDataFull {
|
pub struct CustomerDataFull {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub common: CustomerData,
|
pub common: CustomerData,
|
||||||
|
|
||||||
|
pub requested_services: Option<Vec<CustomerDataService>>,
|
||||||
|
//pub customer_actions: Option,
|
||||||
|
|
||||||
pub address: CustomerAddress,
|
pub address: CustomerAddress,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{login::DHLIdToken, LibraryResult};
|
use crate::{login::DHLIdToken, LibraryError, LibraryResult};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TrackingParams {
|
pub struct TrackingParams {
|
||||||
|
@ -32,15 +32,16 @@ impl crate::WebClient {
|
||||||
endpoint_data_search,
|
endpoint_data_search,
|
||||||
query(&query_parameters_data_search(¶ms, ids)),
|
query(&query_parameters_data_search(¶ms, ids)),
|
||||||
header(api_key.0, api_key.1),
|
header(api_key.0, api_key.1),
|
||||||
header("Cookie", cookie_value)
|
header("cookie", cookie_value),
|
||||||
|
header("x-requested-with", "de.dhl.paket"),
|
||||||
|
header("sec-ch-ua-platform", "\"Android\""),
|
||||||
|
header("sec-ch-ua", r#""Chromium";v="122", "Not(A:Brand";v="24", "Android WebView";v="122""#),
|
||||||
|
header("sec-ch-ua-mobile", "?1"),
|
||||||
|
header("content-type", "application/json"),
|
||||||
|
header("accept", "application/json")
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
request!(
|
return Err(LibraryError::InvalidArgument("only supported with a logged-in session".to_string()));
|
||||||
self.web_client,
|
|
||||||
endpoint_data_search,
|
|
||||||
query(&query_parameters_data_search(¶ms, ids)),
|
|
||||||
header(api_key.0, api_key.1)
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = parse_json_response!(res, Response);
|
let resp = parse_json_response!(res, Response);
|
||||||
|
@ -414,13 +415,17 @@ fn query_parameters_data_search(
|
||||||
params: &TrackingParams,
|
params: &TrackingParams,
|
||||||
mut shippingnumbers: Vec<String>,
|
mut shippingnumbers: Vec<String>,
|
||||||
) -> Vec<(String, String)> {
|
) -> Vec<(String, String)> {
|
||||||
let mut out = vec![("noRedirect".to_string(), "true".to_string())];
|
let mut out = vec![
|
||||||
|
("noRedirect".to_string(), "true".to_string()),
|
||||||
|
("cid".to_string(), "app".to_string()),
|
||||||
|
];
|
||||||
|
|
||||||
if let Some(lang) = params.language.as_ref() {
|
if let Some(lang) = params.language.as_ref() {
|
||||||
out.push(("language".to_string(), lang.clone()));
|
out.push(("language".to_string(), lang.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if shippingnumbers.len() > 0 {
|
if shippingnumbers.len() > 0 {
|
||||||
|
out.push(("inputSearch".to_string(), "true".to_string()));
|
||||||
let mut shippingnumbers_string = shippingnumbers.pop().unwrap();
|
let mut shippingnumbers_string = shippingnumbers.pop().unwrap();
|
||||||
for number in shippingnumbers {
|
for number in shippingnumbers {
|
||||||
shippingnumbers_string = format!(",{}", number);
|
shippingnumbers_string = format!(",{}", number);
|
||||||
|
|
|
@ -26,6 +26,13 @@ impl CookieHeaderValueBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_key_value(mut self, key: String, value: String) -> Self {
|
||||||
|
self.list
|
||||||
|
.push((key, value));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_string(self) -> String {
|
pub fn build_string(self) -> String {
|
||||||
let name_value = self
|
let name_value = self
|
||||||
.list
|
.list
|
||||||
|
@ -179,15 +186,23 @@ macro_rules! request_json {
|
||||||
|
|
||||||
macro_rules! parse_json_response {
|
macro_rules! parse_json_response {
|
||||||
($res: expr, $type: ty) => {{
|
($res: expr, $type: ty) => {{
|
||||||
let res = $res.text().await.unwrap();
|
let status = $res.status();
|
||||||
|
let res: String = $res.text().await.unwrap();
|
||||||
|
// Catch HTML Response early
|
||||||
|
if status == 200 {
|
||||||
|
let res = res.clone();
|
||||||
|
let res = res.trim();
|
||||||
|
if res.starts_with("<!DOCTYPE html>") {
|
||||||
|
println!("got html, exptected json with type {}", stringify!($type));
|
||||||
|
return Err(crate::LibraryError::APIChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
let jd = &mut serde_json::Deserializer::from_str(res.as_str());
|
let jd = &mut serde_json::Deserializer::from_str(res.as_str());
|
||||||
let mut unused = std::collections::BTreeSet::new();
|
let mut unused = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
println!("res({}): {}", stringify!($type), res);
|
|
||||||
let res: Result<$type, _> = serde_ignored::deserialize(jd, |path| {
|
let res: Result<$type, _> = serde_ignored::deserialize(jd, |path| {
|
||||||
unused.insert(path.to_string());
|
unused.insert(path.to_string());
|
||||||
});
|
});
|
||||||
println!("res({}): {:?}", stringify!($type), unused);
|
|
||||||
|
|
||||||
let res: $type = match res {
|
let res: $type = match res {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
|
@ -200,15 +215,23 @@ macro_rules! parse_json_response {
|
||||||
|
|
||||||
macro_rules! parse_json_response_from_apiresult {
|
macro_rules! parse_json_response_from_apiresult {
|
||||||
($res: expr, $type: ty) => {{
|
($res: expr, $type: ty) => {{
|
||||||
let res = $res.text().await.unwrap();
|
let status = $res.status();
|
||||||
|
let res: String = $res.text().await.unwrap();
|
||||||
|
// Catch HTML Response early
|
||||||
|
if status == 200 {
|
||||||
|
let res = res.clone();
|
||||||
|
let res = res.trim();
|
||||||
|
if res.starts_with("<!DOCTYPE html>") {
|
||||||
|
println!("got html, exptected json with type {}", stringify!($type));
|
||||||
|
return Err(crate::LibraryError::APIChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
let jd = &mut serde_json::Deserializer::from_str(res.as_str());
|
let jd = &mut serde_json::Deserializer::from_str(res.as_str());
|
||||||
let mut unused = std::collections::BTreeSet::new();
|
let mut unused = std::collections::BTreeSet::new();
|
||||||
|
|
||||||
println!("res({}): {}", stringify!($type), res);
|
|
||||||
let res: Result<APIResult<$type>, _> = serde_ignored::deserialize(jd, |path| {
|
let res: Result<APIResult<$type>, _> = serde_ignored::deserialize(jd, |path| {
|
||||||
unused.insert(path.to_string());
|
unused.insert(path.to_string());
|
||||||
});
|
});
|
||||||
println!("res({}): {:?}", stringify!($type), unused);
|
|
||||||
|
|
||||||
let res: LibraryResult<$type> = match res {
|
let res: LibraryResult<$type> = match res {
|
||||||
Ok(res) => res.into(),
|
Ok(res) => res.into(),
|
||||||
|
|
|
@ -6,9 +6,11 @@ license.workspace = true
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
relm4 = { version = "0.9" , features = [ "libadwaita", "macros" ] }
|
# using git version, for https://github.com/Relm4/Relm4/pull/677
|
||||||
relm4-components = { version = "0.9" }
|
relm4 = { version = "0.9", features = [ "libadwaita", "macros" ], git = "https://github.com/Relm4/Relm4.git" }
|
||||||
relm4-macros = { version = "0.9" }
|
relm4-components = { version = "0.9", git = "https://github.com/Relm4/Relm4.git" }
|
||||||
|
relm4-macros = { version = "0.9", git = "https://github.com/Relm4/Relm4.git" }
|
||||||
|
relm4-icons = { version = "0.9", git = "https://github.com/Relm4/icons.git" }
|
||||||
tracker = "0.2"
|
tracker = "0.2"
|
||||||
adw = {package = "libadwaita", version = "0.7", features = [ "v1_5" ]}
|
adw = {package = "libadwaita", version = "0.7", features = [ "v1_5" ]}
|
||||||
webkit = { package = "webkit6", version = "0.4" }
|
webkit = { package = "webkit6", version = "0.4" }
|
||||||
|
@ -16,6 +18,5 @@ reqwest = "0.12"
|
||||||
libpaket = { path = "../libpaket" }
|
libpaket = { path = "../libpaket" }
|
||||||
glycin = { version = "2.0.0-beta", features = ["gdk4"] }
|
glycin = { version = "2.0.0-beta", features = ["gdk4"] }
|
||||||
oo7 = { version = "0.3" }
|
oo7 = { version = "0.3" }
|
||||||
relm4-icons = { version = "0.9" }
|
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
gtk = { package = "gtk4", version = "0.9", features = ["v4_16"]}
|
gtk = { package = "gtk4", version = "0.9", features = ["v4_16"]}
|
|
@ -1,15 +1,8 @@
|
||||||
use std::sync::Arc;
|
use adw::{self, glib, prelude::*};
|
||||||
|
use gtk;
|
||||||
use paket::login::{new_login_shared_state, Login, LoginOutput};
|
use paket::login::{new_login_shared_state, Login, LoginOutput};
|
||||||
use paket::ready::{Ready, ReadyOutput};
|
use paket::ready::{Ready, ReadyOutput};
|
||||||
use relm4::{
|
use relm4::{main_adw_application, prelude::*, AsyncComponentSender, RELM_THREADS};
|
||||||
RELM_THREADS,
|
|
||||||
adw, gtk, main_adw_application, prelude::*, tokio::sync::Mutex,
|
|
||||||
AsyncComponentSender, SharedState,
|
|
||||||
};
|
|
||||||
use gtk::prelude::*;
|
|
||||||
use adw::{glib, prelude::*};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum AppState {
|
enum AppState {
|
||||||
|
@ -241,7 +234,13 @@ impl AsyncComponent for App {
|
||||||
}
|
}
|
||||||
AppInput::FatalErrorOccoured(error) => {
|
AppInput::FatalErrorOccoured(error) => {
|
||||||
widgets.fatal_status_page.set_title(&error.short);
|
widgets.fatal_status_page.set_title(&error.short);
|
||||||
widgets.fatal_status_page.set_description(Some(format!("{}\nThis error is fatal, the app can't continue.", &error.long).as_str()));
|
widgets.fatal_status_page.set_description(Some(
|
||||||
|
format!(
|
||||||
|
"{}\nThis error is fatal, the app can't continue.",
|
||||||
|
&error.long
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
));
|
||||||
self.set_state(AppState::FatalError);
|
self.set_state(AppState::FatalError);
|
||||||
}
|
}
|
||||||
AppInput::SwitchToReady => {
|
AppInput::SwitchToReady => {
|
||||||
|
@ -292,6 +291,8 @@ fn main() {
|
||||||
theme.add_resource_path("/de/j4ne/Paket/icons/");
|
theme.add_resource_path("/de/j4ne/Paket/icons/");
|
||||||
theme.add_resource_path("/de/j4ne/Paket/scalable/actions/");
|
theme.add_resource_path("/de/j4ne/Paket/scalable/actions/");
|
||||||
relm4_icons::initialize_icons();
|
relm4_icons::initialize_icons();
|
||||||
|
|
||||||
let app = RelmApp::new(paket::constants::APP_ID);
|
let app = RelmApp::new(paket::constants::APP_ID);
|
||||||
|
|
||||||
app.run_async::<App>(());
|
app.run_async::<App>(());
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,11 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use gtk::prelude::*;
|
|
||||||
use libpaket::{
|
use libpaket::{
|
||||||
login::{create_nonce, CodeVerfier, DHLIdToken, RefreshToken, TokenResponse},
|
login::{create_nonce, CodeVerfier, DHLIdToken, RefreshToken, TokenResponse},
|
||||||
LibraryError, LibraryResult, OpenIdClient,
|
LibraryError, LibraryResult, OpenIdClient,
|
||||||
};
|
};
|
||||||
use relm4::{
|
use relm4::{
|
||||||
adw, gtk,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
tokio::{sync::Mutex, time::sleep},
|
tokio::{sync::Mutex, time::sleep},
|
||||||
AsyncComponentSender, SharedState,
|
AsyncComponentSender, SharedState,
|
||||||
|
@ -119,52 +117,47 @@ impl AsyncComponent for Login {
|
||||||
tracker: 0,
|
tracker: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
let result = oo7::Keyring::new().await;
|
||||||
let result = oo7::Keyring::new().await;
|
match result {
|
||||||
match result {
|
Ok(keyring) => {
|
||||||
Ok(keyring) => {
|
KEYRING.set(keyring).unwrap();
|
||||||
KEYRING.set(keyring).unwrap();
|
if let Err(err) = KEYRING.get().unwrap().unlock().await {
|
||||||
{
|
|
||||||
let keyring = KEYRING.get().unwrap();
|
|
||||||
if let Err(err) = keyring.unlock().await {
|
|
||||||
sender
|
|
||||||
.output(LoginOutput::KeyringError(err))
|
|
||||||
.expect("sender not worky");
|
|
||||||
} else {
|
|
||||||
match keyring
|
|
||||||
.search_items(&HashMap::from(KEYRING_ATTRIBUTES))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(res) => {
|
|
||||||
if res.len() > 0 {
|
|
||||||
let item = &res[0];
|
|
||||||
let refresh_token = item.secret().await.unwrap();
|
|
||||||
let refresh_token =
|
|
||||||
std::str::from_utf8(refresh_token.as_slice()).unwrap();
|
|
||||||
model.refresh_token = Some(
|
|
||||||
RefreshToken::new(refresh_token.to_string()).unwrap(),
|
|
||||||
);
|
|
||||||
sender.input(LoginInput::NeedsRefresh);
|
|
||||||
} else {
|
|
||||||
sender.input(LoginInput::NeedsLogin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
sender
|
|
||||||
.output(LoginOutput::KeyringError(err))
|
|
||||||
.expect("sender not worky");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
sender
|
sender
|
||||||
.output(LoginOutput::KeyringError(err))
|
.output(LoginOutput::KeyringError(err))
|
||||||
.expect("sender not worky");
|
.expect("sender not worky");
|
||||||
|
} else {
|
||||||
|
let keyring = KEYRING.get().unwrap();
|
||||||
|
match keyring
|
||||||
|
.search_items(&HashMap::from(KEYRING_ATTRIBUTES))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(res) => {
|
||||||
|
if res.len() > 0 {
|
||||||
|
let item = &res[0];
|
||||||
|
let refresh_token = item.secret().await.unwrap();
|
||||||
|
let refresh_token =
|
||||||
|
std::str::from_utf8(refresh_token.as_slice()).unwrap();
|
||||||
|
model.refresh_token =
|
||||||
|
Some(RefreshToken::new(refresh_token.to_string()).unwrap());
|
||||||
|
sender.input(LoginInput::NeedsRefresh);
|
||||||
|
} else {
|
||||||
|
sender.input(LoginInput::NeedsLogin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
sender
|
||||||
|
.output(LoginOutput::KeyringError(err))
|
||||||
|
.expect("sender not worky");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
sender
|
||||||
|
.output(LoginOutput::KeyringError(err))
|
||||||
|
.expect("sender not worky");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let webcontext = WebContext::builder().build();
|
let webcontext = WebContext::builder().build();
|
||||||
{
|
{
|
||||||
|
@ -204,7 +197,10 @@ impl AsyncComponent for Login {
|
||||||
match message {
|
match message {
|
||||||
LoginInput::NeedsRefresh => {
|
LoginInput::NeedsRefresh => {
|
||||||
let refresh_token = self.refresh_token.as_ref().unwrap().clone();
|
let refresh_token = self.refresh_token.as_ref().unwrap().clone();
|
||||||
sender.oneshot_command(async { use_refresh_token(refresh_token).await })
|
sender.oneshot_command(async {
|
||||||
|
let res = use_refresh_token(refresh_token).await;
|
||||||
|
res
|
||||||
|
});
|
||||||
}
|
}
|
||||||
LoginInput::ReceivedAuthCode(auth_code) => {
|
LoginInput::ReceivedAuthCode(auth_code) => {
|
||||||
self.set_state(LoginState::Offline);
|
self.set_state(LoginState::Offline);
|
||||||
|
@ -254,12 +250,6 @@ impl AsyncComponent for Login {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum ResponseType {
|
|
||||||
Retry,
|
|
||||||
Okay,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Login {
|
impl Login {
|
||||||
fn construct_request_uri(&self) -> Option<URIRequest> {
|
fn construct_request_uri(&self) -> Option<URIRequest> {
|
||||||
if self.state != LoginState::InFlow {
|
if self.state != LoginState::InFlow {
|
||||||
|
|
|
@ -17,9 +17,7 @@ use relm4::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::advices::{AdvicesViewInput, AdvicesView};
|
use crate::advices::{AdvicesView, AdvicesViewInput};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
pub struct Ready {
|
pub struct Ready {
|
||||||
|
@ -27,6 +25,7 @@ pub struct Ready {
|
||||||
login: crate::LoginSharedState,
|
login: crate::LoginSharedState,
|
||||||
activate: bool,
|
activate: bool,
|
||||||
have_service_advices: bool,
|
have_service_advices: bool,
|
||||||
|
have_service_tracking: bool,
|
||||||
|
|
||||||
#[do_not_track]
|
#[do_not_track]
|
||||||
advices_component: AsyncController<AdvicesView>,
|
advices_component: AsyncController<AdvicesView>,
|
||||||
|
@ -51,12 +50,18 @@ pub enum ReadyCmds {
|
||||||
GotTracking(LibraryResult<Vec<Shipment>>),
|
GotTracking(LibraryResult<Vec<Shipment>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Services {
|
||||||
|
Advices,
|
||||||
|
SendungVerfolgung,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadyInput {
|
pub enum ReadyInput {
|
||||||
Activate,
|
Activate,
|
||||||
Deactivate,
|
Deactivate,
|
||||||
HaveAdvicesService,
|
HaveService(Services),
|
||||||
HavePaketankuendigungService,
|
ServiceBorked(Services),
|
||||||
SearchTracking(String),
|
SearchTracking(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,18 +75,23 @@ impl Component for Ready {
|
||||||
view! {
|
view! {
|
||||||
#[root]
|
#[root]
|
||||||
adw::ViewStack {
|
adw::ViewStack {
|
||||||
add = model.advices_component.widget() -> >k::ScrolledWindow {
|
add = &model.advices_component.widget().clone() -> gtk::ScrolledWindow {
|
||||||
/*#[track(model.changed_have_service_advices())]
|
#[track(model.changed_have_service_advices())]
|
||||||
set_visible: model.have_service_advices,*/
|
set_visible: model.have_service_advices,
|
||||||
|
|
||||||
} -> /*page_advices: adw::ViewStackPage*/ {
|
} -> page_advices: adw::ViewStackPage {
|
||||||
set_title: Some("Mail notification"),
|
set_title: Some("Mail notification"),
|
||||||
set_name: Some("page_advices"),
|
set_name: Some("page_advices"),
|
||||||
/*#[track(model.changed_have_service_advices())]
|
set_icon_name: Some(relm4_icons::icon_names::MAIL),
|
||||||
set_visible: model.have_service_advices,*/
|
|
||||||
|
#[track(model.changed_have_service_advices())]
|
||||||
|
set_visible: model.have_service_advices,
|
||||||
},
|
},
|
||||||
|
|
||||||
add = &adw::Bin {
|
add = &adw::Bin {
|
||||||
|
#[track(model.changed_have_service_tracking())]
|
||||||
|
set_visible: model.have_service_tracking,
|
||||||
|
|
||||||
#[wrap(Some)]
|
#[wrap(Some)]
|
||||||
set_child = >k::ScrolledWindow {
|
set_child = >k::ScrolledWindow {
|
||||||
#[wrap(Some)]
|
#[wrap(Some)]
|
||||||
|
@ -110,9 +120,13 @@ impl Component for Ready {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} -> {
|
} -> page_tracking: adw::ViewStackPage {
|
||||||
set_title: Some("Shipment tracking"),
|
set_title: Some("Shipment tracking"),
|
||||||
set_name: Some("page_tracking"),
|
set_name: Some("page_tracking"),
|
||||||
|
set_icon_name: Some(relm4_icons::icon_names::PACKAGE_X_GENERIC),
|
||||||
|
|
||||||
|
#[track(model.changed_have_service_tracking())]
|
||||||
|
set_visible: model.have_service_tracking,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,6 +141,7 @@ impl Component for Ready {
|
||||||
|
|
||||||
let model = Ready {
|
let model = Ready {
|
||||||
have_service_advices: false,
|
have_service_advices: false,
|
||||||
|
have_service_tracking: true,
|
||||||
login: init.clone(),
|
login: init.clone(),
|
||||||
activate: false,
|
activate: false,
|
||||||
tracking_factory,
|
tracking_factory,
|
||||||
|
@ -210,29 +225,36 @@ impl Component for Ready {
|
||||||
ReadyInput::Deactivate => {
|
ReadyInput::Deactivate => {
|
||||||
self.set_activate(false);
|
self.set_activate(false);
|
||||||
}
|
}
|
||||||
ReadyInput::HavePaketankuendigungService => {
|
ReadyInput::HaveService(service) => match service {
|
||||||
let token = self.login.clone();
|
Services::Advices => {
|
||||||
sender.oneshot_command(async move {
|
self.set_have_service_advices(true);
|
||||||
// fetching advices
|
self.advices_component.emit(AdvicesViewInput::Fetch);
|
||||||
let dhli_token = crate::login::get_id_token(&token).await.unwrap();
|
}
|
||||||
let client = libpaket::WebClient::new();
|
Services::SendungVerfolgung => {
|
||||||
ReadyCmds::GotTracking(
|
self.set_have_service_tracking(true);
|
||||||
client
|
let token = self.login.clone();
|
||||||
.tracking_search(
|
sender.oneshot_command(async move {
|
||||||
TrackingParams {
|
// fetching advices
|
||||||
language: Some("de".to_string()),
|
let dhli_token = crate::login::get_id_token(&token).await.unwrap();
|
||||||
},
|
let client = libpaket::WebClient::new();
|
||||||
Vec::new(),
|
ReadyCmds::GotTracking(
|
||||||
Some(&dhli_token),
|
client
|
||||||
)
|
.tracking_search(
|
||||||
.await,
|
TrackingParams {
|
||||||
)
|
language: Some("de".to_string()),
|
||||||
});
|
},
|
||||||
}
|
Vec::new(),
|
||||||
ReadyInput::HaveAdvicesService => {
|
Some(&dhli_token),
|
||||||
self.have_service_advices = true;
|
)
|
||||||
self.advices_component.emit(AdvicesViewInput::Fetch);
|
.await,
|
||||||
}
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ReadyInput::ServiceBorked(service) => match service {
|
||||||
|
Services::Advices => self.set_have_service_advices(false),
|
||||||
|
Services::SendungVerfolgung => self.set_have_service_tracking(false),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,13 +273,13 @@ impl Component for Ready {
|
||||||
match service {
|
match service {
|
||||||
libpaket::stammdaten::CustomerDataService::Packstation => (),
|
libpaket::stammdaten::CustomerDataService::Packstation => (),
|
||||||
libpaket::stammdaten::CustomerDataService::Paketankuendigung => {
|
libpaket::stammdaten::CustomerDataService::Paketankuendigung => {
|
||||||
sender.input(ReadyInput::HavePaketankuendigungService);
|
sender.input(ReadyInput::HaveService(Services::SendungVerfolgung))
|
||||||
}
|
}
|
||||||
libpaket::stammdaten::CustomerDataService::PostfilialeDirekt => (),
|
libpaket::stammdaten::CustomerDataService::PostfilialeDirekt => (),
|
||||||
libpaket::stammdaten::CustomerDataService::Digiben => (),
|
libpaket::stammdaten::CustomerDataService::Digiben => (),
|
||||||
libpaket::stammdaten::CustomerDataService::GeraetAktiviert => (),
|
libpaket::stammdaten::CustomerDataService::GeraetAktiviert => (),
|
||||||
libpaket::stammdaten::CustomerDataService::Briefankuendigung => {
|
libpaket::stammdaten::CustomerDataService::Briefankuendigung => {
|
||||||
sender.input(ReadyInput::HaveAdvicesService);
|
sender.input(ReadyInput::HaveService(Services::Advices))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,10 +342,15 @@ impl Component for Ready {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
sender.output(ReadyOutput::Error(err)).unwrap();
|
if err == LibraryError::APIChange {
|
||||||
|
println!("Upstream API for parcel tracking broke");
|
||||||
|
sender.input(ReadyInput::ServiceBorked(Services::SendungVerfolgung));
|
||||||
|
sender.output(ReadyOutput::Notification("Shipment Tracking API has changed. Deactivating that service.".to_string())).unwrap();
|
||||||
|
} else {
|
||||||
|
sender.output(ReadyOutput::Error(err)).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue