mirror of
https://github.com/stoatchat/for-legacy-web.git
synced 2026-03-06 17:11:55 +00:00
Show errors / queue on message.
Focus editor / box properly.
This commit is contained in:
@@ -6,18 +6,21 @@ import { Children } from "../../../types/Preact";
|
||||
import Attachment from "./attachments/Attachment";
|
||||
import { attachContextMenu } from "preact-context-menu";
|
||||
import { useUser } from "../../../context/revoltjs/hooks";
|
||||
import { QueuedMessage } from "../../../redux/reducers/queue";
|
||||
import { MessageObject } from "../../../context/revoltjs/util";
|
||||
import MessageBase, { MessageContent, MessageDetail, MessageInfo } from "./MessageBase";
|
||||
import Overline from "../../ui/Overline";
|
||||
|
||||
interface Props {
|
||||
attachContext?: boolean
|
||||
queued?: QueuedMessage
|
||||
message: MessageObject
|
||||
contrast?: boolean
|
||||
content?: Children
|
||||
head?: boolean
|
||||
}
|
||||
|
||||
export default function Message({ attachContext, message, contrast, content: replacement, head }: Props) {
|
||||
export default function Message({ attachContext, message, contrast, content: replacement, head, queued }: Props) {
|
||||
// TODO: Can improve re-renders here by providing a list
|
||||
// TODO: of dependencies. We only need to update on u/avatar.
|
||||
let user = useUser(message.author);
|
||||
@@ -25,9 +28,11 @@ export default function Message({ attachContext, message, contrast, content: rep
|
||||
const content = message.content as string;
|
||||
return (
|
||||
<MessageBase id={message._id}
|
||||
contrast={contrast}
|
||||
head={head}
|
||||
onContextMenu={attachContext ? attachContextMenu('Menu', { message, contextualChannel: message.channel }) : undefined}>
|
||||
contrast={contrast}
|
||||
sending={typeof queued !== 'undefined'}
|
||||
failed={typeof queued?.error !== 'undefined'}
|
||||
onContextMenu={attachContext ? attachContextMenu('Menu', { message, contextualChannel: message.channel, queued }) : undefined}>
|
||||
<MessageInfo>
|
||||
{ head ?
|
||||
<UserIcon target={user} size={36} /> :
|
||||
@@ -39,6 +44,7 @@ export default function Message({ attachContext, message, contrast, content: rep
|
||||
<MessageDetail message={message} position="top" />
|
||||
</span> }
|
||||
{ replacement ?? <Markdown content={content} /> }
|
||||
{ queued?.error && <Overline type="error" error={queued.error} /> }
|
||||
{ message.attachments?.map((attachment, index) =>
|
||||
<Attachment key={index} attachment={attachment} hasContent={ index > 0 || content.length > 0 } />) }
|
||||
{ message.embeds?.map((embed, index) =>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { MessageObject } from "../../../context/revoltjs/util";
|
||||
|
||||
export interface BaseMessageProps {
|
||||
head?: boolean,
|
||||
status?: boolean,
|
||||
failed?: boolean,
|
||||
mention?: boolean,
|
||||
blocked?: boolean,
|
||||
sending?: boolean,
|
||||
@@ -49,7 +49,7 @@ export default styled.div<BaseMessageProps>`
|
||||
color: var(--tertiary-foreground);
|
||||
` }
|
||||
|
||||
${ props => props.status && css`
|
||||
${ props => props.failed && css`
|
||||
color: var(--error);
|
||||
` }
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { SingletonMessageRenderer, SMOOTH_SCROLL_ON_RECEIVE } from "../../../lib
|
||||
|
||||
import FilePreview from './bars/FilePreview';
|
||||
import { debounce } from "../../../lib/debounce";
|
||||
import { internalEmit } from "../../../lib/eventEmitter";
|
||||
|
||||
type Props = WithDispatcher & {
|
||||
channel: Channel;
|
||||
@@ -237,11 +238,22 @@ function MessageBox({ channel, draft, dispatcher }: Props) {
|
||||
/>
|
||||
</Action>
|
||||
<TextAreaAutoSize
|
||||
autoFocus
|
||||
hideBorder
|
||||
maxRows={5}
|
||||
padding={15}
|
||||
id="message"
|
||||
value={draft ?? ''}
|
||||
onKeyDown={e => {
|
||||
if (
|
||||
e.key === "ArrowUp" &&
|
||||
(!draft || draft.length === 0)
|
||||
) {
|
||||
e.preventDefault();
|
||||
internalEmit("MessageRenderer", "edit_last");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.shiftKey && e.key === "Enter" && !isTouchscreenDevice) {
|
||||
e.preventDefault();
|
||||
return send();
|
||||
|
||||
@@ -48,142 +48,3 @@ export default styled.textarea<TextAreaProps>`
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
` }
|
||||
`;
|
||||
|
||||
/*export interface TextAreaProps {
|
||||
id?: string;
|
||||
value: string;
|
||||
maxRows?: number;
|
||||
padding?: number;
|
||||
minHeight?: number;
|
||||
disabled?: boolean;
|
||||
maxLength?: number;
|
||||
className?: string;
|
||||
autoFocus?: boolean;
|
||||
forceFocus?: boolean;
|
||||
placeholder?: string;
|
||||
onKeyDown?: (ev: KeyboardEvent) => void;
|
||||
onKeyUp?: (ev: KeyboardEvent) => void;
|
||||
onChange: (
|
||||
value: string,
|
||||
ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>
|
||||
) => void;
|
||||
onFocus?: (current: HTMLTextAreaElement) => void;
|
||||
onBlur?: () => void;
|
||||
}
|
||||
|
||||
const lineHeight = 20;
|
||||
|
||||
export const TextAreaB = memo((props: TextAreaProps) => {
|
||||
const padding = props.padding ? props.padding * 2 : 0;
|
||||
|
||||
const [height, setHeightState] = useState(
|
||||
props.minHeight ?? lineHeight + padding
|
||||
);
|
||||
const ghost = useRef<HTMLDivElement>();
|
||||
const ref = useRef<HTMLTextAreaElement>();
|
||||
|
||||
function setHeight(h: number = lineHeight) {
|
||||
let newHeight = Math.min(
|
||||
Math.max(
|
||||
lineHeight,
|
||||
props.maxRows ? Math.min(h, props.maxRows * lineHeight) : h
|
||||
),
|
||||
props.minHeight ?? Infinity
|
||||
);
|
||||
|
||||
if (props.padding) newHeight += padding;
|
||||
if (height !== newHeight) {
|
||||
setHeightState(newHeight);
|
||||
}
|
||||
}
|
||||
|
||||
function onChange(ev: JSX.TargetedEvent<HTMLTextAreaElement, Event>) {
|
||||
props.onChange(ev.currentTarget.value, ev);
|
||||
}
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setHeight(ghost.current.clientHeight);
|
||||
}, [ghost, props.value]);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.autoFocus) ref.current.focus();
|
||||
}, [props.value]);
|
||||
|
||||
const inputSelected = () =>
|
||||
["TEXTAREA", "INPUT"].includes(document.activeElement?.nodeName ?? "");
|
||||
|
||||
useEffect(() => {
|
||||
if (props.forceFocus) {
|
||||
ref.current.focus();
|
||||
}
|
||||
|
||||
if (props.autoFocus && !inputSelected()) {
|
||||
ref.current.focus();
|
||||
}
|
||||
|
||||
// ? if you are wondering what this is
|
||||
// ? it is a quick and dirty hack to fix
|
||||
// ? value not setting correctly
|
||||
// ? I have no clue what's going on
|
||||
ref.current.value = props.value;
|
||||
|
||||
if (!props.autoFocus) return;
|
||||
function keyDown(e: KeyboardEvent) {
|
||||
if ((e.ctrlKey && e.key !== "v") || e.altKey || e.metaKey) return;
|
||||
if (e.key.length !== 1) return;
|
||||
if (ref && !inputSelected()) {
|
||||
ref.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
document.body.addEventListener("keydown", keyDown);
|
||||
return () => document.body.removeEventListener("keydown", keyDown);
|
||||
}, [ref]);
|
||||
|
||||
useEffect(() => {
|
||||
function focus(textarea_id: string) {
|
||||
if (props.id === textarea_id) {
|
||||
ref.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// InternalEventEmitter.addListener("focus_textarea", focus);
|
||||
// return () =>
|
||||
// InternalEventEmitter.removeListener("focus_textarea", focus);
|
||||
}, [ref]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, props.className)}>
|
||||
<textarea
|
||||
id={props.id}
|
||||
name={props.id}
|
||||
style={{ height }}
|
||||
value={props.value}
|
||||
onChange={onChange}
|
||||
disabled={props.disabled}
|
||||
maxLength={props.maxLength}
|
||||
className={styles.textarea}
|
||||
onKeyDown={props.onKeyDown}
|
||||
placeholder={props.placeholder}
|
||||
onContextMenu={e => e.stopPropagation()}
|
||||
onKeyUp={ev => {
|
||||
setHeight(ghost.current.clientHeight);
|
||||
props.onKeyUp && props.onKeyUp(ev);
|
||||
}}
|
||||
ref={ref}
|
||||
onFocus={() => props.onFocus && props.onFocus(ref.current)}
|
||||
onBlur={props.onBlur}
|
||||
/>
|
||||
<div className={styles.hide}>
|
||||
<div className={styles.ghost} ref={ghost}>
|
||||
{props.value
|
||||
? props.value
|
||||
.split("\n")
|
||||
.map(x => `${x}`)
|
||||
.join("\n")
|
||||
: undefined ?? "\n"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});*/
|
||||
|
||||
Reference in New Issue
Block a user