GamerC0der commited on
Commit
285efae
·
verified ·
1 Parent(s): 607e905

Update templates/home.html

Browse files
Files changed (1) hide show
  1. templates/home.html +236 -15
templates/home.html CHANGED
@@ -4,12 +4,29 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>AstraPay</title>
 
 
 
 
 
 
7
  <style>
8
  body {
9
  margin: 0;
10
  padding: 0;
11
  min-height: 100vh;
12
- background-color: #000000;
 
 
 
 
 
 
 
 
 
 
 
13
  font-family: Arial, sans-serif;
14
  }
15
  .main-content {
@@ -25,9 +42,11 @@
25
  .title {
26
  color: #ffffff;
27
  font-size: 4rem;
28
- font-weight: bold;
 
29
  text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.3);
30
  margin-bottom: 2rem;
 
31
  }
32
  .button {
33
  background-color: #ffffff;
@@ -220,6 +239,69 @@
220
  .form-textarea::placeholder {
221
  color: rgba(255, 255, 255, 0.4);
222
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  .modal-buttons {
224
  display: flex;
225
  gap: 0.75rem;
@@ -275,6 +357,16 @@
275
  margin-left: auto;
276
  margin-right: auto;
277
  }
 
 
 
 
 
 
 
 
 
 
278
  .banner {
279
  background: rgba(255, 193, 7, 0.15);
280
  border: 1px solid rgba(255, 193, 7, 0.3);
@@ -382,11 +474,14 @@
382
  </style>
383
  </head>
384
  <body>
 
 
 
385
  <div class="main-content">
386
  <div class="container">
387
  <h1 class="title">AstraPay</h1>
388
  <div class="top-banner">
389
- Contact Matthew on Slack if any issues occur.
390
  </div>
391
  <div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
392
  <button class="button" onclick="openModal()" style="display: flex; align-items: center; gap: 0.5rem;">
@@ -432,6 +527,7 @@
432
  <div class="form-group">
433
  <label class="form-label" for="description">Description</label>
434
  <textarea id="description" class="form-textarea" placeholder="Enter payment description" required></textarea>
 
435
  </div>
436
 
437
  <div class="form-group">
@@ -472,8 +568,9 @@
472
  <label class="form-label" for="claim-astras">Number of Astras</label>
473
  <input type="number" id="claim-astras" class="form-input" min="1" max="10000" placeholder="Enter number of astras" required oninput="validateClaimAstrasAmount(this)">
474
  <div style="font-size: 0.8rem; color: rgba(255, 255, 255, 0.6); margin-top: 0.5rem;">
475
- You'll send: <span id="claim-send-amount">-</span> Astras (+1%)<br>
476
- Claimable: <span id="claim-claim-amount">-</span> Astras (-1%)
 
477
  </div>
478
  </div>
479
 
@@ -510,8 +607,8 @@
510
  <div class="modal-header">Enter Link ID</div>
511
  <div class="modal-body">
512
  <div class="form-group">
513
- <label class="form-label" for="link-id">Enter 4-character code</label>
514
- <input type="text" id="link-id" class="form-input" placeholder="e.g., A3B7" maxlength="4" style="text-transform: uppercase; font-size: 1.2rem; letter-spacing: 0.5rem; text-align: center;" required oninput="this.value = this.value.toUpperCase().replace(/[^A-Z0-9]/g, '')" onkeypress="if(event.key === 'Enter') goToLink()">
515
  </div>
516
 
517
  <div class="modal-buttons">
@@ -536,6 +633,91 @@
536
  <div id="toastContainer" class="toast-container"></div>
537
 
538
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  function showToast(message, type = 'error') {
540
  const container = document.getElementById('toastContainer');
541
  const toast = document.createElement('div');
@@ -581,6 +763,9 @@
581
  modal.classList.add('show');
582
  }, 10);
583
  document.body.style.overflow = 'hidden';
 
 
 
584
  }
585
 
586
  function closeModal() {
@@ -591,13 +776,30 @@
591
  }, 200);
592
  document.body.style.overflow = '';
593
  document.getElementById('astras').value = '';
