1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use abstutil::Timer;
use map_gui::load::FutureLoader;
use map_gui::tools::PopupMsg;
use widgetry::{EventCtx, State};
use crate::app::{App, Transition};
pub const PROPOSAL_HOST_URL: &str = "http://localhost:8080/v1";
pub fn upload_proposal(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
let (_, outer_progress_rx) = futures_channel::mpsc::channel(1);
let (_, inner_progress_rx) = futures_channel::mpsc::channel(1);
let edits_json = abstutil::to_json(&app.primary.map.get_edits().to_permanent(&app.primary.map));
FutureLoader::<App, String>::new_state(
ctx,
Box::pin(async move {
let id = abstio::http_post(format!("{}/create", PROPOSAL_HOST_URL), edits_json).await?;
let wrapper: Box<dyn Send + FnOnce(&App) -> String> = Box::new(move |_| id);
Ok(wrapper)
}),
outer_progress_rx,
inner_progress_rx,
"Uploading proposal",
Box::new(|ctx, _, result| {
Transition::Replace(match result {
Ok(id) => {
info!("Proposal uploaded! {}/get?id={}", PROPOSAL_HOST_URL, id);
UploadedProposals::proposal_uploaded(id);
PopupMsg::new_state(ctx, "Success", vec!["You can now share the URL..."])
}
Err(err) => PopupMsg::new_state(
ctx,
"Failure",
vec![format!("Couldn't upload proposal: {}", err)],
),
})
}),
)
}
#[derive(Serialize, Deserialize, Debug)]
pub struct UploadedProposals {
pub md5sums: BTreeSet<String>,
}
impl UploadedProposals {
pub fn load() -> UploadedProposals {
abstio::maybe_read_json::<UploadedProposals>(
abstio::path_player("uploaded_proposals.json"),
&mut Timer::throwaway(),
)
.unwrap_or_else(|_| UploadedProposals {
md5sums: BTreeSet::new(),
})
}
pub fn should_upload_proposal(app: &App) -> bool {
let map = &app.primary.map;
if map.get_edits().commands.is_empty() {
return false;
}
let checksum = map.get_edits().get_checksum(map);
!UploadedProposals::load().md5sums.contains(&checksum)
}
fn proposal_uploaded(checksum: String) {
let mut uploaded = UploadedProposals::load();
uploaded.md5sums.insert(checksum);
abstio::write_json(abstio::path_player("uploaded_proposals.json"), &uploaded);
}
}