Spotify apps tabs - update cache - spotify

I'm trying to display a div inside tab playlists if the href contains a spotifyURI. This will be used to display a playlist under a tab.
Step by step this is my problem:
Click playlist tab and then click the "My playlist1".
The href is displayed in the playlist container under the tab playlists. (perfect so far)
Click the start tab and then click the playlists tab.
Instead of displaying the list of playlists the playlist container is show again. So the last used url is cached?
Then if the playlists tab is clicked again the url will be "reseted" and the list of playlists will be shown and playlist container hidden.
I'd like 4. to show the playlist list right away instead.
Is there a way to reset or what am I missing?
<script>
$(document).ready(function() {
sp = getSpotifyApi(1);
var m = sp.require("sp://import/scripts/api/models");
updateTabs();
m.application.observe(m.EVENT.ARGUMENTSCHANGED, updateTabs);
function updateTabs()
{
console.log(m.application.arguments);
var args = m.application.arguments;
$('.section').hide();
if (args[1] == "spotify") $("#playlist").html("args:"+args).show();
else $("#"+args[0]).show();
}
});
</script>
<div id="playlist" class="section">Container for playlist content</div>
<div id="start" class="section">Welcome</div>
<div id="playlists" class="section">
My playlist1
My playlist2
</div>
Thanks alot for all replys!

Here is how I will proceed using JQuery.
First of all you need to use the Localstorage :
var stor = sp.require("sp://import/scripts/storage");
Then if for exemple you get a list of playlist you can build the list like this
for (var i=0; i<d.playlists.length; i++) {
$('#playlists').append('My <a id="p' + i + '"href="'+ d.playlists[i] +'">playlist1</a>');
$('#playlists #p'+i).live('click', function(e) {
e.preventdefault();
stor.set('choosenplaylist', d.playlists[i]);
});
}
This was for the storage now for when changing tad :
if (!stor.get('choosenplaylist')=='') {
location.href=stor.get('choosenplaylist');
}
Okay this is a suggestion and it need to be tested regarding to your app.

