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
This commit is contained in:
@@ -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: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
|
||||
+5
-21
@@ -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 (
|
||||
<DropdownContent>
|
||||
{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: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
copyToClipboard(currentUrl, t`Link copied to clipboard`);
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
focused={selectedItemId === 'Copy link to view'}
|
||||
onClick={() => {
|
||||
const currentUrl = window.location.href;
|
||||
navigator.clipboard.writeText(currentUrl);
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Link copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
copyToClipboard(currentUrl, t`Link copied to clipboard`);
|
||||
}}
|
||||
LeftIcon={IconCopy}
|
||||
text={t`Copy link to view`}
|
||||
|
||||
+3
-13
@@ -1,10 +1,8 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { LightIconButton } from 'twenty-ui/input';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledButtonContainer = styled.div`
|
||||
padding: 0 ${({ theme }) => theme.spacing(1)};
|
||||
@@ -15,8 +13,7 @@ export type LightCopyIconButtonProps = {
|
||||
};
|
||||
|
||||
export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => {
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const { t } = useLingui();
|
||||
|
||||
return (
|
||||
@@ -24,14 +21,7 @@ export const LightCopyIconButton = ({ copyText }: LightCopyIconButtonProps) => {
|
||||
<LightIconButton
|
||||
Icon={IconCopy}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Text copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(copyText);
|
||||
copyToClipboard(copyText, t`Text copied to clipboard`);
|
||||
}}
|
||||
aria-label="Copy to Clipboard"
|
||||
/>
|
||||
|
||||
+3
-1
@@ -3,6 +3,7 @@ import { EmailsFieldMenuItem } from '@/object-record/record-field/meta-types/inp
|
||||
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
|
||||
import { emailSchema } from '@/object-record/record-field/validation-schemas/emailSchema';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
@@ -20,6 +21,7 @@ export const EmailsFieldInput = ({
|
||||
}: EmailsFieldInputProps) => {
|
||||
const { persistEmailsField, fieldValue } = useEmailsField();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const { t } = useLingui();
|
||||
|
||||
const emails = useMemo<string[]>(
|
||||
() =>
|
||||
@@ -59,7 +61,7 @@ export const EmailsFieldInput = ({
|
||||
};
|
||||
|
||||
const handleCopy = (email: string) => {
|
||||
copyToClipboard(email);
|
||||
copyToClipboard(email, t`Email copied to clipboard`);
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
+4
-14
@@ -1,9 +1,7 @@
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy, OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
import { OverflowingTextWithTooltip } from 'twenty-ui/display';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
type SettingsAdminConfigCopyableTextProps = {
|
||||
text: string;
|
||||
@@ -32,18 +30,10 @@ export const SettingsAdminConfigCopyableText = ({
|
||||
multiline = false,
|
||||
maxRows,
|
||||
}: SettingsAdminConfigCopyableTextProps) => {
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
const copyToClipboardDebounced = useDebouncedCallback((value: string) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Copied to clipboard!`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
},
|
||||
});
|
||||
copyToClipboard(value);
|
||||
}, 200);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -21,10 +18,8 @@ const StyledLinkContainer = styled.div`
|
||||
type ApiKeyInputProps = { apiKey: string };
|
||||
|
||||
export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => {
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledLinkContainer>
|
||||
@@ -34,14 +29,7 @@ export const ApiKeyInput = ({ apiKey }: ApiKeyInputProps) => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`API Key copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(apiKey);
|
||||
copyToClipboard(apiKey, t`API Key copied to clipboard`);
|
||||
}}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
||||
+6
-10
@@ -1,4 +1,3 @@
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
@@ -7,6 +6,7 @@ import { useState } from 'react';
|
||||
import { IconCopy, IconDatabase, IconSitemap } from 'twenty-ui/display';
|
||||
import { Button, CodeEditor } from 'twenty-ui/input';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.background.secondary};
|
||||
@@ -57,8 +57,8 @@ const StyledEditorContainer = styled.div`
|
||||
|
||||
export const SettingsIntegrationMCP = () => {
|
||||
const theme = useTheme();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { t } = useLingui();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
const generateMcpContent = (pathSuffix: string, serverName: string) => {
|
||||
return JSON.stringify(
|
||||
@@ -125,14 +125,10 @@ export const SettingsIntegrationMCP = () => {
|
||||
<Button
|
||||
Icon={IconCopy}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`MCP Configuration copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(selectedOption.content);
|
||||
copyToClipboard(
|
||||
selectedOption.content,
|
||||
t`MCP Configuration copied to clipboard`,
|
||||
);
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
|
||||
+10
-20
@@ -1,8 +1,6 @@
|
||||
/* @license Enterprise */
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
@@ -10,6 +8,7 @@ import { H2Title, IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledInputsContainer = styled.div`
|
||||
display: flex;
|
||||
@@ -36,8 +35,7 @@ const StyledButtonCopy = styled.div`
|
||||
|
||||
export const SettingsSSOOIDCForm = () => {
|
||||
const { control } = useFormContext();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const { t } = useLingui();
|
||||
|
||||
const authorizedUrl = window.location.origin;
|
||||
@@ -66,14 +64,10 @@ export const SettingsSSOOIDCForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Authorized URL copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(authorizedUrl);
|
||||
copyToClipboard(
|
||||
authorizedUrl,
|
||||
t`Authorized URL copied to clipboard`,
|
||||
);
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
@@ -94,14 +88,10 @@ export const SettingsSSOOIDCForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Redirect Url copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(redirectionUrl);
|
||||
copyToClipboard(
|
||||
redirectionUrl,
|
||||
t`Redirect Url copied to clipboard`,
|
||||
);
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
|
||||
+6
-18
@@ -20,6 +20,7 @@ import {
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledUploadFileContainer = styled.div`
|
||||
align-items: center;
|
||||
@@ -55,10 +56,11 @@ const StyledButtonCopy = styled.div`
|
||||
`;
|
||||
|
||||
export const SettingsSSOSAMLForm = () => {
|
||||
const { enqueueErrorSnackBar, enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { setValue, getValues, watch, trigger } = useFormContext();
|
||||
const { t } = useLingui();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
const handleFileChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
||||
if (isDefined(e.target.files)) {
|
||||
@@ -179,16 +181,9 @@ export const SettingsSSOSAMLForm = () => {
|
||||
<StyledButtonCopy>
|
||||
<Button
|
||||
Icon={IconCopy}
|
||||
title="Copy"
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`ACS Url copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(acsUrl);
|
||||
copyToClipboard(acsUrl, t`ACS Url copied to clipboard`);
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
@@ -209,14 +204,7 @@ export const SettingsSSOSAMLForm = () => {
|
||||
Icon={IconCopy}
|
||||
title={t`Copy`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Entity ID copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(entityID);
|
||||
copyToClipboard(entityID, t`Entity ID copied to clipboard`);
|
||||
}}
|
||||
type="button"
|
||||
/>
|
||||
|
||||
+6
-15
@@ -1,7 +1,6 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { FormRawJsonFieldInput } from '@/object-record/record-field/form-types/components/FormRawJsonFieldInput';
|
||||
import { getFunctionOutputSchema } from '@/serverless-functions/utils/getFunctionOutputSchema';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
|
||||
@@ -18,7 +17,6 @@ import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon
|
||||
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
|
||||
import { getWebhookTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getWebhookTriggerDefaultSettings';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
@@ -26,6 +24,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconCopy, useIcons } from 'twenty-ui/display';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { REACT_APP_SERVER_BASE_URL } from '~/config';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
type WorkflowEditTriggerWebhookFormProps = {
|
||||
trigger: WorkflowWebhookTrigger;
|
||||
@@ -51,9 +50,8 @@ export const WorkflowEditTriggerWebhookForm = ({
|
||||
trigger,
|
||||
triggerOptions,
|
||||
}: WorkflowEditTriggerWebhookFormProps) => {
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const theme = useTheme();
|
||||
const { t } = useLingui();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const [errorMessages, setErrorMessages] = useState<FormErrorMessages>({});
|
||||
const [errorMessagesVisible, setErrorMessagesVisible] = useState(false);
|
||||
const { getIcon } = useIcons();
|
||||
@@ -74,17 +72,10 @@ export const WorkflowEditTriggerWebhookForm = ({
|
||||
const webhookUrl = `${REACT_APP_SERVER_BASE_URL}/webhooks/workflows/${currentWorkspace?.id}/${workflowVisualizerWorkflowId}`;
|
||||
const displayWebhookUrl = webhookUrl.replace(/^(https?:\/\/)?(www\.)?/, '');
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
await navigator.clipboard.writeText(webhookUrl);
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Copied to clipboard!`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const copyToClipboardDebounced = useDebouncedCallback(copyToClipboard, 200);
|
||||
const copyToClipboardDebounced = useDebouncedCallback(
|
||||
() => copyToClipboard(webhookUrl),
|
||||
200,
|
||||
);
|
||||
|
||||
if (!isDefined(currentWorkspace)) {
|
||||
return <></>;
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy, IconLink } from 'twenty-ui/display';
|
||||
import { IconLink } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
@@ -26,9 +25,8 @@ export const WorkspaceInviteLink = ({
|
||||
inviteLink,
|
||||
}: WorkspaceInviteLinkProps) => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
return (
|
||||
<StyledContainer data-chromatic="ignore">
|
||||
@@ -46,14 +44,7 @@ export const WorkspaceInviteLink = ({
|
||||
accent="blue"
|
||||
title={t`Copy link`}
|
||||
onClick={() => {
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Link copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
navigator.clipboard.writeText(inviteLink);
|
||||
copyToClipboard(inviteLink, t`Link copied to clipboard`);
|
||||
}}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
||||
@@ -8,7 +8,6 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
|
||||
import { Modal } from '@/ui/layout/modal/components/Modal';
|
||||
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
@@ -26,6 +25,7 @@ import { IconCopy, SeparatorLineText } from 'twenty-ui/display';
|
||||
import { LightButton, MainButton } from 'twenty-ui/input';
|
||||
import { ClickToActionLink } from 'twenty-ui/navigation';
|
||||
import { z } from 'zod';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
import { useCreateWorkspaceInvitation } from '../../modules/workspace-invitation/hooks/useCreateWorkspaceInvitation';
|
||||
|
||||
const StyledAnimatedContainer = styled.div`
|
||||
@@ -63,7 +63,7 @@ type FormInput = z.infer<typeof validationSchema>;
|
||||
|
||||
export const InviteTeam = () => {
|
||||
const { t } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { sendInvitation } = useCreateWorkspaceInvitation();
|
||||
const setNextOnboardingStatus = useSetNextOnboardingStatus();
|
||||
@@ -118,14 +118,7 @@ export const InviteTeam = () => {
|
||||
const copyInviteLink = () => {
|
||||
if (isDefined(currentWorkspace?.inviteHash)) {
|
||||
const inviteLink = `${window.location.origin}/invite/${currentWorkspace?.inviteHash}`;
|
||||
navigator.clipboard.writeText(inviteLink);
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Link copied to clipboard`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
duration: 2000,
|
||||
},
|
||||
});
|
||||
copyToClipboard(inviteLink, t`Link copied to clipboard`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import { TableBody } from '@/ui/layout/table/components/TableBody';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { IconCopy } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { CustomDomainValidRecords } from '~/generated/graphql';
|
||||
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@@ -45,23 +42,12 @@ export const SettingsCustomDomainRecords = ({
|
||||
}: {
|
||||
records: CustomDomainValidRecords['records'];
|
||||
}) => {
|
||||
const { enqueueSuccessSnackBar } = useSnackBar();
|
||||
const { copyToClipboard } = useCopyToClipboard();
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
const copyToClipboard = (value: string) => {
|
||||
navigator.clipboard.writeText(value);
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Copied to clipboard!`,
|
||||
options: {
|
||||
icon: <IconCopy size={theme.icon.size.md} />,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const copyToClipboardDebounced = useDebouncedCallback(copyToClipboard, 200);
|
||||
const copyToClipboardDebounced = useDebouncedCallback(
|
||||
(value: string) => copyToClipboard(value),
|
||||
200,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledTable>
|
||||
|
||||
Reference in New Issue
Block a user