diff --git a/App.tsx b/App.tsx index 367edb13..31149e8a 100644 --- a/App.tsx +++ b/App.tsx @@ -6,6 +6,7 @@ import { resetLocal } from '@root/utils/slices/instancesSlice' import ThemeManager from '@utils/styles/ThemeManager' import chalk from 'chalk' import * as Analytics from 'expo-firebase-analytics' +import { Audio } from 'expo-av' import * as SplashScreen from 'expo-splash-screen' import React, { useCallback, useEffect, useState } from 'react' import { enableScreens } from 'react-native-screens' @@ -51,6 +52,12 @@ Sentry.init({ startingLog('log', 'initializing react-query') const queryClient = new QueryClient() +startingLog('log', 'setting audio playback default options') +Audio.setAudioModeAsync({ + playsInSilentModeIOS: true, + interruptionModeIOS: 1 +}) + startingLog('log', 'initializing native screen') enableScreens() diff --git a/app.config.ts b/app.config.ts index 08d53232..21a2580e 100644 --- a/app.config.ts +++ b/app.config.ts @@ -57,5 +57,8 @@ export default (): ExpoConfig => ({ measurementId: 'G-3J0FS8WV5J' } } + }, + experiments: { + turboModules: true } }) diff --git a/babel.config.js b/babel.config.js index a7820c61..71b5eb8a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -3,7 +3,7 @@ module.exports = function (api) { return { presets: ['babel-preset-expo'], plugins: [ - ['@babel/plugin-proposal-optional-chaining'], + '@babel/plugin-proposal-optional-chaining', [ 'module-resolver', { @@ -17,7 +17,8 @@ module.exports = function (api) { '@utils': './src/utils' } } - ] + ], + 'react-native-reanimated/plugin' ] } } diff --git a/package.json b/package.json index cf9bfa4c..7b2c8cbb 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,12 @@ }, "dependencies": { "@react-native-community/masked-view": "0.1.10", - "@react-native-community/netinfo": "^5.9.9", + "@react-native-community/netinfo": "^5.9.7", "@react-native-community/segmented-control": "2.2.1", - "@react-native-community/slider": "3.0.3", "@react-navigation/bottom-tabs": "^5.11.2", "@react-navigation/native": "^5.8.10", "@reduxjs/toolkit": "^1.5.0", + "@sharcoux/slider": "^5.0.1", "axios": "^0.21.1", "buffer": "^6.0.3", "expo": "^40.0.0", @@ -53,7 +53,7 @@ "react-native-gesture-handler": "~1.8.0", "react-native-htmlview": "^0.16.0", "react-native-image-zoom-viewer": "^3.0.1", - "react-native-reanimated": "~1.13.0", + "react-native-reanimated": "2.0.0-rc.0", "react-native-safe-area-context": "3.1.9", "react-native-screens": "~2.15.0", "react-native-shimmer-placeholder": "^2.0.6", @@ -116,4 +116,4 @@ ] }, "private": true -} +} \ No newline at end of file diff --git a/src/@types/mastodon.d.ts b/src/@types/mastodon.d.ts index 15dce8cb..afc9ab90 100644 --- a/src/@types/mastodon.d.ts +++ b/src/@types/mastodon.d.ts @@ -303,7 +303,14 @@ declare namespace Mastodon { type Notification = { // Base id: string - type: 'follow' | 'mention' | 'reblog' | 'favourite' | 'poll' + type: + | 'follow' + | 'follow_request' + | 'mention' + | 'reblog' + | 'favourite' + | 'poll' + | 'status' created_at: string account: Account diff --git a/src/Index.tsx b/src/Index.tsx index 69d58321..508518b4 100644 --- a/src/Index.tsx +++ b/src/Index.tsx @@ -1,14 +1,16 @@ import client from '@api/client' +import haptics from '@components/haptics' import Icon from '@components/Icon' +import { toast, toastConfig } from '@components/toast' import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native' import ScreenLocal from '@screens/Local' -import ScreenPublic from '@screens/Public' -import ScreenNotifications from '@screens/Notifications' import ScreenMe from '@screens/Me' +import ScreenNotifications from '@screens/Notifications' +import ScreenPublic from '@screens/Public' import { timelineFetch } from '@utils/fetches/timelineFetch' import { getLocalNotification, @@ -18,13 +20,12 @@ import { } from '@utils/slices/instancesSlice' import { useTheme } from '@utils/styles/ThemeManager' import { themes } from '@utils/styles/themes' -import { toast, toastConfig } from '@components/toast' import * as Analytics from 'expo-firebase-analytics' import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { StatusBar } from 'react-native' import Toast from 'react-native-toast-message' -import { useDispatch, useSelector } from 'react-redux' import { useInfiniteQuery } from 'react-query' +import { useDispatch, useSelector } from 'react-redux' const Tab = createBottomTabNavigator() @@ -214,12 +215,13 @@ const Index: React.FC = ({ localCorrupt }) => { }), [localInstance] ) - const tabScreenComposeListeners = useCallback( - ({ navigation }) => ({ + const tabScreenComposeListeners = useMemo( + () => ({ tabPress: (e: any) => { e.preventDefault() if (localInstance) { - navigation.navigate('Screen-Shared-Compose') + haptics('Medium') + navigationRef.current?.navigate('Screen-Shared-Compose') } } }), diff --git a/src/components/BottomSheet.tsx b/src/components/BottomSheet.tsx index 93541c53..e8f7d2cc 100644 --- a/src/components/BottomSheet.tsx +++ b/src/components/BottomSheet.tsx @@ -1,16 +1,19 @@ -import React, { useEffect, useRef } from 'react' -import { - Animated, - Dimensions, - Modal, - PanResponder, - StyleSheet, - View -} from 'react-native' +import React from 'react' +import { Dimensions, Modal, StyleSheet, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { useTheme } from '@utils/styles/ThemeManager' import { StyleConstants } from '@utils/styles/constants' import Button from '@components/Button' +import { PanGestureHandler } from 'react-native-gesture-handler' +import Animated, { + Extrapolate, + interpolate, + runOnJS, + useAnimatedGestureHandler, + useAnimatedStyle, + useSharedValue, + withTiming +} from 'react-native-reanimated' export interface Props { children: React.ReactNode @@ -22,76 +25,63 @@ const BottomSheet: React.FC = ({ children, visible, handleDismiss }) => { const { theme } = useTheme() const insets = useSafeAreaInsets() - const panY = useRef(new Animated.Value(Dimensions.get('screen').height)) - .current - const top = panY.interpolate({ - inputRange: [-1, 0, 1], - outputRange: [0, 0, 1] - }) - const resetModal = Animated.timing(panY, { - toValue: 0, - duration: 300, - useNativeDriver: false - }) - - const closeModal = Animated.timing(panY, { - toValue: Dimensions.get('screen').height, - duration: 350, - useNativeDriver: false - }) - - const panResponder = useRef( - PanResponder.create({ - onStartShouldSetPanResponder: () => true, - onMoveShouldSetPanResponder: () => true, - onPanResponderMove: Animated.event([null, { dy: panY }], { - useNativeDriver: false - }), - onPanResponderRelease: (e, gs) => { - if (gs.dy > 0 && gs.vy > 1) { - return closeModal.start(() => handleDismiss()) - } else if (gs.dy === 0 && gs.vy === 0) { - return closeModal.start(() => handleDismiss()) - } - return resetModal.start() - } - }) - ).current - - useEffect(() => { - if (visible) { - resetModal.start() + const screenHeight = Dimensions.get('screen').height + const panY = useSharedValue(0) + const styleTop = useAnimatedStyle(() => { + return { + top: interpolate( + panY.value, + [0, screenHeight], + [0, screenHeight], + Extrapolate.CLAMP + ) } - }, [visible]) + }) + const callDismiss = () => { + handleDismiss() + } + const onGestureEvent = useAnimatedGestureHandler({ + onActive: ({ translationY }) => { + panY.value = translationY + }, + onEnd: ({ velocityY }) => { + if (velocityY > 500) { + runOnJS(callDismiss)() + } else { + panY.value = withTiming(0) + } + } + }) return ( - + - - {children} -