This commit is contained in:
George Powell
2025-12-19 03:36:59 -05:00
parent 68a946a0a0
commit 0d50ff5f27
5 changed files with 145 additions and 34 deletions

View File

@@ -1,5 +1,9 @@
{
"files.associations": {
"*.css": "tailwindcss"
},
"workbench.colorCustomizations": {
"titleBar.activeBackground": "#7e498b",
"settings.headerBorder": "#fff"
}
}

View File

@@ -3,7 +3,7 @@
</script>
<div
class="mt-12 p-4 bg-linear-to-r from-blue-50 to-indigo-50 rounded-2xl shadow-md text-center text-sm md:text-base text-gray-600"
class="my-12 p-4 bg-linear-to-r from-blue-50 to-indigo-50 rounded-2xl shadow-md text-center text-sm md:text-base text-gray-600"
in:fade={{ delay: 1500, duration: 1000 }}
>
Thank you so much for playing! Feel free to email me directly with feedback:

View File

@@ -9,16 +9,66 @@
averageGuesses: number;
}
interface WeightedMessage {
text: string;
weight: number;
}
let {
grade,
statsData,
correctBookId,
handleShare,
copyToClipboard,
copied = $bindable(false),
statsSubmitted,
guessCount,
} = $props();
let bookName = $derived(getBookById(correctBookId)?.name ?? "");
let hasWebShare = $derived(
typeof navigator !== "undefined" && "share" in navigator,
);
// List of congratulations messages with weights
const congratulationsMessages: WeightedMessage[] = [
{ text: "🎉 Congratulations! 🎉", weight: 1000 },
{ text: "⭐ You got it! ⭐", weight: 10 },
{ text: "🎉 Yup 🎉", weight: 5 },
{ text: "👍🏻 Very nice! 👍🏻", weight: 1 },
];
// Function to select a random message based on weights
function getRandomCongratulationsMessage(): string {
// Special case for first try success
if (guessCount === 1) {
const n = Math.random();
if (n < 0.95) {
return "🤯 First try! 🤯";
} else {
return "‼️ Axios ‼️";
}
}
const totalWeight = congratulationsMessages.reduce(
(sum, msg) => sum + msg.weight,
0,
);
let random = Math.random() * totalWeight;
for (const message of congratulationsMessages) {
random -= message.weight;
if (random <= 0) {
return message.text;
}
}
// Fallback to first message if something goes wrong
return congratulationsMessages[0].text;
}
// Generate the congratulations message
let congratulationsMessage = $derived(getRandomCongratulationsMessage());
</script>
<div
@@ -26,7 +76,7 @@
in:fade={{ delay: 500 }}
>
<h2 class="text-2xl sm:text-4xl font-black mb-4 drop-shadow-lg">
🎉 Congratulations! 🎉
{congratulationsMessage}
</h2>
<p class="text-lg sm:text-xl md:text-2xl">
The verse is from <span
@@ -39,32 +89,48 @@
Your grade: {grade}
</p>
<button
onclick={handleShare}
data-umami-event="Share"
class={`mt-4 text-2xl font-bold p-2 ${
copied
? "bg-green-400/50 hover:bg-green-500/60"
: "bg-white/20 hover:bg-white/30"
} rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none`}
>
{copied ? "Copied to clipboard!" : "📤 Share"}
</button>
{#if hasWebShare}
<button
onclick={handleShare}
data-umami-event="Share"
class="mt-4 text-2xl font-bold p-2 bg-white/20 hover:bg-white/30 rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none"
>
📤 Share
</button>
<button
onclick={copyToClipboard}
data-umami-event="Copy to Clipboard"
class="mt-4 text-2xl font-bold p-2 bg-white/20 hover:bg-white/30 rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none"
>
📋 Copy to clipboard
</button>
{:else}
<button
onclick={handleShare}
data-umami-event="Share"
class={`mt-4 text-2xl font-bold p-2 ${
copied
? "bg-green-400/50 hover:bg-green-500/60"
: "bg-white/20 hover:bg-white/30"
} rounded-lg inline-block transition-all shadow-lg mx-2 cursor-pointer border-none appearance-none`}
>
{copied ? "Copied to clipboard!" : "📤 Share"}
</button>
{/if}
<!-- Statistics Display -->
{#if statsData}
<div class="mt-6 space-y-2 text-lg" in:fade={{ delay: 800 }}>
<p class="font-semibold">
<p class="font-regular">
You were the {toOrdinal(statsData.solveRank)} person to solve today.
</p>
<p class="font-semibold">
You rank <span class="text-2xl font-black"
>{toOrdinal(statsData.guessRank)}</span
> in guesses.
<p class="font-regular">
You rank <span class="">{toOrdinal(statsData.guessRank)}</span> in
guesses.
</p>
<p class="opacity-90">
Average: <span class="font-semibold"
>{statsData.averageGuesses.toFixed(1)}</span
>{Math.ceil(statsData.averageGuesses)}</span
> guesses
</p>
</div>

View File

@@ -1,16 +1,8 @@
<script lang="ts">
import { bibleBooks, type BibleBook } from "$lib/types/bible";
interface Guess {
book: BibleBook;
testamentMatch: boolean;
sectionMatch: boolean;
adjacent: boolean;
}
import type { PageProps } from "./$types";
import { browser } from "$app/environment";
import { fade } from "svelte/transition";
import VerseDisplay from "$lib/components/VerseDisplay.svelte";
import SearchInput from "$lib/components/SearchInput.svelte";
@@ -19,6 +11,13 @@
import Feedback from "$lib/components/Feedback.svelte";
import { getGrade } from "$lib/utils/game";
interface Guess {
book: BibleBook;
testamentMatch: boolean;
sectionMatch: boolean;
adjacent: boolean;
}
let { data }: PageProps = $props();
let dailyVerse = $derived(data.dailyVerse);
@@ -254,9 +253,7 @@
submitStats();
});
async function share() {
if (!browser) return;
function generateShareText(): string {
const emojis = guesses
.slice()
.reverse()
@@ -278,12 +275,18 @@
new Date(`${dailyVerse.date}T00:00:00`),
);
const siteUrl = window.location.origin;
const shareText = [
return [
`📖 Bibdle | ${formattedDate} 📖`,
`${grade} (${guesses.length} guesses)`,
`${emojis}\n`,
siteUrl,
].join("\n");
}
async function share() {
if (!browser) return;
const shareText = generateShareText();
try {
if ("share" in navigator) {
@@ -297,6 +300,23 @@
}
}
async function copyToClipboard() {
if (!browser) return;
const shareText = generateShareText();
try {
await (navigator as any).clipboard.writeText(shareText);
copied = true;
setTimeout(() => {
copied = false;
}, 5000);
} catch (err) {
console.error("Copy to clipboard failed:", err);
throw err;
}
}
function handleShare() {
if (copied || !browser) return;
const useClipboard = !("share" in navigator);
@@ -343,8 +363,10 @@
{statsData}
{correctBookId}
{handleShare}
{copyToClipboard}
bind:copied
{statsSubmitted}
guessCount={guesses.length}
/>
{/if}

25
todo.md
View File

@@ -1,12 +1,31 @@
# todo
## v2
## v3
- avg guesses per bible verse updating daily (on completion: avg. guesses: 6)
- you're the XXXth person to guess correctly today
- HH:MM until next verse (sundown local time)
- https://github.com/GitSquared/node-geolite2-redist
get approximate user location from IP
- https://www.npmjs.com/package/sunrise-sunset-js
get sunrise/sunset times by lat/longitude
- top XX% next to the "rank" stat on complete
-
# bibdle unlimited
- pay for unlimited bible verses daily,
- and extra features like multiple choice, custom groups, custom leaderboards, etc.
# done
- improve design (uniform column widths on desktop)
- moved to bibdle.com
- v2: avg guesses per bible verse updating daily (on completion: avg. guesses: 6)
- v2: you're the XXXth person to guess correctly today
- metadata
- favicon
- site title