Browsing Category

Dev

Ma Première Application Javascript

Présentation

Dans le cadre de ma formation à Ironhack, je dois réaliser un projet final en javascript qui reprend tous les concepts traités lors des précédentes semaines. Mongo, Express, Vue & Node : tout y passe. À ces fins, je souhaite retranscrire jour après jour la manière dont je vais développer cette application.

mevn-javascript

Ce journal de développement a également pour but de montrer à tous mes lecteurs no-tech qu’il est possible de rapidement se mettre à niveau et de pouvoir développer ses projets. Bien évidemment, il reste pas mal de chemin à parcourir avant de pouvoir maintenir une application grand public, mais réaliser un MVP est dorénavant tout à fait jouable.

Ainsi, lors des deux prochaines semaines, je tâcherais d’éditer quotidiennement cet article afin de vous donner tous les détails de ma démarche.

Pour ce projet, j’ai souhaité réaliser une application de dating.

Problématique

Tinder a frappé un grand coup en imposant un standard sur les applications de rencontre : le swipe. Cependant, des applications ont réussi à se frayer un chemin sans utiliser cette fonctionnalité. Je pense notamment ici à Happn qui s’adosse sur la géolocalisation de ses utilisateurs.

Toutefois un outsider, Bumble, réussit à sortir son épingle du jeu. La feature #1 est la suivante : les filles amorcent les discussions sous 24h après le match sinon la mise en relation est supprimée.

Il est intéressant également de citer Abricot qui réinvente l’agence matrimoniale et qui rencontre un succès conséquent auprès des femmes.

Suite à quelques interviews, j’ai pu détecter, auprès des filles, que les applications (comme Tinder qui n’émettent aucun type de restrictions) ont de moins en moins le vent en poupe. On a tous ce pote qui est capable de liker 300 matchs / seconde :)

En effet, il existe un déséquilibre majeur entre les aspirations des femmes et des hommes sur ce type d’application. Le comportement des hommes est parfois perçu comme déplacé par les femmes.

Données

Aujourd’hui, près d’une parisienne sur deux est célibataire. – source : étude ifop – lci

Les applications de rencontres sont démocratisées chez les jeunes (28% des 18-25 ans) et chez les CSP+ (16,3%) – source : BVA

Les utilisateurs n’hésitent pas à avoir plusieurs applications – source interviews

Une personne sur deux avoue à son entourage sa présence sur une application.  – source : BVA

Il est plus facile de faire venir des hommes sur une application mobile que des femmes. – source : interviews & intuition :)

Réponse

Dans un souhait de rendre l’application plus authentique que le niveau moyen, l’idée est ici de contraindre le swipe grâce à un quizz.

Exemple : Je suis l’utilisateur A, je trouve les photos et la description de l’utilisateur B intéressantes, je like ce profil et l’application m’impose de répondre à un quizz, préalablement édité par B, avec des thèmes comme la musique, les films, les voyages ou encore les livres. Si mon taux de réussite est satisfaisant, B sera notifié que je souhaite rentrer en relation avec.

QCM

Véritable pierre angulaire de ce projet, ce quizz a donc vocation d’augmenter la friction sur la mise en relation. Des questions à choix multiples permettent ici de rendre cette fonctionnalité possible à la fois sur un plan d’expérience utilisateur que sur un plan technologique.

En connectant les API de Spotify, IMDB, Google Places et Books il est possible de traiter des cellules très classiques comme :

  • Musique : quel est ton artiste préféré ? quelle est ta musique du moment ?
  • Cinématographie : quel est le film qui t’a le plus marqué ? qui est ton acteur favori ?
  • Voyages : ta destination de vacances préférée ? ton prochain voyage ?
  • Littérature : quel est ton livre de chevet en ce moment ? l’auteur qui t’a le plus inspiré ?

Seuil

L’utilisateur a toutefois la possibilité de garder un contrôle sur cette friction en déclarant un % de réussite minimum/maximum.

Par exemple, un utilisateur qui souhaite recevoir des notifications uniquement en provenance d’autres utilisateurs qui ont réussi à remplir parfaitement le quizz. À l’inverse, un utilisateur pourrait décider d’être notifié sans une seule réponse juste à son quizz.

L’utilisateur gardant, bien entendu, la possibilité d’accepter ou non la mise en relation.

Si l’utilisateur valide, la messagerie sera activée pour cette connexion. Le concept de l’application permet également de briser la glace plus rapidement grâce au quizz préalablement rempli.

Exemple :

Toi aussi tu es fan de Tarantino ?

Technologies employées

Pour arriver au résultat escompté, voici les ensembles technologiques que je vais utiliser :

  • Database : MongoDB + Mongoose
  • Back : Nodejs + Express
  • Front : Vuejs + Vuerouter
  • Mobile : React + ReactNative (dans un second temps)

Prochaines étapes

Comme précédemment décrit, je vais faire tout mon possible pour vous retranscrire mon parcours du combattant. Mais à cette étape du projet, je suis intéressé d’avoir des beta testeurs qui ont des idées/suggestions à proposer.

Jour #1 : Conception

Une journée sans ouvrir mon éditeur de texte c’est l’objectif que je me suis fixé. J’ai déjà réalisé des mini projets qui avaient été attaqués trop tôt sous un aspect technique. Au détriment de la conception et de l’expérience utilisateur. Aujourd’hui, l’idée était de récupérer un maximum de feedbacks de la part de mes camarades de classe. Cet exercice est obligatoire et vous évite à coup sûr des erreurs bien plus difficiles à rattraper par la suite. Grâce à cette première étape, et à plusieurs interviews dispensés au préalable, j’ai pu affiner quelques détails, notamment sur le parcours utilisateur.

Quand demander telle ou telle information ?

Que faire si l’utilisateur ne souhaite pas donner ses informations ? Son quizz ?

Pour le reste de la journée, je me suis seulement attelé à dessiner mes wireframes. Mais soyons honnête, je vais m’inspirer très fortement de l’application desktop de Tinder.

tinder desktop
Pas la peine de réinventer la roue :)

La seule grande différence résidera donc au like qui enverra vers un formulaire édité par l’utilisateur « swipé ».

Schéma de données

Ce fût de loin la tâche la plus complexe de ma journée. En effet, la schématisation de vos données peut vous faire gagner beaucoup de temps, ou vous en faire perdre beaucoup par la suite.

Voici donc les 4 principaux modèles qui devraient me permettre de faire tourner mon application :

  • User : où seront recensées les id, la bio, les photos etc…
  • Quizz : ce modèle étant relié à l’utilisateur
  • Match : qui représentera la réussite d’un questionnaire, avec ses réponses et les 2 user id concernés
  • Conversation : le modèle qui acceptera tous les messages échangés

Ce dernier étant généré lorsque le modèle Match passera pending à granted (deuxième utilisateur validant la mise en relation).

N’hésitez pas à cette étape du projet de sortir votre stylo et votre feuille de papier.   

Gagner du temps

Pour faciliter la tâche lors de l’élaboration du questionnaire, je souhaite connecter plusieurs API afin de :

  • Proposer un choix exhaustif
  • Sanitizer mes données
  • Construire plus rapidement des QCM car les questions ouvertes sont, dans un premier temps, difficiles à gérer

J’ai donc pris la peine de créer une Google Spreadsheet avec tous mes logins, mot de passes, clés API et documentations associées.

Repo

L’appel du clavier. J’ai pas pu résister à écrire quelques lignes. Ou plutôt exécuter quelques lignes de commande pour générer mon application. Pour ce faire, voici les lignes de commande à taper depuis un dossier commun.

$ express server
$ cd server
$ npm install
$ npm start
$ cd ..
$ vue create client
$ cd client
$ npm install
$ npm run serve

Pour express pas besoin de views. Vous pouvez nettoyer votre dossier. Au niveau de vuejs, il suffit de rajouter vue router et la vie est belle.

express-setup vuejs-setupJe vérifie que sur les ports 3000 et 8080 mes deux environnements fonctionnent.

Même si c’est pas tout à fait ça, voici à quoi devrait ressembler approximativement votre application après ce setup.

Plus ou moins…

Jour #2 : Modèles & Signup

On commence à rentrer dans le vif du sujet. L’objectif du jour était de :

  • Créer mes modèles grâce à Mongoose
  • Générer des seeds (données fausses basées sur le schéma)
  • Monter un sign-up via Facebook

L’objectif est atteint, non sans peine, mais tout est prêt pour démarrer l’application côté base de données.

Modèles & Seeds

Je vais pas détailler ici mes 4 modèles mais plutôt un seul, le modèle User.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const userSchema = new Schema({
  facebookId: {
    type: String,
    required: true
  },
  status: {
    type: String,
    required: true,
    default: 'pending',
    enum: ['pending', 'granted', 'killed']
  },
  gender: {
    type: String,
    required: true,
    enum: ['1', '2']
  },
  lookingFor: {
    type: String,
    enum: ['1', '2']
  },
  firstName: {
    type: String,
    required: true,
  },
  age: {
    type: Number,
    required: true,
    min: 18,
    max: 99,
  },
  bio: {
    type: String,
    maxlength: 280
  },
  work: {
    type: String,
    maxlength: 70,
  },
  mail: {
    type: String,
  },
  phone: {
    type: String,
    minlength: 10,
    maxlength: 12
  },
  photos: {
    type: String,
  },
  createdAt: {
    type: String,
    default: new Date(),
  },
  updatedAt: {
    type: String,
    default: new Date(),
  }
});

