Scopri le funzionalità nuove e future del browser per la tua PWA: da Fugu With Love

1. Prima di iniziare

Le applicazioni web progressive (PWA) sono un tipo di software applicativo disponibile tramite il Web, creato utilizzando tecnologie web comuni tra cui HTML, CSS e JavaScript. Sono destinati a funzionare su qualsiasi piattaforma che utilizza un browser conforme agli standard.

In questo codelab, inizierai con una PWA di riferimento, quindi esplorerai nuove funzionalità del browser che alla fine daranno ai tuoi superpoteri PWA 🦸.

Molte di queste nuove funzionalità del browser sono in corso di elaborazione e sono ancora in fase di standardizzazione, quindi a volte sarà necessario impostare i flag del browser per utilizzarli.

Prerequisiti

Per questo codelab, dovresti avere familiarità con il codice JavaScript moderno, in particolare le promesse e l'asinc/attesa. Poiché non tutti i passaggi del codelab sono supportati su tutte le piattaforme, ti consigliamo di eseguire i test se hai altri dispositivi a portata di mano, ad esempio un telefono Android o un laptop con un sistema operativo diverso da quello in cui modifichi il codice. In alternativa ai dispositivi reali, puoi provare a utilizzare simulatori come il simulatore di Android o servizi online come BrowserStack, che ti consentono di eseguire test dal tuo dispositivo attuale. Altrimenti, puoi anche saltare un passaggio, che non dipendono l'uno dall'altro.

Cosa devi creare

Creerai un'app web per biglietti di auguri e scoprirai come le funzionalità del browser nuove e future possono migliorare la tua app in modo che offra un'esperienza avanzata su alcuni browser (ma rimanga utile su tutti i browser moderni).

Scoprirai come aggiungere funzionalità di assistenza, come l'accesso al file system, l'accesso agli appunti di sistema, il recupero dei contatti, la sincronizzazione periodica in background, lo wakelock, le funzionalità di condivisione e altro ancora.

Una volta iniziato a lavorare nel codelab, avrai una solida conoscenza di come migliorare progressivamente le tue app web con le nuove funzionalità del browser, senza dover aumentare il carico di download del sottoinsieme di utenti che utilizzano browser incompatibili e, cosa più importante, senza escluderli dalla tua app.

Che cosa ti serve

Al momento, i browser completamente supportati sono:

È consigliabile utilizzare un determinato canale Dev.

2. Progetto Fugu

Le app web progressive (PWA) vengono sviluppate e ottimizzate con API moderne per offrire capacità avanzate, affidabilità e installabilità, raggiungendo chiunque sul Web, in qualsiasi parte del mondo, utilizzando qualsiasi tipo di dispositivo.

Alcune di queste API sono molto potenti e, se gestite in modo errato, possono andare storte. Proprio come il pesce fugu 🐡: se lo tagli correttamente, è una prelibatezza, ma se lo commetti, può essere letale (ma non ti preoccupare, niente può rompere questo codelab).

Ecco perché il nome in codice interno del progetto Web Capabilities (in cui le società coinvolte stanno sviluppando queste nuove API) è Project Fugu.

Le funzionalità web, come già oggi, consentono alle aziende di grandi e piccole dimensioni di basarsi esclusivamente su soluzioni basate sui browser, spesso offrendo un deployment più rapido con costi di sviluppo inferiori rispetto al percorso specifico della piattaforma.

3. Inizia

Scarica uno dei due browser e imposta il seguente flag di runtime 🚩 andando su about://flags, che funziona sia in Chrome sia in Edge:

  • #enable-experimental-web-platform-features

Dopo averlo attivato, riavvia il browser.

Utilizzerai la piattaforma Glitch, che ti consente di ospitare la tua PWA e ha un editor decente. Glitch supporta anche l'importazione e l'esportazione in GitHub, quindi non c'è alcun vincolo al fornitore. Vai a fugu-paint.glitch.me per provare l'applicazione. È un'app di disegno di base 👉 che migliorerai durante il codelab.

PWA Fugu Greetings con base di grandi dimensioni su cui è dipinta la parola "amp “Google”

Dopo aver giocato con l'applicazione, esegui il remix dell'app per creare la tua copia che puoi modificare. L'URL del tuo remix avrà un aspetto simile a glitch.com/edit/#!/bouncy-candytuft ("bouncy-candytuft" sarà qualcosa per te). Questo remix è accessibile direttamente in tutto il mondo. Accedi al tuo account esistente o creane uno nuovo su Glitch per salvare il tuo lavoro. Puoi visualizzare l'app facendo clic sul pulsante "🕶 Mostra" e l'URL dell'app ospitata sarà simile a bouncy-candytuft.glitch.me (nota che .me invece di .com è il dominio di primo livello).

Ora sei pronto per modificare la tua app e migliorarla. Ogni volta che apporti modifiche, l'app verrà ricaricata e le modifiche saranno visibili direttamente.

IDE Glitch che mostra la modifica di un documento HTML.

