diff --git a/src/@types/mastodon.d.ts b/src/@types/mastodon.d.ts index 6e7d2805..2e1663f7 100644 --- a/src/@types/mastodon.d.ts +++ b/src/@types/mastodon.d.ts @@ -426,6 +426,7 @@ declare namespace Mastodon { reblogs_count: number favourites_count: number replies_count: number + edited_at?: string // FEATURE edit_post favourited: boolean reblogged: boolean muted: boolean @@ -443,6 +444,17 @@ declare namespace Mastodon { text?: string } + type StatusHistory = { + content: Status['content'] + spoiler_text: Status['spoiler_text'] + sensitive: Status['sensitive'] + created_at: Status['created_at'] + poll: Status['poll'] + account: Status['account'] + media_attachments: Status['media_attachments'] + emojis: Status['emojis'] + } + type Source = { // Base note: string diff --git a/src/@types/untyped.d.ts b/src/@types/untyped.d.ts index 9220fed3..e3b7bc1a 100644 --- a/src/@types/untyped.d.ts +++ b/src/@types/untyped.d.ts @@ -4,3 +4,8 @@ declare module 'li' declare module 'react-native-feather' declare module 'react-native-htmlview' declare module 'react-native-toast-message' + +declare module '@helpers/features' { + const features: { feature: string; version: number; reference?: string }[] + export default features +} diff --git a/src/components/Hashtag.tsx b/src/components/Hashtag.tsx index c32a9ef7..fede3ab9 100644 --- a/src/components/Hashtag.tsx +++ b/src/components/Hashtag.tsx @@ -1,5 +1,6 @@ import { useNavigation } from '@react-navigation/native' 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' @@ -19,7 +20,7 @@ const ComponentHashtag: React.FC = ({ }) => { const { colors } = useTheme() const navigation = - useNavigation>() + useNavigation>() const onPress = useCallback(() => { analytics('search_account_press', { page: origin }) diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index 38d88536..518305dd 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -4,6 +4,7 @@ import openLink from '@components/openLink' import ParseEmojis from '@components/Parse/Emojis' import { useNavigation, useRoute } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' +import { TabLocalStackParamList } from '@utils/navigation/navigators' import { getSettingsFontsize } from '@utils/slices/settingsSlice' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' @@ -35,7 +36,7 @@ const renderNode = ({ index: number adaptedFontsize: number adaptedLineheight: number - navigation: StackNavigationProp + navigation: StackNavigationProp mentions?: Mastodon.Mention[] tags?: Mastodon.Tag[] showFullLink: boolean @@ -194,7 +195,7 @@ const ParseHTML = React.memo( ) const navigation = - useNavigation>() + useNavigation>() const route = useRoute() const { colors, theme } = useTheme() const { t, i18n } = useTranslation('componentParse') diff --git a/src/components/Timeline/Conversation.tsx b/src/components/Timeline/Conversation.tsx index 601ba7f9..53378308 100644 --- a/src/components/Timeline/Conversation.tsx +++ b/src/components/Timeline/Conversation.tsx @@ -3,6 +3,7 @@ import analytics from '@components/analytics' import GracefullyImage from '@components/GracefullyImage' 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' @@ -78,7 +79,7 @@ const TimelineConversation: React.FC = ({ }) const navigation = - useNavigation>() + useNavigation>() const onPress = useCallback(() => { analytics('timeline_conversation_press') if (conversation.last_status) { diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 4d02049f..03f325fc 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -9,6 +9,7 @@ import TimelineHeaderDefault from '@components/Timeline/Shared/HeaderDefault' import TimelinePoll from '@components/Timeline/Shared/Poll' 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' @@ -17,7 +18,7 @@ import { uniqBy } from 'lodash' import React, { useCallback } from 'react' import { Pressable, StyleSheet, View } from 'react-native' import { useSelector } from 'react-redux' -import TimelineActionsUsers from './Shared/ActionsUsers' +import TimelineFeedback from './Shared/Feedback' import TimelineFiltered, { shouldFilter } from './Shared/Filtered' import TimelineFullConversation from './Shared/FullConversation' import TimelineTranslate from './Shared/Translate' @@ -45,7 +46,7 @@ const TimelineDefault: React.FC = ({ const { colors } = useTheme() const instanceAccount = useSelector(getInstanceAccount, () => true) const navigation = - useNavigation>() + useNavigation>() const actualStatus = item.reblog ? item.reblog : item @@ -143,7 +144,7 @@ const TimelineDefault: React.FC = ({ ) : null} - + {queryKey && !disableDetails ? ( diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index 14c33f58..5d5693dc 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -9,6 +9,7 @@ import TimelineHeaderNotification from '@components/Timeline/Shared/HeaderNotifi import TimelinePoll from '@components/Timeline/Shared/Poll' 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' @@ -44,7 +45,7 @@ const TimelineNotifications: React.FC = ({ (prev, next) => prev?.id === next?.id ) const navigation = - useNavigation>() + useNavigation>() const actualAccount = notification.status ? notification.status.account diff --git a/src/components/Timeline/Shared/Actioned.tsx b/src/components/Timeline/Shared/Actioned.tsx index c0ce1d26..122b1fae 100644 --- a/src/components/Timeline/Shared/Actioned.tsx +++ b/src/components/Timeline/Shared/Actioned.tsx @@ -3,6 +3,7 @@ import Icon from '@components/Icon' import { ParseEmojis } from '@components/Parse' import { useNavigation } from '@react-navigation/native' 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, useMemo } from 'react' @@ -20,7 +21,7 @@ const TimelineActioned = React.memo( const { t } = useTranslation('componentTimeline') const { colors } = useTheme() const navigation = - useNavigation>() + useNavigation>() const name = account.display_name || account.username const iconColor = colors.primaryDefault diff --git a/src/components/Timeline/Shared/ActionsUsers.tsx b/src/components/Timeline/Shared/ActionsUsers.tsx deleted file mode 100644 index 08ccbf00..00000000 --- a/src/components/Timeline/Shared/ActionsUsers.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import analytics from '@components/analytics' -import { useNavigation } from '@react-navigation/native' -import { StackNavigationProp } from '@react-navigation/stack' -import { StyleConstants } from '@utils/styles/constants' -import { useTheme } from '@utils/styles/ThemeManager' -import React from 'react' -import { useTranslation } from 'react-i18next' -import { StyleSheet, Text, View } from 'react-native' - -export interface Props { - status: Mastodon.Status - highlighted: boolean -} - -const TimelineActionsUsers = React.memo( - ({ status, highlighted }: Props) => { - if (!highlighted) { - return null - } - - const { t } = useTranslation('componentTimeline') - const { colors } = useTheme() - const navigation = - useNavigation>() - - return ( - - {status.reblogs_count > 0 ? ( - { - analytics('timeline_shared_actionsusers_press_boosted', { - count: status.reblogs_count - }) - navigation.push('Tab-Shared-Users', { - reference: 'statuses', - id: status.id, - type: 'reblogged_by', - count: status.reblogs_count - }) - }} - > - {t('shared.actionsUsers.reblogged_by.text', { - count: status.reblogs_count - })} - - ) : null} - {status.favourites_count > 0 ? ( - { - analytics('timeline_shared_actionsusers_press_boosted', { - count: status.favourites_count - }) - navigation.push('Tab-Shared-Users', { - reference: 'statuses', - id: status.id, - type: 'favourited_by', - count: status.favourites_count - }) - }} - > - {t('shared.actionsUsers.favourited_by.text', { - count: status.favourites_count - })} - - ) : null} - - ) - }, - (prev, next) => - prev.status.reblogs_count === next.status.reblogs_count && - prev.status.favourites_count === next.status.favourites_count -) - -const styles = StyleSheet.create({ - base: { - flexDirection: 'row' - }, - text: { - ...StyleConstants.FontStyle.M, - padding: StyleConstants.Spacing.S, - paddingLeft: 0, - marginRight: StyleConstants.Spacing.S - } -}) - -export default TimelineActionsUsers diff --git a/src/components/Timeline/Shared/Attachment.tsx b/src/components/Timeline/Shared/Attachment.tsx index a0444e69..d1a9e322 100644 --- a/src/components/Timeline/Shared/Attachment.tsx +++ b/src/components/Timeline/Shared/Attachment.tsx @@ -6,6 +6,7 @@ import AttachmentImage from '@components/Timeline/Shared/Attachment/Image' import AttachmentUnsupported from '@components/Timeline/Shared/Attachment/Unsupported' import AttachmentVideo from '@components/Timeline/Shared/Attachment/Video' import { useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' import { RootStackParamList } from '@utils/navigation/navigators' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' @@ -38,7 +39,7 @@ const TimelineAttachment = React.memo( const imageUrls = useRef< RootStackParamList['Screen-ImagesViewer']['imageUrls'] >([]) - const navigation = useNavigation() + const navigation = useNavigation>() useEffect(() => { status.media_attachments.forEach((attachment, index) => { switch (attachment.type) { diff --git a/src/components/Timeline/Shared/Avatar.tsx b/src/components/Timeline/Shared/Avatar.tsx index 228a77a6..c4bd9701 100644 --- a/src/components/Timeline/Shared/Avatar.tsx +++ b/src/components/Timeline/Shared/Avatar.tsx @@ -2,6 +2,7 @@ import analytics from '@components/analytics' import GracefullyImage from '@components/GracefullyImage' 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 { StyleConstants } from '@utils/styles/constants' import React, { useCallback } from 'react' @@ -17,7 +18,7 @@ const TimelineAvatar = React.memo( ({ queryKey, account, highlighted }: Props) => { const { t } = useTranslation('componentTimeline') const navigation = - useNavigation>() + useNavigation>() // Need to fix go back root const onPress = useCallback(() => { analytics('timeline_shared_avatar_press', { diff --git a/src/components/Timeline/Shared/Content.tsx b/src/components/Timeline/Shared/Content.tsx index f99f4a04..576f3247 100644 --- a/src/components/Timeline/Shared/Content.tsx +++ b/src/components/Timeline/Shared/Content.tsx @@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' export interface Props { - status: Mastodon.Status + status: Pick & { + mentions?: Mastodon.Status['mentions'] + tags?: Mastodon.Status['tags'] + } numberOfLines?: number highlighted?: boolean disableDetails?: boolean diff --git a/src/components/Timeline/Shared/Feedback.tsx b/src/components/Timeline/Shared/Feedback.tsx new file mode 100644 index 00000000..6aae7492 --- /dev/null +++ b/src/components/Timeline/Shared/Feedback.tsx @@ -0,0 +1,141 @@ +import analytics from '@components/analytics' +import { useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' +import { TabLocalStackParamList } from '@utils/navigation/navigators' +import { useStatusHistory } from '@utils/queryHooks/statusesHistory' +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { StyleSheet, Text, View } from 'react-native' + +export interface Props { + status: Mastodon.Status + highlighted: boolean +} + +const TimelineFeedback = React.memo( + ({ status, highlighted }: Props) => { + if (!highlighted) { + return null + } + + const { t } = useTranslation('componentTimeline') + const { colors } = useTheme() + const navigation = + useNavigation>() + + const { data } = useStatusHistory({ + id: status.id, + options: { enabled: status.edited_at !== undefined } + }) + + return ( + + + {status.reblogs_count > 0 ? ( + { + analytics('timeline_shared_feedback_press_reblog', { + count: status.reblogs_count + }) + navigation.push('Tab-Shared-Users', { + reference: 'statuses', + id: status.id, + type: 'reblogged_by', + count: status.reblogs_count + }) + }} + > + {t('shared.actionsUsers.reblogged_by.text', { + count: status.reblogs_count + })} + + ) : null} + {status.favourites_count > 0 ? ( + { + analytics('timeline_shared_feedback_press_favourite', { + count: status.favourites_count + }) + navigation.push('Tab-Shared-Users', { + reference: 'statuses', + id: status.id, + type: 'favourited_by', + count: status.favourites_count + }) + }} + > + {t('shared.actionsUsers.favourited_by.text', { + count: status.favourites_count + })} + + ) : null} + + + {data && data.length > 1 ? ( + { + analytics('timeline_shared_feedback_press_history', { + count: data.length - 1 + }) + navigation.push('Tab-Shared-History', { id: status.id }) + }} + > + {t('shared.actionsUsers.history.text', { + count: data.length - 1 + })} + + ) : null} + + + ) + }, + (prev, next) => + prev.status.reblogs_count === next.status.reblogs_count && + prev.status.favourites_count === next.status.favourites_count +) + +const styles = StyleSheet.create({ + text: { + ...StyleConstants.FontStyle.M, + padding: StyleConstants.Spacing.S, + paddingLeft: 0, + marginRight: StyleConstants.Spacing.S + } +}) + +export default TimelineFeedback diff --git a/src/components/Timeline/Shared/HeaderConversation.tsx b/src/components/Timeline/Shared/HeaderConversation.tsx index fd0b2083..142b40f2 100644 --- a/src/components/Timeline/Shared/HeaderConversation.tsx +++ b/src/components/Timeline/Shared/HeaderConversation.tsx @@ -103,6 +103,7 @@ const HeaderConversation = React.memo( {conversation.last_status?.created_at ? ( ) : null} diff --git a/src/components/Timeline/Shared/HeaderDefault.tsx b/src/components/Timeline/Shared/HeaderDefault.tsx index f25a09d0..09b5f625 100644 --- a/src/components/Timeline/Shared/HeaderDefault.tsx +++ b/src/components/Timeline/Shared/HeaderDefault.tsx @@ -1,5 +1,7 @@ import Icon from '@components/Icon' import { useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' +import { RootStackParamList } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' @@ -21,7 +23,7 @@ export interface Props { const TimelineHeaderDefault = React.memo( ({ queryKey, rootQueryKey, status }: Props) => { const { t } = useTranslation('componentTimeline') - const navigation = useNavigation() + const navigation = useNavigation>() const { colors } = useTheme() return ( @@ -29,7 +31,10 @@ const TimelineHeaderDefault = React.memo( - + @@ -45,7 +50,6 @@ const TimelineHeaderDefault = React.memo( queryKey, rootQueryKey, status, - url: status.url || status.uri, type: 'status' }) } diff --git a/src/components/Timeline/Shared/HeaderNotification.tsx b/src/components/Timeline/Shared/HeaderNotification.tsx index e12e8fe7..d50ec4de 100644 --- a/src/components/Timeline/Shared/HeaderNotification.tsx +++ b/src/components/Timeline/Shared/HeaderNotification.tsx @@ -4,6 +4,8 @@ import { RelationshipOutgoing } from '@components/Relationship' import { useNavigation } from '@react-navigation/native' +import { StackNavigationProp } from '@react-navigation/stack' +import { RootStackParamList } from '@utils/navigation/navigators' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' @@ -22,7 +24,7 @@ export interface Props { const TimelineHeaderNotification = React.memo( ({ queryKey, notification }: Props) => { - const navigation = useNavigation() + const navigation = useNavigation>() const { colors } = useTheme() const actions = useMemo(() => { @@ -44,8 +46,7 @@ const TimelineHeaderNotification = React.memo( onPress={() => navigation.navigate('Screen-Actions', { queryKey, - status: notification.status, - url: notification.status?.url || notification.status?.uri, + status: notification.status!, type: 'status' }) } @@ -83,7 +84,10 @@ const TimelineHeaderNotification = React.memo( notification.type === 'follow_request') && { withoutName: true })} /> - + {notification.status?.visibility ? ( { + ({ created_at, edited_at }: Props) => { + const { t } = useTranslation('componentTimeline') const { colors } = useTheme() return ( - - - + <> + + + + {edited_at ? ( + + ) : null} + ) }, () => true ) -const styles = StyleSheet.create({ - created_at: { - ...StyleConstants.FontStyle.S - } -}) - export default HeaderSharedCreated diff --git a/src/helpers/features.json b/src/helpers/features.json new file mode 100644 index 00000000..1fc2de40 --- /dev/null +++ b/src/helpers/features.json @@ -0,0 +1,7 @@ +[ + { + "feature": "edit_post", + "version": 3.5, + "reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0" + } +] \ No newline at end of file diff --git a/src/i18n/en/components/timeline.json b/src/i18n/en/components/timeline.json index 6df22065..cf4b5596 100644 --- a/src/i18n/en/components/timeline.json +++ b/src/i18n/en/components/timeline.json @@ -58,6 +58,12 @@ "accessibilityLabel": "{{count}} users have favourited this toot", "accessibilityHint": "Tap to know the users", "text": "$t(screenTabs:shared.users.statuses.favourited_by)" + }, + "history": { + "accessibilityLabel": "This toot has been edited {{count}} times", + "accessibilityHint": "Tap to know view the full history", + "text": "{{count}} edit", + "text_plural": "{{count}} edits" } }, "attachment": { @@ -96,6 +102,9 @@ } }, "application": "Tooted with {{application}}", + "edited": { + "accessibilityLabel": "Toot edited" + }, "muted": { "accessibilityLabel": "Toot muted" }, diff --git a/src/i18n/en/screens/tabs.json b/src/i18n/en/screens/tabs.json index 399daedc..9dc92f8b 100644 --- a/src/i18n/en/screens/tabs.json +++ b/src/i18n/en/screens/tabs.json @@ -331,6 +331,9 @@ "reblogged_by": "{{count}} boosted", "favourited_by": "{{count}} favourited" } + }, + "history": { + "name": "Edit History" } } } \ No newline at end of file diff --git a/src/screens/Tabs/Shared/History.tsx b/src/screens/Tabs/Shared/History.tsx new file mode 100644 index 00000000..c98bb6c8 --- /dev/null +++ b/src/screens/Tabs/Shared/History.tsx @@ -0,0 +1,105 @@ +import Icon from '@components/Icon' +import { ParseEmojis } from '@components/Parse' +import ComponentSeparator from '@components/Separator' +import TimelineAttachment from '@components/Timeline/Shared/Attachment' +import TimelineContent from '@components/Timeline/Shared/Content' +import HeaderSharedCreated from '@components/Timeline/Shared/HeaderShared/Created' +import { TabSharedStackScreenProps } from '@utils/navigation/navigators' +import { useStatusHistory } from '@utils/queryHooks/statusesHistory' +import { StyleConstants } from '@utils/styles/constants' +import { useTheme } from '@utils/styles/ThemeManager' +import React from 'react' +import { Text, View } from 'react-native' +import { ScrollView } from 'react-native-gesture-handler' + +const ContentView = ({ + history, + first, + last +}: { + history: Mastodon.StatusHistory + first: boolean + last: boolean +}) => { + const { colors } = useTheme() + return ( + <> + + + {typeof history.content === 'string' && history.content.length > 0 ? ( + + ) : null} + {history.poll + ? history.poll.options.map((option, index) => ( + + + + + + + + + )) + : null} + {Array.isArray(history.media_attachments) && + history.media_attachments.length ? ( + + ) : null} + + {!last ? : null} + + ) +} + +const TabSharedHistory: React.FC< + TabSharedStackScreenProps<'Tab-Shared-History'> +> = ({ + route: { + params: { id } + } +}) => { + const { data } = useStatusHistory({ id }) + + return ( + + {data && data.length > 0 + ? data + .slice(0) + .reverse() + .map((d, i) => + i !== 0 ? ( + + ) : null + ) + : null} + + ) +} + +export default TabSharedHistory diff --git a/src/screens/Tabs/Shared/Root.tsx b/src/screens/Tabs/Shared/Root.tsx index 87558b83..74b42939 100644 --- a/src/screens/Tabs/Shared/Root.tsx +++ b/src/screens/Tabs/Shared/Root.tsx @@ -4,6 +4,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack' import TabSharedAccount from '@screens/Tabs/Shared/Account' import TabSharedAttachments from '@screens/Tabs/Shared/Attachments' import TabSharedHashtag from '@screens/Tabs/Shared/Hashtag' +import TabSharedHistory from '@screens/Tabs/Shared/History' import TabSharedSearch from '@screens/Tabs/Shared/Search' import TabSharedToot from '@screens/Tabs/Shared/Toot' import TabSharedUsers from '@screens/Tabs/Shared/Users' @@ -95,6 +96,13 @@ const TabSharedRoot = ({ })} /> + + = NativeStackScreenProps +export type RootStackScreenProps = + NativeStackScreenProps export type ScreenComposeStackParamList = { 'Screen-Compose-Root': undefined @@ -90,9 +89,8 @@ export type ScreenTabsStackParamList = { 'Tab-Notifications': NavigatorScreenParams 'Tab-Me': NavigatorScreenParams } -export type ScreenTabsScreenProps< - T extends keyof ScreenTabsStackParamList -> = BottomTabScreenProps +export type ScreenTabsScreenProps = + BottomTabScreenProps export type TabSharedStackParamList = { 'Tab-Shared-Account': { @@ -102,6 +100,9 @@ export type TabSharedStackParamList = { 'Tab-Shared-Hashtag': { hashtag: Mastodon.Tag['name'] } + 'Tab-Shared-History': { + id: Mastodon.Status['id'] + } 'Tab-Shared-Search': { text: string | undefined } 'Tab-Shared-Toot': { toot: Mastodon.Status @@ -121,9 +122,8 @@ export type TabSharedStackParamList = { count: number } } -export type TabSharedStackScreenProps< - T extends keyof TabSharedStackParamList -> = NativeStackScreenProps +export type TabSharedStackScreenProps = + NativeStackScreenProps export type TabLocalStackParamList = { 'Tab-Local-Root': undefined @@ -153,9 +153,8 @@ export type TabMeStackParamList = { 'Tab-Me-Settings-Fontsize': undefined 'Tab-Me-Switch': undefined } & TabSharedStackParamList -export type TabMeStackScreenProps< - T extends keyof TabMeStackParamList -> = NativeStackScreenProps +export type TabMeStackScreenProps = + NativeStackScreenProps export type TabMeStackNavigationProp< RouteName extends keyof TabMeStackParamList > = StackNavigationProp diff --git a/src/utils/queryHooks/statusesHistory.ts b/src/utils/queryHooks/statusesHistory.ts new file mode 100644 index 00000000..a1a3e76b --- /dev/null +++ b/src/utils/queryHooks/statusesHistory.ts @@ -0,0 +1,34 @@ +import apiInstance from '@api/instance' +import { AxiosError } from 'axios' +import { QueryFunctionContext, useQuery, UseQueryOptions } from 'react-query' + +export type QueryKeyStatusesHistory = [ + 'StatusesHistory', + { id: Mastodon.Status['id'] } +] + +const queryFunction = async ({ + queryKey +}: QueryFunctionContext) => { + const { id } = queryKey[1] + const res = await apiInstance({ + method: 'get', + url: `statuses/${id}/history` + }) + return res.body +} + +const useStatusHistory = ({ + options, + ...queryKeyParams +}: QueryKeyStatusesHistory[1] & { + options?: UseQueryOptions +}) => { + const queryKey: QueryKeyStatusesHistory = [ + 'StatusesHistory', + { ...queryKeyParams } + ] + return useQuery(queryKey, queryFunction, options) +} + +export { useStatusHistory } diff --git a/src/utils/slices/instancesSlice.ts b/src/utils/slices/instancesSlice.ts index 1b5eb961..5d62ee10 100644 --- a/src/utils/slices/instancesSlice.ts +++ b/src/utils/slices/instancesSlice.ts @@ -1,4 +1,5 @@ import analytics from '@components/analytics' +import features from '@helpers/features' import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { RootState } from '@root/store' import { ComposeStateDraft } from '@screens/Compose/utils/types' @@ -341,9 +342,17 @@ export const getInstanceUrls = ({ instances: { instances } }: RootState) => export const getInstanceVersion = ({ instances: { instances } }: RootState) => instances[findInstanceActive(instances)]?.version -export const getInstanceVersionInFloat = ({ - instances: { instances } -}: RootState) => parseFloat(instances[findInstanceActive(instances)]?.version) +export const checkInstanceFeature = + (feature: string) => + ({ instances: { instances } }: RootState) => { + return features + .filter(f => f.feature === feature) + .filter( + f => + parseFloat(instances[findInstanceActive(instances)]?.version) >= + f.version + ) + } /* Get Instance Configuration */ export const getInstanceConfigurationStatusMaxChars = ({