const User = mongoose.model('User', userSchema);
module.exports = User;

Ce modèle est classique mais reprend tous les champs nécessaires pour la suite (enfin, je crois).

Je pense qu’il est cependant important de garder des traces des créations, modifications (cf createdAt & updatedAt).

Le gender et le looking for me permettant de gérer par la suite les différentes orientations sexuelles.

Pour vérifier que tout fonctionne, l’idée est de créer un seed pour bien vérifier que mon application envoie les données souhaitées dans ma base de données (MongoDB). Pour ce faire, voici un script d’exemple :

const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI);
const User = require('../../models/user.js');

let userDatas = [
  {
    facebookId: '123456789',
    status: 'pending',
    gender: '1',
    lookingFor: '2',
    firstName: 'Vivian',
    age: 29,
    bio: 'Lorem ipsum dolor sit amet',
    work: 'Student @Ironhack',
    mail: 'yolo@yolo.com',
    phone: '0667721671',
    photos: 'https://lh3.googleusercontent.com/-QRfzdXqbRk0/AAAAAAAAAAI/AAAAAAAARKU/0mVuCX8pVhM/s60-p-rw-no/photo.jpg',
  }
]

User.create(userDatas, (err, user) => {
  if (err) {
    throw err;
  }
  else {
    user.forEach(user => {
      console.log(user);
    });
  }
  mongoose.connection.close();
});

TA-DA ! Ça fonctionne !

Le plus dur c’est de faire le premier, après le reste ça roule. C’est une partie qui peut paraître anodine mais pourtant essentielle.

Signup

Bon là par contre, c’est un peu l’enfer. Pour avoir discuté avec plusieurs développeurs, plus ou moins chevronnés, le sign-up / login est toujours une plaie à coder. Même si il est vrai que des initiatives comme Passport ou JWT vous change la vie, ça reste une étape fastidieuse.

Pour mon application, j’ai pris le parti de ne permettre uniquement le login via Facebook. C’était un choix que nous avions fait à l’époque pour le projet MyBetFriend et nous n’avions pas eu à l’époque de retours négatifs. De plus, la quasi totalité des applications de rencontre ont déjà évangélisé cette manière de s’inscrire.

Pour récupérer :

  • Mail
  • Prénom
  • Genre
  • Photos
  • etc…

Facebook fonctionne à la perfection :)

Le conseil que j’aurais à donner pour cette partie, c’est de prendre le temps de créer (avec d’autres développeurs éventuellement) un boilerplate qui sera réutilisé à l’avenir.

Jour #3 : Préparation de l’API

Journée de transition. En ce troisième jour, je ne fus pas à 100% sur le développement de mon application. Cependant, à cette étape du projet, un travail considérable a pointé le bout de son nez : la réalisation de mon API REST.

Cette API a pour vocation de faire communiquer ma partie client (vuejs) avec mon serveur (express).

J’ai pu d’ores et déjà constaté une erreur sur mon signup. En effet, j’ai réalisé ce dernier côté serveur alors qu’il aurait été plus productif de le réaliser côté client d’entrée. Mais bon c’est en faisant qu’on apprend de ses erreurs non ?

Signup & données

Je me suis donc attaché aujourd’hui à transférer mon signup sur ma partie client et a commencé la préparation de mon API. J’ai pu également améliorer la phase d’inscription en faisant matcher les données Facebook avec mon modèle de données. Grâce à cela, je peux dorénavant faire correspondre le prénom de l’utilisateur sur Facebook avec mon modèle de données.

Je pensais (naïvement) qu’il allait être aisé de rapatrier les photos d’un profil sur mon modèle mais ce thread ouvert sur Stackoverflow m’a quelque peu refroidi.

Il faut dorénavant fournir un screencast de son application à Facebook pour avoir la possibilité de récupérer les photos de profil.

Je vais donc devoir attendre d’avoir une application un peu plus précise pour pouvoir soumettre ma review.

Timestamps

Mais j’ai découvert une astuce relativement intéressante sur Mongoose qui me va me permettre de récupérer mes timestamps lors des créations et des modifications de mes données utilisateurs.

Le tout de manière très simple en rajoutant en second argument de mon modèle :

{
  timestamps: true
}

Ce qui donne en sortie :

J’ai également mis à jour mon modèle en remplaçant les 1 et 2 par male et female.

API

Pour en revenir sur l’API, l’idée est d’écrire côté client un fichier api.js qui va interroger mon serveur. Pour protéger ces routes, je vais utiliser mon token généré au signup.

