@atlaskit/teams-public 0.70.8 → 0.71.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/teams-public
2
2
 
3
+ ## 0.71.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`884f8dadc3c94`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/884f8dadc3c94) -
8
+ Cleaned up feature gate related to the after icon sizing in containers
9
+
10
+ ## 0.70.9
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 0.70.8
4
17
 
5
18
  ### Patch Changes
@@ -15,7 +15,6 @@ var _css = require("@atlaskit/css");
15
15
  var _link = _interopRequireDefault(require("@atlaskit/icon/core/link"));
16
16
  var _linkExternal = _interopRequireDefault(require("@atlaskit/icon/core/link-external"));
17
17
  var _image = _interopRequireDefault(require("@atlaskit/image"));
18
- var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
19
18
  var _compiled = require("@atlaskit/primitives/compiled");
20
19
  var _ConfluenceIcon = _interopRequireDefault(require("../assets/ConfluenceIcon.svg"));
21
20
  var _JiraIcon = _interopRequireDefault(require("../assets/JiraIcon.svg"));
@@ -97,15 +96,14 @@ var getJiraIcon = function getJiraIcon(containerSubTypes) {
97
96
  var getJiraContainerProperties = function getJiraContainerProperties(_ref) {
98
97
  var containerTypeProperties = _ref.containerTypeProperties,
99
98
  _ref$iconSize = _ref.iconSize,
100
- iconSize = _ref$iconSize === void 0 ? (0, _platformFeatureFlags.fg)('ptc-fix-containers-after-icon-size') ? 'medium' : 'small' : _ref$iconSize;
99
+ iconSize = _ref$iconSize === void 0 ? 'medium' : _ref$iconSize;
101
100
  var _ref2 = containerTypeProperties || {},
102
101
  subType = _ref2.subType,
103
102
  name = _ref2.name;
104
- var isAfterIconSizeFixEnabled = (0, _platformFeatureFlags.fg)('ptc-fix-containers-after-icon-size');
105
103
  var baseProperties = {
106
104
  description: /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, messages.jiraProjectDescription),
107
105
  icon: /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
108
- xcss: (0, _css.cx)(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
106
+ xcss: (0, _css.cx)(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
109
107
  }, /*#__PURE__*/_react.default.createElement(_image.default, {
110
108
  src: getJiraIcon(subType),
111
109
  alt: "",
@@ -157,17 +155,16 @@ var getWebLinkContainerProperties = function getWebLinkContainerProperties(_ref3
157
155
  var getContainerProperties = exports.getContainerProperties = function getContainerProperties(_ref4) {
158
156
  var containerType = _ref4.containerType,
159
157
  _ref4$iconSize = _ref4.iconSize,
160
- iconSize = _ref4$iconSize === void 0 ? (0, _platformFeatureFlags.fg)('ptc-fix-containers-after-icon-size') ? 'medium' : 'small' : _ref4$iconSize,
158
+ iconSize = _ref4$iconSize === void 0 ? 'medium' : _ref4$iconSize,
161
159
  containerTypeProperties = _ref4.containerTypeProperties,
162
160
  isEmptyContainer = _ref4.isEmptyContainer,
163
161
  isDisplayedOnProfileCard = _ref4.isDisplayedOnProfileCard;
164
- var isAfterIconSizeFixEnabled = (0, _platformFeatureFlags.fg)('ptc-fix-containers-after-icon-size');
165
162
  switch (containerType) {
166
163
  case 'ConfluenceSpace':
167
164
  return {
168
165
  description: /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, messages.confluenceContainerDescription),
169
166
  icon: /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
170
- xcss: (0, _css.cx)(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
167
+ xcss: (0, _css.cx)(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
171
168
  }, /*#__PURE__*/_react.default.createElement(_image.default, {
172
169
  src: _ConfluenceIcon.default,
173
170
  alt: "",
@@ -180,7 +177,7 @@ var getContainerProperties = exports.getContainerProperties = function getContai
180
177
  return {
181
178
  description: /*#__PURE__*/_react.default.createElement(_reactIntlNext.FormattedMessage, messages.loomSpaceDescription),
182
179
  icon: /*#__PURE__*/_react.default.createElement(_compiled.Flex, {
183
- xcss: (0, _css.cx)(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
180
+ xcss: (0, _css.cx)(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
184
181
  }, /*#__PURE__*/_react.default.createElement(_image.default, {
185
182
  src: _LoomIcon.default,
186
183
  alt: "",
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.spaceInviteScheduler = void 0;
7
+ /**
8
+ * FE debounce mechanism to avoid spamming invites when users consecutively
9
+ * link and unlink teams from containers in quick succession. We'll log the
10
+ * frequency of this occurring and decide whether to move debouncing to the
11
+ * BE as a productionisation task.
12
+ *
13
+ * Uses module-level state rather than a globalThis singleton to avoid polluting
14
+ * the global scope — a simple in-memory workaround is sufficient here.
15
+ */
16
+
17
+ var DEBOUNCE_MS = 30000;
18
+ var pending = new Map();
19
+
20
+ // Callers pass IDs in different formats — the teams package passes full ARIs
21
+ // (e.g. "ari:cloud:identity::team/uuid") while teams-public passes bare IDs
22
+ // (e.g. "uuid"). Normalize to just the resource ID after the last "/" so both
23
+ // formats produce the same map key.
24
+ var extractId = function extractId(ari) {
25
+ var lastSlash = ari.lastIndexOf('/');
26
+ return lastSlash === -1 ? ari : ari.substring(lastSlash + 1);
27
+ };
28
+ var getKey = function getKey(teamId, containerId) {
29
+ return "".concat(extractId(teamId), ":").concat(extractId(containerId));
30
+ };
31
+ var spaceInviteScheduler = exports.spaceInviteScheduler = {
32
+ scheduleInvite: function scheduleInvite(teamId, containerId, callback) {
33
+ var key = getKey(teamId, containerId);
34
+ var existing = pending.get(key);
35
+ if (existing) {
36
+ clearTimeout(existing.timeoutId);
37
+ }
38
+ pending.set(key, {
39
+ timeoutId: setTimeout(function () {
40
+ pending.delete(key);
41
+ callback();
42
+ }, DEBOUNCE_MS)
43
+ });
44
+ },
45
+ cancelInvite: function cancelInvite(teamId, containerId) {
46
+ var key = getKey(teamId, containerId);
47
+ var existing = pending.get(key);
48
+ if (existing) {
49
+ clearTimeout(existing.timeoutId);
50
+ pending.delete(key);
51
+ }
52
+ }
53
+ };
package/dist/cjs/index.js CHANGED
@@ -63,6 +63,12 @@ Object.defineProperty(exports, "hasProductPermission", {
63
63
  return _utils.hasProductPermission;
64
64
  }
65
65
  });
66
+ Object.defineProperty(exports, "spaceInviteScheduler", {
67
+ enumerable: true,
68
+ get: function get() {
69
+ return _spaceInviteScheduler.spaceInviteScheduler;
70
+ }
71
+ });
66
72
  Object.defineProperty(exports, "useConnectedTeams", {
67
73
  enumerable: true,
68
74
  get: function get() {
@@ -117,4 +123,5 @@ var _utils = require("./controllers/product-permission/utils");
117
123
  var _getContainerProperties = require("./common/utils/get-container-properties");
118
124
  var _assets = require("./common/assets");
119
125
  var _separator = require("./common/ui/separator");
120
- var _getIsExperimentEnabled = require("./common/utils/get-is-experiment-enabled");
126
+ var _getIsExperimentEnabled = require("./common/utils/get-is-experiment-enabled");
127
+ var _spaceInviteScheduler = require("./common/utils/spaceInviteScheduler");
@@ -20,6 +20,7 @@ var _teamsAppInternalAnalytics = require("@atlaskit/teams-app-internal-analytics
20
20
  var _teamsAppInternalProductPermissions = require("@atlaskit/teams-app-internal-product-permissions");
21
21
  var _colors = require("@atlaskit/theme/colors");
22
22
  var _teamContainersSkeleton = require("../../common/ui/team-containers-skeleton");
23
+ var _spaceInviteScheduler = require("../../common/utils/spaceInviteScheduler");
23
24
  var _controllers = require("../../controllers");
24
25
  var _useCreateContainers3 = require("../../controllers/hooks/use-create-containers");
25
26
  var _useProductPermission3 = require("../../controllers/hooks/use-product-permission");
@@ -218,6 +219,9 @@ var TeamContainers = exports.TeamContainers = function TeamContainers(_ref) {
218
219
  if (unlinkError) {
219
220
  fireEvent('track.teamContainerUnlinked.failed', {});
220
221
  } else {
222
+ if ((0, _platformFeatureFlags.fg)('space-team_linking_invites_fg')) {
223
+ _spaceInviteScheduler.spaceInviteScheduler.cancelInvite(teamId, containerId);
224
+ }
221
225
  fireEvent('track.teamContainerUnlinked.succeeded', {
222
226
  containerRemoved: {
223
227
  containerId: removedContainer === null || removedContainer === void 0 ? void 0 : removedContainer.id,
@@ -7,7 +7,6 @@ import { cx } from '@atlaskit/css';
7
7
  import LinkIcon from '@atlaskit/icon/core/link';
8
8
  import LinkExternalIcon from '@atlaskit/icon/core/link-external';
9
9
  import Image from '@atlaskit/image';
10
- import { fg } from '@atlaskit/platform-feature-flags';
11
10
  import { Box, Flex, Text } from '@atlaskit/primitives/compiled';
12
11
  import ConfluenceIcon from '../assets/ConfluenceIcon.svg';
13
12
  import JiraIcon from '../assets/JiraIcon.svg';
@@ -86,17 +85,16 @@ const getJiraIcon = containerSubTypes => {
86
85
  };
87
86
  const getJiraContainerProperties = ({
88
87
  containerTypeProperties,
89
- iconSize = fg('ptc-fix-containers-after-icon-size') ? 'medium' : 'small'
88
+ iconSize = 'medium'
90
89
  }) => {
91
90
  const {
92
91
  subType,
93
92
  name
94
93
  } = containerTypeProperties || {};
95
- const isAfterIconSizeFixEnabled = fg('ptc-fix-containers-after-icon-size');
96
94
  const baseProperties = {
97
95
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.jiraProjectDescription),
98
96
  icon: /*#__PURE__*/React.createElement(Flex, {
99
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
97
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
100
98
  }, /*#__PURE__*/React.createElement(Image, {
101
99
  src: getJiraIcon(subType),
102
100
  alt: "",
@@ -149,18 +147,17 @@ const getWebLinkContainerProperties = ({
149
147
  };
150
148
  export const getContainerProperties = ({
151
149
  containerType,
152
- iconSize = fg('ptc-fix-containers-after-icon-size') ? 'medium' : 'small',
150
+ iconSize = 'medium',
153
151
  containerTypeProperties,
154
152
  isEmptyContainer,
155
153
  isDisplayedOnProfileCard
156
154
  }) => {
157
- const isAfterIconSizeFixEnabled = fg('ptc-fix-containers-after-icon-size');
158
155
  switch (containerType) {
159
156
  case 'ConfluenceSpace':
160
157
  return {
161
158
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.confluenceContainerDescription),
162
159
  icon: /*#__PURE__*/React.createElement(Flex, {
163
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
160
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
164
161
  }, /*#__PURE__*/React.createElement(Image, {
165
162
  src: ConfluenceIcon,
166
163
  alt: "",
@@ -173,7 +170,7 @@ export const getContainerProperties = ({
173
170
  return {
174
171
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.loomSpaceDescription),
175
172
  icon: /*#__PURE__*/React.createElement(Flex, {
176
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
173
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
177
174
  }, /*#__PURE__*/React.createElement(Image, {
178
175
  src: LoomIcon,
179
176
  alt: "",
@@ -0,0 +1,45 @@
1
+ /**
2
+ * FE debounce mechanism to avoid spamming invites when users consecutively
3
+ * link and unlink teams from containers in quick succession. We'll log the
4
+ * frequency of this occurring and decide whether to move debouncing to the
5
+ * BE as a productionisation task.
6
+ *
7
+ * Uses module-level state rather than a globalThis singleton to avoid polluting
8
+ * the global scope — a simple in-memory workaround is sufficient here.
9
+ */
10
+
11
+ const DEBOUNCE_MS = 30_000;
12
+ const pending = new Map();
13
+
14
+ // Callers pass IDs in different formats — the teams package passes full ARIs
15
+ // (e.g. "ari:cloud:identity::team/uuid") while teams-public passes bare IDs
16
+ // (e.g. "uuid"). Normalize to just the resource ID after the last "/" so both
17
+ // formats produce the same map key.
18
+ const extractId = ari => {
19
+ const lastSlash = ari.lastIndexOf('/');
20
+ return lastSlash === -1 ? ari : ari.substring(lastSlash + 1);
21
+ };
22
+ const getKey = (teamId, containerId) => `${extractId(teamId)}:${extractId(containerId)}`;
23
+ export const spaceInviteScheduler = {
24
+ scheduleInvite: (teamId, containerId, callback) => {
25
+ const key = getKey(teamId, containerId);
26
+ const existing = pending.get(key);
27
+ if (existing) {
28
+ clearTimeout(existing.timeoutId);
29
+ }
30
+ pending.set(key, {
31
+ timeoutId: setTimeout(() => {
32
+ pending.delete(key);
33
+ callback();
34
+ }, DEBOUNCE_MS)
35
+ });
36
+ },
37
+ cancelInvite: (teamId, containerId) => {
38
+ const key = getKey(teamId, containerId);
39
+ const existing = pending.get(key);
40
+ if (existing) {
41
+ clearTimeout(existing.timeoutId);
42
+ pending.delete(key);
43
+ }
44
+ }
45
+ };
@@ -10,4 +10,5 @@ export { hasProductPermission } from './controllers/product-permission/utils';
10
10
  export { getContainerProperties } from './common/utils/get-container-properties';
11
11
  export { ConfluenceIcon, JiraIcon, LoomIcon } from './common/assets';
12
12
  export { Separator } from './common/ui/separator';
13
- export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
13
+ export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
14
+ export { spaceInviteScheduler } from './common/utils/spaceInviteScheduler';
@@ -10,6 +10,7 @@ import { useAnalyticsEvents } from '@atlaskit/teams-app-internal-analytics';
10
10
  import { hasProductPermission, useProductPermissions } from '@atlaskit/teams-app-internal-product-permissions';
11
11
  import { N0, N90 } from '@atlaskit/theme/colors';
12
12
  import { TeamContainersSkeleton } from '../../common/ui/team-containers-skeleton';
13
+ import { spaceInviteScheduler } from '../../common/utils/spaceInviteScheduler';
13
14
  import { hasProductPermission as hasProductPermissionOld } from '../../controllers';
14
15
  import { useCreateContainers } from '../../controllers/hooks/use-create-containers';
15
16
  import { useProductPermissions as useProductPermissionsOld } from '../../controllers/hooks/use-product-permission';
@@ -169,6 +170,9 @@ export const TeamContainers = ({
169
170
  if (unlinkError) {
170
171
  fireEvent('track.teamContainerUnlinked.failed', {});
171
172
  } else {
173
+ if (fg('space-team_linking_invites_fg')) {
174
+ spaceInviteScheduler.cancelInvite(teamId, containerId);
175
+ }
172
176
  fireEvent('track.teamContainerUnlinked.succeeded', {
173
177
  containerRemoved: {
174
178
  containerId: removedContainer === null || removedContainer === void 0 ? void 0 : removedContainer.id,
@@ -10,7 +10,6 @@ import { cx } from '@atlaskit/css';
10
10
  import LinkIcon from '@atlaskit/icon/core/link';
11
11
  import LinkExternalIcon from '@atlaskit/icon/core/link-external';
12
12
  import Image from '@atlaskit/image';
13
- import { fg } from '@atlaskit/platform-feature-flags';
14
13
  import { Box, Flex, Text } from '@atlaskit/primitives/compiled';
15
14
  import ConfluenceIcon from '../assets/ConfluenceIcon.svg';
16
15
  import JiraIcon from '../assets/JiraIcon.svg';
@@ -90,15 +89,14 @@ var getJiraIcon = function getJiraIcon(containerSubTypes) {
90
89
  var getJiraContainerProperties = function getJiraContainerProperties(_ref) {
91
90
  var containerTypeProperties = _ref.containerTypeProperties,
92
91
  _ref$iconSize = _ref.iconSize,
93
- iconSize = _ref$iconSize === void 0 ? fg('ptc-fix-containers-after-icon-size') ? 'medium' : 'small' : _ref$iconSize;
92
+ iconSize = _ref$iconSize === void 0 ? 'medium' : _ref$iconSize;
94
93
  var _ref2 = containerTypeProperties || {},
95
94
  subType = _ref2.subType,
96
95
  name = _ref2.name;
97
- var isAfterIconSizeFixEnabled = fg('ptc-fix-containers-after-icon-size');
98
96
  var baseProperties = {
99
97
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.jiraProjectDescription),
100
98
  icon: /*#__PURE__*/React.createElement(Flex, {
101
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
99
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
102
100
  }, /*#__PURE__*/React.createElement(Image, {
103
101
  src: getJiraIcon(subType),
104
102
  alt: "",
@@ -150,17 +148,16 @@ var getWebLinkContainerProperties = function getWebLinkContainerProperties(_ref3
150
148
  export var getContainerProperties = function getContainerProperties(_ref4) {
151
149
  var containerType = _ref4.containerType,
152
150
  _ref4$iconSize = _ref4.iconSize,
153
- iconSize = _ref4$iconSize === void 0 ? fg('ptc-fix-containers-after-icon-size') ? 'medium' : 'small' : _ref4$iconSize,
151
+ iconSize = _ref4$iconSize === void 0 ? 'medium' : _ref4$iconSize,
154
152
  containerTypeProperties = _ref4.containerTypeProperties,
155
153
  isEmptyContainer = _ref4.isEmptyContainer,
156
154
  isDisplayedOnProfileCard = _ref4.isDisplayedOnProfileCard;
157
- var isAfterIconSizeFixEnabled = fg('ptc-fix-containers-after-icon-size');
158
155
  switch (containerType) {
159
156
  case 'ConfluenceSpace':
160
157
  return {
161
158
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.confluenceContainerDescription),
162
159
  icon: /*#__PURE__*/React.createElement(Flex, {
163
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
160
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
164
161
  }, /*#__PURE__*/React.createElement(Image, {
165
162
  src: ConfluenceIcon,
166
163
  alt: "",
@@ -173,7 +170,7 @@ export var getContainerProperties = function getContainerProperties(_ref4) {
173
170
  return {
174
171
  description: /*#__PURE__*/React.createElement(FormattedMessage, messages.loomSpaceDescription),
175
172
  icon: /*#__PURE__*/React.createElement(Flex, {
176
- xcss: cx(iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' && isAfterIconSizeFixEnabled ? styles.smallAvatarMargin : styles.avatarMargin)
173
+ xcss: cx(iconSize === 'small' ? styles.smallAvatarWrapper : styles.avatarWrapper, iconSize === 'small' ? styles.smallAvatarMargin : styles.avatarMargin)
177
174
  }, /*#__PURE__*/React.createElement(Image, {
178
175
  src: LoomIcon,
179
176
  alt: "",
@@ -0,0 +1,47 @@
1
+ /**
2
+ * FE debounce mechanism to avoid spamming invites when users consecutively
3
+ * link and unlink teams from containers in quick succession. We'll log the
4
+ * frequency of this occurring and decide whether to move debouncing to the
5
+ * BE as a productionisation task.
6
+ *
7
+ * Uses module-level state rather than a globalThis singleton to avoid polluting
8
+ * the global scope — a simple in-memory workaround is sufficient here.
9
+ */
10
+
11
+ var DEBOUNCE_MS = 30000;
12
+ var pending = new Map();
13
+
14
+ // Callers pass IDs in different formats — the teams package passes full ARIs
15
+ // (e.g. "ari:cloud:identity::team/uuid") while teams-public passes bare IDs
16
+ // (e.g. "uuid"). Normalize to just the resource ID after the last "/" so both
17
+ // formats produce the same map key.
18
+ var extractId = function extractId(ari) {
19
+ var lastSlash = ari.lastIndexOf('/');
20
+ return lastSlash === -1 ? ari : ari.substring(lastSlash + 1);
21
+ };
22
+ var getKey = function getKey(teamId, containerId) {
23
+ return "".concat(extractId(teamId), ":").concat(extractId(containerId));
24
+ };
25
+ export var spaceInviteScheduler = {
26
+ scheduleInvite: function scheduleInvite(teamId, containerId, callback) {
27
+ var key = getKey(teamId, containerId);
28
+ var existing = pending.get(key);
29
+ if (existing) {
30
+ clearTimeout(existing.timeoutId);
31
+ }
32
+ pending.set(key, {
33
+ timeoutId: setTimeout(function () {
34
+ pending.delete(key);
35
+ callback();
36
+ }, DEBOUNCE_MS)
37
+ });
38
+ },
39
+ cancelInvite: function cancelInvite(teamId, containerId) {
40
+ var key = getKey(teamId, containerId);
41
+ var existing = pending.get(key);
42
+ if (existing) {
43
+ clearTimeout(existing.timeoutId);
44
+ pending.delete(key);
45
+ }
46
+ }
47
+ };
package/dist/esm/index.js CHANGED
@@ -10,4 +10,5 @@ export { hasProductPermission } from './controllers/product-permission/utils';
10
10
  export { getContainerProperties } from './common/utils/get-container-properties';
11
11
  export { ConfluenceIcon, JiraIcon, LoomIcon } from './common/assets';
12
12
  export { Separator } from './common/ui/separator';
13
- export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
13
+ export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
14
+ export { spaceInviteScheduler } from './common/utils/spaceInviteScheduler';
@@ -13,6 +13,7 @@ import { useAnalyticsEvents } from '@atlaskit/teams-app-internal-analytics';
13
13
  import { hasProductPermission, useProductPermissions } from '@atlaskit/teams-app-internal-product-permissions';
14
14
  import { N0, N90 } from '@atlaskit/theme/colors';
15
15
  import { TeamContainersSkeleton } from '../../common/ui/team-containers-skeleton';
16
+ import { spaceInviteScheduler } from '../../common/utils/spaceInviteScheduler';
16
17
  import { hasProductPermission as hasProductPermissionOld } from '../../controllers';
17
18
  import { useCreateContainers } from '../../controllers/hooks/use-create-containers';
18
19
  import { useProductPermissions as useProductPermissionsOld } from '../../controllers/hooks/use-product-permission';
@@ -208,6 +209,9 @@ export var TeamContainers = function TeamContainers(_ref) {
208
209
  if (unlinkError) {
209
210
  fireEvent('track.teamContainerUnlinked.failed', {});
210
211
  } else {
212
+ if (fg('space-team_linking_invites_fg')) {
213
+ spaceInviteScheduler.cancelInvite(teamId, containerId);
214
+ }
211
215
  fireEvent('track.teamContainerUnlinked.succeeded', {
212
216
  containerRemoved: {
213
217
  containerId: removedContainer === null || removedContainer === void 0 ? void 0 : removedContainer.id,
@@ -0,0 +1,13 @@
1
+ /**
2
+ * FE debounce mechanism to avoid spamming invites when users consecutively
3
+ * link and unlink teams from containers in quick succession. We'll log the
4
+ * frequency of this occurring and decide whether to move debouncing to the
5
+ * BE as a productionisation task.
6
+ *
7
+ * Uses module-level state rather than a globalThis singleton to avoid polluting
8
+ * the global scope — a simple in-memory workaround is sufficient here.
9
+ */
10
+ export declare const spaceInviteScheduler: {
11
+ scheduleInvite: (teamId: string, containerId: string, callback: () => void) => void;
12
+ cancelInvite: (teamId: string, containerId: string) => void;
13
+ };
@@ -16,3 +16,4 @@ export { getContainerProperties } from './common/utils/get-container-properties'
16
16
  export { ConfluenceIcon, JiraIcon, LoomIcon } from './common/assets';
17
17
  export { Separator } from './common/ui/separator';
18
18
  export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
19
+ export { spaceInviteScheduler } from './common/utils/spaceInviteScheduler';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * FE debounce mechanism to avoid spamming invites when users consecutively
3
+ * link and unlink teams from containers in quick succession. We'll log the
4
+ * frequency of this occurring and decide whether to move debouncing to the
5
+ * BE as a productionisation task.
6
+ *
7
+ * Uses module-level state rather than a globalThis singleton to avoid polluting
8
+ * the global scope — a simple in-memory workaround is sufficient here.
9
+ */
10
+ export declare const spaceInviteScheduler: {
11
+ scheduleInvite: (teamId: string, containerId: string, callback: () => void) => void;
12
+ cancelInvite: (teamId: string, containerId: string) => void;
13
+ };
@@ -16,3 +16,4 @@ export { getContainerProperties } from './common/utils/get-container-properties'
16
16
  export { ConfluenceIcon, JiraIcon, LoomIcon } from './common/assets';
17
17
  export { Separator } from './common/ui/separator';
18
18
  export { getIsExperimentEnabled } from './common/utils/get-is-experiment-enabled';
19
+ export { spaceInviteScheduler } from './common/utils/spaceInviteScheduler';
package/package.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "dependencies": {
27
27
  "@atlaskit/afm-i18n-platform-people-and-teams-teams-public": "2.39.0",
28
28
  "@atlaskit/analytics-next": "^11.1.0",
29
- "@atlaskit/avatar": "^25.9.0",
29
+ "@atlaskit/avatar": "^25.10.0",
30
30
  "@atlaskit/button": "^23.10.0",
31
31
  "@atlaskit/css": "^0.19.0",
32
32
  "@atlaskit/dropdown-menu": "^16.7.0",
@@ -45,7 +45,7 @@
45
45
  "@atlaskit/teams-client": "^4.30.0",
46
46
  "@atlaskit/theme": "^22.0.0",
47
47
  "@atlaskit/tokens": "^11.1.0",
48
- "@atlaskit/tooltip": "^20.14.0",
48
+ "@atlaskit/tooltip": "^21.0.0",
49
49
  "@babel/runtime": "^7.0.0",
50
50
  "@compiled/react": "^0.20.0",
51
51
  "@types/string-hash": "^1.1.3",
@@ -82,7 +82,6 @@
82
82
  ]
83
83
  },
84
84
  "@repo/internal": {
85
- "dom-events": "use-bind-event-listener",
86
85
  "analytics": [
87
86
  "analytics-next"
88
87
  ],
@@ -108,7 +107,7 @@
108
107
  }
109
108
  },
110
109
  "name": "@atlaskit/teams-public",
111
- "version": "0.70.8",
110
+ "version": "0.71.0",
112
111
  "description": "Public components related to teams",
113
112
  "author": "Atlassian Pty Ltd",
114
113
  "license": "Apache-2.0",
@@ -135,10 +134,10 @@
135
134
  "enable-fix-team-container-height": {
136
135
  "type": "boolean"
137
136
  },
138
- "ptc-fix-containers-after-icon-size": {
137
+ "ptc-missed-analytics-migration-events": {
139
138
  "type": "boolean"
140
139
  },
141
- "ptc-missed-analytics-migration-events": {
140
+ "space-team_linking_invites_fg": {
142
141
  "type": "boolean"
143
142
  }
144
143
  }