Keep previous items state in list Vue JS - iframe

I'm creating a simple chat app. The real time logic is done with sockets. I have a list with my Message(messages variable) objects and the final list to render(chatItems). The last one contains messages and date labels.
Also the logic for YouTube videos recognition added too. If a message contains the YouTube video link, the iframe with YouTube player will be rendered. And if I'll try to add a new message to the list, the playing videos will be stopped, because a component will be re-rendered.
This is the code of how I'm getting the final list to render.
private messages: Message[] = []
get chatItems(): ChatItem[] {
return this.messages.flatMap((message: Message, index, array) => {
if (index !== array.length - 1) {
let previousMessage = array[index + 1]
let isTheSameDay = TimeHelper.areTimestampsOfTheSameDay(message.sentAt, previousMessage.sentAt)
switch (isTheSameDay) {
case true:
return new ChatItem(message, 'message')
case false: {
let date = TimeHelper.getDateFromTimeStamp(message.sentAt)
return [new ChatItem(message, 'message'), new ChatItem(date, 'date')]
}
}
} else {
let date = TimeHelper.getDateFromTimeStamp(message.sentAt)
return [new ChatItem(message, 'message'), new ChatItem(date, 'date')]
}
})
}
And this is a Message component:
<div :class="$style.messageWrapper">
<image :image="message.sentBy.user.photo" :is-avatar="true"/>
<div :class="$style.messageContent">
<div :class="$style.messageSenderSentAtWrapper">
<span :class="$style.textMedium14px">{{ message.sentBy.user.name }}</span>
<span :class="$style.textRegular12pxHelper">{{ formatSentTime(message.sentAt) }}</span>
</div>
<span :class="$style.messageBody" v=text="message.body"/>
<div v-show="isYouTubeLink" :class="$style.messageYouTubePlayerWrapper">
<iframe width="450" height="250"
:srcdoc="youtubePlayerSrcDock"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen/>
</div>
</div>
</div>
And the question: how do I can prevent stopping of playing YouTube video in the iframe in my Message component? Accepting all the ways) Cause this is the big problem right now. Thanks.

Related

Get Scorm test result from inside an iframe (same domain)

My company is getting a scorm test from another company. (scorm schemaversion 1.2)
We are embedded the test in an iframe like this:
<html>
</head>
<iframe src="exam/scormcontent/index.html" name="course">
</iframe>
</html>
This is the test folder structure:
I am new this scorm solution. What we are trying to do is to get the final result of the scorm test (student passed/failed) in the parent html page.
The html page and the scorm are planned to be hosted on the same domain.
P.S: The entire project involves a react app, where at some stage, the user is supposed to do the scorm test, and he will only be allowed to continue if he passed the test. I am not sure if our plan to use iframe is what we should do. I would love to learn if there is a better option.
I have found a way to do it based on this:
https://github.com/hershkoy/react_scrom
The idea is to inject javascript code into the iframe (requires that the iframe and the parent are on the same domain).
The injected javascript code is listening to the button click events, and send a postMessage event to the parent when detects that the course is completed.
<div id="result"></div>
<input id="btn" type="button" value="Go to course" name="btnOpenPopup" onClick="OpenNewWindow()" />
<iframe style="display:none;" id="myiframe" src="http://localhost/training/content" name="course" frameborder="0" style="overflow:hidden;height:100%;width:100%" height="100%" width="100%"></iframe>
<script type="text/javascript">
const iframe = document.getElementById('myiframe');
const iframeWin = iframe.contentWindow || iframe;
const iframeDoc = iframe.contentDocument || iframeWin.document;
function OpenNewWindow() {
iframe.style.display="block";
document.getElementById('result').innerHTML = "";
document.getElementById('btn').style.display="none";
}
function injectThis() {
//alert("hi!");
document.addEventListener('click', (event) => {
console.log("click!");
let chk_condition = event &&
event.target &&
event.target.href &&
event.target.href.includes("exam_completed");
if (chk_condition) {
event.preventDefault();
event.stopPropagation();
window.parent.postMessage({type: 'course:completed'}, '*');
//window.close();
};
});
};
window.addEventListener('message', event => {
// IMPORTANT: check the origin of the data!
if ( true /*event.origin.startsWith('http://localhost:3002')*/) {
// The data was sent from your site.
// Data sent with postMessage is stored in event.data:
console.log(event.data);
if (event.data.type=="course:completed"){
iframe.style.display="none";
document.getElementById('result').innerHTML = "TEST PASSED!";
};
} else {
// The data was NOT sent from your site!
// Be careful! Do not use it. This else branch is
// here just for clarity, you usually shouldn't need it.
return;
}
});
var script = iframeDoc.createElement("script");
script.append('window.onload = ' + injectThis.toString() + ';');
iframeDoc.documentElement.appendChild(script);
</script>