J’utiliserai donc ici le module axios pour me faciliter la vie.

import axios from 'axios'

const mtmt = axios.create({
  baseURL: process.env.API_URL || 'http://localhost:3000/',
});

Demain c’est donc un gros chantier qui m’attend, la réalisation des premières méthodes de mon API pour récupérer toutes les datas nécessaires côté client.

Jour #4 : API & Vuejs – Tome I

Si Google est ton ami, Postman est ton meilleur ami. Surtout quand tu commences à écrire ta première API REST. Effectivement, gérer une application de A à Z commence à devenir complexe. Gérer 4 environnements (front, api, serveur et db) et les faire communiquer entre eux n’est pas plus chose aisée. Surtout lorsque vous commencez à rencontrer des problèmes de type CORS.

CORS

Cross-Origin Resource Sharing… Vous ne connaissiez pas cette acronyme ? Vous en avez de la chance. Le cross origin c’est ce qui va permettre de faire discuter votre front (qui tourne chez moi sur un localhost:8080) avec votre serveur (localhost:3000). Dans un soucis de sécurité, votre navigateur n’accepte pas aisément ce va et vient incessant entre ces deux origines. Heureusement qu’il existe une extension Chrome pour gérer ce genre de soucis.

Ne pas oublier que sur des sites comme Facebook ou encore Github le comportement est parfois hasardeux si l’extension est activée.

Node –inspect

Aka la commande qui te sauve la vie ! Effectuer des console.log(‘tutu’) côté client ça va, mais côté serveur on fait comment pour se déboguer ? Heureusement que cette commande existe et qu’elle vous permet d’ouvrir une interface ad hoc pour pouvoir insérer des points d’arrêts dans votre code côté serveur.

Signup

Encore et toujours la même histoire…

Bon, ils sont bien gentils Facebook, mais réaliser un sign-up correct avec eux c’est quand même pas si évident. Je pense que la prochaine fois ça va partir sur du Firebase direct !

En clair, voici le nouveau chemin pour faire fonctionner tout ce beau monde :

  1. Front : clique sur le sign-up
  2. Facebook : retourne les informations dans mon serveur (id, firstName…)
  3. Serveur : envoie le token et les informations de Facebook via un redirect
  4. Front : utilise les req.params pour faire tourner l’application
  5. Il y’a pas de 5, ça suffit.

Vuejs, les prémices

Sur les conseils de mes professeurs émérites, il est de bon ton de développer le front et l’api en même temps, afin de cadrer au mieux avec les desiderata des futurs utilisateurs. C’est donc ainsi que j’ai commencé à faire avancer mon front.

Je saluerai donc jamais assez les personnes qui vont vivre les repos comme Buefy ou Element. Ces modules permettent de réaliser des composants à la vitesse de l’éclair et ça c’est cool.

 

Par exemple, pour proposer un range d’âge sur mon application, voici le composant récupérer sur Element :

Maintenant que mon sign up est en place, il me reste plus qu’à attaquer ma fonctionnalité quizz qui ne sera pas une mince affaire non plus.

Jour #5 : API & Vuejs – Tome II

‘Va y’en avoir combien des tomes ?!?

Il me reste 9 jours de code et j’ai l’impression que j’avance pas. Je veux pas sortir les violons, mais quand même, ce n’est pas chose aisée. Surtout quand votre IDE (Visual Code) décide de péter un plomb. 2 heures de perdues, merci.

Spotify

Le gros morceau de la journée fût de connecter l’API de Spotify à mon modèle de Quizz. Lors du sign-up je demande à l’utilisateur quel est son groupe préféré.

C’était assez chronophage car je devais récupérer le code, puis le token mais bon c’est très instructif. Autre avantage, je vais pouvoir dupliquer sur mes autres API rapidement ce week-end.

En parallèle de ça, j’apprends aussi React (qui est probablement un peu plus complexe pour un débutant). Je veux pas rentrer dans une guéguerre stérile sur les différents frameworks javascript, mais il faut reconnaître que Vuejs à quelques avantages vis à vis de React.

Syntactic sugar

Vuejs est assez facile à lire et à écrire. Par exemple, pour ma génération d’artistes, voici ce que j’ai pu faire :

<figure class="image is-4by3">
  <img v-if="artist.images[0]" v-bind:src=artist.images[0].url alt="Placeholder image">
  <img v-else src="https://placehold.it/600x600" alt="Placeholder image">
</figure>

Spotify n’a pas d’image pour tout ses artistes. Et si une image manque, c’est toute la requête qui tombe à l’eau. Grâce à Vuejs, en 2 lignes de code, vous pouvez vous prémunir de ce genre de comportement. Et ça c’est cool.

