1
0
mirror of https://github.com/tooot-app/app synced 2025-06-05 22:19:13 +02:00

Merge pull request #342 from tooot-app/main

Release v4.1.3
This commit is contained in:
xmflsct
2022-06-17 00:15:08 +02:00
committed by GitHub
10 changed files with 325 additions and 15 deletions

View File

@ -4,7 +4,7 @@
"native": "220603", "native": "220603",
"major": 4, "major": 4,
"minor": 1, "minor": 1,
"patch": 2, "patch": 3,
"expo": "45.0.0" "expo": "45.0.0"
}, },
"description": "tooot app for Mastodon", "description": "tooot app for Mastodon",

View File

@ -5,12 +5,14 @@ import {
AccessibilityProps, AccessibilityProps,
Image, Image,
ImageStyle, ImageStyle,
Platform,
Pressable, Pressable,
StyleProp, StyleProp,
StyleSheet, StyleSheet,
View, View,
ViewStyle ViewStyle
} from 'react-native' } from 'react-native'
import FastImage from 'react-native-fast-image'
import { Blurhash } from 'react-native-blurhash' import { Blurhash } from 'react-native-blurhash'
// blurhas -> if blurhash, show before any loading succeed // blurhas -> if blurhash, show before any loading succeed
@ -125,6 +127,7 @@ const GracefullyImage = ({
]} ]}
/> />
) : null} ) : null}
{Platform.OS === 'ios' ? (
<Image <Image
fadeDuration={0} fadeDuration={0}
source={source} source={source}
@ -132,6 +135,16 @@ const GracefullyImage = ({
onLoad={onLoad} onLoad={onLoad}
onError={onError} onError={onError}
/> />
) : (
<FastImage
fadeDuration={0}
source={source}
// @ts-ignore
style={[{ flex: 1 }, imageStyle]}
onLoad={onLoad}
onError={onError}
/>
)}
{blurhashView} {blurhashView}
</Pressable> </Pressable>
) )

View File

