Skip to content

Commit

Permalink
improved styles and cleaned up methods
Browse files Browse the repository at this point in the history
  • Loading branch information
GitPaulo committed Jan 7, 2025
1 parent 2f560f1 commit 2b0012a
Show file tree
Hide file tree
Showing 10 changed files with 512 additions and 411 deletions.
2 changes: 1 addition & 1 deletion src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ body {
z-index: 2;
padding: 20px;
height: 84vh;
overflow: auto;
overflow: visible; /* Allows content to "pop out" */
background-color: rgba(244, 246, 244, 0.7);
border-radius: inherit;
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/Loading.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="bg-white p-4 rounded-lg shadow-md max-w-max mx-auto">
<p class="text-center text-gray-600">
Preparing your quests... <b>K-kupo!</b>
Preparing your quests... <b>kupo!</b>
</p>
</div>
<img src="loading.gif" alt="Loading" class="mx-auto mt-4" />
3 changes: 1 addition & 2 deletions src/lib/components/Progress.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
import { onMount } from "svelte";
import { fade } from "svelte/transition";
import type { ExpansionProgress } from "$lib/stores/progressStore";
import { progress } from "$lib/stores/progressStore";
import type { ExpansionProgress } from "$lib/stores/questsStore";
let progressData: Record<string, ExpansionProgress> = {};
// Initialize progress data on component mount
onMount(() => {
progress.subscribe((value) => {
progressData = value;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/Title.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script>
import { slide } from "svelte/transition";
import { loading } from "../stores/questsStore";
import { isLoadingQuests } from "../stores/questsStore";
</script>

{#if $loading}
{#if $isLoadingQuests}
<div class="relative flex flex-col mb-8 justify-center items-center">
<div class="bg-white rounded-lg p-6 shadow w-full relative max-w-fit">
<div class="text-center">
Expand Down
39 changes: 15 additions & 24 deletions src/lib/model.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
* Unlock model
*/
export interface Unlock {
Name: string; // Name of the unlock
Image: string; // Path to the unlock image
ContentTypeID: number; // Content type ID
Name: string; // Name of the unlock
Image: string; // Path to the unlock image
ContentTypeID: number; // Content type ID
ContentTypeName: string; // Content type name
}

/**
* Quest model
*/
export type Quest = {
"#": number; // Unique identifier for the quest
Id: string; // Quest identifier string
Name: string; // Name of the quest
"#": number; // Unique identifier for the quest
Id: string; // Quest identifier string
Name: string; // Name of the quest
Description: string | null; // Description of the quest
ExpansionName: string; // Expansion name
EventIconType: number; // Event icon type, representing the type of quest (3 for MSQ)
PreviousQuests: number[]; // Array containing up to 4 previous quest IDs
NextMSQ: number | null; // ID of the next MSQ quest or null if the last MSQ quest
QuestGroup: string | null; // Starting location or null if not applicable
Image: string | null; // Path to the quest image or null if not available
Unlocks: Unlock[]; // Array of unlocks
ExpansionName: string; // Expansion name
EventIconType: number; // Event icon type, representing the type of quest (3 for MSQ)
PreviousQuests: number[]; // Array containing up to 4 previous quest IDs
NextMSQ: number | null; // ID of the next MSQ quest or null if the last MSQ quest
QuestGroup: string | null; // Starting location or null if not applicable
Image: string | null; // Path to the quest image or null if not available
Unlocks: Unlock[]; // Array of unlocks
};

/**
Expand All @@ -36,20 +36,11 @@ export type Quests = {
* Expansion model
*/
export type Expansion = {
name: string; // Name of the expansion
quests: Quests; // Quests grouped by location or type
name: string; // Name of the expansion
quests: Quests;// Quests grouped by location or type
};

/**
* Expansions quests array
*/
export type ExpansionsQuests = Array<Expansion>; // Array of expansions and their quests

/**
* Expansion progress model
*/
export type ExpansionProgress = {
percent: number; // The percentage of quests completed in the expansion
completed: number; // The total number of completed quests
total: number; // The total number of quests in the expansion
};
14 changes: 2 additions & 12 deletions src/lib/stores/modalManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,7 @@ export const modalState = writable<ModalState>({
allowCancel: true,
});

/**
* Opens a modal with the given title and message.
* @param title
* @param message
* @param onConfirm
* @param onCancel
* @param confirmLabel
* @param cancelLabel
*/
// Open a modal with the given title and message.
export function openModal(
title: string,
message: string,
Expand All @@ -52,9 +44,7 @@ export function openModal(
});
}

/**
* Closes the modal.
*/
// Close the modal.
export function closeModal(): void {
modalState.update((state) => ({ ...state, show: false }));
}
214 changes: 191 additions & 23 deletions src/lib/stores/progressStore.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,213 @@
import { writable, get } from "svelte/store";
import { quests, completedQuests } from "$lib/stores/questsStore";
import type { ExpansionProgress } from "$lib/stores/questsStore";
import {
compressToEncodedURIComponent,
decompressFromEncodedURIComponent,
} from "lz-string";
import {
quests,
completedQuests,
currentExpansion,
} from "$lib/stores/questsStore";
import { openModal } from "$lib/stores/modalManager";

export type ExpansionProgress = {
percent: number;
completed: number;
total: number;
};

export type QuestGroupProgress = {
percent: number;
completed: number;
total: number;
};

// Store for overall progress
export const progress = writable<Record<string, ExpansionProgress>>({});

// Function to calculate progress for a single expansion
export function calculateExpansionProgress(expansionName: string) {
const $quests = get(quests);
if (!$quests.length) return { percent: 0, completed: 0, total: 0 };
// Get progress for a single expansion
export function getProgressByExpansion(
expansionName: string
): ExpansionProgress {
return get(progress)[expansionName] || { percent: 0, completed: 0, total: 0 };
}

// Returns the progress for a single quest group inside the expansion
export function getProgressByQuestGroup(
expansionName: string,
questGroup: string
): QuestGroupProgress {
const allQuests = get(quests);
if (!allQuests.length) {
return { percent: 0, completed: 0, total: 0 };
}

// Find the expansion by name
const expansion = allQuests.find((exp) => exp.name === expansionName);
if (!expansion || !expansion.quests) {
return { percent: 0, completed: 0, total: 0 };
}

// Get the quest group within the expansion
const questsArray = expansion.quests[questGroup] || [];
const totalQuests = questsArray.length;
const completedCount = questsArray.reduce((count, quest) => {
return count + (get(completedQuests)[quest["#"]] ? 1 : 0);
}, 0);

const expansion = $quests.find((exp) => exp.name === expansionName);
return {
percent:
totalQuests > 0 ? Math.floor((completedCount / totalQuests) * 100) : 0,
completed: completedCount,
total: totalQuests,
};
}

// Calculate progress for a single expansion
export function calculateExpansionProgress(
expansionName: string
): ExpansionProgress {
const allQuests = get(quests);
if (!allQuests.length) return { percent: 0, completed: 0, total: 0 };

const expansion = allQuests.find((exp) => exp.name === expansionName);
if (!expansion) return { percent: 0, completed: 0, total: 0 };

const questsArray = Object.values(expansion.quests).flat();
const totalQuests = questsArray.length;
const completedQuestsCount = questsArray.filter(
const completedCount = questsArray.filter(
(quest) => get(completedQuests)[quest["#"]]
).length;

const percent =
totalQuests > 0
? Math.floor((completedQuestsCount / totalQuests) * 100)
: 0;

return {
percent,
completed: completedQuestsCount,
percent:
totalQuests > 0 ? Math.floor((completedCount / totalQuests) * 100) : 0,
completed: completedCount,
total: totalQuests,
};
}

// Function to calculate progress for all expansions
export function calculateAllProgress() {
const $quests = get(quests);
if (!$quests.length) return;
// Calculate progress for all expansions
export function initAllExpansionProgress() {
const allQuests = get(quests);
if (!allQuests.length) return;

const updatedProgress: Record<string, ExpansionProgress> = {};
allQuests.forEach((expansion) => {
updatedProgress[expansion.name] = calculateExpansionProgress(
expansion.name
);
});

progress.set(updatedProgress);
}

// Get shared progress from URL
export function getSharedProgress(): string {
try {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get("progress") || "";
} catch {
console.warn("Failed to parse shared progress from URL.");
return "";
}
}

// Check if shared progress exists
export function hasSharedProgress(): boolean {
return !!getSharedProgress();
}

// Load shared progress from URL
export function loadSharedProgress() {
const compressedState = getSharedProgress();

if (compressedState) {
openModal(
"Shared Progress",
"You are viewing a shared progress link. Your progress won't be overwritten without prompt.",
() => {},
() => {
window.location.href =
window.location.origin + window.location.pathname;
},
"Understood",
"",
false
);

const newProgress: Record<string, ExpansionProgress> = {};
for (const expansion of $quests) {
newProgress[expansion.name] = calculateExpansionProgress(expansion.name);
try {
const state = JSON.parse(
decompressFromEncodedURIComponent(compressedState) || "{}"
);

const reconstructedCompleted: Record<number, boolean> = {};
state.completedQuests.forEach((id: string) => {
reconstructedCompleted[Number(id)] = true;
});

completedQuests.set(reconstructedCompleted);
currentExpansion.set(state.currentExpansion);
} catch (error) {
console.error("Failed to decode shared progress:", error);
}
}
}

// Generate a shareable link for progress
export function generateShareableLink() {
const completed = get(completedQuests);
const completedIds = Object.keys(completed).filter(
(id) => completed[Number(id)]
);

progress.set(newProgress);
const state = {
completedQuests: completedIds,
currentExpansion: get(currentExpansion),
};

try {
const compressedState = compressToEncodedURIComponent(
JSON.stringify(state)
);
const basePath =
window.location.origin + window.location.pathname.replace(/\/$/, "");
const shareableLink = `${basePath}?progress=${compressedState}`;

navigator.clipboard
.writeText(shareableLink)
.then(() =>
openModal(
"Shareable Link",
"The progress link has been copied to your clipboard. Share it with your friends!",
() => {},
() => {},
"Understood",
"",
false
)
)
.catch(() =>
openModal(
"Shareable Link",
"Failed to copy the progress link to your clipboard. Please try again.",
() => {},
() => {},
"Understood",
"",
false
)
);
} catch (error) {
console.error("Failed to generate shareable link:", error);
openModal(
"Shareable Link",
"An error occurred while generating the progress link. Please try again later.",
() => {},
() => {},
"Understood",
"",
false
);
}
}
Loading

0 comments on commit 2b0012a

Please sign in to comment.