594
- document.getElementById('description').value = '';
595
  document.getElementById('recipient-email').value = '';
 
 
 
 
 
 
 
 
596
  }
597
 
598
  async function createPaymentLink() {
599
  const astras = document.getElementById('astras').value;
600
- const description = document.getElementById('description').value.trim();
 
 
 
 
 
 
 
 
 
 
601
  const recipientEmail = document.getElementById('recipient-email').value.trim();
602
 
603
  if (!astras) {
@@ -615,7 +817,11 @@
615
 
616
  if (!description) {
617
  showToast('Please enter a description');
618
- document.getElementById('description').focus();
 
 
 
 
619
  return;
620
  }
621
 
@@ -673,18 +879,31 @@
673
  document.getElementById('claim-astras').value = '';
674
  document.getElementById('claim-send-amount').textContent = '-';
675
  document.getElementById('claim-claim-amount').textContent = '-';
 
 
 
676
  }
677
 
678
  function validateClaimAstrasAmount(input) {
679
  const value = parseInt(input.value);
680
  if (!isNaN(value) && value > 0) {
681
- const sendAmount = Math.max(1, Math.floor(value * 1.01));
682
- const claimAmount = Math.max(1, Math.floor(value * 0.99));
 
 
 
 
683
  document.getElementById('claim-send-amount').textContent = sendAmount;
684
  document.getElementById('claim-claim-amount').textContent = claimAmount;
 
 
 
685
  } else {
686
  document.getElementById('claim-send-amount').textContent = '-';
687
  document.getElementById('claim-claim-amount').textContent = '-';
 
 
 
688
  }
689
  if (!isNaN(value) && value > 10000) {
690
  input.value = 10000;
@@ -755,10 +974,12 @@
755
  }
756
 
757
  async function goToLink() {
758
- const linkId = document.getElementById('link-id').value.trim().toUpperCase();
759
 
760
- if (!linkId || linkId.length !== 4) {
761
- showToast('Please enter a valid 4-character code');
 
 
762
  document.getElementById('link-id').focus();
763
  return;
764
  }
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>AstraPay</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Erica+One&display=swap" rel="stylesheet">
10
+ <link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
11
+ <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/turndown@7.1.3/dist/turndown.js"></script>
13
  <style>
14
  body {
15
  margin: 0;
16
  padding: 0;
17
  min-height: 100vh;
18
+ background: linear-gradient(180deg,
19
+ #0f0514 0%,
20
+ #1a0a1a 15%,
21
+ #2d1a3d 35%,
22
+ #4a2a5a 50%,
23
+ #3d2a4a 60%,
24
+ #2d1a3d 70%,
25
+ #1a0a1a 80%,
26
+ #0f0514 85%,
27
+ #000000 100%
28
+ );
29
+ background-attachment: fixed;
30
  font-family: Arial, sans-serif;
31
  }
32
  .main-content {
 
42
  .title {
43
  color: #ffffff;
44
  font-size: 4rem;
45
+ font-weight: normal;
46
+ font-family: 'Erica One', cursive;
47
  text-shadow: 2px 2px 4px rgba(255, 255, 255, 0.3);
48
  margin-bottom: 2rem;
49
+ letter-spacing: 0.02em;
50
  }
51
  .button {
52
  background-color: #ffffff;
 
239
  .form-textarea::placeholder {
240
  color: rgba(255, 255, 255, 0.4);
241
  }
242
+ #description-editor-container {
243
+ margin-top: 0.5rem;
244
+ }
245
+ #description-editor-container .ql-container {
246
+ background-color: rgba(255, 255, 255, 0.05);
247
+ border: 1px solid rgba(255, 255, 255, 0.1);
248
+ border-radius: 0 0 6px 6px;
249
+ color: #ffffff;
250
+ font-family: inherit;
251
+ font-size: 0.9375rem;
252
+ }
253
+ #description-editor-container .ql-toolbar {
254
+ background-color: rgba(255, 255, 255, 0.05);
255
+ border: 1px solid rgba(255, 255, 255, 0.1);
256
+ border-radius: 6px 6px 0 0;
257
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
258
+ }
259
+ #description-editor-container .ql-toolbar .ql-stroke {
260
+ stroke: rgba(255, 255, 255, 0.7);
261
+ }
262
+ #description-editor-container .ql-toolbar .ql-fill {
263
+ fill: rgba(255, 255, 255, 0.7);
264
+ }
265
+ #description-editor-container .ql-toolbar button:hover,
266
+ #description-editor-container .ql-toolbar button.ql-active {
267
+ background-color: rgba(255, 255, 255, 0.1);
268
+ }
269
+ #description-editor-container .ql-toolbar button:hover .ql-stroke,
270
+ #description-editor-container .ql-toolbar button.ql-active .ql-stroke {
271
+ stroke: #ffffff;
272
+ }
273
+ #description-editor-container .ql-toolbar button:hover .ql-fill,
274
+ #description-editor-container .ql-toolbar button.ql-active .ql-fill {
275
+ fill: #ffffff;
276
+ }
277
+ #description-editor-container .ql-editor {
278
+ color: #ffffff;
279
+ min-height: 150px;
280
+ }
281
+ #description-editor-container .ql-editor.ql-blank::before {
282
+ color: rgba(255, 255, 255, 0.4);
283
+ font-style: italic;
284
+ }
285
+ #description-editor-container .ql-snow .ql-picker {
286
+ color: rgba(255, 255, 255, 0.7);
287
+ }
288
+ #description-editor-container .ql-snow .ql-picker-options {
289
+ background-color: rgba(30, 30, 30, 0.95);
290
+ border: 1px solid rgba(255, 255, 255, 0.1);
291
+ }
292
+ #description-editor-container .ql-snow .ql-picker-item {
293
+ color: rgba(255, 255, 255, 0.7);
294
+ }
295
+ #description-editor-container .ql-snow .ql-picker-item:hover {
296
+ background-color: rgba(255, 255, 255, 0.1);
297
+ color: #ffffff;
298
+ }
299
+ #description-editor-container .ql-snow .ql-stroke {
300
+ stroke: rgba(255, 255, 255, 0.7);
301
+ }
302
+ #description-editor-container .ql-snow .ql-fill {
303
+ fill: rgba(255, 255, 255, 0.7);
304
+ }
305
  .modal-buttons {
306
  display: flex;
307
  gap: 0.75rem;
 
357
  margin-left: auto;
358
  margin-right: auto;
359
  }