T’as la photo c’est bien, tu l’as pas, placehold.it fera le reste !

Jour #6 : API & Vuejs – Tome III

Autre avantage, je vais pouvoir dupliquer sur mes autres API rapidement ce week-end.

Rien.

N’est.

Rapide.

Je devrais le  savoir quand même ! Il est vrai que j’ai pu transposer ma recherche de musique sur les films relativement rapidement. L’API de TMDB est super bien faite et il existe de nombreux wrappers sur GitHub.

Mais en partageant mon projet à une amie, elle m’a fait une remarque pertinente.

Non mais c’est bien gentil les musiques, les films, mais le trait de caractère c’est quand ?

Spafo. Du coup, j’ai mis de côté les intégrations API pour partir sur un autocomplete très simple : qualité & défaut.

Autocomplete

Quelle joie ce Buefy. Vous pouvez facilement trouver votre bonheur dans la partie Forms Controls.

Source : https://buefy.github.io/#/documentation/autocomplete

<template>
    <section>
        <p class="content"><b>Selected:</b> {{ selected }}</p>
        <b-field label="Find a JS framework">
            <b-autocomplete
                rounded
                v-model="name"
                :data="filteredDataArray"
                placeholder="e.g. jQuery"
                icon="magnify"
                @select="option => selected = option">
                <template slot="empty">No results found</template>
            </b-autocomplete>
        </b-field>
    </section>
</template>

<script>
    export default {
        data() {
            return {
                data: [
                    'Angular',
                    'Angular 2',
                    'Aurelia',
                    'Backbone',
                    'Ember',
                    'jQuery',
                    'Meteor',
                    'Node.js',
                    'Polymer',
                    'React',
                    'RxJS',
                    'Vue.js'
                ],
                name: '',
                selected: null
            }
        },
        computed: {
            filteredDataArray() {
                return this.data.filter((option) => {
                    return option
                        .toString()
                        .toLowerCase()
                        .indexOf(this.name.toLowerCase()) >= 0
                })
            }
        }
    }
</script>

Comme vous le remarquerez, même la partie .js est gérée. Terminé bonsoir.

Demain petite pause, je vais intégrer la landing page et lâcher un peu le duo infernal Express / Mongoose.

Jour #7 : Landing Page

Petit dimanche détente, intégration de ma landing page. OKLM.

Mon premier jet est disponible ici.

Étant donné qu’il faudra fournir une landing page de notre projet, autant le faire maintenant. Je pense que je serai tellement emmerdé par ma mise en production plus tard…

Mais ce n’est pas une excuse pour faire une landing page moisie. J’ai souhaité incorporer de la parallaxe et quelques animations CSS. Pour ce faire j’ai utilisé deux ressources très intéressantes : paroller & animista.

Paroller

Source : https://github.com/tgomilar/paroller.js/

Paroller est un plugin basé sur jQuery qui permet des animations parallaxes très facilement. Alors oui je sais, utiliser jQuery c’est le mal gnia gnia… Mais bon, ça marche, c’est facile, pas la peine de perdre des heures à reproduire la même chose avec du Tween non ?

Ce que j’ai le plus apprécié dans paroller c’est sa facilité de mise en place. Il suffit de déclarer une classe sur la partie qu’on souhaite animer et la suite se gère très bien :

$(".p1, [data-paroller-factor]").paroller({
  factor: 1,              // multiplier for scrolling speed and offset
  type: 'foreground',     // background, foreground
  direction: 'horizontal' // vertical, horizontal
});

Il suffit de gérer ces 3 paramètres et le tour est joué.

Animista

Source : http://animista.net/

Animista c’est le Google des animations css. Il suffit de piocher son animation, copier coller sa classe, ses keyframes et ça marche.

Jour #8 : De la data

Journée relativement productive. Mise à jour du Trello, finalisation du quizz. J’attaque maintenant le gros morceau : l’interface de mise en relation (swipe + quizz + chat). Mais avant cela, deux travaux relativement importants : enregistrer l’utilisateur localement (local storage) et création de faux profils.

Ce n’est pas pour faire semblant d’avoir une croissance exponentielle, non c’est plutôt pour tester tous les cas de figures. Les combinaisons possibles de recherches sont très élevées (âge, âge recherché, sexe et orientation sexuelle).

Enregistrer l’utilisateur localement

J’avais mis ça de côté en pensant que c’était pas trop compliqué à faire par la suite. C’est le cas, mais autant le faire de suite la prochaine fois :)

data: function() {
    return {
      error: null,
      files: [],
      photos: '',
      id: this.$route.query.id,
      firstName: this.$route.query.firstName
    }
  }

