diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index b619b144..828ba608 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -168,6 +168,7 @@ export interface Props { highlighted?: boolean disableDetails?: boolean selectable?: boolean + setSpoilerExpanded?: React.Dispatch> } const ParseHTML = React.memo( @@ -183,7 +184,8 @@ const ParseHTML = React.memo( expandHint, highlighted = false, disableDetails = false, - selectable = false + selectable = false, + setSpoilerExpanded }: Props) => { const adaptiveFontsize = useSelector(getSettingsFontsize) const adaptedFontsize = adaptiveScale( @@ -253,6 +255,9 @@ const ParseHTML = React.memo( onPress={() => { layoutAnimation() setExpanded(!expanded) + if (setSpoilerExpanded) { + setSpoilerExpanded(!expanded) + } }} style={{ flexDirection: 'row', diff --git a/src/components/Timeline/Conversation.tsx b/src/components/Timeline/Conversation.tsx index 5dbd3d2d..b3311a28 100644 --- a/src/components/Timeline/Conversation.tsx +++ b/src/components/Timeline/Conversation.tsx @@ -4,90 +4,52 @@ import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' -import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { isEqual } from 'lodash' import React, { useCallback } from 'react' import { Pressable, View } from 'react-native' import { useMutation, useQueryClient } from 'react-query' -import { useSelector } from 'react-redux' import TimelineActions from './Shared/Actions' import TimelineContent from './Shared/Content' +import StatusContext from './Shared/Context' import TimelineHeaderConversation from './Shared/HeaderConversation' import TimelinePoll from './Shared/Poll' -const Avatars: React.FC<{ accounts: Mastodon.Account[] }> = ({ accounts }) => { - return ( - - {accounts.slice(0, 4).map(account => ( - 2 - ? StyleConstants.Avatar.M / 2 - : StyleConstants.Avatar.M - }} - style={{ flex: 1, flexBasis: '50%' }} - /> - ))} - - ) -} - export interface Props { conversation: Mastodon.Conversation queryKey: QueryKeyTimeline highlighted?: boolean } -const TimelineConversation = React.memo( - ({ conversation, queryKey, highlighted = false }: Props) => { - const instanceAccount = useSelector( - getInstanceAccount, - (prev, next) => prev?.id === next?.id - ) - const { colors } = useTheme() +const TimelineConversation: React.FC = ({ conversation, queryKey, highlighted = false }) => { + const { colors } = useTheme() - const queryClient = useQueryClient() - const fireMutation = useCallback(() => { - return apiInstance({ - method: 'post', - url: `conversations/${conversation.id}/read` - }) - }, []) - const { mutate } = useMutation(fireMutation, { - onSettled: () => { - queryClient.invalidateQueries(queryKey) - } + const queryClient = useQueryClient() + const fireMutation = useCallback(() => { + return apiInstance({ + method: 'post', + url: `conversations/${conversation.id}/read` }) + }, []) + const { mutate } = useMutation(fireMutation, { + onSettled: () => { + queryClient.invalidateQueries(queryKey) + } + }) - const navigation = - useNavigation>() - const onPress = useCallback(() => { - if (conversation.last_status) { - conversation.unread && mutate() - navigation.push('Tab-Shared-Toot', { - toot: conversation.last_status, - rootQueryKey: queryKey - }) - } - }, []) + const navigation = useNavigation>() + const onPress = useCallback(() => { + if (conversation.last_status) { + conversation.unread && mutate() + navigation.push('Tab-Shared-Toot', { + toot: conversation.last_status, + rootQueryKey: queryKey + }) + } + }, []) - return ( + return ( + - - + + {conversation.accounts.slice(0, 4).map(account => ( + 2 + ? StyleConstants.Avatar.M / 2 + : StyleConstants.Avatar.M + }} + style={{ flex: 1, flexBasis: '50%' }} + /> + ))} + + {conversation.last_status ? ( @@ -120,40 +102,19 @@ const TimelineConversation = React.memo( - - {conversation.last_status.poll ? ( - - ) : null} + + - account.acct)} - reblog={false} - /> + + ) : null} - ) - }, - (prev, next) => isEqual(prev.conversation, next.conversation) -) + + ) +} export default TimelineConversation diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 7660f6a3..65b9d33d 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -16,11 +16,11 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { uniqBy } from 'lodash' -import React, { useRef } from 'react' +import React, { useRef, useState } from 'react' import { Pressable, StyleProp, View, ViewStyle } from 'react-native' import { useSelector } from 'react-redux' import * as ContextMenu from 'zeego/context-menu' +import StatusContext from './Shared/Context' import TimelineFeedback from './Shared/Feedback' import TimelineFiltered, { shouldFilter } from './Shared/Filtered' import TimelineFullConversation from './Shared/FullConversation' @@ -46,31 +46,28 @@ const TimelineDefault: React.FC = ({ disableOnPress = false }) => { const { colors } = useTheme() - const instanceAccount = useSelector(getInstanceAccount, () => true) const navigation = useNavigation>() - const actualStatus = item.reblog ? item.reblog : item - - const ownAccount = actualStatus.account?.id === instanceAccount?.id + const instanceAccount = useSelector(getInstanceAccount, () => true) + const status = item.reblog ? item.reblog : item + const ownAccount = status.account?.id === instanceAccount?.id + const [spoilerExpanded, setSpoilerExpanded] = useState( + instanceAccount.preferences['reading:expand:spoilers'] || false + ) + const spoilerHidden = status.spoiler_text?.length + ? !instanceAccount.preferences['reading:expand:spoilers'] && !spoilerExpanded + : false const copiableContent = useRef<{ content: string; complete: boolean }>({ content: '', complete: false }) - const filtered = queryKey && shouldFilter({ copiableContent, status: actualStatus, queryKey }) + const filtered = queryKey && shouldFilter({ copiableContent, status, queryKey }) if (queryKey && filtered && !highlighted) { return } - const onPress = () => { - if (highlighted) return - navigation.push('Tab-Shared-Toot', { - toot: actualStatus, - rootQueryKey: queryKey - }) - } - const mainStyle: StyleProp = { padding: StyleConstants.Spacing.Global.PagePadding, backgroundColor: colors.backgroundDefault, @@ -79,24 +76,14 @@ const TimelineDefault: React.FC = ({ const main = () => ( <> {item.reblog ? ( - + ) : item._pinned ? ( - + ) : null} - - + + = ({ paddingLeft: highlighted ? 0 : StyleConstants.Avatar.M + StyleConstants.Spacing.S }} > - {typeof actualStatus.content === 'string' && actualStatus.content.length > 0 ? ( - - ) : null} - {queryKey && actualStatus.poll ? ( - - ) : null} - {!disableDetails && - Array.isArray(actualStatus.media_attachments) && - actualStatus.media_attachments.length ? ( - - ) : null} - {!disableDetails && actualStatus.card ? : null} - {!disableDetails ? ( - - ) : null} - - + + + + + + + - {queryKey && !disableDetails ? ( - d?.id !== instanceAccount?.id), - d => d?.id - ).map(d => d?.acct)} - reblog={item.reblog ? true : false} - /> - ) : null} + ) const mShare = menuShare({ - visibility: actualStatus.visibility, + visibility: status.visibility, type: 'status', - url: actualStatus.url || actualStatus.uri, + url: status.url || status.uri, copiableContent }) - const mStatus = menuStatus({ status: actualStatus, queryKey, rootQueryKey }) - const mInstance = menuInstance({ status: actualStatus, queryKey, rootQueryKey }) + const mStatus = menuStatus({ status, queryKey, rootQueryKey }) + const mInstance = menuInstance({ status, queryKey, rootQueryKey }) - return disableOnPress ? ( - {main()} - ) : ( - <> - - - {}} - children={main()} - /> - + return ( + + {disableOnPress ? ( + {main()} + ) : ( + <> + + + + navigation.push('Tab-Shared-Toot', { + toot: status, + rootQueryKey: queryKey + }) + } + onLongPress={() => {}} + children={main()} + /> + - - {mShare.map((mGroup, index) => ( - - {mGroup.map(menu => ( - - - - + + {mShare.map((mGroup, index) => ( + + {mGroup.map(menu => ( + + + + + ))} + ))} - - ))} - {mStatus.map((mGroup, index) => ( - - {mGroup.map(menu => ( - - - - + {mStatus.map((mGroup, index) => ( + + {mGroup.map(menu => ( + + + + + ))} + ))} - - ))} - {mInstance.map((mGroup, index) => ( - - {mGroup.map(menu => ( - - - - + {mInstance.map((mGroup, index) => ( + + {mGroup.map(menu => ( + + + + + ))} + ))} - - ))} - - - - + + + + + )} + ) } diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index 848896e2..802ae3d5 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -16,11 +16,11 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import { uniqBy } from 'lodash' -import React, { useCallback, useRef } from 'react' +import React, { useCallback, useRef, useState } from 'react' import { Pressable, View } from 'react-native' import { useSelector } from 'react-redux' import * as ContextMenu from 'zeego/context-menu' +import StatusContext from './Shared/Context' import TimelineFiltered, { shouldFilter } from './Shared/Filtered' import TimelineFullConversation from './Shared/FullConversation' import TimelineHeaderAndroid from './Shared/HeaderAndroid' @@ -36,6 +36,17 @@ const TimelineNotifications: React.FC = ({ queryKey, highlighted = false }) => { + const instanceAccount = useSelector(getInstanceAccount, () => true) + + const status = notification.status + const account = notification.status ? notification.status.account : notification.account + const ownAccount = notification.account?.id === instanceAccount?.id + const [spoilerExpanded, setSpoilerExpanded] = useState( + instanceAccount.preferences['reading:expand:spoilers'] || false + ) + const spoilerHidden = notification.status?.spoiler_text?.length + ? !instanceAccount.preferences['reading:expand:spoilers'] && !spoilerExpanded + : false const copiableContent = useRef<{ content: string; complete: boolean }>({ content: '', complete: false @@ -53,11 +64,8 @@ const TimelineNotifications: React.FC = ({ } const { colors } = useTheme() - const instanceAccount = useSelector(getInstanceAccount, (prev, next) => prev?.id === next?.id) const navigation = useNavigation>() - const actualAccount = notification.status ? notification.status.account : notification.account - const onPress = useCallback(() => { notification.status && navigation.push('Tab-Shared-Toot', { @@ -70,11 +78,7 @@ const TimelineNotifications: React.FC = ({ return ( <> {notification.type !== 'mention' ? ( - + ) : null} = ({ }} > - - + + {notification.status ? ( @@ -100,41 +104,16 @@ const TimelineNotifications: React.FC = ({ paddingLeft: highlighted ? 0 : StyleConstants.Avatar.M + StyleConstants.Spacing.S }} > - {notification.status.content.length > 0 ? ( - - ) : null} - {notification.status.poll ? ( - - ) : null} - {notification.status.media_attachments.length > 0 ? ( - - ) : null} - {notification.status.card ? : null} - + + + + + ) : null} - {notification.status ? ( - d?.id !== instanceAccount?.id), - d => d?.id - ).map(d => d?.acct)} - reblog={false} - /> - ) : null} + ) } @@ -149,7 +128,17 @@ const TimelineNotifications: React.FC = ({ const mInstance = menuInstance({ status: notification.status, queryKey }) return ( - <> + = ({ ))} - - + + ) } diff --git a/src/components/Timeline/Shared/Actioned.tsx b/src/components/Timeline/Shared/Actioned.tsx index 32390eae..2a5c8f86 100644 --- a/src/components/Timeline/Shared/Actioned.tsx +++ b/src/components/Timeline/Shared/Actioned.tsx @@ -5,163 +5,164 @@ import { StackNavigationProp } from '@react-navigation/stack' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React from 'react' +import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' import { Pressable, StyleSheet, View } from 'react-native' +import StatusContext from './Context' export interface Props { - account: Mastodon.Account - action: Mastodon.Notification['type'] | ('reblog' | 'pinned') - notification?: boolean + action: Mastodon.Notification['type'] | 'reblog' | 'pinned' + isNotification?: boolean + account?: Mastodon.Account } -const TimelineActioned = React.memo( - ({ account, action, notification = false }: Props) => { - const { t } = useTranslation('componentTimeline') - const { colors } = useTheme() - const navigation = useNavigation>() - const name = account?.display_name || account?.username - const iconColor = colors.primaryDefault +const TimelineActioned: React.FC = ({ action, isNotification, ...rest }) => { + const { status } = useContext(StatusContext) + const account = isNotification ? rest.account : status?.account + if (!status || !account) return null - const content = (content: string) => ( - - ) + const { t } = useTranslation('componentTimeline') + const { colors } = useTheme() + const navigation = useNavigation>() + const name = account?.display_name || account?.username + const iconColor = colors.primaryDefault - const onPress = () => navigation.push('Tab-Shared-Account', { account }) + const content = (content: string) => ( + + ) - const children = () => { - switch (action) { - case 'pinned': - return ( - <> - - {content(t('shared.actioned.pinned'))} - - ) - case 'favourite': - return ( - <> - - - {content(t('shared.actioned.favourite', { name }))} - - - ) - case 'follow': - return ( - <> - - - {content(t('shared.actioned.follow', { name }))} - - - ) - case 'follow_request': - return ( - <> - - - {content(t('shared.actioned.follow_request', { name }))} - - - ) - case 'poll': - return ( - <> - - {content(t('shared.actioned.poll'))} - - ) - case 'reblog': - return ( - <> - - - {content( - notification - ? t('shared.actioned.reblog.notification', { name }) - : t('shared.actioned.reblog.default', { name }) - )} - - - ) - case 'status': - return ( - <> - - - {content(t('shared.actioned.status', { name }))} - - - ) - case 'update': - return ( - <> - - {content(t('shared.actioned.update'))} - - ) - default: - return <> - } + const onPress = () => navigation.push('Tab-Shared-Account', { account }) + + const children = () => { + switch (action) { + case 'pinned': + return ( + <> + + {content(t('shared.actioned.pinned'))} + + ) + case 'favourite': + return ( + <> + + + {content(t('shared.actioned.favourite', { name }))} + + + ) + case 'follow': + return ( + <> + + + {content(t('shared.actioned.follow', { name }))} + + + ) + case 'follow_request': + return ( + <> + + + {content(t('shared.actioned.follow_request', { name }))} + + + ) + case 'poll': + return ( + <> + + {content(t('shared.actioned.poll'))} + + ) + case 'reblog': + return ( + <> + + + {content( + isNotification + ? t('shared.actioned.reblog.notification', { name }) + : t('shared.actioned.reblog.default', { name }) + )} + + + ) + case 'status': + return ( + <> + + + {content(t('shared.actioned.status', { name }))} + + + ) + case 'update': + return ( + <> + + {content(t('shared.actioned.update'))} + + ) + default: + return <> } + } - return ( - - {children()} - - ) - }, - () => true -) + return ( + + ) +} const styles = StyleSheet.create({ icon: { diff --git a/src/components/Timeline/Shared/Actions.tsx b/src/components/Timeline/Shared/Actions.tsx index 7300935d..511cbaa9 100644 --- a/src/components/Timeline/Shared/Actions.tsx +++ b/src/components/Timeline/Shared/Actions.tsx @@ -10,32 +10,22 @@ import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline' +import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useCallback, useMemo } from 'react' +import { uniqBy } from 'lodash' +import React, { useCallback, useContext, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Pressable, StyleSheet, View } from 'react-native' import { useQueryClient } from 'react-query' +import { useSelector } from 'react-redux' +import StatusContext from './Context' -export interface Props { - queryKey: QueryKeyTimeline - rootQueryKey?: QueryKeyTimeline - highlighted: boolean - status: Mastodon.Status - ownAccount?: boolean - accts: Mastodon.Account['acct'][] // When replying to conversations - reblog: boolean -} +const TimelineActions: React.FC = () => { + const { queryKey, rootQueryKey, status, isReblog, ownAccount, highlighted, disableDetails } = + useContext(StatusContext) + if (!queryKey || !status || disableDetails) return null -const TimelineActions: React.FC = ({ - queryKey, - rootQueryKey, - highlighted, - status, - ownAccount = false, - accts, - reblog -}) => { const navigation = useNavigation>() const { t } = useTranslation('componentTimeline') const { colors, theme } = useTheme() @@ -83,16 +73,21 @@ const TimelineActions: React.FC = ({ } }) - const onPressReply = useCallback( - () => - navigation.navigate('Screen-Compose', { - type: 'reply', - incomingStatus: status, - accts, - queryKey - }), - [status.replies_count] - ) + const instanceAccount = useSelector(getInstanceAccount, () => true) + const onPressReply = useCallback(() => { + const accts = uniqBy( + ([status.account] as Mastodon.Account[] & Mastodon.Mention[]) + .concat(status.mentions) + .filter(d => d?.id !== instanceAccount?.id), + d => d?.id + ).map(d => d?.acct) + navigation.navigate('Screen-Compose', { + type: 'reply', + incomingStatus: status, + accts, + queryKey + }) + }, [status.replies_count]) const { showActionSheetWithOptions } = useActionSheet() const onPressReblog = useCallback(() => { if (!status.reblogged) { @@ -114,7 +109,7 @@ const TimelineActions: React.FC = ({ queryKey, rootQueryKey, id: status.id, - reblog, + isReblog, payload: { property: 'reblogged', currentValue: status.reblogged, @@ -130,7 +125,7 @@ const TimelineActions: React.FC = ({ queryKey, rootQueryKey, id: status.id, - reblog, + isReblog, payload: { property: 'reblogged', currentValue: status.reblogged, @@ -149,7 +144,7 @@ const TimelineActions: React.FC = ({ queryKey, rootQueryKey, id: status.id, - reblog, + isReblog, payload: { property: 'reblogged', currentValue: status.reblogged, @@ -166,7 +161,7 @@ const TimelineActions: React.FC = ({ queryKey, rootQueryKey, id: status.id, - reblog, + isReblog, payload: { property: 'favourited', currentValue: status.favourited, @@ -181,7 +176,7 @@ const TimelineActions: React.FC = ({ queryKey, rootQueryKey, id: status.id, - reblog, + isReblog, payload: { property: 'bookmarked', currentValue: status.bookmarked, diff --git a/src/components/Timeline/Shared/Attachment.tsx b/src/components/Timeline/Shared/Attachment.tsx index 60b98098..4ff5ab84 100644 --- a/src/components/Timeline/Shared/Attachment.tsx +++ b/src/components/Timeline/Shared/Attachment.tsx @@ -10,51 +10,140 @@ import { RootStackParamList } from '@utils/navigation/navigators' import { getInstanceAccount } from '@utils/slices/instancesSlice' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' -import React, { useState } from 'react' +import React, { useContext, useState } from 'react' import { useTranslation } from 'react-i18next' import { Pressable, View } from 'react-native' import { useSelector } from 'react-redux' +import StatusContext from './Context' -export interface Props { - status: Pick -} +const TimelineAttachment = () => { + const { status, disableDetails } = useContext(StatusContext) + if ( + !status || + disableDetails || + !Array.isArray(status.media_attachments) || + !status.media_attachments.length + ) + return null -const TimelineAttachment = React.memo( - ({ status }: Props) => { - const { t } = useTranslation('componentTimeline') + const { t } = useTranslation('componentTimeline') - const account = useSelector( - getInstanceAccount, - (prev, next) => - prev.preferences['reading:expand:media'] === next.preferences['reading:expand:media'] - ) - const defaultSensitive = () => { - switch (account.preferences['reading:expand:media']) { - case 'show_all': - return false - case 'hide_all': - return true - default: - return status.sensitive - } + const account = useSelector( + getInstanceAccount, + (prev, next) => + prev.preferences['reading:expand:media'] === next.preferences['reading:expand:media'] + ) + const defaultSensitive = () => { + switch (account.preferences['reading:expand:media']) { + case 'show_all': + return false + case 'hide_all': + return true + default: + return status.sensitive } - const [sensitiveShown, setSensitiveShown] = useState(defaultSensitive()) + } + const [sensitiveShown, setSensitiveShown] = useState(defaultSensitive()) - // @ts-ignore - const imageUrls: RootStackParamList['Screen-ImagesViewer']['imageUrls'] = - status.media_attachments - .map(attachment => { + // @ts-ignore + const imageUrls: RootStackParamList['Screen-ImagesViewer']['imageUrls'] = status.media_attachments + .map(attachment => { + switch (attachment.type) { + case 'image': + return { + id: attachment.id, + preview_url: attachment.preview_url, + url: attachment.url, + remote_url: attachment.remote_url, + blurhash: attachment.blurhash, + width: attachment.meta?.original?.width, + height: attachment.meta?.original?.height + } + default: + if ( + attachment.preview_url?.endsWith('.jpg') || + attachment.preview_url?.endsWith('.jpeg') || + attachment.preview_url?.endsWith('.png') || + attachment.preview_url?.endsWith('.gif') || + attachment.remote_url?.endsWith('.jpg') || + attachment.remote_url?.endsWith('.jpeg') || + attachment.remote_url?.endsWith('.png') || + attachment.remote_url?.endsWith('.gif') + ) { + return { + id: attachment.id, + preview_url: attachment.preview_url, + url: attachment.url, + remote_url: attachment.remote_url, + blurhash: attachment.blurhash, + width: attachment.meta?.original?.width, + height: attachment.meta?.original?.height + } + } + } + }) + .filter(i => i) + const navigation = useNavigation>() + const navigateToImagesViewer = (id: string) => { + navigation.navigate('Screen-ImagesViewer', { imageUrls, id }) + } + + return ( + + + {status.media_attachments.map((attachment, index) => { switch (attachment.type) { case 'image': - return { - id: attachment.id, - preview_url: attachment.preview_url, - url: attachment.url, - remote_url: attachment.remote_url, - blurhash: attachment.blurhash, - width: attachment.meta?.original?.width, - height: attachment.meta?.original?.height - } + return ( + + ) + case 'video': + return ( + + ) + case 'gifv': + return ( + + ) + case 'audio': + return ( + + ) default: if ( attachment.preview_url?.endsWith('.jpg') || @@ -66,176 +155,74 @@ const TimelineAttachment = React.memo( attachment.remote_url?.endsWith('.png') || attachment.remote_url?.endsWith('.gif') ) { - return { - id: attachment.id, - preview_url: attachment.preview_url, - url: attachment.url, - remote_url: attachment.remote_url, - blurhash: attachment.blurhash, - width: attachment.meta?.original?.width, - height: attachment.meta?.original?.height - } - } - } - }) - .filter(i => i) - const navigation = useNavigation>() - const navigateToImagesViewer = (id: string) => { - navigation.navigate('Screen-ImagesViewer', { imageUrls, id }) - } - - return ( - - - {status.media_attachments.map((attachment, index) => { - switch (attachment.type) { - case 'image': return ( ) - case 'video': + } else { return ( - ) - case 'gifv': - return ( - - ) - case 'audio': - return ( - - ) - default: - if ( - attachment.preview_url?.endsWith('.jpg') || - attachment.preview_url?.endsWith('.jpeg') || - attachment.preview_url?.endsWith('.png') || - attachment.preview_url?.endsWith('.gif') || - attachment.remote_url?.endsWith('.jpg') || - attachment.remote_url?.endsWith('.jpeg') || - attachment.remote_url?.endsWith('.png') || - attachment.remote_url?.endsWith('.gif') - ) { - return ( - - ) - } else { - return ( - - ) - } - } - })} - + } + } + })} + - {defaultSensitive() && - (sensitiveShown ? ( - -