360
+ .top-page-banner {
361
+ background: rgba(100, 100, 255, 0.15);
362
+ border-bottom: 1px solid rgba(100, 100, 255, 0.3);
363
+ padding: 0.75rem 1.5rem;
364
+ color: #6464ff;
365
+ font-size: 0.9rem;
366
+ font-weight: 500;
367
+ text-align: center;
368
+ width: 100%;
369
+ }
370
  .banner {
371
  background: rgba(255, 193, 7, 0.15);
372
  border: 1px solid rgba(255, 193, 7, 0.3);
 
474
  </style>
475
  </head>
476
  <body>
477
+ <div class="top-page-banner">
478
+ To send astras to AstraPay, please friend astrapay@astranova.org, your friend request will be accepted instantly.
479
+ </div>
480
  <div class="main-content">
481
  <div class="container">
482
  <h1 class="title">AstraPay</h1>
483
  <div class="top-banner">
484
+ Contact Instant on Slack if any issues occur.
485
  </div>
486
  <div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
487
  <button class="button" onclick="openModal()" style="display: flex; align-items: center; gap: 0.5rem;">
 
527
  <div class="form-group">
528
  <label class="form-label" for="description">Description</label>
529
  <textarea id="description" class="form-textarea" placeholder="Enter payment description" required></textarea>
530
+ <div id="description-editor-container"></div>
531
  </div>
532
 
533
  <div class="form-group">
 
568
  <label class="form-label" for="claim-astras">Number of Astras</label>
569
  <input type="number" id="claim-astras" class="form-input" min="1" max="10000" placeholder="Enter number of astras" required oninput="validateClaimAstrasAmount(this)">
570
  <div style="font-size: 0.8rem; color: rgba(255, 255, 255, 0.6); margin-top: 0.5rem;">
571
+ You'll send: <span id="claim-send-amount">-</span> Astras (<span id="claim-fee-percent">1</span>%)<br>
572
+ Claimable: <span id="claim-claim-amount">-</span> Astras (<span id="claim-fee-percent-negative">-1</span>%)<br>
573
+ Fee: <span id="claim-fee-amount">-</span> Astras
574
  </div>
575
  </div>
576
 
 
607
  <div class="modal-header">Enter Link ID</div>
608
  <div class="modal-body">
609
  <div class="form-group">
610
+ <label class="form-label" for="link-id">Enter Link ID</label>
611
+ <input type="text" id="link-id" class="form-input" placeholder="e.g., 550e8400-e29b-41d4-a716-446655440000" maxlength="36" style="font-size: 1rem; text-align: center;" required onkeypress="if(event.key === 'Enter') goToLink()">
612
  </div>
613
 
614
  <div class="modal-buttons">
 
633
  <div id="toastContainer" class="toast-container"></div>
634
 
635
  <script>
636
+ let descriptionEditor = null;
637
+
638
+ function initDescriptionEditor() {
639
+ if (descriptionEditor) {
640
+ const container = document.getElementById('description-editor-container');
641
+ container.innerHTML = '';
642
+ descriptionEditor = null;
643
+ }
644
+
645
+ const textarea = document.getElementById('description');
646
+ textarea.style.display = 'none';
647
+
648
+ const container = document.getElementById('description-editor-container');
649
+ container.innerHTML = '<div id="quill-editor"></div>';
650
+
651
+ descriptionEditor = new Quill('#quill-editor', {
652
+ theme: 'snow',
653
+ placeholder: 'Enter payment description',
654
+ modules: {
655
+ toolbar: [
656
+ [{ 'header': [1, 2, 3, false] }],
657
+ ['bold', 'italic', 'underline', 'strike'],
658
+ [{ 'list': 'ordered'}, { 'list': 'bullet' }],
659
+ ['blockquote', 'code-block'],
660
+ ['link', 'image'],
661
+ ['clean']
662
+ ]
663
+ }
664
+ });
665
+
666
+ const quillContainer = document.querySelector('#description-editor-container .ql-container');
667
+ if (quillContainer) {
668
+ quillContainer.style.backgroundColor = 'rgba(255, 255, 255, 0.05)';
669
+ quillContainer.style.borderColor = 'rgba(255, 255, 255, 0.1)';
670
+ }
671
+ }
672
+
673
+ function getQuillMarkdown() {
674
+ if (!descriptionEditor) return '';
675
+ try {
676
+ const html = descriptionEditor.root.innerHTML;
677
+ if (!html || html === '<p><br></p>' || html.trim() === '') return '';
678
+
679
+ const text = descriptionEditor.getText();
680
+ if (!text || text.trim() === '') return '';
681
+
682
+ if (typeof TurndownService === 'undefined') {
683
+ return text.trim();
684
+ }
685
+
686
+ const turndownService = new TurndownService({
687
+ headingStyle: 'atx',
688
+ codeBlockStyle: 'fenced',
689
+ emDelimiter: '*',
690
+ strongDelimiter: '**',
691
+ bulletListMarker: '-',
692
+ linkStyle: 'inlined'
693
+ });
694
+
695
+ turndownService.addRule('underline', {
696
+ filter: ['u'],
697
+ replacement: function(content) {
698
+ return '__' + content + '__';
699
+ }
700
+ });
701
+
702
+ turndownService.addRule('strikethrough', {
703
+ filter: ['del', 's', 'strike'],
704
+ replacement: function(content) {
705
+ return '~~' + content + '~~';
706
+ }
707
+ });
708
+
709
+ const markdown = turndownService.turndown(html);
710
+ return markdown && typeof markdown === 'string' ? markdown.trim() : text.trim();
711
+ } catch (error) {
712
+ console.error('Error converting to markdown:', error);
713
+ if (descriptionEditor) {
714
+ const text = descriptionEditor.getText();
715
+ return text ? text.trim() : '';
716
+ }
717
+ return '';
718
+ }
719
+ }
720
+
721
  function showToast(message, type = 'error') {
722
  const container = document.getElementById('toastContainer');
723
  const toast = document.createElement('div');
 
763
  modal.classList.add('show');
764
  }, 10);
