diff --git a/README.md b/README.md index a9a3c3cc8..871cceb3f 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,11 @@ Emoji reaction buttons styled for terminal mode via `button[data-reaction-key]` - **Presence badges on members**: Online/busy/away dots shown next to users in the room members drawer and settings members panel (`PresenceBadge` component from `src/app/components/presence/Presence.tsx`). - **Document title unread count**: Tab title updates to `(N) Lotus Chat` for mentions, `· Lotus Chat` for unreads, `Lotus Chat` when clear. +### Server Integration + +- **Server support contact (MSC1929)**: Settings → Help & About displays the homeserver admin contact fetched from `/.well-known/matrix/support`. Shows the admin's Matrix ID and a link to the support page when the homeserver has configured this endpoint. Degrades gracefully when not configured (section is hidden on 404 or network error). In TDS mode the contact text and link render in `--lt-accent-cyan`. Implemented in `src/app/features/settings/about/About.tsx`. +- **Server notices**: Rooms of type `m.server_notice` (system messages from the homeserver) now render with a distinct "Server Notice" `` badge in the room header and a disabled composer showing "This is a server notice room — you cannot send messages here." Previously indistinguishable from regular DMs. Badge in `src/app/features/room/RoomViewHeader.tsx`; composer guard in `src/app/features/room/RoomInput.tsx`. + ### Infrastructure - **Authenticated media**: All avatar/media loads use `mxcUrlToHttp(mx, mxcUrl, useAuthentication, w, h, 'crop')` from `../../utils/matrix` — the Lotus utility that handles MSC3916 authenticated media. (Upstream Cinny uses the SDK method with incorrect argument order for authenticated endpoints.) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 06284a39b..a03f50ff9 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -603,6 +603,16 @@ export const RoomInput = forwardRef( }); }; + if (room.getType() === 'm.server_notice') { + return ( +
+ + This is a server notice room — you cannot send messages here. + +
+ ); + } + return (
{selectedFiles.length > 0 && ( diff --git a/src/app/features/settings/about/About.tsx b/src/app/features/settings/about/About.tsx index 96d21a147..1c6db302a 100644 --- a/src/app/features/settings/about/About.tsx +++ b/src/app/features/settings/about/About.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { Box, Text, IconButton, Icon, Icons, Scroll, Button, config, toRem } from 'folds'; import { Page, PageContent, PageHeader } from '../../../components/page'; import { SequenceCard } from '../../../components/sequence-card'; @@ -8,12 +8,49 @@ import LotusLogo from '../../../../../public/res/Lotus.png'; import pkg from '../../../../../package.json'; import { clearCacheAndReload } from '../../../../client/initMatrix'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; +import { lotusTerminalBodyClass } from '../../../../lotus-terminal.css'; + +type MSC1929Contact = { + matrix_id?: string; + email_address?: string; + role?: string; +}; + +type MSC1929Support = { + contacts?: MSC1929Contact[]; + support_page?: string; +}; + +function useServerSupport(): MSC1929Support | null { + const mx = useMatrixClient(); + const [support, setSupport] = useState(null); + + useEffect(() => { + const baseUrl = (mx as unknown as { baseUrl: string }).baseUrl; + fetch(`${baseUrl}/.well-known/matrix/support`) + .then((res) => { + if (!res.ok) return null; + return res.json() as Promise; + }) + .then((data) => { + if (data && (data.contacts?.length || data.support_page)) { + setSupport(data); + } + }) + .catch(() => { + // Graceful degradation — server may not have this configured + }); + }, [mx]); + + return support; +} type AboutProps = { requestClose: () => void; }; export function About({ requestClose }: AboutProps) { const mx = useMatrixClient(); + const serverSupport = useServerSupport(); return ( @@ -108,6 +145,67 @@ export function About({ requestClose }: AboutProps) { /> + {serverSupport && ( + + Homeserver Support + + + {serverSupport.contacts && serverSupport.contacts.length > 0 && ( + + {serverSupport.contacts.map((contact, i) => ( + + + {contact.role === 'm.role.admin' + ? 'Admin' + : contact.role === 'm.role.security' + ? 'Security' + : 'Contact'} + : + + + {contact.matrix_id ?? contact.email_address ?? ''} + + + ))} + + )} + {serverSupport.support_page && ( + + + Support Page: + + + + {serverSupport.support_page} + + + + )} + + + + )} Credits