How do we detect repeat visitor IP and create a condition in liquid? - timer

I have a custom page, created with Shopify liquid -> https://shop.betterbody.co/pages/nazreen-joel-video-sales-letter-16-july
I have set the timer to load within 3seconds for the sales element to appear.
The question is, I would like to set an if/else condition to immediately show these sales element for repeat visitors. There is a timer.js that sets the time for the sales element to appear. If its a new visitor, timer will trigger, else server will not load the timer. I can't seem to find any solution online. Do I detect visitor IP? or is there any best solution to do this?
Here is the code inside theme.liquid,
{{ 'timer.js' | asset_url | script_tag }} .
Timer.js code:
$(document).ready(function(){
setTimeout(function() {
$(".refference").css({paddingTop: "350px"});
// $("#early-cta, #guarentee, #payments, #info, #details").show();
$("#early-cta, #guarentee, #payments, #info, #details").fadeIn(3000);
}, 3000);
});
Pls help.

You could look into localStorage to do this.
https://www.w3schools.com/htmL/html5_webstorage.asp
Localstorage is used to store data within the browser without any expiration date.
When a visitor visits the site for the first time, you could use localStorage to detect if the user has been to your site, if the user hasn’t, you run the timer, and set a variable that the user has visited.
Upon revisiting the site, you use localStorage and check against the variable to see if the user has been to your site or not, and trigger the script accordingly.

Expounding on #Jacob's answer and my comment, you can do what you need to do with JavaScript and localStorage.
So something like this to add:
function setVisited() {
// object to be added to localStorage
let obj = {
timestamp: new Date().getTime()
// add more stuff here if you need
// someValue: "foo",
// anotherValue: "bar"
}
// add object to localStorage
localStorage.setItem(
"hasVisited", // key that you will use to retrieve data
JSON.stringify(obj)
);
}
and something like this to retrieve:
function getVisited() {
return JSON.parse(localStorage.getItem("hasVisited"));
}
// returns: {timestamp: 1533398672593} or null
Also, as an additional condition to your event, you can choose to "expire" the user's localStorage value by checking the timestamp against the current timestamp and comparing it against a predefined expiration duration.
For example, if I wish to consider a visitor who has not returned 7 days as a new visitor:
let expiration = 86400 * 1000 * 7; // 7 days in milliseconds
function isNewVisitor() {
// get current timestamp
let timeNow = new Date().getTime();
let expired = true;
// if getVisited doesn't return null..
if (getVisited()) {
let timeDiff = timeNow - getVisited().timestamp;
expired = timeDiff > expiration;
}
// if the visitor is old and not expire, return true
if (!getVisited() || expired) {
return true;
} else {
return false;
}
}
So, to integrate with your current function, we will just check the condition before setting the timeout:
// let timeout be 3000 if the visitor is new
let timeout = isNewVisitor() ? 3000 : 0;
setTimeout(function() {
$(".refference").css({paddingTop: "350px"});
$("#early-cta, #guarentee, #payments, #info, #details").fadeIn(3000);
}, timeout);
// set the visited object for new visitors, update for old visitors
setVisited();
Check out the fiddle here: https://jsfiddle.net/fr9hjvc5/15/

Related

Is it possible to make a variable dedicated to a certain author Discord.js?