765
  document.body.style.overflow = 'hidden';
766
+ setTimeout(() => {
767
+ initDescriptionEditor();
768
+ }, 50);
769
  }
770
 
771
  function closeModal() {
 
776
  }, 200);
777
  document.body.style.overflow = '';
778
  document.getElementById('astras').value = '';
 
779
  document.getElementById('recipient-email').value = '';
780
+ if (descriptionEditor) {
781
+ const container = document.getElementById('description-editor-container');
782
+ container.innerHTML = '';
783
+ descriptionEditor = null;
784
+ }
785
+ const textarea = document.getElementById('description');
786
+ textarea.value = '';
787
+ textarea.style.display = 'block';
788
  }
789
 
790
  async function createPaymentLink() {
791
  const astras = document.getElementById('astras').value;
792
+ let description = '';
793
+ if (descriptionEditor) {
794
+ const markdown = getQuillMarkdown();
795
+ description = markdown && typeof markdown === 'string' ? markdown.trim() : '';
796
+ if (!description) {
797
+ const text = descriptionEditor.getText();
798
+ description = text ? text.trim() : '';
799
+ }
800
+ } else {
801
+ description = document.getElementById('description').value.trim();
802
+ }
803
  const recipientEmail = document.getElementById('recipient-email').value.trim();
804
 
805
  if (!astras) {
 
817
 
818
  if (!description) {
819
  showToast('Please enter a description');
820
+ if (descriptionEditor) {
821
+ descriptionEditor.focus();
822
+ } else {
823
+ document.getElementById('description').focus();
824
+ }
825
  return;
826
  }
827
 
 
879
  document.getElementById('claim-astras').value = '';
880
  document.getElementById('claim-send-amount').textContent = '-';
881
  document.getElementById('claim-claim-amount').textContent = '-';
882
+ document.getElementById('claim-fee-percent').textContent = '1';
883
+ document.getElementById('claim-fee-percent-negative').textContent = '-1';
884
+ document.getElementById('claim-fee-amount').textContent = '-';
885
  }
886
 
887
  function validateClaimAstrasAmount(input) {
888
  const value = parseInt(input.value);
889
  if (!isNaN(value) && value > 0) {
890
+ // 2% fee if over 50 astras, otherwise 1% fee
891
+ const feePercent = value > 50 ? 0.02 : 0.01;
892
+ const sendAmount = Math.max(1, Math.floor(value * (1 + feePercent)));
893
+ const claimAmount = Math.max(1, Math.floor(value * (1 - feePercent)));
894
+ const fee = Math.floor(value * feePercent);
895
+
896
  document.getElementById('claim-send-amount').textContent = sendAmount;
897
  document.getElementById('claim-claim-amount').textContent = claimAmount;
898
+ document.getElementById('claim-fee-percent').textContent = (feePercent * 100);
899
+ document.getElementById('claim-fee-percent-negative').textContent = '-' + (feePercent * 100);
900
+ document.getElementById('claim-fee-amount').textContent = fee;
901
  } else {
902
  document.getElementById('claim-send-amount').textContent = '-';
903
  document.getElementById('claim-claim-amount').textContent = '-';
904
+ document.getElementById('claim-fee-percent').textContent = '1';
905
+ document.getElementById('claim-fee-percent-negative').textContent = '-1';
906
+ document.getElementById('claim-fee-amount').textContent = '-';
907
  }
908
  if (!isNaN(value) && value > 10000) {
909
  input.value = 10000;
 
974
  }
975
 
976
  async function goToLink() {
977
+ const linkId = document.getElementById('link-id').value.trim();
978
 
979
+ // Validate UUID4 format (36 characters with hyphens)
980
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
981
+ if (!linkId || !uuidRegex.test(linkId)) {
982
+ showToast('Please enter a valid Link ID');
983
  document.getElementById('link-id').focus();
984
  return;
985
  }