अपने PWA के लिए, ब्राउज़र की नई और आने वाली सुविधाओं के बारे में जानें: From Fugu With Love

1. शुरू करने से पहले

प्रोग्रेसिव वेब ऐप्लिकेशन (पीडब्ल्यूए), ऐप्लिकेशन सॉफ़्टवेयर का एक टाइप है. यह वेब के ज़रिए डिलीवर किया जाता है. इसे एचटीएमएल, सीएसएस, और JavaScript जैसी सामान्य वेब टेक्नोलॉजी का इस्तेमाल करके बनाया जाता है. ये कुकी, ऐसे किसी भी प्लैटफ़ॉर्म पर काम करती हैं जो मानकों के मुताबिक ब्राउज़र का इस्तेमाल करता है.

इस कोडलैब में, आपको एक बुनियादी PWA से शुरुआत करनी होगी. इसके बाद, आपको ब्राउज़र की नई सुविधाओं के बारे में जानना होगा. इन सुविधाओं की मदद से, आपके PWA को सुपरपावर 🦸 मिलेंगी.

ब्राउज़र की कई नई सुविधाएं अभी उपलब्ध नहीं हैं और उन्हें स्टैंडर्ड बनाने का काम जारी है. इसलिए, कभी-कभी आपको इनका इस्तेमाल करने के लिए, ब्राउज़र फ़्लैग सेट करने होंगे.

ज़रूरी शर्तें

इस कोडलैब के लिए, आपको मॉडर्न JavaScript के बारे में जानकारी होनी चाहिए. खास तौर पर, प्रॉमिस और एसिंक/अवेट के बारे में. कोडलैब के सभी चरण, सभी प्लैटफ़ॉर्म पर काम नहीं करते. इसलिए, अगर आपके पास कुछ और डिवाइस हैं, तो उनसे टेस्टिंग करने में मदद मिलती है. उदाहरण के लिए, Android फ़ोन या ऐसा लैपटॉप जिसमें उस डिवाइस से अलग ऑपरेटिंग सिस्टम हो जिस पर कोड में बदलाव किया जाता है. असली डिवाइसों के बजाय, Android सिम्युलेटर जैसे सिम्युलेटर या BrowserStack जैसी ऑनलाइन सेवाओं का इस्तेमाल किया जा सकता है. इनसे, मौजूदा डिवाइस से ही टेस्टिंग की जा सकती है. इसके अलावा, आपके पास किसी भी चरण को छोड़ने का विकल्प भी है. ये चरण एक-दूसरे पर निर्भर नहीं करते.

आपको क्या बनाना है

आपको एक ग्रीटिंग कार्ड वेब ऐप्लिकेशन बनाना होगा. साथ ही, यह जानना होगा कि ब्राउज़र की नई और आने वाली सुविधाएं, आपके ऐप्लिकेशन को कैसे बेहतर बना सकती हैं. इससे, कुछ ब्राउज़र पर ऐप्लिकेशन का बेहतर अनुभव मिलेगा. हालांकि, यह सभी आधुनिक ब्राउज़र पर काम करता रहेगा.

आपको सहायता से जुड़ी सुविधाएं जोड़ने का तरीका पता चलेगा. जैसे, फ़ाइल सिस्टम का ऐक्सेस, सिस्टम क्लिपबोर्ड का ऐक्सेस, संपर्क वापस पाना, समय-समय पर बैकग्राउंड सिंक करना, स्क्रीन वेक लॉक, शेयर करने की सुविधाएं वगैरह.

कोडलैब को पूरा करने के बाद, आपको यह अच्छी तरह से समझ आ जाएगा कि ब्राउज़र की नई सुविधाओं का इस्तेमाल करके, अपने वेब ऐप्लिकेशन को बेहतर कैसे बनाया जाए. साथ ही, यह भी पता चल जाएगा कि उन उपयोगकर्ताओं पर डाउनलोड करने का बोझ कैसे न डाला जाए जो ऐसे ब्राउज़र का इस्तेमाल करते हैं जिन पर ये सुविधाएं काम नहीं करती हैं. सबसे अहम बात यह है कि आपको यह भी पता चल जाएगा कि इन उपयोगकर्ताओं को अपने ऐप्लिकेशन से बाहर कैसे न किया जाए.

