mirror of
https://github.com/tooot-app/app
synced 2025-02-18 12:50:46 +01:00
Edited posts can be viewed
This commit is contained in:
parent
bceb70e805
commit
95ec76f411
12
src/@types/mastodon.d.ts
vendored
12
src/@types/mastodon.d.ts
vendored
@ -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
|
||||
|
5
src/@types/untyped.d.ts
vendored
5
src/@types/untyped.d.ts
vendored
@ -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
|
||||
}
|
||||
|
@ -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<Props> = ({
|
||||
}) => {
|
||||
const { colors } = useTheme()
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const onPress = useCallback(() => {
|
||||
analytics('search_account_press', { page: origin })
|
||||
|
@ -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<Nav.TabLocalStackParamList>
|
||||
navigation: StackNavigationProp<TabLocalStackParamList>
|
||||
mentions?: Mastodon.Mention[]
|
||||
tags?: Mastodon.Tag[]
|
||||
showFullLink: boolean
|
||||
@ -194,7 +195,7 @@ const ParseHTML = React.memo(
|
||||
)
|
||||
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
const route = useRoute()
|
||||
const { colors, theme } = useTheme()
|
||||
const { t, i18n } = useTranslation('componentParse')
|
||||
|
@ -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<Props> = ({
|
||||
})
|
||||
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
const onPress = useCallback(() => {
|
||||
analytics('timeline_conversation_press')
|
||||
if (conversation.last_status) {
|
||||
|
@ -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<Props> = ({
|
||||
const { colors } = useTheme()
|
||||
const instanceAccount = useSelector(getInstanceAccount, () => true)
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const actualStatus = item.reblog ? item.reblog : item
|
||||
|
||||
@ -143,7 +144,7 @@ const TimelineDefault: React.FC<Props> = ({
|
||||
<TimelineFullConversation queryKey={queryKey} status={actualStatus} />
|
||||
) : null}
|
||||
<TimelineTranslate status={actualStatus} highlighted={highlighted} />
|
||||
<TimelineActionsUsers status={actualStatus} highlighted={highlighted} />
|
||||
<TimelineFeedback status={actualStatus} highlighted={highlighted} />
|
||||
</View>
|
||||
|
||||
{queryKey && !disableDetails ? (
|
||||
|
@ -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<Props> = ({
|
||||
(prev, next) => prev?.id === next?.id
|
||||
)
|
||||
const navigation =
|
||||
useNavigation<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const actualAccount = notification.status
|
||||
? notification.status.account
|
||||
|
@ -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<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
const name = account.display_name || account.username
|
||||
const iconColor = colors.primaryDefault
|
||||
|
||||
|
@ -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<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
|
||||
return (
|
||||
<View style={styles.base}>
|
||||
{status.reblogs_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: colors.blue }]}
|
||||
onPress={() => {
|
||||
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
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
{status.favourites_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: colors.blue }]}
|
||||
onPress={() => {
|
||||
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
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
},
|
||||
(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
|
@ -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<StackNavigationProp<RootStackParamList>>()
|
||||
useEffect(() => {
|
||||
status.media_attachments.forEach((attachment, index) => {
|
||||
switch (attachment.type) {
|
||||
|
@ -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<StackNavigationProp<Nav.TabLocalStackParamList>>()
|
||||
useNavigation<StackNavigationProp<TabLocalStackParamList>>()
|
||||
// Need to fix go back root
|
||||
const onPress = useCallback(() => {
|
||||
analytics('timeline_shared_avatar_press', {
|
||||
|
@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
export interface Props {
|
||||
status: Mastodon.Status
|
||||
status: Pick<Mastodon.Status, 'content' | 'spoiler_text' | 'emojis'> & {
|
||||
mentions?: Mastodon.Status['mentions']
|
||||
tags?: Mastodon.Status['tags']
|
||||
}
|
||||
numberOfLines?: number
|
||||
highlighted?: boolean
|
||||
disableDetails?: boolean
|
||||
|
141
src/components/Timeline/Shared/Feedback.tsx
Normal file
141
src/components/Timeline/Shared/Feedback.tsx
Normal file
@ -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<StackNavigationProp<TabLocalStackParamList>>()
|
||||
|
||||
const { data } = useStatusHistory({
|
||||
id: status.id,
|
||||
options: { enabled: status.edited_at !== undefined }
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<View>
|
||||
{status.reblogs_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.reblogged_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: colors.blue }]}
|
||||
onPress={() => {
|
||||
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
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
{status.favourites_count > 0 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityLabel',
|
||||
{
|
||||
count: status.reblogs_count
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.favourited_by.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { color: colors.blue }]}
|
||||
onPress={() => {
|
||||
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
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View>
|
||||
{data && data.length > 1 ? (
|
||||
<Text
|
||||
accessibilityLabel={t(
|
||||
'shared.actionsUsers.history.accessibilityLabel',
|
||||
{
|
||||
count: data.length - 1
|
||||
}
|
||||
)}
|
||||
accessibilityHint={t(
|
||||
'shared.actionsUsers.history.accessibilityHint'
|
||||
)}
|
||||
accessibilityRole='button'
|
||||
style={[styles.text, { marginRight: 0, color: colors.blue }]}
|
||||
onPress={() => {
|
||||
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
|
||||
})}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
(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
|
@ -103,6 +103,7 @@ const HeaderConversation = React.memo(
|
||||
{conversation.last_status?.created_at ? (
|
||||
<HeaderSharedCreated
|
||||
created_at={conversation.last_status?.created_at}
|
||||
edited_at={conversation.last_status?.edited_at}
|
||||
/>
|
||||
) : null}
|
||||
<HeaderSharedMuted muted={conversation.last_status?.muted} />
|
||||
|
@ -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<StackNavigationProp<RootStackParamList>>()
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
@ -29,7 +31,10 @@ const TimelineHeaderDefault = React.memo(
|
||||
<View style={styles.accountAndMeta}>
|
||||
<HeaderSharedAccount account={status.account} />
|
||||
<View style={styles.meta}>
|
||||
<HeaderSharedCreated created_at={status.created_at} />
|
||||
<HeaderSharedCreated
|
||||
created_at={status.created_at}
|
||||
edited_at={status.edited_at}
|
||||
/>
|
||||
<HeaderSharedVisibility visibility={status.visibility} />
|
||||
<HeaderSharedMuted muted={status.muted} />
|
||||
<HeaderSharedApplication application={status.application} />
|
||||
@ -45,7 +50,6 @@ const TimelineHeaderDefault = React.memo(
|
||||
queryKey,
|
||||
rootQueryKey,
|
||||
status,
|
||||
url: status.url || status.uri,
|
||||
type: 'status'
|
||||
})
|
||||
}
|
||||
|
@ -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<StackNavigationProp<RootStackParamList>>()
|
||||
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 })}
|
||||
/>
|
||||
<View style={styles.meta}>
|
||||
<HeaderSharedCreated created_at={notification.created_at} />
|
||||
<HeaderSharedCreated
|
||||
created_at={notification.created_at}
|
||||
edited_at={notification.status?.edited_at}
|
||||
/>
|
||||
{notification.status?.visibility ? (
|
||||
<HeaderSharedVisibility
|
||||
visibility={notification.status.visibility}
|
||||
|
@ -1,30 +1,43 @@
|
||||
import Icon from '@components/Icon'
|
||||
import RelativeTime from '@components/RelativeTime'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import React from 'react'
|
||||
import { StyleSheet, Text } from 'react-native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Text } from 'react-native'
|
||||
|
||||
export interface Props {
|
||||
created_at: Mastodon.Status['created_at'] | number
|
||||
created_at: Mastodon.Status['created_at']
|
||||
edited_at?: Mastodon.Status['edited_at']
|
||||
}
|
||||
|
||||
const HeaderSharedCreated = React.memo(
|
||||
({ created_at }: Props) => {
|
||||
({ created_at, edited_at }: Props) => {
|
||||
const { t } = useTranslation('componentTimeline')
|
||||
const { colors } = useTheme()
|
||||
|
||||
return (
|
||||
<Text style={[styles.created_at, { color: colors.secondary }]}>
|
||||
<RelativeTime date={created_at} />
|
||||
</Text>
|
||||
<>
|
||||
<Text
|
||||
style={{ ...StyleConstants.FontStyle.S, color: colors.secondary }}
|
||||
>
|
||||
<RelativeTime date={edited_at || created_at} />
|
||||
</Text>
|
||||
{edited_at ? (
|
||||
<Icon
|
||||
accessibilityLabel={t(
|
||||
'shared.header.shared.edited.accessibilityLabel'
|
||||
)}
|
||||
name='Edit'
|
||||
size={StyleConstants.Font.Size.S}
|
||||
color={colors.secondary}
|
||||
style={{ marginLeft: StyleConstants.Spacing.S }}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
},
|
||||
() => true
|
||||
)
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
created_at: {
|
||||
...StyleConstants.FontStyle.S
|
||||
}
|
||||
})
|
||||
|
||||
export default HeaderSharedCreated
|
||||
|
7
src/helpers/features.json
Normal file
7
src/helpers/features.json
Normal file
@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"feature": "edit_post",
|
||||
"version": 3.5,
|
||||
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||
}
|
||||
]
|
@ -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"
|
||||
},
|
||||
|
@ -331,6 +331,9 @@
|
||||
"reblogged_by": "{{count}} boosted",
|
||||
"favourited_by": "{{count}} favourited"
|
||||
}
|
||||
},
|
||||
"history": {
|
||||
"name": "Edit History"
|
||||
}
|
||||
}
|
||||
}
|
105
src/screens/Tabs/Shared/History.tsx
Normal file
105
src/screens/Tabs/Shared/History.tsx
Normal file
@ -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 (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
padding: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingTop: first ? 0 : undefined
|
||||
}}
|
||||
>
|
||||
<HeaderSharedCreated created_at={history.created_at} />
|
||||
{typeof history.content === 'string' && history.content.length > 0 ? (
|
||||
<TimelineContent status={history} />
|
||||
) : null}
|
||||
{history.poll
|
||||
? history.poll.options.map((option, index) => (
|
||||
<View
|
||||
key={index}
|
||||
style={{ flex: 1, paddingVertical: StyleConstants.Spacing.S }}
|
||||
>
|
||||
<View style={{ flex: 1, flexDirection: 'row' }}>
|
||||
<Icon
|
||||
style={{
|
||||
paddingTop:
|
||||
StyleConstants.Font.LineHeight.M -
|
||||
StyleConstants.Font.Size.M,
|
||||
marginRight: StyleConstants.Spacing.S
|
||||
}}
|
||||
name='Circle'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={colors.disabled}
|
||||
/>
|
||||
<Text style={{ flex: 1 }}>
|
||||
<ParseEmojis
|
||||
content={option.title}
|
||||
emojis={history.poll?.emojis}
|
||||
/>
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
: null}
|
||||
{Array.isArray(history.media_attachments) &&
|
||||
history.media_attachments.length ? (
|
||||
<TimelineAttachment status={history} />
|
||||
) : null}
|
||||
</View>
|
||||
{!last ? <ComponentSeparator extraMarginLeft={0} /> : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const TabSharedHistory: React.FC<
|
||||
TabSharedStackScreenProps<'Tab-Shared-History'>
|
||||
> = ({
|
||||
route: {
|
||||
params: { id }
|
||||
}
|
||||
}) => {
|
||||
const { data } = useStatusHistory({ id })
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
{data && data.length > 0
|
||||
? data
|
||||
.slice(0)
|
||||
.reverse()
|
||||
.map((d, i) =>
|
||||
i !== 0 ? (
|
||||
<ContentView
|
||||
key={i}
|
||||
history={d}
|
||||
first={i === 1}
|
||||
last={i === data.length - 1}
|
||||
/>
|
||||
) : null
|
||||
)
|
||||
: null}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
export default TabSharedHistory
|
@ -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 = ({
|
||||
})}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
key='Tab-Shared-History'
|
||||
name='Tab-Shared-History'
|
||||
component={TabSharedHistory}
|
||||
options={{ title: t('screenTabs:shared.history.name') }}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
key='Tab-Shared-Search'
|
||||
name='Tab-Shared-Search'
|
||||
|
@ -70,9 +70,8 @@ export type RootStackParamList = {
|
||||
id: Mastodon.Attachment['id']
|
||||
}
|
||||
}
|
||||
export type RootStackScreenProps<
|
||||
T extends keyof RootStackParamList
|
||||
> = NativeStackScreenProps<RootStackParamList, T>
|
||||
export type RootStackScreenProps<T extends keyof RootStackParamList> =
|
||||
NativeStackScreenProps<RootStackParamList, T>
|
||||
|
||||
export type ScreenComposeStackParamList = {
|
||||
'Screen-Compose-Root': undefined
|
||||
@ -90,9 +89,8 @@ export type ScreenTabsStackParamList = {
|
||||
'Tab-Notifications': NavigatorScreenParams<TabNotificationsStackParamList>
|
||||
'Tab-Me': NavigatorScreenParams<TabMeStackParamList>
|
||||
}
|
||||
export type ScreenTabsScreenProps<
|
||||
T extends keyof ScreenTabsStackParamList
|
||||
> = BottomTabScreenProps<ScreenTabsStackParamList, T>
|
||||
export type ScreenTabsScreenProps<T extends keyof ScreenTabsStackParamList> =
|
||||
BottomTabScreenProps<ScreenTabsStackParamList, T>
|
||||
|
||||
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<TabSharedStackParamList, T>
|
||||
export type TabSharedStackScreenProps<T extends keyof TabSharedStackParamList> =
|
||||
NativeStackScreenProps<TabSharedStackParamList, T>
|
||||
|
||||
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<TabMeStackParamList, T>
|
||||
export type TabMeStackScreenProps<T extends keyof TabMeStackParamList> =
|
||||
NativeStackScreenProps<TabMeStackParamList, T>
|
||||
export type TabMeStackNavigationProp<
|
||||
RouteName extends keyof TabMeStackParamList
|
||||
> = StackNavigationProp<TabMeStackParamList, RouteName>
|
||||
|
34
src/utils/queryHooks/statusesHistory.ts
Normal file
34
src/utils/queryHooks/statusesHistory.ts
Normal file
@ -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<QueryKeyStatusesHistory>) => {
|
||||
const { id } = queryKey[1]
|
||||
const res = await apiInstance<Mastodon.StatusHistory[]>({
|
||||
method: 'get',
|
||||
url: `statuses/${id}/history`
|
||||
})
|
||||
return res.body
|
||||
}
|
||||
|
||||
const useStatusHistory = ({
|
||||
options,
|
||||
...queryKeyParams
|
||||
}: QueryKeyStatusesHistory[1] & {
|
||||
options?: UseQueryOptions<Mastodon.StatusHistory[], AxiosError>
|
||||
}) => {
|
||||
const queryKey: QueryKeyStatusesHistory = [
|
||||
'StatusesHistory',
|
||||
{ ...queryKeyParams }
|
||||
]
|
||||
return useQuery(queryKey, queryFunction, options)
|
||||
}
|
||||
|
||||
export { useStatusHistory }
|
@ -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 = ({
|
||||
|
Loading…
x
Reference in New Issue
Block a user