From ae56926238c72675ec1d29a6a4b04591254d3659 Mon Sep 17 00:00:00 2001
From: Niko PLP <niko@nextgraph.org>
Date: Sat, 13 Jul 2024 18:28:34 +0300
Subject: [PATCH] finish recovery PDF and few fixes

---
 nextgraph/src/local_broker.rs         | 79 +++++++++++++++++----------
 ng-app/src/classes.ts                 |  2 +-
 ng-app/src/routes/WalletCreate.svelte | 22 +++++++-
 ng-wallet/src/emojis.rs               | 15 +++--
 ng-wallet/src/lib.rs                  | 21 +------
 5 files changed, 85 insertions(+), 54 deletions(-)

diff --git a/nextgraph/src/local_broker.rs b/nextgraph/src/local_broker.rs
index 69d06b4f..0ab45585 100644
--- a/nextgraph/src/local_broker.rs
+++ b/nextgraph/src/local_broker.rs
@@ -44,8 +44,10 @@ use ng_verifier::types::*;
 use ng_verifier::verifier::Verifier;
 
 use ng_wallet::bip39::encode_mnemonic;
-use ng_wallet::emojis::encode_pazzle;
-use ng_wallet::{create_wallet_first_step_v0, create_wallet_second_step_v0, types::*};
+use ng_wallet::emojis::{display_pazzle, display_pazzle_one, encode_pazzle};
+use ng_wallet::{
+    create_wallet_first_step_v0, create_wallet_second_step_v0, display_mnemonic, types::*,
+};
 
 #[cfg(not(target_family = "wasm"))]
 use ng_client_ws::remote_ws::ConnectionWebSocket;