Im trying this out now, and i can reproduce your bug (im guessing it's a bug, the tab should replace the url in my opinion)
But, until it's fixed, my best guess is to capture the playlist links in an event handler and cancelling the original event, after cancelling you replace the content with the appropriate playlist view.
Tab test code (on gist.github.com)
I've abstracted the actual view binding from the event handler, and added a click event hook that calls the abstract view binder instead of the "real" one, this also supports deep linking into the an app

Related

How to add audio/spotify attachments to azure bot service?

I currently have a node bot embedded on my web app via direct line but I am struggling to attach:
Spotify Audio
I am trying to do so by using the URL attachment or an adaptive card, but the spotify embed doesn't play
Below is the code I use:
var send = {
text: "stuff",
attachments: [
contentType: "audio/ogg",
contentUrl: "spotifyEmbedUrl"
]
}
await stepContext.context.sendActivity(send);
I am unsure on how I can get spotify audio to play.
Is there a way I can return HTML code (and so get around it by adding an iframe into the chat etc?)
OR maybe I could create a modal popup that I could create the embed iframe?
Any help would be appreciated!
Unfortunately, you can't just send a file to a web page and it automatically start playing. Additionally, while Spotify provides embed URLs, which are not a direct link to an audio file, you can't simply tell the browser to play the file.
However, Spotify provides the embed code for displaying a play button that can be used in a page to play a song. Assuming you are using Web Chat in a web site (and even if you're not, this will give you an idea) and that, from the code you supplied, you are wanting to send the song in an activity, you can achieve this by sending the embed code in the activity, instead, via Web Chat's store. When the activity is received, the embed code is passed to a function to update the page and, thus, display the play button.
Be aware, the play button is essentially a UI widget, not a media player. There is no functionality available for telling the play button to auto play, stop, or anything else. The most you can do is display the button after which the user will be required to interact with it.
Also, this is a someone bare bones, simplified implementation. There are many things that aren't accounted for - please don't consider this a complete solution. There are aspects you will need to consider (e.g. multiple cards that utilize a postBack action).
In your bot: You want to send the embed code in an activity. Whether that is an event, message, or something else, is up to you. As you can see below, I have chosen to send a hero card that initiates a postBack when the button is pressed (a postBack sends data behind the scenes without displaying the action to the user).
const card = CardFactory.heroCard(
"Rome Wasn't Built in a Day",
null,
CardFactory.actions([
{
type: 'postBack',
title: 'Read more',
value: `<iframe src="https://open.spotify.com/embed/track/6lzd7dxYNuVSvh7sJDHIa3" width="300" height="380" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>`
}
]),
{
subtitle: 'Artist: Morcheeba',
text: 'Album: Parts of the Process - released 2003'
}
);
await stepContext.context.sendActivity({ attachments: [card]});
Web Chat: First, use Web Chat's store to filter on incoming activities that include attachments where the button type (action) is postBack. When the condition is met, get the last card rendered and assign an event listener. When the card's button is clicked, get the 'spotify' container element and update the innerHTML with the embed code that was sent in the activity, thus displaying the play button.*
Please note, the setTimeout() used below is necessary for enabling the click action. Without the time out, the event listener being appended to the button would occur before the store finished processing the incoming activity.
<div id="webchat" role="main"></div>
<div class='spotify'></div>
[...]
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => action => {
if ( action.type === 'DIRECT_LINE/INCOMING_ACTIVITY' ) {
const activity = action.payload?.activity;
if (activity.attachments && activity.attachments[0].content.buttons[0]?.type === 'postBack') {
setTimeout(() => {
const spotifyIframe = activity.attachments[0].content.buttons[0].value
let cards = document.querySelectorAll( '.ac-adaptiveCard' )
let cardLength = cards.length;
let card = cards[ cardLength - 1 ];
card.querySelectorAll( 'button' ).forEach( button => {
button.addEventListener( 'click', ( e ) => {
e.preventDefault();
const spotifyContainer = document.querySelector( '.spotify' );
spotifyContainer.innerHTML = spotifyIframe
} )
} );
}, 300);
}
next( action );
} );
Hope of help!

Attachment links in Body field in Notes Web UI

I need to modify classical Notes Web UI application. In the UI, it shows mail data. If the data has attachments, they are shown as links in the body field.
I'd like to detect when a user clicks the links, and enable "next" button so that the user can move to the next screen. Is it possbile?
Yes, and this has nothing to do with Domino specifically: put a div around your next button, that initially has the style display: none or give itself an ID and put the display: none directly in the properties box of the button on the html tab, fields ID and style.
Then write a little JavaScript that runs in the onLoad event, selects all a tags with attachments in it (they all have $FILE in the href) and add a function to the click- event to set the style of the button to display: block or something else.
You can prevent the default event (open the attachment) by using preventDefault():
var list = document.getElementsByTagName("a");
for (el of list) {
if (el.href.includes("$FILE")) {
el.addEventListener("click", function(event){
var yourNextDiv = document.getElementById("IDOfDivWithNextButton")
yourNextDiv.style.display = "block"
event.preventDefault()
});
}
}

How can I make PrimeFaces tabs "linkable"?

I would like to be able to link to individual tabs in a PrimeFaces' "tabView". In other words, if my page "test.jsf" has a tabView with a tab entitled "Example", I want to be able to click a link to "Test.jsf#Example" and have the "Example" tab loaded automatically. How can I do this?
This can be done with a wee bit of JavaScript (using jQuery). I hope I have commented the following code well-enough that it can be understood.
<script type="text/javascript">
// What this does: when the page is loaded with a URL fragment (i.e, the #abc in example.com/index.html#abc),
// load the tab (by "clicking" on the link) that has the same text value as the fragment.
// Example: if you go to test.jsf#Example, the tab called "Example" will be clicked and loaded.
// This allows individual tabs to be linked to, and puts what tab you were on in the history.
navigateToTab = function () {
if (window.location.hash) {
jQuery('ul.ui-tabs-nav li a').each(function (i, el) {
if (jQuery(el).text() === window.location.hash.replace('#', '')) {
jQuery(el).click();
return;
}
})
}
};
jQuery().ready(navigateToTab);
jQuery(window).bind('hashchange', navigateToTab);
// This makes it so that if you click a tab, it sets the URL fragment to be the tab's title. See above.
// E.g. if you click on the tab called "Example", then it sets the onclick attribute of the tab's "a" tag
// to be "#Example"
setupTabFragmentLinks = function () {
jQuery('ul.ui-tabs-nav li a').each(function (i, el) {
el.onclick = function() {window.location = '#' + jQuery(el).text()};
})
};
jQuery().ready(setupTabFragmentLinks);
</script>
All you have to do is insert that JavaScript in the page that has the tabs. Then you can get a link to a tab with the usual <a href='test.jsf#Example>Click here!</a>. An added bonus is that the tab you were on becomes part of the browser history; i.e., if you navigate away from the page that has the tabs, then press the "back" button, you are brought back to the tab you were on.
Note: if the tabView changes (e.g. you add or remove tabs), you will need to call setupTabFragmentLinks again.
Primefaces provides a javascript API for the <p:tabView/>(and many other components). You can call the select(index) method on the client side widgetVar name of your <p:tabView/>. For example, on a tab view
<p:tabView id="thePanel" widgetVar="tabPanel"/>
From a <p:CommandButton/>, you can call tabPanel.select(1) in the onclick attribute to select the first tab and so forth
<p:commandButton update=":thePanel" value="Do It " id="doIt" onclick="tabPanel.select(1)"/>

How to bind an event to an element in a chrome extension popup window

I've searched quite a bit, and none of the answers I've found have worked 100%.
Basically, I want the popup to show a handful of buttons, then call the same function with different parameters. Concept:
<button onclick="foo(1,2);">Button 1</button>
<button onclick="foo(2,3);">Button 2</button>
I've tried a few simple and direct methods, none of which work. If I take the code out of the function, and have popup.js contain the code, it works (my function is fine).
I've tried:
$('#btn1').click(function(e) {
alert('btn1');
});
as well as
document.getElementById('btn1').addEventListener('click',doSomething(1,2));
Also, i've tried adding the button addEventListener inside of a document.addEventListener('DOMContentLoaded', function() {....});
The curious part is that if my popup.js contains button.addEventListener, the first one is fired upon clicking the browser action, and then nothing works. This happens regardless of whether the click event listner is inside of a DOMContentLoaded listener or not.
I have a feeling this is a CSP issue, but I can't seem to get it to work.
For those scanning for a question mark:
From within a popup.html/popup.js, how can I call a single function with different parameters based on an onclick event?
Issues
I'm not sure if it was just through your abbreviation when copying to SO but none of the code you have given would work "as pasted":
You have no id's on the buttons so binding to the id (I assume this is just because they weren't relevant in the first paste!)
The second paste uses the jQuery library and so you would need to make sure jQuery is included and allowed: https://stackoverflow.com/a/10928761/969807
In the third paste, the second parameter of addEventListener should be a function accepting the event as the first and only parameter (if wanted). (Read On)
Solution
The most common way to bind an event in the scenario you describe would be like so:
document.getElementById('btn1').addEventListener('click', function (){
foo(1, 2);
}, false);
However if there were a lot (or a variable amount) of buttons, I would probably do it like so:
popup.html:
<div id="buttons">
<button data-param1="1" data-param2="2">Button 1</button>
<button data-param1="2" data-param2="3">Button 2</button>
</div>
popup.js:
var els = document.querySelectorAll('#buttons button'),
i, total, param1, param2;
for (i = 0, total = els.length; i < total; i++) {
el = els[i];
param1 = parseInt(el.getAttribute('data-param1'));
param2 = parseInt(el.getAttribute('data-param2'));
el.addEventListener('click', function (){ foo(param1, param2) }, false);
}

Filtering a page with infinite scroll in AngularJS

I implemented an infinite scrolling feature on my AngularJS + Node.js app.
It is based on this JSfiddle and works the same way: http://jsfiddle.net/vojtajina/U7Bz9/
HTML:
<div id="fixed" when-scrolled="loadMore()">
<ul>
<li ng-repeat="i in items">{{i.id}}</li>
</ul>
</div>​
Javascript:
function Main($scope) {
$scope.items = [];
var counter = 0;
$scope.loadMore = function() {
for (var i = 0; i < 5; i++) {
$scope.items.push({id: counter});
counter += 10;
}
};
$scope.loadMore();
}
angular.module('scroll', []).directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
​
My reasons for implementing the infinite scroll is in order to save my users bandwidth by not loading all 1000 results and their corresponding images unless the user wants to see all of it.
However when searching within the results using an AngularJS filter I am encountering problems, because of course not all the results are there (unless the user has scrolled to the very bottom) so the search will only return a fraction of the required results.
I then removed the infinite scrolling feature in order to have the search function work properly but this provided new issues on chrome (not on Firefox though) when I open the page the browser starts loading images from the top. If I then filter the results by searching for something starting with "z" (at the very bottom of the results) Firefox switches focus and loads the results starting with "z" first (as they are then the only ones being displayed). However chrome continues loading through the list and thus the user will only see the results of their search (for "z") once all the images in the app have been loaded.
I am looking for a way to have angular provide both the infinite scroll and a proper search filter on the results at the same time. If this is not possible then a way to make chrome load the visible images first.
I am currently trying some weird stuff with a bunch of different arrays in the scope but I haven't had any luck so far.
Since several people here had a similar issue and I was struggling myself, I took the time to create a fiddle that works (for my case).
https://jsfiddle.net/lisapfisterer/Lu4sbxps/
The main idea is to dynamically extend the limitTo value using infinite-scroll's function call
<div infinite-scroll="loadMore()" infinite-scroll-distance="20">
<tr data-ng-repeat="logEvent in logEventFilter = (logEvents | filter:searchTerm | limitTo:numberToDisplay) track by $index">
<td> {{$index}} </td>
<td> {{logEvent.name}} </td>
<td> {{numberToDisplay}} </td>
</tr>
</div>
loadMore just increases the limit
$scope.loadMore = function() {
if ($scope.numberToDisplay + 5 < $scope.logEvents.length) {
$scope.numberToDisplay += 5;
} else {
$scope.numberToDisplay = $scope.logEvents.length;
}
};
What you want to do isn't "impossible" but it's certainly going to be a little complicated.
Have your server do all of the "filtering" to ensure that the paginated values returned are the proper values for the filter(s).
When the server returns the results, render all of the HTML to the screen except the src attributes of image tags. This way none of the images will begin loading yet.
Scroll to the proper "page".
Make sure all of the heights prior to the images being loaded are the same, now do some JS magic to figure out which ones are visible.
Set the src attribute of the visible images only, Subscribe to their "load" events and create a $q promise that is complete once all loads are complete.
after that promise completes, set the rest of the image src attributes so the remainder of the images will load.

Resources