Restart a countdown in Livewire with Alpine.js - countdown

I try to build a survey, where a countdown should show a modal each 10 seconds, when the user waits too long.
The countdown should be stopped while the modal is shown and it should be restarted when the "back to survey" button is clicked and also when a question is answered.
I tried it that way with the alpine object:
let Countdown = () => {
return {
open: false,
heart:'',
ccdown(time,hue)
{
time -= 1;
hue += 60;
if (time > 0) {
this.heart=setTimeout(this.ccdown, 500, time, hue);
console.log("time: "+ time);
console.log("hue: " + hue);
}
if(time == 0) {
console.log("done");
this.open = true;
}
}
}
}
This is how I call the function in the blade component:
<div x-data="Countdown()" x-init="ccdown(10,30)">
...
</div>
But it stops after the second time.
Could anybody show me the problem?

The problem is here:
setTimeout(this.ccdown, 500, time, hue);
setTimeout only accepts two arguments, the function, and the timeout. If you have to call the function using arguments, you should either use bind or create a new function. So either it should be:
setTimeout(() => {
this.ccdown(time, hue)
}, 500)
Or,
setTimeout(this.ccdown.bind(this, time, hue), 500)

Related

idle-vue plugin for idle state issue

I am using idle-vue plugin to see if any logged-in user is inactive for more than 10 minutes and if yes then logged them out after displaying a pop-up message.
I have followed this https://medium.com/js-dojo/how-to-set-timer-idle-in-vue-1f4b57beb886 site to implement the code. But here I want to let user to decide whether he wants to continue the session or to logout from active session. and I want that option on idle-dialog page. But as soon as the popup displays and I move cursor to click the option, the popup disappears as the idleVue value becomes false because I as a user has made any activity.
Can anyone please suggest how to achieve this functionality with idle-vue plugin or do I need to use any other plugin?
The idleness of this plugin will break when you do any activity as the mouse moves, keys down, mouse down, and touches start. So as the popup will close when you try to move the mouse.
Reversing the logic can help here.
For example, do not open the popup when the idle time reaches but open it when the user makes any activity after an inactive period.
In another word, do not open the popup instantly after idle time expires but open it when the user comes after long inactivity. In that way, the popup will be visible always and the user will come to know that he has been idle for so long, and now he can make a choice to log out or continue the session.
idle-vue library has two hooks, onIdle and onActive. You can use onActive hook to open the popup.
onActive() {
// Open the dialog when the user becomes active after an inactive period.
this.idle_dialog = true;
},
Now, you can allow users to choose options from that dialog whether to log out or to continue.
Using VueUse's useIdle (with Swal alerts - but can be done with other components). The alert shows up a few minutes before logging you out so you have time to react;
<script setup lang="ts">
import Swal from "sweetalert2";
import { useIdle, useTimestamp, watchThrottled } from "#vueuse/core";
...
const EXTEND_MESSAGE_TIME = 60 * 13; //13 minutes
const IDLE_TIMEOUT = 60 * 15; //15 minutes
const now = useTimestamp();
const { idle, lastActive } = useIdle(IDLE_TIMEOUT);
const authStore = useAuthStore();
const router = useRouter();
const idleAlertShowing = ref<boolean>(false);
let idleTimePassed, idleTimeLeft;
const idledFor = computed(() => {
return Math.floor((now.value - lastActive.value) / 1000);
});
const formatTime = (time: number) => {
const minutes = Math.floor(time / 60);
const seconds = time - minutes * 60;
return `${minutes > 0 ? `${minutes} minutes and ` : ""}${seconds} second${
seconds > 1 ? "s" : ""
}`;
};
...
watchThrottled(
idledFor,
(newValue) => {
if (newValue === EXTEND_MESSAGE_TIME) {
Swal.fire({
html: `You have been inactive for <span id='swal-idle-time-passed'>${formatTime(
newValue
)}</span> and you will be timed out in <span id='swal-idle-time-left'>${formatTime(
IDLE_TIMEOUT - newValue
)}</span>.`,
showCancelButton: false,
showConfirmButton: false
});
idleAlertShowing.value = true;
idleTimePassed = document.querySelector("#swal-idle-time-passed");
idleTimeLeft = document.querySelector("#swal-idle-time-left");
} else if (newValue >= IDLE_TIMEOUT) {
authStore.logout();
router.push({ name: "sign-in" });
if (Swal.isVisible()) {
idleAlertShowing.value = false;
Swal.close();
}
} else if (newValue > EXTEND_MESSAGE_TIME && idle.value === true) {
if (idleTimePassed && idleTimeLeft) {
idleTimePassed.innerText = formatTime(newValue);
idleTimeLeft.innerText = formatTime(IDLE_TIMEOUT - newValue);
}
} else if (newValue <= 0) {
if (idleAlertShowing.value === true && Swal.isVisible()) {
Swal.close();
}
}
},
{ throttle: 500 }
);

change setInterval to requestAnimationFrame

