From 3dbc5fd1e12386fe0224c24c416d70305aa55695 Mon Sep 17 00:00:00 2001
From: Dominik Hebeler <dominik@hebeler.club>
Date: Fri, 14 Jul 2023 15:32:41 +0200
Subject: [PATCH] added new page to make process of creating a key clear

---
 pass/lang/en/key.json                |   7 +-
 pass/lang/en/login.json              |  21 ++++-
 pass/public/js/create.js             |  49 ++++++++++++
 pass/public/styles/key/create.less   | 115 +++++++++++++++++++++++++++
 pass/routes/key.js                   |  26 +++++-
 pass/views/key.ejs                   |   8 +-
 pass/views/login/create.ejs          |  29 +++++++
 pass/views/login/key.ejs             |  13 +--
 pass/views/templates/page_header.ejs |   2 +-
 9 files changed, 240 insertions(+), 30 deletions(-)
 create mode 100644 pass/public/js/create.js
 create mode 100644 pass/public/styles/key/create.less
 create mode 100644 pass/views/login/create.ejs

diff --git a/pass/lang/en/key.json b/pass/lang/en/key.json
index a5c9eae..d465317 100644
--- a/pass/lang/en/key.json
+++ b/pass/lang/en/key.json
@@ -19,14 +19,9 @@
     "fill": "Charge",
     "orders": "Orders",
     "valid_until": "valid until",
-    "new": {
-      "heading": "This is how it continues",
-      "text": "Your MetaGer key has been created and set up. It just needs to be charged. Please make sure beforehand that you have saved the key so that you can enter it if the setting in your browser is deleted. For this you need either the key itself, above URL or the QR code as a file.",
-      "charge": "Charge key now"
-    },
     "startpagelinks": {
       "adfree": "To the ad-free search",
       "regular": "To MetaGer search"
     }
   }
-}
+}
\ No newline at end of file
diff --git a/pass/lang/en/login.json b/pass/lang/en/login.json
index 663357d..92d77ef 100644
--- a/pass/lang/en/login.json
+++ b/pass/lang/en/login.json
@@ -13,5 +13,22 @@
   },
   "submit": "Submit",
   "create": "Set up ad-free search",
