use crate::deals::now_ms;
use serde::{Deserialize, Serialize};
pub const SUPPORT_HANDLE: &str = "st_joy";
pub const COMMUNITY_GROUP: &str = "cyberialand";
pub const HARDFORCE_BOT: Option<&str> = Some("cybervalley_hardforce_bot");
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum OrderKind {
Survey,
Media,
Kyc,
}
impl OrderKind {
pub fn key(self) -> &'static str {
match self {
OrderKind::Survey => "survey",
OrderKind::Media => "media",
OrderKind::Kyc => "kyc",
}
}
pub fn label(self) -> &'static str {
match self {
OrderKind::Survey => "ORDER SURVEY",
OrderKind::Media => "ORDER PHOTO ยท DRONE VIDEO",
OrderKind::Kyc => "SEND KYC",
}
}
pub fn line(self) -> &'static str {
match self {
OrderKind::Survey => "triangulation on the slope by the hard force ยท the figure goes to the page and the lease",
OrderKind::Media => "photos and a drone flight over the flat ยท published on this page",
OrderKind::Kyc => "passport page and a selfie, in a direct message ยท nothing is stored on our servers",
}
}
pub fn text(self, plot_id: &str, plot_name: &str) -> String {
match self {
OrderKind::Survey => format!(
"ORDER SURVEY {plot_id}\n{plot_name} โ triangulation survey of the flat.\nhttps://cyberia.my/plot/{plot_id}/"
),
OrderKind::Media => format!(
"ORDER MEDIA {plot_id}\n{plot_name} โ photos and a drone video of the flat.\nhttps://cyberia.my/plot/{plot_id}/"
),
OrderKind::Kyc => "KYC โ I am starting a lease at Cyber Valley and will send my passport page and a selfie here.".to_string(),
}
}
}
fn encode(s: &str) -> String {
String::from(js_sys::encode_uri_component(s))
}
pub fn order_href(kind: OrderKind, plot_id: &str, plot_name: &str) -> String {
match (HARDFORCE_BOT, kind) {
(Some(bot), OrderKind::Survey | OrderKind::Media) => {
format!("https://t.me/{bot}?start={}_{plot_id}", kind.key())
}
_ => format!(
"https://t.me/{SUPPORT_HANDLE}?text={}",
encode(&kind.text(plot_id, plot_name))
),
}
}
pub fn support_href() -> String {
format!("https://t.me/{SUPPORT_HANDLE}")
}
pub fn community_href() -> String {
format!("https://t.me/{COMMUNITY_GROUP}")
}
pub const ORDERS_LOG_KEY: &str = "cyberia.orders.v1";
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct OrderRec {
pub kind: OrderKind,
pub plot: String,
pub ts_ms: f64,
}
pub fn load_orders_log() -> Vec<OrderRec> {
web_sys::window()
.and_then(|w| w.local_storage().ok().flatten())
.and_then(|ls| ls.get_item(ORDERS_LOG_KEY).ok().flatten())
.and_then(|s| serde_json::from_str(&s).ok())
.unwrap_or_default()
}
pub fn record_order(log: &mut Vec<OrderRec>, kind: OrderKind, plot: &str) {
log.retain(|o| !(o.kind == kind && o.plot == plot));
log.insert(
0,
OrderRec {
kind,
plot: plot.to_string(),
ts_ms: now_ms(),
},
);
if let Some(ls) = web_sys::window().and_then(|w| w.local_storage().ok().flatten()) {
if let Ok(s) = serde_json::to_string(log) {
let _ = ls.set_item(ORDERS_LOG_KEY, &s);
}
}
}
pub fn ordered(log: &[OrderRec], kind: OrderKind, plot: &str) -> Option<f64> {
log.iter().find(|o| o.kind == kind && o.plot == plot).map(|o| o.ts_ms)
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PlotData {
#[serde(default)]
pub survey: Option<Survey>,
#[serde(default)]
pub media: Vec<Media>,
#[serde(default)]
pub deal: Option<PublishedDeal>,
#[serde(default)]
pub updated: String,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PublishedDeal {
#[serde(default)]
pub status: String,
#[serde(default)]
pub holder: String,
#[serde(default)]
pub form: String,
#[serde(default)]
pub premium_pct: Option<u32>,
#[serde(default)]
pub price_usd: Option<f64>,
#[serde(default)]
pub paid_usd: f64,
#[serde(default)]
pub payments: Vec<Payment>,
#[serde(default)]
pub steps: std::collections::BTreeMap<String, String>,
#[serde(default)]
pub updated: String,
}
impl PublishedDeal {
pub fn line(&self) -> &'static str {
match self.status.as_str() {
"reserved" => "reserved โ the premium is received, the papers follow",
"transfer" => "rights in transfer โ signatures on both sides",
"owned" => "signed โ the flat is held",
_ => "open",
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Payment {
#[serde(default)]
pub on: String,
#[serde(default)]
pub usd: f64,
#[serde(default)]
pub method: String,
#[serde(default)]
pub reference: String,
#[serde(default)]
pub note: String,
}
pub async fn fetch_index() -> std::collections::HashMap<String, PublishedDeal> {
fetch_json("/plot-data/index.json").await.unwrap_or_default()
}
async fn fetch_json<T: serde::de::DeserializeOwned>(path: &str) -> Option<T> {
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
let window = web_sys::window()?;
let resp = JsFuture::from(window.fetch_with_str(path)).await.ok()?;
let resp: web_sys::Response = resp.dyn_into().ok()?;
if !resp.ok() {
return None;
}
let text = JsFuture::from(resp.text().ok()?).await.ok()?.as_string()?;
serde_json::from_str(&text).ok()
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Survey {
pub m2: f64,
#[serde(default)]
pub on: String,
#[serde(default)]
pub method: String,
#[serde(default)]
pub by: String,
#[serde(default)]
pub file: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Media {
pub kind: String,
pub url: String,
#[serde(default)]
pub cid: String,
#[serde(default)]
pub taken: String,
#[serde(default)]
pub note: String,
}
pub async fn fetch_plot_data(plot_id: &str) -> Option<PlotData> {
fetch_json(&format!("/plot-data/{plot_id}.json")).await
}