From a708d8c9380dd11f1da4750373cd7191eb2700bb Mon Sep 17 00:00:00 2001
From: ToxicMushroom <32853531+ToxicMushroom@users.noreply.github.com>
Date: Sun, 6 Apr 2025 01:25:35 +0200
Subject: [PATCH] Working diff viewer

---
 server/core/src/https/views/profile.rs        | 54 ++++++++++++------
 .../profile_changes_partial.html              | 57 ++++++++++++-------
 .../user_settings_profile_partial.html        |  2 +-
 3 files changed, 75 insertions(+), 38 deletions(-)

diff --git a/server/core/src/https/views/profile.rs b/server/core/src/https/views/profile.rs
index a3678010f..770ead2b2 100644
--- a/server/core/src/https/views/profile.rs
+++ b/server/core/src/https/views/profile.rs
@@ -10,7 +10,7 @@ use askama::Template;
 use askama_axum::IntoResponse;
 use axum::extract::{Query, State};
 use axum::http::Uri;
-use axum::response::Response;
+use axum::response::{Redirect, Response};
 use axum::Extension;
 use axum_extra::extract::cookie::CookieJar;
 use axum_extra::extract::Form;
@@ -60,6 +60,18 @@ pub(crate) struct SaveProfileQuery {
     primary_email_index: u16,
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub(crate) struct CommitSaveProfileQuery {
+    #[serde(rename = "account_name")]
+    account_name: String,
+    #[serde(rename = "display_name")]
+    display_name: String,
+    #[serde(rename = "emails[]")]
+    emails: Vec<String>,
+    #[serde(rename = "new_primary_mail")]
+    new_primary_mail: Option<String>
+}
+
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub(crate) struct ProfileAttributes {
     account_name: String,
@@ -73,7 +85,9 @@ struct ProfileChangesPartialView {
     menu_active_item: ProfileMenuItems,
     can_rw: bool,
     person: ScimPerson,
+    primary_mail: Option<String>,
     new_attrs: ProfileAttributes,
+    new_primary_mail: Option<String>,
 }
 
 #[derive(Template, Clone)]
@@ -109,7 +123,7 @@ pub(crate) async fn view_profile_get(
             &kopid,
             client_auth_info.clone(),
         )
-        .await?;
+            .await?;
 
     let time = time::OffsetDateTime::now_utc() + time::Duration::new(60, 0);
 
@@ -149,8 +163,7 @@ pub(crate) async fn view_profile_diff_start_save_post(
         state,
         &kopid,
         client_auth_info.clone(),
-    )
-    .await?;
+    ).await?;
 
     let primary_index = query
         .emails_indexes
@@ -166,23 +179,28 @@ pub(crate) async fn view_profile_diff_start_save_post(
             value: email.to_string(),
         })
         .collect();
+    let old_primary_mail = scim_person.mails.iter()
+        .find(|sm| sm.primary)
+        .map(|sm| sm.value.clone());
+
 
     let profile_view = ProfileChangesPartialView {
         menu_active_item: ProfileMenuItems::UserProfile,
         can_rw,
         person: scim_person,
+        primary_mail: old_primary_mail,
         new_attrs: ProfileAttributes {
             account_name: query.account_name,
             display_name: query.display_name,
             emails: new_mails,
         },
+        new_primary_mail: query.emails.get(primary_index).cloned(),
     };
 
     Ok((
         HxPushUrl(Uri::from_static("/ui/profile/diff")),
         profile_view,
-    )
-        .into_response())
+    ).into_response())
 }
 
 pub(crate) async fn view_profile_diff_confirm_save_post(
@@ -191,14 +209,14 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
     VerifiedClientInformation(client_auth_info): VerifiedClientInformation,
     DomainInfo(domain_info): DomainInfo,
     // Form must be the last parameter because it consumes the request body
-    Form(mut new_attrs): Form<ProfileAttributes>,
+    Form(query): Form<CommitSaveProfileQuery>,
 ) -> axum::response::Result<Response> {
     let uat: UserAuthToken = state
         .qe_r_ref
         .handle_whoami_uat(client_auth_info.clone(), kopid.eventid)
         .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
-    dbg!(&new_attrs);
+    dbg!(&query);
 
     let filter = filter_all!(f_and!([f_id(uat.uuid.to_string().as_str())]));
 
@@ -208,24 +226,26 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
             client_auth_info.clone(),
             uat.uuid.to_string(),
             ATTR_DISPLAYNAME.to_string(),
-            vec![new_attrs.display_name],
+            vec![query.display_name],
             filter.clone(),
             kopid.eventid,
         )
         .map_err(|op_err| HtmxError::new(&kopid, op_err, domain_info.clone()))
         .await?;
 
-    new_attrs
-        .emails
-        .sort_by_key(|sm| if sm.primary { 0 } else { 1 });
-    let email_addresses = new_attrs.emails.into_iter().map(|sm| sm.value).collect();
+
+    let mut emails = query.emails;
+    if let Some(primary) = query.new_primary_mail {
+        emails.insert(0, primary);
+    }
+
     state
         .qe_w_ref
         .handle_setattribute(
             client_auth_info.clone(),
             uat.uuid.to_string(),
             ATTR_MAIL.to_string(),
-            email_addresses,
+            emails,
             filter.clone(),
             kopid.eventid,
         )
