Découvrez les nouvelles et prochaines fonctionnalités de navigation pour votre PWA: From Fugu With Love

1. Avant de commencer

Les progressive web apps (PWA) sont un type de logiciels diffusés via le Web, et créés à l'aide de technologies Web courantes telles que HTML, CSS et JavaScript. Ils sont conçus pour fonctionner sur toutes les plates-formes utilisant un navigateur conforme aux normes.

Dans cet atelier de programmation, vous commencerez avec une PWA de référence, puis explorerez de nouvelles fonctionnalités de navigateur qui finiront par offrir des superpouvoirs à votre PWA 🦸.

Un grand nombre de ces nouvelles fonctionnalités de navigateur sont en cours d'exécution et sont en cours de standardisation. Vous devrez donc parfois définir des indicateurs de navigateur pour les utiliser.

Prerequisites

Pour cet atelier de programmation, vous devez vous familiariser avec le code JavaScript moderne, en particulier les promesses et le format asynchrone/await. Toutes les étapes de l'atelier de programmation ne sont pas compatibles avec toutes les plates-formes. C'est pourquoi il est préférable de tester si vous avez des appareils supplémentaires à portée de main, par exemple un téléphone Android ou un ordinateur portable dont le système d'exploitation est différent de celui sur lequel vous modifiez le code. Au lieu d'utiliser de véritables appareils, vous pouvez essayer d'utiliser des simulateurs tels que le simulateur Android ou des services en ligne comme BrowserStack qui vous permettent d'effectuer des tests depuis votre appareil actuel. Sinon, vous pouvez également passer toutes les étapes, elles ne dépendent pas les unes des autres.

Ce que vous allez faire

Vous allez créer une application Web de cartes de vœux et vous familiariser avec les fonctionnalités nouvelles et futures du navigateur afin d'améliorer l'expérience sur certains navigateurs tout en restant utile dans tous les navigateurs récents.

Vous apprendrez à ajouter des fonctionnalités d'assistance, comme l'accès au système de fichiers, l'accès au presse-papiers, la récupération des contacts, la synchronisation en arrière-plan périodique, l'activation de l'écran de démarrage, les fonctionnalités de partage, et bien plus encore.

Après avoir suivi cet atelier de programmation, vous saurez comment améliorer progressivement les applications Web grâce à de nouvelles fonctionnalités de navigateur, tout en limitant les téléchargements sur le sous-ensemble d'utilisateurs qui n'ont pas accès à des navigateurs incompatibles et, surtout, sans les exclure de votre application.

Ce dont vous avez besoin

Pour le moment, les navigateurs entièrement compatibles sont les suivants:

Il est recommandé d'utiliser la version en développement appropriée.

2. Projet Fugu

Les PWA (Progressive Web Apps) sont conçues et améliorées à l'aide d'API modernes. Elles permettent de bénéficier de fonctionnalités avancées, d'une fiabilité et d'une facilité d'installation pour tous les internautes, où qu'ils se trouvent et sur n'importe quel type d'appareil.

Certaines de ces API sont très performantes et peuvent entraîner des problèmes si elles ne sont pas correctement gérées. Tout comme le fugu 🐡: si vous le coupez mal, il peut être fatal mais ne vous inquiétez pas, mais rien ne peut s'arrêter dans cet atelier de programmation.

C'est la raison pour laquelle le nom de code interne du projet Web Operations (dans lequel les entreprises concernées développent ces nouvelles API) est Project Fugu.

Les capacités Web, déjà actuelles, permettent aux petites comme aux grandes entreprises de créer des solutions basées uniquement sur des navigateurs pour accélérer le déploiement à moindre coût par rapport à l'utilisation d'itinéraires spécifiques à la plate-forme.

3. Premiers pas

Téléchargez l'un de ces navigateurs, puis définissez l'indicateur d'exécution suivant 🚩 en accédant à about://flags, qui fonctionne à la fois dans Chrome et Edge:

  • #enable-experimental-web-platform-features

Une fois activé, redémarrez votre navigateur.