get data-src content from the iframe of the active slide, then write an src attribute instead with this content

<iframe class="lazyload iframevimeocover" width="100%" top="-50%" left="-50%"
data-src="https://player.vimeo.com/video/https://vimeo.com/337332051&background=1&muted=1&autoplay=0&loop=1&controls=0&autopause=0&byline=0&portrait=0&title=0&transparent=0&gesture=media&dnt=1&quality=1080p" frameborder="0" allow="autoplay; encrypted-media; fullscreen;" webkitallowfullscreen mozallowfullscreen ></iframe>
I use lazysizes to lazyload.
when it is loaded, ".lazyloaded.iframevimeocover" is the iframe class.
On the code below, I get this error in the console:**
TypeError: slideActive.find('.lazyloaded.iframevimeocover').get(0).attr is not a function. (In 'slideActive.find('.lazyloaded.iframevimeocover').get(0).attr('data-src')', 'slideActive.find('.lazyloaded.iframevimeocover').get(0).attr' is undefined)
I didn't find what I need to change to avoid this error ?
actual code:
//// replace data-src to src, and play video from active slide ////
// select active slide
var slideActive = $('.swiper-slide').eq(swiper.activeIndex);
// get content from "data-src" attribute
let dataSrc = slideActive.find('.lazyloaded.iframevimeocover').get(0).attr('data-src');
// write in the iframe a new attribute 'src' with all the dataSrc content inside
slideActive.find('.lazyloaded.iframevimeocover').get(0).attr('src', dataSrc);
// create a new player
var player = new Vimeo.Player(slideActive.find('.lazyloaded.iframevimeocover').get(0));
// play player
player.play();

Disqus Comments affecting my pagespeed. How can I either lazy load or click to show comments?

