feat: delivery status indicator, GIF picker CSS to TDS file (M-6, M-7)
- Message.tsx: show delivery status (sending/sent/failed) on own messages when no read receipts yet; hidden once server confirms (status null); TDS-styled - GifPicker.tsx: move terminal CSS from runtime <style> tag into lotus-terminal.css.ts eliminating flash of unstyled content (M-6) - lotus-terminal.css.ts: add [data-gif-terminal] selector rules for GIF picker
This commit is contained in:
@@ -8,41 +8,6 @@ import { settingsAtom } from '../state/settings';
|
||||
|
||||
const PICKER_WIDTH = 312;
|
||||
|
||||
const TERMINAL_CSS = `
|
||||
[data-gif-terminal] input,
|
||||
[data-gif-terminal] form {
|
||||
background: #030c14 !important;
|
||||
color: #e8edf5 !important;
|
||||
font-family: 'JetBrains Mono', 'Cascadia Code', 'Fira Code', monospace !important;
|
||||
border: 1px solid rgba(255,107,0,0.35) !important;
|
||||
border-radius: 4px !important;
|
||||
font-size: 12px !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
[data-gif-terminal] input:focus {
|
||||
border-color: rgba(255,107,0,0.7) !important;
|
||||
box-shadow: 0 0 0 2px rgba(255,107,0,0.12) !important;
|
||||
outline: none !important;
|
||||
}
|
||||
[data-gif-terminal] input::placeholder {
|
||||
color: rgba(255,107,0,0.4) !important;
|
||||
font-family: 'JetBrains Mono', monospace !important;
|
||||
}
|
||||
[data-gif-terminal] svg,
|
||||
[data-gif-terminal] button[type="reset"] {
|
||||
display: none !important;
|
||||
}
|
||||
[data-gif-terminal] ::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
[data-gif-terminal] ::-webkit-scrollbar-track {
|
||||
background: #030508;
|
||||
}
|
||||
[data-gif-terminal] ::-webkit-scrollbar-thumb {
|
||||
background: rgba(255,107,0,0.4);
|
||||
border-radius: 2px;
|
||||
}
|
||||
`;
|
||||
|
||||
type GifPickerInnerProps = {
|
||||
onSelect: (url: string, width: number, height: number) => void;
|
||||
@@ -142,7 +107,6 @@ export function GifPicker({ apiKey, onSelect, requestClose }: GifPickerProps) {
|
||||
data-gif-terminal={lotusTerminal ? '' : undefined}
|
||||
style={containerStyle}
|
||||
>
|
||||
{lotusTerminal && <style>{TERMINAL_CSS}</style>}
|
||||
<SearchContextManager apiKey={apiKey} initialTerm="">
|
||||
<GifPickerInner onSelect={onSelect} requestClose={requestClose} lotusTerminal={!!lotusTerminal} />
|
||||
</SearchContextManager>
|
||||
|
||||
@@ -32,7 +32,7 @@ import React, {
|
||||
} from 'react';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { useHover, useFocusWithin } from 'react-aria';
|
||||
import { MatrixEvent, Room } from 'matrix-js-sdk';
|
||||
import { MatrixEvent, Room, EventStatus } from 'matrix-js-sdk';
|
||||
import { Relations } from 'matrix-js-sdk/lib/models/relations';
|
||||
import classNames from 'classnames';
|
||||
import { RoomPinnedEventsEventContent } from 'matrix-js-sdk/lib/types';
|
||||
@@ -60,6 +60,8 @@ import {
|
||||
} from '../../../utils/matrix';
|
||||
import { MessageLayout, MessageSpacing } from '../../../state/settings';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../../state/settings';
|
||||
import { useRecentEmoji } from '../../../hooks/useRecentEmoji';
|
||||
import * as css from './styles.css';
|
||||
import { EventReaders } from '../../../components/event-readers';
|
||||
@@ -82,6 +84,44 @@ import { PowerIcon } from '../../../components/power';
|
||||
import colorMXID from '../../../../util/colorMXID';
|
||||
import { getPowerTagIconSrc } from '../../../hooks/useMemberPowerTag';
|
||||
|
||||
|
||||
// Delivery status indicator for own messages
|
||||
function DeliveryStatus({ status, lotusTerminal }: { status: string | null; lotusTerminal: boolean }) {
|
||||
if (status === null) return null; // confirmed by server — read receipts take over
|
||||
let icon: string;
|
||||
let label: string;
|
||||
let colorStyle: string;
|
||||
if (status === EventStatus.NOT_SENT) {
|
||||
icon = '✕'; label = 'Failed to send'; colorStyle = lotusTerminal ? '#FF3B3B' : color.Critical.Main;
|
||||
} else if (status === EventStatus.SENDING || status === EventStatus.ENCRYPTING) {
|
||||
icon = '⟳'; label = 'Sending...'; colorStyle = lotusTerminal ? 'rgba(0,212,255,0.60)' : color.Secondary.Main;
|
||||
} else {
|
||||
icon = '✓'; label = 'Sent'; colorStyle = lotusTerminal ? 'rgba(0,212,255,0.70)' : color.Secondary.Main;
|
||||
}
|
||||
return (
|
||||
<Box
|
||||
as="span"
|
||||
aria-label={label}
|
||||
title={label}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
marginTop: '2px',
|
||||
fontSize: '10px',
|
||||
lineHeight: 1,
|
||||
color: colorStyle,
|
||||
opacity: 0.85,
|
||||
userSelect: 'none',
|
||||
...(lotusTerminal && status === EventStatus.NOT_SENT
|
||||
? { textShadow: '0 0 6px #FF3B3B' }
|
||||
: {}),
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void;
|
||||
|
||||
type MessageQuickReactionsProps = {
|
||||
@@ -727,6 +767,8 @@ export const Message = as<'div', MessageProps>(
|
||||
const readReceiptUsers = hideReadReceipts
|
||||
? []
|
||||
: (readPositions.get(mEvent.getId() ?? '') ?? []);
|
||||
const isMine = mEvent.getSender() === mx.getUserId();
|
||||
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
const { hoverProps } = useHover({ onHoverChange: setHover });
|
||||
@@ -846,6 +888,9 @@ export const Message = as<'div', MessageProps>(
|
||||
userIds={readReceiptUsers}
|
||||
/>
|
||||
)}
|
||||
{isMine && readReceiptUsers.length === 0 && (
|
||||
<DeliveryStatus status={mEvent.status} lotusTerminal={!!lotusTerminal} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
|
||||
@@ -626,3 +626,37 @@ globalStyle(`html[data-theme="light"] body.${lotusTerminalBodyClass} button[data
|
||||
boxShadow: '0 0 7px rgba(196,78,0,0.18)',
|
||||
});
|
||||
|
||||
// ── GIF picker (terminal mode) ───────────────────────────────────────────────
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] input,` +
|
||||
`body.${lotusTerminalBodyClass} [data-gif-terminal] form`, {
|
||||
background: '#030c14 !important' as any,
|
||||
color: '#e8edf5 !important' as any,
|
||||
fontFamily: "'JetBrains Mono','Cascadia Code','Fira Code',monospace !important" as any,
|
||||
border: '1px solid rgba(255,107,0,0.35) !important' as any,
|
||||
borderRadius: '4px !important' as any,
|
||||
fontSize: '12px !important' as any,
|
||||
boxShadow: 'none !important' as any,
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] input:focus`, {
|
||||
borderColor: 'rgba(255,107,0,0.70) !important' as any,
|
||||
boxShadow: '0 0 0 2px rgba(255,107,0,0.12) !important' as any,
|
||||
outline: 'none !important' as any,
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] input::placeholder`, {
|
||||
color: 'rgba(255,107,0,0.40) !important' as any,
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] svg,` +
|
||||
`body.${lotusTerminalBodyClass} [data-gif-terminal] button[type="reset"]`, {
|
||||
display: 'none !important' as any,
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] ::-webkit-scrollbar`, {
|
||||
width: '4px',
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] ::-webkit-scrollbar-track`, {
|
||||
background: '#030508',
|
||||
});
|
||||
globalStyle(`body.${lotusTerminalBodyClass} [data-gif-terminal] ::-webkit-scrollbar-thumb`, {
|
||||
background: 'rgba(255,107,0,0.40)',
|
||||
borderRadius: '2px',
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user