Vous utiliserez la plate-forme Glitch, car elle vous permet d'héberger votre PWA et d'un éditeur de qualité. Glitch permet également l'importation et l'exportation vers GitHub, ce qui évite toute dépendance vis-à-vis d'un fournisseur. Accédez à fugu-paint.glitch.me pour essayer l'application. C'est une appli de dessin de base 🎨 qui s'améliorera pendant l'atelier de programmation.

La PWA de base Fugu Salutations est dotée d'une large toile sur laquelle est peint le mot “Google”.

Après avoir joué avec l'application, remixez-la pour créer votre propre copie modifiable. L'URL de votre remix ressemblera à ceci : glitch.com/edit/#!/bouncy-candytuft ("bouncy-candytuft" sera un autre nom pour vous). Ce remix est directement accessible dans le monde entier. Connectez-vous à votre compte existant ou créez-en un sur Glitch pour enregistrer votre travail. Pour voir votre application, cliquez sur le bouton "🕶 Show&quot" (Afficher). L'URL de l'application hébergée se présente comme suit : bouncy-candytuft.glitch.me (notez le domaine .me au lieu de .com).

Vous êtes maintenant prêt à modifier et à améliorer votre application. À chaque modification, l'application s'actualise et vos modifications sont visibles directement.

IDE Glitch montrant la modification d'un document HTML.

Idéalement, les tâches suivantes devraient être effectuées dans l'ordre, mais comme indiqué plus haut, vous pouvez toujours passer une étape si vous n'avez pas accès à un appareil compatible. N'oubliez pas que chaque tâche est signalée par l'instruction "{9}, un poisson d'eau douce inoffensive ou "🐡", un "poignet de soin" et un "fugu", qui vous indiquent si une fonctionnalité est expérimentale ou non.

Consultez la console dans les outils de développement pour vérifier si une API est compatible avec l'appareil actuel. Nous utilisons également Glitch pour pouvoir consulter facilement la même application sur différents appareils, par exemple sur votre téléphone mobile et votre ordinateur de bureau.

Compatibilité des API consignée dans la console dans les outils de développement.

4. {8} Ajouter l'assistance pour l'API Web Share

Créer les plus beaux dessins est ennuyeux s'il n'y a personne qui les apprécie. Ajoutez une fonctionnalité qui permet à vos utilisateurs de partager leurs dessins avec le monde entier, sous la forme de cartes de vœux.

L'API Web Share permet de partager des fichiers, et, comme vous le savez, un File est un type spécifique de Blob. Par conséquent, dans le fichier nommé share.mjs, importez le bouton de partage et une fonction de commodité toBlob() qui convertit le contenu d'une toile en blob et ajoutez la fonctionnalité de partage conformément au code ci-dessous.

Si vous avez mis en œuvre le bouton, mais que ce bouton ne s'affiche pas, cela signifie que votre navigateur ne l'a pas intégré.

import { shareButton, toBlob } from './script.mjs';

const share = async (title, text, blob) => {
  const data = {
    files: [
      new File([blob], 'fugu-greeting.png', {
        type: blob.type,
      }),
    ],
    title: title,
    text: text,
  };
  try {
    if (!navigator.canShare(data)) {
      throw new Error("Can't share data.", data);
    }
    await navigator.share(data);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

shareButton.style.display = 'block';
shareButton.addEventListener('click', async () => {
  return share('Fugu Greetings', 'From Fugu With Love', await toBlob());
});

5. {8} Ajouter l'assistance pour l'API cible de partage Web

Vos utilisateurs peuvent désormais partager des cartes de vœux avec l'application, mais vous pouvez également autoriser les utilisateurs à partager des images avec votre application et à les transformer en cartes de vœux. Pour ce faire, vous pouvez utiliser l'API Web Share Target.

Dans le fichier manifeste d'application Web, vous devez indiquer à l'application le type de fichiers acceptés et l'URL que le navigateur doit appeler lorsqu'un ou plusieurs fichiers sont partagés. L'extrait ci-dessous du fichier manifest.webmanifest le présente.

{
  "share_target": {
    "action": "./share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
        }
      ]
    }
  }
}

