basic phone number support
Some checks failed
/ integration-test--firefox (push) Failing after 3m8s

This commit is contained in:
Robert Perce 2026-01-31 21:01:01 -06:00
parent 84c41dda4d
commit 2e1fbd00be
3 changed files with 90 additions and 2 deletions

View file

@ -37,6 +37,13 @@ pub struct Group {
pub slug: String,
}
#[derive(serde::Serialize, Debug)]
pub struct PhoneNumber {
pub contact_id: DbId,
pub label: Option<String>,
pub phone_number: String,
}
pub fn router() -> Router<AppState> {
Router::new()
.route("/contact/new", post(self::post::contact))
@ -93,6 +100,12 @@ mod get {
.fetch_all(pool)
.await?;
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
PhoneNumber,
"select * from phone_numbers where contact_id = $1",
contact_id
).fetch_all(pool).await?;
let lives_with = if contact.lives_with.len() > 1 {
let mention_host = MentionHost {
entity_id: contact_id,
@ -162,6 +175,19 @@ mod get {
}
}
@if phone_numbers.len() > 0 {
label { "phone" }
#phone_numbers {
@for phone_number in phone_numbers {
@let lbl = phone_number.label.unwrap_or(String::new());
.label data-is-empty=(lbl.len() == 0) { (lbl) }
.phone_nunber {
a href=(format!("tel:{}", phone_number.phone_number)) { (phone_number.phone_number) }
}
}
}
}
@if let Some(lives_with) = lives_with {
label { "lives with" }
div { (lives_with) }
@ -223,6 +249,12 @@ mod get {
let pool = &state.db(&auth_session.user.unwrap()).pool;
let contact = HydratedContact::load(contact_id, pool).await?;
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
PhoneNumber,
"select * from phone_numbers where contact_id = $1",
contact_id
).fetch_all(pool).await?;
let addresses: Vec<Address> = sqlx::query_as!(
Address,
"select * from addresses where contact_id = $1",
@ -290,6 +322,20 @@ mod get {
span x-text="date.length ? date.split('T')[0] : '(never)'" {}
input type="button" value="Mark fresh now" x-on:click="date = new Date().toISOString()";
}
label { "phone" }
#phone_numbers x-data=(json!({ "phones": phone_numbers, "new_label": "", "new_number": "" })) {
template x-for="(phone, index) in phones" x-bind:key="index" {
.phone_input {
input name="phone_label" x-model="phone.label" placeholder="home/work/mobile";
input name="phone_number" x-model="phone.phone_number" placeholder="number";
}
}
.phone_input {
input name="phone_label" x-model="new_label" placeholder="home/work/mobile";
input name="phone_number" x-model="new_number" placeholder="number";
}
input type="button" value="Add" x-on:click="phones.push({ label: new_label, phone_number: new_number }); new_label=''; new_number = ''";
}
label { "lives with" }
div {
input name="lives_with" value=(contact.lives_with);
@ -367,6 +413,8 @@ mod put {
birthday: String,
manually_freshened_at: String,
lives_with: String,
phone_label: Option<Vec<String>>,
phone_number: Option<Vec<String>>,
address_label: Option<Vec<String>>,
address_value: Option<Vec<String>>,
group: Option<Vec<String>>,
@ -473,6 +521,41 @@ mod put {
// these blocks are not in functions because payload gets progressively
// partially moved as we handle each field and i don't want to deal with it
{
// update phone numbers
let new_numbers = payload.phone_number.clone().map_or(vec![], |numbers| {
let labels: Vec<String> = payload.phone_label.clone().unwrap();
// TODO sanitize down to linkable on input
labels
.into_iter()
.zip(numbers)
.filter(|(_, val)| val.len() > 0)
.collect::<Vec<(String, String)>>()
});
let old_numbers: Vec<(String, String)> =
sqlx::query_as("select label, phone_number from phone_numbers where contact_id = $1")
.bind(contact_id)
.fetch_all(pool)
.await?;
if new_numbers != old_numbers {
sqlx::query!("delete from phone_numbers where contact_id = $1", contact_id)
.execute(pool)
.await?;
// trailing space in query intentional
QueryBuilder::new("insert into phone_numbers (contact_id, label, phone_number) ")
.push_values(new_numbers, |mut b, (label, phone_number)| {
b.push_bind(contact_id).push_bind(label).push_bind(phone_number);
})
.build()
.execute(pool)
.await?;
}
}
{
// update addresses
let new_addresses = payload.address_value.clone().map(|values| {