From 8a7e78485dac8e8aad2178dc79f41c26350f2b2f Mon Sep 17 00:00:00 2001 From: xmflsct Date: Sat, 3 Dec 2022 23:10:20 +0100 Subject: [PATCH] Add hashtag sparkline --- src/components/Hashtag.tsx | 47 ++++++++++---- src/components/Sparkline.tsx | 86 +++++++++++++++++++++++++ src/screens/Compose/Root/Suggestion.tsx | 2 +- src/screens/Tabs/Shared/Search.tsx | 4 +- 4 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 src/components/Sparkline.tsx diff --git a/src/components/Hashtag.tsx b/src/components/Hashtag.tsx index aebb1adc..721dd73f 100644 --- a/src/components/Hashtag.tsx +++ b/src/components/Hashtag.tsx @@ -3,38 +3,61 @@ 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, { useCallback } from 'react' -import { Pressable } from 'react-native' +import { sumBy } from 'lodash' +import React, { useCallback, useState } from 'react' +import { Dimensions, Pressable, View } from 'react-native' +import Sparkline from './Sparkline' import CustomText from './Text' export interface Props { hashtag: Mastodon.Tag onPress?: () => void - origin?: string } -const ComponentHashtag: React.FC = ({ - hashtag, - onPress: customOnPress, - origin -}) => { +const ComponentHashtag: React.FC = ({ hashtag, onPress: customOnPress }) => { const { colors } = useTheme() - const navigation = - useNavigation>() + const navigation = useNavigation>() const onPress = useCallback(() => { navigation.push('Tab-Shared-Hashtag', { hashtag: hashtag.name }) }, []) + const padding = StyleConstants.Spacing.S * 1.5 + const width = Dimensions.get('window').width / 4 + const [height, setHeight] = useState(0) + return ( setHeight(height - padding * 2 - 1)} > - + #{hashtag.name} + parseInt(h.uses)).reverse()} + width={width} + height={height} + /> ) } diff --git a/src/components/Sparkline.tsx b/src/components/Sparkline.tsx new file mode 100644 index 00000000..f0eb6aa0 --- /dev/null +++ b/src/components/Sparkline.tsx @@ -0,0 +1,86 @@ +import { useTheme } from '@utils/styles/ThemeManager' +import { maxBy, minBy } from 'lodash' +import React from 'react' +import Svg, { G, Path } from 'react-native-svg' + +export interface Props { + data: number[] + width: number + height: number + margin?: number +} + +const Sparkline: React.FC = ({ data, width, height, margin = 0 }) => { + const { colors } = useTheme() + + const dataToPoints = ({ + data, + width, + height + }: { + data: number[] + width: number + height: number + }): { x: number; y: number }[] => { + const max = maxBy(data) || 0 + const min = minBy(data) || 0 + const len = data.length + + const vfactor = (height - margin * 2) / (max - min || 2) + const hfactor = (width - margin * 2) / (len - (len > 1 ? 1 : 0)) + + return data.map((d, i) => ({ + x: i * hfactor + margin, + y: (max === min ? 1 : max - d) * vfactor + margin + })) + } + const points = dataToPoints({ data, width, height }) + const divisor = 0.25 + let prev: { x: number; y: number } + const curve = (p: { x: number; y: number }) => { + let res + if (!prev) { + res = [p.x, p.y] + } else { + const len = (p.x - prev.x) * divisor + res = [ + 'C', + prev.x + len, // x1 + prev.y, // y1 + p.x - len, // x2 + p.y, // y2 + p.x, // x + p.y // y + ] + } + prev = p + return res + } + const linePoints = points.map(p => curve(p)).reduce((a, b) => a.concat(b)) + const closePolyPoints = [ + 'L' + points[points.length - 1].x, + height - margin, + margin, + height - margin, + margin, + points[0].y + ] + const fillPoints = linePoints.concat(closePolyPoints) + + return ( + + + + + + + ) +} + +export default Sparkline diff --git a/src/screens/Compose/Root/Suggestion.tsx b/src/screens/Compose/Root/Suggestion.tsx index c56078b0..0c317c9b 100644 --- a/src/screens/Compose/Root/Suggestion.tsx +++ b/src/screens/Compose/Root/Suggestion.tsx @@ -46,7 +46,7 @@ const ComposeRootSuggestion: React.FC = ({ item }) => { return item.acct ? ( ) : ( - + ) } diff --git a/src/screens/Tabs/Shared/Search.tsx b/src/screens/Tabs/Shared/Search.tsx index 72843e40..c5a7cd43 100644 --- a/src/screens/Tabs/Shared/Search.tsx +++ b/src/screens/Tabs/Shared/Search.tsx @@ -81,7 +81,7 @@ const TabSharedSearch: React.FC> ) } }) - }, []) + }, [mode]) const mapKeyToTranslations = { accounts: t('shared.search.sections.accounts'), @@ -183,7 +183,7 @@ const TabSharedSearch: React.FC> case 'accounts': return case 'hashtags': - return + return case 'statuses': return default: