react-broadcast-sync 1.1.4 → 1.2.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.
@@ -3,6 +3,9 @@ import {
3
3
  BroadcastActions,
4
4
  BroadcastMessage,
5
5
  BroadcastOptions,
6
+ ClearMessage,
7
+ ClearReceivedMessagesOptions,
8
+ ClearSentMessagesOptions,
6
9
  SendMessageOptions,
7
10
  } from '../types/types';
8
11
  import {
@@ -17,9 +20,8 @@ import {
17
20
  } from '../utils/messageUtils';
18
21
  import { debug } from '../utils/debug';
19
22
 
20
- const INTERNAL_MESSAGE_TYPES = {
21
- CLEAR_MESSAGE: 'CLEAR_MESSAGE',
22
- CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
23
+ const INTERNAL_MESSAGE_TYPES: Record<string, ClearMessage> = {
24
+ CLEAR_SENT_MESSAGES: 'CLEAR_SENT_MESSAGES',
23
25
  } as const;
24
26
 
25
27
  export const useBroadcastChannel = (
@@ -46,13 +48,8 @@ export const useBroadcastChannel = (
46
48
  const receivedMessageIds = useRef(new Map<string, number>());
47
49
  const source = useRef(sourceName || generateSourceName()).current;
48
50
  const internalTypes = useRef({
49
- CLEAR_MESSAGE: getInternalMessageType(
50
- INTERNAL_MESSAGE_TYPES.CLEAR_MESSAGE,
51
- channelName,
52
- namespace
53
- ),
54
- CLEAR_ALL_MESSAGES: getInternalMessageType(
55
- INTERNAL_MESSAGE_TYPES.CLEAR_ALL_MESSAGES,
51
+ CLEAR_SENT_MESSAGES: getInternalMessageType(
52
+ INTERNAL_MESSAGE_TYPES.CLEAR_SENT_MESSAGES,
56
53
  channelName,
57
54
  namespace
58
55
  ),
@@ -80,42 +77,6 @@ export const useBroadcastChannel = (
80
77
  setTimeout(() => setError(null), 3000);
81
78
  }, []);
82
79
 
83
- const clearMessage = useCallback(
84
- (id: string) => {
85
- setMessages(prev => prev.filter(msg => msg.id !== id));
86
- setSentMessages(prev => prev.filter(msg => msg.id !== id));
87
- channel.current?.postMessage({
88
- id,
89
- type: internalTypes.CLEAR_MESSAGE,
90
- source,
91
- });
92
- debug.message.cleared(id);
93
- },
94
- [source, internalTypes.CLEAR_MESSAGE]
95
- );
96
-
97
- const clearAllMessages = useCallback(() => {
98
- setMessages([]);
99
- setSentMessages([]);
100
- channel.current?.postMessage({
101
- id: internalTypes.CLEAR_ALL_MESSAGES,
102
- type: internalTypes.CLEAR_ALL_MESSAGES,
103
- source,
104
- });
105
- }, [source, internalTypes.CLEAR_ALL_MESSAGES]);
106
-
107
- const clearSentMessage = useCallback(
108
- (id: string) => {
109
- setSentMessages(prev => prev.filter(msg => msg.id !== id));
110
- channel.current?.postMessage({
111
- id,
112
- type: internalTypes.CLEAR_MESSAGE,
113
- source,
114
- });
115
- },
116
- [source, internalTypes.CLEAR_MESSAGE]
117
- );
118
-
119
80
  const postMessage = useCallback(
120
81
  (messageType: string, messageContent: any, options: SendMessageOptions = {}) => {
121
82
  const channelCurrent = channel.current;
@@ -140,6 +101,55 @@ export const useBroadcastChannel = (
140
101
  [source, setErrorMessage]
141
102
  );
142
103
 
104
+ const clearReceivedMessages = useCallback((options: ClearReceivedMessagesOptions = {}) => {
105
+ const hasFilters = Boolean(
106
+ (options.ids && options.ids.length) ||
107
+ (options.types && options.types.length) ||
108
+ (options.sources && options.sources.length)
109
+ );
110
+
111
+ setMessages(prev =>
112
+ hasFilters
113
+ ? prev.filter(
114
+ msg =>
115
+ !(options.ids && options.ids.includes(msg.id)) &&
116
+ !(options.types && options.types.includes(msg.type)) &&
117
+ !(options.sources && options.sources.includes(msg.source))
118
+ )
119
+ : []
120
+ );
121
+ }, []);
122
+
123
+ const clearSentMessages = useCallback((options: ClearSentMessagesOptions = {}) => {
124
+ const { ids = [], types = [], sync = false } = options ?? {};
125
+ setSentMessages(prev =>
126
+ ids.length > 0 || types.length > 0
127
+ ? prev.filter(msg => {
128
+ // Only consider messages from the same sender
129
+ if (msg.source !== source) return true;
130
+
131
+ // Decide whether this message should be cleared:
132
+ // - If the ids array is empty, treat as wildcard (match all ids)
133
+ // - If the types array is empty, treat as wildcard (match all types)
134
+ const idMatches = ids.length === 0 || ids.includes(msg.id);
135
+ const typeMatches = types.length === 0 || types.includes(msg.type);
136
+
137
+ // Remove when BOTH criteria match (wildcards included)
138
+ return !(idMatches && typeMatches);
139
+ })
140
+ : []
141
+ );
142
+ if (sync) {
143
+ channel.current?.postMessage(
144
+ createMessage(
145
+ internalTypes.CLEAR_SENT_MESSAGES,
146
+ { ids: options.ids ?? [], types: options.types ?? [] },
147
+ source
148
+ )
149
+ );
150
+ }
151
+ }, []);
152
+
143
153
  const handleMessage = useCallback(
144
154
  (event: MessageEvent) => {
145
155
  try {
@@ -160,15 +170,26 @@ export const useBroadcastChannel = (
160
170
  }
161
171
 
162
172
  if (isValidInternalClearMessage(message)) {
163
- if (message.type === internalTypes.CLEAR_MESSAGE) {
173
+ if (message.type === internalTypes.CLEAR_SENT_MESSAGES) {
174
+ const { ids = [], types = [] } = (message as any).message || {};
175
+
164
176
  setMessages(prev =>
165
- prev.filter(msg => !(msg.id === message.id && msg.source === message.source))
177
+ prev.filter(msg => {
178
+ // Only consider messages from the same sender
179
+ if (msg.source !== message.source) return true;
180
+
181
+ // Decide whether this message should be cleared:
182
+ // - If the ids array is empty, treat as wildcard (match all ids)
183
+ // - If the types array is empty, treat as wildcard (match all types)
184
+ const idMatches = ids.length === 0 || ids.includes(msg.id);
185
+ const typeMatches = types.length === 0 || types.includes(msg.type);
186
+
187
+ // Remove when BOTH criteria match (wildcards included)
188
+ return !(idMatches && typeMatches);
189
+ })
166
190
  );
167
- return;
168
- }
169
191
 
170
- if (message.type === internalTypes.CLEAR_ALL_MESSAGES) {
171
- setMessages(prev => prev.filter(msg => msg.source !== message.source));
192
+ // After handling we don't want to process this internal control packet further
172
193
  return;
173
194
  }
174
195
  }
@@ -249,9 +270,8 @@ export const useBroadcastChannel = (
249
270
  messages,
250
271
  sentMessages,
251
272
  postMessage,
252
- clearMessage,
253
- clearAllMessages,
254
- clearSentMessage,
273
+ clearReceivedMessages,
274
+ clearSentMessages,
255
275
  error,
256
276
  };
257
277
  };
@@ -65,19 +65,19 @@ describe('messageUtils', () => {
65
65
  });
66
66
 
67
67
  it('getInternalMessageType produces unique hashes for different channels', () => {
68
- const one = getInternalMessageType('CLEAR_MESSAGE', 'chanA');
69
- const two = getInternalMessageType('CLEAR_MESSAGE', 'chanB');
68
+ const one = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chanA');
69
+ const two = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chanB');
70
70
  expect(one).not.toBe(two);
71
71
  });
72
72
 
73
73
  it('getInternalMessageType generates consistent hash string', () => {
74
- const type = getInternalMessageType('CLEAR_MESSAGE', 'my-channel', 'ns');
74
+ const type = getInternalMessageType('CLEAR_SENT_MESSAGES', 'my-channel', 'ns');
75
75
  expect(type).toContain('__INTERNAL__');
76
- expect(type).toContain('CLEAR_MESSAGE');
76
+ expect(type).toContain('CLEAR_SENT_MESSAGES');
77
77
  });
78
78
 
79
79
  it('isInternalType detects internal message types', () => {
80
- const type = getInternalMessageType('CLEAR_ALL_MESSAGES', 'chan');
80
+ const type = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chan');
81
81
  expect(isInternalType(type)).toBe(true);
82
82
  expect(isInternalType('normal-type')).toBe(false);
83
83
  });
@@ -85,7 +85,7 @@ describe('messageUtils', () => {
85
85
  it('isValidInternalClearMessage detects valid structure', () => {
86
86
  const valid = {
87
87
  id: 'abc',
88
- type: getInternalMessageType('CLEAR_ALL_MESSAGES', 'ch'),
88
+ type: getInternalMessageType('CLEAR_SENT_MESSAGES', 'ch'),
89
89
  source: 'tab-1',
90
90
  };
91
91
  const invalid = { id: 123, type: 'bad', source: 'tab-1' };