diff --git a/.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch b/.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch new file mode 100644 index 00000000..ced94530 --- /dev/null +++ b/.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch @@ -0,0 +1,38 @@ +diff --git a/src/zoom.tsx b/src/zoom.tsx +index 70ce1c8d6a43e711f06b93d1eda3b44a3ad9a659..cdc2713470f2d332b8bf3e9c97e38fd9b78281df 100644 +--- a/src/zoom.tsx ++++ b/src/zoom.tsx +@@ -4,6 +4,7 @@ import Animated, { + useSharedValue, + useAnimatedStyle, + useDerivedValue, ++ withDecay, + withTiming, + cancelAnimation, + runOnJS, +@@ -120,11 +121,22 @@ export function Zoom(props: Props) { + } + } + }) +- .onEnd(() => { ++ .onEnd((event) => { + if (isPinching.value || !isZoomed.value) return; + +- panTranslateX.value = 0; +- panTranslateY.value = 0; ++ const maxTranslateX = (viewWidth.value / 2) * scale.value - viewWidth.value / 2; ++ const minTranslateX = -maxTranslateX; ++ translationX.value = withDecay({ ++ velocity: event.velocityX, ++ clamp: [minTranslateX, maxTranslateX] ++ }); ++ ++ const maxTranslateY = (viewHeight.value / 2) * scale.value - viewHeight.value / 2; ++ const minTranslateY = -maxTranslateY; ++ translationY.value = withDecay({ ++ velocity: event.velocityY, ++ clamp: [minTranslateY, maxTranslateY] ++ }); + }); + + const pinch = Gesture.Pinch() diff --git a/ios/tooot/Info.plist b/ios/tooot/Info.plist index a70dccd6..a7f281e3 100644 --- a/ios/tooot/Info.plist +++ b/ios/tooot/Info.plist @@ -35,6 +35,7 @@ tooot-share tooot + https diff --git a/package.json b/package.json index 1a919d89..1c7747be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tooot", - "version": "4.9.2", + "version": "4.9.3", "description": "tooot for Mastodon", "author": "xmflsct ", "license": "GPL-3.0-or-later", @@ -114,6 +114,7 @@ "expo-av@^13.0.2": "patch:expo-av@npm%3A13.0.2#./.yarn/patches/expo-av-npm-13.0.2-7a651776f1.patch", "react-native-share-menu@^6.0.0": "patch:react-native-share-menu@npm%3A6.0.0#./.yarn/patches/react-native-share-menu-npm-6.0.0-f1094c3204.patch", "@types/react-native-share-menu@^5.0.2": "patch:@types/react-native-share-menu@npm%3A5.0.2#./.yarn/patches/@types-react-native-share-menu-npm-5.0.2-373df17ecc.patch", - "react-native-ios-context-menu@^1.15.1": "patch:react-native-ios-context-menu@npm%3A1.15.1#./.yarn/patches/react-native-ios-context-menu-npm-1.15.1-0034bfa5ba.patch" + "react-native-ios-context-menu@^1.15.1": "patch:react-native-ios-context-menu@npm%3A1.15.1#./.yarn/patches/react-native-ios-context-menu-npm-1.15.1-0034bfa5ba.patch", + "react-native-reanimated-zoom@^0.3.3": "patch:react-native-reanimated-zoom@npm%3A0.3.3#./.yarn/patches/react-native-reanimated-zoom-npm-0.3.3-bbb8d84109.patch" } } diff --git a/src/components/Timeline/Default.tsx b/src/components/Timeline/Default.tsx index 8fb6eb52..3c78fc74 100644 --- a/src/components/Timeline/Default.tsx +++ b/src/components/Timeline/Default.tsx @@ -1,6 +1,3 @@ -import menuInstance from '@components/contextMenu/instance' -import menuShare from '@components/contextMenu/share' -import menuStatus from '@components/contextMenu/status' import TimelineActioned from '@components/Timeline/Shared/Actioned' import TimelineActions from '@components/Timeline/Shared/Actions' import TimelineAttachment from '@components/Timeline/Shared/Attachment' @@ -19,9 +16,8 @@ import { usePreferencesQuery } from '@utils/queryHooks/preferences' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { Fragment, useRef, useState } from 'react' +import React, { useRef, useState } from 'react' import { Pressable, StyleProp, View, ViewStyle } from 'react-native' -import * as ContextMenu from 'zeego/context-menu' import StatusContext from './Shared/Context' import TimelineFeedback from './Shared/Feedback' import TimelineFiltered, { FilteredProps, shouldFilter } from './Shared/Filtered' @@ -126,15 +122,6 @@ const TimelineDefault: React.FC = ({ ) - const mShare = menuShare({ - visibility: status.visibility, - type: 'status', - url: status.url || status.uri, - rawContent - }) - const mStatus = menuStatus({ status, queryKey }) - const mInstance = menuInstance({ status, queryKey }) - if (!isMyAccount) { let filterResults: FilteredProps['filterResults'] = [] const [filterRevealed, setFilterRevealed] = useState(false) @@ -183,67 +170,14 @@ const TimelineDefault: React.FC = ({ {main()} ) : ( <> - - - navigation.push('Tab-Shared-Toot', { toot: status })} - onLongPress={() => {}} - children={main()} - /> - - - - {[mShare, mStatus, mInstance].map((menu, i) => ( - - {menu.map((group, index) => ( - - {group.map(item => { - switch (item.type) { - case 'item': - return ( - - - {item.icon ? ( - - ) : null} - - ) - case 'sub': - return ( - // @ts-ignore - - - - {item.trigger.icon ? ( - - ) : null} - - - {item.items.map(sub => ( - - - {sub.icon ? ( - - ) : null} - - ))} - - - ) - } - })} - - ))} - - ))} - - + navigation.push('Tab-Shared-Toot', { toot: status })} + onLongPress={() => {}} + children={main()} + /> )} diff --git a/src/components/Timeline/Notifications.tsx b/src/components/Timeline/Notifications.tsx index c1030c56..ee1ddfe3 100644 --- a/src/components/Timeline/Notifications.tsx +++ b/src/components/Timeline/Notifications.tsx @@ -1,6 +1,3 @@ -import menuInstance from '@components/contextMenu/instance' -import menuShare from '@components/contextMenu/share' -import menuStatus from '@components/contextMenu/status' import TimelineActioned from '@components/Timeline/Shared/Actioned' import TimelineActions from '@components/Timeline/Shared/Actions' import TimelineAttachment from '@components/Timeline/Shared/Attachment' @@ -18,9 +15,8 @@ import { usePreferencesQuery } from '@utils/queryHooks/preferences' import { QueryKeyTimeline } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { Fragment, useState } from 'react' +import React, { useState } from 'react' import { Pressable, View } from 'react-native' -import * as ContextMenu from 'zeego/context-menu' import StatusContext from './Shared/Context' import TimelineFiltered, { FilteredProps, shouldFilter } from './Shared/Filtered' import TimelineFullConversation from './Shared/FullConversation' @@ -100,14 +96,6 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { ) } - const mShare = menuShare({ - visibility: notification.status?.visibility, - type: 'status', - url: notification.status?.url || notification.status?.uri - }) - const mStatus = menuStatus({ status: notification.status, queryKey }) - const mInstance = menuInstance({ status: notification.status, queryKey }) - if (!isMyAccount) { let filterResults: FilteredProps['filterResults'] = [] const [filterRevealed, setFilterRevealed] = useState(false) @@ -143,67 +131,18 @@ const TimelineNotifications: React.FC = ({ notification, queryKey }) => { spoilerHidden }} > - - - - notification.status && - navigation.push('Tab-Shared-Toot', { toot: notification.status }) - } - onLongPress={() => {}} - children={main()} - /> - - - - {[mShare, mStatus, mInstance].map((menu, i) => ( - - {menu.map((group, index) => ( - - {group.map(item => { - switch (item.type) { - case 'item': - return ( - - - {item.icon ? : null} - - ) - case 'sub': - return ( - // @ts-ignore - - - - {item.trigger.icon ? ( - - ) : null} - - - {item.items.map(sub => ( - - - {sub.icon ? ( - - ) : null} - - ))} - - - ) - } - })} - - ))} - - ))} - - + + notification.status && navigation.push('Tab-Shared-Toot', { toot: notification.status }) + } + onLongPress={() => {}} + children={main()} + /> ) diff --git a/src/components/Timeline/Shared/Card/Neodb.tsx b/src/components/Timeline/Shared/Card/Neodb.tsx index 69fe2018..0e8c453e 100644 --- a/src/components/Timeline/Shared/Card/Neodb.tsx +++ b/src/components/Timeline/Shared/Card/Neodb.tsx @@ -17,7 +17,15 @@ export const CardNeodb: React.FC = ({ card }) => { const { colors } = useTheme() const segments = Linking.parse(card.url).path?.split('/') - if (!segments || !(segments[0] === 'movie' || segments[0] === 'book' || segments[0] === 'tv')) + if ( + !segments || + !( + segments[0] === 'movie' || + segments[0] === 'book' || + (segments[0] === 'tv' && segments[1] !== 'season') || + segments[0] === 'game' + ) + ) return null const [headingLines, setHeadingLines] = useState(3) @@ -26,121 +34,104 @@ export const CardNeodb: React.FC = ({ card }) => { if (!data) return null - const pressableProps = { - style: { - marginTop: StyleConstants.Spacing.M, - backgroundColor: colors.shimmerDefault, - borderRadius: StyleConstants.BorderRadius, - padding: StyleConstants.Spacing.S, - flexDirection: 'row' as 'row' - }, - onPress: () => openLink(card.url) - } - const contentProps = { style: { flex: 1, gap: StyleConstants.Spacing.S } } - - const itemImage = data.cover_image_url ? ( - ( + - ) : null - const itemHeading = (value: string) => ( - setHeadingLines(nativeEvent.lines.length)} - /> - ) - const itemDetails = (value: string) => ( - + onPress={() => openLink(card.url)} + > + {data.cover_image_url ? ( + + ) : null} + + + setHeadingLines(nativeEvent.lines.length)} + children={heading.filter(d => d).join(' ')} + /> + + + + d).join(' / ')} + /> + + ) switch (segments[0]) { case 'movie': return ( - - {itemImage} - - {itemHeading( - [data.title, data.orig_title, data.year ? `(${data.year})` : null] - .filter(d => d) - .join(' ') - )} - - {itemDetails( - [ - data.duration - ? parseInt(data.duration).toString() === data.duration - ? `${data.duration}分钟` - : data.duration - : null, - data.area?.join(' '), - data.genre?.join(' '), - data.director?.join(' ') - ] - .filter(d => d) - .join(' / ') - )} - - + ) case 'book': return ( - - {itemImage} - - {itemHeading(data.title)} - - {itemDetails( - [ - data.author?.join(' '), - data.pages ? `${data.pages}页` : null, - data.language, - data.pub_house - ] - .filter(d => d) - .join(' / ') - )} - - + ) case 'tv': return ( - - {itemImage} - - {itemHeading( - [data.title, data.orig_title, data.year ? `(${data.year})` : null] - .filter(d => d) - .join(' ') - )} - - {itemDetails( - [ - data.season_count ? `共${data.season_count}季` : null, - data.area?.join(' '), - data.genre?.join(' '), - data.director?.join(' ') - ] - .filter(d => d) - .join(' / ') - )} - - + + ) + case 'game': + return ( + ) default: return null diff --git a/src/components/Timeline/Shared/Card/index.tsx b/src/components/Timeline/Shared/Card/index.tsx index 6d6ac8e5..5dc3012a 100644 --- a/src/components/Timeline/Shared/Card/index.tsx +++ b/src/components/Timeline/Shared/Card/index.tsx @@ -4,6 +4,7 @@ import openLink from '@components/openLink' import CustomText from '@components/Text' import { useNavigation } from '@react-navigation/native' import { StackNavigationProp } from '@react-navigation/stack' +import { isDevelopment } from '@utils/helpers/checkEnvironment' import { urlMatcher } from '@utils/helpers/urlMatcher' import { TabLocalStackParamList } from '@utils/navigation/navigators' import { useAccountQuery } from '@utils/queryHooks/account' @@ -17,14 +18,19 @@ import TimelineDefault from '../../Default' import StatusContext from '../Context' import { CardNeodb } from './Neodb' +const CARD_URL_BLACKLISTS = ['weibo.com', 'weibo.cn'] + const TimelineCard: React.FC = () => { const { status, spoilerHidden, disableDetails, inThread } = useContext(StatusContext) if (!status || !status.card) return null + if (CARD_URL_BLACKLISTS.find(domain => status.card?.url.includes(`${domain}/`))) return null + const { i18n } = useTranslation() if ( - status.card.url.includes('://neodb.social/') && - i18n.language.toLowerCase().startsWith('zh-hans') + (status.card.url.includes('://neodb.social/') && + i18n.language.toLowerCase().startsWith('zh-hans')) || + isDevelopment ) { return } diff --git a/src/components/Timeline/Shared/HeaderDefault.tsx b/src/components/Timeline/Shared/HeaderDefault.tsx index 33b9235c..a4b58d56 100644 --- a/src/components/Timeline/Shared/HeaderDefault.tsx +++ b/src/components/Timeline/Shared/HeaderDefault.tsx @@ -1,4 +1,5 @@ import menuAccount from '@components/contextMenu/account' +import menuInstance from '@components/contextMenu/instance' import menuShare from '@components/contextMenu/share' import menuStatus from '@components/contextMenu/status' import Icon from '@components/Icon' @@ -17,7 +18,8 @@ import HeaderSharedReplies from './HeaderShared/Replies' import HeaderSharedVisibility from './HeaderShared/Visibility' const TimelineHeaderDefault: React.FC = () => { - const { queryKey, status, disableDetails, rawContent, isRemote } = useContext(StatusContext) + const { queryKey, status, disableDetails, rawContent, isRemote, highlighted } = + useContext(StatusContext) if (!status) return null const { colors } = useTheme() @@ -37,6 +39,7 @@ const TimelineHeaderDefault: React.FC = () => { ...(status && { status }) }) const mStatus = menuStatus({ status, queryKey }) + const mInstance = highlighted ? menuInstance({ status, queryKey }) : [] return ( @@ -87,7 +90,7 @@ const TimelineHeaderDefault: React.FC = () => { - {[mShare, mAccount, mStatus].map((menu, i) => ( + {[mShare, mAccount, mStatus, mInstance].map((menu, i) => ( {menu.map((group, index) => ( diff --git a/src/i18n/be/screens/tabs.json b/src/i18n/be/screens/tabs.json index 7df2598c..7aa5286a 100644 --- a/src/i18n/be/screens/tabs.json +++ b/src/i18n/be/screens/tabs.json @@ -387,12 +387,12 @@ "account": { "actions": { "accessibilityLabel": "Дзеянні для карыстальніка {{user}}", - "accessibilityHint": "" + "accessibilityHint": "Вы можаце ігнараваць, блакіраваць або абагуліць гэтага карыстальніка" }, "followed_by": " падпісаны на вас", "privateNote": "", "moved": "", - "created_at": "", + "created_at": "Далучыўся: {{date}}", "summary": { "statuses_count": "{{count}} допісаў" }, @@ -467,7 +467,7 @@ "toot": { "name": "Абмеркаванні", "remoteFetch": { - "title": "", + "title": "Змяшчае аддаленае змесціва", "message": "" } }, diff --git a/src/screens/Tabs/Me/Preferences/Filter.tsx b/src/screens/Tabs/Me/Preferences/Filter.tsx index cf98bee7..d01d6ca5 100644 --- a/src/screens/Tabs/Me/Preferences/Filter.tsx +++ b/src/screens/Tabs/Me/Preferences/Filter.tsx @@ -176,18 +176,14 @@ const TabMePreferencesFilter: React.FC< ...(parseInt(expiration) && { expires_in: parseInt(expiration) }), - ...(keywords.filter(keyword => keyword.length).length - ? { - keywords_attributes: keywords - .filter(keyword => keyword.length) - .map(keyword => ({ keyword, whole_word: true })) - } - : params.filter.keywords.length && { - keywords_attributes: params.filter.keywords.map(keyword => ({ - ...keyword, - _destroy: true - })) - }) + keywords_attributes: keywords.map((keyword, index) => + !!params.filter.keywords[index] + ? { + id: params.filter.keywords[index].id, + ...(keyword.length ? { keyword, whole_word: true } : { _destroy: true }) + } + : { keyword, whole_word: true } + ) } }) .then(() => { diff --git a/src/screens/Tabs/Shared/Account/Information/Actions.tsx b/src/screens/Tabs/Shared/Account/Information/Actions.tsx index a9948d9d..c312059b 100644 --- a/src/screens/Tabs/Shared/Account/Information/Actions.tsx +++ b/src/screens/Tabs/Shared/Account/Information/Actions.tsx @@ -37,9 +37,10 @@ const AccountInformationActions: React.FC = () => { return (