Keep a specific property of a object/template non-reactive - meteor

On my site, users can recieve notifications on different events (such as a new comments on a user's post). Users can view these notifications on a special page in their profile. I want when a user come to that page that he can distinguish new ("unread") notifications. Namely, I want the following behavior:
When a user comes to a notifications page, any notification that has never before been shown on that page, is highlighted.
If a user leaves that page and comes later, only the new notifications (that has appeared in the meantime) are highlighted.
Each notification is highlighted for as long as the user stays on the page.
If a new notification appears while the user is on page, it is immediately loaded and shown and highlighted.
If a notification changes and/or is removed while the user stays on the page, the page reflects this change (preferably keeping the highlight status).
I start with having a Notifications collection that has a read field showing whether this notification is "read" (i.e. has already been shown). Now I can highlight only notifications that has read set to false. However, it will not get the required behavior in a simple way.
The main problem is point 3, because I must somehow mark that the notification has been read, but have this mark be taken into account only on the next visit to page.
For example, I might try to set notification's read field to true on template render (for example, in Template.notification.rendered), but it will immediately force notification template update and the highlighting will be removed.
So the particular problem is how to keep the initial value of read property and not redraw the template on this property change?
(I thought also of different approach such as relying on some user-side event such as user leaving the page, and updating read only then, but this does not seem to be reliable.)
For now, I have implemented the following solution:
//// html
<template name='notification'>
<li class="list-group-item {{notificationClass}}">
...
</li>
</template>
//// coffee
Template.notification.helpers
notificationClass: ->
if (this.read && UI._templateInstance().wasRead)
undefined # no highlight
else
"list-group-item-info" # highlight
Template.notification.rendered = ->
this.wasRead = this.data.read
this.data.markAsRead() # ultimately updates the collection
that is, on rendered event I set the template property to remember whether this notification was read initially. This template property does not change as long as the template exists (and even if notification data changes, the template property is there). In template helper, instead of directly accessing the notification read property, I check template property.
This seems to work, but looks too complicated for me. So:
Are there any major disadvantages in this approach? For example, can template's rendered code be re-executed while a user stays on the page?
Is there some better approach for my problem?

Related

GTM Trigger sequence

How can I achieve this using Google Tag Manager?
I want Tag to fire if user made a seeuence of actions. For instance: visits homepage > visits certain category page > clicks on a button that expands additional content > having that content on screen visible at least 90% for 30sec or more > clicking on a button
... exactly in that order.
I would recommend you to use sessionStorage to achieve this. By passing a value to the sessionStorage for each step in the funnel you want to track you can then trigger an event once the user reached the final step.
This solution will require some javascript skills. Here is code snippet to illustrate what I mean.
var url = 'https://www.startpage.com/second';
var start_page = 'https://www.startpage.com';
var second_page = 'https://www.startpage.com/second';
var third_page = 'https://www.startpage.com/second';
if(url = start_page){
sessionStorage.setItem('funnel', 'step 1');
}
if(sessionStorage.funnel){
var previous_step = sessionStorage.funnel;
if(url === second_page && previous_step === start_page){
sessionStorage.funnel = second_page;
}else if(url === third_page && previous_step === third_page){
alert('Send event');
}
}
First of all, you need to enhance Google Tag Manager (GTM) default behavior, buy making some information persistent. By default, GTM tracks information for the current page, so you need to keep track of users making progress in this funnel across several pages. You will find some good advice and examples in this Simo Ahava article.
Second, you need to create all the relevant triggers and tags, that catch these user actions, and update current user's progress in this persistent variable, when they proceed to the next stage. You also need to consider, if there are any actions, which reset the progress, or you allow the user to take other actions as well in the meantime.
The triggers are the following:
Page view for your main page
Page view for your specified category page(s)
Click trigger on the specified element to expand your content
A visibility trigger with proper settings (reference to element, 90% visibility, 30 seconds time on screen)
Finally, a click on your specified element
You should check the progress, and update it when the proper step is taken, compared to the current state. (E.g. complete step three only when user is already on step 2.)
Obviously, you'll need to write the necessary scripts within several Custom HTML tags, but you can probably generalize the behavior with some helper functions for checking the progress, and storing any updates.

How to refer to a user in chat with their UIDs?

I have an array of User IDs which I've set as admins and I would like to print them to clients when they call the /admins command.
The main issue is that I couldn't see or find a way to print a placeholder entity (it's mention in this case) which opens a chat window with the IDs I've specified.
Remember that when you post #username in a chat, Telegram simply binds it as a mention to the referenced user and it jumps on a chat window when you click on it. I wonder if there is an equivalent for #123456789, where the number represents the User ID.
if you create/send a message from a bot, you can set parse_mode to HTML, then send text/caption with HTML anchor tag same this:
for contact me, just click here
but if you want to send message as user, you can just use userName prefixed by #, and creating manual link to tg://user?id=123 or type plain text like tg://user?id=123, don't work in Telegram now. yes; just in iOS (less than 15% of Telegram user) that work since 2013!

Google Appmaker: Update form based on selected dropdown option

