Skip to content

Commit

Permalink
Add toast for achievements
Browse files Browse the repository at this point in the history
  • Loading branch information
aweell committed Nov 19, 2024
1 parent 7b0e54f commit bf3daf4
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 28 deletions.
12 changes: 6 additions & 6 deletions src/pages/advent-calendar-2024/components/calendar-card.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
Text10,
skinVars,
Tag,
Text8,
Text5,
Text,
IconLockEyeClosedFilled,
Circle,
Expand Down Expand Up @@ -86,8 +86,8 @@ const CalendarCard = ({
<div
style={{
position: "absolute",
top: 32,
right: 32,
top: 24,
right: 24,
}}
>
<div
Expand Down Expand Up @@ -148,15 +148,15 @@ const CalendarCard = ({
>
<Stack space="between">
<Stack space={8}>
<Text8
<Text5
color={
status === CARD_STATES.AVAILABLE
? skinVars.colors.textPrimary
: skinVars.colors.textSecondary
}
>
{DayOfWeek}
</Text8>
</Text5>

<StatusIndicator />
</Stack>
Expand All @@ -167,7 +167,7 @@ const CalendarCard = ({

<Inline space="between" alignItems="center">
<Text
size={80}
size={64}
weight="medium"
color={
status === CARD_STATES.AVAILABLE
Expand Down
29 changes: 29 additions & 0 deletions src/pages/advent-calendar-2024/components/toast-wrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useState, useEffect } from "react";
import Toast from "./toast"; // Assuming your Toast component is already implemented

const ToastWrapper = ({ toasts, removeToast }) => {
return (
<div
style={{
position: "absolute",
bottom: "16px",
right: "16px",
}}
>
<div style={{ position: "relative" }}>
{toasts.map((toast, index) => (
<Toast
id={toast.id}
key={toast.id}
title={toast.name}
description={toast.message}
icon={toast.icon}
onClose={() => removeToast(toast.id)} // Dismiss the toast by `id`
/>
))}
</div>
</div>
);
};

export default ToastWrapper;
111 changes: 111 additions & 0 deletions src/pages/advent-calendar-2024/components/toast.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { useEffect, useState, useRef } from "react";
import {
Stack,
Text3,
Text2,
skinVars,
Inline,
ButtonLink,
IconButton,
IconCloseRegular,
} from "@telefonica/mistica";

const Toast = ({
title,
description,
icon: Icon,
duration = 3000,
style,
onClose,
}) => {
const [visible, setVisible] = useState(true);
const [isHovered, setIsHovered] = useState(false);
const timeoutRef = useRef(null);

const clearHideTimeout = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
};

const startHideTimeout = () => {
clearHideTimeout();
timeoutRef.current = setTimeout(() => {
setVisible(false); // This will trigger unmount if necessary
onClose?.(); // Pass the ID to remove the toast
}, duration);
};

useEffect(() => {
if (!isHovered) {
startHideTimeout(); // Start timeout when not hovered
} else {
clearHideTimeout(); // Clear timeout when hovered
}

return () => clearHideTimeout(); // Cleanup timeout on unmount
}, [isHovered, duration]);

// Always render dismiss button, regardless of toast visibility
const handleDismiss = () => {
setVisible(false);
clearHideTimeout();
onClose?.(); // Ensure it triggers onClose from parent
};

if (!visible) return null; // Hide toast when not visible

return (
<div
style={{
background: skinVars.colors.background,
padding: "24px",
borderRadius: "8px",
border: `2px solid ${skinVars.colors.border}`,
zIndex: 1000,
width: 480,
position: "relative", // Ensures dismiss button is on top
...style,
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<Inline space={16}>
<div
style={{
background:
"linear-gradient(120deg, rgba(255,249,208,1) 0%, rgba(255,178,178,1) 24%, rgba(59,95,253,1) 66%, rgba(255,99,114,1) 100%)",
padding: "16px",
display: "inline-flex",
justifyContent: "center",
alignItems: "center",
borderRadius: "8px",
outline: `1px solid ${skinVars.colors.inverse}`,
outlineOffset: "-4px",
position: "relative",
}}
>
<Icon color={skinVars.colors.inverse} />
</div>
<Stack space={8}>
<Text2 weight="bold">Achievement unlocked</Text2>
<Text3 weight="bold">{title}</Text3>
<Text3>{description}</Text3>
<ButtonLink bleedLeft to={"/advent-calendar-2024/progress-view"}>
View progress
</ButtonLink>
</Stack>
{/* Dismiss button should always be on top */}
<div style={{ position: "absolute", top: 8, right: 8 }}>
<IconButton
Icon={IconCloseRegular}
onPress={handleDismiss} // Handle dismiss in the component itself
/>
</div>
</Inline>
</div>
);
};

export default Toast;
23 changes: 22 additions & 1 deletion src/pages/advent-calendar-2024/pages/calendar-view.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
IllustrationWishesLetter,
IllustrationWoolClothes,
} from "../assets/illustrations/illustrations";
import ToastWrapper from "../components/toast-wrapper";

const CalendarView = () => {
const location = useLocation();
Expand All @@ -36,6 +37,23 @@ const CalendarView = () => {
const savedDays = localStorage.getItem("completedDays");
return savedDays ? JSON.parse(savedDays) : [];
});
const [toastContent, setToastContent] = useState(null);
const [showToast, setShowToast] = useState(false);
const [toasts, setToasts] = useState([]); // Array to manage multiple toasts

const handleShowToast = ({ id, icon, message, name }) => {
const newToast = {
id,
icon,
name,
message,
};
setToasts((prevToasts) => [...prevToasts, newToast]);
};

const removeToast = (id) => {
setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
};

const [achievements, setAchievements] = useState([]);

Expand Down Expand Up @@ -88,7 +106,8 @@ const CalendarView = () => {
achievements,
setAchievements,
navigate,
location
location,
handleShowToast
);
}
};
Expand Down Expand Up @@ -184,6 +203,8 @@ const CalendarView = () => {
</Stack>
</Box>
</ResponsiveLayout>

<ToastWrapper toasts={toasts} removeToast={removeToast} />
</>
);
};
Expand Down
31 changes: 10 additions & 21 deletions src/pages/advent-calendar-2024/utils/achievement-config.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const achievementsConfig = [
description: "Complete a task on Christmas Day",
icon: IconBellFilled,
check: (newCompletedDays) => newCompletedDays.some(isChristmasDay),
message: "Achievement Unlocked: You completed a task on Christmas Day!",
message: "You completed a task on Christmas Day!",
isSecret: false,
},
{
Expand All @@ -28,8 +28,7 @@ export const achievementsConfig = [
description: "Unlock the first day of the calendar",
icon: IconEyeFilled,
check: (newCompletedDays) => newCompletedDays.length > 0, // Unlock when the first day is completed
message:
"Achievement Unlocked: First Glance - You have unlocked the first day of the calendar!",
message: "You have unlocked the first day of the calendar!",
isSecret: false,
},
{
Expand All @@ -38,8 +37,7 @@ export const achievementsConfig = [
description: "Complete two tasks on consecutive days",
icon: IconArrowUpDownFilled,
check: (newCompletedDays) => hasConsecutiveDays(newCompletedDays),
message:
"Achievement Unlocked: Dynamic Duo - You have unlocked two consecutive days!",
message: "You have unlocked two consecutive days!",
isSecret: false,
},
{
Expand All @@ -49,8 +47,7 @@ export const achievementsConfig = [
icon: IconCalendarFilled,
check: (newCompletedDays) =>
hasConsecutiveDays(newCompletedDays) && newCompletedDays.length >= 7,
message:
"Achievement Unlocked: What a Week - You have unlocked 7 consecutive days!",
message: "You have unlocked 7 consecutive days!",
isSecret: false,
},
{
Expand All @@ -59,8 +56,7 @@ export const achievementsConfig = [
description: "Unlock a weekend day",
icon: IconBeachUmbrellaFilled,
check: (newCompletedDays) => newCompletedDays.some(isWeekendDay),
message:
"Achievement Unlocked: Rest Day - You have unlocked a weekend day!",
message: "You have unlocked a weekend day!",
isSecret: false,
},
{
Expand All @@ -70,8 +66,7 @@ export const achievementsConfig = [
icon: IconSnowflakeRegular,
check: (newCompletedDays) =>
newCompletedDays.length === TOTAL_CALENDAR_DAYS,
message:
"Achievement Unlocked: Advent Champion - You have unlocked all days!",
message: "You have unlocked all days!",
isSecret: true,
},
];
Expand Down Expand Up @@ -122,32 +117,26 @@ export const checkAndUnlockAchievements = (
achievements,
setAchievements,
navigate,
location
location,
showToast
) => {
achievementsConfig.forEach(({ id, check, message, isSecret }) => {
achievementsConfig.forEach(({ id, check, name, message, icon, isSecret }) => {
const isAchievementUnlocked =
achievements[id]?.isCompleted || getAchievementFromLocalStorage(id);

console.log(
`Checking achievement: ${id}, Unlocked: ${isAchievementUnlocked}`
);

// If the achievement is not yet unlocked and the condition is met
if (!isAchievementUnlocked) {
if (check(newCompletedDays)) {
console.log(`Unlocking achievement: ${id}`); // Debugging
alert(message); // Immediate test for alert functionality

setAchievements((prev) => {
const updatedAchievements = {
...prev,
[id]: { isCompleted: true, isSecret }, // Mark achievement as completed
};
console.log("Updated Achievements:", updatedAchievements); // Check state update
return updatedAchievements;
});

setAchievementToLocalStorage(id, true);
showToast({ id, icon, name, message });
updateAchievements(
Object.keys(achievements)
.filter((key) => achievements[key].isCompleted)
Expand Down

0 comments on commit bf3daf4

Please sign in to comment.