Archives pour la catégorie Clouder

Windows Metro – Clouder! (11) – Utiliser la recherche intégrée à Windows 8 dans son application

Si vous explorer un peu Windows 8, vous verrez qu’à tout moment, vous pouvez faire apparaître la barre de droite, qui contient plusieurs options, notamment rechercher:

Cette interface est unifiée et toujours accessible. L’utilisateur Windows 8 prendra donc l’habitude de rechercher à l’aide de cette fonctionnalité. Vous pouvez dans votre application profiter de cette recherche unifiée Windows 8. Cela vous permet notamment de ne pas vous préoccuper de la gestion du champ de recherche et de l’affichage des suggestions qui sera géré par le système.

Dans un premier temps, on va voir comment ajouter une page de résultats de recherche à son application et comment la peupler avec les résultats de SoundCloud. Dans le tutorial suivant, vous verrez comment afficher des suggestions directement dans Windows 8.

Ajouter une page avec résultats de recherche

Si vous en avez marre de ce tutorial, vous pouvez aller voir du côté de la documentation qui est de très bonne qualité:

Quickstart: Adding search to an app (Metro style apps using JavaScript and HTML)

Rendez-vous dans Visual Studio et arrêtez le debug si vos étiez en train de travailler sur votre application. Dans le Solution Explorer, click droit sur /pages/ > Add > New Folder et nommez le « results ». Plus click droit sur ce nouveau dossier > Add > New Item > Windows Metro Style > Search Target Contract:

Nommez la page « results.html » puis OK. Visual Studio vient de vous mâcher le travail en créant une page pour accueillir les résultats de recherche. Celle-ci est composée d’une liste, ainsi que de filtres. Dans notre application, on a pas besoin des filtres, il faudra donc supprimer tout le code qui s’occupe de ces derniers.

Lire la suite

Windows Metro – Clouder! (10) – Créer un lecteur audio avec le tag

Suite du tutorial sur le développement d’application pour Windows 8 Metro sur la stack HTML5. Jusque là, on a pas vraiment utilisé ce qui fait « HTML5 », c’est-à-dire les nouvelles APIs disponibles. Ici, on va utiliser le tag <audio> qui fonctionne plutôt bien dans IE10 pour jouer le rôle de lecteur multimedia:

HTML5 <audio> Tag

Placement dans l’interface : Utilisation de l’App Bar Windows 8

Avant de commencer à coder, on va se poser une question importante. Où mettre notre lecteur audio.

On pourrait le mettre dans une page mais cela ne serait pas très pratique, car il disparaîtrait lors de la navigation et il faudrait toujours revenir sur la page pour mettre en pause par exemple.

On pourrait aussi le mettre dans la page « default.html », la page « statique » celle qui charge les autres pages. Mais on ne veut pas que le lecteur soit présent en permanence, ce n’est pas très esthétique, cela va réduire la place de toutes nos vues. Il faudrait que l’on puisse afficher / masquer facilement le lecteur audio, ce serait top.

En cherchant un peu, il existe un comportement qui est encouragé dans  la création d’application Windows 8. Il y a en fait un composant nommé App Bar (WinJS.UI.AppBar), prévu dans toutes les applications WinJS, qui peut apparaître en haut ou en bas de l’application.

WinJS.UI.AppBar object

A la souris, l’affichage se fait par un click droit et sur tablette tactile Windows 8 (ou Win+z), l’affichage de l’AppBar se fait par un « swipe » depuis le bord de l’écran vers l’intérieur. Il est possible de mettre des boutons de type AppBarCommand, un composant WinJS bien pratique pour faire de jolis boutons sans trop forcer:

WinJS.UI.AppBarCommand object

Voici à quoi ressemble une AppBar avec quelques AppBarCommand:

Créer une AppBar simple

Une AppBar est déjà ajoutée dans un projet basé sur le template « Navigation ». Rendez-vous dans default.html et décommentez le bloc correspondant:

    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}"></button>
    </div>

Lancez votre application et faites un click droit dans le vide. L’AppBar apparaît:

Dans cette AppBar, vous pouvez placer des button de type AppBarCommand et des séparateurs <hr>.

Mettez donc un composant « <audio> » pour voir:

    <div id="appbar" data-win-control="WinJS.UI.AppBar">
        <audio id="mediaPlayer"></audio>
        <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id:'cmd', label:'Command', icon:'placeholder'}"></button>
    </div>

Lancez votre application et boum, crash de l’application sans aucune précision:

C’est la joie du développement Windows 8-style UI sous Visual Studio, certains crash sont tout sauf explicites. Ici, aucun message d’erreur, aucun warning et impossible de revenir à la méthode qui a fait planter l’application.

J’ai bien passé une heure à chercher d’où venait ce bug étrange, je vais donc vous passer ce moment frustrant ;)

Créer une AppBar « complexe » (=qui ne contient pas que des AppBarCommand)