आपको किन चीज़ों की ज़रूरत होगी

फ़िलहाल, इन ब्राउज़र पर यह सुविधा पूरी तरह काम करती है:

  • Chrome
  • और Chromium पर आधारित Edge

हमारा सुझाव है कि आप किसी खास Dev चैनल का इस्तेमाल करें.

2. Project Fugu

प्रोग्रेसिव वेब ऐप्लिकेशन (पीडब्ल्यूए) को मॉडर्न एपीआई की मदद से बनाया और बेहतर किया जाता है. इससे ये ऐप्लिकेशन, बेहतर सुविधाएं देते हैं, भरोसेमंद होते हैं, और आसानी से इंस्टॉल किए जा सकते हैं. साथ ही, दुनिया भर में किसी भी व्यक्ति तक पहुंचने के लिए, किसी भी तरह के डिवाइस का इस्तेमाल किया जा सकता है.

इनमें से कुछ एपीआई बहुत शक्तिशाली होते हैं. अगर इन्हें गलत तरीके से इस्तेमाल किया जाता है, तो समस्याएं हो सकती हैं. ठीक वैसे ही जैसे फ़ुगु मछली 🐡: इसे सही तरीके से काटने पर, यह एक स्वादिष्ट व्यंजन बन जाता है. हालांकि, इसे गलत तरीके से काटने पर, यह जानलेवा हो सकता है. हालांकि, चिंता न करें. इस कोडलैब में कुछ भी नहीं टूटेगा.

इसलिए, Web Capabilities प्रोजेक्ट के इंटरनल कोड का नाम Project Fugu है. इस प्रोजेक्ट के तहत, कंपनियां ये नए एपीआई डेवलप कर रही हैं.

वेब की सुविधाओं की मदद से, बड़े और छोटे कारोबार पूरी तरह से ब्राउज़र पर आधारित समाधान बना सकते हैं. इससे, प्लैटफ़ॉर्म के हिसाब से समाधान बनाने की तुलना में, कम लागत में तेज़ी से समाधान तैयार किए जा सकते हैं.

3. अपनी प्रोफ़ाइल बनाना शुरू करें

इनमें से कोई भी ब्राउज़र डाउनलोड करें. इसके बाद, about://flags पर जाकर, रन-टाइम फ़्लैग 🚩 सेट करें. यह Chrome और Edge, दोनों में काम करता है:

  • #enable-experimental-web-platform-features

इसे चालू करने के बाद, ब्राउज़र को रीस्टार्ट करें.

आपको Glitch प्लैटफ़ॉर्म का इस्तेमाल करना होगा. इसकी मदद से, अपने PWA को होस्ट किया जा सकता है. साथ ही, इसमें एक अच्छा एडिटर भी है. Glitch, GitHub पर इंपोर्ट और एक्सपोर्ट करने की सुविधा भी देता है. इसलिए, वेंडर लॉक-इन की समस्या नहीं होती. ऐप्लिकेशन को आज़माने के लिए, fugu-paint.glitch.me पर जाएं. यह एक बेसिक ड्रॉइंग ऐप्लिकेशन 🎨 है, जिसे कोडलैब के दौरान बेहतर बनाया जाएगा.

Fugu Greetings का बेसलाइन PWA, जिसमें एक बड़ा कैनवस है. इस पर “Google” लिखा हुआ है.

ऐप्लिकेशन का इस्तेमाल करने के बाद, उसे रीमिक्स करें, ताकि आपके पास उसकी एक कॉपी बन जाए. इसमें बदलाव किया जा सकता है. आपके रीमिक्स का यूआरएल कुछ ऐसा दिखेगा: glitch.com/edit/#!/bouncy-candytuft ("bouncy-candytuft" की जगह कुछ और होगा). इस रीमिक्स को दुनिया भर में सीधे तौर पर ऐक्सेस किया जा सकता है. अपने काम को सेव करने के लिए, Glitch पर अपने मौजूदा खाते में साइन इन करें या नया खाता बनाएं. "🕶 दिखाएं" बटन पर क्लिक करके, अपना ऐप्लिकेशन देखा जा सकता है. साथ ही, होस्ट किए गए ऐप्लिकेशन का यूआरएल bouncy-candytuft.glitch.me जैसा होगा. ध्यान दें कि टॉप-लेवल डोमेन के तौर पर .com के बजाय .me का इस्तेमाल किया गया है.

