import { FragmentType, FragmentBlockquoteType, FragmentBoldType, FragmentBoldItalicsType, FragmentBoldItalicsStrikeType, FragmentItalicsStrikeType, FragmentBoldStrikeType, FragmentCodeType, FragmentImageType, FragmentInlineCodeType, FragmentItalicsType, FragmentLinkType, FragmentPlainType, FragmentShipType, FragmentStrikeType, FragmentKey, FragmentUrLinkType, FragmentReplyType, FragmentTabType, TEXT_TYPES, } from './Bubble.types'; import styled from 'styled-components'; import { capitalizeFirstLetter } from '../../util/strings'; import { Text, TextProps, Flex, FlexProps, skeletonStyle, } from '../../../general'; import { BlockStyle } from '../Block/Block'; import { motion } from 'framer-motion'; import { ImageBlock } from '../../blocks/ImageBlock/ImageBlock'; import { LinkBlock } from '../../blocks/LinkBlock/LinkBlock'; import { BubbleAuthor } from './Bubble.styles'; import { Bookmark } from '../../os/Bookmark/Bookmark'; import { BUBBLE_HEIGHT } from './Bubble.constants'; export const FragmentBase = styled(Text.Custom)` display: inline; user-select: text; margin: 0px 0px; line-height: ${BUBBLE_HEIGHT.rem.fragment}; `; export const BlockWrapper = styled(motion.span)` padding: 0px; display: inline-block; margin-top: 2px; height: 100%; `; export const FragmentBlock = styled(motion.span)` height: 100%; width: 100%; blockquote { margin-bottom: 4px; } `; export const FragmentPlain = styled(FragmentBase)` font-weight: 400; margin: 0 0; line-height: ${BUBBLE_HEIGHT.rem.fragment}; `; export const FragmentBold = styled(FragmentBase)` font-weight: 800; `; export const FragmentItalic = styled(FragmentBase)` font-style: italic; `; export const FragmentStrike = styled(FragmentBase)` text-decoration: line-through; `; export const FragmentBoldItalic = styled(FragmentBase)` font-weight: 800; font-style: italic; `; export const FragmentBoldStrike = styled(FragmentBase)` font-weight: 800; text-decoration: line-through; `; export const FragmentItalicsStrike = styled(FragmentBase)` font-style: italic; text-decoration: line-through; `; export const FragmentBoldItalicsStrike = styled(FragmentBase)` font-weight: 800; font-style: italic; text-decoration: line-through; `; export const FragmentReplyTo = styled(motion.blockquote)` font-style: italic; border-radius: 4px; display: flex; margin: 0px; flex-direction: column; padding: 4px; background: rgba(var(--rlm-card-rgba)); ${FragmentBase} { font-size: 0.86em; color: rgba(var(--rlm-text-rgba)); } ${Text.Custom} { color: rgba(var(--rlm-text-rgba)); } `; export const FragmentInlineCode = styled(FragmentBase)` font-family: 'Fira Code', monospace; border-radius: 4px; /* padding: 0px 3px; */ `; export const FragmentShip = styled(FragmentBase)` color: rgba(var(--rlm-accent-rgba)); background: rgba(var(--rlm-accent-rgba), 0.12); border-radius: 4px; padding: 2px 4px; transition: var(--transition); &:hover { transition: var(--transition); background: rgba(var(--rlm-accent-rgba), 0.18); cursor: pointer; } `; export const CodeWrapper = styled(Flex)` border-radius: 4px; background-color: rgba(0, 0, 0, 0.08); transition: var(--transition); &:hover { transition: var(--transition); background-color: rgba(0, 0, 0, 0.12); } margin-top: 4px; margin-bottom: 4px; padding: 6px 8px; width: 100%; ${Text.Custom} { color: rgba(var(--rlm-text-rgba)) !important; } `; export const FragmentCodeBlock = styled(Text.Custom)` font-family: 'Fira Code', monospace; border-radius: 4px; width: 100%; white-space: pre-wrap; `; type FragmentImageProps = { isSkeleton?: boolean; }; export const FragmentImage = styled(motion.img)` width: 100%; max-width: 20rem; border-radius: 4px; ${({ isSkeleton }) => isSkeleton && skeletonStyle} `; const TabWrapper = styled(Flex)` border-radius: 6px; background: rgba(var(--rlm-card-rgba)); ${Text.Custom} { color: rgba(var(--rlm-text-rgba)); } `; export const FragmentBlockquote = styled(motion.blockquote)` font-style: italic; border-left: 2px solid rgba(var(--rlm-accent-rgba)); padding-left: 6px; padding-right: 8px; border-radius: 6px; padding-top: 6px; padding-bottom: 6px; display: flex; flex-direction: row; align-items: center; background-color: rgba(0, 0, 0, 0.1); .fragment-reply { border-radius: 4px; ${FragmentBase} { font-size: 0.82rem; } ${Text.Custom} { line-height: 1rem; } .block-author { display: none !important; } ${BlockWrapper} { height: 32px !important; width: fit-content !important; } ${BlockStyle} { padding: 0px; margin: 0px; height: 32px !important; width: fit-content !important; } ${FragmentImage} { width: fit-content !important; height: 32px !important; } &.pinned { gap: 0px; ${Text.Custom} { /* line-height: inherit; */ /* font-size: 0.8em; */ } } } &.pinned-or-reply-message { padding-top: 4px; padding-bottom: 4px; padding-left: 6px; padding-right: 4px; border-radius: 3px; height: 46px; width: 100%; gap: 12px; display: flex; flex-direction: row; justify-content: flex-start; align-items: center; margin: 0; background: rgba(var(--rlm-overlay-hover-rgba)); ${FragmentImage} { border-radius: 2px; height: 36px !important; } } &:hover:not([disabled]) { transition: var(--transition); background-color: rgba(var(--rlm-overlay-active-rgba)); cursor: pointer; } `; export const LineBreak = styled.div` width: 100%; height: 4px; margin: 0; padding: 0; `; export const renderFragment = ( id: string, fragment: FragmentType, index: number, author: string, containerWidth?: number, onReplyClick?: (id: string) => void ) => { const key = Object.keys(fragment)[0] as FragmentKey; switch (key) { case 'plain': return ( {(fragment as FragmentPlainType).plain} ); case 'bold': return ( {(fragment as FragmentBoldType).bold} ); case 'italics': return ( {(fragment as FragmentItalicsType).italics} ); case 'strike': return ( {(fragment as FragmentStrikeType).strike} ); case 'bold-italics': return ( {(fragment as FragmentBoldItalicsType)['bold-italics']} ); case 'bold-strike': return ( {(fragment as FragmentBoldStrikeType)['bold-strike']} ); case 'italics-strike': return ( {(fragment as FragmentItalicsStrikeType)['italics-strike']} ); case 'bold-italics-strike': return ( {(fragment as FragmentBoldItalicsStrikeType)['bold-italics-strike']} ); case 'blockquote': return ( {(fragment as FragmentBlockquoteType).blockquote} ); case 'inline-code': return ( {(fragment as FragmentInlineCodeType)['inline-code']} ); case 'ship': return ( {(fragment as FragmentShipType).ship} ); case 'code': return ( {(fragment as FragmentCodeType).code} ); case 'link': return ( { // // do nothing // } () => { // do nothing } } minWidth={320} /> ); case 'image': const imageFrag = fragment as FragmentImageType; return ( ); case 'reply': const msg = (fragment as FragmentReplyType).reply.message[0]; const replyAuthor = (fragment as FragmentReplyType).reply.author; const replyId = (fragment as FragmentReplyType).reply.msgId; const fragmentType: string = Object.keys(msg)[0]; let replyContent = null; if ( !TEXT_TYPES.includes(fragmentType) && fragmentType !== 'image' && fragmentType !== 'reply' ) { replyContent = ( {capitalizeFirstLetter(fragmentType)} ); } else { // TODO flesh out the image case with the following text if (fragmentType === 'image') { // take out precalculated height and width (msg as FragmentImageType).metadata = {}; } replyContent = renderFragment(id, msg, index, replyAuthor); } return ( onReplyClick?.(replyId)} > {replyAuthor} {fragmentType === 'image' && ( Image )} {replyContent} ); case 'tab': const { url, favicon, title } = (fragment as FragmentTabType).tab; return ( { window.open(url, '_blank'); }} /> ); case 'ur-link': return `<${(fragment as FragmentUrLinkType)['ur-link']}>`; case 'break': return ; default: // return fragment[key].data; return ''; } };