@@ -1582,10 +1584,10 @@ pub fn wallet_to_wallet_recovery(
 
 /// Generates the Recovery PDF containing the Wallet, PIN, Pazzle and Mnemonic.
 pub async fn wallet_recovery_pdf(
-    content: NgQRCodeWalletRecoveryV0,
+    recovery: NgQRCodeWalletRecoveryV0,
     size: u32,
 ) -> Result<Vec<u8>, NgError> {
-    let ser = serde_bare::to_vec(&content)?;
+    let ser = serde_bare::to_vec(&recovery)?;
     if ser.len() > 2_953 {
         return Err(NgError::InvalidPayload);
     }
@@ -1607,20 +1609,7 @@ pub async fn wallet_recovery_pdf(
     let tree = svg2pdf::usvg::Tree::from_str(&wallet_svg, &options)
         .map_err(|e| NgError::WalletError(e.to_string()))?;
 
-    // PDF uses A4 format (21cm x 29.7cm)
-    // TODO: instead of to_pdf in the next line, do to_chunk, and then add the text below the SVG.
-    // the SVG should take all the width of the A4 (so that only 29.7-21 = 8cm remains below the SVG, for all the following)
-    // the text is :
-    // - one line with : "PIN = 1234 pazzle = cat_slug:emoji_slug cat_slug:emoji_slug ...[x9]"
-    // - one line with the 9 emoji SVGs (with size so they fit in one line, width of the A4)
-    // - one line with : "mnemonic = [12 words of mnemonic]"
-    // - one line with recovery_str (it is quite long. choose a font size that make it fit here so the whole document is only one page)
-
-    // you can use the methods of pdf_writer library.
-
     let (chunk, qrcode_ref) = svg2pdf::to_chunk(&tree, ConversionOptions::default());
-    // probably then: add the text with chunk.stream() or chunk.indirect()
-
     //let pdf_buf = svg2pdf::to_pdf(&tree, ConversionOptions::default(), PageOptions::default());
 
     // Define some indirect reference ids we'll use.
@@ -1632,17 +1621,52 @@ pub async fn wallet_recovery_pdf(
     let font_name = Name(b"F1");
     let qrcode_name = Name(b"Im1");
 
+    let chunks = recovery_str
+        .as_bytes()
+        .chunks(92)
+        .map(|buf| buf)
+        .collect::<Vec<&[u8]>>();
+
     let mut content = Content::new();
-    content.begin_text();
-    content.set_font(font_name, 14.0);
-    content.next_line(108.0, 734.0);
-    content.show(Str(b"Hello World from Rust!"));
-    content.end_text();
-    content.begin_text();
-    content.set_font(font_name, 14.0);
-    content.next_line(15.0, 810.0);
-    content.show(Str(recovery_str.as_bytes()));
-    content.end_text();
+
+    for (line, string) in chunks.iter().enumerate() {
+        content.begin_text();
+        content.set_font(font_name, 10.0);
+        content.next_line(20.0, 810.0 - line as f32 * 15.0);
+        content.show(Str(*string));
+        content.end_text();
+    }
+
+    let pazzle: Vec<String> = display_pazzle(&recovery.pazzle)
+        .iter()
+        .map(|p| p.1.to_string())
+        .collect();
+    let mnemonic = display_mnemonic(&recovery.mnemonic);
+
+    let credentials = format!(
+        "PIN:{}{}{}{} PAZZLE:{} MNEMONIC:{}",
+        recovery.pin[0],
+        recovery.pin[1],
+        recovery.pin[2],
+        recovery.pin[3],
+        pazzle.join(" "),
+        mnemonic.join(" ")
+    );
+
+    let chunks = credentials
+        .as_bytes()
+        .chunks(92)
+        .map(|buf| buf)
+        .collect::<Vec<&[u8]>>();
+
+    for (line, string) in chunks.iter().enumerate() {
+        content.begin_text();
+        content.set_font(font_name, 10.0);
+        content.next_line(20.0, 630.0 - line as f32 * 15.0);
+        content.show(Str(*string));
+        content.end_text();
+    }
+
     content.save_state();
     content.transform([595.0, 0.0, 0.0, 595.0, 0.0, 0.0]);
     content.x_object(qrcode_name);
@@ -2924,7 +2948,6 @@ mod test {
         let mnemonic = encode_mnemonic(&mnemonic_words).expect("encode_mnemonic");
 
         let wallet_recovery = wallet_to_wallet_recovery(&wallet, pazzle, mnemonic, pin);
-
         let pdf_buffer = wallet_recovery_pdf(wallet_recovery, 600)
             .await
             .expect("wallet_recovery_pdf");
diff --git a/ng-app/src/classes.ts b/ng-app/src/classes.ts
index 2f05ec25..d1f6a159 100644
--- a/ng-app/src/classes.ts
+++ b/ng-app/src/classes.ts
@@ -16,7 +16,7 @@
 // "media/image", "media/reel", "media/album", "media/video", "media/audio", "media/song", "media/subtitle", "media/overlay",
 // "social/channel", "social/stream", "social/contact", "social/event", "social/calendar", "social/scheduler", "social/reaction"
 // "prod/task", "prod/project", "prod/issue", "prod/form", "prod/filling", "prod/cad", "prod/slides", "prod/question", "prod/answer", "prod/poll", "prod/vote"
-// "file", "file/iana/*", "file/gimp", "file/inkscape", "file/kdenlive", "file/blender", "file/openscad", "file/lyx", "file/scribus", "file/libreoffice",
+// "file", "file/iana/*", "file/gimp", "file/inkscape", "file/kdenlive", "file/blender", "file/openscad", "file/lyx", "file/scribus", "file/libreoffice", "file/audacity"
 
 
 // application/vnd.api+json
diff --git a/ng-app/src/routes/WalletCreate.svelte b/ng-app/src/routes/WalletCreate.svelte
index 1dde17ee..ffd7e031 100644
--- a/ng-app/src/routes/WalletCreate.svelte
+++ b/ng-app/src/routes/WalletCreate.svelte
@@ -1539,7 +1539,27 @@
           {#if !ready}
             <div class=" max-w-6xl lg:px-8 mx-auto px-4 text-primary-700">
               {$t("pages.wallet_create.creating")}
-              <Spinner className="mt-10 h-6 w-6 mx-auto" />
+              <svg
+                class="animate-spin mt-10 h-6 w-6 mx-auto"
+                xmlns="http://www.w3.org/2000/svg"
+                fill="none"
+                stroke="currentColor"
+                viewBox="0 0 24 24"
+              >
+                <circle
+                  class="opacity-25"
+                  cx="12"
+                  cy="12"
+                  r="10"
+                  stroke="currentColor"
+                  stroke-width="4"
+                />
+                <path
+                  class="opacity-75"
+                  fill="currentColor"
+                  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
+                />
+              </svg>
             </div>
           {:else}
             <div class="text-left mx-4">
diff --git a/ng-wallet/src/emojis.rs b/ng-wallet/src/emojis.rs
index c5930dd7..8867e029 100644
--- a/ng-wallet/src/emojis.rs
+++ b/ng-wallet/src/emojis.rs
@@ -1258,13 +1258,20 @@ pub fn display_pazzle(pazzle: &Vec<u8>) -> Vec<(&'static str, &'static str)> {
     for emoji in pazzle {
         let cat = (emoji & 240) >> 4;
         let idx = emoji & 15;
-        res.push((
-            EMOJI_CAT[cat as usize],
-            EMOJIS.get(&EMOJI_CAT[cat as usize]).unwrap()[idx as usize].code,
-        ));
+        let cat_str = EMOJI_CAT[cat as usize];
+        res.push((cat_str, EMOJIS.get(cat_str).unwrap()[idx as usize].code));
     }
     res
 }
+
+pub fn display_pazzle_one(pazzle: &Vec<u8>) -> Vec<String> {
+    let res: Vec<String> = display_pazzle(pazzle)
+        .into_iter()
+        .map(|(cat, emoji)| String::from(format!("{cat}:{emoji}")))
+        .collect();
+    res
+}
+
 //use ng_repo::log::*;
 
 /// taking a list of pazzle words, returns a list of u8 codes
diff --git a/ng-wallet/src/lib.rs b/ng-wallet/src/lib.rs
index 6a695944..bd605736 100644
--- a/ng-wallet/src/lib.rs
+++ b/ng-wallet/src/lib.rs
@@ -408,25 +408,6 @@ pub fn display_mnemonic(mnemonic: &[u16; 12]) -> Vec<String> {
     res
 }
 
-use crate::emojis::{EMOJIS, EMOJI_CAT};
-
-pub fn display_pazzle(pazzle: &Vec<u8>) -> Vec<String> {
-    let res: Vec<String> = pazzle
-        .into_iter()
-        .map(|i| {
-            let cat = i >> 4;
-            let idx = i & 15;
-            let cat_str = EMOJI_CAT[cat as usize];
-            String::from(format!(
-                "{}:{}",
-                cat_str,
-                EMOJIS.get(cat_str).unwrap()[idx as usize].code
-            ))
-        })
-        .collect();
-    res
-}
-
 pub fn gen_shuffle_for_pazzle_opening(pazzle_length: u8) -> ShuffledPazzle {
     let mut rng = rand::thread_rng();
     let mut category_indices: Vec<u8> = (0..pazzle_length).collect();
@@ -870,7 +851,7 @@ mod test {
         let _ = file.write_all(&ser_wallet);
 
         log_debug!("wallet id: {}", res.wallet.id());
-        log_debug!("pazzle {:?}", display_pazzle(&res.pazzle));
+        log_debug!("pazzle {:?}", display_pazzle_one(&res.pazzle));
         log_debug!("mnemonic {:?}", display_mnemonic(&res.mnemonic));
         log_debug!("pin {:?}", pin);