Essayez avec d’autres composant que des WinJS.UI.AppBarCommand et vous aurez systématiquement un crash. D’ailleurs, même un <hr> simple doit être déclaré comme un composant AppBarCommand.  Si vous regardez la documentation:

http://msdn.microsoft.com/en-us/library/windows/apps/Hh465309(v=win.10).aspx

Vous trouverez en bas de page:

Note  You can use only AppBarCommands in an app bar.

Ce n’est pas 100% vrai. Vous pouvez en fait ajouter d’autres composants si vous modifiez l’option « layout » de l’AppBar:

AppBar.layout property

Cette fois la remarque a plus de sens:

You can put only AppBarCommands inside an app bar with the « commands » layout.

Mais il est dit plus haut dans la doc que l’on peu mettre « custom » comme layout pour faire ce que l’on souhaite. Au passage, on va aussi fixer le paramètre « sticky », qui fait que l’AppBar ne disparaît pas quand on clique sur l’interface, simplement quand on fait un click droit ou un swipe.

Lire la suite

Windows Metro – Clouder! (9) – Chargement d’un fichier JSON au lieu d’un appel XHR pour améliorer sa productivité

Dans le tutorial précédent, on a réalisé la page d’une « track » dans notre application Windows 8 Metro:

Windows Metro – Clouder! (8) – Chargement des commentaires depuis l’API SoundCloud

Si vous avez fait vous même le tutorial, vous avez pu remarquer que cela a été assez laborieux car à chaque test, vous deviez attendre le retour de l’API SoundCloud, qui met quelques secondes parfois. Pour éviter de perdre en productivité, on va introduire un mode « local » qui va charger un fichier JSON depuis le disque et pas depuis internet.

Je crois que cela peut s’apparenter à un « mock » ou un « stub » lorsque l’on fait du test unitaire (ce que je ne fais pas ici). A noter que je pourrais très bien mettre le contenu JSON en dur dans mon code mais cela vous apprend au passage comment charger un fichier avec les API WinJS pour Windows 8.

Création du fichier JSON

Très simple, je console.log le résultat d’un appel XHR et je met cela à l’intérieur d’un fichier « hotness.json » de mon projet (à la racine)

Télécharger le fichier hotness.json

La méthode simple

Comme souvent, j’ai trouvé la méthode complexe avant la méthode simple. La méthode simple est en fait de modifier l’URL pour non pas pointer vers SoundCloud mais vers le répertoire applicatif. Pour cela, il suffit en fait d’utiliser le préfixe « ms-apps:/// »:

if (SoundCloudService.LOCAL) {
        url = "ms-appx:///hotness.json";
    }
    // error callback passed as second parameter
    // http://msdn.microsoft.com/en-us/library/windows/apps/hh868282.aspx
    WinJS.xhr({ url: url })

Voici la documentation:

Package.InstalledLocation | installedLocation property

Voilà, pour le fun, j’ai quand même mis la méthode plus complexe qui n’apporte aucun avantage par rapport à ce raccourci :)

Chargement d’un fichier JSON avec Windows.Storage.FileIO (méthode complexe)

Pour charger un fichier, on va utiliser la méthode getFileAsync de l’API File fournie par WinJS. On commence par récupérer une référence vers le répertoire applicatif par Windows.ApplicationModel.Package.current.installedLocation. Cela nous renvoie en fait une référence de type StorageFolder:

http://msdn.microsoft.com/en-us/library/windows/apps/br227230.aspx

Sur cet objet StorageFolder, on utilise la méthode getFileAsync qui prend en paramètre le nom du fichier à charger:

http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.storagefolder.getfileasync.aspx

La méthode getFileAsync renvoie une promise (une IAsyncOperation) que l’on peut chaîner avec un done(…) pour placer nos callback success / error:

                var localFileName = "hotness.json";
                var localFolder = Windows.ApplicationModel.Package.current.installedLocation;
                var that = this;
                localFolder.getFileAsync(localFileName).done(function (file) {
                }, function (e) {
                    console.log("erreur" + e);
                });

Ensuite, pour charger notre fichier, on utilise la méthode Windows.Storage.FileIO.readTextAsync(file) qui renvoie aussi une IAsyncOperation que l’on peut chaîner avec un then vers notre callback:

                var localFileName = "hottest.json";
                var localFolder = Windows.ApplicationModel.Package.current.installedLocation;
                var that = this;
                localFolder.getFileAsync(localFileName).done(function (file) {
                    return Windows.Storage.FileIO.readTextAsync(file).then(that.onHottestTracks.bind(that));
                }, function (e) {
                    console.log("erreur" + e);
                });

Voilà, c’était la méthode complexe (car on doit bidouiller avec les scopes et les callback, c’est un peu la soupe ;) )

Résultat:

Conclusion

Un tutorial très court mais important. En cumulé, on va gagner de précieuses minutes pour la suite et surtout éviter d’être distrait en attendant le chargement de l’application, ce qui fait perdre encore plus de temps (d’expérience :) )

Sources du projet

Télécharger les sources du projet