-  "error": "A valid key, or a valid backup file is required."
-}
+  "error": "A valid key, or a valid backup file is required.",
+  "generate": {
+    "heading": "Ihr neuer MetaGer Schlüssel",
+    "first_step": "MetaGer Schlüssel erstellen",
+    "copy-key": "Schlüssel kopieren",
+    "initialize": {
+      "description": "Erstellen Sie sich ganz einfach auf Knopfdruck einen neuen Schlüssel für werbefreie Suchen",
+      "button": "Schlüssel jetzt erstellen"
+    },
+    "processing": {
+      "description": "Einen Moment: Wir erstellen Ihnen gerade einen neuen, zufälligen MetaGer Schlüssel..."
+    },
+    "store": {
+      "description": "Ihr neuer Schlüssel wurde erstellt und in diesem Browser hinterlegt. Bitte speichern Sie ihn dennoch ab! Er wird Ihre Token enthalten, die für die werbefreie Suche verwendet werden. Sie müssen den Schlüssel nur eingeben, wenn Sie ihn auf weiteren Geräten verwenden möchten, oder wenn die Cookies in Ihrem Browser gelöscht wurden.",
+      "url": "Sie können auch diese URL z.B. als Lesezeichen abspeichern, um den Schlüssel ohne manuelle Eingabe im Browser wiederherzustellen."
+    },
+    "next": "Weiter zum nächsten Schritt: "
+  }
+}
\ No newline at end of file
diff --git a/pass/public/js/create.js b/pass/public/js/create.js
new file mode 100644
index 0000000..0b08fa6
--- /dev/null
+++ b/pass/public/js/create.js
@@ -0,0 +1,49 @@
+let container = document.getElementById("create");
+let keyContainer = document.getElementById("key");
+let initializeButton = document.querySelector("div.initialize > button");
+let key = keyContainer.dataset.key;
+
+let characters = ["a", "b", "c", "d", "e", "f", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
+
+container.dataset.state = "initialize";
+keyContainer.parentNode.querySelector("button.button").classList.add("disabled");
+
+let runtime = 5000; // Runtime in milliseconds
+let starttime = Date.now();
+
+initializeButton.addEventListener("click", () => {
+    starttime = Date.now();
+    container.dataset.state = "processing";
+    generateRandomKey(0);
+    let interval = setInterval(() => {
+        if ((starttime + runtime) < Date.now()) {
+            clearInterval(interval);
+            container.dataset.state = "finished";
+            keyContainer.parentNode.querySelector("button.button").classList.remove("disabled");
+        }
+        let steps = runtime / 32;
+        let skipped_characters = Math.ceil((Date.now() - starttime) / steps);
+        generateRandomKey(skipped_characters);
+    }, 100);
+});
+
+function generateRandomKey(skip) {
+    let random_key = "";
+    if (!skip) {
+        skip = 0;
+    } else {
+        skip = Math.max(Math.min(32, skip), 0);
+    }
+    for (let i = 0; i < 32; i++) {
+        let character = "";
+        if (i == 8 || i == 12 || i == 16 || i == 20) {
+            random_key += "-";
+        }
+        character = characters[Math.floor(Math.random() * characters.length)];
+        if (skip > i) {
+            character = key.replaceAll("-", "").charAt(i);
+        }
+        random_key += character;
+    }
+    keyContainer.value = random_key;
+}
\ No newline at end of file
diff --git a/pass/public/styles/key/create.less b/pass/public/styles/key/create.less
new file mode 100644
index 0000000..fff98bd
--- /dev/null
+++ b/pass/public/styles/key/create.less
@@ -0,0 +1,115 @@
+@import "../misc/vars.less";
+#create {
+  display: grid;
+  gap: 2rem;
+  h1,
+  h2 {
+    margin: 0;
+    line-height: 1;
+  }
+  > h2 {
+    border-bottom: 1px solid @color-main;
+  }
+  > div.initialize {
+    display: grid;
+    gap: 1rem;
+    > button {
+      margin: 0 auto;
+      padding-block: 0.5rem;
+    }
+  }
+  div.copy-group {
+    > button {
+      padding-block: 0.5rem;
+      &.disabled {
+        color: lighten(#777, 50%);
+      }
+    }
+    > input {
+      min-width: 50ch;
+      padding-inline: 1rem;
+    }
+    &.key {
+      display: grid;
+      grid-template-columns: max-content max-content;
+      place-content: center;
+      > label {
+        grid-column: span 2;
+        color: lighten(@font-color-on-white, 20%);
+        font-size: 0.8rem;
+      }
+
+      @media (max-width: 580px) {
+        grid-template-columns: 1fr;
+        > label {
+          grid-column: initial;
+        }
+        > input {
+          padding-block: 0.5rem;
+          min-width: initial;
+        }
+        > button {
+          width: 100%;
+        }
+      }
+      > input {
+        letter-spacing: 0.2ch;
+        text-align: center;
+      }
+    }
+  }
+  > div#processing-description {
+    text-align: center;
+  }
+  > div#store {
+    display: grid;
+    gap: 1rem;
+
+    > div.copy-group {
+      display: grid;
+      grid-template-columns: 1fr max-content;
+      place-content: center;
+      @media (max-width: 580px) {
+        grid-template-columns: 1fr;
+        > button {
+          width: 100%;
+        }
+        > input {
+          padding-block: 0.5rem;
+          min-width: initial;
+        }
+      }
+    }
+  }
+  > hr {
+    width: 100%;
+  }
+  > a.button {
+    margin: 0 auto;
+    font-size: clamp(12px, 4vw, 16px);
+  }
+
+  &[data-state="initialize"] {
+    > div.key,
+    > div#processing-description,
+    > div#store,
+    > hr,
+    > a.button {
+      display: none;
+    }
+  }
+  &[data-state="finished"] {
+    > div.initialize,
+    > div#processing-description {
+      display: none;
+    }
+  }
+  &[data-state="processing"] {
+    > div.initialize,
+    > div#store,
+    > hr,
+    > a.button {
+      display: none;
+    }
+  }
+}
diff --git a/pass/routes/key.js b/pass/routes/key.js
index 07f72d2..9aba57b 100644
--- a/pass/routes/key.js
+++ b/pass/routes/key.js
@@ -17,9 +17,28 @@ router.get("/create", function (req, res, next) {
     return res.redirect(`${res.locals.baseDir}/key/enter`);
   }
   Key.GET_NEW_KEY().then((key) => {
-    res.redirect(
-      `${res.locals.baseDir}/key/` + key.get_key() + "?new=true#second-nav"
-    );
+    res.locals.key = key;
+
+    let setting_url = res.baseDir.replace(/\/keys.*/, "");
+    setting_url += "/meta/settings/load-settings?";
+
+    let params = {
+      key: key.get_key(),
+    };
+
+    for (let cookie in req.cookies) {
+      if (
+        cookie.match(
+          /^(dark_mode$|new_tab$|zitate$|web_|bilder_|produkte_|nachrichten_|science_)/
+        )
+      ) {
+        params[cookie] = req.cookies[cookie];
+      }
+    }
+    setting_url += new URLSearchParams(params).toString();
+    res.locals.setting_url = setting_url;
+
+    res.render("login/create");
   });
 });
 
