From e9e7363bfdf0f96f76b09c325c157a3a5839f5fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sat, 21 Feb 2026 22:24:06 +0100 Subject: [PATCH] nicolium: full? antenna management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-fe/src/locales/en.json | 25 ++ .../pl-fe/src/modals/antenna-editor-modal.tsx | 382 +++++++++++++++++- 2 files changed, 386 insertions(+), 21 deletions(-) diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 25c5354bd..3d9064edc 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -268,6 +268,11 @@ "antennas.create.save": "Create antenna", "antennas.create.success": "Antenna created successfully", "antennas.delete": "Delete antenna", + "antennas.domain.add": "Add domain", + "antennas.domain.excluded.add": "Add excluded domain", + "antennas.domain.remove": "Remove domain", + "antennas.domains": "Domains", + "antennas.domains.excluded": "Excluded domains", "antennas.edit": "Edit antenna", "antennas.edit.destination": "Destination", "antennas.edit.destination.antenna": "Antenna timeline only", @@ -287,10 +292,23 @@ "antennas.edit.title": "Antenna title", "antennas.edit.with_media_only": "Media only", "antennas.edit.with_media_only.hint": "Only include posts with media attachments", + "antennas.keyword.add": "Add keyword", + "antennas.keyword.excluded.add": "Add excluded keyword", + "antennas.keyword.remove": "Remove keyword", + "antennas.keywords": "Keywords", + "antennas.keywords.excluded": "Excluded keywords", "antennas.manage_accounts": "Manage antenna accounts", + "antennas.manage_domains": "Manage domains", "antennas.manage_excluded_accounts": "Manage excluded accounts", + "antennas.manage_keywords": "Manage keywords", + "antennas.manage_tags": "Manage tags", "antennas.new.create": "Add antenna", "antennas.subheading": "Your antennas", + "antennas.tag.add": "Add tag", + "antennas.tag.excluded.add": "Add excluded tag", + "antennas.tag.remove": "Remove tag", + "antennas.tags": "Tags", + "antennas.tags.excluded": "Excluded tags", "app_create.name_label": "App name", "app_create.name_placeholder": "e.g. 'pl-fe'", "app_create.redirect_uri_label": "Redirect URIs", @@ -538,6 +556,7 @@ "column.wrenched": "Recent wrenches timeline", "column_forbidden.body": "You do not have permission to access this page.", "column_forbidden.title": "Forbidden", + "common.add": "Add", "common.cancel": "Cancel", "compare_history_modal.header": "Edit history", "compose.character_counter.title": "Used {chars} out of {maxChars} {maxChars, plural, one {character} other {characters}}", @@ -920,7 +939,13 @@ "empty_column.aliases.suggestions": "There are no account suggestions available for the provided term.", "empty_column.antenna": "There is nothing in this antenna yet. When posts matching the criteria will be created, they will appear here.", "empty_column.antenna_accounts": "There are no accounts in this antenna. Use search to find users to add.", + "empty_column.antenna_domains": "There are no domains in this antenna. Add one below.", "empty_column.antenna_excluded_accounts": "There are no excluded accounts in this antenna. Use search to find users to exclude.", + "empty_column.antenna_excluded_domains": "There are no excluded domains in this antenna. Add one below.", + "empty_column.antenna_excluded_keywords": "There are no excluded keywords in this antenna. Add one below.", + "empty_column.antenna_excluded_tags": "There are no excluded tags in this antenna. Add one below.", + "empty_column.antenna_keywords": "There are no keywords in this antenna. Add one below.", + "empty_column.antenna_tags": "There are no tags in this antenna. Add one below.", "empty_column.antennas": "You don't have any antennas yet. When you create one, it will show up here.", "empty_column.blocks": "You haven't blocked any users yet.", "empty_column.bookmarks": "You don't have any bookmarks yet. When you add one, it will show up here.", diff --git a/packages/pl-fe/src/modals/antenna-editor-modal.tsx b/packages/pl-fe/src/modals/antenna-editor-modal.tsx index 910d200d5..ecc827047 100644 --- a/packages/pl-fe/src/modals/antenna-editor-modal.tsx +++ b/packages/pl-fe/src/modals/antenna-editor-modal.tsx @@ -7,6 +7,8 @@ import { CardHeader, CardTitle } from '@/components/ui/card'; import Form from '@/components/ui/form'; import FormActions from '@/components/ui/form-actions'; import FormGroup from '@/components/ui/form-group'; +import HStack from '@/components/ui/hstack'; +import IconButton from '@/components/ui/icon-button'; import Input from '@/components/ui/input'; import Modal from '@/components/ui/modal'; import Spinner from '@/components/ui/spinner'; @@ -16,13 +18,28 @@ import Toggle from '@/components/ui/toggle'; import { SelectDropdown } from '@/features/forms'; import { useAddAccountsToAntenna, + useAddDomainsToAntenna, useAddExcludedAccountsToAntenna, + useAddExcludedDomainsToAntenna, + useAddExcludedKeywordsToAntenna, + useAddExcludedTagsToAntenna, + useAddKeywordsToAntenna, + useAddTagsToAntenna, useAntenna, useAntennaAccounts, + useAntennaDomains, useAntennaExcludedAccounts, + useAntennaKeywords, + useAntennaTags, useCreateAntenna, + useRemoveDomainsFromAntenna, useRemoveAccountsFromAntenna, + useRemoveExcludedDomainsFromAntenna, useRemoveExcludedAccountsFromAntenna, + useRemoveExcludedKeywordsFromAntenna, + useRemoveExcludedTagsFromAntenna, + useRemoveKeywordsFromAntenna, + useRemoveTagsFromAntenna, useUpdateAntenna, } from '@/queries/accounts/use-antennas'; import { useAccountSearch } from '@/queries/search/use-search-accounts'; @@ -34,33 +51,24 @@ import Search from './list-editor-modal/components/search'; import type { BaseModalProps } from '@/features/ui/components/modal-root'; -type Tab = 'info' | 'accounts' | 'excludedAccounts'; - const messages = defineMessages({ createSuccess: { id: 'antennas.create.success', defaultMessage: 'Antenna created successfully' }, editSuccess: { id: 'antennas.edit.success', defaultMessage: 'Antenna updated successfully' }, createError: { id: 'antennas.create.error', defaultMessage: 'Error creating antenna' }, editError: { id: 'antennas.edit.error', defaultMessage: 'Error updating antenna' }, - addToAntenna: { id: 'antennas.account.add', defaultMessage: 'Add to antenna' }, - removeFromAntenna: { id: 'antennas.account.remove', defaultMessage: 'Remove from antenna' }, - addExcludedToAntenna: { - id: 'antennas.account.excluded.add', - defaultMessage: 'Add to excluded accounts', - }, - removeExcludedFromAntenna: { - id: 'antennas.account.excluded.remove', - defaultMessage: 'Remove from excluded accounts', - }, + removeDomain: { id: 'antennas.domain.remove', defaultMessage: 'Remove domain' }, + removeKeyword: { id: 'antennas.keyword.remove', defaultMessage: 'Remove keyword' }, + removeTag: { id: 'antennas.tag.remove', defaultMessage: 'Remove tag' }, }); +type Tab = 'info' | 'accounts' | 'excludedAccounts' | 'domains' | 'keywords' | 'tags'; + interface IAntennaAccountsForm { antennaId: string; excluded?: boolean; } const AntennaAccountsForm: React.FC = ({ antennaId, excluded = false }) => { - const intl = useIntl(); - const [searchValue, setSearchValue] = useState(''); const { data: accountIds = [] } = useAntennaAccounts(antennaId); @@ -99,9 +107,19 @@ const AntennaAccountsForm: React.FC = ({ antennaId, exclud
+ ) : ( + + ) + } />
@@ -135,9 +153,16 @@ const AntennaAccountsForm: React.FC = ({ antennaId, exclud
+ ) : ( + + ) + } /> @@ -157,6 +182,293 @@ const AntennaAccountsForm: React.FC = ({ antennaId, exclud ); }; +interface IAntennaValuesForm { + values: Array; + excludedValues: Array; + addTitle: React.ReactNode; + listTitle: React.ReactNode; + addExcludedTitle: React.ReactNode; + listExcludedTitle: React.ReactNode; + emptyValues: React.ReactNode; + emptyExcludedValues: React.ReactNode; + removeTitle?: string; + onAdd: (value: string) => void; + onRemove: (value: string) => void; + onAddExcluded: (value: string) => void; + onRemoveExcluded: (value: string) => void; +} + +const AntennaValuesForm: React.FC = ({ + values, + excludedValues, + addTitle, + listTitle, + addExcludedTitle, + listExcludedTitle, + emptyValues, + emptyExcludedValues, + removeTitle, + onAdd, + onRemove, + onAddExcluded, + onRemoveExcluded, +}) => { + const [value, setValue] = useState(''); + const [excludedValue, setExcludedValue] = useState(''); + + const handleAdd = () => { + const trimmed = value.trim(); + if (!trimmed) { + return; + } + + onAdd(trimmed); + setValue(''); + }; + + const handleAddExcluded = () => { + const trimmed = excludedValue.trim(); + if (!trimmed) { + return; + } + + onAddExcluded(trimmed); + setExcludedValue(''); + }; + + return ( + + {values.length > 0 ? ( +
+ + + +
+ {values.map((item) => ( + + {item} + onRemove(item)} + /> + + ))} +
+
+ ) : ( + + {emptyValues} + + )} + +
+ + + + + setValue(e.target.value)} + outerClassName='grow' + /> + + +
+ + {excludedValues.length > 0 ? ( +
+ + + +
+ {excludedValues.map((item) => ( + + {item} + onRemoveExcluded(item)} + /> + + ))} +
+
+ ) : ( + + {emptyExcludedValues} + + )} + +
+ + + + + setExcludedValue(e.target.value)} + outerClassName='grow' + /> + + +
+
+ ); +}; + +interface IAntennaStringForm { + antennaId: string; +} + +const AntennaDomainsForm: React.FC = ({ antennaId }) => { + const intl = useIntl(); + const { data } = useAntennaDomains(antennaId); + const { mutate: addDomains } = useAddDomainsToAntenna(antennaId); + const { mutate: removeDomains } = useRemoveDomainsFromAntenna(antennaId); + const { mutate: addExcludedDomains } = useAddExcludedDomainsToAntenna(antennaId); + const { mutate: removeExcludedDomains } = useRemoveExcludedDomainsFromAntenna(antennaId); + + return ( + } + listTitle={} + addExcludedTitle={ + + } + listExcludedTitle={ + + } + removeTitle={intl.formatMessage(messages.removeDomain)} + emptyValues={ + + } + emptyExcludedValues={ + + } + onAdd={(value) => addDomains([value])} + onRemove={(value) => removeDomains([value])} + onAddExcluded={(value) => addExcludedDomains([value])} + onRemoveExcluded={(value) => removeExcludedDomains([value])} + /> + ); +}; + +const AntennaKeywordsForm: React.FC = ({ antennaId }) => { + const intl = useIntl(); + const { data } = useAntennaKeywords(antennaId); + const { mutate: addKeywords } = useAddKeywordsToAntenna(antennaId); + const { mutate: removeKeywords } = useRemoveKeywordsFromAntenna(antennaId); + const { mutate: addExcludedKeywords } = useAddExcludedKeywordsToAntenna(antennaId); + const { mutate: removeExcludedKeywords } = useRemoveExcludedKeywordsFromAntenna(antennaId); + + return ( + } + addTitle={} + listExcludedTitle={ + + } + addExcludedTitle={ + + } + removeTitle={intl.formatMessage(messages.removeKeyword)} + emptyValues={ + + } + emptyExcludedValues={ + + } + onAdd={(value) => addKeywords([value])} + onRemove={(value) => removeKeywords([value])} + onAddExcluded={(value) => addExcludedKeywords([value])} + onRemoveExcluded={(value) => removeExcludedKeywords([value])} + /> + ); +}; + +const AntennaTagsForm: React.FC = ({ antennaId }) => { + const intl = useIntl(); + const { data } = useAntennaTags(antennaId); + const { mutate: addTags } = useAddTagsToAntenna(antennaId); + const { mutate: removeTags } = useRemoveTagsFromAntenna(antennaId); + const { mutate: addExcludedTags } = useAddExcludedTagsToAntenna(antennaId); + const { mutate: removeExcludedTags } = useRemoveExcludedTagsFromAntenna(antennaId); + + return ( + } + addTitle={} + listExcludedTitle={ + + } + addExcludedTitle={ + + } + removeTitle={intl.formatMessage(messages.removeTag)} + emptyValues={ + + } + emptyExcludedValues={ + + } + onAdd={(value) => addTags([value])} + onRemove={(value) => removeTags([value])} + onAddExcluded={(value) => addExcludedTags([value])} + onRemoveExcluded={(value) => removeExcludedTags([value])} + /> + ); +}; + interface IEditAntennaForm { antennaId?: string; setAntennaId: (id: string | undefined) => void; @@ -274,7 +586,7 @@ const EditAntennaForm: React.FC = ({ antennaId, onTabChange }) const value = e.target.value; setInsertFeeds(value === 'home'); if (value === 'list') { - setListId(''); // TODO: add list selection + setListId(''); } else { setListId(undefined); } @@ -343,6 +655,28 @@ const EditAntennaForm: React.FC = ({ antennaId, onTabChange }) onTabChange('excludedAccounts'); }} /> + + } + onClick={() => { + onTabChange('domains'); + }} + /> + + } + onClick={() => { + onTabChange('keywords'); + }} + /> + } + onClick={() => { + onTabChange('tags'); + }} + /> )} @@ -390,6 +724,12 @@ const AntennaEditorModal: React.FC = ( return ; case 'excludedAccounts': return ; + case 'domains': + return ; + case 'keywords': + return ; + case 'tags': + return ; default: return null; }