From 7e32f0d992e90a856de755cbb1bae41682193222 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 19 Nov 2022 18:13:27 -0600 Subject: [PATCH] Add component to style markup from the API --- .../{status-content.css => markup.css} | Bin 1476 -> 1436 bytes app/soapbox/components/markup.tsx | 16 ++++++ app/soapbox/components/status-content.tsx | 51 +++--------------- app/soapbox/components/ui/text/text.tsx | 8 +-- .../ui/components/profile-fields-panel.tsx | 7 +-- .../ui/components/profile-info-panel.tsx | 12 ++--- app/soapbox/normalizers/account.ts | 10 ++++ 7 files changed, 46 insertions(+), 58 deletions(-) rename app/soapbox/components/{status-content.css => markup.css} (62%) create mode 100644 app/soapbox/components/markup.tsx diff --git a/app/soapbox/components/status-content.css b/app/soapbox/components/markup.css similarity index 62% rename from app/soapbox/components/status-content.css rename to app/soapbox/components/markup.css index 997df73f4d30f1ea42cc52b9be1f7b6c3511187d..d89848f2cb082755acb71c8140b91c3232999bdc 100644 GIT binary patch delta 430 zcmX@YJ%^h&Iwi3rQ8zcSD7&;Eb|SwvKZ<}t!Q_L?3TTR|JnK;v6_*s{=cOxDb15h| zBo-9pR4Sz9=auLt<>#bWbJcQjp{h>JMOK+vl9-d3j73>#jt-i}{2Z9Z$zF`YsE(SP z!>ENOvXoI5P2?eCF`7s+leQ|V>k1U?6q55(Qqe4*yq(DnO)n3#BbrDIa}b)yUS=ya Y5jK`BXd)+BWY9#uuvnvs*s-<&02$Jk82|tP delta 453 zcmbQkeT17|uecV3QIgOgia~?pzBS|RjB4t zP;f{rD9EW)$Sg_B$xOE9s^#KBS6G^(gDy7NkWmiZj>$2MS{P!J8Fewl&N3Ebi1{*U zYogm*pkSwvoS%}4Y5QCzHw+U#Fgap~*)a!Uh|OiT!Vr7Hyu}LL_C(~+Da}hsEy~Hv SOT`vIlh3hOV;CvM+6Dj))}=WB diff --git a/app/soapbox/components/markup.tsx b/app/soapbox/components/markup.tsx new file mode 100644 index 0000000000..e20dcb3a2a --- /dev/null +++ b/app/soapbox/components/markup.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import Text, { IText } from './ui/text/text'; +import './markup.css'; + +interface IMarkup extends IText { +} + +/** Styles HTML markup returned by the API, such as in account bios and statuses. */ +const Markup = React.forwardRef((props, ref) => { + return ( + + ); +}); + +export default Markup; \ No newline at end of file diff --git a/app/soapbox/components/status-content.tsx b/app/soapbox/components/status-content.tsx index 2b2754176a..6bfbb8e182 100644 --- a/app/soapbox/components/status-content.tsx +++ b/app/soapbox/components/status-content.tsx @@ -10,8 +10,8 @@ import { onlyEmoji as isOnlyEmoji } from 'soapbox/utils/rich-content'; import { isRtl } from '../rtl'; +import Markup from './markup'; import Poll from './polls/poll'; -import './status-content.css'; import StopPropagation from './stop-propagation'; import type { Status, Mention } from 'soapbox/types/entities'; @@ -19,11 +19,6 @@ import type { Status, Mention } from 'soapbox/types/entities'; const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top) const BIG_EMOJI_LIMIT = 10; -type Point = [ - x: number, - y: number, -] - interface IReadMoreButton { onClick: React.MouseEventHandler, } @@ -52,7 +47,6 @@ const StatusContent: React.FC = ({ status, onClick, collapsable const [collapsed, setCollapsed] = useState(false); const [onlyEmoji, setOnlyEmoji] = useState(false); - const startXY = useRef(); const node = useRef(null); const { greentext } = useSoapboxConfig(); @@ -138,29 +132,6 @@ const StatusContent: React.FC = ({ status, onClick, collapsable updateStatusLinks(); }); - const handleMouseDown: React.EventHandler = (e) => { - startXY.current = [e.clientX, e.clientY]; - }; - - const handleMouseUp: React.EventHandler = (e) => { - if (!startXY.current) return; - const target = e.target as HTMLElement; - const parentNode = target.parentNode as HTMLElement; - - const [startX, startY] = startXY.current; - const [deltaX, deltaY] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)]; - - if (target.localName === 'button' || target.localName === 'a' || (parentNode && (parentNode.localName === 'button' || parentNode.localName === 'a'))) { - return; - } - - if (deltaX + deltaY < 5 && e.button === 0 && !(e.ctrlKey || e.metaKey) && onClick) { - onClick(); - } - - startXY.current = undefined; - }; - const parsedHtml = useMemo((): string => { const html = translatable && status.translation ? status.translation.get('content')! : status.contentHtml; @@ -180,30 +151,24 @@ const StatusContent: React.FC = ({ status, onClick, collapsable const baseClassName = 'text-gray-900 dark:text-gray-100 break-words text-ellipsis overflow-hidden relative focus:outline-none'; const content = { __html: parsedHtml }; - const directionStyle: React.CSSProperties = { direction: 'ltr' }; - const className = classNames(baseClassName, 'status-content', { + const direction = isRtl(status.search_index) ? 'rtl' : 'ltr'; + const className = classNames(baseClassName, { 'cursor-pointer': onClick, 'whitespace-normal': withSpoiler, 'max-h-[300px]': collapsed, 'leading-normal big-emoji': onlyEmoji, }); - if (isRtl(status.search_index)) { - directionStyle.direction = 'rtl'; - } - if (onClick) { const output = [ -
, ]; @@ -219,14 +184,14 @@ const StatusContent: React.FC = ({ status, onClick, collapsable return
{output}
; } else { const output = [ -
, diff --git a/app/soapbox/components/ui/text/text.tsx b/app/soapbox/components/ui/text/text.tsx index 7669f3d2a8..221c070fc4 100644 --- a/app/soapbox/components/ui/text/text.tsx +++ b/app/soapbox/components/ui/text/text.tsx @@ -54,7 +54,9 @@ export type Sizes = keyof typeof sizes type Tags = 'abbr' | 'p' | 'span' | 'pre' | 'time' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'label' type Directions = 'ltr' | 'rtl' -interface IText extends Pick, 'dangerouslySetInnerHTML'> { +interface IText extends Pick, 'dangerouslySetInnerHTML' | 'tabIndex' | 'lang'> { + /** Text content. */ + children?: React.ReactNode, /** How to align the text. */ align?: keyof typeof alignments, /** Extra class names for the outer element. */ @@ -84,8 +86,8 @@ interface IText extends Pick, 'danger } /** UI-friendly text container with dark mode support. */ -const Text: React.FC = React.forwardRef( - (props: IText, ref: React.LegacyRef) => { +const Text = React.forwardRef( + (props, ref) => { const { align, className, diff --git a/app/soapbox/features/ui/components/profile-fields-panel.tsx b/app/soapbox/features/ui/components/profile-fields-panel.tsx index dfa4dc84ad..1ee911e34a 100644 --- a/app/soapbox/features/ui/components/profile-fields-panel.tsx +++ b/app/soapbox/features/ui/components/profile-fields-panel.tsx @@ -2,7 +2,8 @@ import classNames from 'clsx'; import React from 'react'; import { defineMessages, useIntl, FormattedMessage, FormatDateOptions } from 'react-intl'; -import { Widget, Stack, HStack, Icon, Text } from 'soapbox/components/ui'; +import Markup from 'soapbox/components/markup'; +import { Widget, Stack, HStack, Icon } from 'soapbox/components/ui'; import BundleContainer from 'soapbox/features/ui/containers/bundle-container'; import { CryptoAddress } from 'soapbox/features/ui/util/async-components'; @@ -51,7 +52,7 @@ const ProfileField: React.FC = ({ field }) => { return (
- +
= ({ field }) => { )} - +
diff --git a/app/soapbox/features/ui/components/profile-info-panel.tsx b/app/soapbox/features/ui/components/profile-info-panel.tsx index cf97b78e7c..89239bd618 100644 --- a/app/soapbox/features/ui/components/profile-info-panel.tsx +++ b/app/soapbox/features/ui/components/profile-info-panel.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { defineMessages, useIntl, FormattedMessage } from 'react-intl'; import Badge from 'soapbox/components/badge'; +import Markup from 'soapbox/components/markup'; import { Icon, HStack, Stack, Text } from 'soapbox/components/ui'; import VerificationBadge from 'soapbox/components/verification-badge'; import { useSoapboxConfig } from 'soapbox/hooks'; @@ -139,13 +140,6 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => return (
- {/* Not sure if this is actual used. */} - {/*
- -
*/} - @@ -178,8 +172,8 @@ const ProfileInfoPanel: React.FC = ({ account, username }) => - {account.note.length > 0 && account.note !== '

' && ( - + {account.note.length > 0 && ( + )}
diff --git a/app/soapbox/normalizers/account.ts b/app/soapbox/normalizers/account.ts index 07d3ec1e61..39541facf8 100644 --- a/app/soapbox/normalizers/account.ts +++ b/app/soapbox/normalizers/account.ts @@ -269,6 +269,15 @@ const fixBirthday = (account: ImmutableMap) => { return account.set('birthday', birthday || ''); }; +/** Rewrite `

` to empty string. */ +const fixNote = (account: ImmutableMap) => { + if (account.get('note') === '

') { + return account.set('note', ''); + } else { + return account; + } +}; + export const normalizeAccount = (account: Record) => { return AccountRecord( ImmutableMap(fromJS(account)).withMutations(account => { @@ -289,6 +298,7 @@ export const normalizeAccount = (account: Record) => { fixUsername(account); fixDisplayName(account); fixBirthday(account); + fixNote(account); addInternalFields(account); }), );