From 316f2ec38c2a85a372adbb3721b89fdb9cb49e4b Mon Sep 17 00:00:00 2001
From: Nabhag Motivaras <65061890+Nabhag8848@users.noreply.github.com>
Date: Wed, 30 Jul 2025 14:43:55 +0530
Subject: [PATCH] refactor: to useCopyToClipboard to catch errors - when user
has disable copy clipboard permission in browser (#13330)
as discussed here
https://github.com/twentyhq/twenty/issues/13292#issuecomment-3092215050
with @prastoin
- [x] introduce optional message param to `copyToClipboard` method in
`useCopyToClipboard` and refactor it across app, as in case if user has
disallowed clipboard permission in BROWSER unprotected clipboard access
breaks without catch.
- [x] Email copied to clipboard
- [x] run lingui extract
---
.../src/hooks/useCopyToClipboard.tsx | 4 +--
.../ObjectOptionsDropdownMenuContent.tsx | 26 ++++------------
.../components/LightCopyIconButton.tsx | 16 ++--------
.../input/components/EmailsFieldInput.tsx | 4 ++-
.../SettingsAdminConfigCopyableText.tsx | 18 +++--------
.../developers/components/ApiKeyInput.tsx | 18 ++---------
.../components/SettingsIntegrationMCP.tsx | 16 ++++------
.../components/SSO/SettingsSSOOIDCForm.tsx | 30 +++++++------------
.../components/SSO/SettingsSSOSAMLForm.tsx | 24 ++++-----------
.../WorkflowEditTriggerWebhookForm.tsx | 21 ++++---------
.../components/WorkspaceInviteLink.tsx | 17 +++--------
.../src/pages/onboarding/InviteTeam.tsx | 13 ++------
.../workspace/SettingsCustomDomainRecords.tsx | 26 ++++------------
13 files changed, 61 insertions(+), 172 deletions(-)
diff --git a/packages/twenty-front/src/hooks/useCopyToClipboard.tsx b/packages/twenty-front/src/hooks/useCopyToClipboard.tsx
index 588590444db..182fd0ce184 100644
--- a/packages/twenty-front/src/hooks/useCopyToClipboard.tsx
+++ b/packages/twenty-front/src/hooks/useCopyToClipboard.tsx
@@ -8,12 +8,12 @@ export const useCopyToClipboard = () => {
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
const { t } = useLingui();
- const copyToClipboard = async (valueAsString: string) => {
+ const copyToClipboard = async (valueAsString: string, message?: string) => {
try {
await navigator.clipboard.writeText(valueAsString);
enqueueSuccessSnackBar({
- message: t`Copied to clipboard`,
+ message: message || t`Copied to clipboard`,
options: {
icon: ,
duration: 2000,
diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx
index ca07c40e21c..31253b4b29c 100644
--- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx
+++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownMenuContent.tsx
@@ -3,7 +3,6 @@ import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropd
import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown';
import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
-import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
@@ -16,7 +15,6 @@ import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
import { ViewType, viewTypeIconMapping } from '@/views/types/ViewType';
import { useDeleteViewFromCurrentState } from '@/views/view-picker/hooks/useDeleteViewFromCurrentState';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
-import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { capitalize, isDefined } from 'twenty-shared/utils';
import {
@@ -27,6 +25,7 @@ import {
IconTrash,
} from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
+import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
export const ObjectOptionsDropdownMenuContent = () => {
const { t } = useLingui();
@@ -62,9 +61,6 @@ export const ObjectOptionsDropdownMenuContent = () => {
closeDropdown();
};
- const theme = useTheme();
- const { enqueueSuccessSnackBar } = useSnackBar();
-
const isDefaultView = currentView?.key === 'INDEX';
const selectableItemIdArray = [
@@ -80,6 +76,8 @@ export const ObjectOptionsDropdownMenuContent = () => {
OBJECT_OPTIONS_DROPDOWN_ID,
);
+ const { copyToClipboard } = useCopyToClipboard();
+
return (
{currentView && (
@@ -167,28 +165,14 @@ export const ObjectOptionsDropdownMenuContent = () => {
itemId="Copy link to view"
onEnter={() => {
const currentUrl = window.location.href;
- navigator.clipboard.writeText(currentUrl);
- enqueueSuccessSnackBar({
- message: t`Link copied to clipboard`,
- options: {
- icon: ,
- duration: 2000,
- },
- });
+ copyToClipboard(currentUrl, t`Link copied to clipboard`);
}}
>