Continue timer in MVC after reload page - javascript

I'm trying to implement a timer that upon page reload it doesn't resets. I've thought about the ways to do it (model, viewdata, session, etc.) and I think the cookie method is the one that will work best. However I'm running into logical issues with the implementation and don't know how to go forward.
Here are the files:
CSHTML
<div class="mb-3">
<p class="mb-4 bold-txt">#Model.Email</p>
<span class="block" id="countdown-timer"> #localization.GetLocalization("Zenegy_Auth_Email_Verification_Enter_Code"): <span id="ten-countdown"></span></span>
<span class="block" id="code-expired-text" style="display:none"> #localization.GetLocalization("Zenegy_Auth_Email_Verification_Code_Expired")</span>
</div>
.
.
.
<script>
otpInput.initInputs(#(ViewBag.HasError != null && ViewBag.HasError ? "true" : "false"))
console.log(#http.HttpContext.Request.Cookies["time-left"])
countdown("ten-countdown", 5, 0, true, #http.HttpContext.Request.Cookies["time-left"]);
</script>
COUNTDOWN.JS
let countdown = (elementName, minutes, seconds, isResendCodeForm, timeLeft) => {
let element, endTime, hours, mins, msLeft, time, timeLeft;
let twoDigits = (n) => {
return n <= 9 ? "0" + n : n;
};
let updateTimer = () => {
msLeft = endTime - +new Date();
document.cookie = "time-left=" + msLeft;
msLeft = timeLeft;
if (msLeft < 1000) {
if (isResendCodeForm) {
toggleTimerExpired();
}
else {
document.getElementById('ten-countdown').style.display = "none";
document.getElementById('timer-expired-text').style.display = "inline";
}
} else {
time = new Date(msLeft);
hours = time.getUTCHours();
mins = time.getUTCMinutes();
element.innerHTML =
(hours ? hours + ":" + twoDigits(mins) : mins) +
":" +
twoDigits(time.getUTCSeconds()) +
" min";
setTimeout(updateTimer, time.getUTCMilliseconds() + 500);
}
};
element = document.getElementById(elementName);
endTime = +new Date() + 1000 * (60 * minutes + seconds) + 500;
updateTimer();
};
let syncTimer = (timeLeft) => {
msLeft = timeLeft;
}
function toggleTimerExpired() {
document.getElementById('resend-form').style.display = "block";
document.getElementById('code-expired-text').style.display = "block";
document.getElementById('countdown-timer').style.display = "none";
document.getElementById('resend-code-text').style.display = "none";
document.getElementById('verification-code-form').style.display = "none";
}
CONTROLLER
[HttpPost("validateemailcode"), ValidateAntiForgeryToken, UserVerifyAuthorize]
public async Task<ActionResult> ValidateEmailVerificationCode(VerifyUserEmailZenegyViewModel model)
{
var timeLeft = HttpContext.Request.Cookies["time-left"];
if (!ModelState.IsValid)
{
var verifyUserMailViewModel = new VerifyUserEmailZenegyViewModel
{
Email = model.Email
};
CreateErrorNotification("Invalid_Request");
return View("~/Views/AuthZenegy/VerifyUserEmail.cshtml", verifyUserMailViewModel);
}
try
{
await _accountService.ValidateEmailVerificationCodeAsync(model.Email, model.ConcatenatedVerificationCode);
await SignOutAsync();
await SignInAsync(await _authProvider.RefreshLoginAsync(), false);
ViewBag.Header = EmailCodeVerificationSuccessHeaderText;
ViewBag.SubHeader = EmailCodeVerificationSuccessSubHeaderText;
ViewBag.Action = EmailCodeVerificationSuccessAction;
return View("~/Views/AuthZenegy/VerificationSuccess.cshtml");
}
catch (NotAcceptableException)
{
ViewBag.HasError = true;
CreateErrorNotification("VerifyUser_MustSpecify_MailOrPhone_AndCode");
return VerifyEmailView(model);
}
catch (NotFoundException)
{
ViewBag.HasError = true;
CreateErrorNotification("VerifyUser_UserNotFound");
return VerifyEmailView(model);
}
catch (AlreadyExistsException)
{
ViewBag.HasError = true;
CreateErrorNotification("VerifyUser_AlreadyExists_EmailOrMail");
return VerifyEmailView(model);
}
catch (ValidationException)
{
HttpContext.Response.Cookies.Append("time-left", timeLeft);
ViewBag.HasError = true;
CreateErrorNotification("VerifyUser_Invalid_VerificationCode");
return VerifyEmailView(model);
}
catch (Exception)
{
ViewBag.HasError = true;
return VerifyEmailView(model);
}
}
Anyone have any idea what should I do?

Related

How can I make a counter counting down from 25 by keeping only the seconds in the database with php?

I made a counter counting down from 25 with javscript, but when different users request the page, different time values ​​are displayed in the counter. Instead, I want to keep those 25 seconds in the database and count down with php or javascript so that every user sees the same value. How can I set up logic for the database?After 25 seconds it has to start again
var interval = 25000;
function reset() {
localStorage.endTime = +new Date() + interval;
}
if (!localStorage.endTime) {
reset();
}
function millisToMinutesAndSeconds(millis) {
var seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
setInterval(function () {
var remaining = localStorage.endTime - new Date();
if (remaining >= 0) {
document.getElementById("timer").innerText =
millisToMinutesAndSeconds(remaining);
} else {
tensecond();
}
}, 100);
var interval10 = 10000;
function reset() {
localStorage.endTime = +new Date() + interval10;
}
if (!localStorage.endTime) {
reset();
}
function millisToMinutesAndSeconds(millis) {
var seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}
setInterval(function () {
var remaining10 = localStorage.endTime - new Date();
if (remaining10 > 0) {
document.getElementById("timer").innerText =
millisToMinutesAndSeconds(remaining10);
} else {
reset();
}
}, 100);
I would probably instead store the end time of the timer (in UTC) in the database. Then, your clients can do something like:
const startTimer = async () => {
const response = await fetch('https://your-server/timer');
const endTime = await response.json(); // ex: { timestamp: 1663088872145 }
const end = newDate(endTime.timestamp);
const i = setInterval(() => {
const seconds = ((new Date()).valueOf() - end.valueOf()) / 1000
if (i >= 0) {
document.getElementById('timer').html = seconds;
} else {
clearInterval(i);
}
});
}
For example (with a mock backend):
const fetch = async () => {
return new Promise((resolve) => {
resolve({
json: async function() {
return { timestamp: (new Date()).valueOf() + 15000 };
}
});
});
}
const startTimer = async () => {
const response = await fetch('https://your-server/timer');
const endTime = await response.json(); // ex: { timestamp: 1663088872145 }
const end = new Date(endTime.timestamp);
let seconds = Math.round(-((new Date()).valueOf() - end.valueOf()) / 1000)
document.getElementById('timer').innerHTML = seconds;
const i = setInterval(() => {
seconds = Math.round(-((new Date()).valueOf() - end.valueOf()) / 1000)
if (seconds >= 0) {
document.getElementById('timer').innerHTML = seconds;
} else {
clearInterval(i);
}
}, 1000);
}
startTimer();
<div id="timer"/>
Here is a solution without using php and a database which should ensure that multiple users loading the page at different times all see the same value for the count down.
setInterval(() => {
const timerMs = 25000 - (Date.now() % 25000);
document.querySelector('body').innerHTML = millisToSeconds(timerMs);
}, 100);
function millisToSeconds(millis) {
const seconds = ((millis % 60000) / 1000).toFixed(0);
return (seconds < 10 ? "0" : "") + seconds;
}

The element will not take a textContent input

When I click the date value I'm having this error: Uncaught TypeError: Cannot read property 'textContent' of undefined
This is my code can you help me in determining the source of the error and how can I correct this?
function Timer(elem) {
var time = 3000;
var interval;
var offset;
function update() {`enter code here`
time += delta();
var formattedTime = timeFormatter(time);
elem.textContent = formattedTime;
}
function delta() {
var now = Date.now();
var timePassed = now - offset;
offset = now;
return timePassed;
}
function timeFormatter(timeInMilliseconds) {
var time = new Date(timeInMilliseconds)
var minutes = time.getMinutes().toString();
var seconds = time.getSeconds().toString();
if (minutes.length < 2) {
minutes = '0' + minutes;
}
if (seconds.length < 2) {
seconds = '0' + seconds;
}
return minutes + ' : ' + seconds;
}
this.isOn = false;
this.start = function() {};
if (!this.isOn) {
interval = setInterval(update, 10);
offset = Date.now();
this.isOn = true;
}
};
this.stop = function() {
if (this.isOn) {
clearInterval(interval);
interval = nul;
this.isOn = false;
}
};
this.reset = function() {};
Edited version for the other errors.
function Timer(elem) {
var time= 0;
var offset;
var interval;
function update() {
if (this.isOn) {
time += delta();
var formattedTime = timeFormatter(time);
}
elem.textContent = formattedTime;
}
function delta() {
var now = Date.now();
var timePassed = now - offset;
offset = "5:00";
return timePassed;
}
function timeFormatter(time) {
time = new Date(time);
var minutes = time.getMinutes().toString();
var seconds = time.getSeconds().toString();
if (minutes.length < 2) {
minutes = '0' + minutes;
}
if (seconds.length < 2) {
seconds = '0' + seconds;
}
return minutes + ' : ' + seconds;
}
this.start = function() {
interval = setInterval(update.bind(this), 10);
time++;
this.isOn = true;
};
this.stop = function() {
clearInterval(interval)
interval = null;
this.isOn = false;
};
this.reset = function() {
time= 300;
update();
};
this.isOn = false;
}

Alert returning as undefined

I am trying to have my alert show up with the time that my timer shows.
function Stopwatch(elem) {
var time = 0;
var offset;
var interval;
function update() {
if (this.isOn) {
time += delta();
}
elem.textContent = timeFormatter(time);
}
function delta() {
var now = Date.now();
var timePassed = now - offset;
offset = now;
return timePassed;
}
function timeFormatter(time) {
time = new Date(time);
var minutes = time.getMinutes().toString();
var seconds = time.getSeconds().toString();
var milliseconds = time.getMilliseconds().toString();
if (minutes.length < 2) {
minutes = '0' + minutes;
}
if (seconds.length < 2) {
seconds = '0' + seconds;
}
while (milliseconds.length < 3) {
milliseconds = '0' + milliseconds;
}
return minutes + ' : ' + seconds + ' . ' + milliseconds;
}
this.start = function() {
interval = setInterval(update.bind(this), 10);
offset = Date.now();
this.isOn = true;
};
this.stop = function() {
clearInterval(interval);
interval = null;
this.isOn = false;
};
this.reset = function() {
time = 0;
update();
};
this.isOn = false;
}
var timer = document.getElementById('timer');
var toggleBtn = document.getElementById('toggle');
var resetBtn = document.getElementById('reset');
var watch = new Stopwatch(timer);
function start() {
toggleBtn.textContent = 'Stop';
watch.start();
}
function stop() {
toggleBtn.textContent = 'Start';
watch.stop();
}
toggleBtn.addEventListener('click', function() {
watch.isOn ? stop() : start();
});
resetBtn.addEventListener('click', function() {
watch.reset();
});
function alertSystem(){
var timer = document.getElementById('timer')
alert(timer);
}
<h1 id="timer">00 : 00 . 000</h1>
<div>
<button class=button id="toggle">Start</button>
<button class=button id="reset">Reset</button>
<button onclick='alertSystem()'>get number</button>
</div>
It's a lot of code, but it is mostly to get the timer working. The last function called alertSystem() is on the bottom and is the one that triggers the alert call. For me the alert shows up as [object HTMLHeadingElement] or as undefined. The former comes up when I have alert(timer); but if I do alert(timer.value); or alert(timer.length); I get the latter.
Does anyone know how I can just get the value of the timer in the alert?
To get the timer's value, you should do something like:
document.querySelector('#timer').innerHTML
Otherwise , document.getElementById returns a full element as a js object.

Angular 2+ timer function is not working in one of the components

This is very very very strange. I got an timer function which works on all my components except 1. But I just don't know why, I also don't get any errors or something.
What am I missing? The code that I use looks like this:
The HTML
<p>Time in miliseconds: <b id="tick">{{time}}</b></p>
and in my protected.component.ts
timeBegin = new Date();
starts = null;
time = '00:00:00.000';
GetUser(): void {
this.startTime();
this.dataService.getUser().subscribe(res => {
if (res !== undefined) {
this.dataIsReady = true;
this.imgSrc = 'data:image/png;base64,' + res['image'];
}
});
this.stopTime();
}
public clockRun() {
const currentTime = new Date();
const timeElapsed = new Date(currentTime.getTime() - this.timeBegin.getTime());
const hour = timeElapsed.getUTCHours();
const min = timeElapsed.getUTCMinutes();
const sec = timeElapsed.getUTCSeconds();
const ms = timeElapsed.getUTCMilliseconds();
this.time =
(hour > 9 ? hour : '0' + hour) + ':' +
(min > 9 ? min : '0' + min) + ':' +
(sec > 9 ? sec : '0' + sec) + '.' +
(ms > 99 ? ms : ms > 9 ? '0' + ms : '0' + ms);
}
startTime() {
this.timeBegin = new Date();
this.starts = setInterval(this.clockRun.bind(this), 10);
}
stopTime() {
clearInterval(this.starts);
}
I guess you could simplify it a bit. For example:
In HTML better to use DatePipe, like:
{{ interval | date:'HH:mm:ss SSS':'+0000'}}
In Component:
timeBegin: Date;
interval: Date;
ngOnInit() {
this.timeBegin = new Date();
setInterval(this.tick.bind(this), 100);
}
tick() {
let currentTime = new Date();
this.interval = new Date(currentTime.valueOf() - this.timeBegin.valueOf());
}
Also in your example, you immediate stop the time and as result don't see progress. Try to execute stopTime within subscribe block, for example:
GetUser(): void {
this.startTime();
this.dataService.getUser().subscribe(res => {
if (res !== undefined) {
this.dataIsReady = true;
this.imgSrc = 'data:image/png;base64,' + res['image'];
}
this.stopTime();
});
}

Timer not binding DOM value in AngularJS

I'm a backend developer, who's trying hard to make a timer by comparing two different date formats. This part of the script is working great, but whenever I try to make recursive call, nothing is binding.
I almost tried everything, from passing it into a function, using the $interval, the setInterval, and on and on. The main reason is I cannot get the value of its loop, and binding into my DOM.
Here is some of my code. Here I set all variables for the countDown() function.
$scope.timer.list = {};
$scope.timer.date = new Date();
$scope.timer.list.D = '00';
$scope.timer.list.M = '00';
$scope.timer.list.Y = '00';
$scope.timer.list.h = '00';
$scope.timer.list.m = '00';
$scope.timer.list.s = '00';
$scope.begin = {};
$scope.begin.date = {};
$scope.begin.timer = {};
$scope.counter = {
show : false,
text : '00:00'
};
setInterval(function() {
$scope.obj = {
show : $scope.countDown($scope.privateshowcase.begin_at).show,
text : $scope.countDown($scope.privateshowcase.begin_at).text
}
$scope.counter = $scope.obj;
}, 1000);
Then, here is the function:
$scope.countDown = function(begin) {
$scope.timer.date = new Date();
$scope.timer.list.D = $filter('date')($scope.timer.date, 'dd');
$scope.timer.list.M = $filter('date')($scope.timer.date, 'MM');
$scope.timer.list.Y = $filter('date')($scope.timer.date, 'yyyy');
$scope.timer.list.h = $filter('date')($scope.timer.date, 'HH');
$scope.timer.list.m = $filter('date')($scope.timer.date, 'mm');
$scope.timer.list.s = $filter('date')($scope.timer.date, 'ss');
$scope.begin.full = begin.split(" ");
$scope.begin.date = $scope.begin.full[0].split("-");
$scope.begin.timer = $scope.begin.full[1].split(":");
$scope.begin.D = $scope.begin.date[2];
$scope.begin.M = $scope.begin.date[1];
$scope.begin.Y = $scope.begin.date[0];
$scope.begin.h = $scope.begin.timer[0];
$scope.begin.m = $scope.begin.timer[1];
$scope.begin.s = $scope.begin.timer[2];
if($scope.timer.list.Y == $scope.begin.Y) {
if($scope.timer.list.M == $scope.begin.M) {
if($scope.timer.list.D == $scope.begin.D) {
$scope.counter.diff_h = $scope.timer.list.h - $scope.begin.h;
if($scope.counter.diff_h == 0 || $scope.counter.diff_h == -1) {
if($scope.counter.diff_h == 0) {
if($scope.timer.list.m > $scope.begin.m) {
$scope.counter.show = false;
$scope.counter.text = false;
} else if ($scope.timer.list.m <= $scope.begin.m) {
$scope.counter.show = true;
$scope.counter.diff_m = $scope.begin.m - $scope.timer.list.m;
if($scope.counter.diff_m <= 30) {
$scope.counter.diff_s = 60 - $scope.timer.list.s;
if($scope.counter.diff_s == 60) {
$scope.counter.s = "00";
$scope.counter.diff_m_f = $scope.counter.diff_m + 1;
} else if($scope.counter.diff_s >= 1 && $scope.counter.diff_s <= 9) {
$scope.counter.s = "0" + $scope.counter.diff_s;
$scope.counter.diff_m_f = $scope.counter.diff_m;
} else {
$scope.counter.s = $scope.counter.diff_s;
$scope.counter.diff_m_f = $scope.counter.diff_m;
}
if($scope.counter.diff_m_f >= 1 && $scope.counter.diff_m_f <= 9) {
$scope.counter.m = "0" + $scope.counter.diff_m_f;
} else {
$scope.counter.m = $scope.counter.diff_m_f;
}
}
$scope.counter.text = $scope.counter.m + ":" +$scope.counter.s;
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else if ($scope.counter.diff_h == -1) {
$scope.counter.diff_timer = $scope.timer.m - 60;
$scope.counter.diff_m = $scope.begin.m - $scope.counter.diff_timer;
if($scope.counter.diff_m > 30) {
$scope.counter.show = false;
$scope.counter.text = false;
} else if($scope.counter.diff_m <= 30) {
$scope.counter.show = true;
$scope.counter.diff_timer_s = $scope.timer.s - 60;
if($scope.counter.diff_timer_s == 60) {
$scope.counter.s = "00";
$scope.counter.m = $scope.counter.diff_m + 1;
} else if($scope.counter.s >= 1 && $scope.counter.s <= 9) {
$scope.counter.s = "0" + $scope.counter.diff_timer_s;
$scope.counter.m = $scope.counter.diff_m;
} else {
$scope.counter.s = $scope.counter.diff_timer_s;
$scope.counter.m = $scope.counter.diff_m;
}
$scope.counter.text = $scope.counter.m + ":" +$scope.counter.s;
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
} else {
$scope.counter.show = false;
$scope.counter.text = false;
}
return $scope.counter = {
show : $scope.counter.show,
text : $scope.counter.text
};
}
'begin' is : 'YYYY/MM/DAY HH:MM:SS'
Maybe my way of thinking is not the good one, but at list I have a very functional timer, which replace every 1 to 9 into 01 to 09, convert the 60 into 00, can compare 2 different hours.
I think you are over complicating things a little bit. I came up with a simple countDown component made in angularjs 1.6.0 (it can be done with directives for angularjs older versions as well) that compares an input Date with the now Date.
You can play around with the input and change dates to see changes happen on the component, as long as you don't break the date format.
Note on dates: simple way to compare dates:
var date0 = new Date("2017-09-12T14:45:00.640Z");
var date1 = new Date("2017-09-13T14:45:00.640Z");
var dateDiff = new Date(date1.getTime() - date0.getTime());
// "1970-01-02T00:00:00.000Z"
Although dateDiff looks weird, it's basically one day from the zero date 1970-01-01T00:00:00.000Z.
Given that, you just let angularjs do the magic (or maybe trick).
{{ dateDiff | date:"d \'days\' hh:mm:ss" }}
Besides, if you don't want to work with dates in the natural form of javascript, you can use angularjs-moment which provide you date and time utility from momentjs regardless of javascript dates pitfalls.
Here is the working code:
angular
.module('app', [])
.run(function($rootScope) {
$rootScope.countDownToDate = new Date().addDays(2);
})
.component('countDown', {
template: '{{ $ctrl.timer | date:"d \'days\' hh:mm:ss" }}',
bindings: {
to: '<'
},
controller: function CountDownCtrl($interval) {
var $this = this;
this.$onInit = function() {
$interval($this.setTime, 1000);
};
$this.setTime = function() {
$this.timer = new Date(new Date($this.to).getTime() - new Date().getTime());
}
}
});
// bootstrap the app
angular.element(function() {
angular.bootstrap(document, ['app']);
});
// extension to add days on date
Date.prototype.addDays = function(days) {
var dat = new Date(this.valueOf());
dat.setDate(dat.getDate() + days);
return dat;
};
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
<div>
<center>
<h1>
<count-down to="countDownToDate" />
</h1>
<label for="countDownToDate">To Date</label>
<input type="datetime" name="countDownToDate" ng-model="countDownToDate">
</center>
</div>

Categories