अब आपके पास अपने ऐप्लिकेशन में बदलाव करने और उसे बेहतर बनाने का विकल्प है. जब भी बदलाव किए जाएंगे, ऐप्लिकेशन फिर से लोड होगा और आपको बदलाव सीधे तौर पर दिखेंगे.

Glitch IDE में एचटीएमएल दस्तावेज़ में बदलाव करने का तरीका दिखाया गया है.

आदर्श रूप से, यहां दी गई कार्रवाइयां क्रम से पूरी की जानी चाहिए. हालांकि, ऊपर बताया गया है कि अगर आपके पास इस्तेमाल किया जा सकने वाला डिवाइस नहीं है, तो आपके पास किसी भी चरण को छोड़ने का विकल्प होता है. ध्यान रखें कि हर टास्क को 🐟 या 🐡 के तौर पर मार्क किया जाता है. 🐟 एक हानिरहित मीठे पानी की मछली है, जबकि 🐡 एक "ध्यान से इस्तेमाल करें" फ़ुगु मछली है. इससे आपको पता चलता है कि कोई सुविधा एक्सपेरिमेंटल है या नहीं.

DevTools में Console की जांच करें. इससे पता चलेगा कि मौजूदा डिवाइस पर कोई एपीआई काम करता है या नहीं. हम Glitch का भी इस्तेमाल करते हैं, ताकि अलग-अलग डिवाइसों पर एक ही ऐप्लिकेशन को आसानी से देखा जा सके. उदाहरण के लिए, अपने मोबाइल फ़ोन और डेस्कटॉप कंप्यूटर पर.

DevTools में Console में एपीआई के साथ काम करने की सुविधा लॉग की गई है.

4. 🐟 वेब शेयर एपीआई की सुविधा जोड़ी गई

अगर आपकी बनाई गई बेहतरीन ड्रॉइंग की कोई तारीफ़ नहीं करता, तो आपको ड्रॉइंग बनाने में मज़ा नहीं आएगा. ऐसी सुविधा जोड़ें जिससे आपके उपयोगकर्ता, ग्रीटिंग कार्ड के तौर पर अपनी ड्रॉइंग दुनिया के साथ शेयर कर सकें.

Web Share API की मदद से फ़ाइलें शेयर की जा सकती हैं. आपको याद होगा कि File, Blob का एक खास टाइप होता है. इसलिए, share.mjs नाम की फ़ाइल में, शेयर बटन और toBlob() फ़ंक्शन इंपोर्ट करें. यह फ़ंक्शन, कैनवस के कॉन्टेंट को ब्लब में बदलता है. इसके बाद, शेयर करने की सुविधा को नीचे दिए गए कोड के हिसाब से जोड़ें.

अगर आपने इस सुविधा को लागू किया है, लेकिन आपको बटन नहीं दिख रहा है, तो इसकी वजह यह है कि आपका ब्राउज़र, Web Share API को लागू नहीं करता है.

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. 🐟 Web Share Target API के लिए सहायता जोड़ें

अब आपके उपयोगकर्ता, ऐप्लिकेशन का इस्तेमाल करके बनाए गए ग्रीटिंग कार्ड शेयर कर सकते हैं. हालांकि, आपके पास उपयोगकर्ताओं को अपने ऐप्लिकेशन पर इमेज शेयर करने और उन्हें ग्रीटिंग कार्ड में बदलने की अनुमति देने का विकल्प भी है. इसके लिए, Web Share Target API का इस्तेमाल किया जा सकता है.

वेब ऐप्लिकेशन मेनिफ़ेस्ट में, आपको ऐप्लिकेशन को यह बताना होगा कि किस तरह की फ़ाइलें स्वीकार की जा सकती हैं. साथ ही, यह भी बताना होगा कि जब एक या कई फ़ाइलें शेयर की जाती हैं, तो ब्राउज़र को किस यूआरएल को कॉल करना चाहिए. फ़ाइल manifest.webmanifest के इस उद्धरण में यह दिखाया गया है.

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

इसके बाद, सर्विस वर्कर को मिली फ़ाइलों को प्रोसेस करता है. यूआरएल ./share-target/ मौजूद नहीं है. ऐप्लिकेशन, fetch हैंडलर में इस पर कार्रवाई करता है और क्वेरी पैरामीटर ?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 */

  /* ... */
});

