KernelDeimos 1 рік тому
батько
коміт
abc12bd407

+ 2 - 1
src/UI/UIWindowEmailConfirmationRequired.js

@@ -26,7 +26,8 @@ function UIWindowEmailConfirmationRequired(options){
         let final_code = '';
         let is_checking_code = false;
 
-        const submit_btn_txt = 'Confirm Email'
+        const submit_btn_txt = 'Confirm Email';
+
         let h = '';
         h += `<div class="qr-code-window-close-btn generic-close-window-button"> &times; </div>`;
         h += `<div style="-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #3e5362;">`;

+ 8 - 2
src/UI/UIWindowLogin.js

@@ -20,6 +20,7 @@
 import UIWindow from './UIWindow.js'
 import UIWindowSignup from './UIWindowSignup.js'
 import UIWindowRecoverPassword from './UIWindowRecoverPassword.js'
+import UIWindowVerificationCode from './UIWindowVerificationCode.js';
 
 async function UIWindowLogin(options){
     options = options ?? {};
@@ -163,7 +164,12 @@ async function UIWindowLogin(options){
                 headers: headers,
                 contentType: "application/json",
                 data: data,				
-                success: function (data){
+                success: async function (data){
+                    if ( data.next_step === 'otp' ) {
+                        const value = await UIWindowVerificationCode();
+                        console.log('got value', value);
+                    }
+
                     window.update_auth_data(data.token, data.user);
                     
                     if(options.reload_on_success){
@@ -234,4 +240,4 @@ async function UIWindowLogin(options){
     }) 
 }
 
-export default UIWindowLogin
+export default UIWindowLogin

+ 181 - 0
src/UI/UIWindowVerificationCode.js

@@ -0,0 +1,181 @@
+import TeePromise from "../util/TeePromise.js";
+import UIWindow from "./UIWindow.js";
+
+const UIWindowVerificationCode = async function UIWindowVerificationCode ( options )  {
+    options = options ?? {};
+    let final_code = '';
+    let is_checking_code = false;
+
+    const html_title = i18n(options.title_key || 'confirm_code_generic_title');
+    const html_confirm = i18n(options.confirm_key || 'confirm_code_generic_confirm');
+    const html_instruction = i18n(options.instruction_key || 'confirm_code_generic_instruction');
+    const submit_btn_txt = i18n(options.submit_btn_key || 'confirm_code_generic_submit');
+
+    let h = '';
+    h += `<div class="qr-code-window-close-btn generic-close-window-button"> &times; </div>`;
+    h += `<div style="-webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #3e5362;">`;
+        h += `<h3 style="text-align:center; font-weight: 500; font-size: 20px;">${ html_title }</h3>`;
+        h += `<form>`;
+            h += `<p style="text-align:center; padding: 0 20px;">${ html_instruction }</p>`;
+            h += `<div class="error"></div>`;
+            h += `  <fieldset name="number-code" style="border: none; padding:0;" data-number-code-form>
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-0' data-number-code-input='0' required />
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-1' data-number-code-input='1' required />
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-2' data-number-code-input='2' required />
+            <span class="confirm-code-hyphen">-</span>
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-3' data-number-code-input='3' required />
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-4' data-number-code-input='4' required />
+            <input class="digit-input" type="number" min='0' max='9' name='number-code-5' data-number-code-input='5' required />
+            </fieldset>`;
+            h += `<button type="submit" class="button button-block button-primary code-confirm-btn" style="margin-top:10px;" disabled>${submit_btn_txt}</button>`;
+        h += `</form>`;
+        h += `<div style="text-align:center; padding:10px; font-size:14px; margin-top:10px;">`;
+            h += `<span class="send-conf-code">what is this text</span>`;
+        h += `</div>`;
+    h += `</div>`;
+
+    const el_window = await UIWindow({
+        title: null,
+        icon: null,
+        uid: null,
+        is_dir: false,
+        body_content: h,
+        has_head: false,
+        selectable_body: false,
+        draggable_body: true,
+        allow_context_menu: false,
+        is_draggable: options.is_draggable ?? true,
+        is_droppable: false,
+        is_resizable: false,
+        stay_on_top: options.stay_on_top ?? false,
+        allow_native_ctxmenu: true,
+        allow_user_select: true,
+        backdrop: true,
+        width: 390,
+        dominant: true,
+        onAppend: function(el_window){
+            $(el_window).find('.digit-input').first().focus();
+        },
+        window_class: 'window-item-properties',
+        window_css:{
+            height: 'initial',
+        },
+        body_css: {
+            padding: '30px',
+            width: 'initial',
+            height: 'initial',
+            'background-color': 'rgb(247 251 255)',
+            'backdrop-filter': 'blur(3px)',
+        }
+    });
+
+
+    const p = new TeePromise();
+    
+    $(el_window).find('.digit-input').first().focus();
+
+    $(el_window).find('.code-confirm-btn').on('click submit', function(e){
+        e.preventDefault();
+        e.stopPropagation();
+
+        $(el_window).find('.code-confirm-btn').prop('disabled', true);
+        $(el_window).find('.error').hide();
+        
+        // Check if already checking code to prevent multiple requests
+        if(is_checking_code)
+            return;
+        // Confirm button
+        is_checking_code = true;
+
+        // set animation
+        $(el_window).find('.code-confirm-btn').html(`<svg style="width:20px; margin-top: 5px;" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24"><title>circle anim</title><g fill="#fff" class="nc-icon-wrapper"><g class="nc-loop-circle-24-icon-f"><path d="M12 24a12 12 0 1 1 12-12 12.013 12.013 0 0 1-12 12zm0-22a10 10 0 1 0 10 10A10.011 10.011 0 0 0 12 2z" fill="#eee" opacity=".4"></path><path d="M24 12h-2A10.011 10.011 0 0 0 12 2V0a12.013 12.013 0 0 1 12 12z" data-color="color-2"></path></g><style>.nc-loop-circle-24-icon-f{--animation-duration:0.5s;transform-origin:12px 12px;animation:nc-loop-circle-anim var(--animation-duration) infinite linear}@keyframes nc-loop-circle-anim{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}</style></g></svg>`);
+
+        setTimeout(() => {
+            console.log('final code', final_code);
+            p.resolve(final_code);
+        }, 1000);
+    })
+
+    // Elements
+    const numberCodeForm = document.querySelector('[data-number-code-form]');
+    const numberCodeInputs = [...numberCodeForm.querySelectorAll('[data-number-code-input]')];
+
+    // Event listeners
+    numberCodeForm.addEventListener('input', ({ target }) => {
+        if(!target.value.length) { return target.value = null; }
+        const inputLength = target.value.length;
+        let currentIndex = Number(target.dataset.numberCodeInput);
+        if(inputLength === 2){
+            const inputValues = target.value.split('');
+            target.value = inputValues[0];
+        }
+        else if (inputLength > 1) {
+            const inputValues = target.value.split('');
+
+            inputValues.forEach((value, valueIndex) => {
+                const nextValueIndex = currentIndex + valueIndex;
+
+                if (nextValueIndex >= numberCodeInputs.length) { return; }
+
+                numberCodeInputs[nextValueIndex].value = value;
+            });
+            currentIndex += inputValues.length - 2;
+        }
+
+        const nextIndex = currentIndex + 1;
+
+        if (nextIndex < numberCodeInputs.length) {
+            numberCodeInputs[nextIndex].focus();
+        }
+
+        // Concatenate all inputs into one string to create the final code
+        final_code = '';
+        for(let i=0; i< numberCodeInputs.length; i++){
+            final_code += numberCodeInputs[i].value;
+        }
+        // Automatically submit if 6 digits entered
+        if(final_code.length === 6){
+            $(el_window).find('.code-confirm-btn').prop('disabled', false);
+            $(el_window).find('.code-confirm-btn').trigger('click');
+        }
+    });
+
+    numberCodeForm.addEventListener('keydown', (e) => {
+        const { code, target } = e;
+
+        const currentIndex = Number(target.dataset.numberCodeInput);
+        const previousIndex = currentIndex - 1;
+        const nextIndex = currentIndex + 1;
+
+        const hasPreviousIndex = previousIndex >= 0;
+        const hasNextIndex = nextIndex <= numberCodeInputs.length - 1
+
+        switch (code) {
+            case 'ArrowLeft':
+            case 'ArrowUp':
+                if (hasPreviousIndex) {
+                    numberCodeInputs[previousIndex].focus();
+                }
+                e.preventDefault();
+                break;
+
+            case 'ArrowRight':
+            case 'ArrowDown':
+                if (hasNextIndex) {
+                    numberCodeInputs[nextIndex].focus();
+                }
+                e.preventDefault();
+                break;
+            case 'Backspace':
+                if (!e.target.value.length && hasPreviousIndex) {
+                    numberCodeInputs[previousIndex].value = null;
+                    numberCodeInputs[previousIndex].focus();
+                }
+                break;
+            default:
+                break;
+        }
+    });
+}
+
+export default UIWindowVerificationCode;

+ 16 - 0
src/css/style.css

@@ -1946,6 +1946,10 @@ label {
     cursor: pointer;
 }
 
+.send-conf-code {
+    cursor: pointer;
+}
+
 .email-confirm-code-hyphen {
     display: inline-block;
     width: 14%;
@@ -1954,10 +1958,22 @@ label {
     font-weight: 300;
 }
 
+.confirm-code-hyphen {
+    display: inline-block;
+    width: 14%;
+    text-align: center;
+    font-size: 40px;
+    font-weight: 300;
+}
+
 .send-conf-email:hover, .conf-email-log-out:hover {
     text-decoration: underline;
 }
 
+.send-conf-code:hover {
+    text-decoration: underline;
+}
+
 .remove-permission-link, .disassociate-website-link {
     cursor: pointer;
     color: red;

+ 2 - 0
src/i18n/translations/en.js

@@ -47,6 +47,8 @@ const en = {
         color: 'Color',
         hue: 'Hue',
         confirm_account_for_free_referral_storage_c2a: 'Create an account and confirm your email address to receive 1 GB of free storage. Your friend will get 1 GB of free storage too.',
+        confirm_code_generic_title: "Enter Confirmation Code",
+        confirm_code_2fa_title: "Enter 2FA Code",
         confirm_delete_multiple_items: 'Are you sure you want to permanently delete these items?',
         confirm_delete_single_item: 'Do you want to permanently delete this item?',
         confirm_open_apps_log_out: 'You have open apps. Are you sure you want to log out?',

+ 43 - 0
src/util/TeePromise.js

@@ -0,0 +1,43 @@
+export default class TeePromise {
+    static STATUS_PENDING = {};
+    static STATUS_RUNNING = {};
+    static STATUS_DONE = {};
+    constructor () {
+        this.status_ = this.constructor.STATUS_PENDING;
+        this.donePromise = new Promise((resolve, reject) => {
+            this.doneResolve = resolve;
+            this.doneReject = reject;
+        });
+    }
+    get status () {
+        return this.status_;
+    }
+    set status (status) {
+        this.status_ = status;
+        if ( status === this.constructor.STATUS_DONE ) {
+            this.doneResolve();
+        }
+    }
+    resolve (value) {
+        this.status_ = this.constructor.STATUS_DONE;
+        this.doneResolve(value);
+    }
+    awaitDone () {
+        return this.donePromise;
+    }
+    then (fn, rfn) {
+        return this.donePromise.then(fn, rfn);
+    }
+
+    reject (err) {
+        this.status_ = this.constructor.STATUS_DONE;
+        this.doneReject(err);
+    }
+    
+    /**
+     * @deprecated use then() instead
+     */
+    onComplete(fn) {
+        return this.then(fn);
+    }
+}