react-achievements 1.3.7 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +282 -146
  2. package/coverage/clover.xml +1 -1
  3. package/coverage/lcov-report/src/context/AchievementContext.tsx.html +6 -6
  4. package/coverage/lcov-report/src/context/index.html +1 -1
  5. package/coverage/lcov-report/src/index.ts.html +1 -1
  6. package/coverage/lcov.info +1 -1
  7. package/dist/assets/defaultIcons.d.ts +81 -0
  8. package/dist/components/AchievementModal.d.ts +3 -2
  9. package/dist/components/BadgesButton.d.ts +6 -1
  10. package/dist/components/BadgesModal.d.ts +3 -2
  11. package/dist/hooks/useAchievement.d.ts +8 -0
  12. package/dist/index.cjs.js +2100 -103
  13. package/dist/index.d.ts +5 -3
  14. package/dist/index.esm.js +2097 -120
  15. package/dist/providers/AchievementProvider.d.ts +11 -0
  16. package/dist/redux/achievementSlice.d.ts +25 -0
  17. package/dist/redux/notificationSlice.d.ts +7 -0
  18. package/dist/redux/store.d.ts +15 -0
  19. package/dist/types.d.ts +22 -13
  20. package/package.json +26 -18
  21. package/src/assets/defaultIcons.ts +100 -0
  22. package/src/components/AchievementModal.tsx +37 -8
  23. package/src/components/BadgesButton.tsx +33 -7
  24. package/src/components/BadgesModal.tsx +23 -9
  25. package/src/hooks/useAchievement.ts +20 -0
  26. package/src/index.ts +11 -7
  27. package/src/providers/AchievementProvider.tsx +185 -0
  28. package/src/redux/achievementSlice.ts +71 -0
  29. package/src/redux/notificationSlice.ts +26 -0
  30. package/src/redux/store.ts +20 -0
  31. package/src/types.ts +24 -13
  32. package/demo/src/stories/Button.jsx +0 -50
  33. package/demo/src/stories/Button.stories.js +0 -48
  34. package/demo/src/stories/Configure.mdx +0 -364
  35. package/demo/src/stories/Header.jsx +0 -59
  36. package/demo/src/stories/Header.stories.js +0 -28
  37. package/demo/src/stories/Page.jsx +0 -69
  38. package/demo/src/stories/Page.stories.js +0 -28
  39. package/demo/src/stories/assets/accessibility.png +0 -0
  40. package/demo/src/stories/assets/accessibility.svg +0 -1
  41. package/demo/src/stories/assets/addon-library.png +0 -0
  42. package/demo/src/stories/assets/assets.png +0 -0
  43. package/demo/src/stories/assets/avif-test-image.avif +0 -0
  44. package/demo/src/stories/assets/context.png +0 -0
  45. package/demo/src/stories/assets/discord.svg +0 -1
  46. package/demo/src/stories/assets/docs.png +0 -0
  47. package/demo/src/stories/assets/figma-plugin.png +0 -0
  48. package/demo/src/stories/assets/github.svg +0 -1
  49. package/demo/src/stories/assets/share.png +0 -0
  50. package/demo/src/stories/assets/styling.png +0 -0
  51. package/demo/src/stories/assets/testing.png +0 -0
  52. package/demo/src/stories/assets/theming.png +0 -0
  53. package/demo/src/stories/assets/tutorials.svg +0 -1
  54. package/demo/src/stories/assets/youtube.svg +0 -1
  55. package/demo/src/stories/button.css +0 -30
  56. package/demo/src/stories/header.css +0 -32
  57. package/demo/src/stories/page.css +0 -69
  58. package/dist/stories/Button.d.ts +0 -28
  59. package/dist/stories/Button.stories.d.ts +0 -23
  60. package/dist/stories/Header.d.ts +0 -13
  61. package/dist/stories/Header.stories.d.ts +0 -18
  62. package/dist/stories/Page.d.ts +0 -3
  63. package/dist/stories/Page.stories.d.ts +0 -12
  64. package/src/context/AchievementContext.tsx +0 -185
  65. package/src/stories/Button.stories.ts +0 -52
  66. package/src/stories/Button.tsx +0 -48
  67. package/src/stories/Configure.mdx +0 -364
  68. package/src/stories/Header.stories.ts +0 -33
  69. package/src/stories/Header.tsx +0 -56
  70. package/src/stories/Page.stories.ts +0 -32
  71. package/src/stories/Page.tsx +0 -73
  72. package/src/stories/assets/accessibility.png +0 -0
  73. package/src/stories/assets/accessibility.svg +0 -1
  74. package/src/stories/assets/addon-library.png +0 -0
  75. package/src/stories/assets/assets.png +0 -0
  76. package/src/stories/assets/avif-test-image.avif +0 -0
  77. package/src/stories/assets/context.png +0 -0
  78. package/src/stories/assets/discord.svg +0 -1
  79. package/src/stories/assets/docs.png +0 -0
  80. package/src/stories/assets/figma-plugin.png +0 -0
  81. package/src/stories/assets/github.svg +0 -1
  82. package/src/stories/assets/share.png +0 -0
  83. package/src/stories/assets/styling.png +0 -0
  84. package/src/stories/assets/testing.png +0 -0
  85. package/src/stories/assets/theming.png +0 -0
  86. package/src/stories/assets/tutorials.svg +0 -1
  87. package/src/stories/assets/youtube.svg +0 -1
  88. package/src/stories/button.css +0 -30
  89. package/src/stories/header.css +0 -32
  90. package/src/stories/page.css +0 -69
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { AchievementProviderProps, AchievementMetrics as AchievementMetricsType } from '../types';
3
+ export interface AchievementContextType {
4
+ updateMetrics: (newMetrics: AchievementMetricsType | ((prevMetrics: AchievementMetricsType) => AchievementMetricsType)) => void;
5
+ unlockedAchievements: string[];
6
+ resetStorage: () => void;
7
+ }
8
+ export declare const AchievementContext: React.Context<AchievementContextType | undefined>;
9
+ export declare const useAchievementContext: () => AchievementContextType;
10
+ export declare const AchievementProvider: React.FC<AchievementProviderProps>;
11
+ export declare function mergeDeep(target: any, source: any): any;
@@ -0,0 +1,25 @@
1
+ import { PayloadAction } from '@reduxjs/toolkit';
2
+ import { AchievementConfiguration, InitialAchievementMetrics, AchievementMetrics } from '../types';
3
+ export interface AchievementState {
4
+ config: AchievementConfiguration;
5
+ metrics: AchievementMetrics;
6
+ unlockedAchievements: string[];
7
+ storageKey: string | null;
8
+ }
9
+ export declare const achievementSlice: import("@reduxjs/toolkit").Slice<AchievementState, {
10
+ initialize: (state: import("immer/dist/internal").WritableDraft<AchievementState>, action: PayloadAction<{
11
+ config: AchievementConfiguration;
12
+ initialState?: InitialAchievementMetrics;
13
+ storageKey: string;
14
+ }>) => void;
15
+ setMetrics: (state: import("immer/dist/internal").WritableDraft<AchievementState>, action: PayloadAction<AchievementMetrics>) => void;
16
+ unlockAchievement: (state: import("immer/dist/internal").WritableDraft<AchievementState>, action: PayloadAction<string>) => void;
17
+ resetAchievements: (state: import("immer/dist/internal").WritableDraft<AchievementState>) => void;
18
+ }, "achievements">;
19
+ export declare const initialize: import("@reduxjs/toolkit").ActionCreatorWithPayload<{
20
+ config: AchievementConfiguration;
21
+ initialState?: InitialAchievementMetrics;
22
+ storageKey: string;
23
+ }, "achievements/initialize">, setMetrics: import("@reduxjs/toolkit").ActionCreatorWithPayload<AchievementMetrics, "achievements/setMetrics">, unlockAchievement: import("@reduxjs/toolkit").ActionCreatorWithPayload<string, "achievements/unlockAchievement">, resetAchievements: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"achievements/resetAchievements">;
24
+ declare const _default: import("@reduxjs/toolkit").Reducer<AchievementState>;
25
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import { AchievementDetails } from '../types';
2
+ export interface NotificationState {
3
+ notifications: AchievementDetails[];
4
+ }
5
+ export declare const addNotification: import("@reduxjs/toolkit").ActionCreatorWithPayload<AchievementDetails, "notifications/addNotification">, clearNotifications: import("@reduxjs/toolkit").ActionCreatorWithoutPayload<"notifications/clearNotifications">;
6
+ declare const _default: import("@reduxjs/toolkit").Reducer<NotificationState>;
7
+ export default _default;
@@ -0,0 +1,15 @@
1
+ import { AchievementState } from './achievementSlice';
2
+ import { NotificationState } from './notificationSlice';
3
+ export interface RootState {
4
+ achievements: AchievementState;
5
+ notifications: NotificationState;
6
+ }
7
+ declare const store: import("@reduxjs/toolkit/dist/configureStore").ToolkitStore<{
8
+ achievements: AchievementState;
9
+ notifications: NotificationState;
10
+ }, import("@reduxjs/toolkit").AnyAction, [import("@reduxjs/toolkit").ThunkMiddleware<{
11
+ achievements: AchievementState;
12
+ notifications: NotificationState;
13
+ }, import("@reduxjs/toolkit").AnyAction>]>;
14
+ export type AppDispatch = typeof store.dispatch;
15
+ export default store;
package/dist/types.d.ts CHANGED
@@ -1,17 +1,26 @@
1
- export interface AchievementData {
2
- id: string;
3
- title: string;
4
- description: string;
5
- icon: string;
1
+ export type AchievementMetricValue = number | string | boolean | Date;
2
+ export interface AchievementDetails {
3
+ achievementId: string;
4
+ achievementTitle: string;
5
+ achievementDescription: string;
6
+ achievementIconKey?: string;
6
7
  }
7
- export type MetricValue = number | string | boolean | Date;
8
- export interface Metrics {
9
- [key: string]: MetricValue[];
8
+ export type AchievementIconRecord = Record<string, string>;
9
+ export interface AchievementConfiguration {
10
+ [metricName: string]: Array<AchievementUnlockCondition<AchievementMetricValue>>;
10
11
  }
11
- export interface AchievementCondition {
12
- check: (value: MetricValue[]) => boolean;
13
- data: AchievementData;
12
+ export type InitialAchievementMetrics = Record<string, AchievementMetricValue | AchievementMetricValue[] | undefined>;
13
+ export type AchievementMetrics = Record<string, AchievementMetricValue[]>;
14
+ export interface AchievementProviderProps {
15
+ children: React.ReactNode;
16
+ config: AchievementConfiguration;
17
+ initialState?: InitialAchievementMetrics;
18
+ storageKey?: string;
19
+ badgesButtonPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
20
+ styles?: Partial<import('./defaultStyles').Styles>;
21
+ icons?: Record<string, string>;
14
22
  }
15
- export interface AchievementConfig {
16
- [key: string]: AchievementCondition[];
23
+ export interface AchievementUnlockCondition<T extends AchievementMetricValue> {
24
+ isConditionMet: (value: T) => boolean;
25
+ achievementDetails: AchievementDetails;
17
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-achievements",
3
- "version": "1.3.7",
3
+ "version": "2.0.5",
4
4
  "description": "This package allows users to transpose a React achievements engine over their React apps",
5
5
  "keywords": [
6
6
  "react",
@@ -21,29 +21,37 @@
21
21
  "license": "ISC",
22
22
  "devDependencies": {
23
23
  "@chromatic-com/storybook": "^1.6.1",
24
+ "@emotion/react": "^11.14.0",
25
+ "@emotion/styled": "^11.14.0",
26
+ "@mui/icons-material": "^6.4.8",
24
27
  "@rollup/plugin-commonjs": "^26.0.1",
25
28
  "@rollup/plugin-node-resolve": "^15.2.3",
26
- "@storybook/addon-essentials": "^8.2.8",
27
- "@storybook/addon-interactions": "^8.2.8",
28
- "@storybook/addon-links": "^8.2.8",
29
- "@storybook/addon-onboarding": "^8.2.8",
29
+ "@storybook/addon-essentials": "^8.6.8",
30
+ "@storybook/addon-interactions": "^8.6.8",
31
+ "@storybook/addon-links": "^8.6.8",
32
+ "@storybook/addon-onboarding": "^8.6.8",
30
33
  "@storybook/addon-webpack5-compiler-swc": "^1.0.5",
31
- "@storybook/blocks": "^8.2.8",
32
- "@storybook/react": "^8.2.9",
33
- "@storybook/react-webpack5": "^8.2.8",
34
- "@storybook/test": "^8.2.9",
34
+ "@storybook/blocks": "^8.6.8",
35
+ "@storybook/react": "^8.6.8",
36
+ "@storybook/react-webpack5": "^8.6.8",
37
+ "@storybook/test": "^8.6.8",
35
38
  "rollup": "^4.19.0",
36
39
  "rollup-plugin-typescript2": "^0.36.0",
37
- "storybook": "^8.2.8",
38
- "typescript": "^5.5.4"
39
- },
40
- "dependencies": {
40
+ "storybook": "^8.6.8",
41
+ "typescript": "^5.5.4",
41
42
  "@types/jest": "^29.5.12",
42
43
  "@types/node": "^20.14.12",
43
44
  "@types/react": "^18.3.3",
44
- "@types/react-dom": "^18.3.0",
45
- "react": "^18.3.1",
46
- "react-confetti": "^6.1.0",
47
- "react-use": "^17.5.1"
45
+ "@types/react-dom": "^18.3.0"
46
+ },
47
+ "peerDependencies": {
48
+ "react": "^18.0.0",
49
+ "react-dom": "^18.0.0",
50
+ "react-redux": "^8.0.0 || ^9.0.0",
51
+ "@reduxjs/toolkit": "^1.0.0",
52
+ "react-confetti": "^6.0.0",
53
+ "react-use": "^17.0.0"
54
+ },
55
+ "dependencies": {
48
56
  }
49
- }
57
+ }
@@ -0,0 +1,100 @@
1
+ // src/defaultIcons.ts
2
+
3
+ export const defaultAchievementIcons = {
4
+ // General Progress & Milestones
5
+ levelUp: '🏆',
6
+ questComplete: '📜',
7
+ monsterDefeated: '⚔️',
8
+ itemCollected: '📦',
9
+ challengeCompleted: '🏁',
10
+ milestoneReached: '🏅',
11
+ firstStep: '👣',
12
+ newBeginnings: '🌱',
13
+ breakthrough: '💡',
14
+ growth: '📈',
15
+
16
+ // Social & Engagement
17
+ shared: '🔗',
18
+ liked: '❤️',
19
+ commented: '💬',
20
+ followed: '👥',
21
+ invited: '🤝',
22
+ communityMember: '🏘️',
23
+ supporter: '🌟',
24
+ connected: '🌐',
25
+ participant: '🙋',
26
+ influencer: '📣',
27
+
28
+ // Time & Activity
29
+ activeDay: '☀️',
30
+ activeWeek: '📅',
31
+ activeMonth: '🗓️',
32
+ earlyBird: '⏰',
33
+ nightOwl: '🌙',
34
+ streak: '🔥',
35
+ dedicated: '⏳',
36
+ punctual: '⏱️',
37
+ consistent: '🔄',
38
+ marathon: '🏃',
39
+
40
+ // Creativity & Skill
41
+ artist: '🎨',
42
+ writer: '✍️',
43
+ innovator: '🔬',
44
+ creator: '🛠️',
45
+ expert: '🎓',
46
+ master: '👑',
47
+ pioneer: '🚀',
48
+ performer: '🎭',
49
+ thinker: '🧠',
50
+ explorer: '🗺️',
51
+
52
+ // Achievement Types
53
+ bronze: '🥉',
54
+ silver: '🥈',
55
+ gold: '🥇',
56
+ diamond: '💎',
57
+ legendary: '✨',
58
+ epic: '💥',
59
+ rare: '🔮',
60
+ common: '🔘',
61
+ special: '🎁',
62
+ hidden: '❓',
63
+
64
+ // Numbers & Counters
65
+ one: '1️⃣',
66
+ ten: '🔟',
67
+ hundred: '💯',
68
+ thousand: '🔢',
69
+
70
+ // Actions & Interactions
71
+ clicked: '🖱️',
72
+ used: '🔑',
73
+ found: '🔍',
74
+ built: '🧱',
75
+ solved: '🧩',
76
+ discovered: '🔭',
77
+ unlocked: '🔓',
78
+ upgraded: '⬆️',
79
+ repaired: '🔧',
80
+ defended: '🛡️',
81
+
82
+ // Placeholders
83
+ default: '⭐', // A fallback icon
84
+ loading: '⏳',
85
+ error: '⚠️',
86
+ success: '✅',
87
+ failure: '❌',
88
+
89
+ // Miscellaneous
90
+ trophy: '🏆',
91
+ star: '⭐',
92
+ flag: '🚩',
93
+ puzzle: '🧩',
94
+ gem: '💎',
95
+ crown: '👑',
96
+ medal: '🏅',
97
+ ribbon: '🎗️',
98
+ badge: '🎖️',
99
+ shield: '🛡️',
100
+ };
@@ -1,24 +1,53 @@
1
1
  import React from 'react';
2
- import { AchievementData } from '../types';
2
+ import { AchievementDetails, AchievementIconRecord } from '../types';
3
3
  import { Styles } from '../defaultStyles';
4
+ import { defaultAchievementIcons } from '../assets/defaultIcons';
4
5
 
5
6
  interface AchievementModalProps {
6
7
  isOpen: boolean;
7
- achievement: AchievementData | null;
8
+ achievement: AchievementDetails | null;
8
9
  onClose: () => void;
9
10
  styles: Styles['achievementModal'];
11
+ icons?: Record<string, string>;
10
12
  }
11
13
 
12
- const AchievementModal: React.FC<AchievementModalProps> = ({ isOpen, achievement, onClose, styles }) => {
14
+ const AchievementModal: React.FC<AchievementModalProps> = ({ isOpen, achievement, onClose, styles, icons = {} }) => {
13
15
  if (!isOpen || !achievement) return null;
14
16
 
17
+ const mergedIcons: AchievementIconRecord = { ...defaultAchievementIcons, ...icons };
18
+ const iconToDisplay = achievement?.achievementIconKey ? (mergedIcons[achievement.achievementIconKey] || mergedIcons.default) : mergedIcons.default;
19
+
15
20
  return (
16
- <div style={styles.overlay as React.CSSProperties}>
17
- <div style={styles.content}>
21
+ <div style={{
22
+ ...styles.overlay,
23
+ display: 'flex',
24
+ justifyContent: 'center',
25
+ alignItems: 'center',
26
+ position: 'fixed', // Ensure it covers the screen
27
+ top: 0,
28
+ left: 0,
29
+ width: '100%',
30
+ height: '100%',
31
+ backgroundColor: 'rgba(0, 0, 0, 0.5)', // Optional semi-transparent background
32
+ }}>
33
+ <div style={{
34
+ ...styles.content,
35
+ display: 'flex',
36
+ flexDirection: 'column',
37
+ alignItems: 'center', // Center content horizontally
38
+ justifyContent: 'center', // Center content vertically
39
+ padding: '20px',
40
+ borderRadius: '8px',
41
+ backgroundColor: 'white', // Or your modal background color
42
+ }}>
18
43
  <h2 style={styles.title}>Achievement Unlocked!</h2>
19
- <img src={achievement.icon} alt={achievement.title} style={styles.icon} />
20
- <h3 style={styles.title}>{achievement.title}</h3>
21
- <p style={styles.description}>{achievement.description}</p>
44
+ {iconToDisplay.startsWith('http') || iconToDisplay.startsWith('data:image') ? (
45
+ <img src={iconToDisplay} alt={achievement.achievementTitle} style={styles.icon} />
46
+ ) : (
47
+ <p style={{ fontSize: '3em' }}>{iconToDisplay}</p>
48
+ )}
49
+ <h3 style={styles.title}>{achievement.achievementTitle}</h3>
50
+ <p style={styles.description}>{achievement.achievementDescription}</p>
22
51
  <button onClick={onClose} style={styles.button}>Okay</button>
23
52
  </div>
24
53
  </div>
@@ -1,21 +1,47 @@
1
1
  import React from 'react';
2
2
  import { Styles } from '../defaultStyles';
3
+ import { AchievementDetails } from '../types';
3
4
 
4
5
  interface BadgesButtonProps {
5
6
  onClick: () => void;
6
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
7
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
7
8
  styles: Styles['badgesButton'];
9
+ unlockedAchievements: AchievementDetails[];
10
+ icon?: React.ReactNode; // Allow custom icons
11
+ drawer?: boolean; // Indicate if it triggers a drawer
12
+ customStyles?: React.CSSProperties; // Allow custom styles
8
13
  }
9
14
 
10
- const BadgesButton: React.FC<BadgesButtonProps> = ({ onClick, position, styles }) => {
11
- const positionStyle = {
12
- [position.split('-')[0]]: '20px',
13
- [position.split('-')[1]]: '20px',
15
+ const BadgesButton: React.FC<BadgesButtonProps> = ({
16
+ onClick,
17
+ position,
18
+ styles,
19
+ unlockedAchievements,
20
+ icon,
21
+ drawer = false,
22
+ customStyles,
23
+ }) => {
24
+ const positionStyle = position
25
+ ? {
26
+ [position.split('-')[0]]: '20px',
27
+ [position.split('-')[1]]: '20px',
28
+ }
29
+ : {};
30
+
31
+ const handleButtonClick = () => {
32
+ onClick();
14
33
  };
15
34
 
35
+ const achievementsText = 'View Achievements';
36
+
37
+ const buttonContent = icon ? icon : achievementsText;
38
+
16
39
  return (
17
- <button onClick={onClick} style={{ ...styles, ...positionStyle }}>
18
- View Achievements
40
+ <button
41
+ onClick={handleButtonClick}
42
+ style={{ ...styles, ...positionStyle, ...customStyles }}
43
+ >
44
+ {buttonContent}
19
45
  </button>
20
46
  );
21
47
  };
@@ -1,15 +1,17 @@
1
1
  import React from 'react';
2
- import { AchievementData } from '../types';
2
+ import {AchievementDetails, AchievementIconRecord} from '../types';
3
3
  import { Styles } from '../defaultStyles';
4
+ import { defaultAchievementIcons } from '../assets/defaultIcons'; // Import default icons
4
5
 
5
6
  interface BadgesModalProps {
6
7
  isOpen: boolean;
7
- achievements: AchievementData[];
8
+ achievements: AchievementDetails[];
8
9
  onClose: () => void;
9
10
  styles: Styles['badgesModal'];
11
+ icons?: Record<string, string>; // Optional user-specified icons
10
12
  }
11
13
 
12
- const BadgesModal: React.FC<BadgesModalProps> = ({ isOpen, achievements, onClose, styles }) => {
14
+ const BadgesModal: React.FC<BadgesModalProps> = ({ isOpen, achievements, onClose, styles, icons = {} }) => {
13
15
  if (!isOpen) return null;
14
16
 
15
17
  return (
@@ -17,12 +19,24 @@ const BadgesModal: React.FC<BadgesModalProps> = ({ isOpen, achievements, onClose
17
19
  <div style={styles.content}>
18
20
  <h2 style={styles.title}>Your Achievements</h2>
19
21
  <div style={styles.badgeContainer}>
20
- {achievements.map((achievement) => (
21
- <div key={achievement.id} style={styles.badge}>
22
- <img src={achievement.icon} alt={achievement.title} style={styles.badgeIcon} />
23
- <span style={styles.badgeTitle}>{achievement.title}</span>
24
- </div>
25
- ))}
22
+ {achievements.map((achievement) => {
23
+ const mergedIcons: AchievementIconRecord = { ...defaultAchievementIcons, ...icons };
24
+ let iconToDisplay: string | undefined = mergedIcons.default;
25
+ if (achievement.achievementIconKey && mergedIcons[achievement.achievementIconKey]) {
26
+ iconToDisplay = mergedIcons[achievement.achievementIconKey];
27
+ }
28
+ return (
29
+ <div key={achievement.achievementId} style={styles.badge}>
30
+ {/* Render icon based on type (Unicode or image path) */}
31
+ {iconToDisplay.startsWith('http') || iconToDisplay.startsWith('data:image') ? (
32
+ <img src={iconToDisplay} alt={achievement.achievementTitle} style={styles.badgeIcon} />
33
+ ) : (
34
+ <p style={{ fontSize: '2em' }}>{iconToDisplay}</p> // Render Unicode as large text
35
+ )}
36
+ <span style={styles.badgeTitle}>{achievement.achievementTitle}</span>
37
+ </div>
38
+ );
39
+ })}
26
40
  </div>
27
41
  <button onClick={onClose} style={styles.button}>Close</button>
28
42
  </div>
@@ -0,0 +1,20 @@
1
+ import { useSelector, useDispatch } from 'react-redux';
2
+ import { RootState, AppDispatch } from '../redux/store';
3
+ import { useAchievementContext } from '../providers/AchievementProvider';
4
+
5
+ export const useAchievement = () => {
6
+ const dispatch: AppDispatch = useDispatch();
7
+ const { updateMetrics, unlockedAchievements, resetStorage } = useAchievementContext() || {};
8
+ const metrics = useSelector((state: RootState) => state.achievements.metrics);
9
+ const notifications = useSelector((state: RootState) => state.notifications.notifications);
10
+ const config = useSelector((state: RootState) => state.achievements.config);
11
+
12
+ return {
13
+ metrics: metrics,
14
+ unlockedAchievements: unlockedAchievements || [],
15
+ notifications: notifications,
16
+ config: config,
17
+ updateMetrics: updateMetrics || (() => {}),
18
+ resetStorage: resetStorage || (() => {}),
19
+ };
20
+ };
package/src/index.ts CHANGED
@@ -1,13 +1,17 @@
1
- import { AchievementProvider, useAchievement } from './context/AchievementContext';
2
- import { Metrics, AchievementConfig, AchievementData, AchievementCondition } from './types';
1
+ import { AchievementProvider, useAchievementContext as useAchievement } from './providers/AchievementProvider';
2
+ import { AchievementMetrics, AchievementConfiguration, AchievementDetails, AchievementUnlockCondition } from './types';
3
3
  import ConfettiWrapper from './components/ConfettiWrapper';
4
+ import achievementReducer from './redux/achievementSlice';
5
+ import notificationReducer from './redux/notificationSlice'
4
6
 
5
7
  export {
6
8
  AchievementProvider,
7
9
  useAchievement,
8
- Metrics,
9
- AchievementConfig,
10
- AchievementData,
11
- AchievementCondition,
12
- ConfettiWrapper
10
+ AchievementMetrics,
11
+ AchievementConfiguration,
12
+ AchievementDetails,
13
+ AchievementUnlockCondition,
14
+ ConfettiWrapper,
15
+ achievementReducer,
16
+ notificationReducer,
13
17
  };