Au sign-up, je récupère l’id depuis l’url de callback de Facebook.

Une fois présenté, je suis en mesure de l’envoyer, avec axios, dans mon local storage (developer tools > application > local storage).

NB : j’envoie aussi mon token mais pas au même endroit :)

.then( res => {
        const { data } = res;
        localStorage.setItem('id', obj._id);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
        return data;
      })

C’est pas les lignes de code de l’année mais ça fait le taf.

Par la suite, je récupère facilement l’id comme suit :

        .sendTreshold({
          userId: localStorage.getItem('id'),                
          treshold: this.treshold
        })

Faker.js

La trouvaille. Je pensais devoir scrapper des sites de rencontre pour pouvoir avoir une première base de test. Mais bon, comme vous devez le savoir, le scrapping, c’est pas bien.

Heureusement je suis tombé sur Faker qui est une librairie permettant de générer des quantités phénoménales de données. L’un des énormes avantages, c’est que Faker est capable de rester fidèle à la localité. Ce qui permet, dans mon cas, de mettre uniquement des personnes françaises.

const mongoose = require('mongoose');
mongoose.connect(process.env.MONGODB_URI || "mongodb://localhost/mtmt");
const User = require('../../models/user.js');

var faker = require('faker');
faker.locale = "fr";

var file = [];
for (let i = 0; i < 2000; i++) {
  
  let fakeDatas = {
    facebookId: faker.random.number(),
    status: 'granted',
    gender: faker.random.arrayElement(['male', 'female']),
    lookingForGender: faker.random.arrayElement(['male', 'female']),
    lookingForRange: [{'min' : faker.random.number({min:18, max:24}), 'max' : faker.random.number({min:32, max:49})}],
    firstName: faker.name.firstName(),
    age: faker.random.number({min:18, max:49}),
    bio: 'Je suis un faux compte et je le vis bien.',
    work: faker.company.companyName(),
    mail: faker.internet.exampleEmail(),
    phone: faker.phone.phoneNumber(),
    photos: faker.image.avatar(),
  }
  file.push(fakeDatas)
}

User.create(file, (err, user) => {
  if (err) {
    throw err;
  } else {
    user.forEach(user => {
      console.log(user);
    });
  }
  mongoose.connection.close();
});

Extrait :

{ facebookId: '37981',
  status: 'granted',
  gender: 'male',
  lookingForGender: 'female',
  lookingForRange: [ { min: 22, max: 46, _id: 5a9e51b9c932da671f9b1c2b } ],
  firstName: 'Lena',
  age: 48,
  bio: 'Je suis un faux compte et je le vis bien.',
  mail: 'Pauline90@example.net',
  phone: '0689125768',
  photos: 
   [ 'https://s3.amazonaws.com/uifaces/faces/twitter/nfedoroff/128.jpg' ],
  swiped: [],
  _id: 5a9e51b9c932da671f9b1c2c,
  createdAt: 2018-03-06T08:30:49.843Z,
  updatedAt: 2018-03-06T08:30:49.843Z,
  __v: 0 }
{ facebookId: '74494',
  status: 'granted',
  gender: 'male',
  lookingForGender: 'female',
  lookingForRange: [ { min: 19, max: 45, _id: 5a9e51b9c932da671f9b1c2d } ],
  firstName: 'Célia',
  age: 41,
  bio: 'Je suis un faux compte et je le vis bien.',
  mail: 'Lo_Leroux@example.com',
  phone: '+33 614805908',
  photos: 
   [ 'https://s3.amazonaws.com/uifaces/faces/twitter/kianoshp/128.jpg' ],
  swiped: [],
  _id: 5a9e51b9c932da671f9b1c2e,
  createdAt: 2018-03-06T08:30:49.843Z,
  updatedAt: 2018-03-06T08:30:49.843Z,
  __v: 0 }

La vie est belle.

Jour #9 : Vuejs et ses composants

J’ai pu goûter aujourd’hui à la puissance de  Vuejs. Autre force de ce framework c’est la structuration de ses composants et de ses vues.

Pour mon interface de swiper j’ai donc un composant Card et un autre Quizz. Ces deux composants se retrouvant dans une vue Swiper.

  1. Swiper.vue – view
  2. Card.vue – component
  3. Quizz.vue – component

De base, le quizz est désactivé. Je l’active sur le click Like sur la Card.vue. Mais je le referme depuis le composant Quizz.vue grâce à un event : $this.emit(‘hide’)

Ce dernier est récupéré dans Card.vue avec @hide=hideQuizz

Plutôt facile non ?

Dernière petite astuce, utiliser beforeMount() pour charger l’API avant le composant dans le but d’afficher de la data dès que le composant prend vie.

