collavre_slack 0.2.2 → 0.2.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50c834145802449d01365be47375e1d4bbb4a05a2622abee20449d75b24c8c2c
4
- data.tar.gz: d7b9d88bad8aac159618cd4f336937aea08e43362c2c2891304a0a895bfd2ece
3
+ metadata.gz: 13a4005b2d4412089caa83e43e2be4868ce50aa024a77904e7644320d30a5cbb
4
+ data.tar.gz: c5a8501c5e9149b4648a10dc5eac2ede04451e0474cc0d59119f123b2d6763dd
5
5
  SHA512:
6
- metadata.gz: b1c34b49e220e6b2cd44e1978f5b3b9fd088d2f18d85f6e8628817486ae7ecf110e2f09a81cf25fb6ea9f6aa4e3a76fcbba61970067a3a5fb866495a82d9050b
7
- data.tar.gz: 60b924d455319f87ea3de4ab8a340fb7b93b20df413cb1e96d5e3c78bacb09d94888eda06f0ed08028072e3cae19265b3c88aa5403856147dd0bb7c2766b67cc
6
+ metadata.gz: 82d93fd5f769a694bb3afc27246b75f161b8f5d50afd9a09e1f1939eb26f5e774c193e9b6a2ce4d04c0ac5faf088c13abb6d693afdd0080463bd4b5ccb1fa463
7
+ data.tar.gz: 9de647749fb26de533513da779f9726185781071129c28080568402b8f30fb3037694175c7bf91f007f25acc005f07f44fc1186f49d01b15f719409633fe1a2a
@@ -24,3 +24,36 @@
24
24
  background: var(--color-drag-over);
25
25
  border-left: 3px solid var(--color-secondary-active);
26
26
  }
27
+
28
+ /* Slack badge in comments popup header */
29
+ .slack-channel-badge {
30
+ display: none;
31
+ font-size: 0.7em;
32
+ font-weight: 600;
33
+ background: #4A154B;
34
+ color: white;
35
+ padding: 0.15em 0.45em;
36
+ border-radius: 4px;
37
+ margin-left: 0.5em;
38
+ cursor: pointer;
39
+ white-space: nowrap;
40
+ vertical-align: middle;
41
+ line-height: 1.4;
42
+ }
43
+
44
+ .slack-channel-badge:hover {
45
+ background: #611f64;
46
+ }
47
+
48
+ .slack-channel-tooltip {
49
+ display: none;
50
+ font-size: 0.75em;
51
+ color: var(--color-text-secondary);
52
+ margin-left: 0.4em;
53
+ white-space: nowrap;
54
+ vertical-align: middle;
55
+ }
56
+
57
+ .slack-channel-tooltip.visible {
58
+ display: inline;
59
+ }
@@ -60,6 +60,17 @@ module CollavreSlack
60
60
  end
61
61
  end
62
62
 
63
+ # Lightweight endpoint for badge display — returns only existing links, no Slack API calls
64
+ def badge
65
+ target_creative = @creative.effective_origin
66
+ links = SlackChannelLink.where(creative: target_creative)
67
+ render json: {
68
+ links: links.map { |link|
69
+ { channel_name: link.channel_name }
70
+ }
71
+ }
72
+ end
73
+
63
74
  def destroy
64
75
  link = SlackChannelLink.find(params[:id])
65
76
  # Check against origin creative
@@ -487,35 +487,79 @@ if (!slackIntegrationInitialized) {
487
487
  });
488
488
 
489
489
  // Slack badge for comments popup
