9
Fitur Favorit di Local Storage
Fitur favorit FreeStuffDev tidak menyentuh server. Setiap tool yang ditandai pembaca disimpan di localStorage browser, di bawah satu kunci bernama favoritedItems (src/layouts/Layout.astro:51-631). Halaman /favorites membaca kunci yang sama, jadi daftar simpanan hanya terlihat di browser itu sendiri. Halaman tersebut sengaja dirender dengan noIndex={true} dan teks header yang menjelaskan bahwa daftar tidak bisa dibagikan ke orang lain (src/pages/favorites.astro:14-352). Tautan masuknya ada di sidebar, pada tombol berlabel Saved (src/layouts/Sidebar.astro:26-293).
Satu kunci, empat fungsi global
Blok save features di src/layouts/Layout.astro ditulis sebagai <script is:inline>, sehingga tidak di-bundle dan deklarasinya menjadi global. Itulah sebabnya script terpisah di /favorites bisa memanggil FAV_KEY_NAME dan getObjectLS tanpa mengimpor apa pun (src/pages/favorites.astro:37-574).
| Identifier | Rentang | Perilaku |
|---|---|---|
FAV_KEY_NAME | src/layouts/Layout.astro:51-631 | Konstanta string "favoritedItems"; satu-satunya nama kunci yang dipakai save, unsave, dan halaman /favorites. |
getObjectLS(key) | src/layouts/Layout.astro:51-631 | Membungkus JSON.parse(localStorage.getItem(key)); mengembalikan null bila kunci belum pernah ditulis. |
setObjectLS(key, val) | src/layouts/Layout.astro:51-631 | Membungkus localStorage.setItem(key, JSON.stringify(val)). |
save(event, link) | src/layouts/Layout.astro:65-765 | Ambil array lama atau [], push(link), buang duplikat dengan [...new Set(favorites)], tulis balik, lalu ubah teks tombol menjadi Saved! dan warnanya #FE4032. |
unsave(event, link) | src/layouts/Layout.astro:78-886 | filter slug yang tidak sama dengan link, tulis balik, lalu tambahkan class hidden pada .cards terdekat. |
Isi simpanan adalah array slug, bukan objek tool. save selalu memanggil event.preventDefault() lebih dulu agar tautan href="#" tidak melompat, dan deduplikasi lewat new Set membuat klik ganda tidak menggandakan slug (src/layouts/Layout.astro:65-765).
Siklus simpan dan hapus
stateDiagram-v2
[*] --> NotSaved
NotSaved --> Stored: klik .save-button
Stored --> Stored: dedupe lewat new Set
Stored --> Hidden: klik .unsave-button
Hidden --> [*]
note right of Stored
favoritedItems di localStorage
end note
style NotSaved fill:#f59e0b,color:#000
style Stored fill:#336791,color:#fff
style Hidden fill:#dc2626,color:#fff
%% fig: Siklus state favorit di localStorage
%% desc: save memindahkan tool ke state tersimpan dengan deduplikasi, unsave menghapus slug dan menyembunyikan kartunya; seluruh state hidup di localStorage favoritedItems.Perhatikan bahwa Hidden bukan state yang dihapus dari DOM. Di halaman /favorites, unsave hanya menambahkan class hidden pada pembungkus .cards setelah slug dibuang dari array, sehingga kartu tetap ada di dokumen sampai halaman dimuat ulang (src/layouts/Layout.astro:78-886).
Tombol di dalam Card
Setiap kartu mencetak dua tautan aksi yang saling menggantikan: .unsave-button sudah membawa class hidden, sedangkan .save-button tampil normal. Keduanya memanggil fungsi global dengan slug tool sebagai argumen, yaitu unsave(event, "${tool.slug}") dan save(event, "${tool.slug}") (src/components/Card.astro:61-697). Karena kartu yang sama dipakai di beranda, /recent, halaman tag, dan halaman alternatif, tombol Save tersedia di semua tempat itu.
Slug juga dicetak di elemen <input type="hidden" name="tool-slug" value={tool.slug}> di awal kartu. Elemen itulah yang dicari halaman favorit untuk menemukan pembungkus kartu yang cocok (src/components/Card.astro:14-168).
Membaca ulang di /favorites
Halaman favorit merender seluruh collection tools lebih dulu, semuanya dengan isHidden={true}, lalu menyembunyikan pesan kosong .empty-message (src/pages/favorites.astro:14-352). Script setelahnya membalik urutan array hasil reverse() sehingga tool yang paling akhir disimpan muncul paling atas, lalu untuk setiap slug: mencari kartu lewat document.querySelector(input[value="${itemSlug}"]), membuang class hidden dari parentNode, menyembunyikan .save-button, dan menampilkan .unsave-button (src/pages/favorites.astro:37-574).
Pesan You haven't saved any tools yet hanya muncul bila kunci favoritedItems ada tetapi array-nya kosong, karena pemeriksaan savedItems.length === 0 berada di dalam cabang !== null (src/pages/favorites.astro:37-574).
⚠️
unsavetidak punya fallback[]sepertisave. Bila kuncifavoritedItemsbelum pernah ditulis,getObjectLS(FAV_KEY_NAME)mengembalikannulldanfavorites.filter(...)dijalankan pada nilainull(src/layouts/Layout.astro:78-886).
⚠️ Pencarian kartu di
/favoritesmemakaidocument.querySelector(...).parentNodetanpa pemeriksaannull. Slug yang tersimpan dilocalStoragetetapi sudah tidak ada di collectiontoolsakan menghentikan script sebelum sisa daftar ditampilkan (src/pages/favorites.astro:37-574).
Karena tidak ada sinkronisasi ke server maupun event lintas tab, status favorit hanya berubah ketika sebuah halaman memuat ulang dirinya. Perilaku localStorage dan ViewTransitions yang membungkus semua halaman dibahas di Layout, Sidebar, dan Tema.