heartraite 1.0.213 → 1.0.215

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.
@@ -1,5 +1,6 @@
1
1
  export declare enum NotificationSetting {
2
2
  NEW_MATCH = "new-match",
3
3
  NEW_LIKE = "new-like",
4
- NEW_MESSAGE = "new-message"
4
+ NEW_MESSAGE = "new-message",
5
+ MATCH_SUGGESTION = "match-suggestion"
5
6
  }
@@ -6,4 +6,5 @@ var NotificationSetting;
6
6
  NotificationSetting["NEW_MATCH"] = "new-match";
7
7
  NotificationSetting["NEW_LIKE"] = "new-like";
8
8
  NotificationSetting["NEW_MESSAGE"] = "new-message";
9
+ NotificationSetting["MATCH_SUGGESTION"] = "match-suggestion";
9
10
  })(NotificationSetting || (exports.NotificationSetting = NotificationSetting = {}));
@@ -8,5 +8,6 @@ export declare const useTypewriter: ({ text, typingSpeed, initialDelay, blinking
8
8
  displayedText: string;
9
9
  showDot: boolean;
10
10
  isComplete: boolean;
11
+ revealAll: () => void;
11
12
  };
12
13
  export {};
@@ -6,56 +6,63 @@ const useTypewriter = ({ text, typingSpeed = 50, initialDelay = 1000, blinkingSp
6
6
  const [isComplete, setIsComplete] = (0, react_1.useState)(false);
7
7
  const [displayedText, setDisplayedText] = (0, react_1.useState)("");
8
8
  const [showDot, setShowDot] = (0, react_1.useState)(false);
9
+ const timersRef = (0, react_1.useRef)({ blinking: null, timeouts: new Set() });
10
+ const clearTimers = (0, react_1.useCallback)(() => {
11
+ const timers = timersRef.current;
12
+ if (timers.blinking)
13
+ clearInterval(timers.blinking);
14
+ timers.blinking = null;
15
+ timers.timeouts.forEach(clearTimeout);
16
+ timers.timeouts.clear();
17
+ }, []);
9
18
  (0, react_1.useEffect)(() => {
10
19
  if (!text || text.length === 0) {
11
- setDisplayedText(""); // No text to display
12
- setIsComplete(true); // Mark typing as complete immediately
13
- setShowDot(false); // No blinking dot for empty text
14
- return; // Exit early if text is empty
20
+ setDisplayedText("");
21
+ setIsComplete(true);
22
+ setShowDot(false);
23
+ return;
15
24
  }
16
- let blinkingInterval = null;
17
- let typingTimeout = null;
18
- // Initialize state
19
- setDisplayedText(""); // Clear the displayed text
20
- setShowDot(true); // Start with a blinking dot
21
- setIsComplete(false); // Reset completion flag
22
- // Start blinking dot effect
23
- blinkingInterval = setInterval(() => {
25
+ const timers = timersRef.current;
26
+ const schedule = (fn, ms) => {
27
+ const id = setTimeout(() => {
28
+ timers.timeouts.delete(id);
29
+ fn();
30
+ }, ms);
31
+ timers.timeouts.add(id);
32
+ };
33
+ setDisplayedText("");
34
+ setShowDot(true);
35
+ setIsComplete(false);
36
+ timers.blinking = setInterval(() => {
24
37
  setShowDot((prev) => !prev);
25
38
  }, blinkingSpeed);
26
- // Start typing after the initial delay
27
- typingTimeout = setTimeout(() => {
28
- if (blinkingInterval) {
29
- clearInterval(blinkingInterval); // Stop blinking before typing
30
- setShowDot(false); // Hide blinking dot
31
- }
32
- // Typing effect
33
- let currentIndex = 0;
39
+ schedule(() => {
40
+ if (timers.blinking)
41
+ clearInterval(timers.blinking);
42
+ timers.blinking = null;
43
+ setShowDot(false);
44
+ let currentIndex = 1;
34
45
  const typeNextChar = () => {
35
46
  if (currentIndex < text.length) {
36
- const charToAdd = text[currentIndex];
37
- setDisplayedText((prev) => prev + charToAdd); // Append character
38
47
  currentIndex++;
39
- setTimeout(typeNextChar, typingSpeed); // Schedule next character
48
+ setDisplayedText(text.slice(0, currentIndex));
49
+ schedule(typeNextChar, typingSpeed);
40
50
  }
41
51
  else {
42
- setIsComplete(true); // Mark as complete
52
+ setIsComplete(true);
43
53
  }
44
54
  };
45
- // Trigger the first character manually
46
55
  setDisplayedText(text.charAt(0));
47
- currentIndex = 1;
48
- // Schedule the rest of the characters
49
- setTimeout(typeNextChar, typingSpeed);
56
+ schedule(typeNextChar, typingSpeed);
50
57
  }, initialDelay);
51
- // Cleanup on unmount or dependencies change
52
- return () => {
53
- if (blinkingInterval)
54
- clearInterval(blinkingInterval);
55
- if (typingTimeout)
56
- clearTimeout(typingTimeout);
57
- };
58
- }, [text, typingSpeed, initialDelay, blinkingSpeed]);
59
- return { displayedText, showDot, isComplete };
58
+ return clearTimers;
59
+ }, [text, typingSpeed, initialDelay, blinkingSpeed, clearTimers]);
60
+ const revealAll = (0, react_1.useCallback)(() => {
61
+ clearTimers();
62
+ setDisplayedText(text ?? "");
63
+ setShowDot(false);
64
+ setIsComplete(true);
65
+ }, [text, clearTimers]);
66
+ return { displayedText, showDot, isComplete, revealAll };
60
67
  };
61
68
  exports.useTypewriter = useTypewriter;
@@ -68,4 +68,43 @@ describe("useTypewriter", () => {
68
68
  });
69
69
  expect(result.current.displayedText).toBe(sampleText.slice(0, 2));
70
70
  });
71
+ it("reveals the full text immediately when revealAll is called mid-typing", () => {
72
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)(defaultOptions));
73
+ (0, react_1.act)(() => {
74
+ jest.advanceTimersByTime(defaultOptions.initialDelay + 100);
75
+ });
76
+ expect(result.current.isComplete).toBe(false);
77
+ (0, react_1.act)(() => {
78
+ result.current.revealAll();
79
+ });
80
+ expect(result.current.displayedText).toBe(sampleText);
81
+ expect(result.current.isComplete).toBe(true);
82
+ expect(result.current.showDot).toBe(false);
83
+ (0, react_1.act)(() => {
84
+ jest.advanceTimersByTime(sampleText.length * defaultOptions.typingSpeed);
85
+ });
86
+ expect(result.current.displayedText).toBe(sampleText);
87
+ });
88
+ it("reveals the full text when revealAll is called before typing starts", () => {
89
+ const { result } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)(defaultOptions));
90
+ (0, react_1.act)(() => {
91
+ result.current.revealAll();
92
+ });
93
+ expect(result.current.displayedText).toBe(sampleText);
94
+ expect(result.current.isComplete).toBe(true);
95
+ expect(result.current.showDot).toBe(false);
96
+ (0, react_1.act)(() => {
97
+ jest.advanceTimersByTime(defaultOptions.initialDelay * 2);
98
+ });
99
+ expect(result.current.displayedText).toBe(sampleText);
100
+ expect(result.current.showDot).toBe(false);
101
+ });
102
+ it("stops all timers on unmount", () => {
103
+ const { unmount } = (0, react_1.renderHook)(() => (0, useTypewriter_1.useTypewriter)(defaultOptions));
104
+ (0, react_1.act)(() => {
105
+ jest.advanceTimersByTime(defaultOptions.initialDelay + 100);
106
+ });
107
+ unmount();
108
+ expect(jest.getTimerCount()).toBe(0);
109
+ });
71
110
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heartraite",
3
- "version": "1.0.213",
3
+ "version": "1.0.215",
4
4
  "description": "Heartraite npm package for common functionality",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -2,4 +2,5 @@ export enum NotificationSetting {
2
2
  NEW_MATCH = "new-match",
3
3
  NEW_LIKE = "new-like",
4
4
  NEW_MESSAGE = "new-message",
5
+ MATCH_SUGGESTION = "match-suggestion",
5
6
  }