Jour #10 : Like & Dislike + Heroku

Je dois présenter mon projet vendredi. Ça serait pas mal de faire les fonctionnalités annoncées non ? Avec un petit déploiement également ?

Aujourd’hui je me suis donc attaché à coder mes features clés qui Like & Dislike l’utilisateur avec une première mise en production pour pas avoir trop mal vendredi matin.

De toutes façons on fait jamais une MEP un vendredi… Source

Like & Dislike : MongoDB & Vuejs

Pour que l’application cadre avec le souhait de l’utilisateur, il est impérieux de proposer des profils qui cadrent avec le sexe, l’orientation sexuelle et l’âge.

Pour ce faire…

Ma route

router.get('/api/getmatches/:id', function (req, res, next) {
  User.findById(req.params.id, function (err, user) {
    if (err) {
      next (err);
    } else {
      User
      .find({
        gender: user.lookingForGender,
        age: { $gte : user.lookingForRange.min, $lte: user.lookingForRange.max } 
      })
      .limit(100)
      .exec((err, users) => {
        if (err) {
          next(err);
        } else {
          res.json(users)
        }
      })
    }
  })
})

Qui sera améliorée demain bien entendu…

Comme vous le voyez, rien de très compliqué. En fonction de l’id de l’utilisateur, je récupère les informations nécessaires pour lui proposer les matches qui correspondent à ses envies.

Par la suite, je requête la base en entier sur ses critères. Je limite à 100 dans un premier temps, pas besoin de plus pour tester.

Mes méthodes

methods: {
    getMatches() {
      api
        .getMatches(localStorage.getItem("id"))
        .then((users) => {
          this.users = users
        })
        .catch(err => {
          this.error = err;
        })
    },
    increment(){
      this.counter++;
    }
  },
  beforeMount() {
    this.getMatches()
  },

Sur Vuejs il faut conceptualiser plusieurs choses.

L’appel de l’API se fait bien sur la base de l’id de l’utilisateur. C’est donc tout naturel de le récupérer depuis le local storage. 

Ce que j’utilise en revanche c’est une incrémentation pour faire passer les profils les uns après les autres. Il serait peut être intéressant que je filtre l’objet en sortie sur la date d’inscription, pour faire passer en avant les anciens profils. À voir.

Ne pas oublier le beforeMount() et on est bons.

Heroku

Mouairf… Pas très glorieux tout ça. Le déploiement est à mon sens une tâche réellement ingrate. Très difficile à déboguer, vous pouvez passer un temps considérable pour un résultat faible. Bon, il est vrai que vous pourrez toujours crâner auprès de vos petits copains en fin de journée.

Trève de plaisanteries, Heroku est quand même bien pratique et très bien documenté. En plus c’est gratuit.

Jour #11 & #12 : Fin.

Pour les derniers jours impossible de prendre le temps d’écrire. Sur la journée #11 j’ai réalisé pas moins de 20 commits sur mon application. Record battu :)

Quizz

Sur cette journée j’ai tout simplement réussi à mettre en place ma fonctionnalité principale : le quizz.

La plus grande complexité fût de réaliser des faux quizz et des les lier aux faux utilisateurs.

const mongoose = require("mongoose");
mongoose.connect(process.env.MONGODB_URI || "mongodb://localhost/mtmt");
const Quizz = require("../../models/quizz.js");
const User = require("../../models/user.js");

var faker = require("faker");
var randomArtists = require("./random/randomArtists");
var randomMovies = require("./random/randomMovies");
var quality = require("./random/quality");
var defect = require("./random/defect");

var users = [];
User.find({})
    .select({
        _id: 1
    })
    .limit(100)
    .exec((err, users) => {
        if (err) {
            next(err);
        } else {
            var file = [];
            for (let i = 0; i < 20000; i++) {
                let fakeDatas = {
                    userId: faker.random.arrayElement(users),
                    treshold: faker.random.number({ min: 0, max: 100 }),
                    music: {
                        answer: faker.random.arrayElement(randomArtists),
                        badResponses: [
                            faker.random.arrayElement(randomArtists),
                            faker.random.arrayElement(randomArtists),
                            faker.random.arrayElement(randomArtists)
                        ]
                    },
                    movie: {
                        answer: faker.random.arrayElement(randomMovies),
                        badResponses: [
                            faker.random.arrayElement(randomMovies),
                            faker.random.arrayElement(randomMovies),
                            faker.random.arrayElement(randomMovies)
                        ]
                    },
                    traits: {
                        "quality.answer": faker.random.arrayElement(quality),
                        "quality.badResponses": [
                            faker.random.arrayElement(quality),
                            faker.random.arrayElement(quality),
                            faker.random.arrayElement(quality)
                        ],
                        "defect.answer": faker.random.arrayElement(defect),
                        "defect.badResponses": [
                            faker.random.arrayElement(defect),
                            faker.random.arrayElement(defect),
                            faker.random.arrayElement(defect)
                        ]
                    }
                };
                file.push(fakeDatas);
            }

            Quizz.create(file, (err, quizz) => {
                if (err) {
                    throw err;
                } else {
                    quizz.forEach(quizz => {
                        console.log(quizz);
                    });
                }
                mongoose.connection.close();
            });
        }
    });