I have an AppMaker app that has a from based off of one address table/datasource. I can get a form with next/prev buttons, but replaced the key field (name) with a dropdown list of all names (a user can start typing names to jump there, with the dropdown showing).
My hope is that when a user selects from the dropdown, the entire form updates and the next/prev buttons work properly as well (there too many records to page thru with next/prev only). I don't have to have next/prev functionality if it complicates things too much.
Currently the dropdown is working, but I cannot get the index for the next/prev buttons set or the rest of the form to reflect the selected dropdown record.
I've tried to set the "onValueEdit" event to something like this...
var selected = widget.value;
var idx = widget.options.indexOf(selected);
console.log("Selected: "+selected+", index = "+idx+"\n");
if(idx < 0) { //...this error is never hit
console.log("Index error - setting to zero!\n");
idx = 0;
}
widget.datasource.loadPage(idx); //...update form?
Two observations via console logging:
The "idx" var is never set to the selected dropdown index reliably, and is
often "0" (tho no error msg ever shows), so the "indexOf()" function
isn't working as expected.
The "selected" var (name) is always correct.
If I call widget.datasource.loadPage(...) with a fixed value (say 5) it has no effect on what is shows in the form either (previous loaded data remains) - obviously not the way to do it :v/
Can you steer a noob in the right direction?
If you are using default App Maker form, then you can see that so-called pager, doesn't actually paginate. It triggers prevItem/nextItem datasource methods, in other words it navigates through datasource items, not pages. Here is a quote from App Maker docs:
nextItem: Selects the next item. For Query Datasources, if the current item is the last item on the page, then this loads the next page and selects the first item on the newly loaded page.
So, if you already have all your items loaded(you set query page size for your datasource to 0), then you need just to change selected item within datasource:
// onValueEdit dropdown event
// assuming, that form and dropdown share same datasource
widget.datasource.selectKey(newValue._key);
If you really have lots of items and it is not feasible to load all of them in one call... then it will be all another story...
UPDATE:
It's important that Options and Value are set as shown in the image below!
However, I had trouble setting them that way (read: wasted hours!) until I wiped them both completely using More options in the binding picklist, and tried again (I had even tried on a brand new app!). I was being forced to choose ..projections.. and then a final field before the OK button would be available.
Not sure if AppMaker is buggy here or there is something simple I'm not understanding!
None of the coding in my original question is required.
Once set this way, binding just works as you would expect it!!
All other fields are set as #datasource.item. and are bound to whatever item is chosen. No Events settings are necessary for the dropdown either, as I thought they might be.
I deleted this page and started again, and replaced the default business name data field with a drop down, I set the dropdown as:
Options: #datasources.Addresses.items
Value: #datasources.Addresses.item
It works fine?! Not sure what happend in my original page!
UPDATE:
So it seems you need to delete both the Value and Options and then re-enter these. The OK will light up when you do.
Also, my original take on App Maker was to build the UI and attach data. That was my first mistake. You build the data then have App Maker build edit/add pages for you.

Turn off notifications for Meteor collections

How do I tell Meteor to stop publishing changes on a collection (for a moment)?
Also how to tell it to resume and that the collection changed?
Basically (on the server):
People = new Meteor.Collection("people")
insertPeople = ->
// Don't notify clients of the following changes
// Insert a bunch of people into the People collection
// Resume notifications
Put a flag in each document, 'updating'.
Add the new ones with this set to true; render their template with a css class that hides them based on this field.
When ready, update the collection to updating: false. They will be visible pretty quickly.
This being said, there are events you can plug into to make transitions more pleasant/animated. Didn't think you were asking that, but it may be a better answer.
To the comment:
Inserting a template for an additional document triggers DOM changes, which are fairly expensive, and then the device has to figure out how to display. Updating a property requires just the second part, that the device has to figure out how to display.

Prompt to save data if and when changes have been made

I am using asp.net and I need to display a prompt to the user if they have made changes to the web page, and they accidentally close down the browser.
The page could be anything from "Edit Profile" to a "Submit a Claim" etc.
How can I can display the messagebox, ensuring that it is displayed only if changes have been made (as opposed to, the user making changes, then undo-ing the changes, and shutting down the browser)
What I have done in the past is use some client side scripting which does this check during the onbeforeunload event....
var showPrompt=true;
var isDirty=false;
var hidePopup=false;
function onBeforeUnload() {
if (showPrompt) {
if (isDirty) {
if (hidePopup || confirm("Click ok to save your changes or click cancel to discard your changes.")) {
doSave();
}
}
}
showPrompt = true;
hidePopup = false;
}
ShowPrompt can be set to false when your clicking on an anchor tag which won't navigate you away from the page. For example <a onclick='showPrompt=false' href='javascript:doX()'/>
isDirty can be used to track when you need to save something. You could for example do something like $("input").onchange(function(){isDirty=true;}); To track undo's you might want to replace isDirty with a function which checks the current state from the last saved state.
HidePopup lets us force a save without confirming to the user.
That's very difficult to even touch without understanding what's on the page. If it's a few controls you capture value at page load and store them so you can later compare. If it's a complex page you'd need to do an exact comparison to the entire viewstate.
Typically you'd handle this type of situation by setting a boolean to TRUE the first time any change is made and disregard the user changing it back. If you're just trying to avoid accidential non-save of data the user should be smart enough to know they've "undone" their changes.
You can do this with javascript. See this question and either of the first two answers.

Resources