@@ -145,7 +164,6 @@ router.use("/:key", param("key").isUUID(4), async (req, res, next) => {
 
   Key.GET_KEY(req.params.key, false).then((key) => {
     req.data = {
-      created_new: req.query.new === "true" ? true : false,
       price: config.get("price"),
       key: {
         key: key,
diff --git a/pass/views/key.ejs b/pass/views/key.ejs
index 16b52e2..eedb006 100644
--- a/pass/views/key.ejs
+++ b/pass/views/key.ejs
@@ -40,13 +40,7 @@
   <a class="button <% if(page === "fill") { _%> active<%_ } _%>" href="<%= links.fill_url %>"><%= req.t("key.fill", {ns: "key"}) _%></a>
   <a class="button <% if(page === "order") { _%> active<%_ } %>" href="<%= links.orders_url %>"><%= req.t("key.orders", {ns: "key"}) _%></a>
 </nav>
-<%_ if (created_new) { _%>
-<div id="store">
-  <h2><%= req.t("key.new.heading", {ns: "key"}) %>:</h2>
-  <p><%= req.t("key.new.text", {ns: "key"}) %></p>
-  <a class="button" href="<%= baseDir _%>/key/<%= key.key.get_key() %>#second-nav"><%= req.t("key.new.charge", {ns: "key"}) %></a>
-</div>
-<% } else if (page === "order") { %>
+<% if (page === "order") { %>
 <%_ if(typeof order === "undefined") { _%>
 <%- include('./orders/orders', {}); %>
 <%_ } else { _%>
diff --git a/pass/views/login/create.ejs b/pass/views/login/create.ejs
new file mode 100644
index 0000000..e80d616
--- /dev/null
+++ b/pass/views/login/create.ejs
@@ -0,0 +1,29 @@
+<%- include('../templates/page_header', {css:
+[`${baseDir}/styles/key/create.css`], js:[`${baseDir}/js/create.js`]}); %>
+
+<div id="create" data-state="finished">
+  <h1><%= req.t("generate.heading", {ns: "login"}) _%></h1>
+  <h2>1. <%= req.t("generate.first_step", {ns: "login"}) _%></h2>
+  <div class="initialize">
+    <div><%= req.t("generate.initialize.description", {ns: "login"}) _%></div>
+    <button class="button"><%= req.t("generate.initialize.button", {ns: "login"}) _%></button>
+  </div>
+  <div class="copy-group key">
+    <label for="key"><%= req.t("title", {ns: "pageheader"}) _%></label>
+    <input type="text" name="key" id="key" value="<%= key.get_key() _%>" data-key="<%= key.get_key() _%>" readonly>
+    <button class="copy button" data-target="key"><%= req.t("generate.copy-key", {ns: "login"}) _%></button>
+  </div>
+  <div id="processing-description"><%= req.t("generate.processing.description", {ns: "login"}) _%></div>
+  <div id="store">
+
+    <div><%= req.t("generate.store.description", {ns: "login"}) _%></div>
+    <div><%= req.t("generate.store.url", {ns: "login"}) _%></div>
+    <div class="copy-group setting-url">
+      <input type="text" name="setting-url" id="setting-url" value="<%= setting_url _%>">
+      <button class="copy button" data-target="setting-url"><%= req.t("key.copy-url", {ns: "key"}) _%></button>
+    </div>
+  </div>
+  <hr>
+  <a class="button" href="<%= baseDir _%>/key/<%= key.get_key() _%>#charge"><%= req.t("generate.next", {ns: "login"}) _%> <%= req.t("howitworks.steps.1.heading", {ns: "index"}) _%></a>
+
+  <%- include('../templates/page_footer'); -%>
\ No newline at end of file
diff --git a/pass/views/login/key.ejs b/pass/views/login/key.ejs
index 51787fa..7441d54 100644
--- a/pass/views/login/key.ejs
+++ b/pass/views/login/key.ejs
@@ -8,12 +8,7 @@
         <%= req.t("heading", {ns: "login"}) _%>
         <a href="<%= baseDir %>" class="hint-icon">?</a>
       </h3>
-      <form
-        id="key-form"
-        action="<%= baseDir _%>/key/enter"
-        method="post"
-        enctype="multipart/form-data"
-      >
+      <form id="key-form" action="<%= baseDir _%>/key/enter" method="post" enctype="multipart/form-data">
         <input type="text" name="key" id="key" placeholder="<%=
         req.t("key-input.placeholder", {ns: "login"}) _%>" />
       </form>
@@ -57,9 +52,7 @@
   </div>
 </div>
 <div id="create">
-  <a href="<%= baseDir _%>/key/create#second-nav" class="button"
-    ><%= req.t("create", {ns: "login"}) _%></a
-  >
+  <a href="<%= baseDir _%>/key/create#create" class="button"><%= req.t("create", {ns: "login"}) _%></a>
 </div>
 
-<%- include('../templates/page_footer'); -%>
+<%- include('../templates/page_footer'); -%>
\ No newline at end of file
diff --git a/pass/views/templates/page_header.ejs b/pass/views/templates/page_header.ejs
index 6b5ddfb..d0f036c 100644
--- a/pass/views/templates/page_header.ejs
+++ b/pass/views/templates/page_header.ejs
@@ -92,7 +92,7 @@
       <li><a href="<%= baseDir _%>/key/remove" class="" id="key-remove"><%= req.t("rightnav.logout", { ns: 'pageheader'}) _%></a></li>
       <%_ } else { _%>
       <li><a href="<%= baseDir _%>/key/enter"><%= req.t("rightnav.enter", { ns: 'pageheader'}) _%></a></li>
-      <li><a href="<%= baseDir _%>/key/create" class="button"><%= req.t("rightnav.start", { ns: 'pageheader'}) _%></a></li>
+      <li><a href="<%= baseDir _%>/key/create#create" class="button"><%= req.t("rightnav.start", { ns: 'pageheader'}) _%></a></li>
       <%_ } _%>
     </ul>
 
-- 
GitLab