Pour ce faire j’utilise toujours ce bon vieux Faker.js qui fait gagner un temps considérable. Une fois le fichier créé je le pousse en base via Mongoose.

Refacto

Sur la doc de React j’ai trouvé une partie très intéressante : Thinking In React. Je n’ai pas (encore) trouvé son pendant côté Vuejs.

Cette partie  vous explique comment découper une petite application web en composants. C’est très intéressant, car si vous commencez à créer une application avec une architecture bancale, la moindre fonctionnalité va devenir très compliquée à produire.

C’est en ce sens que j’ai souhaité (la veille de mon rendu) « refactoriser » mon interface de swipe.

Ce n’est pas un choix que je regrette car j’ai pu avancer beaucoup plus vite par la suite. Je pense que pour la prochaine application, je vais utiliser beaucoup plus de papier et de feuilles blanches pour conceptualiser l’application.

Toasts

Petite fonctionnalité intéressante, qui plus est pour l’utilisateur final, le toast. C’est d’une facilité enfantine à mettre en place et le résultat est plutôt sympa pour l’utilisateur.

          .then(data => {
            this.$toast.open({
              message: "Bien reçu",
              type: "is-success"
            });
            this.$router.push("/quizz-info");
          })

Vous pouvez rajouter rapidement dans votre retour de données API ce petit bout de code qui avertira l’utilisateur que tout est OK. Le tout avant de le rediriger vers la prochaine page.

It’s a match!

Pour les besoins de la démonstration live, j’ai du mettre insérer un pattern fixe sur les bonnes réponses. J’aurais bien entendu pas fait ça comme ça pour une vraie mise en production. Mais bon j’ai codé ça en quatrième vitesse et ça faisait le taf, donc bon…

router.post("/api/postquizz/", function(req, res, next) {
    Quizz.findById(req.body._id, function(err, quizz) {
        if (err) {
            next(err);
        } else {
            var score = 0;
            if (req.body.musicAnswer === "A") {
                score = 25;
            }
            if (req.body.movieAnswer === "B") {
                score += 25;
            }
            if (req.body.qualityAnswer === "C") {
                score += 25;
            }
            if (req.body.defectAnswer === "A") {
                score += 25;
            }
            if (score > quizz.treshold) {
                Match.create({
                    _quizzId: req.body._id,
                    _userRequester: req.body._userRequester,
                    _userCandidate: req.body._userCandidate,
                    average: score / 100
                });
                res.json({
                    message: "BRAVO"
                });
            } else {
                res.json({
                    message: "NOPE"
                });
            }
        }
    });
});

npm run build

Autre élément important appris lors de ces folles journées du code, l’intérêt de faire passer un build de production assez rapidement. En effet, Vue (et les autres) vont compiler votre code et le distribuer dans un dossier /dist. Le seul petit soucis, c’est que si vous avez fait le barbare comme moi en bougeant des classes css natives vous allez retrouver votre appli tout en désordre. Donc ce qui peut être pas mal c’est de faire un build en fin de journée pour bien vérifier que tout est à sa place.

Ou sinon vous codez correctement et vous faîtes des classes partout.

Conclusion

Voila. http://mtmt.viviansarazin.com/

NB : L’application n’est pas optimale en version mobile

Deux semaines de code, une application. Je pense que la prochaine fois je pourrais faire la même application en une semaine. Mais bon il faut passer par là !

Rétrospectivement, je dirais que j’aurais pu passer un peu plus de temps sur la phase de conception afin de mieux préparer mon API, mes composants toussa toussa.

J’ai aussi perdu pas mal de temps sur le setup, plus précisément sur le signup via Facebook. La prochaine fois je pense que je partirais directement sur du Firebase. Autre possibilité, réaliser un boilerplate MEVN pour gagner du temps.

Remerciements

C’est le moment de sortir les mouchoirs. Faut bien que ça s’arrête quand même !

Pour les profs : Merci Eduardo, Guillaume, Maxence, Mickaël A, Mickaël B, Nizar & Yacine.

Et les autres : Merci Antoine, Charlotte, Karim & Maya.

 

Bien à vous :)

Pour vous inscrire à la newsletter c’est par ici :