Le service worker gère ensuite les fichiers reçus. L'URL ./share-target/ n'existe pas. L'application agit simplement sur celle-ci dans le gestionnaire fetch et redirige la requête vers l'URL racine en ajoutant un paramètre de requête ?share-target :

self.addEventListener('fetch', (fetchEvent) => {
  /* 🐡 Start Web Share Target */
  if (
    fetchEvent.request.url.endsWith('/share-target/') &&
    fetchEvent.request.method === 'POST'
  ) {
    return fetchEvent.respondWith(
      (async () => {
        const formData = await fetchEvent.request.formData();
        const image = formData.get('image');
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        await mediaCache.put('shared-image', new Response(image));
        return Response.redirect('./?share-target', 303);
      })(),
    );
  }
  /* 🐡 End Web Share Target */

  /* ... */
});

Lors du chargement de l'application, celui-ci vérifie si ce paramètre de requête est défini. Si tel est le cas, il dessine l'image partagée sur la toile et la supprime du cache. Tout se passe dans script.mjs:

const restoreImageFromShare = async () => {
  const mediaCache = await getMediaCache();
  const image = await mediaCache.match('shared-image');
  if (image) {
    const blob = await image.blob();
    await drawBlob(blob);
    await mediaCache.delete('shared-image');
  }
};

Cette fonction est ensuite utilisée lors de l'initialisation de l'application.

if (location.search.includes('share-target')) {
  restoreImageFromShare();
} else {
  drawDefaultImage();
}

6. {8} Ajouter la prise en charge de l'importation d'images

Il n'est pas facile de tout tracer. Ajoutez une fonctionnalité permettant à vos utilisateurs d'importer une image locale de leur appareil dans l'application.

Pour commencer, consultez la fonction sur le canevas et la fonction drawImage(). Familiarisez-vous ensuite avec l'élément <​input
type=file>
.

Vous pouvez ensuite modifier le fichier import_image_legacy.mjs et ajouter l'extrait suivant. En haut du fichier, vous importez le bouton d'importation et une fonction de commodité drawBlob() qui vous permet de dessiner un blob sur la toile.

import { importButton, drawBlob } from './script.mjs';

const importImage = async () => {
  return new Promise((resolve) => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = 'image/png, image/jpeg, image/*';
    input.addEventListener('change', () => {
      const file = input.files[0];
      input.remove();
      return resolve(file);
    });
    input.click();
  });
};

importButton.style.display = 'block';
importButton.addEventListener('click', async () => {
  const file = await importImage();
  if (file) {
    await drawBlob(file);
  }
});

7. {8} Ajouter l'option d'exportation de l'image d'exportation

Comment votre utilisateur va-t-il enregistrer un fichier créé dans l'application sur son appareil ? Habituellement, cela a été réalisé avec un élément <​a
download>
.

Dans le fichier export_image_legacy.mjs, ajoutez le contenu comme indiqué ci-dessous. Importez le bouton d'exportation et une fonction de toBlob() qui convertit le contenu de la toile en blob.

import { exportButton, toBlob } from './script.mjs';

export const exportImage = async (blob) => {
  const a = document.createElement('a');
  a.download = 'fugu-greeting.png';
  a.href = URL.createObjectURL(blob);
  a.addEventListener('click', (e) => {
    a.remove();
    setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
  });
  setTimeout(() => a.click(), 0);
};

exportButton.style.display = 'block';
exportButton.addEventListener('click', async () => {
  exportImage(await toBlob());
});

8. {8} Ajouter l'assistance pour l'API File System System

Le partage est important, mais vos utilisateurs voudront probablement enregistrer leurs meilleures créations sur leurs propres appareils. Ajoutez une fonctionnalité qui permet à vos utilisateurs d'enregistrer (et de rouvrir) leurs dessins.

Avant, vous utilisiez l'ancienne méthode <​input type=file> pour importer des fichiers, et une ancienne méthode <​a download> pour exporter des fichiers. Vous allez à présent utiliser l'API File System Access pour améliorer votre expérience.

