@umituz/react-native-mascot 1.0.1

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.
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Mascot Context
3
+ * Provides mascot state and functionality to components
4
+ */
5
+
6
+ import React, { createContext, useContext, useState, useCallback, useRef } from 'react';
7
+ import { Mascot } from '../../domain/entities/Mascot';
8
+ import type { MascotConfig, MascotMood } from '../../domain/types/MascotTypes';
9
+ import { MascotFactory } from '../../infrastructure/managers/MascotFactory';
10
+ import { AnimationController } from '../../infrastructure/controllers/AnimationController';
11
+ import type { AnimationOptions } from '../../domain/interfaces/IAnimationController';
12
+
13
+ export interface MascotContextValue {
14
+ mascot: Mascot | null;
15
+ isPlaying: boolean;
16
+ currentAnimation: string | null;
17
+ initializeMascot: (config: MascotConfig) => void;
18
+ initializeFromTemplate: (template: string, customizations?: Partial<MascotConfig>) => void;
19
+ setMood: (mood: MascotMood) => void;
20
+ playAnimation: (animationId: string, options?: AnimationOptions) => Promise<void>;
21
+ stopAnimation: () => void;
22
+ updateAppearance: (appearance: Partial<MascotConfig['appearance']>) => void;
23
+ setVisible: (visible: boolean) => void;
24
+ }
25
+
26
+ const MascotContext = createContext<MascotContextValue | undefined>(undefined);
27
+
28
+ export interface MascotProviderProps extends React.PropsWithChildren {
29
+ initialConfig?: MascotConfig;
30
+ template?: string;
31
+ }
32
+
33
+ export const MascotProvider: React.FC<MascotProviderProps> = ({
34
+ children,
35
+ initialConfig: _initialConfig,
36
+ template: _template,
37
+ }) => {
38
+ const [mascot, setMascot] = useState<Mascot | null>(null);
39
+ const [isPlaying, setIsPlaying] = useState(false);
40
+ const [currentAnimation, setCurrentAnimation] = useState<string | null>(null);
41
+ const animationControllerRef = useRef<AnimationController | null>(null);
42
+
43
+ const initializeMascot = useCallback((config: MascotConfig) => {
44
+ const newMascot = new Mascot(config);
45
+ setMascot(newMascot);
46
+ if (!animationControllerRef.current) {
47
+ animationControllerRef.current = new AnimationController();
48
+ }
49
+ }, []);
50
+
51
+ const initializeFromTemplate = useCallback((
52
+ templateName: string,
53
+ customizations?: Partial<MascotConfig>
54
+ ) => {
55
+ const template = templateName as 'friendly-bot' | 'cute-pet' | 'wise-owl' | 'pixel-hero';
56
+ const newMascot = MascotFactory.createFromTemplate(template, customizations);
57
+ setMascot(newMascot);
58
+ if (!animationControllerRef.current) {
59
+ animationControllerRef.current = new AnimationController();
60
+ }
61
+ }, []);
62
+
63
+ const setMood = useCallback((mood: MascotMood) => {
64
+ setMascot((prev) => {
65
+ if (!prev) return null;
66
+ prev.setMood(mood);
67
+ return prev.clone();
68
+ });
69
+ }, []);
70
+
71
+ const playAnimation = useCallback(async (animationId: string, options?: AnimationOptions) => {
72
+ if (!mascot || !animationControllerRef.current) return;
73
+
74
+ const animation = mascot.getAnimation(animationId);
75
+ if (!animation) {
76
+ console.warn(`Animation ${animationId} not found`);
77
+ return;
78
+ }
79
+
80
+ setIsPlaying(true);
81
+ setCurrentAnimation(animationId);
82
+
83
+ if (animationControllerRef.current) {
84
+ await animationControllerRef.current.play(animation, options);
85
+ }
86
+
87
+ setIsPlaying(false);
88
+ setCurrentAnimation(null);
89
+ }, [mascot]);
90
+
91
+ const stopAnimation = useCallback(() => {
92
+ if (animationControllerRef.current) {
93
+ animationControllerRef.current.stop();
94
+ }
95
+ setIsPlaying(false);
96
+ setCurrentAnimation(null);
97
+ }, []);
98
+
99
+ const updateAppearance = useCallback((appearance: Partial<MascotConfig['appearance']>) => {
100
+ setMascot((prev) => {
101
+ if (!prev) return null;
102
+ prev.updateAppearance(appearance);
103
+ return prev.clone();
104
+ });
105
+ }, []);
106
+
107
+ const setVisible = useCallback((visible: boolean) => {
108
+ setMascot((prev) => {
109
+ if (!prev) return null;
110
+ prev.setVisible(visible);
111
+ return prev.clone();
112
+ });
113
+ }, []);
114
+
115
+ const value: MascotContextValue = {
116
+ mascot,
117
+ isPlaying,
118
+ currentAnimation,
119
+ initializeMascot,
120
+ initializeFromTemplate,
121
+ setMood,
122
+ playAnimation,
123
+ stopAnimation,
124
+ updateAppearance,
125
+ setVisible,
126
+ };
127
+
128
+ return (
129
+ <MascotContext.Provider value={value}>
130
+ {children}
131
+ </MascotContext.Provider>
132
+ );
133
+ };
134
+
135
+ export const useMascotContext = (): MascotContextValue => {
136
+ const context = useContext(MascotContext);
137
+ if (!context) {
138
+ throw new Error('useMascotContext must be used within a MascotProvider');
139
+ }
140
+ return context;
141
+ };
@@ -0,0 +1,224 @@
1
+ /**
2
+ * useMascot Hook
3
+ * Main hook for mascot management
4
+ */
5
+
6
+ import { useCallback, useEffect, useState, useRef } from 'react';
7
+ import { Mascot } from '../../domain/entities/Mascot';
8
+ import type {
9
+ MascotConfig,
10
+ MascotMood,
11
+ MascotAppearance,
12
+ } from '../../domain/types/MascotTypes';
13
+ import { MascotFactory } from '../../infrastructure/managers/MascotFactory';
14
+ import { AnimationController } from '../../infrastructure/controllers/AnimationController';
15
+ import type { AnimationOptions } from '../../domain/interfaces/IAnimationController';
16
+
17
+ export interface UseMascotOptions {
18
+ config?: MascotConfig;
19
+ template?: string;
20
+ autoInitialize?: boolean;
21
+ }
22
+
23
+ export interface UseMascotReturn {
24
+ mascot: Mascot | null;
25
+ isReady: boolean;
26
+ isPlaying: boolean;
27
+ currentAnimation: string | null;
28
+ initialize: (config: MascotConfig) => void;
29
+ initializeFromTemplate: (template: string, customizations?: Partial<MascotConfig>) => void;
30
+ setMood: (mood: MascotMood) => void;
31
+ setEnergy: (energy: number) => void;
32
+ playAnimation: (animationId: string, options?: AnimationOptions) => Promise<void>;
33
+ stopAnimation: () => void;
34
+ updateAppearance: (appearance: Partial<MascotAppearance>) => void;
35
+ setBaseColor: (color: string) => void;
36
+ setAccentColor: (color: string) => void;
37
+ addAccessory: (accessory: {
38
+ id: string;
39
+ type: string;
40
+ color?: string;
41
+ position?: { x: number; y: number };
42
+ }) => void;
43
+ removeAccessory: (accessoryId: string) => void;
44
+ setVisible: (visible: boolean) => void;
45
+ setPosition: (position: { x: number; y: number }) => void;
46
+ }
47
+
48
+ export function useMascot(options: UseMascotOptions = {}): UseMascotReturn {
49
+ const {
50
+ config: initialConfig,
51
+ template: initialTemplate,
52
+ autoInitialize = true,
53
+ } = options;
54
+
55
+ const [mascot, setMascot] = useState<Mascot | null>(null);
56
+ const [isReady, setIsReady] = useState(false);
57
+ const [isPlaying, setIsPlaying] = useState(false);
58
+ const [currentAnimation, setCurrentAnimation] = useState<string | null>(null);
59
+
60
+ const animationControllerRef = useRef<AnimationController | null>(null);
61
+
62
+ // Initialize mascot
63
+ const initialize = useCallback((config: MascotConfig) => {
64
+ const newMascot = new Mascot(config);
65
+ setMascot(newMascot);
66
+ setIsReady(true);
67
+ if (!animationControllerRef.current) {
68
+ animationControllerRef.current = new AnimationController();
69
+ }
70
+ }, []);
71
+
72
+ const initializeFromTemplate = useCallback((
73
+ template: string,
74
+ customizations?: Partial<MascotConfig>
75
+ ) => {
76
+ const newMascot = MascotFactory.createFromTemplate(
77
+ template as 'friendly-bot' | 'cute-pet' | 'wise-owl' | 'pixel-hero',
78
+ customizations
79
+ );
80
+ setMascot(newMascot);
81
+ setIsReady(true);
82
+ if (!animationControllerRef.current) {
83
+ animationControllerRef.current = new AnimationController();
84
+ }
85
+ }, []);
86
+
87
+ // Mood management
88
+ const setMood = useCallback((mood: MascotMood) => {
89
+ setMascot((prev) => {
90
+ if (!prev) return null;
91
+ prev.setMood(mood);
92
+ return prev.clone();
93
+ });
94
+ }, []);
95
+
96
+ const setEnergy = useCallback((energy: number) => {
97
+ setMascot((prev) => {
98
+ if (!prev) return null;
99
+ prev.setEnergy(energy);
100
+ return prev.clone();
101
+ });
102
+ }, []);
103
+
104
+ // Animation management
105
+ const playAnimation = useCallback(async (animationId: string, options?: AnimationOptions) => {
106
+ if (!mascot || !animationControllerRef.current) return;
107
+
108
+ const animation = mascot.getAnimation(animationId);
109
+ if (!animation) {
110
+ console.warn(`Animation ${animationId} not found`);
111
+ return;
112
+ }
113
+
114
+ setIsPlaying(true);
115
+ setCurrentAnimation(animationId);
116
+
117
+ if (animationControllerRef.current) {
118
+ await animationControllerRef.current.play(animation, options);
119
+ }
120
+
121
+ setIsPlaying(false);
122
+ setCurrentAnimation(null);
123
+ }, [mascot]);
124
+
125
+ const stopAnimation = useCallback(() => {
126
+ if (animationControllerRef.current) {
127
+ animationControllerRef.current.stop();
128
+ }
129
+ setIsPlaying(false);
130
+ setCurrentAnimation(null);
131
+ }, []);
132
+
133
+ // Appearance management
134
+ const updateAppearance = useCallback((appearance: Partial<MascotAppearance>) => {
135
+ setMascot((prev) => {
136
+ if (!prev) return null;
137
+ prev.updateAppearance(appearance);
138
+ return prev.clone();
139
+ });
140
+ }, []);
141
+
142
+ const setBaseColor = useCallback((color: string) => {
143
+ setMascot((prev) => {
144
+ if (!prev) return null;
145
+ prev.setBaseColor(color);
146
+ return prev.clone();
147
+ });
148
+ }, []);
149
+
150
+ const setAccentColor = useCallback((color: string) => {
151
+ setMascot((prev) => {
152
+ if (!prev) return null;
153
+ prev.setAccentColor(color);
154
+ return prev.clone();
155
+ });
156
+ }, []);
157
+
158
+ const addAccessory = useCallback((accessory: {
159
+ id: string;
160
+ type: string;
161
+ color?: string;
162
+ position?: { x: number; y: number };
163
+ }) => {
164
+ setMascot((prev) => {
165
+ if (!prev) return null;
166
+ prev.addAccessory(accessory);
167
+ return prev.clone();
168
+ });
169
+ }, []);
170
+
171
+ const removeAccessory = useCallback((accessoryId: string) => {
172
+ setMascot((prev) => {
173
+ if (!prev) return null;
174
+ prev.removeAccessory(accessoryId);
175
+ return prev.clone();
176
+ });
177
+ }, []);
178
+
179
+ // Visibility and position
180
+ const setVisible = useCallback((visible: boolean) => {
181
+ setMascot((prev) => {
182
+ if (!prev) return null;
183
+ prev.setVisible(visible);
184
+ return prev.clone();
185
+ });
186
+ }, []);
187
+
188
+ const setPosition = useCallback((position: { x: number; y: number }) => {
189
+ setMascot((prev) => {
190
+ if (!prev) return null;
191
+ prev.setPosition(position);
192
+ return prev.clone();
193
+ });
194
+ }, []);
195
+
196
+ // Auto-initialize
197
+ useEffect(() => {
198
+ if (autoInitialize && initialConfig) {
199
+ initialize(initialConfig);
200
+ } else if (autoInitialize && initialTemplate) {
201
+ initializeFromTemplate(initialTemplate);
202
+ }
203
+ }, [autoInitialize, initialConfig, initialTemplate, initialize, initializeFromTemplate]);
204
+
205
+ return {
206
+ mascot,
207
+ isReady,
208
+ isPlaying,
209
+ currentAnimation,
210
+ initialize,
211
+ initializeFromTemplate,
212
+ setMood,
213
+ setEnergy,
214
+ playAnimation,
215
+ stopAnimation,
216
+ updateAppearance,
217
+ setBaseColor,
218
+ setAccentColor,
219
+ addAccessory,
220
+ removeAccessory,
221
+ setVisible,
222
+ setPosition,
223
+ };
224
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * useMascotAnimation Hook
3
+ * Advanced animation control with queue and sequencing
4
+ */
5
+
6
+ import { useCallback, useRef, useState } from 'react';
7
+ import type { Mascot } from '../../domain/entities/Mascot';
8
+ import type { AnimationSpeed } from '../../domain/types/MascotTypes';
9
+ import { AnimationController } from '../../infrastructure/controllers/AnimationController';
10
+ import type { AnimationOptions } from '../../domain/interfaces/IAnimationController';
11
+
12
+ export interface UseMascotAnimationOptions {
13
+ mascot: Mascot | null;
14
+ autoplay?: boolean;
15
+ queue?: boolean;
16
+ speed?: AnimationSpeed;
17
+ }
18
+
19
+ export interface UseMascotAnimationReturn {
20
+ isPlaying: boolean;
21
+ currentAnimation: string | null;
22
+ progress: number;
23
+ queue: string[];
24
+ play: (animationId: string, options?: AnimationOptions) => Promise<void>;
25
+ pause: () => void;
26
+ resume: () => void;
27
+ stop: () => void;
28
+ setSpeed: (speed: number) => void;
29
+ setProgress: (progress: number) => void;
30
+ queueAnimation: (animationId: string) => void;
31
+ clearQueue: () => void;
32
+ playSequence: (animationIds: string[]) => Promise<void>;
33
+ processQueue: () => Promise<void>;
34
+ }
35
+
36
+ const SPEED_MULTIPLIERS: Record<AnimationSpeed, number> = {
37
+ 'very-slow': 0.25,
38
+ 'slow': 0.5,
39
+ 'normal': 1,
40
+ 'fast': 1.5,
41
+ 'very-fast': 2,
42
+ };
43
+
44
+ export function useMascotAnimation(
45
+ options: UseMascotAnimationOptions
46
+ ): UseMascotAnimationReturn {
47
+ const { mascot, speed = 'normal' } = options;
48
+
49
+ const [isPlaying, setIsPlaying] = useState(false);
50
+ const [currentAnimation, setCurrentAnimation] = useState<string | null>(null);
51
+ const [progress, setProgress] = useState(0);
52
+ const [queue, setQueue] = useState<string[]>([]);
53
+
54
+ const animationControllerRef = useRef<AnimationController | null>(null);
55
+ const isProcessingQueueRef = useRef(false);
56
+
57
+ // Initialize animation controller
58
+ if (!animationControllerRef.current) {
59
+ animationControllerRef.current = new AnimationController();
60
+ }
61
+
62
+ // Setup progress tracking
63
+ const animationController = animationControllerRef.current;
64
+ animationController.on('progress', (data: unknown) => {
65
+ const { progress: newProgress } = data as { progress: number };
66
+ setProgress(newProgress);
67
+ });
68
+
69
+ const play = useCallback(async (animationId: string, options?: AnimationOptions) => {
70
+ if (!mascot) {
71
+ console.warn('Mascot not initialized');
72
+ return;
73
+ }
74
+
75
+ const animation = mascot.getAnimation(animationId);
76
+ if (!animation) {
77
+ console.warn(`Animation ${animationId} not found`);
78
+ return;
79
+ }
80
+
81
+ setIsPlaying(true);
82
+ setCurrentAnimation(animationId);
83
+
84
+ const speedMultiplier = SPEED_MULTIPLIERS[speed];
85
+ const finalOptions: AnimationOptions = {
86
+ ...options,
87
+ speed: (options?.speed || 1) * speedMultiplier,
88
+ };
89
+
90
+ await animationController.play(animation, finalOptions);
91
+
92
+ setIsPlaying(false);
93
+ setCurrentAnimation(null);
94
+ setProgress(0);
95
+ }, [mascot, speed, animationController]);
96
+
97
+ const pause = useCallback(() => {
98
+ animationController.pause();
99
+ }, [animationController]);
100
+
101
+ const resume = useCallback(() => {
102
+ animationController.resume();
103
+ }, [animationController]);
104
+
105
+ const stop = useCallback(() => {
106
+ animationController.stop();
107
+ setIsPlaying(false);
108
+ setCurrentAnimation(null);
109
+ setProgress(0);
110
+ }, [animationController]);
111
+
112
+ const setSpeed = useCallback((newSpeed: number) => {
113
+ animationController.setSpeed(newSpeed);
114
+ }, [animationController]);
115
+
116
+ const setProgressValue = useCallback((newProgress: number) => {
117
+ animationController.setProgress(newProgress);
118
+ setProgress(newProgress);
119
+ }, [animationController]);
120
+
121
+ const queueAnimation = useCallback((animationId: string) => {
122
+ setQueue((prev) => [...prev, animationId]);
123
+ }, []);
124
+
125
+ const clearQueue = useCallback(() => {
126
+ setQueue([]);
127
+ }, []);
128
+
129
+ const playSequence = useCallback(async (animationIds: string[]) => {
130
+ for (const animationId of animationIds) {
131
+ await play(animationId);
132
+ }
133
+ }, [play]);
134
+
135
+ // Process queue automatically
136
+ const processQueue = useCallback(async () => {
137
+ if (isProcessingQueueRef.current || queue.length === 0 || !mascot) {
138
+ return;
139
+ }
140
+
141
+ isProcessingQueueRef.current = true;
142
+
143
+ while (queue.length > 0) {
144
+ const nextAnimation = queue[0];
145
+ setQueue((prev) => prev.slice(1));
146
+ await play(nextAnimation);
147
+ }
148
+
149
+ isProcessingQueueRef.current = false;
150
+ }, [queue, mascot, play]);
151
+
152
+ return {
153
+ isPlaying,
154
+ currentAnimation,
155
+ progress,
156
+ queue,
157
+ play,
158
+ pause,
159
+ resume,
160
+ stop,
161
+ setSpeed,
162
+ setProgress: setProgressValue,
163
+ queueAnimation,
164
+ clearQueue,
165
+ playSequence,
166
+ processQueue,
167
+ };
168
+ }