]> _ Git - odl.git/commitdiff
Wait #5027 @5.5
authorStephen Cameron <stephen@cubedesigners.com>
Tue, 18 Jan 2022 12:18:34 +0000 (13:18 +0100)
committerStephen Cameron <stephen@cubedesigners.com>
Tue, 18 Jan 2022 12:18:34 +0000 (13:18 +0100)
resources/css/common/animations.css
resources/views/components/pin-code.blade.php [new file with mode: 0644]
resources/views/front/splash.blade.php

index 9de2f1c817f2677f41ed1124fa6e63d87661e033..7aec73136d8f12a384205f3fee05eb89fdb4115d 100644 (file)
 }
 
 /*============================================================*/
+
+.animate-shake-horizontal {
+    animation-name: animate-shake-horizontal;
+    animation-timing-function: cubic-bezier(0.455, 0.030, 0.515, 0.955);
+}
+
+@keyframes animate-shake-horizontal {
+    0%,
+    100% {
+        transform: translateX(0);
+    }
+    10%,
+    30%,
+    50%,
+    70% {
+        transform: translateX(-10px);
+    }
+    20%,
+    40%,
+    60% {
+        transform: translateX(10px);
+    }
+    80% {
+        transform: translateX(8px);
+    }
+    90% {
+        transform: translateX(-8px);
+    }
+}
+
+/*============================================================*/
diff --git a/resources/views/components/pin-code.blade.php b/resources/views/components/pin-code.blade.php
new file mode 100644 (file)
index 0000000..0ba9b1e
--- /dev/null
@@ -0,0 +1,167 @@
+{{-- PIN Code Component --}}
+@php
+    $pin = $pin ?? '1234';
+@endphp
+
+<div x-data="lockscreen()"
+     @keydown.window="keyboard(event)"
+     class="absolute top-0 left-0
+            w-full h-screen
+            flex items-center justify-center
+            text-3xl select-none"
+     x-show="!unlocked"
+     x-transition.opacity
+     x-cloak>
+
+    <div class="relative flex flex-col items-center">
+        <h3 class="font-semibold">Saisir le code d&rsquo;accès</h3>
+
+        <div class="absolute -top-10 font-semibold text-red text-lg" x-show="failed" x-transition.opacity>
+            Ce code n&rsquo;est pas valide
+        </div>
+
+        {{-- PIN DIGITS --}}
+        <div class="mt-6 space-x-3" :class="{ 'animate animate-shake-horizontal': failed }" style="--animation-duration: 0.7s">
+            <template x-for="i in pin.length">
+                <div :class="{
+                        'border-[#d5d5d5]': !success && !failed && !input[i - 1] && (i !== input.length + 1),
+                        'bg-blue border-blue': !success && !failed && input[i - 1],
+                        'bg-red border-red': failed,
+                        'bg-green border-green': success,
+                        'pin-input-focused border-white': (i === input.length + 1)
+                     }"
+                     class="relative w-16 h-16
+                            inline-flex items-center justify-center
+                            text-white
+                            border border-2 rounded-lg
+                            transition">
+                    <span class="inline-block text-3xl mt-2">*</span>
+                </div>
+            </template>
+        </div>
+
+        {{-- KEYPAD BUTTONS --}}
+        <div class="grid gap-3 mt-8" style="grid-template-columns: repeat(3, min-content)">
+            <?php
+                $digits = [
+                    '1', '2', '3',
+                    '4', '5', '6',
+                    '7', '8', '9',
+                    '-', '0', 'x',
+                ];
+
+                foreach($digits as $digit) {
+
+                    $click = "@click=\"type('$digit')\"";
+
+                    $class = 'inline-flex items-center justify-center
+                              w-22 h-22
+                              bg-[#fafafa] hover:bg-[#d5d5d5]
+                              rounded-full
+                              cursor-pointer';
+
+                    if ($digit === '-') {
+                        $digit = $click = $class = '';
+                    } elseif ($digit === 'x') {
+                        $digit = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23.9 17.9" xml:space="preserve" class="fill-current w-[1em]"><path d="M19.69 17.9H9.93c-1.15 0-2.25-.44-3.08-1.24l-6.2-5.94C-.19 9.9-.21 8.57.59 7.75l6.22-6.39C7.64.5 8.81 0 10 0h9.68c2.32 0 4.21 1.89 4.21 4.21v9.48a4.202 4.202 0 0 1-4.2 4.21zM10.01 1.28c-.85 0-1.69.35-2.28.97L1.51 8.63a.82.82 0 0 0 .01 1.16l6.2 5.94c.59.57 1.38.88 2.2.89h9.76c1.62 0 2.93-1.32 2.93-2.94V4.21c0-1.62-1.32-2.93-2.93-2.93h-9.67z"/><path d="m15.35 8.96 2.67-2.67c.25-.25.25-.65 0-.9a.634.634 0 0 0-.9 0l-2.67 2.67-2.67-2.67c-.25-.25-.65-.25-.9 0s-.25.65 0 .9l2.67 2.67-2.67 2.67c-.25.25-.25.65 0 .9.12.12.29.19.45.19s.33-.06.45-.19l2.67-2.67 2.67 2.67c.12.12.29.19.45.19s.33-.06.45-.19c.25-.25.25-.65 0-.9l-2.67-2.67z"/></svg>';
+                    }
+                ?>
+                    <span class="{{ $class }}" {!! $click !!}>
+                        {!! $digit !!}
+                    </span>
+                <?php } ?>
+        </div>
+
+    </div>
+
+</div>
+
+@push('after_css')
+    <style>
+        .pin-input-focused {
+            position: relative;
+            box-shadow: 0 5px 25px rgb(0 0 0 / 15%);
+        }
+        .pin-input-focused:after {
+            content: '';
+            position: absolute;
+            top: 50%;
+            left: 50%;
+            transform: translate(-50%, -50%);
+            width: 3px;
+            height: 1em;
+            background-color: black;
+            animation: 1.25s cursor-blink step-end infinite;
+        }
+
+        @keyframes cursor-blink {
+            from, to {
+                background-color: transparent;
+            }
+            50% {
+                background-color: black;
+            }
+        }
+    </style>
+@endpush
+
+@push('before_scripts')
+    <script>
+        function lockscreen() {
+            return {
+                {{-- Some very light protection so PIN isn't directly visible in source --}}
+                secret: '{{ base64_encode($pin) }}',
+                failed: false,
+                success: false,
+                input: [],
+
+                get pin() {
+                    return atob(this.secret); {{-- JS equivalent of base64_decode --}}
+                },
+
+                type(key) {
+                    if (key === 'x') { // Delete key
+                        this.input.pop();
+                        return true;
+                    }
+
+                    // Don't accept more digits if all are entered already
+                    if (this.input.length >= this.pin.length) {
+                        return false;
+                    }
+
+                    this.input.push(key);
+                    this.verify();
+                },
+
+                keyboard(event) {
+                    // Handle physical keyboard entry but only allow numbers + backspace key
+                    let key = event.key.replace(/\D/g, "");
+                    if (key !== '') {
+                        this.type(key);
+                    } else if (event.key === 'Backspace') {
+                        this.type('x'); // Special case
+                    }
+                },
+
+                verify() {
+                    if (this.input.length < this.pin.length) return false;
+
+                    this.success = this.input.join('') === this.pin;
+
+                    if (this.success) {
+                        setTimeout(() => this.unlocked = true, 500);
+                    } else {
+                        this.failed = true;
+
+                        // Reset input and errors after a delay
+                        setTimeout(() => {
+                            this.input = [];
+                            this.failed = false;
+                        }, 2500);
+                    }
+                },
+            }
+        }
+    </script>
+@endpush
index 01e49ca7407b20f905b5176d7d992cf8780fbb09..35b897bbc73397fb1a41381f4803674f3644d7c7 100644 (file)
@@ -2,13 +2,13 @@
 
 @section('body_tag')
     {{-- Make sure no scrollbars are present because they affect the background scaling --}}
-    <body class="overflow-hidden">
+    <body class="overflow-hidden font-primary">
 @endsection
 
 @section('main')
 
     {{-- Title + Illustration --}}
-    <div x-data="{ shown: false }" x-intersect="shown = true"
+    <div x-data="splash()"
          class="h-screen flex items-center z-10"
          x-cloak>
 
@@ -17,7 +17,7 @@
             <video playsinline
                    muted
                    preload="auto"
-                   data-delay="1000" {{-- How long in milliseconds to wait before starting the video --}}
+                   data-delay="0" {{-- How long in milliseconds to wait before starting the video --}}
                    id="background_video"
                    class="w-full absolute left-0 bottom-0">
                 <source src="{{ asset('images/splash.mp4') }}" type="video/mp4">
 
         <x-link href="home"
                 id="home_link"
+                data-delay="4000" {{-- How long in ms to wait before triggering click once unlocked --}}
                 class="w-full max-h-[90vh] flex flex-col items-center justify-around">
-            <img x-show="shown"
+            <img x-show="unlocked"
                  x-transition.opacity.scale.75.origin.bottom.duration.1000ms
                  class="max-w-[488px] mb-16"
                  src="{{ asset('images/splash-text.svg') }}"
                  alt="Source d'avenir">
-            <img x-show="shown"
+            <img x-show="unlocked"
                  x-transition.opacity.scale.95.origin.center.duration.1200ms.delay.800ms
                  class="max-w-[520px]"
                  style="backface-visibility: hidden"
                  src="{{ asset('images/splash-illustration.png') }}">
         </x-link>
 
+        <x-pin-code :pin="$settings->get('pin')" />
+
     </div>
 
 @endsection
 
-@push('after_scripts')
+@push('before_scripts')
     <script>
-        // Delay start of video
-        const startVideo = async (video) => {
-            try {
-                await video.play();
-                video.setAttribute('autoplay', true);
-            } catch (err) {
-                console.warn(err, 'Error playing video');
-            }
-        }
+        function splash() {
+            return {
+                unlocked: false,
+
+                init() {
+                    this.startVideo();
+                    this.handleUnlock();
+                },
 
-        //========================================================
+                startVideo() {
+                    // Delay start of video
+                    const startVideo = async (video) => {
+                        try {
+                            await video.play();
+                            video.setAttribute('autoplay', true);
+                        } catch (err) {
+                            console.warn(err, 'Error playing video');
+                        }
+                    }
 
-        const video = document.querySelector('#background_video');
+                    //========================================================
 
-        // Trigger click on main link when video ends
-        video.addEventListener('ended', () => document.querySelector('#home_link').click());
+                    const video = document.querySelector('#background_video');
 
-        // Start video after a certain delay
-        setTimeout(() => startVideo(video), video.dataset?.delay);
+                    // Start video after a certain delay
+                    setTimeout(() => startVideo(video), video.dataset?.delay);
+                },
+
+                handleUnlock() {
+                    this.homeLink = document.querySelector('#home_link');
+                    this.redirectDelay = this.homeLink.dataset.delay || 2000;
+
+                    // Once the interface is unlocked, trigger click on main link after a specified delay
+                    this.$watch('unlocked', (isUnlocked) => {
+                        if (isUnlocked) {
+                            let $this = this;
+                            setTimeout(() => $this.homeLink.click(), parseInt(this.redirectDelay));
+                        }
+                    });
+                },
+            }
+        }
     </script>
 @endpush