Hi I've static website(made with Hugo) with Disqus installed. My website load time is really affected by ~30% in negative. So, I wish I could prevent it from loading it until a person reaches to that section or a better approach is that there'll be 'show comments' button that totally leave on visitor to decide.
I'm already using lazyload(lozad js library) but it's not working with disqus even I tried following code too
var iframes = doc.querySelectorAll('iframe');
iframes.forEach(function (e){
e.classList.add('lozad'); // adds required 'lozad' class
var iframeAttr = e.getAttribute('src');
e.setAttribute('data-src', iframeAttr); // lozad needs source data in data-src attribute
e.setAttribute('src', ''); // empty src attribute so that library use it at right time
});
Zachary wrote a tutorial for Hugo sites on how to display a Show comments button and have the Disqus comments only displayed when a reader click on the button.
As a bonus, the Disqus Javascript file embed.js will only load when the button is clicked.
To add this to your site, first, create a partial and name it disqus.html. Inside this file, put the following code:
<div id="disqus-container">
{{ with .Site.DisqusShortname }}
<button id="disqus-button" onclick="showComments()">Show comments</button>
<div id="disqus-comments">
{{ $isDummyName := eq . "yourdiscussshortname" }}
{{ $isServer := $.Site.IsServer }}
{{ if or $isDummyName $isServer }}
<p>Disqus comments are disabled.</p>
<script type="application/javascript">
function showComments() {
{{ partial "disqus-js-common.js" . | safeJS }}
}
</script>
{{ else }}
<div id="disqus_thread">
</div>
<script type="application/javascript">
function showComments() {
{{ partial "disqus-js-main.js" . | safeJS }}
{{ partial "disqus-js-common.js" . | safeJS }}
}
</script>
{{ end }}
<noscript>Enable JavaScript to view Disqus comments.</noscript>
</div>
{{ end }}
</div>
Then, create two Javascript files. First one call it disqus-js-common.js. Inside this file, add the following code:
// Remove button
var disqusButton = document.getElementById('disqus-button');
disqusButton.parentNode.removeChild(disqusButton);
// Un-hide comments
var disqusComments = document.getElementById('disqus-comments');
disqusComments.style.display = 'block';
The second Javascript file call it disqus-js-main.js. And inside this one, add the following code:
// Config
var disqus_config = function () {
};
// Build and append comments
var d = document;
var s = d.createElement('script');
s.async = true;
s.src = '//' + "{{ . }}" + '.disqus.com/embed.js';
s.setAttribute('data-timestamp', + new Date());
(d.head || d.body).appendChild(s);
Finally, add a little bit of CSS to make everything looks better:
#disqus-container {
font-size: 0.85rem;
border: 1px solid;
padding: 1.5rem;
}
#disqus-button {
width: 100%;
}
#disqus-comments {
display: none;
}
#disqus-comments,
#disqus-comments iframe {
max-height: 65vh !important;
overflow-y: auto;
}
Source
You can use the IntersectionObserver API to lazily load & initialize Disqus only once a user scrolls to a particular part of the page. The IO API is well-supported in modern browsers, so you wouldn't need to load any other lazy loading package (or custom JS) that'd further bog down your site.
Here's an example I got working. It uses the div that Disqus uses to mount its comments to as the point on the page that triggers everything to load & initialize. Note that a good chunk of this (it's commented) is copied straight from their documentation.
<script>
let mountNode = document.querySelector("#disqus_thread");
let options = {
rootMargin: "0px",
threshold: 0,
};
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Initialize Disqus!");
//-- Copied from their documentation: START
var disqus_config = function () {
this.page.url = "https://whatever.com";
this.page.identifier = "whatever!";
};
var d = document,
s = d.createElement("script");
s.src = "https://your-shortname.disqus.com/embed.js";
s.setAttribute("data-timestamp", +new Date());
(d.head || d.body).appendChild(s);
//-- Copied from their documentation: END
// After initializing, we don't need this observer anymore.
observer.unobserve(mountNode);
return;
}
});
}, options);
observer.observe(mountNode);
</script>
All that said, even this sort of approach is gonna get you so far. Building comments and a form client-side is an inherently expensive set of tasks, and so you're still gonna see some detrimental effects to page performance. If not load time, then the blocking of the main thread by all that Disqus is executing on the page when it starts up. You're also gonna miss out on the SEO benefit of having status HTML comments on your page.
All of this represents the bulk of the reasons I ditched disqus on my own site and built my own service for it, which I eventually opened up to the public. It integrates with different platforms (not yet Hugo, but it's down the road hopefully), and spits out statically-rendered comments and avoides the-side bloat, etc. Keep it in mind for any future rebuild you go through:
https://jamcomments.com/
It can be achieved with pure HTML and JavaScript
But since I really like Alpine.js this is how it can be done
<button
x-data="disqusComponent()"
x-show="!loaded"
#click="toggle()"
>Show/Post Comments
</button>
<div id="disqus_thread"></div>
See Load Disqus Comments on demand

How do I access one 'sibling' variable in a meteor template helper when I am in the context of another?

How do I access one 'sibling' variable in a meteor template helper, when I am in the context of another? I want to determine whether the user that is logged in and viewing the page is the same user that posted the ride offering, so that I can hide or show the "bid" button accordingly.
For example, here is my template (html) file:
<!-- client/views/ride_offers.html -->
<template name="RideOfferPage">
<p>UserIsOwner:{{UserIsOwner}}</p>
{{#with CurrentRideOffer}}
{{> RideOffer}}
{{/with}}
</template>
<template name="RideOffer">
<div class="post">
<div class="post-content">
<p>Details, Author: {{author}}, From: {{origin}}, To: {{destination}}, between {{earliest}} and {{latest}} for {{nseats}} person(s). Asking ${{price}}.
<button type="button" class="btn btn-primary" >Bid</button><p>
<p>UserIsOwner:{{UserIsOwner}}</p>
</div>
</div>
</template>
And here is my JavaScript file:
Template.RideOfferPage.helpers({
CurrentRideOffer: function() {
return RideOffers.findOne(Session.get('CurrentOfferId'));
},
UserIsOwner: function() {
return RideOffers.find({_id: Session.get('CurrentOfferId'), userId: Meteor.userId()}).count() > 0;
}
});
In the "RideOffer" template, I am able access the variables author, origin, ..., price. But I am unable to access the boolean UserIsOwner. I am, however, able to access the boolean UserIsOwner in the "RideOfferPage" template.
Does anyone know how I can access the boolean UserIsOwner in the "RideOffer" template?
Cheers,
Put the userIsOwner function outside the helper as an anonymous function and then call it from both templates.
Template.RideOfferPage.helpers({
CurrentRideOffer: function() {
return RideOffers.findOne(Session.get('CurrentOfferId'));
},
UserIsOwner: checkUserIsOwner()
});
Template.RideOffer.helpers({
UserIsOwner: checkUserIsOwner()
});
checkUserIsOwner= function() {
return RideOffers.find({_id: Session.get('CurrentOfferId'), userId: Meteor.userId()}).count() > 0;
}
There are several ways to do what you're asking.
In your particular example you are not asking about siblings, but about parents, since the RideOfferPage template renders the RideOffer template. You can access variables in the parent data context (but not helpers) like so:
<template name="RideOffer">
<div class="post">
<div class="post-content">
<!--
other stuff
-->
<p>UserIsOwner:{{../UserIsOwner}}</p>
</div>
</div>
</template>
In other cases, you may have a template being rendered as a sibling of this one. In that case, you can't actually know what the sibling is until the template is actually on the page; however, you can find it in the rendered callback:
Template.foo.rendered = function() {
var current = this.firstNode;
var next = $(currentItem).next(); // or .prev()
if (next.length) {
nextData = Spark.getDataContext(next[0]);
}
// Do something with nextData
};
Finally, you can get the parent context of any rendered DOM element by repeatedly iterating through its parents. This isn't super efficient but I've used it in places where there is extensive drag and drop with DOMFragments moving around on the page:
Template.bar.events = {
"click .something": function(e) {
var target = e.target;
var context = Spark.getDataContext(target);
var parentContext = context;
while (parentContext === context) {
parentContext = Spark.getDataContext(target = target.parentNode);
}
// Do something with parentContext
}
};
I'm curious to know if there is a better way to do the last thing, which may potentially have to iterate through many DOM elements. In any case, you may want to check out my meteor-autocomplete package for this and other cool tricks.

aria.touch.swipeend event issue--not working as expected

I am trying to use swipe events to obtain iphone like toggle switch. i want to handle swipemove and swipeend events. For example:
<div class="xyz" {on swipemove {fn:"swipemoveHandler"} /}> </div>
is working as expected while,
<div class="xyz" {on swipeend {fn:"swipeendHandler"} /}> </div>
is throwing error "The event type: 'swipeend' is an invalid event type."
I am using AT1.3.7 and any help in this regard is greatly helpful.
Thanks in Advance
You can use 'swipe' event of Aria templates which is triggered after a swipe is completed.
Please refer to sample below. This was included in AT 1.3.4
swipeHandler : function (event) {
event.preventDefault(true);
document.getElementById("touchMe").style.visibility = "hidden";
document.getElementById("swipeDirection").innerHTML = event.direction;
document.getElementById("swipeDistance").innerHTML = event.distance;
document.getElementById("swipeLength").innerHTML = event.duration;
document.getElementById("swipeStartX").innerHTML = event.startX;
document.getElementById("swipeStartY").innerHTML = event.startY;
document.getElementById("swipeEndX").innerHTML = event.endX;
document.getElementById("swipeEndY").innerHTML = event.endY;
return false;
}
Below you can see how event can be attached to an element
<div id="touchboard"
{on swipe {
fn : this.swipeHandler,
scope : this
}/}
>
<!-- your content -->
</div>
Refer to link for more help http://snippets.ariatemplates.com/samples/github.com/ariatemplates/documentation-code/samples/utils/touch/swipe/

Resources