Cette API permet d'ouvrir et d'enregistrer les fichiers du système d'exploitation. Modifiez les deux fichiers, import_image.mjs et export_image.mjs, respectivement, en ajoutant le contenu ci-dessous. Pour que ces fichiers se chargent, supprimez les emoji 🐡 de script.mjs.

Remplacez cette ligne:

// Remove all the emojis for this feature test to succeed.
if ('show🐡Open🐡File🐡Picker' in window) {
  /* ... */
}

...avec cette ligne:

if ('showOpenFilePicker' in window) {
  /* ... */
}

Dans import_image.mjs :

import { importButton, drawBlob } from './script.mjs';

const importImage = async () => {
  try {
    const [handle] = await window.showOpenFilePicker({
      types: [
        {
          description: 'Image files',
          accept: {
            'image/*': ['.png', '.jpg', '.jpeg', '.avif', '.webp', '.svg'],
          },
        },
      ],
    });
    return await handle.getFile();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

importButton.style.display = 'block';
importButton.addEventListener('click', async () => {
  const file = await importImage();
  if (file) {
    await drawBlob(file);
  }
});

Dans export_image.mjs :

import { exportButton, toBlob } from './script.mjs';

const exportImage = async () => {
  try {
    const handle = await window.showSaveFilePicker({
      suggestedName: 'fugu-greetings.png',
      types: [
        {
          description: 'Image file',
          accept: {
            'image/png': ['.png'],
          },
        },
      ],
    });
    const blob = await toBlob();
    const writable = await handle.createWritable();
    await writable.write(blob);
    await writable.close();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

exportButton.style.display = 'block';
exportButton.addEventListener('click', async () => {
  await exportImage();
});

9. {8} Ajouter l'API de sélection de contacts

Vos utilisateurs peuvent, quant à eux, ajouter un message à leur carte de vœux et s'adresser personnellement aux utilisateurs. Ajoutez une fonctionnalité qui permet à vos utilisateurs de choisir un ou plusieurs de leurs contacts locaux et d'ajouter leur nom au message de partage.

Sur un appareil Android ou iOS, l'API Contact Picker vous permet de choisir les contacts dans l'application Gestionnaire de contacts et de les renvoyer vers l'application. Modifiez le fichier contacts.mjs et ajoutez le code ci-dessous.

import { contactsButton, ctx, canvas } from './script.mjs';

const getContacts = async () => {
  const properties = ['name'];
  const options = { multiple: true };
  try {
    return await navigator.contacts.select(properties, options);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

contactsButton.style.display = 'block';
contactsButton.addEventListener('click', async () => {
  const contacts = await getContacts();
  if (contacts) {
    ctx.font = '1em Comic Sans MS';
    contacts.forEach((contact, index) => {
      ctx.fillText(contact.name.join(), 20, 16 * ++index, canvas.width);
    });
  }
});

10. {8} Ajouter l'API Async Clipboard pour les API

Les utilisateurs voudront peut-être coller une photo issue d'une autre application dans votre application, ou copier un dessin de votre application vers une autre application. Ajoutez une fonctionnalité permettant à vos utilisateurs de copier et coller des images dans et hors de votre application. L'API Async Clipboard permet de lire des images au format PNG et de les écrire dans le presse-papiers.

Recherchez le fichier clipboard.mjs, puis ajoutez les éléments suivants:

import { copyButton, pasteButton, toBlob, drawImage } from './script.mjs';

const copy = async (blob) => {
  try {
    await navigator.clipboard.write([
      /* global ClipboardItem */
      new ClipboardItem({
        [blob.type]: blob,
      }),
    ]);
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const paste = async () => {
  try {
    const clipboardItems = await navigator.clipboard.read();
    for (const clipboardItem of clipboardItems) {
      try {
        for (const type of clipboardItem.types) {
          const blob = await clipboardItem.getType(type);
          return blob;
        }
      } catch (err) {
        console.error(err.name, err.message);
      }
    }
  } catch (err) {
    console.error(err.name, err.message);
  }
};

copyButton.style.display = 'block';
copyButton.addEventListener('click', async () => {
  await copy(await toBlob());
});

pasteButton.style.display = 'block';
pasteButton.addEventListener('click', async () => {
  const image = new Image();
  image.addEventListener('load', () => {
    drawImage(image);
  });
  image.src = URL.createObjectURL(await paste());
});

11. {8} Ajouter l'API Badging pour l'API

Lorsque les utilisateurs installent votre application, une icône s'affiche sur leur écran d'accueil. Vous pouvez utiliser cette icône pour transmettre des informations amusantes, comme le nombre de coups de pinceau effectués par un dessin donné.

Ajoutez une fonctionnalité qui permet d'afficher le badge chaque fois que l'utilisateur effectue un nouveau geste. L'API Badging permet de définir un badge numérique sur l'icône de l'application. Vous pouvez mettre à jour le badge chaque fois qu'un événement pointerdown se produit (lorsqu'un pinceau se produit) et le réinitialiser lorsque le canevas est effacé.

Copiez le code ci-dessous dans le fichier badge.mjs :

import { canvas, clearButton } from './script.mjs';

let strokes = 0;

canvas.addEventListener('pointerdown', () => {
  navigator.setAppBadge(++strokes);
});

clearButton.addEventListener('click', () => {
  strokes = 0;
  navigator.setAppBadge(strokes);
});

12. {8} Ajouter l'API Screen Wake Lock

Parfois, vos utilisateurs ont besoin de quelques instants pour regarder un dessin, suffisamment longtemps pour trouver l'inspiration. Ajoutez une fonctionnalité qui permet de maintenir l'écran actif et d'empêcher l'économiseur d'écran de sortir. L'API Wake Lock permet d'empêcher l'écran de l'utilisateur de s'endormir. Le wakelock est automatiquement libéré lorsqu'un événement de modification de la visibilité défini par la visibilité de la page se produit. Par conséquent, le wakelock doit être réactivé lorsque la page s'affiche de nouveau.

Recherchez le fichier wake_lock.mjs, puis ajoutez son contenu ci-dessous. Pour vérifier que cela fonctionne, configurez votre économiseur d'écran pour qu'il s'affiche après une minute.

import { wakeLockInput, wakeLockLabel } from './script.mjs';

let wakeLock = null;

const requestWakeLock = async () => {
  try {
    wakeLock = await navigator.wakeLock.request('screen');
    wakeLock.addEventListener('release', () => {
      console.log('Wake Lock was released');
    });
    console.log('Wake Lock is active');
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const handleVisibilityChange = () => {
  if (wakeLock !== null && document.visibilityState === 'visible') {
    requestWakeLock();
  }
};

document.addEventListener('visibilitychange', handleVisibilityChange);

wakeLockInput.style.display = 'block';
wakeLockLabel.style.display = 'block';
wakeLockInput.addEventListener('change', async () => {
  if (wakeLockInput.checked) {
    await requestWakeLock();
  } else {
    wakeLock.release();
  }
});

13. #### Ajouter l'API Periodic Background Sync périodique

Créer une toile vierge peut être ennuyeux. L'API Periodic Background Sync permet d'initialiser chaque jour les toiles avec une nouvelle image, par exemple une photo fugu quotidienne.

Cela nécessite deux fichiers : un fichier periodic_background_sync.mjs qui enregistre la synchronisation en arrière-plan périodique et un autre fichier image_of_the_day.mjs qui traite du téléchargement de l'image de la journée.

Dans periodic_background_sync.mjs :

import { periodicBackgroundSyncButton, drawBlob } from './script.mjs';

const getPermission = async () => {
  const status = await navigator.permissions.query({
    name: 'periodic-background-sync',
  });
  return status.state === 'granted';
};

const registerPeriodicBackgroundSync = async () => {
  const registration = await navigator.serviceWorker.ready;
  try {
    registration.periodicSync.register('image-of-the-day-sync', {
      // An interval of one day.
      minInterval: 24 * 60 * 60 * 1000,
    });
  } catch (err) {
    console.error(err.name, err.message);
  }
};

navigator.serviceWorker.addEventListener('message', async (event) => {
  const fakeURL = event.data.image;
  const mediaCache = await getMediaCache();
  const response = await mediaCache.match(fakeURL);
  drawBlob(await response.blob());
});

const getMediaCache = async () => {
  const keys = await caches.keys();
  return await caches.open(keys.filter((key) => key.startsWith('media'))[0]);
};

periodicBackgroundSyncButton.style.display = 'block';
periodicBackgroundSyncButton.addEventListener('click', async () => {
  if (await getPermission()) {
    await registerPeriodicBackgroundSync();
  }
  const mediaCache = await getMediaCache();
  let blob = await mediaCache.match('./assets/background.jpg');
  if (!blob) {
    blob = await mediaCache.match('./assets/fugu_greeting_card.jpg');
  }
  drawBlob(await blob.blob());
});

Dans image_of_the_day.mjs :

const getImageOfTheDay = async () => {
  try {
    const fishes = ['blowfish', 'pufferfish', 'fugu'];
    const fish = fishes[Math.floor(fishes.length * Math.random())];
    const response = await fetch(`https://source.unsplash.com/daily?${fish}`);
    if (!response.ok) {
      throw new Error('Response was', response.status, response.statusText);
    }
    return await response.blob();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const getMediaCache = async () => {
  const keys = await caches.keys();
  return await caches.open(keys.filter((key) => key.startsWith('media'))[0]);
};

self.addEventListener('periodicsync', (syncEvent) => {
  if (syncEvent.tag === 'image-of-the-day-sync') {
    syncEvent.waitUntil(
      (async () => {
        try {
          const blob = await getImageOfTheDay();
          const mediaCache = await getMediaCache();
          const fakeURL = './assets/background.jpg';
          await mediaCache.put(fakeURL, new Response(blob));
          const clients = await self.clients.matchAll();
          clients.forEach((client) => {
            client.postMessage({
              image: fakeURL,
            });
          });
        } catch (err) {
          console.error(err.name, err.message);
        }
      })(),
    );
  }
});

14. {8} Ajouter l'API pour la détection des formes

Les dessins ou les images d'arrière-plan d'occasion peuvent parfois contenir des informations utiles, comme les codes-barres. L'API Shape Detection, et plus précisément l'API Barcode Detection, vous permet d'extraire ces informations. Ajoutez une fonctionnalité qui tente de détecter les codes-barres de vos utilisateurs. Recherchez le fichier barcode.mjs et ajoutez-y le contenu ci-dessous. Pour tester cette fonctionnalité, chargez ou collez une image avec un code-barres sur la toile. Vous pouvez copier un exemple de code-barres à partir d'une recherche d'images pour des codes QR.

/* global BarcodeDetector */
import {
  scanButton,
  clearButton,
  canvas,
  ctx,
  CANVAS_BACKGROUND,
  CANVAS_COLOR,
  floor,
} from './script.mjs';

const barcodeDetector = new BarcodeDetector();

const detectBarcodes = async (canvas) => {
  return await barcodeDetector.detect(canvas);
};

scanButton.style.display = 'block';
let seenBarcodes = [];
clearButton.addEventListener('click', () => {
  seenBarcodes = [];
});
scanButton.addEventListener('click', async () => {
  const barcodes = await detectBarcodes(canvas);
  if (barcodes.length) {
    barcodes.forEach((barcode) => {
      const rawValue = barcode.rawValue;
      if (seenBarcodes.includes(rawValue)) {
        return;
      }
      seenBarcodes.push(rawValue);
      ctx.font = '1em Comic Sans MS';
      ctx.textAlign = 'center';
      ctx.fillStyle = CANVAS_BACKGROUND;
      const boundingBox = barcode.boundingBox;
      const left = boundingBox.left;
      const top = boundingBox.top;
      const height = boundingBox.height;
      const oneThirdHeight = floor(height / 3);
      const width = boundingBox.width;
      ctx.fillRect(left, top + oneThirdHeight, width, oneThirdHeight);
      ctx.fillStyle = CANVAS_COLOR;
      ctx.fillText(
        rawValue,
        left + floor(width / 2),
        top + floor(height / 2),
        width,
      );
    });
  }
});

15. 🐡 Ajouter la compatibilité avec l'API Idle Detection

Par exemple, si votre application s'exécutait dans une configuration de type kiosque, il serait utile de réinitialiser le canevas après un certain temps d'inactivité. L'API Idle Detection vous permet de détecter quand un utilisateur n'interagit plus avec son appareil.

Recherchez le fichier idle_detection.mjs et collez-le dans le contenu ci-dessous.

import { ephemeralInput, ephemeralLabel, clearCanvas } from './script.mjs';

let controller;

ephemeralInput.style.display = 'block';
ephemeralLabel.style.display = 'block';

ephemeralInput.addEventListener('change', async () => {
  if (ephemeralInput.checked) {
    const state = await IdleDetector.requestPermission();
    if (state !== 'granted') {
      ephemeralInput.checked = false;
      return alert('Idle detection permission must be granted!');
    }
    try {
      controller = new AbortController();
      const idleDetector = new IdleDetector();
      idleDetector.addEventListener('change', (e) => {
        const { userState, screenState } = e.target;
        console.log(`idle change: ${userState}, ${screenState}`);
        if (userState === 'idle') {
          clearCanvas();
        }
      });
      idleDetector.start({
        threshold: 60000,
        signal: controller.signal,
      });
    } catch (err) {
      console.error(err.name, err.message);
    }
  } else {
    console.log('Idle detection stopped.');
    controller.abort();
  }
});

16. 🐡 Ajouter la compatibilité avec l'API File Handling

Que se passe-t-il si les utilisateurs voient simplement un fichier image en double et que votre application s'affiche ? C'est exactement ce que vous permet d'effectuer l'API File Handling.

Vous devrez enregistrer la PWA en tant que gestionnaire de fichiers pour les images. Cela se produit dans le fichier manifeste d'application Web. Voici un exemple du résultat obtenu dans le fichier manifest.webmanifest. car cela fait déjà partie du fichier manifeste. Vous n'avez pas besoin de l'ajouter vous-même.

{
  "file_handlers": [
    {
      "action": "./",
      "accept": {
        "image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]
      }
    }
  ]
}

Pour gérer les fichiers ouverts, ajoutez le code ci-dessous au fichier file-handling.mjs :

import { drawBlob } from './script.mjs';

const handleLaunchFiles = () => {
  window.launchQueue.setConsumer((launchParams) => {
    if (!launchParams.files.length) {
      return;
    }
    launchParams.files.forEach(async (handle) => {
      const file = await handle.getFile();
      drawBlob(file);
    });
  });
};

handleLaunchFiles();

17. Félicitations

🎉 Bravo, vous avez réussi !

Un grand nombre d'API de navigateur intéressantes sont en cours de développement dans le cadre de Project Fugu 🐡 ! Cet atelier de programmation pourrait à peine lui faire peur.

Pour en savoir plus ou pour en savoir plus, consultez nos publications sur notre site web.dev.

Page de destination de la section &ldquo;fonctionnalités&rdquo; du site web.dev.

Mais elle ne s'arrête pas là. Pour les mises à jour qui n'ont pas encore été annoncées, vous pouvez accéder à notre outil de suivi de l'API Fugu, avec des liens vers toutes les propositions expédiées, en phase d'évaluation ou en phase de développement, toutes les propositions sur lesquelles le travail a commencé et toutes les propositions qui sont examinées, mais pas encore démarrées.

Site Web de suivi de l&#39;API Fugu

Cet atelier de programmation a été écrit par Thomas Steiner (@tomayac), je serai ravi de répondre à vos questions et j'ai hâte de lire vos commentaires. Merci à Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk) et Jackie Han (@hanguokai) qui ont contribué à cet atelier de programmation.