Ananto30/ask-hadith

View on GitHub
web/src/lib/Hadith.svelte

Summary

Maintainability
Test Coverage
<script lang="ts">
    import { onMount } from 'svelte';
    import { fade, slide } from 'svelte/transition';
    import BookmarkSvg from '$lib/svgs/bookmark.svelte';
    import type { HadithModel } from '../models';
    import { searchKey } from '../store';

    export let hadith: HadithModel;

    let copied = false;
    let bookmarked = false;

    const copyText = () => {
        let text = '';
        if (hadith.hadith_no) {
            text += hadith.narrator_en + '\n';
        }
        text +=
            hadith.body_en +
            '\n' +
            '\n' +
            hadith.collection +
            ' (Book: ' +
            hadith.book_no +
            ', Hadith: ' +
            hadith.book_ref_no +
            ') ';
        if (hadith.hadith_no) {
            text += 'Hadith No: ' + hadith.hadith_no;
        }
        text += '\n' + 'Book:' + hadith.book_en + '\n';
        if (hadith.chapter_en) {
            text += 'Chapter:' + hadith.chapter_en + '\n';
        }
        if (hadith.hadith_grade) {
            text += 'Grade:' + hadith.hadith_grade + '\n';
        }

        const urlEncodedSearchKey = encodeURIComponent($searchKey);

        text += `https://askhadith.com/book?collection_id=${hadith.collection_id}&book=${hadith.book_no}&ref_no=${hadith.book_ref_no}&search_key=${urlEncodedSearchKey}`;

        navigator.clipboard.writeText(text);
        copied = true;
        setTimeout(() => {
            copied = false;
        }, 3000);
    };

    const getLocalBookmarkedHadiths = (): HadithModel[] => {
        let hadiths = JSON.parse(localStorage.getItem('bookmarkedHadiths') || '[]');
        if (!hadiths || !Array.isArray(hadiths) || hadiths.length === 0) {
            hadiths = [];
        }
        return hadiths;
    };

    const bookmarkHadith = () => {
        let hadiths = getLocalBookmarkedHadiths();
        hadiths.push(hadith);
        localStorage.setItem('bookmarkedHadiths', JSON.stringify(hadiths));
        bookmarked = true;
    };

    const unBookmarkHadith = () => {
        let hadiths = getLocalBookmarkedHadiths();
        for (let i = 0; i < hadiths.length; i++) {
            if (
                hadiths[i].book_no === hadith.book_no &&
                hadiths[i].book_ref_no === hadith.book_ref_no &&
                hadiths[i].collection_id === hadith.collection_id
            ) {
                hadiths.splice(i, 1);
                break;
            }
        }
        localStorage.setItem('bookmarkedHadiths', JSON.stringify(hadiths));
        bookmarked = false;
    };

    const isBookmarked = (): boolean => {
        let hadiths = getLocalBookmarkedHadiths();
        for (let i = 0; i < hadiths.length; i++) {
            if (
                hadiths[i].book_no === hadith.book_no &&
                hadiths[i].book_ref_no === hadith.book_ref_no &&
                hadiths[i].collection_id === hadith.collection_id
            ) {
                return true;
            }
        }
        return false;
    };

    onMount(() => {
        bookmarked = isBookmarked();
    });
</script>

<div
    transition:slide
    class="max-w-3xl space-y-2 bg-white p-4 shadow dark:bg-gray-800 md:rounded-lg md:p-6"
>
    <div class="flex flex-row items-center">
        <div class="w-full dark:text-gray-300">
            <h2 class="leading-tight">{hadith.collection}</h2>
            <span class="flex text-xs text-gray-600 dark:text-gray-400">
                Book: {hadith.book_no}, Hadith: {hadith.book_ref_no}
                {#if hadith.hadith_no}
                    (Hadith No: {hadith.hadith_no})
                {/if}
            </span>
        </div>
        <button
            aria-label="Bookmark Hadith"
            class="ml-2 flex h-5 w-5"
            on:click={() => {
                if (bookmarked) unBookmarkHadith();
                else bookmarkHadith();
            }}
        >
            <BookmarkSvg bind:bookmarked />
        </button>
    </div>
    <div class="text-md flex flex-col gap-2 py-2">
        {#if hadith.narrator_en}
            <p class="text-sm text-gray-500 dark:text-gray-400">
                {hadith.narrator_en}
            </p>
        {/if}
        <p class="font-serif dark:text-gray-300">
            {#each hadith.body_en.split(' ') as word}
                {#if hadith.highlights && hadith.highlights.includes(word.replace(/[.,/#!$%^&*;:{}=\-_`~()"']/g, ''))}
                    <span class="font-bold text-green-500">{word} </span>
                {:else}
                    <span class="">{word} </span>
                {/if}
            {/each}
        </p>
    </div>
    <div class="flex space-x-2">
        <div class="flex w-full flex-col pt-2 text-xs text-gray-500 dark:text-gray-400">
            <span>Book: {hadith.book_en} </span>
            <p class="">
                {#if hadith.chapter_en}
                    Chapter:
                    {#each hadith.chapter_en.split(' ') as word}
                        {#if hadith.highlights && hadith.highlights.includes(word.replace(/[.,/#!$%^&*;:{}=\-_`~()"']/g, ''))}
                            <span class="font-bold text-green-500">{word} </span>
                        {:else}
                            <span class="">{word} </span>
                        {/if}
                    {/each}
                {/if}
            </p>
            <p class="">
                <span>Grade: {hadith.hadith_grade || 'Unknown'}</span>
            </p>
        </div>
        <div class="mt-2 flex justify-end">
            <button
                aria-label="Copy Hadith"
                class="my-auto h-7 rounded-lg border border-gray-300 px-2 text-xs text-gray-600 transition duration-200 ease-in-out hover:bg-gray-800 hover:text-white dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-200"
                on:click={copyText}
            >
                {copied ? 'Copied' : 'Copy'}
            </button>
        </div>
    </div>
</div>