commit ff50b613b651068652ba7d764f8017b73ebe962c
parent a7e9cfae214d1fe2fdb5d3ee217faadc64d97855
Author: AbdallahWario <abdallwario96@gmail.com>
Date: Thu, 22 Aug 2024 03:17:13 -0400
ui made more intuitive and responsive
Diffstat:
M | index.html | | | 274 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | style.css | | | 492 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
2 files changed, 651 insertions(+), 115 deletions(-)
diff --git a/index.html b/index.html
@@ -1,6 +1,8 @@
<html>
<head>
<title>Forro contact form</title>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script defer src="node_modules/alpinejs/dist/cdn.min.js"></script>
<script src="node_modules/openpgp/dist/openpgp.min.js"></script>
<script src="node_modules/jssha/dist/sha256.js"></script>
@@ -14,7 +16,9 @@
<link rel="stylesheet" type="text/css" href="style.css"></link>
</head>
- <body x-data="{
+ <body
+
+ x-data="{
unlock_set: false,
message_status: '',
message_count: g_counter,
@@ -78,81 +82,16 @@
>
- <h1><a href="https://defalsify.org/git/forro"
- x-text="getTitle"></a>
- </h1>
+
<div id="app">
- <div id="helpdiv" style="height: 6em;"
- x-show="isHelp"
- >
- <hr/>
- <template x-data="{
- help_cap: 128, // if set to 0 then no buffer
- help_display_count: 5, // if help_cap is 0, ignore
- help_contents: [],
- help_lastcount: 0,
- help_count: 0,
- help_crsr: -1,
-
- addHelpContents(v) {
- if (this.help_cap == 0) {
- this.help_display_count = v.length;
- this.help_count = v.length;
- this.help_contents = v;
- this.help_crsr = this.help_count - 1;
- } else {
- for (let i = 0; i < v.length; i++) {
- this.help_crsr += 1;
- this.help_crsr %= this.help_cap;
- this.help_contents[this.help_crsr] = v[i];
- if (this.help_count < this.help_cap) {
- this.help_count += 1;
- }
- }
- }
- if (v.length > 0) {
- this.help_lastcount = v.length;
- }
- },
-
- get helpContents() {
- if (this.help_crsr < 0) {
- return [];
- }
- let contents = [];
-
- let l = this.help_display_count;
- if (l > this.help_count) {
- l = this.help_count;
- }
+
+ <h1><a href="https://defalsify.org/git/forro"
+ x-text="getTitle"></a>
+ </h1>
- let c = (this.help_crsr - l + 1);
- if (c < 0) {
- c = l + c;
- }
-
- new_threshold = l - this.help_lastcount;
- for (let i = 0; i < l; i++) {
- if (this.help_cap > 0) {
- c %= this.help_cap;
- }
- is_old = i < new_threshold;
- v = [this.help_contents[c], is_old ? 'old' : 'new'];
- contents.push(v); //this.help_contents[c]);
- c += 1;
- }
- return contents;
- },
- }"
- x-init="tryHelpFor('welcome');"
- x-for="(v) in helpContents"
- @help.window="addHelpContents($event.detail.v);">
- <p style="font-size: 0.8em; line-height: 0.3em;" x-html="v[0]" :class="v[1]" />
- </template>
- </div>
- <hr/>
+
<div id="localkey" x-data="{
passphrase_cache: '',
passphrase_status: '',
@@ -165,6 +104,8 @@
x-show='!unlockedKey'
+ class="localkey-container"
+
>
<input name="pwd" type="password"
x-model='passphrase_cache'
@@ -172,6 +113,8 @@
@passfail.window='passphrase_status = "wrong_passphrase"; passphrase_cache = "";';
@messagestatechange.window='if (checkState(STATE["LOCAL_KEY"])) { passphrase_default_status = "please unlock key"; };'
@rst.window='passphrase_status = "please create new key";'
+ class="input-passphrase"
+
>
<button x-data="{
go_label: 'go',
@@ -183,12 +126,19 @@
@rst.window='go_label = "go";'
@click='setPwd(passphrase_cache);'
+ class="btn-primary"
+
></button>
- <button x-show='!haveKey' @click='tryHelpFor("nopass"); setPwd();' >without passphrase</button>
+ <button x-show='!haveKey' @click='tryHelpFor("nopass"); setPwd();' class="btn-secondary"
+ >without passphrase</button>
</div>
- <div id="message_panel"
+
+
+ <div class="container">
+
+ <div class="form" id="message_panel"
x-show='unlockedKey'
x-init='setUp();'
x-data="{
@@ -267,32 +217,131 @@
document.getElementById("fileAdder").value="";
}
'>
- <dl>
- <dt>Status:</dt>
- <dd x-text="message_status" x-on:messagestatechange.window="defaultname = !g_local_key_identified;"></dd>
-
- <dt>Your identity:</dt>
- <dd><a x-text="keyDisplay" title="Click to download your private key" x-bind:href="localKeyArmor" x-bind:download="localKeyFilename"></a></dd>
- <dt>Their identity:</dt>
- <dd><a x-text="rkeyDisplay" title="Click to download the recipient's public key" x-bind:href="remoteKeyArmor" x-bind:download="remoteKeyFilename"></a></dd>
- <dt>Message number:</dt>
- <dd x-text="message_count"></dd>
- <dt>Your receipt:<dt>
- <dd><a x-bind:href="g_data_endpoint + '/' + rcpt" x-text="rcpt"></a></dd>
- <dt>Add message:</dt>
- <dd>
+
+
+ <div id="helpdiv"
+ x-show="isHelp"
+ >
+ <h3 class="title">Let's get in touch</h3>
+
+ <template x-data="{
+ help_cap: 128, // if set to 0 then no buffer
+ help_display_count: 5, // if help_cap is 0, ignore
+ help_contents: [],
+ help_lastcount: 0,
+ help_count: 0,
+ help_crsr: -1,
+
+ addHelpContents(v) {
+ if (this.help_cap == 0) {
+ this.help_display_count = v.length;
+ this.help_count = v.length;
+ this.help_contents = v;
+ this.help_crsr = this.help_count - 1;
+ } else {
+ for (let i = 0; i < v.length; i++) {
+ this.help_crsr += 1;
+ this.help_crsr %= this.help_cap;
+ this.help_contents[this.help_crsr] = v[i];
+ if (this.help_count < this.help_cap) {
+ this.help_count += 1;
+ }
+ }
+ }
+ if (v.length > 0) {
+ this.help_lastcount = v.length;
+ }
+ },
+
+ get helpContents() {
+ if (this.help_crsr < 0) {
+ return [];
+ }
+ let contents = [];
+
+ let l = this.help_display_count;
+ if (l > this.help_count) {
+ l = this.help_count;
+ }
+
+
+ let c = (this.help_crsr - l + 1);
+ if (c < 0) {
+ c = l + c;
+ }
+
+ new_threshold = l - this.help_lastcount;
+ for (let i = 0; i < l; i++) {
+ if (this.help_cap > 0) {
+ c %= this.help_cap;
+ }
+ is_old = i < new_threshold;
+ v = [this.help_contents[c], is_old ? 'old' : 'new'];
+ contents.push(v); //this.help_contents[c]);
+ c += 1;
+ }
+ return contents;
+ },
+ }"
+ x-init="tryHelpFor('welcome');"
+ x-for="(v) in helpContents"
+ @help.window="addHelpContents($event.detail.v);">
+ <p x-html="v[0]" :class="v[1] == 'old' ? 'help-text old' : 'help-text new'" />
+
+ </template>
+ </div>
+
+ <div class="contact-form">
+
+
+
+ <form autocomplete="off">
+ <h3 class="title">Get in touch!</h3>
+ <div class="details">
+
+ <div >
+ <label for="status">Status:</label>
+ <span x-text="message_status" x-on:messagestatechange.window="defaultname = !g_local_key_identified;"></span>
+ </div>
+
+ <div>
+ <label for="your-identity">Your identity:</label>
+ <p><a x-text="keyDisplay" title="Click to download your private key" x-bind:href="localKeyArmor" x-bind:download="localKeyFilename"></a></p>
+ </div>
+
+
+
+ <div>
+ <label for="their-identity">Their identity:</label>
+ <p><a x-text="rkeyDisplay" title="Click to download the recipient's public key" x-bind:href="remoteKeyArmor" x-bind:download="remoteKeyFilename"></a></p>
+ </div>
+
+
+ <div>
+ <label for="message-number">Message number:</label>
+ <span x-text="message_count"></span>
+ </div>
+
+ <div>
+ <label for="receipt">Your receipt:</label>
+ <p><a x-bind:href="g_data_endpoint + '/' + rcpt" x-text="rcpt"></a></p>
+ </div>
+ </div>
+ <div class="input-container textarea">
+ <label for="">Message:</label>
+
<textarea
- cols=72
- rows=10
+ name="message"
+ placeholder="message"
+ class="input"
x-model="content"
@focus="tryHelpFor('writemsg');"
@messagestatechange.window="if (checkState(STATE.ACK_MESSAGE)) {console.log('foo'); content = '';}"
>
</textarea>
- </dd>
- <dt>Add files:</dt>
- <dd>
- <input type="file" id="fileAdder"
+ </div>
+ <div class="input-container">
+ <input class="btn" type="file" id="fileAdder"
@change="fileChange();"
/>
<ol>
@@ -305,14 +354,13 @@
<li x-text="v"></li>
</template>
</ol>
- </dd>
- </dl>
- <br/>
+ </div>
+
<div x-data='{
realname: "",
realemail: "",
}'>
- <div x-show='defaultname'
+ <div x-show='defaultname && !g_local_key_identified'
x-data='{
identify: false,
}'
@@ -323,8 +371,12 @@
<option value=0 defaultselected>Stay anonymous</option>
<option value=1>Identify yourself</option>
</select>
- <div x-show="identify">
- <input name="id_name" placeholder="name" x-model="realname" /> <input name="id_email" placeholder="email" x-model="realemail" />
+ <div class="input-container identify" x-show="identify">
+
+ <input name="id_name" class="input" type="text" placeholder="Name" x-model="realname" />
+ <input name="id_email" class="input" type="email" placeholder="Email" x-model="realemail" />
+
+
</div>
</div>
<div x-data="{
@@ -333,21 +385,25 @@
x-on:messagestatechange.window='ready = checkState(STATE["RTS"]);'
>
<button
+ class="btn"
x-bind:disabled='!ready'
- @click="tryDispatch(content, realname, realemail, filez);">sign, encrypt and send</button>
+ @click="tryDispatch(content, realname, realemail, filez);">
+ sign, encrypt and send
+ </button>
</div>
</div>
+
+ </form>
+ <div id="reset" x-data="{ rst: false }">
+ <button x-show="haveKey && !rst" @click="rst = true;">Discard key</button>
+ <button x-show="rst" @click="if (confirm('Are you sure you want to discard the key?')) { rst = false; $dispatch('rst'); purgeLocalKey(); }">Discard key</button>
</div>
- <div id="reset"
- x-data="{
- rst: false,
- }">
- <button x-show='haveKey && !rst' @click='rst = true;'>Discard key</button>
- <button x-show='rst' @dblclick='rst = false; $dispatch("rst"); purgeLocalKey();'>Double click to confirm discard key</button>
+ </div>
</div>
-
+ </div>
+
+
<div id="dev" x-show='isDev'>
- <hr/>
<h2>Devmode details</h2>
<dl>
<dt>last event</dt>
diff --git a/style.css b/style.css
@@ -1,8 +1,487 @@
-div#helpdiv .old {
- color: #aaa;
+@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800&display=swap");
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
}
-h1 {
- margin-block-end: 1em;
- margin-block-start: 0.5em;
- font-size: 2em;
+
+body,
+input,
+textarea {
+ font-family: "Poppins", sans-serif;
}
+
+.container {
+ position: relative;
+ width: 100%;
+ min-height: 100vh;
+ padding: 2rem;
+ background-color: #fafafa;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.localkey-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.5rem;
+ padding: 2rem;
+ background-color: #ffffff;
+ border: 1px solid #e5e7eb;
+ border-radius: 0.5rem;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ width: 50%;
+ margin: 2rem auto;
+}
+
+.input-passphrase {
+ width: 80%;
+ padding: 0.75rem;
+ border: 1px solid #d1d5db;
+ border-radius: 0.375rem;
+ outline: none;
+ font-size: 1rem;
+}
+
+.input-passphrase:focus {
+ border-color: #3b82f6;
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
+}
+
+.btn-primary, .btn-secondary {
+ width: 80%;
+ padding: 0.75rem 1rem;
+ text-align: center;
+ font-weight: 500;
+ border-radius: 0.375rem;
+ cursor: pointer;
+ outline: none;
+ transition: background-color 0.3s ease, box-shadow 0.3s ease;
+}
+
+.btn-primary {
+ background-color: #3b82f6;
+ color: #ffffff;
+}
+
+.btn-primary:hover {
+ background-color: #2563eb;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.btn-secondary {
+ background-color: #6b7280;
+ color: #ffffff;
+}
+
+.btn-secondary:hover {
+ background-color: #4b5563;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.help-text {
+ font-size: 1rem;
+ color: #333;
+ margin-bottom: 0.5rem;
+ transition: background-color 0.3s, color 0.3s;
+ padding: 0.5rem;
+ border-radius: 4px;
+}
+
+.help-text.new {
+ background-color: #e0f7fa;
+ color: #007bff;
+}
+
+.help-text.old {
+ background-color: #f1f1f1;
+ color: #999;
+}
+
+.form {
+ width: 100%;
+ max-width: 820px;
+ background-color: #fff;
+ border-radius: 10px;
+ box-shadow: 0 0 20px 1px rgba(0, 0, 0, 0.1);
+ z-index: 1000;
+ overflow: hidden;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+}
+
+.contact-form {
+ background-color: #1abc9c;
+ position: relative;
+}
+
+
+.contact-form:before {
+ content: "";
+ position: absolute;
+ width: 26px;
+ height: 26px;
+ background-color: #1abc9c;
+ transform: rotate(45deg);
+ top: 50px;
+ left: -13px;
+}
+
+form {
+ padding: 1rem;
+ z-index: 10;
+ overflow: hidden;
+ position: relative;
+}
+
+.title {
+ color: #fff;
+ font-weight: 500;
+ font-size: 1.5rem;
+ line-height: 1;
+ margin-bottom: 0.7rem;
+}
+
+.input-container {
+ position: relative;
+ margin: 1rem 0;
+}
+
+form {
+ max-width: 600px;
+ width: 100%;
+ padding: 2rem;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ border-radius: 8px;
+ }
+
+
+
+ .input-container input[type="file"] {
+ padding: 10px;
+ font-size: 16px;
+ color: #1abc9c;
+ background-color: #ffffff;
+ border: 2px solid #1abc9c;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.3s ease, color 0.3s ease;
+ }
+
+ .input-container input[type="file"]:hover {
+ background-color: #0056b3;
+ }
+
+ .input-container input[type="file"]:focus {
+ outline: none;
+ box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.5);
+ }
+
+ .input-container ol {
+ list-style-type: decimal;
+ padding-left: 20px;
+ }
+
+ .input-container li {
+ font-size: 14px;
+ color: #333;
+ padding: 5px 0;
+ }
+
+.input {
+ width: 100%;
+ outline: none;
+ border: 2px solid #fafafa;
+ background: none;
+ padding: 0.6rem 1.2rem;
+ color: #000;
+ font-weight: 500;
+ letter-spacing: 0.5px;
+ border-radius: 5px;
+ transition: 0.3s;
+ font-size: 16px;
+
+}
+
+textarea.input {
+ padding: 0.8rem 1.2rem;
+ min-height: 150px;
+ border-radius: 5px;
+ resize: none;
+ overflow-y: auto;
+}
+
+
+
+.btn {
+ padding: 0.6rem 1.3rem;
+ background-color: #fff;
+ border: 2px solid #fafafa;
+ font-size: 0.95rem;
+ color: #1abc9c;
+ line-height: 1;
+ border-radius: 5px;
+ outline: none;
+ cursor: pointer;
+ transition: 0.3s;
+ margin: 0;
+}
+
+#identity_select{
+ margin-bottom: 10px;
+ width: 200px;
+ padding: 10px;
+ font-size: 16px;
+ color: #333;
+ background-color: #f8f8f8;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ background-image: url('data:image/svg+xml;utf8,<svg fill="%23333" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/></svg>');
+ background-repeat: no-repeat;
+ background-position-x: 95%;
+ background-position-y: 50%;
+ cursor: pointer;
+}
+.btn:hover {
+ background-color: transparent;
+ color: #fff;
+}
+
+.input-container span {
+ position: absolute;
+ top: 0;
+ left: 25px;
+ transform: translateY(-50%);
+ font-size: 0.8rem;
+ padding: 0 0.4rem;
+ color: transparent;
+ pointer-events: none;
+ z-index: 500;
+}
+
+.input-container span:before,
+.input-container span:after {
+ content: "";
+ position: absolute;
+ width: 10%;
+ opacity: 0;
+ transition: 0.3s;
+ height: 5px;
+ background-color: #1abc9c;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.input-container span:before {
+ left: 50%;
+}
+
+.input-container span:after {
+ right: 50%;
+}
+
+
+
+
+.input-container.focus span:before,
+.input-container.focus span:after {
+ width: 50%;
+ opacity: 1;
+}
+
+#helpdiv {
+ padding: 2.3rem 2.2rem;
+ position: relative;
+}
+
+#helpdiv .title {
+ color: #1abc9c;
+}
+
+.text {
+ color: #333;
+ margin: 1.5rem 0 2rem 0;
+}
+
+.icon {
+ width: 28px;
+ margin-right: 0.7rem;
+}
+
+
+
+
+.square {
+ position: absolute;
+ height: 400px;
+ top: 50%;
+ left: 50%;
+ transform: translate(181%, 11%);
+ opacity: 0.2;
+}
+
+.details{
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.identify{
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+#reset {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ justify-content: center;
+ margin-top: 5px;
+ margin-bottom:5px ;
+ }
+
+ #reset button {
+ padding: 10px 20px;
+ font-size: 16px;
+ color: #fff;
+ background-color: #007bff;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ transition: background-color 0.3s ease;
+ }
+
+ #reset button:hover {
+ background-color: #0056b3;
+ }
+
+ #reset button:active {
+ background-color: #004085;
+ }
+
+ #reset button:focus {
+ outline: none;
+ box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.5);
+ }
+
+ p,
+span,dd {
+ word-wrap: break-word;
+ word-break: break-all;
+ max-width: 100%;
+}
+#dev {
+ margin-top: 2rem;
+ padding: 1rem;
+ background-color: #f1f1f1;
+ border: 1px solid #ccc;
+ border-radius: 8px;
+}
+
+#dev hr {
+ margin-bottom: 1rem;
+}
+
+#dev h2 {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+}
+
+#dev dl {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+#dev dt {
+ width: 100%;
+ font-weight: bold;
+ margin-top: 1rem;
+}
+
+#dev dd {
+ width: 100%;
+ margin-left: 0;
+ margin-bottom: 1rem;
+ overflow-wrap: break-word;
+}
+
+@media (max-width: 850px) {
+ .form {
+ grid-template-columns: 1fr;
+ }
+
+
+ .square {
+ transform: translate(140%, 43%);
+ height: 350px;
+ }
+
+
+ .text {
+ margin: 1rem 0 1.5rem 0;
+ }
+
+
+ .localkey-container {
+ width: 80%;
+ padding: 1.5rem;
+}
+
+.input-passphrase, .btn-primary, .btn-secondary {
+ width: 100%;
+ font-size: 1rem;
+}
+}
+
+@media (max-width: 480px) {
+ .container {
+ padding: 0.2rem;
+ }
+
+
+ .square{
+ display: none;
+ }
+
+
+ form, #helpdiv {
+ padding: 0.5rem;
+ }
+
+ .text,
+ .information{
+ font-size: 0.8rem;
+ }
+
+ .title {
+ font-size: 1.15rem;
+ }
+
+ .input {
+ padding: 0.45rem 1.2rem;
+ }
+
+ .btn {
+ padding: 0.45rem 1.2rem;
+ }
+ .localkey-container {
+ width: 95%;
+ padding: 1rem;
+}
+
+.input-passphrase, .btn-primary, .btn-secondary {
+ width: 100%;
+ font-size: 0.9rem;
+ padding: 0.65rem;
+}
+
+.btn-primary, .btn-secondary {
+ font-size: 1rem;
+ padding: 0.75rem 1rem;
+}
+}
+\ No newline at end of file