@ -78,6 +78,7 @@ const TimelineDefault: React.FC<Props> = ({
status={actualStatus} status={actualStatus}
queryKey={queryKey} queryKey={queryKey}
rootQueryKey={rootQueryKey} rootQueryKey={rootQueryKey}
disabled={highlighted}
> >
<Pressable <Pressable
accessible={highlighted ? false : true} accessible={highlighted ? false : true}

View File

@ -59,7 +59,11 @@ const TimelineNotifications = React.memo(
}, []) }, [])
return ( return (
<TimelineContextMenu status={notification.status} queryKey={queryKey}> <TimelineContextMenu
status={notification.status}
queryKey={queryKey}
disabled={highlighted}
>
<Pressable <Pressable
style={{ style={{
padding: StyleConstants.Spacing.Global.PagePadding, padding: StyleConstants.Spacing.Global.PagePadding,
@ -96,7 +100,10 @@ const TimelineNotifications = React.memo(
account={actualAccount} account={actualAccount}
highlighted={highlighted} highlighted={highlighted}
/> />
<TimelineHeaderNotification notification={notification} /> <TimelineHeaderNotification
queryKey={queryKey}
notification={notification}
/>
</View> </View>
{notification.status ? ( {notification.status ? (

View File

@ -5,6 +5,7 @@ import contextMenuStatus from '@components/ContextMenu/status'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import React from 'react' import React from 'react'
import { createContext } from 'react' import { createContext } from 'react'
import { Platform } from 'react-native'
import ContextMenu, { import ContextMenu, {
ContextMenuAction, ContextMenuAction,
ContextMenuProps ContextMenuProps
@ -14,6 +15,7 @@ export interface Props {
status?: Mastodon.Status status?: Mastodon.Status
queryKey?: QueryKeyTimeline queryKey?: QueryKeyTimeline
rootQueryKey?: QueryKeyTimeline rootQueryKey?: QueryKeyTimeline
disabled?: boolean // Allowing toot to be copied when highlighted
} }
export const ContextMenuContext = createContext<ContextMenuAction[]>([]) export const ContextMenuContext = createContext<ContextMenuAction[]>([])
@ -23,9 +25,10 @@ const TimelineContextMenu: React.FC<Props & ContextMenuProps> = ({
status, status,
queryKey, queryKey,
rootQueryKey, rootQueryKey,
disabled,
...props ...props
}) => { }) => {
if (!status || !queryKey) { if (!status || !queryKey || disabled || Platform.OS === 'android') {
return <>{children}</> return <>{children}</>
} }

View File

@ -0,0 +1,120 @@
import contextMenuAccount from '@components/ContextMenu/account'
import contextMenuInstance from '@components/ContextMenu/instance'
import contextMenuShare from '@components/ContextMenu/share'
import contextMenuStatus from '@components/ContextMenu/status'
import Icon from '@components/Icon'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { Platform, Pressable, View } from 'react-native'
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
import { ContextMenuContext } from './ContextMenu'
import HeaderSharedAccount from './HeaderShared/Account'
import HeaderSharedApplication from './HeaderShared/Application'
import HeaderSharedCreated from './HeaderShared/Created'
import HeaderSharedMuted from './HeaderShared/Muted'
import HeaderSharedVisibility from './HeaderShared/Visibility'
export interface Props {
queryKey?: QueryKeyTimeline
status: Mastodon.Status
highlighted: boolean
}
const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => {
if (!queryKey) return
const { t } = useTranslation('componentContextMenu')
const { colors } = useTheme()
const actions: ContextMenuAction[] = []
const shareOnPress =
status.visibility !== 'direct'
? contextMenuShare({
actions,
type: 'status',
url: status.url || status.uri
})
: null
const statusOnPress = contextMenuStatus({
actions,
status,
queryKey
})
const accountOnPress = contextMenuAccount({
actions,
type: 'status',
queryKey,
id: status.account.id
})
const instanceOnPress = contextMenuInstance({
actions,
status,
queryKey
})
return (
<View style={{ flex: 1, flexDirection: 'row' }}>
<View style={{ flex: 7 }}>
<HeaderSharedAccount account={status.account} />
<View
style={{
flexDirection: 'row',
alignItems: 'center',
marginTop: StyleConstants.Spacing.XS,
marginBottom: StyleConstants.Spacing.S
}}
>
<HeaderSharedCreated
created_at={status.created_at}
edited_at={status.edited_at}
highlighted={highlighted}
/>
<HeaderSharedVisibility visibility={status.visibility} />
<HeaderSharedMuted muted={status.muted} />
<HeaderSharedApplication application={status.application} />
</View>
</View>
{queryKey ? (
<Pressable
accessibilityHint={t('accessibilityHint')}
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
marginBottom: StyleConstants.Spacing.L
}}
>
<ContextMenu
dropdownMenuMode
actions={actions}
onPress={({ nativeEvent: { index } }) => {
console.log('index', index)
for (const on of [
shareOnPress,
statusOnPress,
accountOnPress,
instanceOnPress
]) {
on && on(index)
}
}}
children={
<Icon
name='MoreHorizontal'
color={colors.secondary}
size={StyleConstants.Font.Size.L}
/>
}
/>
</Pressable>
) : null}
</View>
)
}
export default TimelineHeaderDefault

View File

@ -4,7 +4,7 @@ import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext } from 'react' import React, { useContext } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Pressable, View } from 'react-native' import { Platform, Pressable, View } from 'react-native'
import ContextMenu from 'react-native-context-menu-view' import ContextMenu from 'react-native-context-menu-view'
import { ContextMenuContext } from './ContextMenu' import { ContextMenuContext } from './ContextMenu'
import HeaderSharedAccount from './HeaderShared/Account' import HeaderSharedAccount from './HeaderShared/Account'
@ -48,7 +48,7 @@ const TimelineHeaderDefault = ({ queryKey, status, highlighted }: Props) => {
</View> </View>
</View> </View>
{queryKey ? ( {queryKey && !highlighted ? (
<Pressable <Pressable
accessibilityHint={t('accessibilityHint')} accessibilityHint={t('accessibilityHint')}
style={{ style={{

View File

@ -0,0 +1,163 @@
import contextMenuAccount from '@components/ContextMenu/account'
import contextMenuInstance from '@components/ContextMenu/instance'
import contextMenuShare from '@components/ContextMenu/share'
import contextMenuStatus from '@components/ContextMenu/status'
import Icon from '@components/Icon'
import {
RelationshipIncoming,
RelationshipOutgoing
} from '@components/Relationship'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import React, { useMemo } from 'react'
import { Pressable, View } from 'react-native'
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
import HeaderSharedAccount from './HeaderShared/Account'
import HeaderSharedApplication from './HeaderShared/Application'
import HeaderSharedCreated from './HeaderShared/Created'
import HeaderSharedMuted from './HeaderShared/Muted'
import HeaderSharedVisibility from './HeaderShared/Visibility'
export interface Props {
queryKey?: QueryKeyTimeline
notification: Mastodon.Notification
}
const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
const { colors } = useTheme()
const contextMenuActions: ContextMenuAction[] = []
const status = notification.status
const shareOnPress =
status && status?.visibility !== 'direct'
? contextMenuShare({
actions: contextMenuActions,
type: 'status',
url: status.url || status.uri
})
: null
const statusOnPress = contextMenuStatus({
actions: contextMenuActions,
status,
queryKey
})
const accountOnPress = contextMenuAccount({
actions: contextMenuActions,
type: 'status',
queryKey,
id: status?.account.id
})
const instanceOnPress = contextMenuInstance({
actions: contextMenuActions,
status,
queryKey
})
const actions = useMemo(() => {
switch (notification.type) {
case 'follow':
return <RelationshipOutgoing id={notification.account.id} />
case 'follow_request':
return <RelationshipIncoming id={notification.account.id} />
default:
if (notification.status) {
return (
<Pressable
style={{
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
paddingBottom: StyleConstants.Spacing.S
}}
children={
<ContextMenu
dropdownMenuMode
actions={contextMenuActions}
onPress={({ nativeEvent: { index } }) => {
for (const on of [
shareOnPress,
statusOnPress,
accountOnPress,
instanceOnPress
]) {
on && on(index)
}
}}
children={
<Icon
name='MoreHorizontal'
color={colors.secondary}
size={StyleConstants.Font.Size.L}
/>
}
/>
}
/>
)
}
}
}, [notification.type])
return (
<View style={{ flex: 1, flexDirection: 'row' }}>
<View
style={{
flex:
notification.type === 'follow' ||
notification.type === 'follow_request'
? 1
: 4
}}
>
<HeaderSharedAccount
account={
notification.status
? notification.status.account
: notification.account
}
{...((notification.type === 'follow' ||
notification.type === 'follow_request') && { withoutName: true })}
/>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
marginTop: StyleConstants.Spacing.XS,
marginBottom: StyleConstants.Spacing.S
}}
>
<HeaderSharedCreated
created_at={
notification.status?.created_at || notification.created_at
}
edited_at={notification.status?.edited_at}
/>
{notification.status?.visibility ? (
<HeaderSharedVisibility
visibility={notification.status.visibility}
/>
) : null}
<HeaderSharedMuted muted={notification.status?.muted} />
<HeaderSharedApplication
application={notification.status?.application}
/>
</View>
</View>
<View
style={[
{ marginLeft: StyleConstants.Spacing.M },
notification.type === 'follow' ||
notification.type === 'follow_request'
? { flexShrink: 1 }
: { flex: 1 }
]}
>
{actions}
</View>
</View>
)
}
export default TimelineHeaderNotification

View File

@ -3,6 +3,7 @@ import {
RelationshipIncoming, RelationshipIncoming,
RelationshipOutgoing RelationshipOutgoing
} from '@components/Relationship' } from '@components/Relationship'
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
import { StyleConstants } from '@utils/styles/constants' import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React, { useContext, useMemo } from 'react' import React, { useContext, useMemo } from 'react'
@ -16,6 +17,7 @@ import HeaderSharedMuted from './HeaderShared/Muted'
import HeaderSharedVisibility from './HeaderShared/Visibility' import HeaderSharedVisibility from './HeaderShared/Visibility'
export interface Props { export interface Props {
queryKey: QueryKeyTimeline
notification: Mastodon.Notification notification: Mastodon.Notification
} }

View File

@ -2,7 +2,8 @@ import Button from '@components/Button'
import { useAccessibility } from '@utils/accessibility/AccessibilityManager' import { useAccessibility } from '@utils/accessibility/AccessibilityManager'
import { useTheme } from '@utils/styles/ThemeManager' import { useTheme } from '@utils/styles/ThemeManager'
import React from 'react' import React from 'react'
import { Dimensions, Image, View } from 'react-native' import { Dimensions, View } from 'react-native'
import FastImage from 'react-native-fast-image'
import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useSafeAreaInsets } from 'react-native-safe-area-context'
export interface Props { export interface Props {
@ -18,7 +19,7 @@ const AccountHeader = React.memo(
return ( return (
<View> <View>
<Image <FastImage
source={{ source={{
uri: reduceMotionEnabled ? account?.header_static : account?.header uri: reduceMotionEnabled ? account?.header_static : account?.header
}} }}