Windows Metro – Clouder! (8) – Chargement des commentaires depuis l’API SoundCloud

Jusque là, on est arrivé à faire pas mal de choses avec un simple appel XHR vers SoundCloud. Pour permettre à notre utilisateur de découvrir d’autres personnes qui ont aussi apprécié la chanson, on va charger les commentaires associés puis on fera un lieu au click sur un commentaire vers la page de l’utilisateur en question plus tard.

Ajout de la ListView dans le DOM

Maintenant, vous savez faire ;):

	<div id="secondColumn">
		<div class="comments" data-win-control="WinJS.UI.ListView" data-win-options="{layout: WinJS.UI.ListLayout,  selectionMode: 'none'}"></div>
	</div>

Chargement des commentaires

On va ajouter une fonction dans notre SoundCloudService pour récupérer les commentaires qui sont accessibles à l’URL:

https://api.soundcloud.com/tracks/ » + trackId + « /comments.json?client_id=…

Voici l’appel complet:

SoundCloudService.prototype.getComments = function (trackId, success, error) {
    var url = SoundCloudService.API_BASE_URL + "tracks/" + trackId + "/comments.json?" + SoundCloudService.CLIENT_ID;
    WinJS.xhr({ url: url }).then(function (request) {
        var data = JSON.parse(request.responseText);
        var items = [];
        for (var i = 0, l = data.length; i < l; i++) {
            items.push(SoundCloudService.formatComment(data[i]));
        }
        success(items);
    }, error);
};

Puis on ajoute encore un post-traitement pour que la donnée soit plus simple à afficher plus tard:

SoundCloudService.formatComment = function (item) {
    item.avatar_url = item.user.avatar_url;
    item.username = item.user.username;
    item.timing = Utils.getTiming(item.timestamp);
    item.body = window.toStaticHTML(Utils.linkify(item.body));
return item;
};

Ici, j’utilise deux nouvelles fonctions « utilitaires ». La première « getTiming » permet à partir du timestamp de récupérer une chaîne du type « at 04:40 ». La deuxième permet de mettre des balises <a> autour des liens HTTP présents dans le contenu du commentaire. Cela permettra de mettre en évidence directement les liens dans les commentaires (les liens HTML ont un style par défaut, soulignés et d’une autre couleur).

A noter la méthode window.toStaticHTML qui vient de WinJS et qui permet d’éviter les injections de code HTML (tags script dans le commentaire par exemple).

Aucun mérite pour ces fonctions, je les ai trouvé sur le net, merci StackOverflow :)

Voici le code de ces 2 fonctions utilitaires à placer dans utils.js:

Utils.getTiming = function(timestamp) {
    if (!timestamp || timestamp == 0) {
        return "";
    }
    var x = timestamp / 1000;
    var seconds = x % 60;
    x /= 60;
    var minutes = x % 60;
    seconds = Math.floor(seconds);
    minutes = Math.floor(minutes);
    var secondsAsString = seconds > 9 ? seconds : "0" + seconds;
    var minutesAsString = minutes > 9 ? minutes : "0" + minutes;
    return "at " + minutesAsString + ":" + secondsAsString;
};

/* http://stackoverflow.com/questions/37684/how-to-replace-plain-urls-with-links */
Utils.linkify = function(inputText) {
    var replacedText, replacePattern1, replacePattern2, replacePattern3;

    //URLs starting with http://, https://, or ftp://
    replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
    replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>');

    //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
    replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
    replacedText = replacedText.replace(replacePattern2, '$1<a href="http://$2" target="_blank">$2</a>');

    //Change email addresses to mailto:: links.
    replacePattern3 = /(\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim;
    replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>');

    replacedText = replacedText.replace(/\r\n/gim, "<br />");
    return replacedText;
};

Appel et affichage dans la liste

Encore une fois, très facile, on va créer une WinJS.Binding.List() puis assigner la propriété itemDataSource de notre liste:

        onComments: function (data) {
            var list = new WinJS.Binding.List(data);
            var listView = document.querySelector(".comments");
            WinJS.UI.setOptions(listView.winControl, {
                itemDataSource: list.dataSource
            });
        },

        ready: function (element, options) {
            this.track = options.selectedTrack;
            document.getElementById("trackPageArtist").textContent = this.track.user.username;
            document.getElementById("releaseDate").textContent = Utils.parseTwitterDate(this.track.created_at);
            document.getElementById("avatarUrl").src = this.track.user.avatar_url;
            document.getElementById("artworkImgBig").src = this.track.artwork_url ? this.track.artwork_url.replace("large", "crop") : null;
            document.getElementById("commentCount").innerHTML = this.track.comment_count;
            document.getElementById("playCount").innerHTML = this.track.playback_count;
            document.getElementById("favoriteCount").innerHTML = this.track.favoritings_count;
            window.soundCloudService.getComments(this.track.id, this.onComments);
        },

Et on obtient comme d’habitude notre liste sans template:

Lire la suite