@@ -266,9 +286,9 @@ pub(crate) async fn view_profile_diff_confirm_save_post(
         VerifiedClientInformation(client_auth_info),
         DomainInfo(domain_info),
     )
-    .await
+        .await
     {
-        Ok(pv) => Ok(pv.into_response()),
+        Ok(_) => Ok(Redirect::to(Urls::Profile.as_ref()).into_response()),
         Err(e) => Ok(e.into_response()),
     }
 }
@@ -331,5 +351,5 @@ pub(crate) async fn view_profile_unlock_get(
         Urls::Profile.as_ref(),
         display_ctx,
     )
-    .await)
+        .await)
 }
diff --git a/server/core/templates/user_settings/profile_changes_partial.html b/server/core/templates/user_settings/profile_changes_partial.html
index 078c6f134..43229b24e 100644
--- a/server/core/templates/user_settings/profile_changes_partial.html
+++ b/server/core/templates/user_settings/profile_changes_partial.html
@@ -13,10 +13,15 @@ Profile Difference
     <input type="hidden" name="display_name" value="(( new_attrs.display_name ))"/>
 <!--    <input type="hidden" name="legal_name" value=" new_attrs.legal_name "/>-->
     (% for email in new_attrs.emails %)
-        <input type="hidden" name="emails[]" value="(( email.value ))"/>
+        (% if !email.primary %)
+            <input type="hidden" name="emails[]" value="(( email.value ))"/>
+        (% endif %)
     (% endfor %)
+    (% if let Some(new_primary_mail) = new_primary_mail %)
+        <input type="hidden" name="new_primary_mail" value="(( new_primary_mail ))"/>
+    (% endif %)
 
-    <table class="table table-bordered table-responsive">
+    <table class="table table-bordered overflow-x-scroll">
         <thead>
             <tr>
                 <th scope="col">Attribute</th>
@@ -39,32 +44,44 @@ Profile Difference
             <td class="text-break">(( new_attrs.display_name ))</td>
         </tr>
     (% endif %)
-
-
-        <!-- TODO: List new items with +, same items with . -->
-        <tr>
-            <th scope="row">Emails</th>
-            <td class="text-break">
-                <ul>
+    <!-- TODO: List new items with +, same items with . -->
+    <tr>
+        <th scope="row">Primary Emails</th>
+        <td class="text-nowrap">
+            (( primary_mail.clone().unwrap_or("none".to_string()) ))
+        </td>
+        <td class="text-nowrap">
+            (( new_primary_mail.clone().unwrap_or("none".to_string()) ))
+        </td>
+    </tr>
+    <tr>
+        <th scope="row">Secondary Emails</th>
+        <td class="text-break">
+            <ul class="ps-3">
                 (% for email in person.mails %)
-                    <li>(( email.value ))</li>
+                (% if !email.primary %)
+                <li class="text-nowrap">(( email.value ))</li>
+                (% endif %)
                 (% endfor %)
-                </ul>
-            </td>
-            <td class="text-break">
-                <ul>
+            </ul>
+        </td>
+        <td class="text-break">
+            <ul class="ps-3">
                 (% for email in new_attrs.emails %)
-                    <li>(( email.value ))</li>
+                (% if !email.primary %)
+                <li class="text-nowrap">(( email.value ))</li>
+                (% endif %)
                 (% endfor %)
-                </ul>
-            </td>
-        </tr>
+            </ul>
+        </td>
+    </tr>
+
     </table>
     <!-- Edit button -->
     <div class="pt-4" hx-target="#user_settings_container" hx-swap="outerHTML">
         (% if can_rw %)
-        <button class="btn btn-danger" type="button" hx-get="/ui/profile" hx-target="#user_settings_container" hx-swap="outerHTML">Discard Changes</button>
-        <button class="btn btn-primary" type="button" hx-post="/ui/api/user_settings/confirm_profile" hx-target="#user_settings_container" hx-swap="outerHTML">Confirm Changes</button>
+        <button class="btn btn-danger" type="button" hx-get="/ui/profile" hx-target="body" hx-swap="outerHTML">Discard Changes</button>
+        <button class="btn btn-primary" type="button" hx-post="/ui/api/user_settings/confirm_profile" hx-target="body" hx-swap="outerHTML">Confirm Changes</button>
         (% else %)
         <a href="/ui/profile/unlock" hx-boost="false">
             <!-- TODO: at the moment, session expiring here means progress is lost. Do we just show an error screen ? We can't pass the update state through the reauth session, and we don't have profile update sessions like cred update. -->
diff --git a/server/core/templates/user_settings_profile_partial.html b/server/core/templates/user_settings_profile_partial.html
index 0f170280b..24a788b2d 100644
--- a/server/core/templates/user_settings_profile_partial.html
+++ b/server/core/templates/user_settings_profile_partial.html
@@ -23,7 +23,7 @@ Profile
 
 <form id="user_settings_container" class="needs-validation"
       hx-post="/ui/api/user_settings/edit_profile"
-      hx-target="#user_settings_container"
+      hx-target="#main"
       hx-swap="outerHTML"
       hx-validate="true"
       hx-ext="bs-validation"