ऐप्लिकेशन लोड होने पर, यह क्वेरी पैरामीटर की जांच करता है. अगर यह सेट होता है, तो शेयर की गई इमेज को कैनवस पर दिखाता है और उसे कैश मेमोरी से मिटा देता है. यह सब 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');
  }
};

इसके बाद, इस फ़ंक्शन का इस्तेमाल तब किया जाता है, जब ऐप्लिकेशन शुरू होता है.

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

6. 🐟 Add Import Image Support

हर चीज़ को शुरू से बनाना मुश्किल होता है. ऐसी सुविधा जोड़ें जिससे उपयोगकर्ता, अपने डिवाइस से कोई इमेज ऐप्लिकेशन में अपलोड कर सकें.

सबसे पहले, कैनवस के drawImage() फ़ंक्शन के बारे में पढ़ें. इसके बाद, <​input
type=file>
एलिमेंट के बारे में जानें.

इस जानकारी के साथ, import_image_legacy.mjs नाम की फ़ाइल में बदलाव किया जा सकता है. साथ ही, यह स्निपेट जोड़ा जा सकता है. इंपोर्ट की गई फ़ाइल में सबसे ऊपर, इंपोर्ट बटन और एक सुविधाजनक फ़ंक्शन drawBlob() होता है. इसकी मदद से, कैनवस पर कोई ब्लब बनाया जा सकता है.

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. 🐟 एक्सपोर्ट इमेज की सुविधा जोड़ें

आपका उपयोगकर्ता, ऐप्लिकेशन में बनाई गई फ़ाइल को अपने डिवाइस पर कैसे सेव करेगा? पहले, यह काम <​a
download>
एलिमेंट की मदद से किया जाता था.

export_image_legacy.mjs फ़ाइल में, यहां दिया गया कॉन्टेंट जोड़ें. एक्सपोर्ट बटन और toBlob() फ़ंक्शन को इंपोर्ट करें. यह फ़ंक्शन, कैनवस के कॉन्टेंट को ब्लोब में बदलता है.

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. 🐟 File System Access API के लिए सहायता जोड़ें

शेयर करना अच्छी बात है, लेकिन आपके उपयोगकर्ता शायद अपने सबसे अच्छे काम को अपने डिवाइसों पर सेव करना चाहें. ऐसी सुविधा जोड़ें जिसकी मदद से, उपयोगकर्ता अपनी ड्रॉइंग सेव कर सकें और उन्हें फिर से खोल सकें.

पहले, फ़ाइलों को इंपोर्ट करने के लिए <​input type=file> लेगसी तरीका और फ़ाइलों को एक्सपोर्ट करने के लिए <​a download> लेगसी तरीका इस्तेमाल किया जाता था. अब आपको बेहतर अनुभव देने के लिए, File System Access API का इस्तेमाल करना होगा.

इस एपीआई की मदद से, ऑपरेटिंग सिस्टम के फ़ाइल सिस्टम से फ़ाइलें खोली और सेव की जा सकती हैं. नीचे दिया गया कॉन्टेंट जोड़कर, import_image.mjs और export_image.mjs फ़ाइलों में बदलाव करें. इन फ़ाइलों को लोड करने के लिए, script.mjs से 🐡 इमोजी हटाएं.

इस लाइन को बदलें:

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

...इस लाइन के साथ:

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

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

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. 🐟 कॉन्टैक्ट पिकर एपीआई के लिए सहायता जोड़ी गई

आपके उपयोगकर्ता, ग्रीटिंग कार्ड में कोई मैसेज जोड़ना और किसी व्यक्ति को निजी तौर पर संबोधित करना चाहें. ऐसी सुविधा जोड़ें जिससे आपके ऐप्लिकेशन के उपयोगकर्ता, अपने डिवाइस में सेव किए गए संपर्कों में से एक या उससे ज़्यादा संपर्क चुन सकें. साथ ही, शेयर किए जाने वाले मैसेज में उनके नाम जोड़ सकें.

Android या iOS डिवाइस पर, Contact Picker API की मदद से, डिवाइस के Contacts ऐप्लिकेशन से संपर्क चुने जा सकते हैं. इसके बाद, उन्हें ऐप्लिकेशन में वापस भेजा जा सकता है. फ़ाइल contacts.mjs में बदलाव करें और नीचे दिया गया कोड जोड़ें.

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. 🐟 Add Async Clipboard API Support

आपके ऐप्लिकेशन के उपयोगकर्ता, किसी दूसरे ऐप्लिकेशन से कोई इमेज कॉपी करके आपके ऐप्लिकेशन में चिपकाना या आपके ऐप्लिकेशन से कोई ड्राइंग कॉपी करके किसी दूसरे ऐप्लिकेशन में चिपकाना चाहें. इसलिए, एक ऐसी सुविधा जोड़ें जिससे उपयोगकर्ता, आपके ऐप्लिकेशन में इमेज कॉपी करके चिपका सकें और आपके ऐप्लिकेशन से इमेज कॉपी करके चिपका सकें. Async Clipboard API, PNG इमेज के साथ काम करता है. इसलिए, अब क्लिपबोर्ड में इमेज डेटा को पढ़ा और लिखा जा सकता है.

clipboard.mjs फ़ाइल ढूंढें और इसमें यह जानकारी जोड़ें:

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. 🐟 Badging API की सुविधा जोड़ना

जब उपयोगकर्ता आपका ऐप्लिकेशन इंस्टॉल करेंगे, तब उनकी होम स्क्रीन पर एक आइकॉन दिखेगा. इस आइकॉन का इस्तेमाल करके, मज़ेदार जानकारी दी जा सकती है. जैसे, किसी ड्राइंग को बनाने में कितने ब्रुश स्ट्रोक लगे.

ऐसी सुविधा जोड़ें जो उपयोगकर्ता के हर नए ब्रशिंग स्ट्रोक पर बैज की गिनती बढ़ाती है. बैजिंग एपीआई की मदद से, ऐप्लिकेशन के आइकॉन पर संख्या वाला बैज सेट किया जा सकता है. जब भी कोई pointerdown इवेंट होता है (यानी कि जब कोई ब्रशस्ट्रोक होता है), तब बैज को अपडेट किया जा सकता है. साथ ही, कैनवस को मिटाए जाने पर बैज को रीसेट किया जा सकता है.

नीचे दिए गए कोड को 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. 🐟 Add Screen Wake Lock API Support

कभी-कभी, आपके उपयोगकर्ताओं को किसी ड्राइंग को देखने के लिए कुछ समय चाहिए होता है, ताकि उन्हें प्रेरणा मिल सके. ऐसी सुविधा जोड़ें जिससे स्क्रीन चालू रहे और स्क्रीन सेवर चालू न हो. स्क्रीन वेक लॉक एपीआई, उपयोगकर्ता की स्क्रीन को स्लीप मोड में जाने से रोकता है. पेज दिखने की स्थिति के हिसाब से, पेज दिखने की स्थिति में बदलाव होने पर, वेक लॉक अपने-आप हट जाता है. इसलिए, जब पेज वापस दिखता है, तब वेक लॉक को फिर से हासिल करना ज़रूरी होता है.

wake_lock.mjs फ़ाइल ढूंढें और इसमें नीचे दिया गया कॉन्टेंट जोड़ें. यह सुविधा काम कर रही है या नहीं, यह देखने के लिए स्क्रीनसेवर को एक मिनट बाद दिखने के लिए कॉन्फ़िगर करें.

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. 🐟 Add Periodic Background Sync API Support

खाली कैनवस से शुरुआत करना बोरिंग हो सकता है. Periodic Background Sync API का इस्तेमाल करके, हर दिन अपने उपयोगकर्ताओं के कैनवस पर नई इमेज सेट की जा सकती है. उदाहरण के लिए, Unsplash की डेली फ़ुगु फ़ोटो.

इसके लिए दो फ़ाइलों की ज़रूरत होती है. पहली फ़ाइल periodic_background_sync.mjs, जो समय-समय पर बैकग्राउंड में सिंक होने की सुविधा को रजिस्टर करती है. दूसरी फ़ाइल image_of_the_day.mjs, जो दिन की इमेज को डाउनलोड करने का काम करती है.

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

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. 🐟 शेप डिटेक्शन एपीआई के लिए सहायता जोड़ें