I'm making a store bot and I ran into some SetInterval errors. I want to make it so each variable has the user's id so when I run a different command, it will know which interval to stop. (If that makes sense).
Here is my code:
if(message.content.startsWith(`!open`) {
var cashier1[message.author.id] = function () {
BalanceJSON[message.author.id].bal += 10
Fs.writeFileSync(`./DB/balance.json`, JSON.stringify(BalanceJSON));
}
setInterval(cashier1[message.author.id], 5000);
}
All this code is in a bot.on('message', message => { })
I wanna be able to stop an certain player's interval with clearInterval(cashier1[message.author.id])
The function setInterval returns a unique id which can be used to clear the interval again (See the example for more information).
The solution to your problem is to store the unique id of the interval in some object or database and use that to clear the interval again. See the example code below:
// Create an object to store the intervals.
const cashierIntervals = {};
// Inside your message handler.
// Some dummy if statement for demonstration purpose.
if (message.content === 'setInterval') {
// Create the setInterval and store the unique id in the cashier intervals object.
// You should probably add more checks to see if there is already an interval stored for this author id etc.
cashierIntervals[message.author.id] = setInterval(() => {
BalanceJSON[message.author.id].bal += 10;
Fs.writeFileSync(`./DB/balance.json`, JSON.stringify(BalanceJSON));
}, 5000);
} else if (message.content === 'clearInterval') {
// Clear the interval based on the author id.
// Again, probably add more checks to see if the author id has an interval stored etc.
clearInterval(cashierIntervals[message.author.id]);
// Delete the stored interval entry from the global intervals object.
// Not necessary but it keeps the intervals object small.
delete cashierIntervals[message.author.id];
}
Create an object that takes an id as a key. Your value will be the function you want to interval
Your main file:
const cashier1 = {
// Template for your key:values
'999999999': yourRepeatingFunction(),
}
// Lets say message.author.id returns '999999999'
// Doing setInterval(cashier1[message.author.id], 5000) Will call yourRepeatingFunction()

React function generating available time slots doesn't generate the correct time slots

Hi I want to make a function that generates available time slots. It should generate the time slots while keeping in mind that the time slot can't overlap with an already made appointment.Before the time slots are generated a user can specify which kind of appointment to schedule. Each appointment sort has a duration. So it should also check if the time slot added with the duration doesn't overlap.
I'm struggling to make this all working so far I get time slots but it seems to only checks the start of an already made appointment. I'm kind of running in circles here and would love for some advice or part solutions that I can implement to make my idea work
const GenerateAvailableTimeSlots = (start, serviceObject, allAppointments) => {
const moment = extendMoment(Moment);
var x = {
nextSlot: 15,
appointmentsOfThatDay: [],
startTime: '8:00',
endTime: '20:00'
};
// function to filter only the appointment that occur on specified day --> ( start )
let filterAppointments = (allAppointments, start) => {
let results = [];
let filterAppoinments = allAppointments.filter(appoinment => appoinment.date === start.format('MMMM Do YYYY'));
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
console.log("results", results);
return results;
};
x.appointmentsOfThatDay = filterAppointments(allAppointments, start)
console.log("appointmentsOfThatDay", x.appointmentsOfThatDay)
var slotTime = moment(x.startTime, "HH:mm");
var endTime = moment(x.endTime, "HH:mm");
// function to check time slot overlaps with already made appointments
function OverlapsScheduledAppointment(slotTime, appointments) {
//added duration to timeslot so I could check if a suggested timeslot + the duration also doesn't overlap with already made appointment
var slotTimeWithDuration = slotTime.clone().add(serviceObject.hours, 'hours').add(serviceObject.minutes, 'minutes');
// I don't know where I also could check for slotTimeWithDuration overlap
return appointments.some((br) => {
console.log(slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
return (slotTime >= moment(br[0], "HH:mm") && slotTime < moment(br[1], "HH:mm"));
});
}
let times = [];
while (slotTime < endTime) {
if (!OverlapsScheduledAppointment(slotTime, x.appointmentsOfThatDay)) {
times.push(slotTime.format("HH:mm"));
}
slotTime = slotTime.add(x.nextSlot, 'minutes');
}
return times;
};
I've found the answer to my question.
I was going in the right direction with the above code but in order for generating available time slots that keep in mind the duration of the service you want to schedule and the appointment that are already scheduled.
I had to change this line of code:
// this line just pushes the filtered appointment for a specific day
filterAppoinments.map(appoinment => results.push([appoinment.start.format('HH:mm'), appoinment.end.format('HH:mm')]))
To this
// this line filters the appointment for a specific day and also adds the duration of a service to the start time of an already scheduled appointment. This way when I check if a generated time slot for a service will overlap with an already scheduled appointment it filters out the ones that will overlap
filterAppoinments.map(appoinment => results.push([appoinment.start.clone().subtract(serviceObject.hours, 'hours').subtract(serviceObject.minutes, 'minutes').format('HH:mm'), appoinment.end.format('HH:mm')]))

How do I make it such that .observe in Firebase retrieves all its data before calling reload collection view

How do I set it such that the function below knows when it has retrieved all data before calling reload collection view rather than reloading everytime a new URL is appended in the array.
func generateDataForRecents() {
if URLArrayStringRecents.count == 0 {
self.activityIndicator2.isHidden = false
self.activityIndicator2.startAnimating()
}
let queryGallery2 = FIRDatabase.database().reference().child("palettes")
queryGallery2.observe(.childAdded, with: {(snapshot) in
let URL = snapshot.childSnapshot(forPath: "URL").value as! String
self.URLArrayStringRecents.append(URL)
self.activityIndicator2.stopAnimating()
self.activityIndicator2.isHidden = true
self.whatsNewCollectionView.reloadData()
})
}
The reason why I need such a method is because I wish to implement pull to refresh to reload the collection view in my view controller.
Try :-
If you are calling for .value as FIRDataEventType only then its possible for you to check the count
let count = snapshot.childrenCount
if self.URLArrayStringRecents.count == Int(count){
self.whatsNewCollectionView.reloadData()
}
Otherwise if you are calling for .childAdded as FIRDataEventType, it will return single snapshot at a time , you can either fix up a NSTimer which waits for a certain time period after calling a .reloadData().

In Firebase, is there a way to get the number of children of a node without loading all the node data?

You can get the child count via
firebase_node.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });
But I believe this fetches the entire sub-tree of that node from the server. For huge lists, that seems RAM and latency intensive. Is there a way of getting the count (and/or a list of child names) without fetching the whole thing?
The code snippet you gave does indeed load the entire set of data and then counts it client-side, which can be very slow for large amounts of data.
Firebase doesn't currently have a way to count children without loading data, but we do plan to add it.
For now, one solution would be to maintain a counter of the number of children and update it every time you add a new child. You could use a transaction to count items, like in this code tracking upvodes:
var upvotesRef = new Firebase('https://docs-examples.firebaseio.com/android/saving-data/fireblog/posts/-JRHTHaIs-jNPLXOQivY/upvotes');
upvotesRef.transaction(function (current_value) {
return (current_value || 0) + 1;
});
For more info, see https://www.firebase.com/docs/transactions.html
UPDATE:
Firebase recently released Cloud Functions. With Cloud Functions, you don't need to create your own Server. You can simply write JavaScript functions and upload it to Firebase. Firebase will be responsible for triggering functions whenever an event occurs.
If you want to count upvotes for example, you should create a structure similar to this one:
{
"posts" : {
"-JRHTHaIs-jNPLXOQivY" : {
"upvotes_count":5,
"upvotes" : {
"userX" : true,
"userY" : true,
"userZ" : true,
...
}
}
}
}
And then write a javascript function to increase the upvotes_count when there is a new write to the upvotes node.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.countlikes = functions.database.ref('/posts/$postid/upvotes').onWrite(event => {
return event.data.ref.parent.child('upvotes_count').set(event.data.numChildren());
});
You can read the Documentation to know how to Get Started with Cloud Functions.
Also, another example of counting posts is here:
https://github.com/firebase/functions-samples/blob/master/child-count/functions/index.js
Update January 2018
The firebase docs have changed so instead of event we now have change and context.
The given example throws an error complaining that event.data is undefined. This pattern seems to work better:
exports.countPrescriptions = functions.database.ref(`/prescriptions`).onWrite((change, context) => {
const data = change.after.val();
const count = Object.keys(data).length;
return change.after.ref.child('_count').set(count);
});
```
This is a little late in the game as several others have already answered nicely, but I'll share how I might implement it.
This hinges on the fact that the Firebase REST API offers a shallow=true parameter.
Assume you have a post object and each one can have a number of comments:
{
"posts": {
"$postKey": {
"comments": {
...
}
}
}
}
You obviously don't want to fetch all of the comments, just the number of comments.
Assuming you have the key for a post, you can send a GET request to
https://yourapp.firebaseio.com/posts/[the post key]/comments?shallow=true.
This will return an object of key-value pairs, where each key is the key of a comment and its value is true:
{
"comment1key": true,
"comment2key": true,
...,
"comment9999key": true
}
The size of this response is much smaller than requesting the equivalent data, and now you can calculate the number of keys in the response to find your value (e.g. commentCount = Object.keys(result).length).
This may not completely solve your problem, as you are still calculating the number of keys returned, and you can't necessarily subscribe to the value as it changes, but it does greatly reduce the size of the returned data without requiring any changes to your schema.
Save the count as you go - and use validation to enforce it. I hacked this together - for keeping a count of unique votes and counts which keeps coming up!. But this time I have tested my suggestion! (notwithstanding cut/paste errors!).
The 'trick' here is to use the node priority to as the vote count...
The data is:
vote/$issueBeingVotedOn/user/$uniqueIdOfVoter = thisVotesCount, priority=thisVotesCount
vote/$issueBeingVotedOn/count = 'user/'+$idOfLastVoter, priority=CountofLastVote
,"vote": {
".read" : true
,".write" : true
,"$issue" : {
"user" : {
"$user" : {
".validate" : "!data.exists() &&
newData.val()==data.parent().parent().child('count').getPriority()+1 &&
newData.val()==newData.GetPriority()"
user can only vote once && count must be one higher than current count && data value must be same as priority.
}
}
,"count" : {
".validate" : "data.parent().child(newData.val()).val()==newData.getPriority() &&
newData.getPriority()==data.getPriority()+1 "
}
count (last voter really) - vote must exist and its count equal newcount, && newcount (priority) can only go up by one.
}
}
Test script to add 10 votes by different users (for this example, id's faked, should user auth.uid in production). Count down by (i--) 10 to see validation fail.
<script src='https://cdn.firebase.com/v0/firebase.js'></script>
<script>
window.fb = new Firebase('https:...vote/iss1/');
window.fb.child('count').once('value', function (dss) {
votes = dss.getPriority();
for (var i=1;i<10;i++) vote(dss,i+votes);
} );
function vote(dss,count)
{
var user='user/zz' + count; // replace with auth.id or whatever
window.fb.child(user).setWithPriority(count,count);
window.fb.child('count').setWithPriority(user,count);
}
</script>
The 'risk' here is that a vote is cast, but the count not updated (haking or script failure). This is why the votes have a unique 'priority' - the script should really start by ensuring that there is no vote with priority higher than the current count, if there is it should complete that transaction before doing its own - get your clients to clean up for you :)
The count needs to be initialised with a priority before you start - forge doesn't let you do this, so a stub script is needed (before the validation is active!).
write a cloud function to and update the node count.
// below function to get the given node count.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.userscount = functions.database.ref('/users/')
.onWrite(event => {
console.log('users number : ', event.data.numChildren());
return event.data.ref.parent.child('count/users').set(event.data.numChildren());
});
Refer :https://firebase.google.com/docs/functions/database-events
root--|
|-users ( this node contains all users list)
|
|-count
|-userscount :
(this node added dynamically by cloud function with the user count)

in CakePHP I want to have 2 cookies as they need different expiry times...can I do this?

I have been trying like this -
// ================
// = set a Cookie for the users city =
// ================
function set($cityId = null){
$this->components[] = 'RequestHandler';
$this->components[] = 'Cookie';
$this->Cookie->name = 'Exampleoffers';
//$this->Cookie->time = 3600; // or '1 hour'
//$this->Cookie->path = '/bakers/preferences/';
$this->Cookie->domain = 'www.example.co.uk';
$this->Cookie->secure = false;
$this->Cookie->key = 'kkjdDqSI232qs*&sXOw!';
$cities = ($this->City->find('list'));
if($cities[$cityId]) {
$this->Cookie->write ("Deal.city_id", (int) $cityId, false, "+2 months");
} else {
$this->Cookie->write ("Deal.city_id", key($cities), false, "+2 months");
}
however, I am not sure if it is clashing with my Authsome cookie (?) or something else, but I am unable to read this value back.
Is there some way to specify which cookie you want to read() from in CakePHP?
Is there a way to have a cookie with 2 diffferent values of expiry times? - i.e. a cookie has User.id with a expiry of 1 week, and a Deal.city_id with a expiry of 2 months, say? Or am I right to think I DO need 2 cookies?
many thanks for any tips. It's cake 1.3 btw !
You can, remember cookies are saved on the system so if you only save the cookie one time on that system it will have the set values, however, you cannot have 2 cookies with the same name set, which means that when you go and save the cookies you'll have to do this:
$this->Cookie->write('Name1', $data, false, $time);
$this->Cookie->write('Name2', $data, false, $time);
If you don't, one will overwrite the other.
EDIT: Adding some links in case you have more doubts:
API page of CookieComponent: http://api13.cakephp.org/class/cookie-component
Cookbook page of CookieComponent: http://book.cakephp.org/view/1280/Cookies

Resources