diff --git a/App.tsx b/App.tsx index 73ace6f4..a206104d 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,11 @@ +import { ActionSheetProvider } from '@expo/react-native-action-sheet' import Index from '@root/Index' +import dev from '@root/startup/dev' +import sentry from '@root/startup/sentry' +import log from '@root/startup/log' +import audio from '@root/startup/audio' +import onlineStatus from '@root/startup/onlineStatus' +import netInfo from '@root/startup/netInfo' import { persistor, store } from '@root/store' import ThemeManager from '@utils/styles/ThemeManager' import * as SplashScreen from 'expo-splash-screen' @@ -7,17 +14,22 @@ import { enableScreens } from 'react-native-screens' import { QueryClient, QueryClientProvider } from 'react-query' import { Provider } from 'react-redux' import { PersistGate } from 'redux-persist/integration/react' -import dev from '@root/startup/dev' -import sentry from '@root/startup/sentry' -import log from '@root/startup/log' -import audio from '@root/startup/audio' -import onlineStatus from '@root/startup/onlineStatus' -import netInfo from '@root/startup/netInfo' +import { LogBox, Platform } from 'react-native' +import Moment from 'react-moment' + +import moment from 'moment' +import 'moment/min/locales' + +if (Platform.OS === 'android') { + LogBox.ignoreLogs(['Setting a timer for a long period of time']) +} dev() sentry() audio() onlineStatus() +Moment.globalMoment = moment +Moment.startPooledTimer(1000) log('log', 'react-query', 'initializing') const queryClient = new QueryClient() @@ -68,9 +80,11 @@ const App: React.FC = () => { log('log', 'App', 'loading actual app :)') require('@root/i18n/i18n') return ( - - - + + + + + ) } else { return null diff --git a/app.config.ts b/app.config.ts index 21a2580e..5ee1889b 100644 --- a/app.config.ts +++ b/app.config.ts @@ -24,6 +24,10 @@ export default (): ExpoConfig => ({ }, googleServicesFile: './configs/GoogleService-Info.plist' }, + android: { + package: 'com.xmflsct.app.mastodon', + googleServicesFile: './configs/google-services.json' + }, // locales: { // zh: { // CFBundleDisplayName: '我的嘟嘟' @@ -57,8 +61,5 @@ export default (): ExpoConfig => ({ measurementId: 'G-3J0FS8WV5J' } } - }, - experiments: { - turboModules: true } }) diff --git a/configs/Microsoft_AutoUpdate_4.30.20121301_Updater.pkg b/configs/Microsoft_AutoUpdate_4.30.20121301_Updater.pkg deleted file mode 100644 index f54c3035..00000000 Binary files a/configs/Microsoft_AutoUpdate_4.30.20121301_Updater.pkg and /dev/null differ diff --git a/configs/google-services.json b/configs/google-services.json new file mode 100644 index 00000000..c074d550 --- /dev/null +++ b/configs/google-services.json @@ -0,0 +1,46 @@ +{ + "project_info": { + "project_number": "661638997772", + "project_id": "xmflsct-mastodon-app", + "storage_bucket": "xmflsct-mastodon-app.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:661638997772:android:0f929e1c9ebd7f969f8b29", + "android_client_info": { + "package_name": "com.xmflsct.app.mastodon" + } + }, + "oauth_client": [ + { + "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDUw4s-mhQsHvs4hdIsldsi68ZIygM5MC4" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "661638997772-6aiqk97aema0rt280i7nfar3ha2mlgno.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "661638997772-sqa4raeghhrieqt9guljhcul9b51dvna.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "com.xmflsct.app.mastodon" + } + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/package.json b/package.json index 90f86a6e..965720d0 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "test": "jest --watchAll" }, "dependencies": { + "@expo/react-native-action-sheet": "^3.8.0", "@react-native-async-storage/async-storage": "^1.13.2", "@react-native-community/masked-view": "0.1.10", "@react-native-community/netinfo": "^5.9.7", @@ -43,10 +44,12 @@ "gl-react-expo": "^4.0.1", "i18next": "^19.8.4", "lodash": "^4.17.20", + "moment": "^2.29.1", "pretty-bytes": "^5.5.0", "react": "16.13.1", "react-dom": "16.13.1", "react-i18next": "^11.8.5", + "react-moment": "^1.1.1", "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.0.tar.gz", "react-native-animated-spinkit": "^1.4.2", "react-native-expo-image-cache": "^4.1.0", @@ -100,4 +103,4 @@ "typescript": "~4.1.3" }, "private": true -} \ No newline at end of file +} diff --git a/src/Index.tsx b/src/Index.tsx index 457a082f..35a76da9 100644 --- a/src/Index.tsx +++ b/src/Index.tsx @@ -108,7 +108,6 @@ const Index: React.FC = ({ localCorrupt }) => { refetchIntervalInBackground: true } }) - const prevNotification = useSelector(getLocalNotification) useEffect(() => { if (queryNotification.data?.pages) { @@ -244,7 +243,7 @@ const Index: React.FC = ({ localCorrupt }) => { ) return ( <> - + = ({ localCorrupt }) => { - + {/* */} ) diff --git a/src/components/CustomRefreshControl.tsx b/src/components/CustomRefreshControl.tsx deleted file mode 100644 index 18369a28..00000000 --- a/src/components/CustomRefreshControl.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { QueryKeyTimeline } from '@utils/queryHooks/timeline' -import React, { useRef } from 'react' -import { RefreshControl } from 'react-native' -import { InfiniteData, useQueryClient } from 'react-query' - -export interface Props { - queryKey: QueryKeyTimeline - isFetchingPreviousPage: boolean - isFetching: boolean - fetchPreviousPage: () => void - refetch: () => void -} - -const CustomRefreshControl = React.memo( - ({ - queryKey, - isFetchingPreviousPage, - isFetching, - fetchPreviousPage, - refetch - }: Props) => { - const queryClient = useQueryClient() - const refreshCount = useRef(0) - - return ( - { - if (refreshCount.current < 2) { - await fetchPreviousPage() - refreshCount.current++ - } else { - queryClient.setQueryData | undefined>( - queryKey, - data => { - if (data) { - return { - pages: data.pages.slice(1), - pageParams: data.pageParams.slice(1) - } - } - } - ) - await refetch() - refreshCount.current = 0 - } - }} - /> - ) - }, - (prev, next) => { - let skipUpdate = true - skipUpdate = prev.isFetchingPreviousPage === next.isFetchingPreviousPage - skipUpdate = prev.isFetching === next.isFetching - return skipUpdate - } -) - -export default CustomRefreshControl diff --git a/src/components/Menu/Row.tsx b/src/components/Menu/Row.tsx index 3ba447a8..026bb1bd 100644 --- a/src/components/Menu/Row.tsx +++ b/src/components/Menu/Row.tsx @@ -65,7 +65,7 @@ const MenuRow: React.FC = ({ {iconFront && ( @@ -118,7 +118,7 @@ const MenuRow: React.FC = ({ <> @@ -139,6 +139,7 @@ const styles = StyleSheet.create({ core: { flex: 1, flexDirection: 'row', + alignItems: 'center', paddingLeft: StyleConstants.Spacing.Global.PagePadding, paddingRight: StyleConstants.Spacing.Global.PagePadding }, diff --git a/src/components/Parse/HTML.tsx b/src/components/Parse/HTML.tsx index b25f2cb8..d2bfc527 100644 --- a/src/components/Parse/HTML.tsx +++ b/src/components/Parse/HTML.tsx @@ -45,12 +45,9 @@ const renderNode = ({ ? routeParams.hashtag !== tag[1] && routeParams.hashtag !== tag[2] : true return ( - { !disableDetails && differentTag && @@ -59,9 +56,16 @@ const renderNode = ({ }) }} > - {node.children[0].data} - {node.children[1]?.children[0].data} - + + {node.children[0].data} + {node.children[1]?.children[0].data} + + ) } else if (classes.includes('mention') && mentions) { const accountIndex = mentions.findIndex( @@ -71,12 +75,9 @@ const renderNode = ({ ? routeParams.account.id !== mentions[accountIndex].id : true return ( - { accountIndex !== -1 && !disableDetails && @@ -86,9 +87,16 @@ const renderNode = ({ }) }} > - {node.children[0].data} - {node.children[1]?.children[0].data} - + + {node.children[0].data} + {node.children[1]?.children[0].data} + + ) } } else { @@ -99,12 +107,9 @@ const renderNode = ({ const shouldBeTag = tags && tags.filter(tag => `#${tag.name}` === content).length > 0 return ( - !disableDetails && !shouldBeTag ? await openLink(href) @@ -113,15 +118,22 @@ const renderNode = ({ }) } > - {!shouldBeTag ? ( - - ) : null} - {content || (showFullLink ? href : domain[1])} - + + {!shouldBeTag ? ( + + ) : null} + {content || (showFullLink ? href : domain[1])} + + ) } break @@ -206,7 +218,7 @@ const ParseHTML: React.FC = ({ }, []) return ( - + = ({ layoutAnimation() setExpanded(!expanded) }} - style={{ marginTop: expanded ? 0 : -lineHeight * 2.25 }} + style={{ + marginTop: expanded + ? 0 + : -lineHeight * (numberOfLines === 0 ? 1 : 2) + }} > = ({ name, content }) => { ) return ( - + = ({ () => , [hasNextPage] ) + + const queryClient = useQueryClient() + const refreshCount = useRef(0) const refreshControl = useMemo( () => ( - { + if (refreshCount.current < 2) { + await fetchPreviousPage() + refreshCount.current++ + } else { + queryClient.setQueryData | undefined>( + queryKey, + data => { + if (data) { + return { + pages: data.pages.slice(1), + pageParams: data.pageParams.slice(1) + } + } + } + ) + await refetch() + refreshCount.current = 0 + } + }} /> ), [isFetchingPreviousPage, isFetching] @@ -199,10 +221,10 @@ const Timeline: React.FC = ({ {...(queryKey && queryKey[1].page === 'RemotePublic' && { ListHeaderComponent })} {...(toot && isSuccess && { onScrollToIndexFailed })} - maintainVisibleContentPosition={{ - minIndexForVisible: 0, - autoscrollToTopThreshold: 2 - }} + // maintainVisibleContentPosition={{ + // minIndexForVisible: 0, + // autoscrollToTopThreshold: 2 + // }} /> ) } diff --git a/src/components/Timelines/Timeline/End.tsx b/src/components/Timelines/Timeline/End.tsx index 12474a33..5c381fc5 100644 --- a/src/components/Timelines/Timeline/End.tsx +++ b/src/components/Timelines/Timeline/End.tsx @@ -20,7 +20,7 @@ const TimelineEnd: React.FC = ({ hasNextPage }) => { ) : ( fallbacks to defaults if not provided + i18nKey='timeline:shared.end.message' components={[ = ({ queryKey, status, reblog }) => { onSuccess: (_, params) => { const theParams = params as MutationVarsTimelineUpdateStatusProperty if ( + // Un-bookmark from bookmarks page (queryKey[1].page === 'Bookmarks' && theParams.payload.property === 'bookmarked') || + // Un-favourite from favourites page (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) + } else if (theParams.payload.property === 'reblogged') { + // When reblogged, update cache of following page + const tempQueryKey: QueryKeyTimeline = [ + 'Timeline', + { page: 'Following' } + ] + queryClient.invalidateQueries(tempQueryKey) + } else if (theParams.payload.property === 'favourited') { + // When favourited, update favourited page + const tempQueryKey: QueryKeyTimeline = [ + 'Timeline', + { page: 'Favourites' } + ] + queryClient.invalidateQueries(tempQueryKey) + } else if (theParams.payload.property === 'bookmarked') { + // When bookmarked, update bookmark page + const tempQueryKey: QueryKeyTimeline = [ + 'Timeline', + { page: 'Bookmarks' } + ] + queryClient.invalidateQueries(tempQueryKey) } }, onError: (err: any, params, oldData) => { @@ -115,23 +149,18 @@ const TimelineActions: React.FC = ({ queryKey, status, reblog }) => { }), [status.bookmarked] ) - const onPressShare = useCallback( - () => - ActionSheetIOS.showShareActionSheetWithOptions( - { - url: status.uri, - excludedActivityTypes: [ - 'com.apple.UIKit.activity.Mail', - 'com.apple.UIKit.activity.Print', - 'com.apple.UIKit.activity.SaveToCameraRoll', - 'com.apple.UIKit.activity.OpenInIBooks' - ] - }, - () => haptics('Error'), - () => haptics('Success') - ), - [] - ) + const onPressShare = useCallback(() => { + switch (Platform.OS) { + case 'ios': + return Share.share({ + url: status.uri + }) + case 'android': + return Share.share({ + message: status.uri + }) + } + }, []) const childrenReply = useMemo( () => ( @@ -139,7 +168,7 @@ const TimelineActions: React.FC = ({ queryKey, status, reblog }) => { {status.replies_count > 0 && ( = ({ queryKey, status, reblog }) => { ? theme.disabled : iconColorAction(status.reblogged) } - size={StyleConstants.Font.Size.M + 2} + size={StyleConstants.Font.Size.L} /> ), [status.reblogged] @@ -175,7 +204,7 @@ const TimelineActions: React.FC = ({ queryKey, status, reblog }) => { ), [status.favourited] @@ -185,18 +214,14 @@ const TimelineActions: React.FC = ({ queryKey, status, reblog }) => { ), [status.bookmarked] ) const childrenShare = useMemo( () => ( - + ), [] ) @@ -252,6 +277,7 @@ const styles = StyleSheet.create({ width: '20%', flexDirection: 'row', justifyContent: 'center', + alignItems: 'center', paddingVertical: StyleConstants.Spacing.S } }) diff --git a/src/components/Timelines/Timeline/Shared/HeaderActions/Root.tsx b/src/components/Timelines/Timeline/Shared/HeaderActions/Root.tsx index 808ed97f..2f9f3964 100644 --- a/src/components/Timelines/Timeline/Shared/HeaderActions/Root.tsx +++ b/src/components/Timelines/Timeline/Shared/HeaderActions/Root.tsx @@ -36,7 +36,7 @@ const HeaderActions = React.memo( ), [] diff --git a/src/components/Timelines/Timeline/Shared/HeaderConversation.tsx b/src/components/Timelines/Timeline/Shared/HeaderConversation.tsx index 6c1f5ee8..177a3fc9 100644 --- a/src/components/Timelines/Timeline/Shared/HeaderConversation.tsx +++ b/src/components/Timelines/Timeline/Shared/HeaderConversation.tsx @@ -65,7 +65,7 @@ const HeaderConversation: React.FC = ({ queryKey, conversation }) => { ), [] diff --git a/src/components/Timelines/Timeline/Shared/HeaderShared/Created.tsx b/src/components/Timelines/Timeline/Shared/HeaderShared/Created.tsx index a47f4e9e..b576a3a1 100644 --- a/src/components/Timelines/Timeline/Shared/HeaderShared/Created.tsx +++ b/src/components/Timelines/Timeline/Shared/HeaderShared/Created.tsx @@ -1,8 +1,8 @@ -import relativeTime from '@components/relativeTime' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' -import React, { useEffect, useState } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' +import Moment from 'react-moment' import { StyleSheet, Text } from 'react-native' export interface Props { @@ -13,16 +13,10 @@ const HeaderSharedCreated: React.FC = ({ created_at }) => { const { theme } = useTheme() const { i18n } = useTranslation() - const [since, setSince] = useState(relativeTime(created_at, i18n.language)) - useEffect(() => { - const timer = setTimeout(() => { - setSince(relativeTime(created_at, i18n.language)) - }, 1000) - return () => clearTimeout(timer) - }, [since]) - return ( - {since} + + + ) } diff --git a/src/components/Timelines/Timeline/Shared/Poll.tsx b/src/components/Timelines/Timeline/Shared/Poll.tsx index d318c351..b31e7952 100644 --- a/src/components/Timelines/Timeline/Shared/Poll.tsx +++ b/src/components/Timelines/Timeline/Shared/Poll.tsx @@ -10,8 +10,10 @@ import { } from '@utils/queryHooks/timeline' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' +import { maxBy } from 'lodash' import React, { useCallback, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' +import { Trans, useTranslation } from 'react-i18next' +import Moment from 'react-moment' import { Pressable, StyleSheet, Text, View } from 'react-native' import { useQueryClient } from 'react-query' @@ -123,16 +125,24 @@ const TimelinePoll: React.FC = ({ } else { return ( - {t('shared.poll.meta.expiration.until', { - at: relativeTime(poll.expires_at, i18n.language) - })} + + ]} + /> ) } }, [mode, poll.expired, poll.expires_at]) const isSelected = useCallback( - (index: number): any => + (index: number): string => allOptions[index] ? `Check${poll.multiple ? 'Square' : 'Circle'}` : `${poll.multiple ? 'Square' : 'Circle'}`, @@ -140,6 +150,8 @@ const TimelinePoll: React.FC = ({ ) const pollBodyDisallow = useMemo(() => { + const maxValue = maxBy(poll.options, option => option.votes_count) + ?.votes_count return poll.options.map((option, index) => ( @@ -152,7 +164,7 @@ const TimelinePoll: React.FC = ({ } size={StyleConstants.Font.Size.M} color={ - poll.own_votes?.includes(index) ? theme.primary : theme.disabled + poll.own_votes?.includes(index) ? theme.blue : theme.disabled } /> @@ -160,7 +172,11 @@ const TimelinePoll: React.FC = ({ {poll.votes_count - ? Math.round((option.votes_count / poll.voters_count) * 100) + ? Math.round( + (option.votes_count / + (poll.voters_count || poll.votes_count)) * + 100 + ) : 0} % @@ -171,9 +187,11 @@ const TimelinePoll: React.FC = ({ styles.background, { width: `${Math.round( - (option.votes_count / poll.voters_count) * 100 + (option.votes_count / (poll.voters_count || poll.votes_count)) * + 100 )}%`, - backgroundColor: theme.disabled + backgroundColor: + option.votes_count === maxValue ? theme.blue : theme.disabled } ]} /> @@ -221,14 +239,28 @@ const TimelinePoll: React.FC = ({ )) }, [mode, allOptions]) + const pollVoteCounts = useMemo(() => { + if (poll.voters_count !== null) { + return ( + + {t('shared.poll.meta.count.voters', { count: poll.voters_count })} + + ) + } else if (poll.votes_count !== null) { + return ( + + {t('shared.poll.meta.count.votes', { count: poll.votes_count })} + + ) + } + }, [poll.voters_count, poll.votes_count]) + return ( {poll.expired || poll.voted ? pollBodyDisallow : pollBodyAllow} {pollButton} - - {t('shared.poll.meta.voted', { count: poll.voters_count })} - + {pollVoteCounts} {pollExpiration} diff --git a/src/components/relativeTime.ts b/src/components/relativeTime.ts deleted file mode 100644 index d4b103c3..00000000 --- a/src/components/relativeTime.ts +++ /dev/null @@ -1,27 +0,0 @@ -const relativeTime = (date: string, language: string) => { - const units = { - year: 24 * 60 * 60 * 1000 * 365, - month: (24 * 60 * 60 * 1000 * 365) / 12, - day: 24 * 60 * 60 * 1000, - hour: 60 * 60 * 1000, - minute: 60 * 1000, - second: 1000 - } - - const rtf = new Intl.RelativeTimeFormat(language, { - numeric: 'auto' - }) - - const elapsed = +new Date(date) - +new Date() - - // "Math.abs" accounts for both "past" & "future" scenarios - for (const u in units) { - // @ts-ignore - if (Math.abs(elapsed) > units[u] || u == 'second') { - // @ts-ignore - return rtf.format(Math.round(elapsed / units[u]), u) - } - } -} - -export default relativeTime diff --git a/src/i18n/i18n.ts b/src/i18n/i18n.ts index 5ae6ba83..fc9520e1 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/i18n.ts @@ -13,29 +13,24 @@ import { store } from '@root/store' if (!getSettingsLanguage(store.getState())) { const deviceLocal = Localization.locale if (deviceLocal.startsWith('zh')) { - store.dispatch(changeLanguage('zh')) + store.dispatch(changeLanguage('zh-CN')) } else { - store.dispatch(changeLanguage('en')) + store.dispatch(changeLanguage('en-US')) } } - i18next.use(initReactI18next).init({ - lng: getSettingsLanguage(store.getState()), - fallbackLng: 'en', - supportedLngs: ['zh', 'en'], - nonExplicitSupportedLngs: true, + lng: 'zh-CN', + fallbackLng: 'en-US', + supportedLngs: ['zh-CN', 'en-US'], ns: ['common'], defaultNS: 'common', - resources: { - zh: zh, - en: en - }, + resources: { 'zh-CN': zh, 'en-US': en }, saveMissing: true, missingKeyHandler: (lng, ns, key, fallbackValue) => { - console.warn('i18n missing: ' + ns + ' : ' + key) + console.log('i18n missing: ' + lng + ' - ' + ns + ' : ' + key) }, // react options diff --git a/src/i18n/zh/components/timeline.ts b/src/i18n/zh/components/timeline.ts index a98e56f2..246213d5 100644 --- a/src/i18n/zh/components/timeline.ts +++ b/src/i18n/zh/components/timeline.ts @@ -123,11 +123,14 @@ export default { vote: '投票', refresh: '刷新' }, + count: { + voters: '已投{{count}}人 • ', + votes: '{{count}}票 • ' + }, expiration: { expired: '投票已结束', - until: '{{at}}截止' - }, - voted: '已投{{count}}人 • ' + until: '<0 />截止' + } } } } diff --git a/src/screens/Me.tsx b/src/screens/Me.tsx index ed0d46e9..6dd68a24 100644 --- a/src/screens/Me.tsx +++ b/src/screens/Me.tsx @@ -19,7 +19,7 @@ const ScreenMe: React.FC = () => { const { t } = useTranslation() return ( - + > = ({ - route: { - params: { navigateAway } - }, - navigation -}) => { +>> = ({ route: { params }, navigation }) => { useEffect(() => { - if (navigateAway) { - navigation.navigate(navigateAway) + if (params && params.navigateAway) { + console.log('oops') + navigation.navigate(params.navigateAway) } - }, [navigateAway]) + }, [params]) const localActiveIndex = useSelector(getLocalActiveIndex) const scrollRef = useRef(null) diff --git a/src/screens/Me/Settings.tsx b/src/screens/Me/Settings.tsx index 963daf3b..e05cb521 100644 --- a/src/screens/Me/Settings.tsx +++ b/src/screens/Me/Settings.tsx @@ -1,5 +1,6 @@ import Button from '@components/Button' import { MenuContainer, MenuRow } from '@components/Menu' +import { useActionSheet } from '@expo/react-native-action-sheet' import { useNavigation } from '@react-navigation/native' import haptics from '@root/components/haptics' import { persistor } from '@root/store' @@ -23,11 +24,13 @@ import { useTheme } from '@utils/styles/ThemeManager' import prettyBytes from 'pretty-bytes' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { ActionSheetIOS, StyleSheet, Text } from 'react-native' +import { StyleSheet, Text } from 'react-native' import { CacheManager } from 'react-native-expo-image-cache' +import { ScrollView } from 'react-native-gesture-handler' import { useDispatch, useSelector } from 'react-redux' const DevDebug: React.FC = () => { + const { showActionSheetWithOptions } = useActionSheet() const localActiveIndex = useSelector(getLocalActiveIndex) const localInstances = useSelector(getLocalInstances) @@ -43,7 +46,7 @@ const DevDebug: React.FC = () => { content={localInstances.length.toString()} iconBack='ChevronRight' onPress={() => - ActionSheetIOS.showActionSheetWithOptions( + showActionSheetWithOptions( { options: localInstances .map(instance => { @@ -71,6 +74,7 @@ const DevDebug: React.FC = () => { } const ScreenMeSettings: React.FC = () => { + const { showActionSheetWithOptions } = useActionSheet() const navigation = useNavigation() const { t, i18n } = useTranslation('meSettings') const { setTheme, theme } = useTheme() @@ -87,15 +91,16 @@ const ScreenMeSettings: React.FC = () => { }, []) return ( - <> + - ActionSheetIOS.showActionSheetWithOptions( + showActionSheetWithOptions( { + title: t('content.language.heading'), options: [ t('content.language.options.zh'), t('content.language.options.en'), @@ -107,13 +112,13 @@ const ScreenMeSettings: React.FC = () => { switch (buttonIndex) { case 0: haptics('Success') - dispatch(changeLanguage('zh')) - i18n.changeLanguage('zh') + dispatch(changeLanguage('zh-CN')) + i18n.changeLanguage('zh-CN') break case 1: haptics('Success') - dispatch(changeLanguage('en')) - i18n.changeLanguage('en') + dispatch(changeLanguage('en-US')) + i18n.changeLanguage('en-US') break } } @@ -125,8 +130,9 @@ const ScreenMeSettings: React.FC = () => { content={t(`content.theme.options.${settingsTheme}`)} iconBack='ChevronRight' onPress={() => - ActionSheetIOS.showActionSheetWithOptions( + showActionSheetWithOptions( { + title: t('content.theme.heading'), options: [ t('content.theme.options.auto'), t('content.theme.options.light'), @@ -161,8 +167,9 @@ const ScreenMeSettings: React.FC = () => { content={t(`content.browser.options.${settingsBrowser}`)} iconBack='ChevronRight' onPress={() => - ActionSheetIOS.showActionSheetWithOptions( + showActionSheetWithOptions( { + title: t('content.browser.heading'), options: [ t('content.browser.options.internal'), t('content.browser.options.external'), @@ -226,7 +233,7 @@ const ScreenMeSettings: React.FC = () => { {__DEV__ ? : null} - + ) } diff --git a/src/screens/Me/Switch.tsx b/src/screens/Me/Switch.tsx index 65007df2..391c10db 100644 --- a/src/screens/Me/Switch.tsx +++ b/src/screens/Me/Switch.tsx @@ -8,7 +8,7 @@ const Stack = createNativeStackNavigator() const ScreenMeSwitch: React.FC = ({ navigation }) => { return ( - + { diff --git a/src/screens/Public.tsx b/src/screens/Public.tsx index 95a6587d..bf1b5516 100644 --- a/src/screens/Public.tsx +++ b/src/screens/Public.tsx @@ -1,5 +1,4 @@ import Timelines from '@components/Timelines' -import { getRemoteUrl } from '@utils/slices/instancesSlice' import React from 'react' import { useTranslation } from 'react-i18next' diff --git a/src/screens/Shared/Account/Information.tsx b/src/screens/Shared/Account/Information.tsx index 4c9a6883..4a3ac645 100644 --- a/src/screens/Shared/Account/Information.tsx +++ b/src/screens/Shared/Account/Information.tsx @@ -73,19 +73,24 @@ const AccountInformation: React.FC = ({ - {account?.fields && account.fields.length > 0 ? ( - + {!ownAccount ? ( + <> + {account?.fields && account.fields.length > 0 ? ( + + ) : null} + {account?.note && + account.note.length > 0 && + account.note !== '

' ? ( + // Empty notes might generate empty p tag + + ) : null} + + ) : null} - {account?.note && - account.note.length > 0 && - account.note !== '

' ? ( - // Empty notes might generate empty p tag - - ) : null} - - -
) diff --git a/src/screens/Shared/Announcements.tsx b/src/screens/Shared/Announcements.tsx index 5c7e013c..1970e599 100644 --- a/src/screens/Shared/Announcements.tsx +++ b/src/screens/Shared/Announcements.tsx @@ -1,8 +1,6 @@ -import client from '@api/client' import Button from '@components/Button' import haptics from '@components/haptics' import { ParseHTML } from '@components/Parse' -import relativeTime from '@components/relativeTime' import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs' import { useAnnouncementMutation, @@ -12,6 +10,7 @@ import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import Moment from 'react-moment' import { Dimensions, Image, @@ -25,33 +24,6 @@ import { FlatList, ScrollView } from 'react-native-gesture-handler' import { SafeAreaView } from 'react-native-safe-area-context' import { SharedAnnouncementsProp } from './sharedScreens' -const fireMutation = async ({ - announcementId, - type, - name, - me -}: { - announcementId: Mastodon.Announcement['id'] - type: 'reaction' | 'dismiss' - name?: Mastodon.AnnouncementReaction['name'] - me?: boolean -}) => { - switch (type) { - case 'reaction': - return client<{}>({ - method: me ? 'delete' : 'put', - instance: 'local', - url: `announcements/${announcementId}/reactions/${name}` - }) - case 'dismiss': - return client<{}>({ - method: 'post', - instance: 'local', - url: `announcements/${announcementId}/dismiss` - }) - } -} - const ScreenSharedAnnouncements: React.FC = ({ route: { params: { showAll = false } @@ -108,7 +80,13 @@ const ScreenSharedAnnouncements: React.FC = ({ ]} > - 发布于 {relativeTime(item.published_at, i18n.language)} + 发布于{' '} + = ({ edges={hasKeyboard ? ['left', 'right'] : ['left', 'right', 'bottom']} > - + { + const { showActionSheetWithOptions } = useActionSheet() const { composeState, composeDispatch } = useContext(ComposeContext) const { theme } = useTheme() @@ -24,7 +26,10 @@ const ComposeActions: React.FC = () => { if (composeState.poll.active) return if (composeState.attachments.uploads.length < 4) { - return await addAttachment({ composeDispatch }) + return await addAttachment({ + composeDispatch, + showActionSheetWithOptions + }) } }, [composeState.poll.active, composeState.attachments.uploads]) @@ -64,7 +69,7 @@ const ComposeActions: React.FC = () => { }, [composeState.visibility]) const visibilityOnPress = useCallback(() => { if (!composeState.visibilityLock) { - ActionSheetIOS.showActionSheetWithOptions( + showActionSheetWithOptions( { options: ['公开', '不公开', '仅关注着', '私信', '取消'], cancelButtonIndex: 4 diff --git a/src/screens/Shared/Compose/Attachments.tsx b/src/screens/Shared/Compose/Attachments.tsx index 6c8a6684..6f8cf070 100644 --- a/src/screens/Shared/Compose/Attachments.tsx +++ b/src/screens/Shared/Compose/Attachments.tsx @@ -1,6 +1,7 @@ import Button from '@components/Button' import haptics from '@components/haptics' import Icon from '@components/Icon' +import { useActionSheet } from '@expo/react-native-action-sheet' import { useNavigation } from '@react-navigation/native' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' @@ -28,6 +29,7 @@ import { ExtendedAttachment } from './utils/types' const DEFAULT_HEIGHT = 200 const ComposeAttachments: React.FC = () => { + const { showActionSheetWithOptions } = useActionSheet() const { composeState, composeDispatch } = useContext(ComposeContext) const { theme } = useTheme() const navigation = useNavigation() @@ -192,7 +194,9 @@ const ComposeAttachments: React.FC = () => { backgroundColor: theme.backgroundOverlay } ]} - onPress={async () => await addAttachment({ composeDispatch })} + onPress={async () => + await addAttachment({ composeDispatch, showActionSheetWithOptions }) + } >