Fictional World logo FICTIONAL WORLDLIBRARY
Your next obsession starts here

Some stories stay with you long after the last page.

Step into unforgettable romances, dangerous secrets, powerful alphas and worlds you won't want to leave.

Find your world

Choose a genre

Stories to fall into

Ongoing stories

New chapters await

Completed reads

Read at your pace
Discover

Find your next story

All stories

Your personal shelf

My Library

Log in to keep your reading library connected to your account.
Stay in the loop

Notifications

Log in to view your notifications.

Welcome to your world

Welcome back

Log in to continue your reading journey.

Reset password

Your account

My Profile

โ™ก

Reader

Sign in to view your profile

Reader ID: โ€”

Gender: Not provided

Membership: Free

Coin balance0
Saved books0
MembershipFree
Fiction

Story title

Chapters

Chapter

Select a chapter to begin reading.
Your reading balance

Wallet

Available coins

0 ๐Ÿช™

Coins are used to unlock paid chapters.

Choose a coin package

Transaction history

Log in to view transactions.

Read more. Enjoy more.

Reader Memberships

Choose a plan that suits your reading habit.

Everything in one place

More

We're here to help

Customer Service

For account, reading, or general help, contact our support team at fictionhomeofficial@gmail.com

For reader support, email us and weโ€™ll help you as quickly as possible.

Your story deserves a world

Be Our Author

Bring your imagination to readers who love compelling fiction.

Short stories

12โ€“25 chapters, with 600โ€“1,000 words per chapter.

At least 5 completed chapters to apply.

Long stories

25+ chapters and a deeper story arc.

A strong synopsis and clear author profile are recommended.

Applications are reviewed in approximately 3โ€“7 working days. If you are interested, send your name, story type, genre, and a short author bio to fictionhomeofficial@gmail.com.

Make it yours

Settings

Appearance

Choose a theme for your reading experience.

Quick links

About Fictional World Library

Fictional World Library is a place for readers who want to discover immersive stories, compelling characters, and unforgettable worlds.

Contact us for sponsorships, partnerships, or ad placement at fictionhomeofficial@gmail.com.

Terms & Conditions

Use of Fictional World Library is subject to the platform's applicable terms, content rules, payment policies, and account rules.

By creating an account, you agree to use the platform lawfully and responsibly.

Privacy Policy

Fictional World Library uses account information to provide reader features, protect accounts, and support transaction records.

We do not sell or misuse your reader information. For privacy or account questions, email fictionhomeofficial@gmail.com.

