diff --git a/packages/twenty-front/src/hooks/useCopyToClipboard.tsx b/packages/twenty-front/src/hooks/useCopyToClipboard.tsx index 182fd0ce18..22ee1cbec9 100644 --- a/packages/twenty-front/src/hooks/useCopyToClipboard.tsx +++ b/packages/twenty-front/src/hooks/useCopyToClipboard.tsx @@ -9,6 +9,18 @@ export const useCopyToClipboard = () => { const { t } = useLingui(); const copyToClipboard = async (valueAsString: string, message?: string) => { + if (!window.isSecureContext) { + enqueueErrorSnackBar({ + message: t`Clipboard requires a secure connection (HTTPS). Please access this app over HTTPS to enable copying.`, + options: { + icon: , + duration: 6000, + }, + }); + + return; + } + try { await navigator.clipboard.writeText(valueAsString); diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx index 487dd69854..532c83ebf5 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx +++ b/packages/twenty-front/src/modules/auth/sign-in-up/components/internal/SignInUpTwoFactorAuthenticationProvision.tsx @@ -5,7 +5,6 @@ import { signInUpStepState, } from '@/auth/states/signInUpStepState'; import { extractSecretFromOtpUri } from '@/settings/two-factor-authentication/utils/extractSecretFromOtpUri'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Trans, useLingui } from '@lingui/react/macro'; @@ -15,6 +14,7 @@ import { IconCopy } from 'twenty-ui/display'; import { Loader } from 'twenty-ui/feedback'; import { MainButton } from 'twenty-ui/input'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; const StyledMainContentContainer = styled.div` margin-bottom: ${({ theme }) => theme.spacing(8)}; @@ -69,7 +69,7 @@ const StyledCopySetupKeyLink = styled.button` export const SignInUpTwoFactorAuthenticationProvision = () => { const { t } = useLingui(); const theme = useTheme(); - const { enqueueSuccessSnackBar } = useSnackBar(); + const { copyToClipboard } = useCopyToClipboard(); const qrCode = useRecoilValueV2(qrCodeState); const setSignInUpStep = useSetRecoilState(signInUpStepState); @@ -82,14 +82,7 @@ export const SignInUpTwoFactorAuthenticationProvision = () => { const secret = extractSecretFromOtpUri(qrCode); if (secret !== null) { - await navigator.clipboard.writeText(secret); - enqueueSuccessSnackBar({ - message: t`Setup key copied to clipboard`, - options: { - icon: , - duration: 2000, - }, - }); + await copyToClipboard(secret, t`Setup key copied to clipboard`); } }; diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx index 1a8404d0f0..4bea9240ce 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownVisibilityContent.tsx @@ -1,7 +1,6 @@ import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId'; import { useObjectOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsDropdown'; import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; @@ -15,7 +14,6 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { useCanPersistViewChanges } from '@/views/hooks/useCanPersistViewChanges'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; -import { useTheme } from '@emotion/react'; import { useLingui } from '@lingui/react/macro'; import { createPortal } from 'react-dom'; import { @@ -30,14 +28,14 @@ import { ViewVisibility, PermissionFlagType, } from '~/generated-metadata/graphql'; +import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; export const ObjectOptionsDropdownVisibilityContent = () => { const { t } = useLingui(); - const theme = useTheme(); const { resetContent } = useObjectOptionsDropdown(); const { currentView } = useGetCurrentViewOnly(); const { updateCurrentView } = useUpdateCurrentView(); - const { enqueueSuccessSnackBar } = useSnackBar(); + const { copyToClipboard } = useCopyToClipboard(); const hasViewsPermission = useHasPermissionFlag(PermissionFlagType.VIEWS); const { canPersistChanges } = useCanPersistViewChanges(); @@ -59,16 +57,9 @@ export const ObjectOptionsDropdownVisibilityContent = () => { resetContent(); }; - const handleCopyLink = () => { + const handleCopyLink = async () => { const currentUrl = window.location.href; - navigator.clipboard.writeText(currentUrl); - enqueueSuccessSnackBar({ - message: t`Link copied to clipboard`, - options: { - icon: , - duration: 2000, - }, - }); + await copyToClipboard(currentUrl, t`Link copied to clipboard`); }; const currentVisibility = currentView?.visibility ?? ViewVisibility.WORKSPACE; diff --git a/packages/twenty-front/src/pages/settings/SettingsTwoFactorAuthenticationMethod.tsx b/packages/twenty-front/src/pages/settings/SettingsTwoFactorAuthenticationMethod.tsx index 10009f69d4..1f34939127 100644 --- a/packages/twenty-front/src/pages/settings/SettingsTwoFactorAuthenticationMethod.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsTwoFactorAuthenticationMethod.tsx @@ -12,15 +12,14 @@ import { TwoFactorAuthenticationVerificationForSettings } from '@/settings/two-f import { useCurrentUserWorkspaceTwoFactorAuthentication } from '@/settings/two-factor-authentication/hooks/useCurrentUserWorkspaceTwoFactorAuthentication'; import { useTwoFactorVerificationForSettings } from '@/settings/two-factor-authentication/hooks/useTwoFactorVerificationForSettings'; import { extractSecretFromOtpUri } from '@/settings/two-factor-authentication/utils/extractSecretFromOtpUri'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; -import { useTheme } from '@emotion/react'; import { SettingsPath } from 'twenty-shared/types'; import { getSettingsPath } from 'twenty-shared/utils'; -import { H2Title, IconCopy } from 'twenty-ui/display'; +import { H2Title } from 'twenty-ui/display'; import { Loader } from 'twenty-ui/feedback'; import { Section } from 'twenty-ui/layout'; import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2'; +import { useCopyToClipboard } from '~/hooks/useCopyToClipboard'; const StyledQRCodeContainer = styled.div` margin: ${({ theme }) => theme.spacing(4)} 0; @@ -79,8 +78,7 @@ const StyledDivider = styled.div` export const SettingsTwoFactorAuthenticationMethod = () => { const { t } = useLingui(); - const theme = useTheme(); - const { enqueueSuccessSnackBar } = useSnackBar(); + const { copyToClipboard } = useCopyToClipboard(); const qrCode = useRecoilValueV2(qrCodeState); const { currentUserWorkspaceTwoFactorAuthenticationMethods } = @@ -99,14 +97,7 @@ export const SettingsTwoFactorAuthenticationMethod = () => { const secret = extractSecretFromOtpUri(qrCode); if (secret !== null) { - await navigator.clipboard.writeText(secret); - enqueueSuccessSnackBar({ - message: t`Setup key copied to clipboard`, - options: { - icon: , - duration: 2000, - }, - }); + await copyToClipboard(secret, t`Setup key copied to clipboard`); } };