adding sync for multiple terminal using global timestamps (copy of #1 because resubmitting did not work) #2

open
opened by rubberducky.guru targeting main from rubberducky.guru/thought-stream-cli: main

Added sync functionality if you have several terminals open. Not much on it's own, but it also makes the timestamp of each messages use the created_at property from the blip record instead of just taking whatever the current time is before displaying it.

Changed files
+24 -21
src
+20 -15
src/client.rs
···
use anyhow::{Context, Result};
use chrono::Utc;
-
use reqwest::{Client as HttpClient, header::{HeaderMap, HeaderValue, AUTHORIZATION}};
+
use reqwest::{
+
header::{HeaderMap, HeaderValue, AUTHORIZATION},
+
Client as HttpClient,
+
};
use serde::{Deserialize, Serialize};
use serde_json::Value;
···
pub async fn login(&mut self, credentials: &Credentials) -> Result<()> {
let login_url = format!("{}/xrpc/com.atproto.server.createSession", self.base_url);
-
+
let request = LoginRequest {
identifier: credentials.username.clone(),
password: credentials.password.clone(),
};
-
let response = self.http_client
+
let response = self
+
.http_client
.post(&login_url)
.header("Content-Type", "application/json")
.json(&request)
···
}
pub async fn publish_blip(&self, content: &str) -> Result<String> {
-
let session = self.session.as_ref()
+
let session = self
+
.session
+
.as_ref()
.context("Not authenticated. Please run 'thought login' first.")?;
+
let timestamp = Utc::now().to_rfc3339().replace("+00:00", "Z");
+
let record = BlipRecord {
record_type: "stream.thought.blip".to_string(),
content: content.to_string(),
-
created_at: Utc::now().to_rfc3339().replace("+00:00", "Z"),
+
created_at: timestamp.clone(),
};
let request = CreateRecordRequest {
repo: session.did.clone(),
collection: "stream.thought.blip".to_string(),
-
record: serde_json::to_value(&record)
-
.context("Failed to serialize blip record")?,
+
record: serde_json::to_value(&record).context("Failed to serialize blip record")?,
};
let create_url = format!("{}/xrpc/com.atproto.repo.createRecord", self.base_url);
-
+
let mut headers = HeaderMap::new();
headers.insert(
AUTHORIZATION,
HeaderValue::from_str(&format!("Bearer {}", session.access_jwt))
.context("Invalid authorization header")?,
);
-
headers.insert(
-
"Content-Type",
-
HeaderValue::from_static("application/json"),
-
);
+
headers.insert("Content-Type", HeaderValue::from_static("application/json"));
-
let response = self.http_client
+
let response = self
+
.http_client
.post(&create_url)
.headers(headers)
.json(&request)
···
.await
.context("Failed to parse create record response")?;
-
Ok(create_response.uri)
+
Ok(timestamp)
}
pub fn is_authenticated(&self) -> bool {
···
pub fn get_user_did(&self) -> Option<String> {
self.session.as_ref().map(|s| s.did.clone())
}
-
}
+
}
+4 -6
src/tui.rs
···
use crate::client::AtProtoClient;
-
#[derive(Debug, Clone, PartialEq)]
+
#[derive(Debug, Clone)]
pub struct Message {
pub handle: String,
pub content: String,
···
"you".to_string(),
message,
true,
-
Some(
-
DateTime::parse_from_rfc3339(&t)
-
.unwrap()
-
.with_timezone(&Utc),
-
),
+
DateTime::parse_from_rfc3339(&t)
+
.map(|dt| dt.with_timezone(&Utc))
+
.ok(), // Parse RFC3339 → UTC, None if invalid (so current timestamp instead)
));
}
Err(e) => {