document.getElementById(id); function notify(message) { const toast = $("toast"); if (!toast) return; toast.textContent = message; toast.style.display = "block"; clearTimeout(window.toastTimer); window.toastTimer = setTimeout(() => toast.style.display = "none", 3500); } function escapeHTML(value = "") { return String(value).replace(/[&<>"']/g, c => ({ "&":"&","<":"<",">":">","\"":""","'":"'" })[c]); } function showPage(name) { if (name === "profile" || name === "account") name = reader ? "profile" : "account"; const page = $("page-" + name); if (!page) return; document.querySelectorAll(".page").forEach(p => p.classList.remove("active")); page.classList.add("active"); document.querySelectorAll("[data-page]").forEach(b => b.classList.toggle("active", b.dataset.page === name)); window.scrollTo({ top: 0, behavior: "smooth" }); if (name === "profile" && reader) loadProfile(); if (name === "notifications") loadNotifications(); if (name === "wallet" && reader) loadTransactions(); } function renderGenres() { ["home-genres","browse-genres"].forEach(id => { const el = $(id); if (!el) return; el.innerHTML = genres.map(g => `` ).join(""); }); } function bookCard(book) { const id = escapeHTML(book.id); const title = escapeHTML(book.title || "Untitled story"); const author = escapeHTML(book.author || book.author_name || "Fictional World"); const genre = escapeHTML(book.genre || "Fiction"); const cover = book.cover_url || book.cover_image || ""; return `
${cover ? `${title}` : ""}
${title}

${title}

${author}

${genre}
`; } function renderBookList(target, list) { const el = $(target); if (!el) return; el.innerHTML = list.length ? list.map(bookCard).join("") : `
No stories to display yet.
`; } function filteredBooks() { const q = ($("search-input")?.value || "").trim().toLowerCase(); return books.filter(b => { const genreMatch = selectedGenre === "All" || String(b.genre || "").toLowerCase() === selectedGenre.toLowerCase(); const searchMatch = !q || [b.title, b.author, b.author_name, b.genre].some(v => String(v || "").toLowerCase().includes(q)); return genreMatch && searchMatch; }); } function renderBooks() { const featured = books.filter(b => b.featured === true); renderBookList("featured-books", featured.slice(0, 5).concat(books.filter(b => !b.featured).slice(0, Math.max(0, 5 - featured.length)))); renderBookList("ongoing-books", books.filter(b => /ongoing/i.test(b.status || "")).slice(0, 5)); renderBookList("completed-books", books.filter(b => /completed/i.test(b.status || "")).slice(0, 5)); const list = filteredBooks(); renderBookList("all-books", list); const total = $("book-total"); if (total) total.textContent = `${list.length} stories`; const heading = $("browse-heading"); if (heading) heading.textContent = selectedGenre === "All" ? "All stories" : selectedGenre; renderGenres(); } async function loadBooks() { const allBooks = $("all-books"); if (allBooks) allBooks.innerHTML = `

Loading stories...

`; const { data, error } = await supabase.from("books").select("*").limit(100); if (error) { console.error("Books:", error.message); if (allBooks) allBooks.innerHTML = `
Stories could not be loaded. Please refresh and try again.
`; notify("Unable to load stories. Please try again."); return; } books = data || []; renderBooks(); } function renderPackages() { const packages = [ { id: "coins_100", coins: 100, price: "$2.99" }, { id: "coins_300", coins: 300, price: "$7.99" }, { id: "coins_600", coins: 600, price: "$14.99" }, { id: "coins_1200", coins: 1200, price: "$27.99" } ]; const el = $("coin-packages"); if (el) el.innerHTML = packages.map(p => `
Coin package

${p.coins.toLocaleString()} coins

${p.price}
`).join(""); const plans = [ { id: "readers_pass", name: "Readers Pass", monthly: "$5.99", yearly: "$49.99", benefits: ["No banner ad","50 free coins monthly","5% off on coin purchases"] }, { id: "vip_reader", name: "VIP Reader", monthly: "$9.99", yearly: "$89.99", benefits: ["No banner ad","150 free coins monthly","9% off on coin purchases"] }, { id: "exclusive_reader", name: "Exclusive Reader", monthly: "$19.99", yearly: "$179.99", benefits: ["No banner ad","300 free coins monthly","15% off on coin purchases","2 free chapter unlocks"] } ]; const membershipEl = $("membership-plans"); if (membershipEl) membershipEl.innerHTML = plans.map((p, i) => `
${i === 1 ? "Popular choice" : "Reader membership"}

${p.name}

${p.monthly} / month

${p.yearly} when billed yearly

    ${p.benefits.map(x => `
  • ${x}
  • `).join("")}
`).join(""); } function updateAccountUI() { $("login-nav").textContent = "My Account"; if (!reader) { $("profile-email").textContent = "Sign in to view your profile"; $("profile-name").textContent = "Reader"; $("profile-reader-id").textContent = "โ€”"; $("profile-gender").textContent = "Not provided"; $("profile-avatar").textContent = "โ™ก"; return; } $("profile-email").textContent = reader.email || ""; $("profile-name").textContent = reader.user_metadata?.display_name || reader.email?.split("@")[0] || "Reader"; } async function getSession() { try { const { data: { session }, error } = await supabase.auth.getSession(); if (error) { console.error("Session error:", error); reader = null; return; } reader = session?.user || null; if (!reader) { updateAccountUI(); $("coin-count").textContent = "0"; $("wallet-coins").textContent = "0"; $("profile-coins").textContent = "0"; return; } await loadProfile(); await loadLibrary(); await loadTransactions(); await loadNotifications(); if ($("page-account") && $("page-account").classList.contains("active")) showPage("profile"); } catch (error) { console.error("Could not get session:", error); reader = null; } } async function loadProfile() { if (!reader) return; const { data, error } = await supabase.from("users").select("*").eq("auth_user_id", reader.id).maybeSingle(); if (error) console.warn("Profile load error:", error.message); const profile = data || {}; const name = profile.name || reader.user_metadata?.display_name || reader.email?.split("@")[0] || "Reader"; const avatar = profile.avatar_url || profile.profile_picture || profile.photo_url || reader.user_metadata?.avatar_url || reader.user_metadata?.picture || ""; const gender = profile.gender || reader.user_metadata?.gender || "Not provided"; const coins = Number(profile.coin_balance ?? 0); $("coin-count").textContent = coins; $("wallet-coins").textContent = coins; $("profile-coins").textContent = coins; $("profile-display-name").value = name; $("profile-name").textContent = name; $("profile-email").textContent = reader.email || profile.email || "Email not available"; $("profile-reader-id").textContent = profile.id ?? reader.id; $("profile-gender").textContent = gender; $("profile-tier").textContent = profile.subscription_tier || "Free"; $("profile-tier-stat").textContent = profile.subscription_tier || "Free"; $("profile-avatar").innerHTML = avatar ? `Profile picture` : "โ™ก"; } async function loadLibrary() { if (!reader) { $("library-note").hidden = false; renderBookList("library-books", []); return; } const { data, error } = await supabase.from("library").select("*").eq("user_id", reader.id); if (error) { console.warn("Library load error:", error.message); return; } libraryIds = new Set((data || []).map(x => String(x.book_id))); $("profile-saved").textContent = libraryIds.size; $("library-note").hidden = libraryIds.size > 0; renderBookList("library-books", books.filter(b => libraryIds.has(String(b.id)))); } async function loadTransactions() { if (!reader) return; const { data, error } = await supabase.from("transactions").select("*").eq("user_id", reader.id).order("created_at", { ascending: false }).limit(20); if (error) { console.warn("Transactions load error:", error.message); $("transactions").innerHTML = `

Transaction history is unavailable right now.

`; return; } if (!data || data.length === 0) { $("transactions").innerHTML = `

No transactions yet.

`; return; } $("transactions").innerHTML = data.map(t => `
${t.payment_status || "Transaction"} ยท ${t.amount_ngn ?? 0} NGN ${t.coins_added ?? 0} coins
`).join(""); } async function loadNotifications() { const list = $("notification-list"); if (!list) return; if (!reader) { list.innerHTML = `

Log in to view your notifications.

`; return; } const { data, error } = await supabase.from("notification").select("*").eq("user_id", reader.id).order("created_at", { ascending: false }).limit(30); if (error) { console.warn("Notifications load error:", error.message); list.innerHTML = `

Notifications are unavailable right now.

`; return; } if (!data || data.length === 0) { list.innerHTML = `

You're all caught up. No notifications are available.

`; return; } list.innerHTML = data.map(n => `
${escapeHTML(n.title || "Notification")}

${escapeHTML(n.message || n.body || "")}

${new Date(n.created_at).toLocaleString()}
${escapeHTML(n.type || "info")}
`).join(""); } async function openBook(id) { currentBook = books.find(b => String(b.id) === String(id)); if (!currentBook) return; $("detail-title").textContent = currentBook.title || "Untitled story"; $("detail-author").textContent = currentBook.author || currentBook.author_name || ""; $("detail-genre").textContent = currentBook.genre || "Fiction"; $("detail-description").textContent = currentBook.synopsis || currentBook.description || "Discover this story on Fictional World Library."; $("detail-status").textContent = currentBook.status || ""; const cover = currentBook.cover_url || currentBook.cover_image; $("detail-cover").innerHTML = cover ? `` : `
${escapeHTML(currentBook.title || "Story")}
`; $("save-book").textContent = libraryIds.has(String(id)) ? "โœ“ In my library" : "๏ผ‹ Add to library"; $("chapter-list").innerHTML = `

Loading chapters...

`; showPage("book"); const { data, error } = await supabase.from("chapters").select("*").eq("book_id", String(id)).order("chapter_number", { ascending: true }); if (error) { console.error("Chapters:", error); $("chapter-list").innerHTML = `

Chapters could not be loaded. Please try again.

`; return; } chapters = (data || []).filter(c => String(c.pub_status || "").trim().toLowerCase() === "published"); renderChapterList(); } function renderChapterList() { $("chapter-list").innerHTML = chapters.length ? chapters.map((c, i) => { const locked = c.locked === true && !unlockedChapterIds.has(String(c.id)); return `
${locked ? "๐Ÿ”’" : "๐Ÿ“–"} ${escapeHTML(c.title || `Chapter ${i + 1}`)} ${locked ? "10 coins" : "FREE"}
`; }).join("") : `
No published chapters are available yet.
`; } async function openChapter(index) { if (!reader) { notify("Please log in to read chapters."); showPage("account"); return; } const chapter = chapters[index]; if (!chapter) return; const chapterId = String(chapter.id); const isLocked = chapter.locked === true && !unlockedChapterIds.has(chapterId); if (isLocked) { if (!confirm("Unlock this chapter for 10 coins?")) return; const { data: unlockData, error: unlockError } = await supabase.rpc("unlock_chapter", { p_chapter_id: chapterId }); if (unlockError) { console.error("Unlock error:", unlockError); notify(unlockError.message || "Unable to unlock chapter."); return; } if (unlockData?.success !== true) { notify("The chapter could not be unlocked."); return; } unlockedChapterIds.add(chapterId); await loadProfile(); renderChapterList(); } const { data, error } = await supabase.rpc("get_chapter_content", { p_chapter_id: chapterId }); if (error) { console.error("Chapter content error:", error); notify(error.message || "The chapter could not be opened."); return; } const content = typeof data === "string" ? data : data?.content || ""; if (!content) { notify("Chapter content is unavailable."); return; } displayChapter(index, chapter, content); } function displayChapter(index, chapter, content) { currentChapterIndex = index; $("reader-book-title").textContent = currentBook?.title || ""; $("reader-chapter-title").textContent = chapter.title || `Chapter ${index + 1}`; $("reader-content").textContent = content; $("previous-chapter").disabled = index <= 0; $("next-chapter").disabled = index >= chapters.length - 1; showPage("reader"); } async function buyProduct(type, id) { if (!reader) { notify("Please log in before making a purchase."); showPage("account"); return; } notify("Connecting to secure checkout..."); const { data, error } = await supabase.functions.invoke("initialize-payment", { body: { product_type: type, product_id: id, billing_cycle: "monthly" } }); if (error || !data?.authorization_url) { notify("Checkout could not be started. Please try again later."); console.error(error || data); return; } window.location.href = data.authorization_url; } document.addEventListener("click", async e => { const pageButton = e.target.closest("[data-page]"); if (pageButton) { e.preventDefault(); showPage(pageButton.dataset.page); } const genreButton = e.target.closest("[data-genre]"); if (genreButton) { selectedGenre = genreButton.dataset.genre; renderBooks(); showPage("browse"); } const card = e.target.closest("[data-open-book]"); if (card) await openBook(card.dataset.openBook); const chapterButton = e.target.closest(".open-chapter"); if (chapterButton) await openChapter(Number(chapterButton.dataset.index)); const purchase = e.target.closest(".buy-product"); if (purchase) await buyProduct(purchase.dataset.type, purchase.dataset.id); const libraryFilter = e.target.closest("[data-library-filter]"); if (libraryFilter) { const status = libraryFilter.dataset.libraryFilter; renderBookList("library-books", books.filter(b => libraryIds.has(String(b.id)) && (status === "all" || String(b.status || "").toLowerCase() === status) )); } }); $("search-btn")?.addEventListener("click", renderBooks); $("search-input")?.addEventListener("input", renderBooks); $("theme-toggle")?.addEventListener("click", () => { document.body.classList.toggle("light"); localStorage.setItem("fw-theme", document.body.classList.contains("light") ? "light" : "dark"); }); $("settings-theme")?.addEventListener("click", () => $("theme-toggle")?.click()); if (localStorage.getItem("fw-theme") === "light") document.body.classList.add("light"); $("switch-auth")?.addEventListener("click", () => { isRegister = !isRegister; $("account-heading").textContent = isRegister ? "Create your account" : "Welcome back"; $("account-description").textContent = isRegister ? "Join Fictional World Library." : "Log in to continue your reading journey."; $("name-field").hidden = !isRegister; $("auth-name").required = isRegister; $("auth-submit").textContent = isRegister ? "Create account" : "Log in"; $("switch-auth").textContent = isRegister ? "Already have an account? Log in" : "Create an account"; }); $("auth-form")?.addEventListener("submit", async e => { e.preventDefault(); const email = $("auth-email").value.trim(); const password = $("auth-password").value; $("auth-message").textContent = "Please wait..."; let result; if (isRegister) { result = await supabase.auth.signUp({ email, password, options: { data: { display_name: $("auth-name").value.trim() } } }); } else { result = await supabase.auth.signInWithPassword({ email, password }); } if (result.error) { $("auth-message").textContent = result.error.message; return; } if (isRegister && !result.data.session) { $("auth-message").textContent = "Check your email to confirm your account."; return; } reader = result.data.user || result.data.session?.user || null; $("auth-message").textContent = "You're signed in."; updateAccountUI(); await loadProfile(); await loadLibrary(); await loadTransactions(); await loadNotifications(); showPage("profile"); }); $("forgot-password")?.addEventListener("click", () => showPage("reset")); $("reset-form")?.addEventListener("submit", async e => { e.preventDefault(); const { error } = await supabase.auth.resetPasswordForEmail($("reset-email").value.trim()); notify(error ? error.message : "If the email is registered, a reset link will be sent."); }); $("logout-btn")?.addEventListener("click", async () => { const { error } = await supabase.auth.signOut(); if (error) { notify(error.message || "Could not sign out."); return; } reader = null; libraryIds.clear(); unlockedChapterIds.clear(); updateAccountUI(); $("coin-count").textContent = "0"; $("wallet-coins").textContent = "0"; $("profile-coins").textContent = "0"; $("profile-saved").textContent = "0"; $("transactions").innerHTML = `

Log in to view transactions.

`; notify("You have logged out."); showPage("home"); }); $("save-profile")?.addEventListener("click", async () => { if (!reader) { notify("Please log in first."); showPage("account"); return; } const name = $("profile-display-name").value.trim(); const { error } = await supabase.auth.updateUser({ data: { display_name: name } }); if (error) notify(error.message); else { $("profile-name").textContent = name || "Reader"; notify("Profile updated."); } }); $("save-book")?.addEventListener("click", async () => { if (!reader || !currentBook) { notify("Please log in to save stories."); showPage("account"); return; } const id = String(currentBook.id); if (libraryIds.has(id)) { notify("This story is already in your library."); return; } const { error } = await supabase.from("library").insert({ user_id: reader.id, book_id: id }); if (error) notify(error.message); else { libraryIds.add(id); $("save-book").textContent = "โœ“ In my library"; await loadLibrary(); notify("Added to your library."); } }); $("start-reading")?.addEventListener("click", () => { if (chapters.length) openChapter(0); else notify("No chapters are available yet."); }); $("back-to-book")?.addEventListener("click", () => showPage("book")); $("previous-chapter")?.addEventListener("click", () => { if (currentChapterIndex > 0) openChapter(currentChapterIndex - 1); }); $("next-chapter")?.addEventListener("click", () => { if (currentChapterIndex < chapters.length - 1) openChapter(currentChapterIndex + 1); }); supabase.auth.onAuthStateChange(async (_event, session) => { reader = session?.user || null; updateAccountUI(); if (reader) { await loadProfile(); await loadLibrary(); await loadTransactions(); await loadNotifications(); if ($("page-account") && $("page-account").classList.contains("active")) { showPage("profile"); } } else { libraryIds.clear(); } }); renderGenres(); renderPackages(); loadBooks(); getSession();