Nom de l’auteur/autrice :yvonh

javascript

Test de nullité en Javascrip : Cas où l’ordre a une importance

    const handleAccountCreation = async () => {
        const exist = await userExistCheck(result.email)
        console.warn('social login', exist)

        if (exist.exist !== null) {
            socialLogin(result.email)
            console.warn('social login')
        }
        else {
            createAccount(result.email)
            console.warn('social create account')
        }
    }

La condition dans le if échoue si exist est null, car exist.exist reverra une erreur undefined.

Il faut mettre une condition autrement

    const handleAccountCreation = async () => {
        const exist = await userExistCheck(result.email)
        console.warn('social login', exist)

        if (exist == null) {
             createAccount(result.email)
             console.warn('social login')
        }
        else {
            socialLogin(result.email)
            console.warn('social create account')
        }
    }

javascript

Opérateur point d’interrogation ?. en Javascript (Null propagation operator)

A ne pas confondre avec le null coalesce operator en Javascript ?? . Cet opérateur est encore appelé optional chaining operator.

        if (data.error?.message) {
          Alert.alert('Erreur carte',
            `Votre transaction ne s\'est pas terminée correctement Message de Stripe.com : ${data.error.message}`)
        } else {
          return data.id
        }

Si error n’existe pas alors data.error?.message évaluera en undefined

https://ponyfoo.com/articles/null-propagation-operator

Linux

Compresser une dossier avec la commande tar (tarball)

Cette commande est rapide et puissante. Vous pouvez archiver une dossier complet, et avoir la possibilité d’excluer certains répertoire. Vous pouvez aussi à la volée compresser l’archive.

Archive simple

tar -cvf archive.tar /folder

Archiver en excluant des répertoires

$ tar --exclude='./folder' --exclude='./upload/folder2' -zcvf /backup/filename.tgz .
react native

Configurer Apple Pay sur React Native (Part 1)

Tuto React Native Apple Pay avec Laravel en backend

C’est quoi Apple Pay?

Pour pouvoir payer avec Apple Pay, vous devez ouvrir l’application Wallet, scanner votre carte bancaire, un process de validation va suivre et si c’est OK vous pourrez utiliser votre Iphone pour payer. C’est en fait une dématérialisation de votre carte bancaire, qui ne fera qu’un avec votre téléphone, vous n’avez plus besoin d’utiliser votre carte bancaire. Apple Pay agit comme un proxy de votre carte bancaire. Vous avez un portefeuille numérique (Digital Wallet)

Ce qu’Apple Pay n’est pas