@@ -83,4 +83,51 @@ describe("useTypewriter", () => {
83
83
  });
84
84
  expect(result.current.displayedText).toBe(sampleText.slice(0, 2));
85
85
  });
86
+
87
+ it("reveals the full text immediately when revealAll is called mid-typing", () => {
88
+ const { result } = renderHook(() => useTypewriter(defaultOptions));
89
+
90
+ act(() => {
91
+ jest.advanceTimersByTime(defaultOptions.initialDelay + 100);
92
+ });
93
+ expect(result.current.isComplete).toBe(false);
94
+
95
+ act(() => {
96
+ result.current.revealAll();
97
+ });
98
+ expect(result.current.displayedText).toBe(sampleText);
99
+ expect(result.current.isComplete).toBe(true);
100
+ expect(result.current.showDot).toBe(false);
101
+
102
+ act(() => {
103
+ jest.advanceTimersByTime(sampleText.length * defaultOptions.typingSpeed);
104
+ });
105
+ expect(result.current.displayedText).toBe(sampleText);
106
+ });
107
+
108
+ it("reveals the full text when revealAll is called before typing starts", () => {
109
+ const { result } = renderHook(() => useTypewriter(defaultOptions));
110
+
111
+ act(() => {
112
+ result.current.revealAll();
113
+ });
114
+ expect(result.current.displayedText).toBe(sampleText);
115
+ expect(result.current.isComplete).toBe(true);
116
+ expect(result.current.showDot).toBe(false);
117
+
118
+ act(() => {
119
+ jest.advanceTimersByTime(defaultOptions.initialDelay * 2);
120
+ });
121
+ expect(result.current.displayedText).toBe(sampleText);
122
+ expect(result.current.showDot).toBe(false);
123
+ });
124
+
125
+ it("stops all timers on unmount", () => {
126
+ const { unmount } = renderHook(() => useTypewriter(defaultOptions));
127
+ act(() => {
128
+ jest.advanceTimersByTime(defaultOptions.initialDelay + 100);
129
+ });
130
+ unmount();
131
+ expect(jest.getTimerCount()).toBe(0);
132
+ });
86
133
  });
