[WIP] Terms update support
This commit is contained in:
parent
43af786d16
commit
b81276dd18
6 changed files with 251 additions and 91 deletions
|
|
@ -3,41 +3,71 @@ use crate::{
|
||||||
doc::Doc,
|
doc::Doc,
|
||||||
terms_checker,
|
terms_checker,
|
||||||
terms_getter::{Type, get_current_docs, get_newest_docs},
|
terms_getter::{Type, get_current_docs, get_newest_docs},
|
||||||
|
terms_updater,
|
||||||
},
|
},
|
||||||
util::file_util::load_file,
|
util::file_util::{load_file, save_file},
|
||||||
};
|
};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use tokio::task;
|
||||||
|
|
||||||
pub struct ConsentManager;
|
pub struct ConsentManager;
|
||||||
impl ConsentManager {
|
impl ConsentManager {
|
||||||
pub async fn check() -> (bool, bool) {
|
pub async fn check() -> (bool, bool) {
|
||||||
let file = load_file("", "agreements");
|
let file = load_file("", "agreements");
|
||||||
let file_state = ConsentState::from_str(&file).sanitize();
|
let mut file_state = ConsentState::from_str(&file).sanitize();
|
||||||
let accepted_state = if !&file_state.accepted_eula {
|
|
||||||
terms_checker::run_consent_ui(file_state).await
|
if !file_state.accepted_eula {
|
||||||
} else {
|
file_state = terms_checker::run_consent_ui(file_state).await;
|
||||||
file_state
|
|
||||||
|
println!("{}", &file_state.clone().sanitize().to_string());
|
||||||
|
println!("-----------------------");
|
||||||
|
|
||||||
|
save_file("", "agreements", &file_state.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !file_state.accepted_eula {
|
||||||
|
return (false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((eula_update, tos_update, privacy_update)) = Self::get_updates().await {
|
||||||
|
let is_forced = eula_update.is_err() || tos_update.is_err() || privacy_update.is_err();
|
||||||
|
|
||||||
|
let updater_logic = async move {
|
||||||
|
let state_to_update = terms_updater::run_consent_ui(
|
||||||
|
file_state,
|
||||||
|
eula_update,
|
||||||
|
tos_update,
|
||||||
|
privacy_update,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
println!("{}", &state_to_update.clone().sanitize().to_string());
|
||||||
|
|
||||||
|
save_file("", "agreements", &state_to_update.sanitize().to_string());
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((current_eula, current_tos, current_privacy)) = get_current_docs().await {
|
if is_forced {
|
||||||
if current_eula.equals_some(&accepted_state.eula) {
|
updater_logic.await;
|
||||||
if current_tos.equals_some(&accepted_state.tos)
|
|
||||||
&& current_privacy.equals_some(&accepted_state.pp)
|
|
||||||
{
|
|
||||||
(true, true)
|
|
||||||
} else {
|
} else {
|
||||||
(true, false)
|
task::spawn(updater_logic);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(false, false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("There was an error while loading our EULA, please retry later!");
|
|
||||||
(false, false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_updates() -> Option<(Option<Doc>, Option<Doc>, Option<Doc>)> {
|
let final_state = ConsentState::from_str(&load_file("", "agreements")).sanitize();
|
||||||
|
(
|
||||||
|
final_state.accepted_eula,
|
||||||
|
final_state.accepted_tos && final_state.accepted_pp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_updates() -> Option<(
|
||||||
|
// Ok(None) indicates no update
|
||||||
|
// Ok(Some) Indicates a future update
|
||||||
|
// Err indicates a update that has to be accepted before the programm can continue
|
||||||
|
Result<Option<Doc>, Doc>,
|
||||||
|
Result<Option<Doc>, Doc>,
|
||||||
|
Result<Option<Doc>, Doc>,
|
||||||
|
)> {
|
||||||
let file = load_file("", "agreements");
|
let file = load_file("", "agreements");
|
||||||
let accepted_state = ConsentState::from_str(&file).sanitize();
|
let accepted_state = ConsentState::from_str(&file).sanitize();
|
||||||
|
|
||||||
|
|
@ -46,7 +76,48 @@ impl ConsentManager {
|
||||||
Some((newest_eula, newest_tos, newest_privacy)),
|
Some((newest_eula, newest_tos, newest_privacy)),
|
||||||
) = (get_current_docs().await, get_newest_docs().await)
|
) = (get_current_docs().await, get_newest_docs().await)
|
||||||
{
|
{
|
||||||
None
|
let eula_update: Result<Option<Doc>, Doc> =
|
||||||
|
if current_eula.equals_some(&accepted_state.eula) {
|
||||||
|
if current_eula.equals(&newest_eula) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(newest_eula))
|
||||||
|
}
|
||||||
|
} else if newest_eula.equals_some(&accepted_state.eula) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(current_eula)
|
||||||
|
};
|
||||||
|
|
||||||
|
let tos_update: Result<Option<Doc>, Doc> = if newest_tos
|
||||||
|
.equals_some(&accepted_state.tos)
|
||||||
|
{
|
||||||
|
Ok(None)
|
||||||
|
} else if accepted_state.accepted_tos && current_tos.equals_some(&accepted_state.tos) {
|
||||||
|
if current_tos.equals(&newest_tos) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(newest_tos))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(current_tos)
|
||||||
|
};
|
||||||
|
|
||||||
|
let privacy_update: Result<Option<Doc>, Doc> =
|
||||||
|
if newest_privacy.equals_some(&accepted_state.privacy) {
|
||||||
|
Ok(None)
|
||||||
|
} else if accepted_state.accepted_pp
|
||||||
|
&& current_privacy.equals_some(&accepted_state.privacy)
|
||||||
|
{
|
||||||
|
if current_privacy.equals(&newest_privacy) {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(newest_privacy))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(current_privacy)
|
||||||
|
};
|
||||||
|
Some((eula_update, tos_update, privacy_update))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +137,7 @@ pub struct ConsentState {
|
||||||
pub accepted_eula: bool,
|
pub accepted_eula: bool,
|
||||||
pub tos: Option<Doc>,
|
pub tos: Option<Doc>,
|
||||||
pub accepted_tos: bool,
|
pub accepted_tos: bool,
|
||||||
pub pp: Option<Doc>,
|
pub privacy: Option<Doc>,
|
||||||
pub accepted_pp: bool,
|
pub accepted_pp: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +150,7 @@ impl ConsentState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn to_string(self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
let current_secs = SystemTime::now()
|
let current_secs = SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -93,9 +164,13 @@ impl ConsentState {
|
||||||
current_secs
|
current_secs
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.accepted_eula
|
if let Some(eula) = &self.eula {
|
||||||
&& let Some(eula) = self.eula
|
println!(
|
||||||
{
|
"EULA: {}, {}, {}",
|
||||||
|
self.accepted_eula,
|
||||||
|
eula.get_version(),
|
||||||
|
eula.get_hash()
|
||||||
|
);
|
||||||
file_out.push_str(&format!("\
|
file_out.push_str(&format!("\
|
||||||
\n\"EULA=true\" indicates that you read, understood and accepted Tensamin's End User Licence agreement. You can find our EULA at https://legal.tensamin.net/eula/\
|
\n\"EULA=true\" indicates that you read, understood and accepted Tensamin's End User Licence agreement. You can find our EULA at https://legal.tensamin.net/eula/\
|
||||||
\nEULA={}\
|
\nEULA={}\
|
||||||
|
|
@ -104,17 +179,17 @@ impl ConsentState {
|
||||||
", self.accepted_eula, eula.get_version(), eula.get_hash()));
|
", self.accepted_eula, eula.get_version(), eula.get_hash()));
|
||||||
|
|
||||||
if self.accepted_tos
|
if self.accepted_tos
|
||||||
&& let Some(tos) = self.tos
|
&& let Some(tos) = &self.tos
|
||||||
{
|
{
|
||||||
file_out.push_str(&format!("\
|
file_out.push_str(&format!("\
|
||||||
\n\"ToS=true\" indicates that you read, understood and accepted Tensamin's Terms of Service. You can find our Terms of Serivce at https://legal.tensamin.net/tos/\
|
\n\"Terms-of-Service=true\" indicates that you read, understood and accepted Tensamin's Terms of Service. You can find our Terms of Serivce at https://legal.tensamin.net/tos/\
|
||||||
\nToS={}\
|
\nTerms-of-Service={}\
|
||||||
\nToS-VERSION={}\
|
\nTerms-of-Service-VERSION={}\
|
||||||
\nToS-HASH={}\
|
\nTerms-of-Service-HASH={}\
|
||||||
", self.accepted_tos, tos.get_version(), tos.get_hash()));
|
", self.accepted_tos, tos.get_version(), tos.get_hash()));
|
||||||
}
|
}
|
||||||
if self.accepted_pp
|
if self.accepted_pp
|
||||||
&& let Some(pp) = self.pp
|
&& let Some(pp) = &self.privacy
|
||||||
{
|
{
|
||||||
file_out.push_str(&format!("\
|
file_out.push_str(&format!("\
|
||||||
\n\"Privacy-Policy=true\" indicates that you read, understood and accepted Tensamin's Privacy Policy. You can find our Privacy Policy at https://legal.tensamin.net/privacy/\
|
\n\"Privacy-Policy=true\" indicates that you read, understood and accepted Tensamin's Privacy Policy. You can find our Privacy Policy at https://legal.tensamin.net/privacy/\
|
||||||
|
|
@ -152,17 +227,17 @@ impl ConsentState {
|
||||||
eula_version = v.to_string();
|
eula_version = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("EULA-HASH=") {
|
} else if let Some(v) = line.strip_prefix("EULA-HASH=") {
|
||||||
eula_hash = v.to_string();
|
eula_hash = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("ToS=") {
|
} else if let Some(v) = line.strip_prefix("Terms-of-Service=") {
|
||||||
tos = v == "true";
|
tos = v == "true";
|
||||||
} else if let Some(v) = line.strip_prefix("ToS-VERSION=") {
|
} else if let Some(v) = line.strip_prefix("Terms-of-Service-VERSION=") {
|
||||||
tos_version = v.to_string();
|
tos_version = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("ToS-HASH=") {
|
} else if let Some(v) = line.strip_prefix("Terms-of-Service-HASH=") {
|
||||||
tos_hash = v.to_string();
|
tos_hash = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("PrivacyPolicy=") {
|
} else if let Some(v) = line.strip_prefix("Privacy-Policy=") {
|
||||||
pp = v == "true";
|
pp = v == "true";
|
||||||
} else if let Some(v) = line.strip_prefix("PrivacyPolicy-VERSION=") {
|
} else if let Some(v) = line.strip_prefix("Privacy-Policy-VERSION=") {
|
||||||
pp_version = v.to_string();
|
pp_version = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("PrivacyPolicy-HASH=") {
|
} else if let Some(v) = line.strip_prefix("Privacy-Policy-HASH=") {
|
||||||
pp_hash = v.to_string();
|
pp_hash = v.to_string();
|
||||||
} else if let Some(v) = line.strip_prefix("UNIX=") {
|
} else if let Some(v) = line.strip_prefix("UNIX=") {
|
||||||
unix = v.to_string();
|
unix = v.to_string();
|
||||||
|
|
@ -173,7 +248,7 @@ impl ConsentState {
|
||||||
accepted_eula: eula,
|
accepted_eula: eula,
|
||||||
eula: Some(Doc::new(eula_version, eula_hash, Type::EULA, unix)),
|
eula: Some(Doc::new(eula_version, eula_hash, Type::EULA, unix)),
|
||||||
accepted_pp: pp,
|
accepted_pp: pp,
|
||||||
pp: Some(Doc::new(pp_version, pp_hash, Type::PP, unix)),
|
privacy: Some(Doc::new(pp_version, pp_hash, Type::PP, unix)),
|
||||||
accepted_tos: tos,
|
accepted_tos: tos,
|
||||||
tos: Some(Doc::new(tos_version, tos_hash, Type::TOS, unix)),
|
tos: Some(Doc::new(tos_version, tos_hash, Type::TOS, unix)),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ use json::{JsonValue, object::Object};
|
||||||
|
|
||||||
use crate::{terms::terms_getter::Type, util::file_util::load_file};
|
use crate::{terms::terms_getter::Type, util::file_util::load_file};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct Doc {
|
pub struct Doc {
|
||||||
version: String,
|
version: String,
|
||||||
hash: String,
|
hash: String,
|
||||||
doc_type: Type,
|
pub doc_type: Type,
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Focus {
|
pub enum Focus {
|
||||||
Eula,
|
Eula,
|
||||||
Tos,
|
Tos,
|
||||||
|
|
@ -7,6 +7,7 @@ pub enum Focus {
|
||||||
Continue,
|
Continue,
|
||||||
ContinueAll,
|
ContinueAll,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Focus {
|
impl Focus {
|
||||||
pub fn next(&mut self, state: (bool, bool, bool)) {
|
pub fn next(&mut self, state: (bool, bool, bool)) {
|
||||||
*self = match self {
|
*self = match self {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::terms::{
|
||||||
consent_state::{ConsentState, UserChoice},
|
consent_state::{ConsentState, UserChoice},
|
||||||
focus::Focus,
|
focus::Focus,
|
||||||
md_viewer::FileViewer,
|
md_viewer::FileViewer,
|
||||||
terms_getter::{Type, get_link, get_terms},
|
terms_getter::{Type, get_current_docs, get_link, get_terms},
|
||||||
};
|
};
|
||||||
use crossterm::event::{self, Event, KeyCode};
|
use crossterm::event::{self, Event, KeyCode};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
|
|
@ -52,19 +52,19 @@ pub async fn run_consent_ui(mut consent: ConsentState) -> ConsentState {
|
||||||
height: content_height,
|
height: content_height,
|
||||||
});
|
});
|
||||||
let eula_text = if size.width < 70 {
|
let eula_text = if size.width < 70 {
|
||||||
"EULA ¹ (https://legal.tensamin.net/eula/)"
|
"EULA ¹ (https://legal.tensamin.net/eula/newest/)"
|
||||||
} else {
|
} else {
|
||||||
"End User Licence Agreement ¹ (https://legal.tensamin.net/eula/)"
|
"End User Licence Agreement ¹ (https://legal.tensamin.net/eula/newest/)"
|
||||||
};
|
};
|
||||||
let tos_text = if size.width < 72 {
|
let tos_text = if size.width < 72 {
|
||||||
"ToS ² (https://legal.tensamin.net/terms-of-service/)"
|
"ToS ² (https://legal.tensamin.net/terms-of-service/newest/)"
|
||||||
} else {
|
} else {
|
||||||
"Terms of Service ² (https://legal.tensamin.net/terms-of-service/)"
|
"Terms of Service ² (https://legal.tensamin.net/terms-of-service/newest/)"
|
||||||
};
|
};
|
||||||
let pp_text = if size.width < 68 {
|
let pp_text = if size.width < 68 {
|
||||||
"PP ² (https://legal.tensamin.net/privacy-policy/)"
|
"PP ² (https://legal.tensamin.net/privacy-policy/newest/)"
|
||||||
} else {
|
} else {
|
||||||
"Privacy Policy ² (https://legal.tensamin.net/privacy-policy/)"
|
"Privacy Policy ² (https://legal.tensamin.net/privacy-policy/newest/)"
|
||||||
};
|
};
|
||||||
|
|
||||||
let (mut optional_lines, agree_lines): (Vec<i16>, Vec<&str>) =
|
let (mut optional_lines, agree_lines): (Vec<i16>, Vec<&str>) =
|
||||||
|
|
@ -275,6 +275,12 @@ pub async fn run_consent_ui(mut consent: ConsentState) -> ConsentState {
|
||||||
};
|
};
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
|
|
||||||
|
if let Some((eula, tos, privacy)) = get_current_docs().await {
|
||||||
|
consent.eula = Some(eula);
|
||||||
|
consent.tos = Some(tos);
|
||||||
|
consent.privacy = Some(privacy);
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
UserChoice::AcceptAll => {
|
UserChoice::AcceptAll => {
|
||||||
consent.accepted_eula = true;
|
consent.accepted_eula = true;
|
||||||
|
|
@ -292,6 +298,7 @@ pub async fn run_consent_ui(mut consent: ConsentState) -> ConsentState {
|
||||||
consent.accepted_pp = false;
|
consent.accepted_pp = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
consent
|
consent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
3
src/terms/terms_getter.rs
Normal file → Executable file
3
src/terms/terms_getter.rs
Normal file → Executable file
|
|
@ -2,7 +2,7 @@ use json::JsonValue::Object;
|
||||||
|
|
||||||
use crate::terms::doc::Doc;
|
use crate::terms::doc::Doc;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
EULA,
|
EULA,
|
||||||
TOS,
|
TOS,
|
||||||
|
|
@ -87,7 +87,6 @@ pub async fn get_newest_docs() -> Option<(Doc, Doc, Doc)> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_terms(terms_type: Type) -> Option<String> {
|
pub async fn get_terms(terms_type: Type) -> Option<String> {
|
||||||
let body = reqwest::get(format!(
|
let body = reqwest::get(format!(
|
||||||
"https://legal.tensamin.net/api/text/{}/",
|
"https://legal.tensamin.net/api/text/{}/",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::terms::{
|
use crate::terms::{
|
||||||
consent_state::UserChoice,
|
consent_state::{ConsentState, UserChoice},
|
||||||
|
doc::Doc,
|
||||||
focus::Focus,
|
focus::Focus,
|
||||||
md_viewer::FileViewer,
|
md_viewer::FileViewer,
|
||||||
terms_getter::{Type, get_link, get_terms},
|
terms_getter::{Type, get_link, get_terms},
|
||||||
|
|
@ -13,10 +14,29 @@ use ratatui::{
|
||||||
};
|
};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub async fn run_consent_ui() -> UserChoice {
|
pub async fn run_consent_ui(
|
||||||
|
mut consent: ConsentState,
|
||||||
|
consent_eula: Result<Option<Doc>, Doc>,
|
||||||
|
consent_tos: Result<Option<Doc>, Doc>,
|
||||||
|
consent_pp: Result<Option<Doc>, Doc>,
|
||||||
|
) -> ConsentState {
|
||||||
let mut terminal = ratatui::init();
|
let mut terminal = ratatui::init();
|
||||||
|
let (eula_needed, eula_future, future_eula) = match consent_eula {
|
||||||
let (mut eula, mut tos, mut pp) = (false, false, false);
|
Ok(Some(future)) => (true, true, Some(future)),
|
||||||
|
Ok(None) => (false, false, None),
|
||||||
|
Err(future) => (true, false, Some(future)),
|
||||||
|
};
|
||||||
|
let (tos_needed, tos_future, future_tos) = match consent_tos {
|
||||||
|
Ok(Some(future)) => (true, true, Some(future)),
|
||||||
|
Ok(None) => (false, false, None),
|
||||||
|
Err(future) => (true, false, Some(future)),
|
||||||
|
};
|
||||||
|
let (pp_needed, pp_future, future_privacy) = match consent_pp {
|
||||||
|
Ok(Some(future)) => (true, true, Some(future)),
|
||||||
|
Ok(None) => (false, false, None),
|
||||||
|
Err(future) => (true, false, Some(future)),
|
||||||
|
};
|
||||||
|
let (mut eula, mut tos, mut pp) = (!eula_needed, !tos_needed, !pp_needed);
|
||||||
let mut focus = Focus::Eula;
|
let mut focus = Focus::Eula;
|
||||||
|
|
||||||
let result = loop {
|
let result = loop {
|
||||||
|
|
@ -51,28 +71,68 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
width: content_width,
|
width: content_width,
|
||||||
height: content_height,
|
height: content_height,
|
||||||
});
|
});
|
||||||
let eula_text = if size.width < 70 {
|
|
||||||
"EULA ¹ (https://legal.tensamin.net/eula/)"
|
|
||||||
|
let mut text_lines = Vec::new();
|
||||||
|
|
||||||
|
|
||||||
|
if eula_needed {
|
||||||
|
if eula_future {
|
||||||
|
if size.width < 70 {
|
||||||
|
text_lines.push(checkbox("EULA ¹³ (https://legal.tensamin.net/eula/newest/)", eula, focus == Focus::Eula, true));
|
||||||
} else {
|
} else {
|
||||||
"End User Licence Agreement ¹ (https://legal.tensamin.net/eula/)"
|
text_lines.push(checkbox("End User Licence Agreement ¹³ (https://legal.tensamin.net/eula/newest/)", eula, focus == Focus::Eula, true));
|
||||||
};
|
}
|
||||||
let tos_text = if size.width < 72 {
|
|
||||||
"ToS ² (https://legal.tensamin.net/terms-of-service/)"
|
|
||||||
} else {
|
} else {
|
||||||
"Terms of Service ² (https://legal.tensamin.net/terms-of-service/)"
|
if size.width < 70 {
|
||||||
};
|
text_lines.push(checkbox("EULA ¹ (https://legal.tensamin.net/eula/newest/)", eula, focus == Focus::Eula, true));
|
||||||
let pp_text = if size.width < 68 {
|
|
||||||
"PP ² (https://legal.tensamin.net/privacy-policy/)"
|
|
||||||
} else {
|
} else {
|
||||||
"Privacy Policy ² (https://legal.tensamin.net/privacy-policy/)"
|
text_lines.push(checkbox("End User Licence Agreement ¹ (https://legal.tensamin.net/eula/newest/)", eula, focus == Focus::Eula, true));
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tos_needed {
|
||||||
|
if tos_future {
|
||||||
|
if size.width < 70 {
|
||||||
|
text_lines.push(checkbox("ToS ²³ (https://legal.tensamin.net/terms-of-service/newest/)", tos, focus == Focus::Tos, eula));
|
||||||
|
} else {
|
||||||
|
text_lines.push(checkbox("Terms of Service ²³ (https://legal.tensamin.net/terms-of-service/newest/)", tos, focus == Focus::Tos, eula));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if size.width < 70 {
|
||||||
|
text_lines.push(checkbox("ToS ² (https://legal.tensamin.net/terms-of-service/newest/)", tos, focus == Focus::Tos, eula));
|
||||||
|
} else {
|
||||||
|
text_lines.push(checkbox("Terms of Service ² (https://legal.tensamin.net/terms-of-service/newest/)", tos, focus == Focus::Tos, eula));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pp_needed {
|
||||||
|
if pp_future {
|
||||||
|
if size.width < 70 {
|
||||||
|
text_lines.push(checkbox("PP ²³ (https://legal.tensamin.net/privacy-policy/newest/)", pp, focus == Focus::Pp, eula));
|
||||||
|
} else {
|
||||||
|
text_lines.push(checkbox("Privacy Policy ²³ (https://legal.tensamin.net/privacy-policy/newest/)", pp, focus == Focus::Pp, eula));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if size.width < 70 {
|
||||||
|
text_lines.push(checkbox("PP ² (https://legal.tensamin.net/privacy-policy/newest/)", pp, focus == Focus::Pp, pp));
|
||||||
|
} else {
|
||||||
|
text_lines.push(checkbox("Privacy Policy ² (https://legal.tensamin.net/privacy-policy/newest/)", pp, focus == Focus::Pp, eula));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
text_lines.push(Line::from(""));
|
||||||
|
text_lines.push(Line::from("¹ Necessary, ² Optional, ³ Future"));
|
||||||
|
text_lines.push(Line::from(""));
|
||||||
|
|
||||||
let (mut optional_lines, agree_lines): (Vec<i16>, Vec<&str>) =
|
let (mut optional_lines, agree_lines): (Vec<i16>, Vec<&str>) =
|
||||||
if size.width > 143 {
|
if size.width > 143 {
|
||||||
(
|
(
|
||||||
vec![7, 3, 7, 4],
|
vec![7, 3, 7, 4],
|
||||||
vec![
|
vec![
|
||||||
"",
|
|
||||||
"By selecting Continue, you confirm that you have read, understood and agree to the End User License Agreement and applicable Terms of Service.",
|
"By selecting Continue, you confirm that you have read, understood and agree to the End User License Agreement and applicable Terms of Service.",
|
||||||
"",
|
"",
|
||||||
"Tensamin services require acceptance of the Terms of Service and Privacy Policy.",
|
"Tensamin services require acceptance of the Terms of Service and Privacy Policy.",
|
||||||
|
|
@ -97,7 +157,6 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
(
|
(
|
||||||
vec![8, 3, 8, 4],
|
vec![8, 3, 8, 4],
|
||||||
vec![
|
vec![
|
||||||
"",
|
|
||||||
"By selecting Continue, you confirm that you have read, understood and",
|
"By selecting Continue, you confirm that you have read, understood and",
|
||||||
"agree to the End User License Agreement and applicable Terms of Service.",
|
"agree to the End User License Agreement and applicable Terms of Service.",
|
||||||
"",
|
"",
|
||||||
|
|
@ -111,7 +170,6 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
(
|
(
|
||||||
vec![9, 3, 10, 4],
|
vec![9, 3, 10, 4],
|
||||||
vec![
|
vec![
|
||||||
"",
|
|
||||||
"By selecting Continue, you confirm that you have",
|
"By selecting Continue, you confirm that you have",
|
||||||
"read, understood and agree to the End User License",
|
"read, understood and agree to the End User License",
|
||||||
"Agreement and applicable Terms of Service.",
|
"Agreement and applicable Terms of Service.",
|
||||||
|
|
@ -124,13 +182,8 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let mut text_lines = vec![
|
|
||||||
checkbox(eula_text, eula, focus == Focus::Eula, true),
|
|
||||||
checkbox(tos_text, tos, focus == Focus::Tos, eula),
|
|
||||||
checkbox(pp_text, pp, focus == Focus::Pp, eula),
|
|
||||||
Line::from(""),
|
|
||||||
Line::from("¹ Necessary, ² Optional"),
|
|
||||||
];
|
|
||||||
|
|
||||||
for line in agree_lines {
|
for line in agree_lines {
|
||||||
text_lines.insert(text_lines.len(), Line::from(line));
|
text_lines.insert(text_lines.len(), Line::from(line));
|
||||||
|
|
@ -191,10 +244,10 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
let consent_block = Paragraph::new(Text::from(text_lines))
|
let consent_block = Paragraph::new(Text::from(text_lines))
|
||||||
.block(Block::default().title(" Tensamin User Consent [Q to Quit] ").borders(Borders::ALL));
|
.block(Block::default().title(format!(" Update Tensamin User Consent [Q to Quit] {}, {}, {}", pp_needed, eula_needed, tos_needed)).borders(Borders::ALL));
|
||||||
f.render_widget(consent_block, chunks[0]);
|
f.render_widget(consent_block, chunks[0]);
|
||||||
|
|
||||||
draw_buttons(f, chunks[1], (eula, tos, pp));
|
draw_buttons(f, chunks[1], focus, (eula, tos, pp));
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
@ -275,7 +328,23 @@ pub async fn run_consent_ui() -> UserChoice {
|
||||||
};
|
};
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
|
|
||||||
result
|
match result {
|
||||||
|
UserChoice::AcceptAll => {
|
||||||
|
consent.accepted_eula = true;
|
||||||
|
consent.accepted_tos = true;
|
||||||
|
consent.accepted_pp = true;
|
||||||
|
consent.eula = future_eula;
|
||||||
|
consent.tos = future_tos;
|
||||||
|
consent.privacy = future_privacy;
|
||||||
|
}
|
||||||
|
UserChoice::AcceptEULA => {
|
||||||
|
consent.accepted_eula = true;
|
||||||
|
consent.eula = future_eula;
|
||||||
|
}
|
||||||
|
UserChoice::Deny => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
consent
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(mismatched_lifetime_syntaxes)]
|
#[allow(mismatched_lifetime_syntaxes)]
|
||||||
|
|
@ -313,7 +382,12 @@ fn draw_button(f: &mut ratatui::Frame, area: Rect, label: &str, style: Style) {
|
||||||
.block(Block::default().borders(Borders::ALL));
|
.block(Block::default().borders(Borders::ALL));
|
||||||
f.render_widget(p, area);
|
f.render_widget(p, area);
|
||||||
}
|
}
|
||||||
fn draw_buttons(f: &mut ratatui::Frame, area: Rect, state: (bool, bool, bool)) {
|
fn draw_buttons(
|
||||||
|
f: &mut ratatui::Frame,
|
||||||
|
area: Rect,
|
||||||
|
current_focus: Focus,
|
||||||
|
state: (bool, bool, bool),
|
||||||
|
) {
|
||||||
let buttons = vec![
|
let buttons = vec![
|
||||||
("[Q] Cancel", Focus::Cancel),
|
("[Q] Cancel", Focus::Cancel),
|
||||||
("Continue", Focus::Continue),
|
("Continue", Focus::Continue),
|
||||||
|
|
@ -339,9 +413,11 @@ fn draw_buttons(f: &mut ratatui::Frame, area: Rect, state: (bool, bool, bool)) {
|
||||||
};
|
};
|
||||||
x += width;
|
x += width;
|
||||||
|
|
||||||
|
let is_focused = current_focus == *focus;
|
||||||
|
|
||||||
let style = match focus {
|
let style = match focus {
|
||||||
Focus::Cancel => {
|
Focus::Cancel => {
|
||||||
if focus == &Focus::Cancel {
|
if is_focused {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Black)
|
.fg(Color::Black)
|
||||||
.bg(Color::Red)
|
.bg(Color::Red)
|
||||||
|
|
@ -350,20 +426,22 @@ fn draw_buttons(f: &mut ratatui::Frame, area: Rect, state: (bool, bool, bool)) {
|
||||||
Style::default().fg(Color::Red)
|
Style::default().fg(Color::Red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Focus::Continue => {
|
Focus::Continue => {
|
||||||
if focus == &Focus::Continue && state.0 {
|
if is_focused && state.0 {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Black)
|
.fg(Color::Black)
|
||||||
.bg(Color::Green)
|
.bg(Color::Green)
|
||||||
.add_modifier(Modifier::BOLD)
|
.add_modifier(Modifier::BOLD)
|
||||||
} else if state.0 && state.1 && state.2 {
|
} else if state.0 {
|
||||||
Style::default().fg(Color::Green)
|
Style::default().fg(Color::Green) // enabled but not focused
|
||||||
} else {
|
} else {
|
||||||
Style::default().fg(Color::DarkGray)
|
Style::default().fg(Color::DarkGray)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Focus::ContinueAll => {
|
Focus::ContinueAll => {
|
||||||
if focus == &Focus::ContinueAll && state.0 && state.1 && state.2 {
|
if is_focused && state.0 && state.1 && state.2 {
|
||||||
Style::default()
|
Style::default()
|
||||||
.fg(Color::Black)
|
.fg(Color::Black)
|
||||||
.bg(Color::Green)
|
.bg(Color::Green)
|
||||||
|
|
@ -374,13 +452,13 @@ fn draw_buttons(f: &mut ratatui::Frame, area: Rect, state: (bool, bool, bool)) {
|
||||||
Style::default().fg(Color::DarkGray)
|
Style::default().fg(Color::DarkGray)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Style::default().fg(Color::DarkGray),
|
_ => Style::default().fg(Color::DarkGray),
|
||||||
};
|
};
|
||||||
|
|
||||||
draw_button(f, chunk, label, style);
|
draw_button(f, chunk, label, style);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_widths(area_width: u16, min_widths: &[u16]) -> Vec<u16> {
|
fn compute_widths(area_width: u16, min_widths: &[u16]) -> Vec<u16> {
|
||||||
let mut widths = vec![0; min_widths.len()];
|
let mut widths = vec![0; min_widths.len()];
|
||||||
let mut remaining: Vec<usize> = (0..min_widths.len()).collect();
|
let mut remaining: Vec<usize> = (0..min_widths.len()).collect();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue