1. Sebelum memulai
Progressive Web Application (PWA) adalah jenis software aplikasi yang disediakan melalui web, yang dibangun menggunakan teknologi web umum termasuk HTML, CSS, dan JavaScript. Aplikasi ini dimaksudkan untuk berfungsi di platform apa pun yang menggunakan browser yang sesuai dengan standar.
Dalam codelab ini, Anda akan memulai dengan PWA dasar, lalu menjelajahi kemampuan browser baru yang pada akhirnya akan memberi PWA Anda kekuatan super 🦸.
Banyak kemampuan browser baru ini sedang dalam proses pengembangan dan masih distandardisasi, sehingga terkadang Anda perlu menyetel tanda browser untuk menggunakannya.
Prasyarat
Untuk codelab ini, Anda harus memahami JavaScript modern, khususnya promise dan async/await. Karena tidak semua langkah codelab didukung di semua platform, akan lebih baik jika Anda memiliki perangkat tambahan untuk pengujian, misalnya, ponsel Android atau laptop yang menggunakan sistem operasi yang berbeda dengan perangkat yang Anda gunakan untuk mengedit kode. Sebagai alternatif untuk perangkat sebenarnya, Anda dapat mencoba menggunakan simulator seperti simulator Android atau layanan online seperti BrowserStack yang memungkinkan Anda melakukan pengujian dari perangkat Anda saat ini. Atau, Anda juga dapat melewati langkah apa pun, karena langkah-langkah tersebut tidak saling bergantung.
Yang akan Anda build
Anda akan membuat aplikasi web kartu ucapan, dan mempelajari cara kemampuan browser baru dan mendatang dapat meningkatkan kualitas aplikasi Anda sehingga memberikan pengalaman yang canggih di browser tertentu (tetapi tetap berguna di semua browser modern).
Anda akan mempelajari cara menambahkan kemampuan dukungan, seperti akses sistem file, akses papan klip sistem, pengambilan kontak, sinkronisasi latar belakang berkala, kunci tetap aktif layar, fitur berbagi, dan lainnya.
Setelah menyelesaikan codelab ini, Anda akan memiliki pemahaman yang kuat tentang cara meningkatkan kualitas aplikasi web Anda secara progresif dengan fitur browser baru, tanpa membebani download pada sebagian kecil pengguna yang menggunakan browser yang tidak kompatibel, dan yang terpenting, tanpa mengecualikan mereka dari aplikasi Anda sejak awal.
Yang Anda butuhkan
Browser yang didukung sepenuhnya saat ini adalah:
Sebaiknya gunakan saluran Dev tertentu.
2. Project Fugu
Progressive Web App (PWA) dibuat dan ditingkatkan dengan API modern untuk memberikan kemampuan, keandalan, dan kemampuan penginstalan yang lebih baik sekaligus menjangkau siapa pun di web, di mana pun di dunia, menggunakan jenis perangkat apa pun.
Beberapa API ini sangat canggih, dan jika ditangani dengan tidak benar, dapat menimbulkan masalah. Seperti ikan fugu 🐡: Jika dipotong dengan benar, ikan ini akan menjadi hidangan lezat, tetapi jika dipotong dengan salah, ikan ini bisa mematikan (tetapi jangan khawatir, tidak ada yang bisa rusak dalam codelab ini).
Itulah sebabnya nama kode internal project Kemampuan Web (yang digunakan perusahaan yang terlibat untuk mengembangkan API baru ini) adalah Project Fugu.
Kemampuan web—saat ini—memungkinkan perusahaan besar dan kecil membangun solusi berbasis browser murni, yang sering kali memungkinkan deployment lebih cepat dengan biaya pengembangan yang lebih rendah dibandingkan dengan menggunakan jalur khusus platform.
3. Mulai
Download salah satu browser, lalu tetapkan tanda waktu proses 🚩 berikut dengan membuka about://flags
, yang berfungsi di Chrome dan Edge:
#enable-experimental-web-platform-features
Setelah Anda mengaktifkannya, mulai ulang browser Anda.
Anda akan menggunakan platform Glitch, karena platform ini memungkinkan Anda menghosting PWA dan memiliki editor yang cukup baik. Glitch juga mendukung impor dan ekspor ke GitHub, sehingga tidak ada penguncian vendor. Buka fugu-paint.glitch.me untuk mencoba aplikasi. Ini adalah aplikasi gambar dasar 🎨 yang akan Anda tingkatkan selama codelab.
Setelah mencoba aplikasi, remix aplikasi untuk membuat salinan Anda sendiri yang dapat Anda edit. URL remix Anda akan terlihat seperti glitch.com/edit/#!/bouncy-candytuft ("bouncy-candytuft" akan berbeda untuk Anda). Remix ini dapat diakses langsung di seluruh dunia. Login ke akun yang sudah ada atau buat akun baru di Glitch untuk menyimpan pekerjaan Anda. Anda dapat melihat aplikasi dengan mengklik tombol "🕶 Tampilkan", dan URL aplikasi yang dihosting akan terlihat seperti bouncy-candytuft.glitch.me (perhatikan .me
, bukan .com
sebagai domain level teratas).
Sekarang Anda siap mengedit dan meningkatkan kualitas aplikasi. Setiap kali Anda melakukan perubahan, aplikasi akan dimuat ulang dan perubahan Anda akan langsung terlihat.
Idealnya, tugas berikut harus diselesaikan secara berurutan, tetapi seperti yang disebutkan di atas, Anda selalu dapat melewati langkah jika tidak memiliki akses ke perangkat yang kompatibel. Ingat, setiap tugas ditandai dengan 🐟, ikan air tawar yang tidak berbahaya, atau 🐡, ikan fugu "hati-hati", yang menunjukkan seberapa eksperimental atau tidak suatu fitur.
Periksa Konsol di DevTools untuk melihat apakah API didukung di perangkat saat ini. Kami juga menggunakan Glitch sehingga Anda dapat memeriksa aplikasi yang sama di berbagai perangkat dengan mudah, misalnya di ponsel dan komputer desktop Anda.
4. 🐟 Menambahkan Dukungan Web Share API
Membuat gambar yang paling menakjubkan akan terasa membosankan jika tidak ada yang mengaguminya. Tambahkan fitur yang memungkinkan pengguna Anda membagikan gambar mereka kepada dunia, dalam bentuk kartu ucapan.
Web Share API mendukung berbagi file, dan seperti yang mungkin Anda ingat, File
hanyalah jenis Blob
tertentu. Oleh karena itu, di file bernama share.mjs
, impor tombol bagikan dan fungsi praktis toBlob()
yang mengonversi konten kanvas menjadi blob dan tambahkan fungsi berbagi sesuai kode di bawah.
Jika Anda telah menerapkannya, tetapi tidak melihat tombol, hal ini karena browser Anda tidak menerapkan 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. 🐟 Menambahkan Dukungan Web Share Target API
Sekarang pengguna Anda dapat membagikan kartu ucapan yang dibuat menggunakan aplikasi, tetapi Anda juga dapat mengizinkan pengguna membagikan gambar ke aplikasi Anda dan mengubahnya menjadi kartu ucapan. Untuk melakukannya, Anda dapat menggunakan Web Share Target API.
Dalam Manifes Aplikasi Web, Anda perlu memberi tahu aplikasi jenis file yang dapat Anda terima dan URL yang harus dipanggil browser saat satu atau beberapa file dibagikan. Cuplikan di bawah dari file manifest.webmanifest
menunjukkan hal ini.
{
"share_target": {
"action": "./share-target/",
"method": "POST",
"enctype": "multipart/form-data",
"params": {
"files": [
{
"name": "image",
"accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
}
]
}
}
}
Kemudian, pekerja layanan akan menangani file yang diterima. URL ./share-target/
sebenarnya tidak ada, aplikasi hanya bertindak di pengendali fetch
dan mengalihkan permintaan ke URL root dengan menambahkan parameter kueri ?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 */
/* ... */
});
Saat aplikasi dimuat, aplikasi akan memeriksa apakah parameter kueri ini ditetapkan, dan jika ya, akan menggambar gambar yang dibagikan ke kanvas dan menghapusnya dari cache. Semua ini terjadi di 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');
}
};
Fungsi ini kemudian digunakan saat aplikasi diinisialisasi.
if (location.search.includes('share-target')) {
restoreImageFromShare();
} else {
drawDefaultImage();
}
6. 🐟 Menambahkan Dukungan Impor Gambar
Menggambar semuanya dari awal itu sulit. Tambahkan fitur yang memungkinkan pengguna mengupload gambar lokal dari perangkat mereka ke dalam aplikasi.
Pertama, baca fungsi drawImage()
kanvas. Selanjutnya, pahami elemen <input
.
type=file>
Dengan pengetahuan ini, Anda dapat mengedit file bernama import_image_legacy.mjs
dan menambahkan cuplikan berikut. Di bagian atas file, Anda mengimpor tombol impor dan fungsi praktis drawBlob()
yang memungkinkan Anda menggambar blob ke kanvas.
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. 🐟 Menambahkan Dukungan Ekspor Gambar
Bagaimana cara pengguna menyimpan file yang dibuat di aplikasi ke perangkat mereka? Biasanya, hal ini dilakukan dengan elemen <a
.
download>
Dalam file export_image_legacy.mjs
, tambahkan konten sebagai berikut di bawah. Impor tombol ekspor dan fungsi praktis toBlob()
yang mengonversi konten kanvas menjadi 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. 🐟 Menambahkan Dukungan File System Access API
Berbagi itu peduli, tetapi pengguna Anda mungkin ingin menyimpan hasil terbaik pekerjaan mereka ke perangkat mereka sendiri. Tambahkan fitur yang memungkinkan pengguna menyimpan (dan membuka kembali) gambar mereka.
Sebelumnya, Anda menggunakan <input type=file>
pendekatan lama untuk mengimpor file dan <a download>
pendekatan lama untuk mengekspor file. Sekarang, Anda akan menggunakan File System Access API untuk meningkatkan kualitas pengalaman.
API ini memungkinkan pembukaan dan penyimpanan file dari sistem file sistem operasi. Edit kedua file, import_image.mjs
dan export_image.mjs
, dengan menambahkan konten di bawah. Agar file ini dapat dimuat, hapus emoji 🐡 dari script.mjs
.
Ganti baris ini:
// Remove all the emojis for this feature test to succeed.
if ('show🐡Open🐡File🐡Picker' in window) {
/* ... */
}
...dengan baris ini:
if ('showOpenFilePicker' in window) {
/* ... */
}
Di 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);
}
});
Di 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. 🐟 Menambahkan Dukungan Contact Picker API
Pengguna Anda mungkin ingin menambahkan pesan ke kartu ucapan mereka, dan menyapa seseorang secara pribadi. Tambahkan fitur yang memungkinkan pengguna memilih satu (atau beberapa) kontak lokal mereka dan menambahkan nama mereka ke pesan berbagi.
Di perangkat Android atau iOS, Contact Picker API memungkinkan Anda memilih kontak dari aplikasi pengelola kontak perangkat dan menampilkannya kembali ke aplikasi. Edit file contacts.mjs
dan tambahkan kode di bawah.
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. 🐟 Menambahkan Dukungan Async Clipboard API
Pengguna Anda mungkin ingin menempelkan gambar dari aplikasi lain ke aplikasi Anda, atau menyalin gambar dari aplikasi Anda ke aplikasi lain. Tambahkan fitur yang memungkinkan pengguna Anda menyalin dan menempelkan gambar ke dalam dan ke luar aplikasi Anda. Async Clipboard API mendukung gambar PNG, sehingga kini Anda dapat membaca dan menulis data gambar ke papan klip.
Temukan file clipboard.mjs
, lalu tambahkan kode berikut:
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. 🐟 Menambahkan Dukungan Badging API
Saat pengguna menginstal aplikasi Anda, ikon akan muncul di layar utama mereka. Anda dapat menggunakan ikon ini untuk menyampaikan informasi menarik, seperti jumlah sapuan kuas yang digunakan dalam gambar tertentu.
Tambahkan fitur yang menghitung badge setiap kali pengguna membuat sapuan kuas baru. Badging API memungkinkan badge numerik disetel pada ikon aplikasi. Anda dapat memperbarui badge setiap kali peristiwa pointerdown
terjadi (yaitu, saat goresan kuas terjadi), dan mereset badge saat kanvas dibersihkan.
Masukkan kode di bawah ke dalam 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. 🐟 Menambahkan Dukungan Screen Wake Lock API
Terkadang, pengguna Anda mungkin perlu beberapa saat untuk menatap gambar, cukup lama hingga inspirasi datang. Menambahkan fitur yang menjaga layar tetap aktif dan menghentikan screensaver agar tidak aktif. Screen Wake Lock API mencegah layar pengguna menjadi tidak aktif. Wake lock otomatis dilepaskan saat peristiwa perubahan visibilitas seperti yang ditentukan oleh Page Visibility terjadi. Oleh karena itu, kunci tetap aktif harus diperoleh kembali saat halaman kembali terlihat.
Temukan file wake_lock.mjs
dan tambahkan konten di bawah. Untuk menguji apakah fitur ini berfungsi, konfigurasi screensaver Anda agar ditampilkan setelah satu menit.
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. 🐟 Menambahkan Dukungan Periodic Background Sync API
Memulai dengan kanvas kosong bisa jadi membosankan. Anda dapat menggunakan Periodic Background Sync API untuk menginisialisasi kanvas pengguna dengan gambar baru setiap hari, misalnya, foto fugu harian Unsplash.
Hal ini memerlukan dua file, yaitu file periodic_background_sync.mjs
yang mendaftarkan Sinkronisasi Latar Belakang Berkala dan file image_of_the_day.mjs
yang menangani download gambar hari ini.
Di 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());
});
Di 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. 🐟 Menambahkan Dukungan Shape Detection API
Terkadang gambar yang dibuat pengguna atau gambar latar belakang yang digunakan dapat berisi informasi yang berguna seperti, misalnya, kode batang. Shape Detection API, dan khususnya, Barcode Detection API, memungkinkan Anda mengekstrak informasi ini. Tambahkan fitur yang mencoba mendeteksi kode batang dari gambar pengguna Anda. Temukan file barcode.mjs
dan tambahkan konten di bawah. Untuk menguji fitur ini, cukup muat atau tempelkan gambar dengan kode batang ke kanvas. Anda dapat menyalin contoh kode batang dari penelusuran gambar untuk kode 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. 🐡 Menambahkan Dukungan Idle Detection API
Jika Anda membayangkan aplikasi Anda berjalan dalam penyiapan seperti kios, fitur yang berguna adalah mereset kanvas setelah tidak ada aktivitas selama jangka waktu tertentu. Idle Detection API memungkinkan Anda mendeteksi saat pengguna tidak lagi berinteraksi dengan perangkatnya.
Temukan file idle_detection.mjs
dan tempelkan konten di bawah.
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. 🐡 Menambahkan Dukungan File Handling API
Bagaimana jika pengguna Anda dapat mengklik dua kali file gambar dan aplikasi Anda akan muncul? File Handling API memungkinkan Anda melakukan hal tersebut.
Anda harus mendaftarkan PWA sebagai handler file untuk gambar. Hal ini terjadi di Manifes Aplikasi Web, cuplikan di bawah file manifest.webmanifest
menunjukkan hal ini. (Ini sudah menjadi bagian dari manifes, Anda tidak perlu menambahkannya sendiri.)
{
"file_handlers": [
{
"action": "./",
"accept": {
"image/*": [".jpg", ".jpeg", ".png", ".webp", ".svg"]
}
}
]
}
Untuk benar-benar menangani file yang dibuka, tambahkan kode di bawah ke 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. Selamat
🎉 Hore, Anda berhasil!
Ada begitu banyak API browser menarik yang dikembangkan dalam konteks Project Fugu 🐡 sehingga codelab ini hampir tidak dapat membahasnya secara menyeluruh.
Untuk mempelajari lebih dalam, atau sekadar ingin tahu lebih lanjut, ikuti publikasi kami di situs web.dev.
Namun, bukan hanya itu. Untuk mengetahui info terbaru yang belum dipublikasikan, Anda dapat mengakses pelacak Fugu API kami dengan link ke semua proposal yang telah diluncurkan, sedang dalam uji coba origin atau uji coba dev, semua proposal yang pengerjaannya telah dimulai, dan semua yang sedang dipertimbangkan, tetapi belum dimulai.
Codelab ini ditulis oleh Thomas Steiner (@tomayac). Dengan senang hati kami akan menjawab pertanyaan Anda dan menantikan masukan Anda. Terima kasih khusus kepada Hemanth H.M (@GNUmanth), Christian Liebel (@christianliebel), Sven May (@Svenmay), Lars Knudsen (@larsgk), dan Jackie Han (@hanguokai) yang telah membantu membentuk codelab ini.