mirror of
https://github.com/tooot-app/app
synced 2025-06-05 22:19:13 +02:00
Merge branch 'main' into candidate
This commit is contained in:
@ -9,6 +9,7 @@ import {
|
|||||||
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
import { getInstanceAccount } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Platform } from 'react-native'
|
||||||
import { ContextMenuAction } from 'react-native-context-menu-view'
|
import { ContextMenuAction } from 'react-native-context-menu-view'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
@ -94,7 +95,33 @@ const contextMenuAccount = ({
|
|||||||
context: (relationship?.muting || false).toString()
|
context: (relationship?.muting || false).toString()
|
||||||
}),
|
}),
|
||||||
systemIcon: 'eye.slash'
|
systemIcon: 'eye.slash'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
switch (Platform.OS) {
|
||||||
|
case 'ios':
|
||||||
|
actions.push({
|
||||||
|
id: 'account',
|
||||||
|
title: t('account.title'),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
id: 'account-block',
|
||||||
|
title: t('account.block.action', {
|
||||||
|
context: (relationship?.blocking || false).toString()
|
||||||
|
}),
|
||||||
|
systemIcon: 'xmark.circle',
|
||||||
|
destructive: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'account-reports',
|
||||||
|
title: t('account.reports.action'),
|
||||||
|
systemIcon: 'flag',
|
||||||
|
destructive: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
actions.push(
|
||||||
{
|
{
|
||||||
id: 'account-block',
|
id: 'account-block',
|
||||||
title: t('account.block.action', {
|
title: t('account.block.action', {
|
||||||
@ -110,6 +137,8 @@ const contextMenuAccount = ({
|
|||||||
destructive: true
|
destructive: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (index: number) => {
|
return (index: number) => {
|
||||||
|
@ -54,9 +54,6 @@ const EmojisList = () => {
|
|||||||
|
|
||||||
const addedLength = spaceFront.length + shortcode.length + spaceRear.length
|
const addedLength = spaceFront.length + shortcode.length + spaceRear.length
|
||||||
setSelection({ start: selection.start + addedLength })
|
setSelection({ start: selection.start + addedLength })
|
||||||
ref?.current?.setNativeProps({
|
|
||||||
selection: { start: selection.start + addedLength }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const listItem = ({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
const listItem = ({ index, item }: { item: Mastodon.Emoji[]; index: number }) => {
|
||||||
|
@ -33,7 +33,7 @@ const RelationshipOutgoing = React.memo(
|
|||||||
queryKeyRelationship,
|
queryKeyRelationship,
|
||||||
[res]
|
[res]
|
||||||
)
|
)
|
||||||
if (action === 'follow' || action === 'block') {
|
if (action === 'block') {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
||||||
queryClient.invalidateQueries(queryKey)
|
queryClient.invalidateQueries(queryKey)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import analytics from '@components/analytics'
|
|||||||
import Icon from '@components/Icon'
|
import Icon from '@components/Icon'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
|
import { useActionSheet } from '@expo/react-native-action-sheet'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { StackNavigationProp } from '@react-navigation/stack'
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
import { RootStackParamList } from '@utils/navigation/navigators'
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
@ -48,40 +49,18 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
const theParams = params as MutationVarsTimelineUpdateStatusProperty
|
const theParams = params as MutationVarsTimelineUpdateStatusProperty
|
||||||
if (
|
if (
|
||||||
// Un-bookmark from bookmarks page
|
// Un-bookmark from bookmarks page
|
||||||
(queryKey[1].page === 'Bookmarks' &&
|
(queryKey[1].page === 'Bookmarks' && theParams.payload.property === 'bookmarked') ||
|
||||||
theParams.payload.property === 'bookmarked') ||
|
|
||||||
// Un-favourite from favourites page
|
// Un-favourite from favourites page
|
||||||
(queryKey[1].page === 'Favourites' &&
|
(queryKey[1].page === 'Favourites' && theParams.payload.property === 'favourited')
|
||||||
theParams.payload.property === 'favourited') ||
|
|
||||||
// Un-reblog from following page
|
|
||||||
(queryKey[1].page === 'Following' &&
|
|
||||||
theParams.payload.property === 'reblogged' &&
|
|
||||||
theParams.payload.currentValue === true)
|
|
||||||
) {
|
) {
|
||||||
queryClient.invalidateQueries(queryKey)
|
queryClient.invalidateQueries(queryKey)
|
||||||
} else if (
|
|
||||||
theParams.payload.property === 'reblogged' &&
|
|
||||||
queryKey[1].page !== 'Following'
|
|
||||||
) {
|
|
||||||
// When reblogged, update cache of following page
|
|
||||||
const tempQueryKey: QueryKeyTimeline = [
|
|
||||||
'Timeline',
|
|
||||||
{ page: 'Following' }
|
|
||||||
]
|
|
||||||
queryClient.invalidateQueries(tempQueryKey)
|
|
||||||
} else if (theParams.payload.property === 'favourited') {
|
} else if (theParams.payload.property === 'favourited') {
|
||||||
// When favourited, update favourited page
|
// When favourited, update favourited page
|
||||||
const tempQueryKey: QueryKeyTimeline = [
|
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Favourites' }]
|
||||||
'Timeline',
|
|
||||||
{ page: 'Favourites' }
|
|
||||||
]
|
|
||||||
queryClient.invalidateQueries(tempQueryKey)
|
queryClient.invalidateQueries(tempQueryKey)
|
||||||
} else if (theParams.payload.property === 'bookmarked') {
|
} else if (theParams.payload.property === 'bookmarked') {
|
||||||
// When bookmarked, update bookmark page
|
// When bookmarked, update bookmark page
|
||||||
const tempQueryKey: QueryKeyTimeline = [
|
const tempQueryKey: QueryKeyTimeline = ['Timeline', { page: 'Bookmarks' }]
|
||||||
'Timeline',
|
|
||||||
{ page: 'Bookmarks' }
|
|
||||||
]
|
|
||||||
queryClient.invalidateQueries(tempQueryKey)
|
queryClient.invalidateQueries(tempQueryKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -91,9 +70,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
theme,
|
theme,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: t('common:message.error.message', {
|
message: t('common:message.error.message', {
|
||||||
function: t(
|
function: t(`shared.actions.${correctParam.payload.property}.function`)
|
||||||
`shared.actions.${correctParam.payload.property}.function`
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
...(err.status &&
|
...(err.status &&
|
||||||
typeof err.status === 'number' &&
|
typeof err.status === 'number' &&
|
||||||
@ -119,7 +96,67 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
queryKey
|
queryKey
|
||||||
})
|
})
|
||||||
}, [status.replies_count])
|
}, [status.replies_count])
|
||||||
|
const { showActionSheetWithOptions } = useActionSheet()
|
||||||
const onPressReblog = useCallback(() => {
|
const onPressReblog = useCallback(() => {
|
||||||
|
if (!status.reblogged) {
|
||||||
|
showActionSheetWithOptions(
|
||||||
|
{
|
||||||
|
title: t('shared.actions.reblogged.options.title'),
|
||||||
|
options: [
|
||||||
|
t('shared.actions.reblogged.options.public'),
|
||||||
|
t('shared.actions.reblogged.options.unlisted'),
|
||||||
|
t('common:buttons.cancel')
|
||||||
|
],
|
||||||
|
cancelButtonIndex: 2
|
||||||
|
},
|
||||||
|
(selectedIndex: number) => {
|
||||||
|
switch (selectedIndex) {
|
||||||
|
case 0:
|
||||||
|
analytics('timeline_shared_actions_reblog_public_press', {
|
||||||
|
page: queryKey[1].page,
|
||||||
|
count: status.reblogs_count,
|
||||||
|
current: status.reblogged
|
||||||
|
})
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'updateStatusProperty',
|
||||||
|
queryKey,
|
||||||
|
rootQueryKey,
|
||||||
|
id: status.id,
|
||||||
|
reblog,
|
||||||
|
payload: {
|
||||||
|
property: 'reblogged',
|
||||||
|
currentValue: status.reblogged,
|
||||||
|
propertyCount: 'reblogs_count',
|
||||||
|
countValue: status.reblogs_count,
|
||||||
|
visibility: 'public'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
analytics('timeline_shared_actions_reblog_unlisted_press', {
|
||||||
|
page: queryKey[1].page,
|
||||||
|
count: status.reblogs_count,
|
||||||
|
current: status.reblogged
|
||||||
|
})
|
||||||
|
mutation.mutate({
|
||||||
|
type: 'updateStatusProperty',
|
||||||
|
queryKey,
|
||||||
|
rootQueryKey,
|
||||||
|
id: status.id,
|
||||||
|
reblog,
|
||||||
|
payload: {
|
||||||
|
property: 'reblogged',
|
||||||
|
currentValue: status.reblogged,
|
||||||
|
propertyCount: 'reblogs_count',
|
||||||
|
countValue: status.reblogs_count,
|
||||||
|
visibility: 'unlisted'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
analytics('timeline_shared_actions_reblog_press', {
|
analytics('timeline_shared_actions_reblog_press', {
|
||||||
page: queryKey[1].page,
|
page: queryKey[1].page,
|
||||||
count: status.reblogs_count,
|
count: status.reblogs_count,
|
||||||
@ -135,9 +172,11 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
property: 'reblogged',
|
property: 'reblogged',
|
||||||
currentValue: status.reblogged,
|
currentValue: status.reblogged,
|
||||||
propertyCount: 'reblogs_count',
|
propertyCount: 'reblogs_count',
|
||||||
countValue: status.reblogs_count
|
countValue: status.reblogs_count,
|
||||||
|
visibility: 'public'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}, [status.reblogged, status.reblogs_count])
|
}, [status.reblogged, status.reblogs_count])
|
||||||
const onPressFavourite = useCallback(() => {
|
const onPressFavourite = useCallback(() => {
|
||||||
analytics('timeline_shared_actions_favourite_press', {
|
analytics('timeline_shared_actions_favourite_press', {
|
||||||
@ -182,11 +221,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
const childrenReply = useMemo(
|
const childrenReply = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<>
|
<>
|
||||||
<Icon
|
<Icon name='MessageCircle' color={iconColor} size={StyleConstants.Font.Size.L} />
|
||||||
name='MessageCircle'
|
|
||||||
color={iconColor}
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
/>
|
|
||||||
{status.replies_count > 0 ? (
|
{status.replies_count > 0 ? (
|
||||||
<CustomText
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
@ -209,8 +244,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
<Icon
|
<Icon
|
||||||
name='Repeat'
|
name='Repeat'
|
||||||
color={
|
color={
|
||||||
status.visibility === 'direct' ||
|
status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount)
|
||||||
(status.visibility === 'private' && !ownAccount)
|
|
||||||
? colors.disabled
|
? colors.disabled
|
||||||
: color(status.reblogged)
|
: color(status.reblogged)
|
||||||
}
|
}
|
||||||
@ -237,11 +271,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
const color = (state: boolean) => (state ? colors.red : colors.secondary)
|
const color = (state: boolean) => (state ? colors.red : colors.secondary)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Icon
|
<Icon name='Heart' color={color(status.favourited)} size={StyleConstants.Font.Size.L} />
|
||||||
name='Heart'
|
|
||||||
color={color(status.favourited)}
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
/>
|
|
||||||
{status.favourites_count > 0 ? (
|
{status.favourites_count > 0 ? (
|
||||||
<CustomText
|
<CustomText
|
||||||
style={{
|
style={{
|
||||||
@ -260,29 +290,21 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
const childrenBookmark = useMemo(() => {
|
const childrenBookmark = useMemo(() => {
|
||||||
const color = (state: boolean) => (state ? colors.yellow : colors.secondary)
|
const color = (state: boolean) => (state ? colors.yellow : colors.secondary)
|
||||||
return (
|
return (
|
||||||
<Icon
|
<Icon name='Bookmark' color={color(status.bookmarked)} size={StyleConstants.Font.Size.L} />
|
||||||
name='Bookmark'
|
|
||||||
color={color(status.bookmarked)}
|
|
||||||
size={StyleConstants.Font.Size.L}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
}, [status.bookmarked])
|
}, [status.bookmarked])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
paddingLeft: highlighted
|
paddingLeft: highlighted ? 0 : StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
||||||
? 0
|
|
||||||
: StyleConstants.Avatar.M + StyleConstants.Spacing.S
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View style={{ flexDirection: 'row' }}>
|
<View style={{ flexDirection: 'row' }}>
|
||||||
<Pressable
|
<Pressable
|
||||||
{...(highlighted
|
{...(highlighted
|
||||||
? {
|
? {
|
||||||
accessibilityLabel: t(
|
accessibilityLabel: t('shared.actions.reply.accessibilityLabel'),
|
||||||
'shared.actions.reply.accessibilityLabel'
|
|
||||||
),
|
|
||||||
accessibilityRole: 'button'
|
accessibilityRole: 'button'
|
||||||
}
|
}
|
||||||
: { accessibilityLabel: '' })}
|
: { accessibilityLabel: '' })}
|
||||||
@ -294,9 +316,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
<Pressable
|
<Pressable
|
||||||
{...(highlighted
|
{...(highlighted
|
||||||
? {
|
? {
|
||||||
accessibilityLabel: t(
|
accessibilityLabel: t('shared.actions.reblogged.accessibilityLabel'),
|
||||||
'shared.actions.reblogged.accessibilityLabel'
|
|
||||||
),
|
|
||||||
accessibilityRole: 'button'
|
accessibilityRole: 'button'
|
||||||
}
|
}
|
||||||
: { accessibilityLabel: '' })}
|
: { accessibilityLabel: '' })}
|
||||||
@ -304,17 +324,14 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
onPress={onPressReblog}
|
onPress={onPressReblog}
|
||||||
children={childrenReblog}
|
children={childrenReblog}
|
||||||
disabled={
|
disabled={
|
||||||
status.visibility === 'direct' ||
|
status.visibility === 'direct' || (status.visibility === 'private' && !ownAccount)
|
||||||
(status.visibility === 'private' && !ownAccount)
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Pressable
|
<Pressable
|
||||||
{...(highlighted
|
{...(highlighted
|
||||||
? {
|
? {
|
||||||
accessibilityLabel: t(
|
accessibilityLabel: t('shared.actions.favourited.accessibilityLabel'),
|
||||||
'shared.actions.favourited.accessibilityLabel'
|
|
||||||
),
|
|
||||||
accessibilityRole: 'button'
|
accessibilityRole: 'button'
|
||||||
}
|
}
|
||||||
: { accessibilityLabel: '' })}
|
: { accessibilityLabel: '' })}
|
||||||
@ -326,9 +343,7 @@ const TimelineActions: React.FC<Props> = ({
|
|||||||
<Pressable
|
<Pressable
|
||||||
{...(highlighted
|
{...(highlighted
|
||||||
? {
|
? {
|
||||||
accessibilityLabel: t(
|
accessibilityLabel: t('shared.actions.bookmarked.accessibilityLabel'),
|
||||||
'shared.actions.bookmarked.accessibilityLabel'
|
|
||||||
),
|
|
||||||
accessibilityRole: 'button'
|
accessibilityRole: 'button'
|
||||||
}
|
}
|
||||||
: { accessibilityLabel: '' })}
|
: { accessibilityLabel: '' })}
|
||||||
|
@ -38,7 +38,12 @@
|
|||||||
},
|
},
|
||||||
"reblogged": {
|
"reblogged": {
|
||||||
"accessibilityLabel": "Boost this toot",
|
"accessibilityLabel": "Boost this toot",
|
||||||
"function": "Boost toot"
|
"function": "Boost toot",
|
||||||
|
"options": {
|
||||||
|
"title": "Choose boost visibility",
|
||||||
|
"public": "Public boost",
|
||||||
|
"unlisted": "Unlist boost"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"favourited": {
|
"favourited": {
|
||||||
"accessibilityLabel": "Add this toot to favourites",
|
"accessibilityLabel": "Add this toot to favourites",
|
||||||
|
@ -9,7 +9,7 @@ import formatText from '@screens/Compose/formatText'
|
|||||||
import ComposeRoot from '@screens/Compose/Root'
|
import ComposeRoot from '@screens/Compose/Root'
|
||||||
import * as Sentry from '@sentry/react-native'
|
import * as Sentry from '@sentry/react-native'
|
||||||
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
import { RootStackScreenProps } from '@utils/navigation/navigators'
|
||||||
import { QueryKeyTimeline, useTimelineMutation } from '@utils/queryHooks/timeline'
|
import { useTimelineMutation } from '@utils/queryHooks/timeline'
|
||||||
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
import { updateStoreReview } from '@utils/slices/contextsSlice'
|
||||||
import {
|
import {
|
||||||
getInstanceAccount,
|
getInstanceAccount,
|
||||||
@ -280,8 +280,6 @@ const ScreenCompose: React.FC<RootStackScreenProps<'Screen-Compose'>> = ({
|
|||||||
} else {
|
} else {
|
||||||
dispatch(updateStoreReview(1))
|
dispatch(updateStoreReview(1))
|
||||||
}
|
}
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Following' }]
|
|
||||||
queryClient.invalidateQueries(queryKey)
|
|
||||||
|
|
||||||
switch (params?.type) {
|
switch (params?.type) {
|
||||||
case 'edit':
|
case 'edit':
|
||||||
|
@ -293,11 +293,18 @@ export type MutationVarsTimelineUpdateStatusProperty = {
|
|||||||
countValue: undefined
|
countValue: undefined
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
property: 'favourited' | 'reblogged'
|
property: 'favourited'
|
||||||
currentValue: boolean
|
currentValue: boolean
|
||||||
propertyCount: 'favourites_count' | 'reblogs_count'
|
propertyCount: 'favourites_count' | 'reblogs_count'
|
||||||
countValue: number
|
countValue: number
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
property: 'reblogged'
|
||||||
|
currentValue: boolean
|
||||||
|
propertyCount: 'favourites_count' | 'reblogs_count'
|
||||||
|
countValue: number
|
||||||
|
visibility: 'public' | 'unlisted'
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
property: 'poll'
|
property: 'poll'
|
||||||
id: Mastodon.Poll['id']
|
id: Mastodon.Poll['id']
|
||||||
@ -371,11 +378,16 @@ const mutationFunction = async (params: MutationVarsTimeline) => {
|
|||||||
...(params.payload.type === 'vote' && { body: formData })
|
...(params.payload.type === 'vote' && { body: formData })
|
||||||
})
|
})
|
||||||
default:
|
default:
|
||||||
|
const body = new FormData()
|
||||||
|
if (params.payload.property === 'reblogged') {
|
||||||
|
body.append('visibility', params.payload.visibility)
|
||||||
|
}
|
||||||
return apiInstance<Mastodon.Status>({
|
return apiInstance<Mastodon.Status>({
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: `statuses/${params.id}/${
|
url: `statuses/${params.id}/${
|
||||||
params.payload.currentValue ? 'un' : ''
|
params.payload.currentValue ? 'un' : ''
|
||||||
}${MapPropertyToUrl[params.payload.property]}`
|
}${MapPropertyToUrl[params.payload.property]}`,
|
||||||
|
body
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
case 'updateAccountProperty':
|
case 'updateAccountProperty':
|
||||||
|
Reference in New Issue
Block a user