Apple Pay n’est pas un moyen de paiement au sens Payment Gateway (comme l’est Stripe, Briaintree,, ça n’encaisse pas d’argent sur un compte Apple, ce n’est pas le In App purchase sur lequel Apple prélève 30%. Apple Pay n e peut donc être utilisé pour l’In App purchase. C’est pour ça qu’une fois Apple Pay est intégré sur une application mobile, si vous utilisez Strip epour encaisser, il faudra utiliser le tuto de Stripe pour intégrer Apple Pay, car votre code Apple Pay va faire une requête vers Stripe.

Procédure de configuration sur le site d’Apple

  • créer un CSR depuis le trousseau de clé Application > Utilitaire
  • Aller dans le compte développeur Apple, créer un Id Merchant
  • Aller dans Certificate créer un certificat cochez Apple Pay Payment Processing Certificate
  • Uploadez le CSR généré, attention pour générer un certificat pour Apple Pay, c’est différent il faut associer une clé spéciale
  • Vous serez invité à téléchargé le certificat
  • Allez dans Certificat,Identifier & Profile, cliquez sur l’application sur laquelle vous voulez utiliser Apple Pay, Apple Pay Payment Processing, choisissez le Merchant ID cochez, un nouveau provisioning profile sera créé (à chaque fois que vous ajoutez ou enlevez une Capabilities
  • Ouvrez Xcode, choisissez onglet Signing&Capabilities, cliquez sur le bouton « +Capabilities« , une liste apparait, double cliquez sur Apple Pay
  • Apple Pay apparait maintenant dans l’onglet, cochez la case « MerchantID »

https://medium.com/enappd/implement-apple-pay-in-react-native-apps-dec9cd5c6808, pour ce lien ne suivez pas l’utilisation de la librairie mais seulement la configuration côté Apple developper Account, elle n’est plus maintenue, et préférer ces deux liens, IOS et Android.

Génération du certificat Procédure

Générer le CSR pour Apple Pay : https://docs.skillz.com/docs/v23.2.1/apple-pay/

On your Mac, open Keychain Access
In the menu bar go to Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority....
Input your preferred email address
Select Saved to Disk
Check Let me specify key pair information
Press Continue
Choose a location to save the .certSigningRequest file, and give it a name.
Select Algorithm ECC
Select Key Size 256 Bits ** NOTE: You MUST select ECC before the 256 Bits option becomes available
Press Continue

https://docs.skillz.com/docs/v23.2.1/apple-pay/

Créer un Sandbox tester Account

Pour éviter d’utiliser votre vraie carte, il vous faut ce type de compte. Allez dans AppStore Connect, dans Utilisateurs et Accès, barre latérale gauche, dans Sandbox, cliquez sur Testeurs, ajoutez un testeur, entrez les informations et validez le compte via email.

Attention la procédure suivante nécessite de vous déconnecter de votre Iphone et de vous connecter avec le compte de test.

Une fois connecté, allez dans vos paramètres > wallet and apple Pay ou en cliquant sur l’icône Wallet, il vous faut entrer manuellement votre carte de test. Vérifiez que la région de votre compte est bien dans un pays supporté par Apple Pay. Cette page officielle contient des numéro sde carte, mais je n’ai pu réussir qu’avec le numéro sur cette page.(je n’ai pas tout essayé non plus)

react native

Ajouter Facebook Login dans une application React Native

https://enappd.com/blog/facebook-login-in-react-native-apps/89/

https://developers.facebook.com/docs/react-native/login/

Résumé des étapes pour intégrer le Facebook Login

  • créer une ap dans Facebook developer Console, récuper l’Id de l’app
  • Installer le package pour React Native
  • Implémenter le code 2 composants sont nécessaire LoginButton, LoginManager

Bien suivre les instructions de cette videos, j’ai suivi le tuto, j’ai pu faire marcher sur Android, mais pas sur IOS qui refuse de builder malgré le paramétrage, la solution a été proposée sur Stackoverflow, solution non intuitive si on n’est pa développeur natif.

https://www.youtube.com/watch?v=J-VIu_i0NRM

Ce lien propose une solution quand le projet ne veut lus builder après l’installation de fbsdk sur IOS.

https://stackoverflow.com/questions/50096025/it-gives-errors-when-using-swift-static-library-with-objective-c-project/56187043#56187043

Facebook Login et le système existant de connexion à votre application

Si vous faites coexister votre système de login et celui de Facebook Login, des questions intéressantes peuvent se poser. A la première connexion, l’utilisateur n’a pas de compte sur votre application, il peut choisir soit de recourir à votre système, soit à celui de Facebook. Je suppose que l’ backend de l’appli est un serveur sous PHP (ex Laravel ou Symfony), et que l’application React Native communique par Rest API, donc authentification par JWT.

Votre système de création de compte et de connexion

  • Création de compte : l’utilisateur entre email et mot de passe et valide la création
  • l’utilisateur se connecte en entrant email et mot de passe
  • le serveur envoit un token d’authentification qui sera stocké sur l’application, et envoyé à chaque requête Ajax

Création avec Facebook Login

  • L’utilisateur clique sur le bouton Facebook Login, s’authentifie, et Facebook envoit un Access Token

Mais voilà à ce stade il a le choix de créer son compte ou non. S’il crée un compte sur l’application, on doit pouvoir recueillir son email automatiquement (c’est possible avec Facebook Login de demander l’email, nom et prénom), envoyer vers le serveur (s’assurer que le compte n’existe pas déjà en comparant l’email), la création de compte étant fini, il faut pouvoir renvoyer un token d’authentification vers l’application qui stockera le token.

Obtenir l’email avec le facebook login

import React, { Component } from 'react';
import { View } from 'react-native';
import { LoginButton, AccessToken, LoginManager, GraphRequestManager, GraphRequest } from 'react-native-fbsdk';

export default class Login extends Component {
    render() {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <LoginButton
                    onLoginFinished={
                        (error, result) => {
                            LoginManager.logInWithPermissions(['public_profile', 'email']).then(
                                function (result) {
                                    if (result.isCancelled) {
                                        console.log("Login cancelled");
                                    } else {
                                        console.log(
                                            "Login success with permissions: " +
                                            result.grantedPermissions.toString()
                                        );

                                        AccessToken.getCurrentAccessToken().then(
                                            (data) => {
                                                console.log(data)
                                                let accessToken = data.accessToken;

                                                const responseInfoCallback = (error, result) => {
                                                    if (error) {
                                                        console.log(error)
                                                        alert('Error fetching data: ' + error.toString());
                                                    } else {
                                                        console.log(result)
                                                        alert('Success fetching data: ' + result.toString());
                                                    }
                                                }


                                                const infoRequest = new GraphRequest(
                                                    '/me',
                                                    {
                                                        accessToken: accessToken,
                                                        parameters: {
                                                            fields: {
                                                                string: 'email,name,first_name,middle_name,last_name'
                                                            }
                                                        }
                                                    },
                                                    responseInfoCallback
                                                );

                                                // Start the graph request.
                                                new GraphRequestManager().addRequest(infoRequest).start();


                                            }
                                        )
                                    }
                                },
                                function (error) {
                                    console.log("Login fail with error: " + error);
                                }
                            );
                        }
                    }
                    onLogoutFinished={() => console.log("logout.")} />
            </View>
        );
    }
};

Anoter que la section

LoginManager.logInWithPermissions(['public_profile', 'email']).then(
                                function (result) {
                                    if (result.isCancelled) {
                                        console.log("Login cancelled");
                                    } else {
                                        console.log(

la façon dont est géré la promesse ne permet pas de lire le bon mot clé this, donc si on passait une props, elle ne sera pas récupérable on aura un undefined, la bonne technique est:

LoginManager.logInWithPermissions(['public_profile', 'email']).then( (result) => {
if(!result){
    console.log("Login cancelled");
}
else {
// le code quand tout est bon
}

https://github.com/lakshmantgld/react-native-fbsdk-example

https://developers.facebook.com/docs/facebook-login/multiple-providers/

Voir aussi Intégrer Google SignIn

react native

Boucler dans un JSX

La syntaxe JSX n’est pas forcément facile à appréhender, imaginez du HTML dans un return…

Pour faire une boucle sur un tableau afin d’afficher une liste par exemple d’élément JSX, il existe plusieurs façons, mais la plus répandue (99% semble-t-il) est d’utiliser map qui est une méthode de l’objet Array, qui retourne un tableau avec le même nombre d’éléments que le tableau d’entrée)

Façon ES6, la plus compacte avec fonction flèche

<div>
    {montableau.map((item, i) => <ObjectRow obj={item} key={i} />)}
</div>

Méthode avec le mot clé function

<div>
    {montableau.map(function(object, i){
        return <ObjectRow obj={item} key={i} />;
    })}
</div>

Avec la boucle for

Cette méthode est rarement vue mais elle marche

<div>
    for (var i=0; i < objects.length; i++) {
        <ObjectRow obj={objects[i]} key={i}>
    } 
</div>

https://stackoverflow.com/questions/22876978/loop-inside-react-jsx

Wordpress

Créer une page personnalisée dans WordPress

Page de statistique (par exemple)

Création des pages php

Dans le menu du thème, il faut créer une page statistique.php

Ajout dans functions.php le lien vers la page dans le menu

Dans le fichier functions.php il faut insérer dynamiquement les instructions pour charger dans le menu administration de WordPress le lien vers la page statistique

//statistique de jeu 

function stat_setup_menu(){
    add_menu_page( 'Statistiques Franchise', 'Statistiques', 'manage_options', 'export-stat', 'stat_init' );
}
function stat_init(){
    include(get_template_directory().'/statistiques.php');
}
add_action('admin_menu', 'stat_setup_menu');
logo laravel

Les variable d’environnement dans Laravel

Il faut utiliser la librairie Dotenv, cependant pas besoin de la charger avec un use

env('LA_CLE')  // La clé qui se trouve dans le fichier .env
#fichier .env
LA_CLE=lavaleurcorrespondante

Attention si vous n’arrivez pas à lire la clé, il est vraisemblable que le problème soit du côté du cache

php artisan config:clear    // devrait suffire
php artisan cache:clear     // si ça ne marche toujours pas 

https://stackoverflow.com/questions/51987140/laravel-accessing-env-variables

logo laravel

Le routing dans laravel

Route prenant un paramètre

Mais vous voudriez aussi gérer le cas où le paramètre eput être optionnel, dans ce cas créer les deux routes (un avec et un sans)

Route::get('/products','ProductController@getProducts');

Route::get('/products/{type}','ProductController@getProducts')->named('produits');

Ensuite dans le controller gérez le paramètre optionnel

    public function getProducts($type = 'chemise'){
        $product = DB::table('products')
            ->where('type','=',$type)
            ->where('status','=','1')
            ->get()
            ->toArray();
        return response()->json([$product][0]);
    }

Ainsi si vous passez la route '/products', vous aurez les chemises, et si vous passez la route '/products/pantalon' vous aurez les pantalons, de même '/products/chemise' vous renverra aussi les chemises.

logo laravel

Injection de dépendance dans Laravel

Qu’est ce que c’est?

L’injection de dépendance, un mot bine effrayant pour désigner quelque chose de très simple , c’est de passer en paramètre d’une fonction un objet, et ce de façon « automatique », le moteur de Laravel se chargeant d’injecter dans la fonction automatiquement.

 class Mailer
{
    protected $mailer;   
    function __construct(PHPMailer $mailer)
    {
        $this->mailer = $mailer;
    }
    public function sendMail($recipient,$content,$subject){
      //...code envoi email
    }
}

Maintenant supposons que nous soyons dans un e méthode de controller appelée NotifyLogin, qui envoit un mail à l’administrateur lorsque quelqu’un se connecte.

use Illuminate\Http\Request;
use use App\Helper\Mailer;

class LoginController extends Controller
{
    public function __construct(){}
    public function notifyLogin(Request $request, Mailer $mailer){
    $email = $request->email;

    $mailer->sendMail($email,'Un utilisateur s\'est connecté','Connexion');
    
    }
}

On voit que on injecte $request, mais aussi $mailer, il n’est pas besoin d’instancier avec new, Laravel le fait automatiquement pour nous. Ceci est rendu possible parce qu’en amont, le constructeur de la class Mailer a lui-même utilisé l’injection de dépendance pour s’instancier. AInsi de proche en proche le framework arrive à fournir à la méthode notifyLogin le $mailer avec un minimum de travail de notre part, et en plus notre code est plus lisible et mieux organisé.

react native

React Navigation V5 (librairie de React Native)

Cette librairie est considérée comme importante car elle est incluse par défaut quand vous bootstrapez un projet React Native.

React Navigation V5

Bare react Native CLI

⚠️ StackNavigator V4 is deprecated, use createStackNavigator instead in V5

#Step 1:
$ npm install @react-navigation/native @react-navigation/stack
#Step 2: install the required peer dependencies
$ npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

Configuration initiale de React Navigator

Il vous faut installer le package principal et tous les package suivants

$ npm install @react-navigation/native
$ npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

Configuration native pour Android

Pour Android, pas d’installation, mais ajouter deux lignes dans le fichier build.gradle dans votre_projet/android/app/build.gradle dans la directive dependencies

 dependencies {
    ....
  implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
  implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
}

Installation des fichiers natifs IOS

$ cd ios; pod install; cd ..

Chargement de gesture handler en tout début de script du fichier d’entrée App.js ou index.js, cet import doit être vraiment le tout premier code Js de votre projet.

# dans index.js (ou App.js si pas de index.js)
import 'react-native-gesture-handler';

Pour IOS React navigation a besoin de cette opération: Cocoapod

Note: For iOS with bare React Native project, make sure you have Cocoapods installed

IOS. install cocoapod : mandatory for React Native 0.60+ (https://medium.com/@appstud/add-a-splash-screen-to-a-react-native-app-810492e773f9)

$ sudo gem install cocoapods

***Passing data to Screen:

<Button
        title="Go to Details"
        onPress={() => {
          /* 1. Navigate to the Details route with params */
          navigation.navigate('Details', {
            itemId: 86,
            otherParam: 'anything you want here',
          });
        }}
      />

passing initial param to a screen from <Stack.Screen>
<Stack.Screen
  name="Details"
  component={DetailsScreen}
  initialParams={{ itemId: 42 }}
/>


Retrieving from the destination Screen :
function DetailsScreen({ route, navigation }) {
  /* 2. Get the param */
  const { itemId } = route.params;
  const { otherParam } = route.params;
    ....

Pour migrer de la version 4 de React Navigation à la 5, voici le lien, principale différence dans la version 5

« In React Navigation 5.x, we have split the navigation prop into 2 props: navigation prop contains helper methods such as navigate, goBack etc., route prop contains the current screen’s data (previously accessed via navigation.state).

In React Navigation 5.x there’s no createAppContainer which provided screens with navigation context. You’ll need to wrap your app with NavigationContainer provider.

Imbrication de routes

On a des creens qui sont contenu dans des navigators, si on a un lien d’un autre navigator, si depuis l’écran SignupScreen, on a un lien vers HistoryScreen, il y aura une erreur, car le screen History n’est pas connu du HomeStack navigator.

<HomeStack.Navigator>
    <HomeStack.Screen name="SignUp" component={SignUpScreen} />
</HomeStack.Navigator>

<ProfileStack.Navigator>
        <ProfileStack.Screen name="Profil" component={ProfileScreen} />
        <ProfileStack.Screen name="History" component={HistoryScreen} />
        <ProfileStack.Screen name="Edit" component={EditScreen} />
</ProfileStack.Navigator>

Pour permettre la navigation inter navigator, il faut déclarer dans le Homestack navigator le screen History

<HomeStack.Navigator>
    <HomeStack.Screen name="SignUp" component={SignUpScreen} />
    <HomeStack.Screen name="History" component={HistoryScreen} />
</HomeStack.Navigator>

<ProfileStack.Navigator>
        <ProfileStack.Screen name="Profil" component={ProfileScreen} />
        <ProfileStack.Screen name="History" component={HistoryScreen} />
        <ProfileStack.Screen name="Edit" component={EditScreen} />
</ProfileStack.Navigator>

Nesting Navigation: https://reactnavigation.org/docs/nesting-navigators/

function Home() {
  return (
    <Tab.Navigator>
      <Tab.Screen name="Feed" component={Feed} />
      <Tab.Screen name="Messages" component={Messages} />
    </Tab.Navigator>
  );
}

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={Home} />
        <Stack.Screen name="Profile" component={Profile} />
        <Stack.Screen name="Settings" component={Settings} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

PRO TIP : Régler le problème du refresh lors d’un goBack()

React navigation possède un défaut : imaginez que vous soyez dans votre page profile dans une application, vous voulez éditer, et quand l’édition est finie vous revenez visualiser votre page de profile, les informations ne se rafraichissent pas, car le goBack() ne gère pas le rafraichissement.

Voici une méthode pour forcer le rafraichissement de la page profile (qui est la page parente, la page édition de profile étant la page enfant).

Quand vous passez de la page Profile à EditionProfile, vous allez passer une fonction en props, que vous allez exécuter dans la page enfant EditionProfile, juste avant de revenir à la page parente Profile. Cette fonction va muter une variable arbitraire qui existe dans la page parente, comme ça on pourra détecter la mutation de cette variable au sein de notre hook useEffect.

const [foo, setFoo] = useState(1) 

   useEffect(() => {

        fetch(getUserInfoUrl, {
            method: 'GET',
            headers: headers,
        })
            .then(response => {
                return response.json()
            })
            .then(data => {
                console.log(data)
                setUser(data)
            })
        //AJAX LOAD
        setShowLoader(false)

    }, [foo]) /// foo est la variable surveillée par useEffect pour se déclencher, si vous ne savez pas ce qu'est un hook useEffect allez voir la documentation.

La fonction définie dans Profile et passée puis exécutée dans la page EditionProfile (pas dans Profile, oui on peut passer des props fonction dans React)

    const handleOnNavigateBack = () => {
        setFoo(foo + 1)
    }

Dans la page ProfileEdit

...
          onNavigateBack() // exécuter cette fonction va incrémenter foo dans la page parente !
          navigation.navigate('Profil')
...
logo laravel

Laravel et logging

Le logging consiste en la sauvegarde de chaines de textes dans un fichier. Basiquement c’est l’idée, on peut affiner ceci. Le fichier de log sert surtout à aider le debug, et ceci en mode production.

Laravel et Monolog

Monolog est une librairie bien connue en PHP pour logger. elle est bien intégrée à Laravel.

Où se trouve le fichier de log de Laravel ?

la grande question ! il se trouve (Laravel 7) dans le dossier /storage/logs/laravel.log.

Comment logger une entrée?

use Illuminate\Support\Facades\Log;

Log::info('Someone tried login : ' . $request->query->get('email'));

Est ce que Monolog permet de logger les requêtes SQL?

Non. Pour ce faire il faut utiliser la facade Debug (DB)

use Illuminate\Support\Facade\Debug;
...
DB::enablQueryLog()
php

Streamer une image en PHP

Qu’est qu’un stream?

un stream est un flux, c’est à dire un envoi continu d’information. Utilisé dans le monde de la vidéo ou de la musique, la lecture d’un stream est le fait de lire sans attendre la fin du téléchargement du fichier, dans un but but de rapidité bien entendu. Dans notre exemple, il s’agit de télécharger l’image sous la forme d’un flux.

Par rappor à la méthode classique d’inclusion d’une image en HTML

<img src="chemin/vers/image.png" />

On va mettre à la place un fichier PHP

<img src="myimage.php" />

Qui aura pour but de montrer l’image in fine, mais au travers d’un fichier PHP. Et ceci est très intéressant car il va nous permettre de faire beaucoup de choses.

Le besoin est par exemple de savoir combien de fois l’image a été téléchargée, car grâce au PHP on peu implémenter facilement ce code, car si on parse les log du serveur web, c’est fastidieux, alors que là c’est facile.

Script d’exemple

<?php
header('Content-type: image/jpeg');
$file = './streamimage.jpg';
$src = imagecreatefromjpeg($file);
$res = imagejpeg($src);
if($res){
    $handle = fopen('stat.txt','a+');
    $record = date('y-m-d H:i:s') . " " . $_SERVER['HTTP_REFERER'] .  "\n";
    fwrite($handle,$record);
    fclose($handle);
}
die;

Dans l’exemple ci-dessus, chaque fois que l’image est streamée, on logue dans un fichier l’heure et le referer.

vs code logo

Créer des snippet sur VSCode pour augmenter votre productivité

Qui n’a pas tappé un millier de fois echo ou console.log? Voir function(){}, ou encore () => {} ?

Grâce aux snippets customisés, vous allez pouvoir vous affranchir de cette corvée et être plus productif.

Trouver le menu des custom snippet dans VSCode

Faites la combinaison de touches CTRL + SHIFT + P (sous Windows) et tapez « snippet », sélectionnez custom

Ceci va vous amener au fichier csl.code-snippets . Voici un example de code que vous pouvez faire :

	"Print to console": {
		"scope": "javascript,typescript",
		"prefix": "cls",
		"body": [
			"console.log('$1','$2');",
		],
		"description": "Log output to console"
	}

Vous tapez le prefix « cls » et le body s’affichera. Notez les placeholder $1 et $2, vous pourrez basculer de l’un à l’autre avec la touche TAB.

Un site pour générer le code pour vos snippets.

Les snippets par framework ou langage

Il existe déjà dans la marketplace, des plugins où les snippets sont déjà définis. Allez dans la marketplace et tapez « snippet ». Emmet est intégré d’office et s’occupe du HTML et CSS. Pour le reste je vous laisse découvrir, il y en a pour tous les langages et frameworks. Normalement en 2024 VSCode est capable de détecter le langage de votre projet et vous propose des plugins à installer. Par exemple, si vous avez un fichier docker compose, il va vous proposer le plugin docker correspondant.

javascript

Ecouter les événements du clavier avec Javascript

Cette thématique est rarement abordée, mais je trouve qu’il est intéressant d’ajouter des raccourcis clavier à votre application. Il existe des plugin qui le font déjà, mais back to basics, regardons comment on le fait avec du Javascript pur

Ecouter les touches alpha numérique de base

document.addEventListener('keydown', function (event) {
  if (event.key === 'a') {
    //votre code javascript ici
  }
  if (event.key === 'd') {
    //votre code javascript ici
  }
});

Il est assez simple d’ajouter un listener, mais l’astuce est de le faire sur l’élément document, comme ça, ça concernera toute votre page et on utilisera la délégation d’événement.

Ecouter les touches spéciales

document.addEventListener('keydown', function (event) {
  // CTRL + D combo
  if (event.ctrlKey && event.key === 'd') {
    //votre code
  }
  // CTRL + L combo 
  if (event.ctrlKey && event.key === 'l') {
    //votre code
  }
});

Eviter les conflits avec les autres raccourcis clavier de votre navigateur, par exemple sur Windows ou Linux, CTRL + D sert à bookmarker une page, pour éviter cela on va utiliser le célèbre preventDefault !!

document.addEventListener('keydown', function (event) {
event.preventDefault()
  // CTRL + D combo
  if (event.ctrlKey && event.key === 'd') {
    //votre code
  }
  // CTRL + L combo 
  if (event.ctrlKey && event.key === 'l') {
    //votre code
  }
});

Les autres touches péciales Alt, Shift etc

alt :  event.altKey
shift : event.shiftKey
Wordpress

Requêter des tables non WordPress

Imaginez que vous devez faire quelquechose de custom, et que vous devez enregistrer des données dans une table non standard de WordPress, est-il possible d’utiliser les fonction de WordPress pour faire des opération CRUD? sans passer par PDO par exemple?

La réponse est OUI ! Je passe sur le fait de devoir faire une connexion vous même en récupérant les données de connexion à votre base de données.

Récupérer les identifiant de connexion dans une page

Il est facile de récupérer les identifiants de connexion quand vous formattez correctement votre page.

/**
 * The template for displaying all pages.
 *
 * Template Name: Page Classement
 * Visibility par
 * Template Post Type:  page_application
 *
 * Page template with minimal formatting, a fixed 940px container and right sidebar layout
 *
 * @package WordPress
 * @subpackage WP-Bootstrap
 * @since WP-Bootstrap 1.0
 */

//var_dump(DB_NAME, DB_USER, DB_PASSWORD, DB_HOST);

global $wpdb;
global $wp_query;

$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}classement ", OBJECT );
var_dump($results);

Le code ci-dessus est dans une page dans un thème (sous réserve que le thème soit actif, ce code marchera)

Vous n’avez pas besoin de faire de connexion, en utilisant l’objet global $wpdb, la connexion est déjà faite, $wpdb est l’objet connexion.

Utilisation de $wpdb

Le documentation officielle de cet objet est sur cette page.

Read

$results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}classement ", OBJECT );

Insert

$wpdb->insert( 
    "{$wpdb->prefix}classement", 
    array( 
        'nom' => 'value1', 
        'temps' => 123, 
        'jeu' => 'Templiers', 
        'status' => 'enabled', 
        'created_at' => '2020-08-27 15:10:18', 
    )/*, 
    array( 
        '%s', 
        '%d', 
    ) */
);

Update

$wpdb->update( 
    'classement', 
    array( 
        'nom' => 'value1',   // string
        'position' => 'value2'    // integer (number) 
    )
)

Delete

$wpdb->delete( 'table', array( 'ID' => 1 ) );
Android

Déploiement d’une application React Native sur Android

Les Builds sur IOS et Android

Les builds c’est vraiment la difficulté dans le développement des application mobile, au moins au début, mais c’est suffisamment difficile et décourageant car c’est d’une très grande complexité, et un même problème peux avoir deux causes différentes, et comme vous êtres développeur, vous n’êtes pas axé sur cet aspect, qui est pourtant nécessaire, sans quoi vos compétences de développeur ne seront rien, en effet à quoi bon avoir l’application codées si on ne peut pas la déployer sur le store?

Un build sur Android nécessite Android Studio, un build sur IOS nécessite Xcode. Si vous êtes sur Windows, vous ne pourrez développer que pour Android, pas pour IOS. SI vous être sur Mac OS vous pourrez développer pour Android et IOS ! Donc voilà une raison de vous acheter un macBook Pro !

La difficulté tient à la complexité d’un processus de build, d’un cache non vidé, en effet un build réussi peut très bien échouer pour X raison mais vous ne pourrez résoudre ce problème que si vous remettez tout à zéro !

Néanmoins il y a des messages d’erreur qui reviennent souvent et vous devez connaitre.

Mais avant il faudra vous familiariser avec le parcours nominal de configuration d’un build, pour Android, il s’agira bien sûr de mettre en place Android Studio et tout ce qui va avec, télécharger les SDK qu’il faut. Pour IOS c’est plus fluide, néanmoins vous devez connaître le flow de build, les commandes correspondantes.

D’autre part, notez qu’en développement vous pouvez builder pour un émulateur, ou votre terminal physique.

Déployer sur le store est une autre paire de manche, spécialement pour Apple Store, où le fameux provisioning profile en fera suer plus d’un ! oui le développement d’application mobile est un métier, et nettement plus fastidieux que de faire un site web. Par moment vous vous direz « Vive les PWA ! ».

Problèmes de builds pour Android

A quoi sert AppCompat?
When new versions of android are published, Google will have to support the older versions of android. So AppCompat is a set of support libraries which can be used to make the apps developed with newer versions work with older versions.

Troubleshoot configuration : https://www.youtube.com/watch?v=GUZVGidimqY
https://medium.com/pvtl/react-native-android-development-on-mac-ef7481f65e47

React navigation causes android not working because of maybe

java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/swiperefreshlayout/widget/SwipeRefreshLayout;

Gradle build file add two lines to build.gradle: (https://github.com/react-navigation/react-navigation/issues/6267 and https://github.com/software-mansion/react-native-screens#usage-with-react-navigation-without-expo)

lignes à ajouter dans VOTRE_APP/android/app/build.gradle

dans la section dependencies

dependencies {
 ...
 implementation 'androidx.appcompat:appcompat:1.1.0-rc01'
 implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02'
    }    

Error running android: Gradle project sync failed. Please fix your project and try again
Goto File -> Invalidate caches / Restart

Erreur gradle ne trouve pas une dépendance ‘react-native-screen’, parfois mettre à jour le plugin de Gradle pour Android Studio peut résoudre le problème.

Lien suite les versions compatible entre la version du plugin pour Android et la version de Gradle:
https://stackoverflow.com/questions/17727645/how-to-update-gradle-in-android-studio

Déploiement sur Android Device (depuis MacOs)

Ouvrez Android Studio, branchez votre device Android (vous devrez avoir configuré en mode développeur). autorisez le transfert de fichier entre votre device et l’ordinateur.

Il y a deux façon de déployer sur votre device, soir en ligne de commande, soit en via Android Studio. Il faut au préalable avoir branché le device et ensuite faire la même commande. Voci la commande pour détecter les devices trouvés :

adb devices

SI à côté du nom de device c’est marqué offline, ça ne marchera pas, rebranchez le devices et jusqu’à ce que vous obtenez device marqué à droite du nom de l’appareil. Pour maintenir la connexion active depuis votre device Android faites cette ligne pour rerouter les requêtes vers le serveur (sinon le live reload ne marchera pas) :

https://reactnative.dev/docs/running-on-device

adb reverse tcp:8081 tcp:8081

Erreurs récurrentes lors de la compilation sur device ou émulateur

Cycle inside ; building could produce unreliable results: Xcode 10 Error

Vous pouvez résoudre ce problème en effaçant les données du répertoire de build, c’est une procédure très complexe, et parfois le plus simple et de force à recompiler en effaçant toutes les données.

#efface le contenu du répertoire derived data (Xcode)
rm -rf /Users/utilisateur/Library/Developer/Xcode/DerivedData/*

Effacer le cache React Native

Il est assez difficile d’effacer le cache de react Native. Si vous avez React Native version < 0.50

 watchman watch-del-all && rm -rf $TMPDIR/react-* &&
 rm -rf node_modules/ && npm cache clean && npm install && 
 npm start -- --reset-cache

Si vous avez React Native version > 0.50

watchman watch-del-all && 
rm -rf $TMPDIR/react-native-packager-cache-* &&
rm -rf $TMPDIR/metro-bundler-cache-* && 
rm -rf node_modules/ 
&& npm cache clean --force &&
npm install && 
npm start -- --reset-cache

lien : https://stackoverflow.com/questions/46878638/how-to-clear-react-native-cache

Une fois j’ai malgré avoir appliqué TOUTES ces méthodes n’ai pas pu rafraichir mon application, j’ai renommé par précaution mon répertoire build qui est dans le répertoire android, et j’ai rebuildé le projet. Un nouveau répertoire build est créé avec un build plus frais !

react native

Que faire quand Metro ne se lance pas (React native)

Metro se lance sur le port 8081, il y a de grande chance que ce soit occupé par un autre process.

Dans ce cas faire un npm start peut déclencher l’erreur suivante :

listen EADDRINUSE: address already in use :::8081

Dans ce cas il faut killer le process en question. Pour le trouver, faire la commande :

lsof -n -i4TCP:8081

Ensuite pour killer :

kill -9 <PID>

react native

Connaitre l’UDID de son appareil sur MacOS Catalina

Pour une application React Native, allez dans le Finder, le répertoire IOS, et cliquez sur le fichier avec extension xcworkspace et non celui avec l’extension xcodeproj .

Branchez votre device sur votre Mac, dans Finder, cliquez sur votre devie sur la barre latérale, votre nom de device sera présent, cliquez sur le nom sur la barre horizontale, le UDID s’affiche, ensuite clic droit et copiez l’UDID

xcode

XCode iPhone is not available. Please reconnect the device

Pourquoi le message XCode iPhone is not available. Please reconnect the device ?

Vous recevez ce message en général après une longue période d’absence de développement sur votre applicationmobile, ou peut être le lendemain alors que tous se passait bien la veille.

Vous essayer de redémarrer Metro (dans le cas de React Native) mais rien n’y fait. Il est vraisemblable que la version de XCode n’est pas synchro avec la version de IOS du device.

Quelle version de Xcode pour quelle version de IOS ?

Xcode 12 → iOS 14

Xcode 11.6 → iOS 13.6

Xcode 11.5 → iOS 13.5

Xcode 11.4 → iOS 13.4

Voici la page de téléchargement des binaires et entre autre de XCode. Vous pouvez aussi télécharger XCode depuis le le store de Mac OS, mais j’ai eu des lenteurs extrêmes, donc c’est quelque chose que je ne vous conseille pas, faisons à l’ancienne !

Quand Xcode est à jour et que ça ne marche toujours pas

Il m’est arrivé, je vérifie que XCode est bien à jour, si ça ne marche toujours pas, aller dans Windows > devices, il y aura un message d’erreur, du type « impossible de configurer … », essayez de rafraichir cette fenêtre en changeant des paramètres :

Passez sur Cordova par exemple (attention l’image vient d’un projet Ionic, pour React Native ce sera différent)

Puis revenez en mode normale, le contenu de la fenêtre aura varié, ça peut prendre quelques dizaines de secondes, refaites un build.

Astuce : empêchez IOS de se mettre à jours !

Allez dans Paramètre > Itunes store et Appstore > Mise à jour d’apps

Allez aussi dans Général > Mise à jour Logicielle, décochez « Téléchargez les mises à jours »

bougez le switch pour désactiver

tuto ionic 4

Détecter dans le canvas (ou sur l’écran) la position du touch dans Ionic

Lorsque vous réalisez par exemple un canvas dans Ionic, vous devez gérer les événement souris et touch (souris pour le développement et touch pour la production), malheureusement ce n’est pas trivial, s’il est assez facile de détecter la position de la souris, cela demande un peu plus de travail avec le touch (encore que vous le sachiez !)

Les événements touch

Soit le code du canvas suivant par exemple dans Ionic

<canvas #imageCanvas
(mousedown)="startDrawing($event)" (touchstart)="startDrawing($event)"
(mousemove)="moved($event)" (touchmove)="moved($event)"
(mouseup)="endDrawing()" (touchend)="endDrawing()"
></canvas>

Il faut dans un premier temps détecter le type d’événement. L’image ci dessous est un dump de l’objet événement touchstart

Comme vous le voyez les variables de position ne sont pas au même endroit ! pensez-y

Eviter le scroll sur mobile

Si vous développez pour une application mobile, comme Ionic, vous aurez sans doute le problème du scroll quand vous dessinez, l’écran va scroller et c’est gênant. Ce qu’il faut faire c’est d’empêcher l’événement de se propager

            this.canvasElement.addEventListener("touchstart",  function(event) {event.preventDefault()})
            this.canvasElement.addEventListener("touchmove",   function(event) {event.preventDefault()})
            this.canvasElement.addEventListener("touchend",    function(event) {event.preventDefault()})

this.canvasElement est l’élément Canvas que vous avez en variable.

javascript

CSS Positionnement et modèle de boite

Lorsque vous positionnez des éléments en position relative, il faut faire attention au modèle de boite.

Soit les div de classe carre, il sont dans une autre div, et positionnés avec top et left.

        .carre {
            box-sizing: border-box;
            // box-sizing: content-box;
            position: relative;
            width: 100px;            height: 100px;
            background: blue;            border:red solid 1px;
        }

box-sizing : content-box

Ici on prend en compte l’épaisseur de la bordure pour détermine la position de l’élément suivant.

Ici est tenu compte de l’épaisseur des toutes les bordures (élément avant et après)

box-sizing:border-box

Ici on ne tient pas compte de l’épaisseur des bordures pour détermine la position de l’élément suivant

En faisant fi de l’épaisseur des bordure, le placement est beaucoup plus facile (pour pas dire possible)
javascript

Travailler avec la position de la souris ou des éléments en JS

La position de la souris

pageX, pageY

mouseevent.pageX et mouseevent.pageY représentent la position de la souris dans la page. La position 0 correspond au coin supérieur gauche de la page web.

Si vous scroller le document vers la droit de 200 pixel, et que la souris pointe à 100 pixel à droite du bord gauche du navigateur, pageX indiquera 300 au lieu de 100, donc ça tient compte du scrolling.

clientX, clientY

mouseevent.clientX et mouseevent.clientY représent la position de la souris par rapport à la fenêtre du navigateur seulement, et donc insensible au scrolling.

Position d’un élément getBoundingClientRect()

Cette méthode renvoit un obhet DOMRect, qui contient les information de position et de taille

element.getBoundingClientRect()
#affichage
{    bottom: 311 // sur axe Y,par rapport au bord haut de la page (origine point de vue)
     height: 100 // hauteur de l'élément
    left: 209 // sur axe X
    right: 309  // sur axe X
    top: 211 // sur axe Y
    width: 100 // largeur de l'élément
    x: 209
    y: 211
}

https://developer.mozilla.org/fr/docs/XPCOM_Interface_Reference/nsIDOMClientRect

element.offsetLeft

Renvoit le décalage horizontal en pixel de l’élément par rapport au parent (de coin supérieur gauche à coin supérieur gauche)

element.offsetTop

Renvoit le décalage vertical en pixel de l’élément par rapport au parent (de coin supérieur gauche à coin supérieur gauche)

Les modules en Javascript (import/export)

Tout d’abord sachez que les modules existent aussi bien ôté serveur que client. Les modules on été inventé pour permettre d’inclure ds fichier de script javascript comme on purrait le faire en PHP avec include.

Il existait plusieur type de module

  • AMD (Asynchronous Module Definition) un des plus ancien cf la librairie require.js
  • CommonJS, le systyme de mdule de NodeJS
  • UMD (universal Module Definition) Universal comme rassemblant AMD et CommonJS.

C’est quoi un module?

Un module est un fichier JS. Pour charger les uns des autres, il y a les mots clé import et export.

Exemple :

// sayHi.js
export function saluer(user) {
  alert(`Bonjour, ${user}!`);
}

Dans un autre fichier qui veut utiliser cette fonction

// main.js
import {sayHi} from './sayHi.js';
saluer('Jean'); // Bonjour Jean

ES2015 et ses apports

Grâce à ES2015, nous avons nativement les instructions import et export côté client. Si nous voulons importer un module dans une page HTML, nous devons utiliser le type module. Pour accéder au fichier HTML ci-dessous, vous devez avoir un serveur local, car ouvrir le fichier directement dans le navigateur avec le protocole file:// ne va pas marcher.

<!doctype html>
<script type="module">
  import { saluer} from './sayHi.js;
 document.body.innerHTML = sayHi('John');
</script>

Les particularités des modules

  • utiliser use strict
  • chaque module a son scope, les varaibles de deux modules sont invisible mutuellement
  • un module est exécutéune seule fois même s’il est importé de multiples fois

Export et import

Nous avons vu que à la base du système de module, ce sont ces deux mot clé qui sont incontournables.

Nous pouvons exporte une constante, un tableau , un objet, ou un fonction, ou encore une classe.

export const Pi = 3.14
export let nom = "Dupont"
expot class User{
   constructor(name){
      this.name = name
   }
} // pas de ; à la fin

function sayHi(user) {
  alert(`Hello, ${user}!`);
}

function sayBye(user) {
  alert(`Bye, ${user}!`);
}

export {sayHi, sayBye}; // a list of exported variables

Export default

On rencontre très couramment cette syntaxe. Un seul export défault par fichier JS. Et l’import se fait sans accolade.

export default class User { // just add "default"
  constructor(name) {
    this.name = name;
  }
}
// main.js
import User from './user.js'; // et non {User}

donc import utiliser les accolades pour les exports nommés, et pas d’accolade pour les export defaults.

https://javascript.info/modules-intro

https://javascript.info/import-export

Réaliser une application web avec Firebase, Firestore

Firebase d’avoir une base de données NoSQL principalement dans le cloud. Mais ça ne se limite pas à ça, il y a aussi du stockage d fichier, de code, de l’analytics, mais le principal usage à ma connaissance est la base temps réel NoSQL, son avantage est de permettre de mettre sur pied rapidement une application, pour peu qu’on soit familier avec lui.

Création d’un compte Firebase

Pour créer un compte Firebase, vous devez disposer d’un compte Google, et ensuite activer votre compte Firebase.

Contexte du tutoriel

Le contexte de l’application est une application dans une page html, donc pas besoin d’avoir NodeJs, nous verrons qu’il est possible de créer un projet de cette façon et de faire appel aux librairies, mais pour des raisons de simplicité, nous allons au plus simple.

Ensuite vous devez créer un projet et remplir des informations sur votre projet. Vous disposez d’niformation de configuration pour votre application.

Inclusion des scripts

Il y a plusieurs scripts à inclure en fonction de ce que vous voulez faire (analytics ou non) mais le principal fichier à inclure est le firebase-app.js

    <!-- The core Firebase JS SDK is always required and must be listed first -->
    <script src="https://www.gstatic.com/firebasejs/7.15.0/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.17.1/firebase-auth.js"></script>

    <script src="https://www.gstatic.com/firebasejs/7.15.0/firebase-firestore.js"></script>
    <!-- TODO: Add SDKs for Firebase products that you want to use
     https://firebase.google.com/docs/web/setup#available-libraries -->
    <script src="https://www.gstatic.com/firebasejs/7.15.0/firebase-analytics.js"></script>
#exemple de configuration Firebase
        // Your web app's Firebase configuration
        var firebaseConfig = {
            apiKey: "DCZ28p7gilP15WoymVHn-A",
            authDomain: "myapp-bull3503.firebaseapp.com",
            databaseURL: "https://myapp-bull3503.firebaseio.com",
            projectId: "myapp-bull3503",
            storageBucket: "myapp-bull3503.appspot.com",
            messagingSenderId: "2017024",
            appId: "1:2017024:web:e282485405d09",
            measurementId: "G-J4XXXQPHT9"
        };

// initialisation de l'app
firebase.initializeApp(firebaseConfig);

Créer une base de données orientée documents Cloud Firestore

Cette base est en accès public pour une durée de 30 jours, vous pourrez vous y connecter sans mot de passe, passé ce délai, vous recevrez une alerte comme quoi la base ne sera plus accessible en public. Dans la cnsole, vous verrez Realtime Database et Cloud Firestore, quelle est la différence? C’est expliqué sur cet article.

#paramétrage de la base de données
à l'origine le paramétrage est permissif pour vous permettre de tester la base

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.time  < timestampe.date(2020,7,6);
    }
  }
}

Mais au bout de 30 jours vous recevez un mail pour modifier l'accès à la base avec des règles plus strictes.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Mais au bout de 30 jours vous recevez un mail pour modifier l'accès à la base avec des règles plus strictes.

Créer un utilisateur

Pour créer un utilisateur, chargez d’abord la librairie auth, qui vous permet de créer un utilisateur et de gérer les connexion.

#création d'un utilisateur via un formulaire
        firebase.auth().createUserWithEmailAndPassword(email, password).catch(function (error) {
            // Handle Errors here.
            var errorCode = error.code;
            var errorMessage = error.message;
            console.log(errorCode, ':', errorMessage)
        });

Si la création d’un utilisateur s’est faite avec succès, vous êtes automatiquement loggé. Si vous n’arrivez pas à créer un utilisateur de cette façon (probablement parce que vous avez oublié d charger la librairie auth) et que vous désirez rapidement en créer un, vous pouvez aller directement sur la console, dans la sidebar de gauche Authentification puis Ajouterun utilisateur

Connexion d’un utilisateur

        firebase.auth().signInWithEmailAndPassword(email, password).catch(function (error) {
            // Handle Errors here.
            var errorCode = error.code;
            var errorMessage = error.message;
            console.log(errorCode, ':', errorMessage)
        });

Pour maintenir la connexion de l’utilisateur entre différentes pages, il vous faudra utiliser le localStorage.

Lire une table

Je suppose que vous incluiez à chaque page les librairie correspondantes.

        var firebaseConfig = {
            ...
        };
        // Initialize Firebase
        firebase.initializeApp(firebaseConfig);
        firebase.auth().signInWithEmailAndPassword(email, password).catch(function (error) {
          ...
        });
#instanciation de la connexion à Firestore
var db = firebase.firestore();

Découvrez comment administrer la base de données depuis le site de Firebase.

Retour en haut