releasebird-javascript-sdk 1.0.87 → 1.0.89

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.
@@ -54,6 +54,9 @@ export default class RbirdWebsiteWidget {
54
54
  initialX = 0;
55
55
  initialY = 0;
56
56
 
57
+ // Notification sound
58
+ audioContext = null;
59
+
57
60
  static getInstance() {
58
61
  if (!this.instance) {
59
62
  this.instance = new RbirdWebsiteWidget();
@@ -369,8 +372,9 @@ export default class RbirdWebsiteWidget {
369
372
 
370
373
  if (e.data === 'newMessageArrived') {
371
374
  this.countNotifications();
372
- // Also refresh message bubbles when new message arrives
375
+ // Play notification sound and refresh message bubbles when new message arrives
373
376
  if (!RbirdUtils.hasClass(this.widgetContent, 'cta__modal--visible')) {
377
+ this.playNotificationSound();
374
378
  this.fetchUnreadMessages();
375
379
  if (this.iframe) {
376
380
  this.iframe.contentWindow?.postMessage({
@@ -588,6 +592,43 @@ export default class RbirdWebsiteWidget {
588
592
  this.messageBubblesContainer.style.right = 'unset';
589
593
  }
590
594
 
595
+ /**
596
+ * Play a notification sound when new messages arrive
597
+ */
598
+ playNotificationSound() {
599
+ try {
600
+ // Create AudioContext lazily (required for browser autoplay policies)
601
+ if (!this.audioContext) {
602
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
603
+ }
604
+
605
+ const ctx = this.audioContext;
606
+ const now = ctx.currentTime;
607
+
608
+ // Create oscillator for the "plopp" sound
609
+ const oscillator = ctx.createOscillator();
610
+ const gainNode = ctx.createGain();
611
+
612
+ oscillator.connect(gainNode);
613
+ gainNode.connect(ctx.destination);
614
+
615
+ // Plopp sound: quick frequency drop with fast decay
616
+ oscillator.type = 'sine';
617
+ oscillator.frequency.setValueAtTime(800, now);
618
+ oscillator.frequency.exponentialRampToValueAtTime(300, now + 0.1);
619
+
620
+ // Quick attack and decay for the "plopp" effect
621
+ gainNode.gain.setValueAtTime(0, now);
622
+ gainNode.gain.linearRampToValueAtTime(0.3, now + 0.02);
623
+ gainNode.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
624
+
625
+ oscillator.start(now);
626
+ oscillator.stop(now + 0.15);
627
+ } catch (e) {
628
+ // Silently fail if audio is not supported
629
+ }
630
+ }
631
+
591
632
  /**
592
633
  * Create a single message bubble element
593
634
  * @param {Object} msg - Message object with chatId, text, senderName, senderAvatar, timestamp
@@ -763,6 +804,20 @@ export default class RbirdWebsiteWidget {
763
804
  this.dragHandle.addEventListener('touchstart', (e) => this.startDrag(e), { passive: false });
764
805
  document.addEventListener('touchmove', (e) => this.onDrag(e), { passive: false });
765
806
  document.addEventListener('touchend', (e) => this.endDrag(e));
807
+
808
+ // Window resize listener to update position when viewport changes
809
+ window.addEventListener('resize', () => this.onWindowResize());
810
+ }
811
+
812
+ /**
813
+ * Handle window resize - update widget position to stay relative to viewport
814
+ */
815
+ onWindowResize() {
816
+ const savedPosition = this.loadWidgetPosition();
817
+ if (savedPosition && this.widgetWrapper) {
818
+ this.updateWidgetPosition(savedPosition.x, savedPosition.y);
819
+ this.updateRelatedElementsPosition();
820
+ }
766
821
  }
767
822
 
768
823
  /**
@@ -863,11 +918,16 @@ export default class RbirdWebsiteWidget {
863
918
  }
864
919
 
865
920
  /**
866
- * Save widget position to localStorage
921
+ * Save widget position to localStorage as percentage of viewport
867
922
  */
868
923
  saveWidgetPosition(x, y) {
869
924
  try {
870
- const position = { x, y, timestamp: Date.now() };
925
+ // Store position as percentage of viewport for responsive behavior
926
+ const position = {
927
+ xPercent: x / window.innerWidth,
928
+ yPercent: y / window.innerHeight,
929
+ timestamp: Date.now()
930
+ };
871
931
  window.localStorage.setItem('rbird_widget_position', JSON.stringify(position));
872
932
  } catch (e) {
873
933
  console.warn('[RbirdWidget] Could not save position to localStorage:', e);
@@ -875,17 +935,38 @@ export default class RbirdWebsiteWidget {
875
935
  }
876
936
 
877
937
  /**
878
- * Load widget position from localStorage
938
+ * Load widget position from localStorage and convert to current viewport pixels
879
939
  */
880
940
  loadWidgetPosition() {
881
941
  try {
882
942
  const saved = window.localStorage.getItem('rbird_widget_position');
883
943
  if (saved) {
884
944
  const position = JSON.parse(saved);
885
- // Validate position is still within viewport
886
- if (position.x >= 0 && position.x < window.innerWidth &&
887
- position.y >= 0 && position.y < window.innerHeight) {
888
- return position;
945
+
946
+ // Handle new percentage-based format
947
+ if (position.xPercent !== undefined && position.yPercent !== undefined) {
948
+ const x = position.xPercent * window.innerWidth;
949
+ const y = position.yPercent * window.innerHeight;
950
+
951
+ // Ensure position is within viewport bounds
952
+ const bubbleSize = 60; // approximate bubble size
953
+ const clampedX = Math.max(0, Math.min(x, window.innerWidth - bubbleSize));
954
+ const clampedY = Math.max(0, Math.min(y, window.innerHeight - bubbleSize));
955
+
956
+ return { x: clampedX, y: clampedY };
957
+ }
958
+
959
+ // Handle legacy absolute pixel format - convert to percentage and save
960
+ if (position.x !== undefined && position.y !== undefined) {
961
+ // Clamp to current viewport
962
+ const bubbleSize = 60;
963
+ const clampedX = Math.max(0, Math.min(position.x, window.innerWidth - bubbleSize));
964
+ const clampedY = Math.max(0, Math.min(position.y, window.innerHeight - bubbleSize));
965
+
966
+ // Convert to percentage and save for future
967
+ this.saveWidgetPosition(clampedX, clampedY);
968
+
969
+ return { x: clampedX, y: clampedY };
889
970
  }
890
971
  }
891
972
  } catch (e) {
package/src/index.js CHANGED
@@ -4,6 +4,8 @@ import RbirdWebsiteWidget from "./RbirdWebsiteWidget";
4
4
  import { ReleasebirdConsoleLogger } from "./ReleasebirdConsoleLogger";
5
5
  import { RbirdBannerManager } from "./RbirdBannerManager";
6
6
  import { RbirdFormManager } from "./RbirdFormManager";
7
+ import { RbirdSurveyManager } from "./RbirdSurveyManager";
8
+ import { RbirdAutomationManager } from "./RbirdAutomationManager";
7
9
 
8
10
  class Rbird {
9
11
 
@@ -93,6 +95,15 @@ class Rbird {
93
95
  // Initialize form manager
94
96
  const formManager = RbirdFormManager.getInstance();
95
97
  formManager.init(apiKey);
98
+
99
+ // Initialize survey manager
100
+ const surveyManager = RbirdSurveyManager.getInstance();
101
+ surveyManager.init(apiKey);
102
+
103
+ // Initialize automation manager
104
+ const automationManager = RbirdAutomationManager.getInstance();
105
+ automationManager.init(apiKey);
106
+
96
107
  resolve();
97
108
  });
98
109
  });
@@ -161,6 +172,37 @@ class Rbird {
161
172
  }
162
173
  }
163
174
 
175
+ /**
176
+ * Show a survey by its ID
177
+ * @param {string} surveyId - The ID of the survey to show
178
+ * @param {Object} options - Optional configuration
179
+ * @param {string} options.target - CSS selector for inline rendering (optional, shows as modal if not provided)
180
+ * @param {Function} options.onSubmit - Callback when survey is submitted
181
+ * @param {Function} options.onClose - Callback when survey is closed
182
+ */
183
+ static showSurvey(surveyId, options = {}) {
184
+ if (typeof window === 'undefined') return;
185
+ try {
186
+ RbirdSurveyManager.getInstance().showSurvey(surveyId, options);
187
+ } catch (e) {
188
+ console.error(e);
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Track a custom event for automations
194
+ * @param {string} eventName - The name of the event
195
+ * @param {Object} data - Additional event data
196
+ */
197
+ static track(eventName, data = {}) {
198
+ if (typeof window === 'undefined') return;
199
+ try {
200
+ RbirdAutomationManager.getInstance().track(eventName, data);
201
+ } catch (e) {
202
+ console.error(e);
203
+ }
204
+ }
205
+
164
206
  }
165
207
 
166
208
  export const runFunctionWhenDomIsReady = (callback) => {