nicolium: extend antenna editor

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2026-02-21 21:51:44 +01:00
parent 58b07cdcb3
commit 1b51d3fe92
2 changed files with 181 additions and 18 deletions

View File

@ -260,13 +260,30 @@
"alt_text_modal.saving_failed": "Failed to save alt text",
"announcements.title": "Announcements",
"antennas.create": "Create antenna",
"antennas.create.error": "Error creating antenna",
"antennas.create.save": "Create antenna",
"antennas.create.success": "Antenna created successfully",
"antennas.delete": "Delete antenna",
"antennas.edit": "Edit antenna",
"antennas.edit.destination": "Destination",
"antennas.edit.destination.antenna": "Antenna timeline only",
"antennas.edit.destination.home": "Insert to home timeline",
"antennas.edit.destination.list": "Insert to list",
"antennas.edit.error": "Error updating antenna",
"antennas.edit.favourite": "Favourite",
"antennas.edit.favourite.hint": "The antenna will be marked as favourite (not used by Nicolium yet)",
"antennas.edit.ignore_reblogs": "Ignore reblogs",
"antennas.edit.ignore_reblogs.hint": "Reblogs will not be included in the antenna",
"antennas.edit.mode": "Mode",
"antennas.edit.mode.filtering": "Filtering",
"antennas.edit.mode.ltl": "Local timeline mode",
"antennas.edit.mode.stl": "Social timeline mode",
"antennas.edit.save": "Save antenna",
"antennas.edit.success": "Antenna updated successfully",
"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.manage_accounts": "Manage antenna accounts",
"antennas.new.create": "Add antenna",
"antennas.subheading": "Your antennas",
"app_create.name_label": "App name",

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import List, { ListItem } from '@/components/list';
import Button from '@/components/ui/button';
import Form from '@/components/ui/form';
import FormActions from '@/components/ui/form-actions';
@ -8,7 +9,10 @@ import FormGroup from '@/components/ui/form-group';
import Input from '@/components/ui/input';
import Modal from '@/components/ui/modal';
import Spinner from '@/components/ui/spinner';
import Toggle from '@/components/ui/toggle';
import { SelectDropdown } from '@/features/forms';
import { useAntenna, useCreateAntenna, useUpdateAntenna } from '@/queries/accounts/use-antennas';
import { useModalsActions } from '@/stores/modals';
import toast from '@/toast';
import type { BaseModalProps } from '@/features/ui/components/modal-root';
@ -16,15 +20,18 @@ import type { BaseModalProps } from '@/features/ui/components/modal-root';
type Tab = 'info' | 'accounts' | 'excludedAccounts';
const messages = defineMessages({
success: { id: 'antennas.edit.success', defaultMessage: 'Antenna updated successfully' },
error: { id: 'antennas.edit.error', defaultMessage: 'Error updating antenna' },
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' },
});
interface IAntennaMembersForm {
antennaId?: string;
interface IAntennaAccountsForm {
antennaId: string;
excluded?: boolean;
}
const AntennaMembersForm: React.FC<IAntennaMembersForm> = () => null;
const AntennaAccountsForm: React.FC<IAntennaAccountsForm> = () => null;
interface IEditAntennaForm {
antennaId?: string;
@ -34,6 +41,7 @@ interface IEditAntennaForm {
const EditAntennaForm: React.FC<IEditAntennaForm> = ({ antennaId, onTabChange }) => {
const intl = useIntl();
const { closeModal } = useModalsActions();
const { data: antenna } = useAntenna(antennaId);
const { mutate: updateAntenna, isPending: updateDisabled } = useUpdateAntenna(antennaId!);
@ -42,6 +50,13 @@ const EditAntennaForm: React.FC<IEditAntennaForm> = ({ antennaId, onTabChange })
const disabled = antennaId ? updateDisabled : createDisabled;
const [title, setTitle] = useState(antenna ? antenna.title : '');
const [ltl, setLtl] = useState(antenna ? antenna.ltl : false);
const [stl, setStl] = useState(antenna ? antenna.stl : false);
const [insertFeeds, setInsertFeeds] = useState(antenna ? antenna.insert_feeds : false);
const [withMediaOnly, setWithMediaOnly] = useState(antenna ? antenna.with_media_only : false);
const [ignoreReblog, setIgnoreReblog] = useState(antenna ? antenna.ignore_reblog : false);
const [favourite, setFavourite] = useState(antenna ? antenna.favourite : false);
const [listId, setListId] = useState(antenna?.list?.id || undefined);
const handleSubmit: React.FormEventHandler = (e) => {
e.preventDefault();
@ -50,13 +65,25 @@ const EditAntennaForm: React.FC<IEditAntennaForm> = ({ antennaId, onTabChange })
const handleUpdate = () => {
(antennaId ? updateAntenna : createAntenna)(
{ title },
{
title,
stl,
ltl,
insert_feeds: insertFeeds,
with_media_only: withMediaOnly,
ignore_reblog: ignoreReblog,
favourite,
list_id: listId,
},
{
onSuccess: () => {
toast.success(intl.formatMessage(messages.success));
toast.success(
intl.formatMessage(antennaId ? messages.editSuccess : messages.createSuccess),
);
closeModal('ANTENNA_EDITOR');
},
onError: () => {
toast.error(intl.formatMessage(messages.error));
toast.error(intl.formatMessage(antennaId ? messages.editError : messages.createError));
},
},
);
@ -76,6 +103,114 @@ const EditAntennaForm: React.FC<IEditAntennaForm> = ({ antennaId, onTabChange })
}}
/>
</FormGroup>
<FormGroup labelText={<FormattedMessage id='antennas.edit.mode' defaultMessage='Mode' />}>
<SelectDropdown
items={{
stl: intl.formatMessage({
id: 'antennas.edit.mode.stl',
defaultMessage: 'Social timeline mode',
}),
ltl: intl.formatMessage({
id: 'antennas.edit.mode.ltl',
defaultMessage: 'Local timeline mode',
}),
filtering: intl.formatMessage({
id: 'antennas.edit.mode.filtering',
defaultMessage: 'Filtering',
}),
}}
defaultValue={stl ? 'stl' : ltl ? 'ltl' : 'filtering'}
onChange={(e) => {
const value = e.target.value;
setStl(value === 'stl');
setLtl(value === 'ltl');
}}
/>
</FormGroup>
<FormGroup
labelText={<FormattedMessage id='antennas.edit.destination' defaultMessage='Destination' />}
>
<SelectDropdown
items={{
home: intl.formatMessage({
id: 'antennas.edit.destination.home',
defaultMessage: 'Insert to home timeline',
}),
list: intl.formatMessage({
id: 'antennas.edit.destination.list',
defaultMessage: 'Insert to list',
}),
antenna: intl.formatMessage({
id: 'antennas.edit.destination.antenna',
defaultMessage: 'Antenna timeline only',
}),
}}
defaultValue={insertFeeds ? 'home' : listId ? 'list' : 'antenna'}
onChange={(e) => {
const value = e.target.value;
setInsertFeeds(value === 'home');
if (value === 'list') {
setListId(''); // TODO: add list selection
} else {
setListId(undefined);
}
}}
/>
</FormGroup>
<List>
<ListItem
label={
<FormattedMessage id='antennas.edit.with_media_only' defaultMessage='Media only' />
}
hint={
<FormattedMessage
id='antennas.edit.with_media_only.hint'
defaultMessage='Only include posts with media attachments'
/>
}
>
<Toggle checked={withMediaOnly} onChange={(e) => setWithMediaOnly(e.target.checked)} />
</ListItem>
<ListItem
label={
<FormattedMessage id='antennas.edit.ignore_reblogs' defaultMessage='Ignore reblogs' />
}
hint={
<FormattedMessage
id='antennas.edit.ignore_reblogs.hint'
defaultMessage='Reblogs will not be included in the antenna'
/>
}
>
<Toggle checked={ignoreReblog} onChange={(e) => setIgnoreReblog(e.target.checked)} />
</ListItem>
<ListItem
label={<FormattedMessage id='antennas.edit.favourite' defaultMessage='Favourite' />}
hint={
<FormattedMessage
id='antennas.edit.favourite.hint'
defaultMessage='The antenna will be marked as favourite (not used by Nicolium yet)'
/>
}
>
<Toggle checked={favourite} onChange={(e) => setFavourite(e.target.checked)} />
</ListItem>
{antennaId && (
<>
<ListItem
label={
<FormattedMessage
id='antennas.manage_accounts'
defaultMessage='Manage antenna accounts'
/>
}
onClick={() => {
onTabChange('accounts');
}}
/>
</>
)}
</List>
<FormActions>
<Button onClick={handleUpdate} disabled={disabled}>
{antennaId ? (
@ -106,6 +241,25 @@ const AntennaEditorModal: React.FC<BaseModalProps & AntennaEditorModalProps> = (
onClose('ANTENNA_EDITOR');
};
const tabContent = useMemo(() => {
if (!isFetched) {
return <Spinner />;
}
switch (tab) {
case 'info':
return (
<EditAntennaForm antennaId={antennaId} setAntennaId={setAntennaId} onTabChange={setTab} />
);
case 'accounts':
return <AntennaAccountsForm antennaId={antennaId!} />;
case 'excludedAccounts':
return <AntennaAccountsForm antennaId={antennaId!} excluded />;
default:
return null;
}
}, [tab, antennaId, isFetched]);
return (
<Modal
title={
@ -124,15 +278,7 @@ const AntennaEditorModal: React.FC<BaseModalProps & AntennaEditorModalProps> = (
}
}
>
{isFetched ? (
tab === 'info' ? (
<EditAntennaForm antennaId={antennaId} setAntennaId={setAntennaId} onTabChange={setTab} />
) : (
<AntennaMembersForm antennaId={antennaId} />
)
) : (
<Spinner />
)}
{tabContent}
</Modal>
);
};