कभी-कभी, आपके उपयोगकर्ताओं की ड्रॉइंग या इस्तेमाल की गई बैकग्राउंड इमेज में बारकोड जैसी काम की जानकारी हो सकती है. Shape Detection API और खास तौर पर Barcode Detection API की मदद से, यह जानकारी निकाली जा सकती है. ऐसी सुविधा जोड़ें जो लोगों की ड्रॉइंग से बारकोड का पता लगा सके. barcode.mjs फ़ाइल ढूंढें और उसमें नीचे दिया गया कॉन्टेंट जोड़ें. इस सुविधा को आज़माने के लिए, बारकोड वाली किसी इमेज को कैनवस पर लोड करें या चिपकाएं. क्यूआर कोड की इमेज खोजकरके, बारकोड का कोई उदाहरण कॉपी किया जा सकता है.

/* 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. 🐡 Add Idle Detection API Support

अगर आपका ऐप्लिकेशन कियॉस्क जैसे सेटअप में चल रहा है, तो कुछ समय तक इस्तेमाल न होने के बाद कैनवस को रीसेट करने की सुविधा काम की हो सकती है. Idle Detection API की मदद से, यह पता लगाया जा सकता है कि कोई उपयोगकर्ता अपने डिवाइस के साथ कब तक इंटरैक्ट नहीं करता है.

idle_detection.mjs फ़ाइल ढूंढें और उसमें यहां दिया गया कॉन्टेंट चिपकाएं.

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. 🐡 फ़ाइल मैनेज करने वाले एपीआई के साथ काम करने की सुविधा जोड़ें

अगर आपके उपयोगकर्ता किसी इमेज फ़ाइल पर सिर्फ़ दो बार क्लिक करें और आपका ऐप्लिकेशन खुल जाए, तो क्या होगा? फ़ाइल मैनेज करने वाले एपीआई की मदद से, ऐसा किया जा सकता है.

आपको PWA को इमेज के लिए फ़ाइल हैंडलर के तौर पर रजिस्टर करना होगा. ऐसा वेब ऐप्लिकेशन मेनिफ़ेस्ट में होता है. फ़ाइल manifest.webmanifest का नीचे दिया गया स्निपेट, इसे दिखाता है. (यह मेनिफ़ेस्ट का हिस्सा है. इसे खुद से जोड़ने की ज़रूरत नहीं है.)

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

खुली हुई फ़ाइलों को मैनेज करने के लिए, फ़ाइल 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. बधाई हो

🎉 वाह, आपने कमाल कर दिखाया!

Project Fugu 🐡 के तहत, कई बेहतरीन ब्राउज़र एपीआई डेवलप किए जा रहे हैं. इस कोडलैब में, इनके बारे में बहुत कम जानकारी दी गई है.

ज़्यादा जानकारी पाने या इसके बारे में ज़्यादा जानने के लिए, हमारी साइट web.dev पर हमारे पब्लिकेशन पढ़ें.

यह web.dev साइट के “Capabilities” सेक्शन का लैंडिंग पेज है.

हालांकि, यह यहीं खत्म नहीं होता. जिन अपडेट के बारे में अब तक सार्वजनिक तौर पर जानकारी नहीं दी गई है उनके बारे में जानने के लिए, Fugu API ट्रैकर का इस्तेमाल करें. इसमें उन सभी प्रस्तावों के लिंक दिए गए हैं जिन्हें शिप कर दिया गया है, जो ओरिजिन ट्रायल या देव ट्रायल में हैं, जिन पर काम शुरू हो गया है, और जिन पर विचार किया जा रहा है, लेकिन अब तक काम शुरू नहीं हुआ है.

Fugu API ट्रैकर वेबसाइट

इस कोडलैब को थॉमस स्टाइनर (@tomayac) ने लिखा है. हमें आपके सवालों के जवाब देने में खुशी होगी. साथ ही, हमें आपके सुझाव/राय पढ़ने का इंतज़ार रहेगा! इस कोडलैब को बेहतर बनाने में मदद करने के लिए, हम इन लोगों का शुक्रिया अदा करना चाहते हैं: हेमंत एच.एम (@GNUmanth), क्रिश्चियन लीबेल (@christianliebel), स्वेन मे (@Svenmay), लार्स नुडसन (@larsgk), और जैकी हान (@hanguokai)!