hookshot/web/components/roomConfig/FeedsConfig.tsx
Will Hunt f0651a6cff
Reduce bundle size for widget room configs (#985)
* Add dynamic imports for room configs.

* Try to move code editor to imports

* Load code editor on demand to save a bundle load.

* Load fonts from npm

* changelog

* lint
2024-11-19 09:13:42 +00:00

108 lines
4.9 KiB
TypeScript

import { FunctionComponent, createRef } from "preact";
import { useCallback, useState } from "preact/hooks"
import { BridgeConfig } from "../../BridgeAPI";
import { FeedConnectionState, FeedResponseItem } from "../../../src/Connections/FeedConnection";
import { ConnectionConfigurationProps, IRoomConfigText, RoomConfig } from "./RoomConfig";
import { Button, ButtonSet, InputField } from "../elements";
import styles from "./FeedConnection.module.scss";
import FeedsIcon from "../../icons/feeds.png";
const DEFAULT_TEMPLATE = "New post in $FEEDNAME: $LINK"
const FeedRecentResults: FunctionComponent<{item: FeedResponseItem}> = ({ item }) => {
if (!item.secrets) {
return null;
}
return <>
<h3>Recent feed results</h3>
{!item.secrets.lastResults.length && <span>There have been no recent updates for this feed.</span>}
<ul>
{item.secrets.lastResults.map(item => <li className={styles.resultListItem} key={item.timestamp}>
{new Date(item.timestamp).toLocaleString()}:
{item.ok && `✅ Successful fetch`}
{!item.ok && `⚠️ ${item.error}`}
</li>)}
</ul>
</>;
}
const DOCUMENTATION_LINK = "https://matrix-org.github.io/matrix-hookshot/latest/setup/feeds.html#feed-templates";
const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ServiceConfig, FeedResponseItem, FeedConnectionState>> = ({existingConnection, onSave, onRemove, isMigrationCandidate, isUpdating}) => {
const urlRef = createRef<HTMLInputElement>();
const labelRef = createRef<HTMLInputElement>();
const templateRef = createRef<HTMLInputElement>();
const canSave = !existingConnection?.id || (existingConnection?.canEdit ?? false);
const canEdit = canSave && !isMigrationCandidate;
const [notifyOnFailure, setNotifyOnFailure] = useState<boolean>(existingConnection?.config.notifyOnFailure ?? false);
const handleSave = useCallback((evt: Event) => {
evt.preventDefault();
if (!canSave) {
return;
}
const url = urlRef?.current?.value || existingConnection?.config.url;
if (url) {
onSave({
url,
label: labelRef?.current?.value || existingConnection?.config.label,
template: templateRef.current?.value || existingConnection?.config.template,
notifyOnFailure,
})
}
}, [canSave, onSave, urlRef, labelRef, templateRef, notifyOnFailure, existingConnection]);
const onlyVisibleOnExistingConnection = !!existingConnection;
return <form onSubmit={handleSave}>
{ existingConnection && <FeedRecentResults item={existingConnection} />}
<InputField visible={true} label="URL" noPadding={true}>
<input ref={urlRef} disabled={!canEdit || !!existingConnection} type="text" value={existingConnection?.config.url} />
</InputField>
<InputField visible={true} label="Label" noPadding={true}>
<input ref={labelRef} disabled={!canSave} type="text" value={existingConnection?.config.label} />
</InputField>
<InputField visible={onlyVisibleOnExistingConnection} label="Send a notice on read failure" noPadding={true}>
<input disabled={!canSave} type="checkbox" checked={notifyOnFailure} onChange={useCallback(() => setNotifyOnFailure(v => !v), [])} />
</InputField>
<InputField visible={onlyVisibleOnExistingConnection} label="Template" noPadding={true}>
<input ref={templateRef} disabled={!canSave} type="text" value={existingConnection?.config.template} placeholder={DEFAULT_TEMPLATE} />
<p> See the <a target="_blank" rel="noopener noreferrer" href={DOCUMENTATION_LINK}>documentation</a> for help writing templates. </p>
</InputField>
<ButtonSet>
{ canSave && <Button type="submit" disabled={isUpdating}>{ existingConnection?.id ? "Save" : "Subscribe" }</Button>}
{ canEdit && existingConnection?.id && <Button intent="remove" onClick={onRemove} disabled={isUpdating}>Unsubscribe</Button>}
</ButtonSet>
</form>;
};
interface ServiceConfig {
pollIntervalSeconds: number,
}
const roomConfigText: IRoomConfigText = {
header: 'RSS/Atom feeds',
createNew: 'Subscribe to a feed',
listCanEdit: 'Feeds subscribed to',
listCantEdit: 'Feeds subscribed to',
};
const RoomConfigListItemFunc = (c: FeedResponseItem) => c.config.label || c.config.url;
const FeedsConfig: BridgeConfig = ({ roomId, showHeader }) => {
return <RoomConfig<ServiceConfig, FeedResponseItem, FeedConnectionState>
headerImg={FeedsIcon}
showHeader={showHeader}
roomId={roomId}
type="feeds"
connectionEventType="uk.half-shot.matrix-hookshot.feed"
text={roomConfigText}
listItemName={RoomConfigListItemFunc}
connectionConfigComponent={ConnectionConfiguration}
/>;
};
export default FeedsConfig;