2020-10-30 20:34:04 +00:00
// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2020-08-27 00:07:26 +00:00
import * as React from 'react' ;
2023-04-10 16:31:45 +00:00
import { action } from '@storybook/addon-actions' ;
2023-10-11 19:06:43 +00:00
import type { Meta } from '@storybook/react' ;
2021-10-26 19:15:33 +00:00
import type { Props } from './MessageBody' ;
import { MessageBody } from './MessageBody' ;
2021-09-18 00:30:08 +00:00
import { setupI18n } from '../../util/setupI18n' ;
2020-08-27 00:07:26 +00:00
import enMessages from '../../../_locales/en/messages.json' ;
2023-04-10 16:31:45 +00:00
import { BodyRange } from '../../types/BodyRange' ;
2023-08-10 16:43:33 +00:00
import { generateAci } from '../../types/ServiceId' ;
2023-04-10 16:31:45 +00:00
import { RenderLocation } from './MessageTextRenderer' ;
2020-09-14 19:51:27 +00:00
2023-08-10 16:43:33 +00:00
const SERVICE_ID_1 = generateAci ( ) ;
const SERVICE_ID_2 = generateAci ( ) ;
const SERVICE_ID_3 = generateAci ( ) ;
const SERVICE_ID_4 = generateAci ( ) ;
const SERVICE_ID_5 = generateAci ( ) ;
const SERVICE_ID_6 = generateAci ( ) ;
const SERVICE_ID_7 = generateAci ( ) ;
const SERVICE_ID_8 = generateAci ( ) ;
const SERVICE_ID_9 = generateAci ( ) ;
const SERVICE_ID_10 = generateAci ( ) ;
const SERVICE_ID_11 = generateAci ( ) ;
2020-08-27 00:07:26 +00:00
const i18n = setupI18n ( 'en' , enMessages ) ;
2022-06-07 00:48:02 +00:00
export default {
title : 'Components/Conversation/MessageBody' ,
2023-10-11 19:06:43 +00:00
} satisfies Meta < Props > ;
2020-08-27 00:07:26 +00:00
const createProps = ( overrideProps : Partial < Props > = { } ) : Props = > ( {
2020-09-16 22:42:48 +00:00
bodyRanges : overrideProps.bodyRanges ,
2023-04-10 16:31:45 +00:00
disableJumbomoji : overrideProps.disableJumbomoji || false ,
disableLinks : overrideProps.disableLinks || false ,
2020-09-16 22:42:48 +00:00
direction : 'incoming' ,
2020-08-27 00:07:26 +00:00
i18n ,
2023-05-10 00:40:19 +00:00
isSpoilerExpanded : overrideProps.isSpoilerExpanded || { } ,
2023-04-10 16:31:45 +00:00
onExpandSpoiler : overrideProps.onExpandSpoiler || action ( 'onExpandSpoiler' ) ,
renderLocation : RenderLocation.Timeline ,
showConversation :
overrideProps . showConversation || action ( 'showConversation' ) ,
text : overrideProps.text || '' ,
2022-05-23 23:07:41 +00:00
textAttachment : overrideProps.textAttachment || {
2023-04-10 16:31:45 +00:00
pending : false ,
2022-05-23 23:07:41 +00:00
} ,
2020-08-27 00:07:26 +00:00
} ) ;
2022-11-18 00:45:19 +00:00
export function LinksEnabled ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
text : 'Check out https://www.signal.org' ,
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-08-27 00:07:26 +00:00
2022-11-18 00:45:19 +00:00
export function LinksDisabled ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
disableLinks : true ,
text : 'Check out https://www.signal.org' ,
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-08-27 00:07:26 +00:00
2022-11-18 00:45:19 +00:00
export function EmojiSizeBasedOnCount ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( ) ;
return (
< >
< MessageBody { ...props } text = "😹" / >
< br / >
< MessageBody { ...props } text = "😹😹😹" / >
< br / >
< MessageBody { ...props } text = "😹😹😹😹😹" / >
< br / >
< MessageBody { ...props } text = "😹😹😹😹😹😹😹" / >
< br / >
< MessageBody { ...props } text = "😹😹😹😹😹😹😹😹😹" / >
< / >
) ;
2022-11-18 00:45:19 +00:00
}
2020-08-27 00:07:26 +00:00
2022-11-18 00:45:19 +00:00
export function JumbomojiEnabled ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
text : '😹' ,
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-08-27 00:07:26 +00:00
2022-11-18 00:45:19 +00:00
export function JumbomojiDisabled ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
disableJumbomoji : true ,
text : '😹' ,
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-08-27 00:07:26 +00:00
2022-11-18 00:45:19 +00:00
export function JumbomojiDisabledByText ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
text : 'not a jumbo kitty 😹' ,
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2022-06-07 00:48:02 +00:00
2022-11-18 00:45:19 +00:00
export function TextPending ( ) : JSX . Element {
2020-08-27 00:07:26 +00:00
const props = createProps ( {
text : 'Check out https://www.signal.org' ,
2022-05-23 23:07:41 +00:00
textAttachment : {
pending : true ,
} ,
2020-08-27 00:07:26 +00:00
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-09-16 22:42:48 +00:00
2023-12-18 18:14:59 +00:00
export function MessageTooLong ( ) : JSX . Element {
const props = createProps ( {
text : 'Check out https://www.signal.org' ,
textAttachment : {
wasTooBig : true ,
} ,
} ) ;
return < MessageBody { ...props } / > ;
}
2022-11-18 00:45:19 +00:00
export function Mention ( ) : JSX . Element {
2020-09-16 22:42:48 +00:00
const props = createProps ( {
bodyRanges : [
{
start : 5 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_1 ,
2020-09-16 22:42:48 +00:00
replacementText : 'Bender B Rodriguez 🤖' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2020-09-16 22:42:48 +00:00
} ,
] ,
2021-11-11 22:43:05 +00:00
text : 'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots' ,
2020-09-16 22:42:48 +00:00
} ) ;
return < MessageBody { ...props } / > ;
2022-11-18 00:45:19 +00:00
}
2020-09-16 22:42:48 +00:00
2022-11-18 00:45:19 +00:00
export function MultipleMentions ( ) : JSX . Element {
2020-09-16 22:42:48 +00:00
const props = createProps ( {
2021-03-19 20:37:06 +00:00
// These are intentionally in a mixed order to test how we deal with that
2020-09-16 22:42:48 +00:00
bodyRanges : [
{
start : 2 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_2 ,
2020-09-16 22:42:48 +00:00
replacementText : 'Philip J Fry' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2020-09-16 22:42:48 +00:00
} ,
2021-03-19 20:37:06 +00:00
{
start : 4 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_3 ,
2021-03-19 20:37:06 +00:00
replacementText : 'Professor Farnsworth' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2021-03-19 20:37:06 +00:00
} ,
2020-09-16 22:42:48 +00:00
{
start : 0 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_4 ,
2020-09-16 22:42:48 +00:00
replacementText : 'Yancy Fry' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2020-09-16 22:42:48 +00:00
} ,
] ,
text : '\uFFFC \uFFFC \uFFFC' ,
} ) ;
2023-04-10 16:31:45 +00:00
return (
< >
< MessageBody { ...props } / >
< hr / >
< MessageBody { ...props } disableLinks / >
< / >
) ;
2022-11-18 00:45:19 +00:00
}
2020-09-16 22:42:48 +00:00
2022-11-18 00:45:19 +00:00
export function ComplexMessageBody ( ) : JSX . Element {
2020-09-16 22:42:48 +00:00
const props = createProps ( {
bodyRanges : [
2021-03-19 20:37:06 +00:00
// These are intentionally in a mixed order to test how we deal with that
2020-09-16 22:42:48 +00:00
{
start : 78 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_5 ,
2020-09-16 22:42:48 +00:00
replacementText : 'Acid Burn' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2020-09-16 22:42:48 +00:00
} ,
2021-03-19 20:37:06 +00:00
{
start : 80 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_6 ,
2021-03-19 20:37:06 +00:00
replacementText : 'Cereal Killer' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2021-03-19 20:37:06 +00:00
} ,
2020-09-16 22:42:48 +00:00
{
start : 4 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_6 ,
2020-09-16 22:42:48 +00:00
replacementText : 'Zero Cool' ,
2022-11-10 04:59:36 +00:00
conversationID : 'x' ,
2020-09-16 22:42:48 +00:00
} ,
] ,
direction : 'outgoing' ,
2021-11-11 22:43:05 +00:00
text : 'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC' ,
2020-09-16 22:42:48 +00:00
} ) ;
2023-04-10 16:31:45 +00:00
return (
< >
< MessageBody { ...props } / >
< hr / >
< MessageBody { ...props } disableLinks / >
< / >
) ;
2022-11-18 00:45:19 +00:00
}
2022-06-07 00:48:02 +00:00
2023-04-10 16:31:45 +00:00
export function FormattingBasic ( ) : JSX . Element {
2023-05-10 00:40:19 +00:00
const [ isSpoilerExpanded , setIsSpoilerExpanded ] = React . useState ( { } ) ;
2023-04-10 16:31:45 +00:00
const props = createProps ( {
bodyRanges : [
// Abracadabra
{
start : 36 ,
length : 11 ,
style : BodyRange.Style.BOLD ,
} ,
// Open Sesame
{
start : 46 ,
length : 10 ,
style : BodyRange.Style.ITALIC ,
} ,
// This is the key! And the treasure, too, if we can only get our hands on it!
{
start : 357 ,
length : 75 ,
style : BodyRange.Style.MONOSPACE ,
} ,
// The real magic is to understand which words work, and when, and for what
{
start : 138 ,
length : 73 ,
style : BodyRange.Style.STRIKETHROUGH ,
} ,
// as if the key to the treasure is the treasure!
{
start : 446 ,
length : 46 ,
style : BodyRange.Style.SPOILER ,
} ,
{
start : 110 ,
length : 27 ,
style : BodyRange.Style.NONE ,
} ,
] ,
isSpoilerExpanded ,
2023-05-10 00:40:19 +00:00
onExpandSpoiler : data = > setIsSpoilerExpanded ( data ) ,
2023-04-10 16:31:45 +00:00
text : '… It’ s in words that the magic is – Abracadabra, Open Sesame, and the rest – but the magic words in one story aren’ t magical in the next. The real magic is to understand which words work, and when, and for what; the trick is to learn the trick. … And those words are made from the letters of our alphabet: a couple-dozen squiggles we can draw with the pen. This is the key! And the treasure, too, if we can only get our hands on it! It’ s as if – as if the key to the treasure is the treasure!' ,
} ) ;
return (
< >
< MessageBody { ...props } / >
< hr / >
< MessageBody { ...props } disableLinks / >
< / >
) ;
}
export function FormattingSpoiler ( ) : JSX . Element {
2023-05-10 00:40:19 +00:00
const [ isSpoilerExpanded , setIsSpoilerExpanded ] = React . useState ( { } ) ;
2023-04-10 16:31:45 +00:00
const props = createProps ( {
bodyRanges : [
{
start : 8 ,
2023-08-01 19:06:20 +00:00
length : 60 ,
style : BodyRange.Style.SPOILER ,
} ,
// This is touching, but not overlapping; they should not reveal together
{
start : 68 ,
length : 29 ,
style : BodyRange.Style.SPOILER ,
} ,
// Note: in overlaps, the last spoiler wins
{
start : 94 ,
length : 6 ,
2023-04-10 16:31:45 +00:00
style : BodyRange.Style.SPOILER ,
} ,
{
start : 46 ,
length : 22 ,
style : BodyRange.Style.MONOSPACE ,
} ,
{
start : 72 ,
length : 12 ,
style : BodyRange.Style.BOLD ,
} ,
{
start : 90 ,
length : 7 ,
style : BodyRange.Style.ITALIC ,
} ,
{
start : 54 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_7 ,
2023-04-10 16:31:45 +00:00
conversationID : 'a' ,
replacementText : '🅰️ Alice' ,
} ,
{
start : 60 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_8 ,
2023-04-10 16:31:45 +00:00
conversationID : 'b' ,
replacementText : '🅱️ Bob' ,
} ,
] ,
isSpoilerExpanded ,
2023-05-10 00:40:19 +00:00
onExpandSpoiler : data = > setIsSpoilerExpanded ( data ) ,
2023-04-10 16:31:45 +00:00
text : "This is a very secret https://somewhere.com 💡 thing, \uFFFC and \uFFFC, that you shouldn't be able to read. Stay away!" ,
} ) ;
return (
< >
< MessageBody { ...props } / >
< hr / >
< MessageBody { ...props } disableLinks / >
< hr / >
2023-08-01 19:06:20 +00:00
< MessageBody
{ . . . props }
onExpandSpoiler = { ( ) = > null }
isSpoilerExpanded = { { } }
/ >
2023-04-10 16:31:45 +00:00
< hr / >
2023-05-10 00:40:19 +00:00
< MessageBody { ...props } disableLinks isSpoilerExpanded = { { } } / >
2023-04-10 16:31:45 +00:00
< / >
) ;
}
export function FormattingNesting ( ) : JSX . Element {
const props = createProps ( {
bodyRanges : [
{
start : 0 ,
length : 40 ,
style : BodyRange.Style.BOLD ,
} ,
{
start : 0 ,
length : 111 ,
style : BodyRange.Style.ITALIC ,
} ,
{
start : 40 ,
length : 60 ,
style : BodyRange.Style.STRIKETHROUGH ,
} ,
{
start : 64 ,
length : 14 ,
style : BodyRange.Style.MONOSPACE ,
} ,
{
start : 29 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_7 ,
2023-04-10 16:31:45 +00:00
conversationID : 'a' ,
replacementText : '🅰️ Alice' ,
} ,
{
start : 61 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_8 ,
2023-04-10 16:31:45 +00:00
conversationID : 'b' ,
replacementText : '🅱️ Bob' ,
} ,
{
start : 68 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_9 ,
2023-04-10 16:31:45 +00:00
conversationID : 'c' ,
replacementText : 'Charlie' ,
} ,
{
start : 80 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_10 ,
2023-04-10 16:31:45 +00:00
conversationID : 'd' ,
replacementText : 'Dan' ,
} ,
{
start : 105 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_11 ,
2023-04-10 16:31:45 +00:00
conversationID : 'e' ,
replacementText : 'Eve' ,
} ,
] ,
/* eslint-disable max-len */
// m m
// b bs s
// i i
/* eslint-enable max-len */
text : 'Italic Start and Bold Start .\uFFFC. Bold EndStrikethrough Start .\uFFFC. Mono\uFFFCpace Pop! .\uFFFC. Strikethrough End Ital\uFFFCc End' ,
} ) ;
return (
< >
< MessageBody { ...props } / >
< hr / >
< MessageBody { ...props } disableLinks / >
< / >
) ;
}
export function FormattingComplex ( ) : JSX . Element {
2023-05-10 00:40:19 +00:00
const [ isSpoilerExpanded , setIsSpoilerExpanded ] = React . useState ( { } ) ;
2023-04-10 16:31:45 +00:00
const text =
'Computational processes \uFFFC are abstract beings that inhabit computers. ' +
'As they evolve, processes manipulate other abstract things called data. ' +
'The evolution of a process is directed by a pattern of rules called a program. ' +
'People create programs to direct processes. In effect, we conjure the spirits of ' +
'the computer with our spells.\n\n' +
'link preceded by emoji: 🤖https://signal.org/\n\n' +
'link overlapping strikethrough: https://signal.org/ (up to "...//signal")\n\n' +
'strikethrough going through mention \uFFFC all the way' ;
const props = createProps ( {
bodyRanges : [
// mention
{
start : 24 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_3 ,
2023-04-10 16:31:45 +00:00
conversationID : 'x' ,
replacementText : '🤖 Hello' ,
} ,
// bold wraps mention
{
start : 14 ,
length : 31 ,
style : BodyRange.Style.BOLD ,
} ,
// italic overlaps with bold
{
start : 29 ,
length : 39 ,
style : BodyRange.Style.ITALIC ,
} ,
// strikethrough overlaps link
{
start : 397 ,
length : 29 ,
style : BodyRange.Style.STRIKETHROUGH ,
} ,
// strikethrough over mention
{
start : 465 ,
length : 31 ,
style : BodyRange.Style.STRIKETHROUGH ,
} ,
// mention 2
{
start : 491 ,
length : 1 ,
2023-08-16 20:54:39 +00:00
mentionAci : SERVICE_ID_3 ,
2023-04-10 16:31:45 +00:00
conversationID : 'x' ,
replacementText : '🤖 Hello' ,
} ,
] ,
isSpoilerExpanded ,
2023-05-10 00:40:19 +00:00
onExpandSpoiler : data = > setIsSpoilerExpanded ( data ) ,
2023-04-10 16:31:45 +00:00
text ,
} ) ;
return < MessageBody { ...props } / > ;
}
export function ZalgoText ( ) : JSX . Element {
const text = 'T̸͎̆̏̇̊̄͜ͅh̸͙̟͎̯̍͋͜͜i̸̪͚̼̜̦̲̇͒̇͝͝ś̴̡̩͙͜͝ ̴̼̣̩͂͑͠i̸̡̞̯͗s̵͙̔͛͊͑̔ ̶͇̒͝f̴̗͇͙̳͕̅̈́̏̉ò̵̲͉̤̬̖̱ȓ̶̳̫͗͝m̶̗͚̓ą̶̘̳͉̣̿̋t̴͎͎̞̤̱̅̓͝͝t̶̝͊͗é̵̛̥̔̃̀d̸̢̘̹̥̋͆ ̸̘͓͐̓̅̚ẕ̸͉̊̊͝a̴̙̖͎̥̥̅̽́͑͘ͅl̴͔̪͙͔̑̈́g̴͔̝̙̰̊͆̎͌́ǫ̵̪̤̖̖͗̑̎̿̄̎ ̵̪͈̲͇̫̼͌̌͛̚t̸̠́ẽ̴̡̺̖͘x̵͈̰̮͔̃̔͗̑̓͘t' ;
const props = createProps ( {
bodyRanges : [
// This
{
start : 0 ,
length : 39 ,
style : BodyRange.Style.BOLD ,
} ,
// is
{
start : 49 ,
length : 13 ,
style : BodyRange.Style.ITALIC ,
} ,
// formatted
{
start : 65 ,
length : 73 ,
style : BodyRange.Style.STRIKETHROUGH ,
} ,
// zalgo text
{
start : 145 ,
length : 92 ,
style : BodyRange.Style.MONOSPACE ,
} ,
] ,
text ,
} ) ;
return < MessageBody { ...props } / > ;
}