How do I change the following code? I did not know how to convert this code. Can anyone help me?
let timer = ref(30);
let interval = setInterval(() => {
if (timer.value === 0) {
clearInterval(interval)
alert('done')
} else {
timer.value--
}
}, 1000)
This should work the same way as your original code. Of course, you don't need to pass an interval since requestAnimationFrame will be timed to the screen refresh rate.
let timer = ref(30);
function mainLoop() {
if (timer.value === 0) {
alert('done')
} else {
timer.value--
}
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
First the function mainLoop is defined. Then, it is called through requestAnimationFrame at the end of the script to 'kickstart' the loop. The subsequent calls to requestAnimationFrame within the function are there to maintain it.

preventDefault not working with wheel event [SOLVED]

EDIT: It worked by adding prevent modifier to the element in the template
I'm working on a slider that changes the slides on wheel event, I'm throttling the event to get it work just one time on each scroll, I have this piece of code
<div
ref="animated-figure"
class="animated-figure-wrapper"
>
<h1 class="animated-figure__caption">YOU</h1>
</div>
mounted() {
this.$refs['animated-figure'].addEventListener('wheel', this.throttle(this.scrollDirection, 800));
},
methods: {
throttle(func, interval) {
let lastCall = 0;
return function() {
const now = Date.now();
if (lastCall + interval < now) {
lastCall = now;
return func.apply(this, arguments);
}
};
},
scrollDirection(e) {
e.preventDefault();
e.wheelDeltaY > 0
? console.log("DOWN")
: console.log("UP")
e.stopImmediatePropagation();
}
}
Prevent default here is not working as intended and it's continue scrolling, any ideas?

How to correctly use setInterval for multiple functions being called repeatedly in React Native

I am building a simple app in React Native that aims to flash different colors on the screen at certain time intervals. My implementation is as follows:
useEffect(() => {
var blinkOnValue;
var blinkOffValue;
function blinkOn() {
const colorAndWord = getRandomColor(colorArray);
setBackground(colorAndWord.color);
}
function blinkOff() {
setBackground('#F3F3F3');
}
if (strobeStart) {
if (on) {
blinkOnValue = setInterval(() => {
blinkOn();
setOn(false);
}, info.length * 1000);
} else {
blinkOffValue = setInterval(() => {
blinkOff();
setOn(true);
}, info.delay * 1000);
}
}
return () => {
on ? clearInterval(blinkOnValue) : clearInterval(blinkOffValue);
};
}, [colorArray, info.delay, info.length, on, strobeStart]);
The blinkOn function sets the background a certain color and the blinkOff function sets the background a default light gray-ish color. These functions should alternate back and forth, blinking on and off at different intervals. For example, if info.length is 2 and info.delay is 0.5, then the color should flash on for 2 seconds and then the screen should be light gray for 0.5 seconds and repeat. However, the duration of both of the blinkOn and blinkOff are happening for the same amount of time, no matter what the two values are. Sometimes it uses the value from info.length, and sometimes it uses the value from info.delay which is also quite strange.
I think it has something to do with components mounting and unmounting correctly but honestly I am quite lost. If anyone has any advice on how to make this code consistently work where it flashes appropriately I would really appreciate it.
Instead of trying to time your events just right, I suggest using a single timer and computing the blink state from the current system time.
var oldState = true;
function blink() {
var ms = new Date().getTime();
var t = ms % (info.delay + info.length);
var state = (t < info.length ? true : false);
if (state == oldState) return;
if (state) {
blinkOn();
}
else
{
blinkOff();
}
oldState = state;
}
Now set a short timer to check the time and update the blink state as needed:
setInterval( () => blink(), 100 );

createjs newbie issue - changing score on event

I'm trying to decrease score of my hero on event. Specifically, when she misses her score decreases.
I've tried putting the event in the tick function and outside the tick function but both continue to decrement the score continuously.
function reducePower()
{
princessPower -= 1;
text.text = "Princess Power: " + princessPower;
gameStage.update();
}
Within the tick() function this code exists:
if(hitOrMiss == 'Miss')
{
reducePower();
//setTimeout(makeMada, 1);
}
The gameStage.update here causes the score to reduce constantly, I just want it to take one off every time the condition is met i.e. once when she misses - as in onChange. I see there is a createjs onchange function but when I try to use it, it fails for me, I'm probably using it wrong. The code is large so not saved it all but I've created a JSfiddle here: http://jsfiddle.net/3AJ48/
Any help appreciated.
Thanks,
I fixed this one myself. The score needed to be added outside the tick function as below for those who looked at the JSFiddle:
function onDeviceReady()
{
$("#image").rotate(
{
bind:
{
touchstart: function()
{
var rot = Math.floor ( Math.random() *24 ) *15;
value += rot;
$(this).rotate({ animateTo:value});
if(value % 2 === 0)
{
setTimeout(reduceMonsterPower, 2500);
setTimeout(hit,1000);
}
else
{
setTimeout(reducePower, 1500);
setTimeout(miss,1000);
}
}
}
});
}
function reducePower()
{
princessPower = princessPower - 20;
}
function reduceMonsterPower()
{
monsterPower = monsterPower - 40;
}