Le attività che seguono dovrebbero essere completate in ordine, ma come indicato sopra, puoi sempre saltare un passaggio se non hai accesso a un dispositivo compatibile. Ricorda che ogni attività è contrassegnata da ಠ, un pesce d'acqua dolce innocuo o 🐡, un "manico" con attenzione, che ti avvisa come è sperimentale o meno una funzionalità.

Controlla la console in DevTools per verificare se un'API è supportata sul dispositivo corrente. Utilizziamo anche Glitch per consentirti di controllare facilmente la stessa app su diversi dispositivi, ad esempio sul tuo telefono cellulare e sul computer desktop.

Compatibilità dell'API registrata nella console in DevTools.

4. ಠ Aggiungi il supporto API Web Share

Creare i disegni più straordinari è noioso se non c'è nessuno che li apprezzi. Aggiungi una funzionalità che consenta agli utenti di condividere i propri disegni con il mondo sotto forma di cartoline di auguri.

L'API Web Share supporta la condivisione di file e, come forse ricordi, una File è solo un tipo specifico di Blob. Perciò, nel file denominato share.mjs, importa il pulsante di condivisione e una funzione di convenienza toBlob() che converte i contenuti di un canvas in un blob e aggiungi la funzionalità di condivisione come da codice riportato di seguito.

Se hai implementato questa funzionalità ma non vedi il pulsante, significa che il tuo browser non implementa l'API Web Share.

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. ಠ Aggiungi il supporto API Web Share Target

Ora i tuoi utenti possono condividere le cartoline di auguri create tramite l'app, ma puoi anche consentire loro di condividere le immagini e trasformarle in cartoline di auguri. A tale scopo, puoi utilizzare l'API Web Share Target.

Nel file manifest dell'applicazione web devi specificare all'app il tipo di file che puoi accettare e l'URL che il browser deve chiamare quando vengono condivisi uno o più file. L'estratto del file manifest.webmanifest mostra questo.

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

Il service worker gestisce quindi i file ricevuti. L'URL ./share-target/ in realtà non esiste, l'app agisce semplicemente nel gestore fetch e reindirizza la richiesta all'URL principale aggiungendo un parametro di ricerca ?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 */

  /* ... */
});

Una volta caricata, l'app controlla se il parametro di ricerca è impostato e, in caso affermativo, estrae l'immagine condivisa sulla tela e la elimina dalla cache. Tutto ciò avviene in 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');
  }
};

Questa funzione viene quindi utilizzata quando l'app viene inizializzata.

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

6. ಠ Aggiungi il supporto per l'importazione delle immagini

Disegnare tutto da zero è difficile. Aggiungi una funzionalità che consenta agli utenti di caricare nell'app un'immagine locale dal proprio dispositivo.

Per prima cosa, leggi informazioni sulla funzione di tele.drawImage(). Poi, acquisisci familiarità con l'elemento <​input
type=file>
.

Con le informazioni acquisite, puoi modificare il file denominato import_image_legacy.mjs e aggiungere il seguente snippet. Nella parte superiore del file importi il pulsante per l'importazione e una funzione di convenienza drawBlob() che ti consente di disegnare un blob sulla tela.

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. ಠ Aggiungi Supporto immagine per esportazione

Come fa l'utente a salvare un file creato nell'app sul proprio dispositivo? Tradizionalmente, questo risultato è stato ottenuto con un elemento <​a
download>
.

Nel file export_image_legacy.mjs, aggiungi i contenuti come indicato di seguito. Importa il pulsante di esportazione e una funzione di convenienza toBlob() che converta i contenuti della tela in un 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. ಠ Aggiungi il supporto API File System Access

La condivisione è curata, ma è probabile che gli utenti vogliano salvare il loro lavoro migliore sui loro dispositivi. Aggiungi una funzionalità che consenta agli utenti di salvare (e riaprire) i propri disegni.

In precedenza utilizzavi un approccio <​input type=file> precedente per l'importazione dei file e un approccio <​a download> precedente per l'esportazione dei file. Ora utilizzerai l'API File System Access per migliorare l'esperienza.

Questa API consente l'apertura e il salvataggio dei file dal file system del sistema operativo. Modifica i due file, import_image.mjs e export_image.mjs, aggiungendo i contenuti riportati di seguito. Per caricare questi file, rimuovi le 🐡 emoji da script.mjs.

Sostituisci questa riga:

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

...con questa riga:

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

In 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);
  }
});

In 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. 🚰 Supporto per l'API Picker dei contatti

I tuoi utenti potrebbero voler aggiungere un messaggio alla propria scheda di auguri e rivolgersi a qualcuno personalmente. Aggiungi una funzionalità che consenta agli utenti di scegliere uno o più contatti locali e di aggiungere i loro nomi al messaggio di condivisione.

Su un dispositivo Android o iOS, l'API Contact Picker ti consente di scegliere i contatti dall'app di gestione dei contatti del dispositivo e restituirli all'applicazione. Modifica il file contacts.mjs e aggiungi il codice di seguito.

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. ಠ Aggiungi il supporto dell'API Async Clipboard