490
+ // Store references for cleanup to avoid handler accumulation across popup opens
491
+ let slackBadgeClickHandler = null;
492
+ let slackDocumentClickHandler = null;
493
+ let slackBadgeRequestId = 0; // Guard against stale async responses
494
+
490
495
  document.addEventListener('comments-popup:opened', async function (event) {
491
496
  const { creativeId, badgeContainer } = event.detail;
492
497
  if (!badgeContainer || !creativeId) return;
493
498
 
499
+ // Increment request id to invalidate any in-flight fetch
500
+ const currentRequestId = ++slackBadgeRequestId;
501
+
494
502
  // Create or find slack badge element
495
503
  let badge = badgeContainer.querySelector('[data-slack-badge]');
496
504
  if (!badge) {
497
505
  badge = document.createElement('span');
498
506
  badge.setAttribute('data-slack-badge', '');
499
507
  badge.className = 'slack-channel-badge';
500
- badge.style.cssText = 'display:none;font-size:0.75em;background:#4A154B;color:white;padding:0.15em 0.5em;border-radius:4px;margin-left:0.5em;';
501
508
  badgeContainer.appendChild(badge);
502
509
  }
503
510
 
511
+ // Clean up previous handlers before re-registering
512
+ if (slackBadgeClickHandler) {
513
+ badge.removeEventListener('click', slackBadgeClickHandler);
514
+ slackBadgeClickHandler = null;
515
+ }
516
+ if (slackDocumentClickHandler) {
517
+ document.removeEventListener('click', slackDocumentClickHandler);
518
+ slackDocumentClickHandler = null;
519
+ }
520
+
521
+ // Remove any existing tooltip
522
+ const existingTooltip = badgeContainer.querySelector('[data-slack-tooltip]');
523
+ if (existingTooltip) existingTooltip.remove();
524
+
504
525
  // Hide badge initially
505
526
  badge.style.display = 'none';
506
527
  badge.textContent = '';
507
528
 
508
529
  try {
509
- const response = await fetch(`/slack/creatives/${creativeId}/slack_integrations`, {
530
+ const response = await fetch(`/slack/creatives/${creativeId}/slack_integrations/badge`, {
510
531
  headers: { Accept: 'application/json' }
511
532
  });
533
+
534
+ // Discard response if a newer popup open has occurred
535
+ if (currentRequestId !== slackBadgeRequestId) return;
536
+
512
537
  if (!response.ok) return;
513
538
 
514
539
  const data = await response.json();
515
540
  if (data.links && data.links.length > 0) {
516
- const channelNames = data.links.map(link => `#${link.channel_name}`).join(', ');
517
- badge.textContent = `Slack: ${channelNames}`;
541
+ badge.textContent = 'Slack';
518
542
  badge.style.display = 'inline-block';
543
+ badge.title = data.links.map(link => `#${link.channel_name}`).join(', ');
544
+
545
+ // Create tooltip element for click display
546
+ const tooltip = document.createElement('span');
547
+ tooltip.setAttribute('data-slack-tooltip', '');
548
+ tooltip.className = 'slack-channel-tooltip';
549
+ tooltip.textContent = data.links.map(link => `#${link.channel_name}`).join(', ');
550
+ badgeContainer.appendChild(tooltip);
551
+
552
+ slackBadgeClickHandler = function (e) {
553
+ e.stopPropagation();
554
+ tooltip.classList.toggle('visible');
555
+ };
556
+ badge.addEventListener('click', slackBadgeClickHandler);
557
+
558
+ // Close tooltip when clicking elsewhere
559
+ slackDocumentClickHandler = function () {
560
+ tooltip.classList.remove('visible');
561
+ };
562
+ document.addEventListener('click', slackDocumentClickHandler);
519
563
  }
520
564
  } catch (error) {
521
565
  console.warn('Failed to load Slack badge:', error);
@@ -526,10 +570,23 @@ if (!slackIntegrationInitialized) {
526
570
  const { badgeContainer } = event.detail;
527
571
  if (!badgeContainer) return;
528
572
 
573
+ // Invalidate any in-flight badge fetch so late responses are discarded
574
+ slackBadgeRequestId++;
575
+
529
576
  const badge = badgeContainer.querySelector('[data-slack-badge]');
530
577
  if (badge) {
531
578
  badge.style.display = 'none';
532
579
  badge.textContent = '';
580
+ if (slackBadgeClickHandler) {
581
+ badge.removeEventListener('click', slackBadgeClickHandler);
582
+ slackBadgeClickHandler = null;
583
+ }
584
+ }
585
+ if (slackDocumentClickHandler) {
586
+ document.removeEventListener('click', slackDocumentClickHandler);
587
+ slackDocumentClickHandler = null;
533
588
  }
589
+ const tooltip = badgeContainer.querySelector('[data-slack-tooltip]');
590
+ if (tooltip) tooltip.remove();
534
591
  });
535
592
  }
data/config/routes.rb CHANGED
@@ -3,7 +3,11 @@ CollavreSlack::Engine.routes.draw do
3
3
  get "/auth/slack/callback", to: "slack_auth#callback"
4
4
 
5
5
  resources :creatives, only: [] do
6
- resources :slack_integrations, module: :creatives, only: [ :index, :create, :destroy ]
6
+ resources :slack_integrations, module: :creatives, only: [ :index, :create, :destroy ] do
7
+ collection do
8
+ get :badge
9
+ end
10
+ end
7
11
  resources :slack_messages, only: [ :create ]
8
12
  end
9
13
 
@@ -1,3 +1,3 @@
1
1
  module CollavreSlack
2
- VERSION = "0.2.2"
2
+ VERSION = "0.2.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collavre_slack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collavre