@seamly/web-ui 22.3.3 → 22.3.5-beta.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.
Files changed (35) hide show
  1. package/build/dist/lib/components.js +1485 -15
  2. package/build/dist/lib/components.js.map +1 -1
  3. package/build/dist/lib/components.min.js +1 -1
  4. package/build/dist/lib/components.min.js.map +1 -1
  5. package/build/dist/lib/hooks.js +1519 -9
  6. package/build/dist/lib/hooks.js.map +1 -1
  7. package/build/dist/lib/hooks.min.js +1 -1
  8. package/build/dist/lib/hooks.min.js.map +1 -1
  9. package/build/dist/lib/index.debug.js +16 -5
  10. package/build/dist/lib/index.debug.js.map +1 -1
  11. package/build/dist/lib/index.debug.min.js +1 -1
  12. package/build/dist/lib/index.debug.min.js.LICENSE.txt +4 -0
  13. package/build/dist/lib/index.debug.min.js.map +1 -1
  14. package/build/dist/lib/index.js +259 -245
  15. package/build/dist/lib/index.js.map +1 -1
  16. package/build/dist/lib/index.min.js +1 -1
  17. package/build/dist/lib/index.min.js.map +1 -1
  18. package/build/dist/lib/standalone.js +537 -246
  19. package/build/dist/lib/standalone.js.map +1 -1
  20. package/build/dist/lib/standalone.min.js +1 -1
  21. package/build/dist/lib/standalone.min.js.map +1 -1
  22. package/build/dist/lib/style-guide.js +266 -252
  23. package/build/dist/lib/style-guide.js.map +1 -1
  24. package/build/dist/lib/style-guide.min.js +1 -1
  25. package/build/dist/lib/style-guide.min.js.map +1 -1
  26. package/build/dist/lib/utils.js +8498 -8428
  27. package/build/dist/lib/utils.js.map +1 -1
  28. package/build/dist/lib/utils.min.js +1 -1
  29. package/build/dist/lib/utils.min.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/javascripts/api/conversation-connector.ts +48 -32
  32. package/src/javascripts/ui/components/core/seamly-event-subscriber.ts +197 -218
  33. package/src/javascripts/ui/hooks/seamly-hooks.js +5 -5
  34. package/src/javascripts/ui/hooks/use-notifications.ts +1 -1
  35. package/src/javascripts/ui/hooks/use-seamly-conversation.ts +13 -0
@@ -3641,6 +3641,14 @@ var _ConversationConnector_instances, _ConversationConnector_connectionListeners
3641
3641
 
3642
3642
 
3643
3643
  const log = debug_default()('seamly');