@@ -1,10 +1,10 @@
1
- import { useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
 
3
3
  interface UseTypewriterOptions {
4
4
  text: string;
5
- typingSpeed?: number; // Time in ms between each character
6
- initialDelay?: number; // Time in ms before typing starts
7
- blinkingSpeed?: number; // Time in ms for the blinking dot toggle
5
+ typingSpeed?: number;
6
+ initialDelay?: number;
7
+ blinkingSpeed?: number;
8
8
  }
9
9
 
10
10
  export const useTypewriter = ({
@@ -16,63 +16,73 @@ export const useTypewriter = ({
16
16
  const [isComplete, setIsComplete] = useState(false);
17
17
  const [displayedText, setDisplayedText] = useState("");
18
18
  const [showDot, setShowDot] = useState(false);
19
+ const timersRef = useRef<{
20
+ blinking: ReturnType<typeof setInterval> | null;
21
+ timeouts: Set<ReturnType<typeof setTimeout>>;
22
+ }>({ blinking: null, timeouts: new Set() });
23
+
24
+ const clearTimers = useCallback(() => {
25
+ const timers = timersRef.current;
26
+ if (timers.blinking) clearInterval(timers.blinking);
27
+ timers.blinking = null;
28
+ timers.timeouts.forEach(clearTimeout);
29
+ timers.timeouts.clear();
30
+ }, []);
19
31
 
20
32
  useEffect(() => {
21
33
  if (!text || text.length === 0) {
22
- setDisplayedText(""); // No text to display
23
- setIsComplete(true); // Mark typing as complete immediately
24
- setShowDot(false); // No blinking dot for empty text
25
- return; // Exit early if text is empty
34
+ setDisplayedText("");
35
+ setIsComplete(true);
36
+ setShowDot(false);
37
+ return;
26
38
  }
27
39
 
28
- let blinkingInterval: NodeJS.Timeout | null = null;
29
- let typingTimeout: NodeJS.Timeout | null = null;
40
+ const timers = timersRef.current;
41
+ const schedule = (fn: () => void, ms: number) => {
42
+ const id = setTimeout(() => {
43
+ timers.timeouts.delete(id);
44
+ fn();
45
+ }, ms);
46
+ timers.timeouts.add(id);
47
+ };
30
48
 
31
- // Initialize state
32
- setDisplayedText(""); // Clear the displayed text
33
- setShowDot(true); // Start with a blinking dot
34
- setIsComplete(false); // Reset completion flag
49
+ setDisplayedText("");
50
+ setShowDot(true);
51
+ setIsComplete(false);
35
52
 
36
- // Start blinking dot effect
37
- blinkingInterval = setInterval(() => {
53
+ timers.blinking = setInterval(() => {
38
54
  setShowDot((prev) => !prev);
39
55
  }, blinkingSpeed);
40
56
 
41
- // Start typing after the initial delay
42
- typingTimeout = setTimeout(() => {
43
- if (blinkingInterval) {
44
- clearInterval(blinkingInterval); // Stop blinking before typing
45
- setShowDot(false); // Hide blinking dot
46
- }
47
-
48
- // Typing effect
49
- let currentIndex = 0;
57
+ schedule(() => {
58
+ if (timers.blinking) clearInterval(timers.blinking);
59
+ timers.blinking = null;
60
+ setShowDot(false);
50
61
 
62
+ let currentIndex = 1;
51
63
  const typeNextChar = () => {
52
64
  if (currentIndex < text.length) {
53
- const charToAdd = text[currentIndex];
54
- setDisplayedText((prev) => prev + charToAdd); // Append character
55
65
  currentIndex++;
56
- setTimeout(typeNextChar, typingSpeed); // Schedule next character
66
+ setDisplayedText(text.slice(0, currentIndex));
67
+ schedule(typeNextChar, typingSpeed);
57
68
  } else {
58
- setIsComplete(true); // Mark as complete
69
+ setIsComplete(true);
59
70
  }
60
71
  };
61
72
 
62
- // Trigger the first character manually
63
73
  setDisplayedText(text.charAt(0));
64
- currentIndex = 1;
65
-
66
- // Schedule the rest of the characters
67
- setTimeout(typeNextChar, typingSpeed);
74
+ schedule(typeNextChar, typingSpeed);
68
75
  }, initialDelay);
69
76
 
70
- // Cleanup on unmount or dependencies change
71
- return () => {
72
- if (blinkingInterval) clearInterval(blinkingInterval);
73
- if (typingTimeout) clearTimeout(typingTimeout);
74
- };
75
- }, [text, typingSpeed, initialDelay, blinkingSpeed]);
77
+ return clearTimers;
78
+ }, [text, typingSpeed, initialDelay, blinkingSpeed, clearTimers]);
79
+
80
+ const revealAll = useCallback(() => {
81
+ clearTimers();
82
+ setDisplayedText(text ?? "");
83
+ setShowDot(false);
84
+ setIsComplete(true);
85
+ }, [text, clearTimers]);
76
86
 
77
- return { displayedText, showDot, isComplete };
87
+ return { displayedText, showDot, isComplete, revealAll };
78
88
  };