fix(blacklist): merge back CopyButton changes, disable button when there's nothing to copy

This commit is contained in:
Ben Beauchamp
2025-02-06 20:01:18 -06:00
parent bc733ddf0e
commit 618343d4d2
5 changed files with 72 additions and 76 deletions

View File

@@ -1,12 +1,10 @@
import Modal from '@app/components/Common/Modal';
import Tooltip from '@app/components/Common/Tooltip';
import CopyButton from '@app/components/Settings/CopyButton';
import { encodeURIExtraParams } from '@app/hooks/useDiscover';
import defineMessages from '@app/utils/defineMessages';
import { Transition } from '@headlessui/react';
import {
ArrowDownIcon,
ClipboardDocumentIcon,
} from '@heroicons/react/24/solid';
import { ArrowDownIcon } from '@heroicons/react/24/solid';
import type { TmdbKeywordSearchResponse } from '@server/api/themoviedb/interfaces';
import type { Keyword } from '@server/models/common';
import { useFormikContext } from 'formik';
@@ -22,12 +20,11 @@ import { useIntl } from 'react-intl';
import type { ClearIndicatorProps, GroupBase, MultiValue } from 'react-select';
import { components } from 'react-select';
import AsyncSelect from 'react-select/async';
import { useToasts } from 'react-toast-notifications';
import useClipboard from 'react-use-clipboard';
const messages = defineMessages('components.Settings', {
copyBlacklistedTags: 'Copied blacklisted tags to clipboard.',
copyBlacklistedTagsTip: 'Copy blacklisted tags configuration',
copyBlacklistedTagsTip: 'Copy blacklisted tag configuration',
copyBlacklistedTagsEmpty: 'Nothing to copy',
importBlacklistedTagsTip: 'Import blacklisted tag configuration',
clearBlacklistedTagsConfirm:
'Are you sure you want to clear the blacklisted tags?',
@@ -58,6 +55,7 @@ const BlacklistedTagsSelector = ({
}: BlacklistedTagsSelectorProps) => {
const { setFieldValue } = useFormikContext();
const [value, setValue] = useState<string | undefined>(defaultValue);
const intl = useIntl();
const [selectorValue, setSelectorValue] =
useState<MultiValue<SingleVal> | null>(null);
@@ -71,6 +69,8 @@ const BlacklistedTagsSelector = ({
[setSelectorValue, setValue, setFieldValue]
);
const copyDisabled = value === null || value?.length === 0;
return (
<>
<ControlledKeywordSelector
@@ -84,7 +84,17 @@ const BlacklistedTagsSelector = ({
}}
/>
<BlacklistedTagsCopyButton value={value ?? ''} />
<CopyButton
textToCopy={value ?? ''}
disabled={copyDisabled}
toastMessage={intl.formatMessage(messages.copyBlacklistedTags)}
tooltipContent={intl.formatMessage(
copyDisabled
? messages.copyBlacklistedTagsEmpty
: messages.copyBlacklistedTagsTip
)}
tooltipConfig={{ followCursor: false }}
/>
<BlacklistedTagsImportButton setSelector={update} />
</>
);
@@ -170,54 +180,13 @@ const ControlledKeywordSelector = ({
);
};
type BlacklistedTagsCopyButtonProps = {
value: string;
};
const BlacklistedTagsCopyButton = ({
value,
}: BlacklistedTagsCopyButtonProps) => {
const intl = useIntl();
const [isCopied, setCopied] = useClipboard(value, {
successDuration: 1000,
});
const { addToast } = useToasts();
useEffect(() => {
if (isCopied) {
addToast(intl.formatMessage(messages.copyBlacklistedTags), {
appearance: 'info',
autoDismiss: true,
});
}
}, [isCopied, addToast, intl]);
return (
<Tooltip
content={intl.formatMessage(messages.copyBlacklistedTagsTip)}
tooltipConfig={{ followCursor: false }}
>
<button
onClick={(e) => {
e.preventDefault();
setCopied();
}}
className="input-action"
type="button"
>
<ClipboardDocumentIcon />
</button>
</Tooltip>
);
};
type BlacklistedTagsImportButton = {
type BlacklistedTagsImportButtonProps = {
setSelector: (value: MultiValue<SingleVal>) => void;
};
const BlacklistedTagsImportButton = ({
setSelector,
}: BlacklistedTagsImportButton) => {
}: BlacklistedTagsImportButtonProps) => {
const [show, setShow] = useState(false);
const formRef = useRef<HTMLFormElement>(null);
const intl = useIntl();
@@ -269,7 +238,7 @@ const BlacklistedTagsImportButton = ({
);
};
type BlacklistedTagImportFormProps = BlacklistedTagsImportButton;
type BlacklistedTagImportFormProps = BlacklistedTagsImportButtonProps;
const BlacklistedTagImportForm = forwardRef<
Partial<HTMLFormElement>,

View File

@@ -1,41 +1,54 @@
import defineMessages from '@app/utils/defineMessages';
import Tooltip from '@app/components/Common/Tooltip';
import { ClipboardDocumentIcon } from '@heroicons/react/24/solid';
import { useEffect } from 'react';
import { useIntl } from 'react-intl';
import React, { useEffect } from 'react';
import type { Config } from 'react-popper-tooltip';
import { useToasts } from 'react-toast-notifications';
import useClipboard from 'react-use-clipboard';
const messages = defineMessages('components.Settings', {
copied: 'Copied API key to clipboard.',
});
type CopyButtonProps = {
textToCopy: string;
disabled?: boolean;
toastMessage?: string;
const CopyButton = ({ textToCopy }: { textToCopy: string }) => {
const intl = useIntl();
tooltipContent?: React.ReactNode;
tooltipConfig?: Partial<Config>;
};
const CopyButton = ({
textToCopy,
disabled,
toastMessage,
tooltipContent,
tooltipConfig,
}: CopyButtonProps) => {
const [isCopied, setCopied] = useClipboard(textToCopy, {
successDuration: 1000,
});
const { addToast } = useToasts();
useEffect(() => {
if (isCopied) {
addToast(intl.formatMessage(messages.copied), {
if (isCopied && toastMessage) {
addToast(toastMessage, {
appearance: 'info',
autoDismiss: true,
});
}
}, [isCopied, addToast, intl]);
}, [isCopied, addToast, toastMessage]);
return (
<button
onClick={(e) => {
e.preventDefault();
setCopied();
}}
className="input-action"
type="button"
>
<ClipboardDocumentIcon />
</button>
<Tooltip content={tooltipContent} tooltipConfig={tooltipConfig}>
<button
onClick={(e) => {
e.preventDefault();
setCopied();
}}
className="input-action"
type="button"
disabled={disabled}
>
<ClipboardDocumentIcon />
</button>
</Tooltip>
);
};

View File

@@ -29,6 +29,7 @@ const messages = defineMessages('components.Settings.SettingsMain', {
generalsettingsDescription:
'Configure global and default settings for Jellyseerr.',
apikey: 'API Key',
apikeyCopied: 'Copied API key to clipboard.',
applicationTitle: 'Application Title',
applicationurl: 'Application URL',
discoverRegion: 'Discover Region',
@@ -232,6 +233,9 @@ const SettingsMain = () => {
/>
<CopyButton
textToCopy={data?.apiKey ?? ''}
toastMessage={intl.formatMessage(
messages.apikeyCopied
)}
key={data?.apiKey}
/>
<button

View File

@@ -917,6 +917,7 @@
"components.Settings.SettingsLogs.time": "Timestamp",
"components.Settings.SettingsLogs.viewdetails": "View Details",
"components.Settings.SettingsMain.apikey": "API Key",
"components.Settings.SettingsMain.apikeyCopied": "Copied API key to clipboard.",
"components.Settings.SettingsMain.applicationTitle": "Application Title",
"components.Settings.SettingsMain.applicationurl": "Application URL",
"components.Settings.SettingsMain.blacklistedTags": "Blacklist Content with Tags",
@@ -1055,11 +1056,12 @@
"components.Settings.apiKey": "API key",
"components.Settings.blacklistedTagImportInstructions": "Paste blacklist tag configuration below.",
"components.Settings.blacklistedTagImportTitle": "Import Blacklisted Tag Configuration",
"components.Settings.blacklistedTagsText": "Blacklisted Tags",
"components.Settings.cancelscan": "Cancel Scan",
"components.Settings.clearBlacklistedTagsConfirm": "Are you sure you want to clear the blacklisted tags?",
"components.Settings.copied": "Copied API key to clipboard.",
"components.Settings.copyBlacklistedTags": "Copied blacklisted tags to clipboard.",
"components.Settings.copyBlacklistedTagsTip": "Copy blacklisted tags configuration",
"components.Settings.copyBlacklistedTagsEmpty": "Nothing to copy",
"components.Settings.copyBlacklistedTagsTip": "Copy blacklisted tag configuration",
"components.Settings.currentlibrary": "Current Library: {name}",
"components.Settings.default": "Default",
"components.Settings.default4k": "Default 4K",

View File

@@ -342,7 +342,15 @@
}
button.input-action {
@apply relative -ml-px inline-flex items-center border border-gray-500 bg-indigo-600 bg-opacity-80 px-3 py-2 text-sm font-medium leading-5 text-white transition duration-150 ease-in-out last:rounded-r-md hover:bg-opacity-100 active:bg-gray-100 active:text-gray-700 sm:px-3.5;
@apply relative -ml-px inline-flex items-center border border-gray-500 bg-indigo-600 bg-opacity-80 px-3 py-2 text-sm font-medium leading-5 text-white last:rounded-r-md sm:px-3.5;
}
button.input-action[disabled] {
filter: grayscale(100%);
}
button.input-action:not([disabled]) {
@apply transition duration-150 ease-in-out hover:bg-opacity-100 active:bg-gray-100 active:text-gray-700;
}
.button-md :where(svg),