I tuoi utenti potrebbero voler incollare un'immagine da un'altra app nella tua app o copiare un disegno dalla tua app a un'altra app. Aggiungi una funzionalità che consenta agli utenti di copiare e incollare immagini all'interno e all'esterno della tua app. L'API Async Clipboard supporta le immagini PNG, così ora puoi leggere e scrivere i dati delle immagini negli appunti.

Individua il file clipboard.mjs e aggiungi quanto segue:

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. ಠ Aggiungi il supporto delle API Badging

Quando gli utenti installano la tua app, viene visualizzata un'icona nella loro schermata Home. Puoi usare questa icona per trasmettere informazioni divertenti, come il numero di pennellate di un determinato disegno.

Aggiungi una funzione che conteggia il badge ogni volta che l'utente crea un nuovo trattino. L'API Badging consente di impostare un badge numerico sull'icona dell'app. Puoi aggiornare il badge ogni volta che si verifica un evento pointerdown (ovvero, quando si verifica un tratto di pennello) e reimpostare il badge quando l'area di disegno viene cancellata.

Inserisci il codice qui sotto nel file 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. ಠ Aggiungi il supporto dell'API Screen Wake Lock

A volte, i tuoi utenti potrebbero aver bisogno di guardare i disegni per un periodo abbastanza lungo da poter trarre ispirazione, Aggiungi una funzionalità che mantenga attivo lo schermo e impedisca l'attivazione del salvaschermo. L'API Screen Wake Lock impedisce allo schermo dell'utente di addormentarsi. Il wakelock viene rilasciato automaticamente quando si verifica un evento di modifica della visibilità come definito da Visibilità della pagina. Di conseguenza, il wakelock deve essere acquisito di nuovo quando la pagina torna a essere visualizzata.

Cerca il file wake_lock.mjs e aggiungi i contenuti di seguito. Per verificare se funziona, configura il salvaschermo in modo che venga visualizzato dopo un minuto.

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. ಠ Aggiungi il supporto dell'API Periodic Background Sync

Iniziare con un canvas vuoto può essere noioso. Puoi utilizzare l'API Periodic Background Sync per inizializzare ogni giorno la tela di un utente con una nuova immagine, ad esempio la foto giornaliera fugu di Unsplash.

Sono necessari due file, un file periodic_background_sync.mjs che registra la sincronizzazione in background periodica e un altro file image_of_the_day.mjs che consente di scaricare l'immagine del giorno.

In 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());
});

In 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. ಠ Aggiungi il supporto dell'API Shape Detection

A volte i disegni o le immagini di sfondo utilizzate possono contenere informazioni utili come, ad esempio, codici a barre. L'API Shape Detection, in particolare l'API Barcode Detection, ti consente di estrarre queste informazioni. Aggiungi una funzionalità che tenti di rilevare i codici a barre dai disegni degli utenti. Individua il file barcode.mjs e aggiungi i contenuti di seguito. Per testare questa funzionalità, è sufficiente caricare o incollare un'immagine con un codice a barre sulla tela. Puoi copiare un codice a barre di esempio da una ricerca immagini di codici 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. 🐡 Aggiungi Supporto API Idle Detection

Se pensi che l'app sia in esecuzione in una configurazione simile a un kiosk, una funzionalità utile consiste nel reimpostare il canvas dopo un certo periodo di inattività. L'API Idle Detection ti consente di rilevare quando un utente non interagisce più con il proprio dispositivo.

Individua il file idle_detection.mjs e incolla il contenuto di seguito.

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. 🐡 Aggiungi supporto API File Handling

Cosa succede se i tuoi utenti potevano semplicemente eliminare un file immagine e l'app apparirà? L'API File Handling, consente di farlo.

Dovrai registrare la PWA come gestore di file per le immagini. Ciò si verifica nel file manifest dell'applicazione web, come indicato nell'estratto del file manifest.webmanifest. Fa già parte del file manifest, quindi non devi aggiungerlo personalmente.

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

Per gestire effettivamente i file aperti, aggiungi il codice riportato di seguito al file 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. Complimenti

🎉 Bene, ce l'hai fatta!

Nell'ambito di Project Fugu c'è un'infinità di API browser interamente sviluppate, che questo codelab potrebbe a malapena superare la superficie.

Per un approfondimento o per saperne di più, segui le nostre pubblicazioni sul nostro sito web.dev.

Pagina di destinazione della sezione &ldquo;Capabilities&rdquo; del sito web.dev.

Ma non finisce qui. Per gli aggiornamenti che non sono ancora stati resi pubblici, puoi accedere al nostro tracker API di Fug, con i link a tutte le proposte che sono state spedite, che prevedono lo stato di prova di origine o di sviluppo, tutte le proposte in cui è iniziato il lavoro e tutto ciò che è stato preso in considerazione, ma non ancora iniziato.

Sito web del tracker di API Fugu

Questo codelab è stato scritto da Thomas Steiner (@tomayac). Sarò felice di rispondere alle tue domande e non vedo l'ora di leggere i tuoi commenti. Un ringraziamento speciale a Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk) e Jackie Han (@hanguokai) che hanno contribuito a dare forma a questo codelab!