Lompat ke konten
dokumentasi.id
Commit 314576d
v1
Star2 rb

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-63). 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-35). Tautan masuknya ada di sidebar, pada tombol berlabel Saved (src/layouts/Sidebar.astro:26-29).

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-57).

IdentifierRentangPerilaku
FAV_KEY_NAMEsrc/layouts/Layout.astro:51-63Konstanta string "favoritedItems"; satu-satunya nama kunci yang dipakai save, unsave, dan halaman /favorites.
getObjectLS(key)src/layouts/Layout.astro:51-63Membungkus JSON.parse(localStorage.getItem(key)); mengembalikan null bila kunci belum pernah ditulis.
setObjectLS(key, val)src/layouts/Layout.astro:51-63Membungkus localStorage.setItem(key, JSON.stringify(val)).
save(event, link)src/layouts/Layout.astro:65-76Ambil 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-88filter 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-76).

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.
Fig. 1: Siklus state favorit di localStoragesave 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-88).

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-69). 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-16).

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-35). 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-57).

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-57).

⚠️ unsave tidak punya fallback [] seperti save. Bila kunci favoritedItems belum pernah ditulis, getObjectLS(FAV_KEY_NAME) mengembalikan null dan favorites.filter(...) dijalankan pada nilai null (src/layouts/Layout.astro:78-88).

⚠️ Pencarian kartu di /favorites memakai document.querySelector(...).parentNode tanpa pemeriksaan null. Slug yang tersimpan di localStorage tetapi sudah tidak ada di collection tools akan menghentikan script sebelum sisa daftar ditampilkan (src/pages/favorites.astro:37-57).

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.