diff --git a/src/components/AchievementBadge.vue b/src/components/AchievementBadge.vue
index bd725b9..fc3e6a0 100644
--- a/src/components/AchievementBadge.vue
+++ b/src/components/AchievementBadge.vue
@@ -22,6 +22,20 @@ type AchievementProps = Record<
>;
const achievementProps: AchievementProps = {
+ challengeAccepted: {
+ [AchievementLevel.Bronze]: {
+ textProps: [5],
+ emoji: 'π΄',
+ },
+ [AchievementLevel.Silver]: {
+ textProps: [10],
+ emoji: 'π΅',
+ },
+ [AchievementLevel.Gold]: {
+ textProps: [20],
+ emoji: 'π',
+ },
+ },
committed: {
[AchievementLevel.Bronze]: {
textProps: [3],
@@ -43,7 +57,7 @@ const achievementProps: AchievementProps = {
},
[AchievementLevel.Silver]: {
textProps: [80],
- emoji: 'π₯',
+ emoji: 'π§',
},
[AchievementLevel.Gold]: {
textProps: [150],
diff --git a/src/components/AchievementList.vue b/src/components/AchievementList.vue
index 3fc76a4..a845a25 100644
--- a/src/components/AchievementList.vue
+++ b/src/components/AchievementList.vue
@@ -28,6 +28,26 @@ const {achievements} = storeToRefs(useAppStateStore());
/>
+
The Committed
diff --git a/src/components/__tests__/achievements.spec.ts b/src/components/__tests__/achievements.spec.ts
index 8d60ee3..0531148 100644
--- a/src/components/__tests__/achievements.spec.ts
+++ b/src/components/__tests__/achievements.spec.ts
@@ -17,6 +17,7 @@ const withSetup = (hook: () => T) =>
});
const defaultAchievements: Achievements = {
+ challengeAccepted: AchievementLevel.NoAchievement,
committed: AchievementLevel.NoAchievement,
completionist: AchievementLevel.NoAchievement,
hotStreak: AchievementLevel.NoAchievement,
@@ -36,110 +37,123 @@ describe('achievements', () => {
it('advances completionist', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements([...Array(39)], 0, 0);
+ advanceAchievements([...Array(39)], 0, 0, 0);
expect(achievements.value.completionist).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements([...Array(40)], 0, 0);
+ advanceAchievements([...Array(40)], 0, 0, 0);
expect(achievements.value.completionist).toBe(AchievementLevel.Bronze);
- advanceAchievements([...Array(80)], 0, 0);
+ advanceAchievements([...Array(80)], 0, 0, 0);
expect(achievements.value.completionist).toBe(AchievementLevel.Silver);
- advanceAchievements([...Array(150)], 0, 0);
+ advanceAchievements([...Array(150)], 0, 0, 0);
expect(achievements.value.completionist).toBe(AchievementLevel.Gold);
});
it('goes straight to silver', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements([...Array(80)], 0, 0);
+ advanceAchievements([...Array(80)], 0, 0, 0);
expect(achievements.value.completionist).toBe(AchievementLevel.Silver);
});
it('goes straight to gold', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements([...Array(150)], 0, 0);
+ advanceAchievements([...Array(150)], 0, 0, 0);
expect(achievements.value.completionist).toBe(AchievementLevel.Gold);
});
it('advances experimenterFruit', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(FRUITS, 14), 0, 0);
+ advanceAchievements(take(FRUITS, 14), 0, 0, 0);
expect(achievements.value.experimenterFruit).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(FRUITS, 15), 0, 0);
+ advanceAchievements(take(FRUITS, 15), 0, 0, 0);
expect(achievements.value.experimenterFruit).toBe(AchievementLevel.Gold);
});
it('goes straight to gold', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(FRUITS, 15), 0, 0);
+ advanceAchievements(take(FRUITS, 15), 0, 0, 0);
expect(achievements.value.experimenterFruit).toBe(AchievementLevel.Gold);
});
it('advances experimenterVegetable', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(VEGETABLES, 14), 0, 0);
+ advanceAchievements(take(VEGETABLES, 14), 0, 0, 0);
expect(achievements.value.experimenterVegetable).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(VEGETABLES, 15), 0, 0);
+ advanceAchievements(take(VEGETABLES, 15), 0, 0, 0);
expect(achievements.value.experimenterVegetable).toBe(AchievementLevel.Gold);
});
it('advances experimenterLeafy', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(LEAFIES, 14), 0, 0);
+ advanceAchievements(take(LEAFIES, 14), 0, 0, 0);
expect(achievements.value.experimenterLeafy).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(LEAFIES, 15), 0, 0);
+ advanceAchievements(take(LEAFIES, 15), 0, 0, 0);
expect(achievements.value.experimenterLeafy).toBe(AchievementLevel.Gold);
});
it('advances experimenterBean', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(BEANS, 14), 0, 0);
+ advanceAchievements(take(BEANS, 14), 0, 0, 0);
expect(achievements.value.experimenterBean).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(BEANS, 15), 0, 0);
+ advanceAchievements(take(BEANS, 15), 0, 0, 0);
expect(achievements.value.experimenterBean).toBe(AchievementLevel.Gold);
});
it('advances experimenterRoot', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(ROOTS, 14), 0, 0);
+ advanceAchievements(take(ROOTS, 14), 0, 0, 0);
expect(achievements.value.experimenterRoot).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(ROOTS, 15), 0, 0);
+ advanceAchievements(take(ROOTS, 15), 0, 0, 0);
expect(achievements.value.experimenterRoot).toBe(AchievementLevel.Gold);
});
it('advances experimenterGrain', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements(take(GRAINS, 14), 0, 0);
+ advanceAchievements(take(GRAINS, 14), 0, 0, 0);
expect(achievements.value.experimenterGrain).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements(take(GRAINS, 15), 0, 0);
+ advanceAchievements(take(GRAINS, 15), 0, 0, 0);
expect(achievements.value.experimenterGrain).toBe(AchievementLevel.Gold);
});
it('advances hot streak', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements([], 4, 0);
+ advanceAchievements([], 4, 0, 0);
expect(achievements.value.hotStreak).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements([], 5, 0);
+ advanceAchievements([], 5, 0, 0);
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Bronze);
- advanceAchievements([], 10, 0);
+ advanceAchievements([], 10, 0, 0);
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Silver);
- advanceAchievements([], 20, 0);
+ advanceAchievements([], 20, 0, 0);
expect(achievements.value.hotStreak).toEqual(AchievementLevel.Gold);
});
it('advances committed', async () => {
const {advanceAchievements, achievements} = await withSetup(useAchievements);
- advanceAchievements([], 0, 11);
+ advanceAchievements([], 0, 11, 0);
expect(achievements.value.committed).toEqual(AchievementLevel.NoAchievement);
- advanceAchievements([], 0, 12);
+ advanceAchievements([], 0, 12, 0);
expect(achievements.value.committed).toEqual(AchievementLevel.Bronze);
- advanceAchievements([], 0, 26);
+ advanceAchievements([], 0, 26, 0);
expect(achievements.value.committed).toEqual(AchievementLevel.Silver);
- advanceAchievements([], 0, 52);
+ advanceAchievements([], 0, 52, 0);
expect(achievements.value.committed).toEqual(AchievementLevel.Gold);
});
+ it('advances challengeAccepted', async () => {
+ const {advanceAchievements, achievements} = await withSetup(useAchievements);
+ advanceAchievements([], 0, 0, 4);
+ expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.NoAchievement);
+ advanceAchievements([], 0, 0, 5);
+ expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Bronze);
+ advanceAchievements([], 0, 0, 10);
+ expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Silver);
+ advanceAchievements([], 0, 0, 20);
+ expect(achievements.value.challengeAccepted).toEqual(AchievementLevel.Gold);
+ });
+
it('resets achievements', async () => {
const {advanceAchievements, achievements, resetAchievements} = await withSetup(useAchievements);
- advanceAchievements(ALL_VEGGIES, 30, 52);
+ advanceAchievements(ALL_VEGGIES, 30, 52, 20);
expect(achievements.value).toEqual({
+ challengeAccepted: 3,
committed: 3,
completionist: 3,
experimenterBean: 3,
diff --git a/src/components/__tests__/activityStore.spec.ts b/src/components/__tests__/activityStore.spec.ts
index 5e812c0..b92fc47 100644
--- a/src/components/__tests__/activityStore.spec.ts
+++ b/src/components/__tests__/activityStore.spec.ts
@@ -81,6 +81,40 @@ describe('activityStore', () => {
expect(activityStore.currentVeggies).toEqual(['cucumber', 'tomato']);
});
+ it('returns completed challenges', () => {
+ activityStore.startDate = twoWeeksAgo;
+ activityStore.weeks.push(
+ {
+ startDate: twoWeeksAgo,
+ veggies: ['cucumber'],
+ },
+ {
+ startDate: lastWeek,
+ veggies: ['wheat', 'rye', 'strawberry'],
+ },
+ {
+ startDate: thisWeek,
+ veggies: ['rice', 'leek'],
+ },
+ );
+ activityStore.challenges.push(
+ {
+ startDate: twoWeeksAgo,
+ veggie: 'cucumber',
+ },
+ {
+ startDate: lastWeek,
+ veggie: 'leek',
+ },
+ {
+ startDate: thisWeek,
+ veggie: 'rice',
+ },
+ );
+
+ expect(activityStore.completedChallenges).toEqual(2);
+ });
+
// it("sets this week's veggies", () => {
// activityStore.startDate = lastWeek;
// activityStore.weeks.push(
diff --git a/src/hooks/achievements.ts b/src/hooks/achievements.ts
index 94d292b..a151dd3 100644
--- a/src/hooks/achievements.ts
+++ b/src/hooks/achievements.ts
@@ -8,6 +8,7 @@ import type {Achievements} from '@/utils/types';
type AdvanceEvent = {
type: 'ADVANCE';
uniqueVeggies: string[];
+ completedChallenges: number;
hotStreakLength: number;
totalWeeks: number;
};
@@ -17,6 +18,10 @@ type ResetEvent = {
};
const guards = {
+ challengeAccepted:
+ (threshold: number) =>
+ ({event}: GuardArgs) =>
+ event.completedChallenges >= threshold,
committed:
(threshold: number) =>
({event}: GuardArgs) =>
@@ -46,6 +51,9 @@ export function useAchievements() {
type: 'parallel',
on: {
RESET: [
+ {
+ target: '.challengeAccepted.0',
+ },
{
target: `.committed.0`,
},
@@ -73,6 +81,52 @@ export function useAchievements() {
],
},
states: {
+ challengeAccepted: {
+ initial: '0',
+ states: {
+ '0': {
+ on: {
+ ADVANCE: [
+ {
+ target: '3',
+ guard: guards.challengeAccepted(20),
+ },
+ {
+ target: '2',
+ guard: guards.challengeAccepted(10),
+ },
+ {
+ target: '1',
+ guard: guards.challengeAccepted(5),
+ },
+ ],
+ },
+ },
+ '1': {
+ on: {
+ ADVANCE: [
+ {
+ target: '3',
+ guard: guards.challengeAccepted(20),
+ },
+ {
+ target: '2',
+ guard: guards.challengeAccepted(10),
+ },
+ ],
+ },
+ },
+ '2': {
+ on: {
+ ADVANCE: {
+ target: '3',
+ guard: guards.challengeAccepted(20),
+ },
+ },
+ },
+ '3': {},
+ },
+ },
committed: {
initial: '0',
states: {
@@ -315,8 +369,19 @@ export function useAchievements() {
return {
achievements,
- advanceAchievements: (uniqueVeggies: string[], hotStreakLength: number, totalWeeks: number) =>
- actor.send({type: 'ADVANCE', uniqueVeggies, hotStreakLength, totalWeeks}),
+ advanceAchievements: (
+ uniqueVeggies: string[],
+ hotStreakLength: number,
+ totalWeeks: number,
+ completedChallenges: number,
+ ) =>
+ actor.send({
+ type: 'ADVANCE',
+ uniqueVeggies,
+ hotStreakLength,
+ totalWeeks,
+ completedChallenges,
+ }),
resetAchievements: () => actor.send({type: 'RESET'}),
};
}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index e43e4cc..9937d9f 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1,5 +1,13 @@
{
"achievements": {
+ "challengeAccepted": {
+ "title": "Challenge Accepted",
+ "ariaLabel": "{0} completed weekly challenges",
+ "badgeText": "{0} challenges",
+ "1": "You have completed five weekly challenges, earning you the bronze achievement!",
+ "2": "You have completed ten weekly challenges, earning you the silver achievement!",
+ "3": "You have completed twenty weekly challenges, earning you the gold achievement!"
+ },
"committed": {
"title": "The Committed",
"ariaLabel": "{0} months of active use",
diff --git a/src/i18n/fi.json b/src/i18n/fi.json
index b7f5ddf..86e11e5 100644
--- a/src/i18n/fi.json
+++ b/src/i18n/fi.json
@@ -1,5 +1,13 @@
{
"achievements": {
+ "challengeAccepted": {
+ "title": "Haaste vastaanotettu",
+ "ariaLabel": "{0} suoritettua viikkohaastetta",
+ "badgeText": "{0} haastetta",
+ "1": "Olet suorittanut viisi viikkohastetta ja ansainnut pronssisen palkinnon!",
+ "2": "Olet suorittanut kymmenen viikkohastetta ja ansainnut hopeisen palkinnon!",
+ "3": "Olet suorittanut 20 viikkohastetta ja ansainnut kultaisen palkinnon!"
+ },
"committed": {
"title": "Sitoutunut",
"ariaLabel": "{0} kuukauden aktiivinen kΓ€yttΓΆ",
diff --git a/src/stores/activityStore.ts b/src/stores/activityStore.ts
index 682f031..de477a0 100644
--- a/src/stores/activityStore.ts
+++ b/src/stores/activityStore.ts
@@ -67,6 +67,13 @@ export const useActivityStore = defineStore('activity', () => {
const uniqueVeggies = computed(() => unique(allVeggies.value));
+ const completedChallenges = computed(
+ () =>
+ challenges.value.filter(({startDate, veggie}) =>
+ veggiesForWeek.value(startDate).includes(veggie),
+ ).length,
+ );
+
const veggiesForWeek = computed(
() => (weekStart: DateTime) =>
weeks.value.find(({startDate}) => startDate.equals(weekStart))?.veggies ?? [],
@@ -143,6 +150,7 @@ export const useActivityStore = defineStore('activity', () => {
getWeekStarts,
getTotalWeeks,
hotStreak,
+ completedChallenges,
currentVeggies,
currentChallenge,
allVeggies,
diff --git a/src/stores/appStateStore.ts b/src/stores/appStateStore.ts
index f0e3532..2ce45fb 100644
--- a/src/stores/appStateStore.ts
+++ b/src/stores/appStateStore.ts
@@ -12,10 +12,15 @@ type Message = {
export const useAppStateStore = defineStore('appState', () => {
const {advanceAchievements, achievements, resetAchievements} = useAchievements();
- const {uniqueVeggies, hotStreak, weeks} = storeToRefs(useActivityStore());
+ const {uniqueVeggies, hotStreak, weeks, completedChallenges} = storeToRefs(useActivityStore());
watchEffect(() => {
- advanceAchievements(uniqueVeggies.value, hotStreak.value, weeks.value.length);
+ advanceAchievements(
+ uniqueVeggies.value,
+ hotStreak.value,
+ weeks.value.length,
+ completedChallenges.value,
+ );
});
// State refs
diff --git a/src/utils/types.ts b/src/utils/types.ts
index 2ef43c9..f772346 100644
--- a/src/utils/types.ts
+++ b/src/utils/types.ts
@@ -42,6 +42,7 @@ export enum AchievementLevel {
}
export type Achievements = {
+ challengeAccepted: AchievementLevel;
committed: AchievementLevel;
completionist: AchievementLevel;
hotStreak: AchievementLevel;