3644
+ // Subscribers set which are used to subscribe to changes in the conversation.
3645
+ // Needs to be outside of the ConversationConnector class so its not recreated for each instance.
3646
+ const subscribers = new Set();
3647
+ // Syncs the lifecycle of the conversation with Preact. Each subscriber will fetch the latest value from the conversation if needed.
3648
+ const emitChange = () => {
3649
+ // Call the callback function for each subscriber
3650
+ subscribers.forEach(callback => callback());
3651
+ };
3644
3652
  class ConversationConnector {
3645
3653
  constructor() {
3646
3654
  _ConversationConnector_instances.add(this);
@@ -3656,35 +3664,37 @@ class ConversationConnector {
3656
3664
  url: splittedUrl,
3657
3665
  params
3658
3666
  } = split_url_params(this.url);
3659
- if (!this.socket) {
3660
- this.socket = new Socket(splittedUrl, {
3661
- params: Object.assign(Object.assign({}, params), {
3662
- v: apiVersion
3663
- }),
3664
- reconnectAfterMs: tries => {
3665
- // Calculate the backoff time based on the number of tries.
3666
- const backoff = Math.pow(2, tries) * 250;
3667
- // Limit the backoff time to 10 seconds.
3668
- return Math.min(backoff, 10000);
3669
- }
3670
- });
3671
- }
3672
- // Checks if the socket is not already connected before attempting a connection.
3673
- // This prevents multiple connection attempts during server reconnects, network switches or SPA route changes.
3674
- if (!this.socket.isConnected()) {
3675
- this.socket.connect();
3676
- }
3677
- this.channel = this.socket.channel(this.channelTopic, {
3678
- authorization: `Bearer ${this.accessToken}`,
3679
- channelName: this.channelName
3667
+ this.socket = new Socket(splittedUrl, {
3668
+ params: Object.assign(Object.assign({}, params), {
3669
+ v: apiVersion
3670
+ }),
3671
+ reconnectAfterMs: tries => {
3672
+ // Calculate the backoff time based on the number of tries.
3673
+ const backoff = Math.pow(2, tries) * 250;
3674
+ // Limit the backoff time to 10 seconds.
3675
+ return Math.min(backoff, 10000);
3676
+ }
3680
3677
  });
3681
- this.start();
3682
3678
  this.socket.onError(err => {
3683
3679
  log('[SOCKET][ERROR]', err);
3684
3680
  });
3685
3681
  this.socket.onOpen(() => {
3686
3682
  log('[SOCKET]OPEN');
3687
3683
  });
3684
+ this.socket.onClose(() => {
3685
+ log('[SOCKET]CLOSE');
3686
+ __classPrivateFieldGet(this, _ConversationConnector_instances, "m", _ConversationConnector_emitConnectionState).call(this, {
3687
+ connected: false,
3688
+ ready: false,
3689
+ currentState: 'socket_closed'
3690
+ });
3691
+ });
3692
+ this.socket.connect();
3693
+ this.channel = this.socket.channel(this.channelTopic, {
3694
+ authorization: `Bearer ${this.accessToken}`,
3695
+ channelName: this.channelName
3696
+ });
3697
+ this.start();
3688
3698
  this.channel.on('system', msg => {
3689
3699
  switch (msg.type) {
3690
3700
  case 'attach_channel_succeeded':
@@ -3696,14 +3706,6 @@ class ConversationConnector {
3696
3706
  break;
3697
3707
  }
3698
3708
  });
3699
- this.socket.onClose(() => {
3700
- log('[SOCKET]CLOSE');
3701
- __classPrivateFieldGet(this, _ConversationConnector_instances, "m", _ConversationConnector_emitConnectionState).call(this, {
3702
- connected: false,
3703
- ready: false,
3704
- currentState: 'socket_closed'
3705
- });
3706
- });
3707
3709
  this.channel.onClose(() => {
3708
3710
  log('[CHANNEL]CLOSE');
3709
3711
  __classPrivateFieldGet(this, _ConversationConnector_instances, "m", _ConversationConnector_emitConnectionState).call(this, {
@@ -3757,6 +3759,13 @@ class ConversationConnector {
3757
3759
  pushToChannel(command, payload, timeout = 10000) {
3758
3760
  this.channel.push(command, payload, timeout);
3759
3761
  }
3762
+ // Adds a callback function to the subscribers set and returns a method to unsubscribe from the store.
3763
+ // This method is used to sync the state with the lifecycle of Preact.
3764
+ static subscribe(callback) {
3765
+ subscribers.add(callback);
3766
+ // Returns a function that removes the callback from the subscribers set.
3767
+ return () => subscribers.delete(callback);
3768
+ }
3760
3769
  }
3761
3770
  _ConversationConnector_connectionListeners = new WeakMap(), _ConversationConnector_instances = new WeakSet(), _ConversationConnector_listenTo = function _ConversationConnector_listenTo(...types) {
3762
3771
  types.forEach(type => {
@@ -3770,6 +3779,7 @@ _ConversationConnector_connectionListeners = new WeakMap(), _ConversationConnect
3770
3779
  // If we only want to execute the callback once, remove it from the listener
3771
3780
  return !complete;
3772
3781
  }), "f");
3782
+ emitChange();
3773
3783
  };
3774
3784
  /* harmony default export */ const conversation_connector = (ConversationConnector);
3775
3785
  ;// CONCATENATED MODULE: ./src/javascripts/api/index.ts
@@ -4289,7 +4299,7 @@ _API_ready = new WeakMap(), _API_externalId = new WeakMap(), _API_conversationAu
4289
4299
  return {
4290
4300
  clientName: "@seamly/web-ui",
4291
4301
  clientVariant: api_classPrivateFieldGet(this, _API_layoutMode, "f"),
4292
- clientVersion: "22.3.3",
4302
+ clientVersion: "22.3.5-beta.1",
4293
4303
  currentUrl: window.location.toString(),
4294
4304
  screenResolution: `${window.screen.width}x${window.screen.height}`,
4295
4305
  timezone: getTimeZone(),
@@ -11597,6 +11607,18 @@ const useSeamlyMessageContainerClassNames = event => {
11597
11607
  }
11598
11608
  return classNames;
11599
11609
  };
11610
+ ;// CONCATENATED MODULE: external "preact/compat"
11611
+ const compat_namespaceObject = require("preact/compat");
11612
+ ;// CONCATENATED MODULE: ./src/javascripts/ui/hooks/use-seamly-conversation.ts
11613
+
11614
+
11615
+
11616
+ const useSeamlyConversation = () => {
11617
+ const api = useSeamlyApiContext();
11618
+ const getSnapshot = () => api.conversation;
11619
+ return (0,compat_namespaceObject.useSyncExternalStore)(conversation_connector.subscribe, getSnapshot);
11620
+ };
11621
+ /* harmony default export */ const use_seamly_conversation = (useSeamlyConversation);
11600
11622
  ;// CONCATENATED MODULE: ./src/javascripts/ui/hooks/focus-helper-hooks.js
11601
11623
 
11602
11624
 
@@ -12037,12 +12059,12 @@ const useStableCallback = callback => {
12037
12059
  // and imported directly from this file
12038
12060
  // Please do not remove
12039
12061
  const useSeamlyEventStream = (nextFn, filterFn) => {
12040
- const api = useSeamlyApiContext();
12062
+ const conversation = use_seamly_conversation();
12041
12063
  (0,hooks_.useEffect)(() => {
12042
- if (api.connectionInfo && api.conversation?.channel) {
12064
+ if (conversation.channel) {
12043
12065
  const {
12044
12066
  channel
12045
- } = api.conversation;
12067
+ } = conversation;
12046
12068
  channel.onMessage = (type, payload) => {
12047
12069
  if (!filterFn || filterFn({
12048
12070
  type,
@@ -12056,7 +12078,7 @@ const useSeamlyEventStream = (nextFn, filterFn) => {
12056
12078
  return payload;
12057
12079
  };
12058
12080
  }
12059
- }, [nextFn, filterFn, api.connectionInfo, api.conversation]);
12081
+ }, [nextFn, filterFn, conversation]);
12060
12082
  };
12061
12083
  ;// CONCATENATED MODULE: ./src/javascripts/domains/translations/selectors.ts
12062
12084
 
@@ -15637,9 +15659,10 @@ var seamly_event_subscriber_rest = undefined && undefined.__rest || function (s,
15637
15659
 
15638
15660
 
15639
15661
 
15662
+
15640
15663
  const EMITTABLE_MESSAGE_TYPES = ['text', 'choice_prompt', 'image', 'video'];
15641
15664
  const SeamlyEventSubscriber = () => {
15642
- const api = useSeamlyApiContext();
15665
+ const conversation = use_seamly_conversation();
15643
15666
  const syncChannelRef = (0,hooks_.useRef)();
15644
15667
  const messageChannelRef = (0,hooks_.useRef)();
15645
15668
  const dispatch = useAppDispatch();
@@ -15653,10 +15676,10 @@ const SeamlyEventSubscriber = () => {
15653
15676
  emitEvent
15654
15677
  } = use_seamly_commands();
15655
15678
  (0,hooks_.useEffect)(() => {
15656
- if (api.connectionInfo && api.conversation.socket) {
15679
+ if (conversation.socket) {
15657
15680
  const {
15658
15681
  socket
15659
- } = api.conversation;
15682
+ } = conversation;
15660
15683
  socket.onError(err => {
15661
15684
  const seamlyOfflineError = new SeamlyOfflineError(err);
15662
15685
  dispatch(setInterrupt({
@@ -15672,10 +15695,10 @@ const SeamlyEventSubscriber = () => {
15672
15695
  dispatch(clearInterrupt());
15673
15696
  });
15674
15697
  }
15675
- }, [api, api.connectionInfo, api.conversation.socket, dispatch]);
15698
+ }, [conversation, dispatch]);
15676
15699
  (0,hooks_.useEffect)(() => {
15677
- if (api.connectionInfo && api.conversation.socket) {
15678
- api.conversation.onConnection(({
15700
+ if (conversation.socket) {
15701
+ conversation.onConnection(({
15679
15702
  currentState
15680
15703
  }) => {
15681
15704
  if (currentState === 'join_channel_erred') {
@@ -15693,217 +15716,210 @@ const SeamlyEventSubscriber = () => {
15693
15716
  return false;
15694
15717
  });
15695
15718
  }
15696
- }, [api, api.connectionInfo, api.conversation.channel, dispatch]);
15719
+ }, [conversation, dispatch]);
15697
15720
  (0,hooks_.useEffect)(() => {
15698
- if (api.connectionInfo) {
15699
- const updateParticipant = event => {
15700
- if (event.type !== 'participant') {
15701
- return;
15702
- }
15703
- const {
15721
+ const updateParticipant = event => {
15722
+ if (event.type !== 'participant') {
15723
+ return;
15724
+ }
15725
+ const {
15726
+ payload
15727
+ } = event;
15728
+ if (!payload || !payload.participant) {
15729
+ return;
15730
+ }
15731
+ const {
15732
+ fromClient,
15733
+ participant
15734
+ } = payload;
15735
+ if (!fromClient && typeof participant !== 'string' && (participant === null || participant === void 0 ? void 0 : participant.name)) {
15736
+ dispatch(setHeaderSubTitle(participant.name));
15737
+ }
15738
+ dispatch(setParticipant({
15739
+ participant,
15740
+ fromClient
15741
+ }));
15742
+ if (typeof participant !== 'string' && participant.introduction) {
15743
+ dispatch(addEvent(event));
15744
+ }
15745
+ };
15746
+ conversation.onConnection(({
15747
+ connected
15748
+ }) => {
15749
+ if (!connected) return false;
15750
+ const {
15751
+ channel
15752
+ } = conversation;
15753
+ channel.onMessage = (type, payload) => {
15754
+ const event = {
15755
+ type,
15704
15756
  payload
15705
- } = event;
15706
- if (!payload || !payload.participant) {
15707
- return;
15708
- }
15709
- const {
15710
- fromClient,
15711
- participant
15712
- } = payload;
15713
- if (!fromClient && typeof participant !== 'string' && (participant === null || participant === void 0 ? void 0 : participant.name)) {
15714
- dispatch(setHeaderSubTitle(participant.name));
15715
- }
15716
- dispatch(setParticipant({
15717
- participant,
15718
- fromClient
15719
- }));
15720
- if (typeof participant !== 'string' && participant.introduction) {
15721
- dispatch(addEvent(event));
15722
- }
15723
- };
15724
- api.conversation.onConnection(({
15725
- connected
15726
- }) => {
15727
- if (!connected) return false;
15728
- const {
15729
- channel
15730
- } = api.conversation;
15731
- channel.onMessage = (type, payload) => {
15732
- const event = {
15733
- type,
15734
- payload
15735
- };
15736
- switch (type) {
15737
- case 'ui':
15738
- if (payload.state && payload.state.hasOwnProperty('loading')) {
15739
- dispatch(setIsLoading(payload.state.loading));
15740
- }
15741
- switch (payload.type) {
15742
- case 'idle_detach_countdown':
15743
- initCountdown(payload.body.duration);
15744
- break;
15745
- case 'idle_detach_countdown_elapsed':
15746
- endCountdown(undefined, true);
15747
- break;
15748
- case 'resume_conversation_prompt':
15749
- dispatch(initResumeConversationPrompt());
15750
- break;
15751
- case 'translation_proposal':
15752
- dispatch(setTranslationProposalPrompt(payload.body));
15753
- break;
15754
- case 'user_first_response':
15755
- dispatch(setHasResponded(true));
15756
- // @ts-ignore
15757
- eventBus.emit('system.userFirstResponse', payload.body);
15758
- break;
15759
- }
15760
- break;
15761
- case 'message':
15762
- updateParticipant(payload);
15763
- switch (payload.type) {
15764
- case 'text':
15765
- case 'choice_prompt':
15766
- case 'splash':
15767
- case 'image':
15768
- case 'upload':
15769
- case 'video':
15770
- case 'cta':
15771
- case 'custom':
15772
- case 'carousel':
15773
- case 'card':
15774
- if (payload.service && payload.service.serviceSessionId) {
15775
- dispatch(setActiveService(payload.service.serviceSessionId));
15776
- }
15777
- dispatch(addEvent(event));
15778
- break;
15779
- }
15780
- break;
15781
- case 'participant':
15782
- updateParticipant(event);
15783
- break;
15784
- case 'service_data':
15785
- if (payload.persist) {
15786
- dispatch(setServiceDataItem(payload));
15787
- }
15788
- break;
15789
- case 'ack':
15790
- dispatch(ackEvent(event));
15791
- break;
15792
- case 'system':
15793
- if (payload.type === 'service_changed') {
15794
- const {
15795
- serviceSettings
15796
- } = payload,
15797
- eventPayload = seamly_event_subscriber_rest(payload, ["serviceSettings"]);
15798
- const {
15799
- entry,
15800
- proactiveMessages
15801
- } = serviceSettings;
15802
- const {
15803
- upload
15804
- } = entry.options;
15805
- dispatch(setFeatureEnabledState({
15806
- key: featureKeys.uploads,
15807
- enabled: !!(upload && upload.enabled)
15808
- }));
15809
- dispatch(setProactiveMessages(proactiveMessages.enabled || false));
15810
- dispatch(setServiceEntryMetadata(entry));
15811
- if (payload.serviceSessionId) {
15812
- dispatch(setActiveService(payload.serviceSessionId));
15757
+ };
15758
+ switch (type) {
15759
+ case 'ui':
15760
+ if (payload.state && payload.state.hasOwnProperty('loading')) {
15761
+ dispatch(setIsLoading(payload.state.loading));
15762
+ }
15763
+ switch (payload.type) {
15764
+ case 'idle_detach_countdown':
15765
+ initCountdown(payload.body.duration);
15766
+ break;
15767
+ case 'idle_detach_countdown_elapsed':
15768
+ endCountdown(undefined, true);
15769
+ break;
15770
+ case 'resume_conversation_prompt':
15771
+ dispatch(initResumeConversationPrompt());
15772
+ break;
15773
+ case 'translation_proposal':
15774
+ dispatch(setTranslationProposalPrompt(payload.body));
15775
+ break;
15776
+ case 'user_first_response':
15777
+ dispatch(setHasResponded(true));
15778
+ // @ts-ignore
15779
+ eventBus.emit('system.userFirstResponse', payload.body);
15780
+ break;
15781
+ }
15782
+ break;
15783
+ case 'message':
15784
+ updateParticipant(payload);
15785
+ switch (payload.type) {
15786
+ case 'text':
15787
+ case 'choice_prompt':
15788
+ case 'splash':
15789
+ case 'image':
15790
+ case 'upload':
15791
+ case 'video':
15792
+ case 'cta':
15793
+ case 'custom':
15794
+ case 'carousel':
15795
+ case 'card':
15796
+ if (payload.service && payload.service.serviceSessionId) {
15797
+ dispatch(setActiveService(payload.service.serviceSessionId));
15813
15798
  }
15814
- emitEvent('system.serviceChanged', eventPayload);
15815
- }
15816
- break;
15817
- case 'info':
15818
- if (payload.type === 'divider' || payload.type === 'text' || payload.type === 'translation') {
15819
15799
  dispatch(addEvent(event));
15800
+ break;
15801
+ }
15802
+ break;
15803
+ case 'participant':
15804
+ updateParticipant(event);
15805
+ break;
15806
+ case 'service_data':
15807
+ if (payload.persist) {
15808
+ dispatch(setServiceDataItem(payload));
15809
+ }
15810
+ break;
15811
+ case 'ack':
15812
+ dispatch(ackEvent(event));
15813
+ break;
15814
+ case 'system':
15815
+ if (payload.type === 'service_changed') {
15816
+ const {
15817
+ serviceSettings
15818
+ } = payload,
15819
+ eventPayload = seamly_event_subscriber_rest(payload, ["serviceSettings"]);
15820
+ const {
15821
+ entry,
15822
+ proactiveMessages
15823
+ } = serviceSettings;
15824
+ const {
15825
+ upload
15826
+ } = entry.options;
15827
+ dispatch(setFeatureEnabledState({
15828
+ key: featureKeys.uploads,
15829
+ enabled: !!(upload && upload.enabled)
15830
+ }));
15831
+ dispatch(setProactiveMessages(proactiveMessages.enabled || false));
15832
+ dispatch(setServiceEntryMetadata(entry));
15833
+ if (payload.serviceSessionId) {
15834
+ dispatch(setActiveService(payload.serviceSessionId));
15820
15835
  }
15821
- break;
15822
- case 'error':
15823
- switch (payload.type) {
15824
- case 'find_conversation_erred':
15825
- const seamlySessionExpiredError = new SeamlySessionExpiredError(event);
15826
- dispatch(setInterrupt({
15827
- name: seamlySessionExpiredError.name,
15828
- action: seamlySessionExpiredError.action,
15829
- message: seamlySessionExpiredError.message,
15830
- originalEvent: seamlySessionExpiredError.originalEvent,
15831
- originalError: seamlySessionExpiredError.originalError
15832
- }));
15833
- break;
15834
- case 'conversation_erred':
15835
- case 'attach_channel_erred':
15836
- const seamlyGeneralError = new SeamlyGeneralError(event);
15837
- dispatch(setInterrupt({
15838
- name: seamlyGeneralError.name,
15839
- message: seamlyGeneralError.message,
15840
- langKey: seamlyGeneralError.langKey,
15841
- originalEvent: seamlyGeneralError.originalEvent,
15842
- originalError: seamlyGeneralError.originalError,
15843
- action: seamlyGeneralError.action
15844
- }));
15845
- break;
15846
- }
15847
- }
15848
- return payload;
15849
- };
15850
- return false;
15851
- });
15852
- }
15853
- }, [api, api.connectionInfo, api.conversation.channel, dispatch, emitEvent, endCountdown, eventBus, initCountdown]);
15854
- (0,hooks_.useEffect)(() => {
15855
- if (api.connectionInfo) {
15856
- api.conversation.onConnection(({
15857
- connected
15858
- }) => {
15859
- if (!connected) return false;
15860
- const {
15861
- channel
15862
- } = api.conversation;
15863
- if (messageChannelRef.current) {
15864
- channel === null || channel === void 0 ? void 0 : channel.off('message', messageChannelRef.current);
15836
+ emitEvent('system.serviceChanged', eventPayload);
15837
+ }
15838
+ break;
15839
+ case 'info':
15840
+ if (payload.type === 'divider' || payload.type === 'text' || payload.type === 'translation') {
15841
+ dispatch(addEvent(event));
15842
+ }
15843
+ break;
15844
+ case 'error':
15845
+ switch (payload.type) {
15846
+ case 'find_conversation_erred':
15847
+ const seamlySessionExpiredError = new SeamlySessionExpiredError(event);
15848
+ dispatch(setInterrupt({
15849
+ name: seamlySessionExpiredError.name,
15850
+ action: seamlySessionExpiredError.action,
15851
+ message: seamlySessionExpiredError.message,
15852
+ originalEvent: seamlySessionExpiredError.originalEvent,
15853
+ originalError: seamlySessionExpiredError.originalError
15854
+ }));
15855
+ break;
15856
+ case 'conversation_erred':
15857
+ case 'attach_channel_erred':
15858
+ const seamlyGeneralError = new SeamlyGeneralError(event);
15859
+ dispatch(setInterrupt({
15860
+ name: seamlyGeneralError.name,
15861
+ message: seamlyGeneralError.message,
15862
+ langKey: seamlyGeneralError.langKey,
15863
+ originalEvent: seamlyGeneralError.originalEvent,
15864
+ originalError: seamlyGeneralError.originalError,
15865
+ action: seamlyGeneralError.action
15866
+ }));
15867
+ break;
15868
+ }
15865
15869
  }
15866
- messageChannelRef.current = channel.on('message', payload => {
15867
- if (!EMITTABLE_MESSAGE_TYPES.includes(payload.type)) {
15868
- return payload;
15869
- }
15870
- // This check dedupes the sending of messages via
15871
- // the bus if a duplicate connection exists in an
15872
- // error situation.
15873
- if (payload.id !== prevEmittedEventId.current) {
15874
- // @ts-ignore
15875
- eventBus.emit('message', payload);
15876
- }
15877
- prevEmittedEventId.current = payload.id;
15870
+ return payload;
15871
+ };
15872
+ return false;
15873
+ });
15874
+ }, [conversation, dispatch, emitEvent, endCountdown, eventBus, initCountdown]);
15875
+ (0,hooks_.useEffect)(() => {
15876
+ conversation.onConnection(({
15877
+ connected
15878
+ }) => {
15879
+ if (!connected) return false;
15880
+ const {
15881
+ channel
15882
+ } = conversation;
15883
+ if (messageChannelRef.current) {
15884
+ channel.off('message', messageChannelRef.current);
15885
+ }
15886
+ messageChannelRef.current = channel.on('message', payload => {
15887
+ if (!EMITTABLE_MESSAGE_TYPES.includes(payload.type)) {
15878
15888
  return payload;
15879
- });
15880
- return true;
15889
+ }
15890
+ // This check dedupes the sending of messages via
15891
+ // the bus if a duplicate connection exists in an
15892
+ // error situation.
15893
+ if (payload.id !== prevEmittedEventId.current) {
15894
+ // @ts-ignore
15895
+ eventBus.emit('message', payload);
15896
+ }
15897
+ prevEmittedEventId.current = payload.id;
15898
+ return payload;
15881
15899
  });
15882
- }
15883
- }, [api, api.connectionInfo, api.conversation.channel, eventBus]);
15900
+ return true;
15901
+ });
15902
+ }, [conversation, eventBus]);
15884
15903
  (0,hooks_.useEffect)(() => {
15885
- if (api.connectionInfo) {
15886
- api.conversation.onConnection(({
15887
- connected
15888
- }) => {
15889
- var _a;
15890
- if (!connected) return false;
15891
- if (syncChannelRef.current) {
15892
- (_a = api.conversation.channel) === null || _a === void 0 ? void 0 : _a.off('sync', syncChannelRef.current);
15904
+ conversation.onConnection(({
15905
+ connected
15906
+ }) => {
15907
+ if (!connected) return false;
15908
+ if (syncChannelRef.current) {
15909
+ conversation.channel.off('sync', syncChannelRef.current);
15910
+ }
15911
+ syncChannelRef.current = conversation.channel.on('sync', payload => seamly_event_subscriber_awaiter(void 0, void 0, void 0, function* () {
15912
+ try {
15913
+ const history = yield dispatch(getConversation(payload)).unwrap();
15914
+ if (!history) return;
15915
+ dispatch(setHistory(history));
15916
+ } catch (_e) {
15917
+ // nothing to do, the error is handled in the thunk
15893
15918
  }
15894
- syncChannelRef.current = api.conversation.channel.on('sync', payload => seamly_event_subscriber_awaiter(void 0, void 0, void 0, function* () {
15895
- try {
15896
- const history = yield dispatch(getConversation(payload)).unwrap();
15897
- if (!history) return;
15898
- dispatch(setHistory(history));
15899
- } catch (_e) {
15900
- // nothing to do, the error is handled in the thunk
15901
- }
15902
- }));
15903
- return true;
15904
- });
15905
- }
15906
- }, [api, api.connectionInfo, api.conversation.channel, dispatch]);
15919
+ }));
15920
+ return true;
15921
+ });
15922
+ }, [conversation, dispatch]);
15907
15923
  return null;
15908
15924
  };
15909
15925
  /* harmony default export */ const seamly_event_subscriber = (SeamlyEventSubscriber);
@@ -16733,8 +16749,6 @@ function AbortTransactionButton() {
16733
16749
  ]), type: "button", onClick: handleAbortTransaction, children: abortTransaction.label }) }));
16734
16750
  }
16735
16751
 
16736
- ;// CONCATENATED MODULE: external "preact/compat"
16737
- const compat_namespaceObject = require("preact/compat");
16738
16752
  ;// CONCATENATED MODULE: ./src/javascripts/ui/components/conversation/event/chat-scroll/chat-scroll-context.ts
16739
16753
 
16740
16754
  const ChatScrollContext = (0,external_preact_.createContext)(null);
@@ -19585,7 +19599,7 @@ const useNotification = () => {
19585
19599
  const [visibilityState, setVisibilityState] = (0,hooks_.useState)(document.visibilityState);
19586
19600
  const showNotifications = useSelector_useSelector(selectShowNotifications);
19587
19601
  const requestPermission = (0,hooks_.useCallback)(() => use_notifications_awaiter(void 0, void 0, void 0, function* () {
19588
- if (Notification && permission !== 'granted') {
19602
+ if (hasNotificationSupport && permission !== 'granted') {
19589
19603
  const notificationPermission = yield Notification.requestPermission();
19590
19604
  setPermission(notificationPermission);
19591
19605
  }