use super::auth::{AuthConfig,AuthError};
use super::config::ConfigLocation;
use super::types::*;
+use super::posting::Post;
#[derive(Hash, Debug, PartialEq, Eq, Clone, Copy)]
pub enum Boosts { Show, Hide }
}
}
- #[allow(unused)] // FIXME: remove this once we start using it
fn post(url_suffix: &str) -> Self {
Req {
method: reqwest::Method::POST,
}.to_owned()
}
}
+impl ReqParam for Visibility {
+ fn param_value(self) -> String {
+ // A bit roundabout, but means we get to reuse the 'rename'
+ // strings defined in types.rs
+ let encoded = serde_json::to_string(&self).expect("can't fail");
+ let decoded: String = serde_json::from_str(&encoded)
+ .expect("can't fail either");
+ decoded
+ }
+}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FeedExtend {
self.cache_account(&ac);
Ok(ac)
}
+
+ pub fn post_status(&mut self, post: &Post) -> Result<(), ClientError> {
+ // FIXME: separate Post from a single status, so we can post threads
+ let req = Req::post("v1/statuses")
+ .param("status", post.text.trim_end_matches('\n'))
+ .param("visibility", post.m.visibility)
+ .param("language", &post.m.language);
+ let req = match &post.m.content_warning {
+ None => req,
+ Some(text) => req
+ .param("sensitive", true)
+ .param("spoiler_text", text),
+ };
+
+ let (url, req) = self.api_request(req)?;
+ let rsp = req.send()?;
+ let rspstatus = rsp.status();
+ if !rspstatus.is_success() {
+ Err(ClientError::UrlError(url.clone(), rspstatus.to_string()))
+ } else {
+ Ok(())
+ }
+ }
}
/// Directory containing configuration files.
#[arg(short, long)]
config: Option<std::path::PathBuf>,
+
+ // Read-only mode: the client prevents accidental posting.
+ #[arg(short, long, action(clap::ArgAction::SetTrue))]
+ readonly: bool,
}
#[derive(Debug)]
None => ConfigLocation::default()?,
Some(dir) => ConfigLocation::from_pathbuf(dir),
};
- Tui::run(&cfgloc)?;
+ Tui::run(&cfgloc, cli.readonly)?;
Ok(())
}
self.post.m.language.clone(), EditLang);
LogicalAction::Nothing
}
+
+ fn post(&self, client: &mut Client) -> LogicalAction {
+ match client.post_status(&self.post) {
+ Ok(_) => LogicalAction::Pop,
+ Err(_) => LogicalAction::Beep, // FIXME: report the error!
+ }
+ }
}
impl ActivityState for PostMenu {
(lines, cursorpos)
}
- fn handle_keypress(&mut self, key: OurKey, _client: &mut Client) ->
+ fn handle_keypress(&mut self, key: OurKey, client: &mut Client) ->
LogicalAction
{
match self.mode {
Normal => match key {
- Space => LogicalAction::Beep, // FIXME: post
+ Space => self.post(client),
Pr('q') | Pr('Q') => LogicalAction::Pop,
Pr('a') | Pr('A') => LogicalAction::PostReEdit(
self.post.clone()),
}
impl Tui {
- pub fn run(cfgloc: &ConfigLocation) -> Result<(), TuiError> {
+ pub fn run(cfgloc: &ConfigLocation, readonly: bool) ->
+ Result<(), TuiError>
+ {
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
let input_sender = sender.clone();
}
});
- let client = Client::new(cfgloc)?;
+ let mut client = Client::new(cfgloc)?;
+ client.set_writable(!readonly);
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
use chrono::{DateTime, NaiveDate, Utc};
-use serde::{Deserialize};
+use serde::{Deserialize, Serialize};
use strum::EnumIter;
use std::boxed::Box;
use std::option::Option;
pub website: Option<String>,
}
-#[derive(Deserialize, Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, EnumIter)]
pub enum Visibility {
#[serde(rename = "public")] Public,
#[serde(rename = "unlisted")] Unlisted,