mirror of https://github.com/tooot-app/app
commit
6a1f8b3c73
|
@ -68,12 +68,12 @@
|
||||||
"expo-updates": "0.13.1",
|
"expo-updates": "0.13.1",
|
||||||
"expo-video-thumbnails": "6.3.0",
|
"expo-video-thumbnails": "6.3.0",
|
||||||
"expo-web-browser": "10.2.0",
|
"expo-web-browser": "10.2.0",
|
||||||
"i18next": "21.8.4",
|
"i18next": "21.8.8",
|
||||||
"li": "1.3.0",
|
"li": "1.3.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
"react-i18next": "11.16.9",
|
"react-i18next": "11.17.0",
|
||||||
"react-intl": "^6.0.3",
|
"react-intl": "^6.0.3",
|
||||||
"react-native": "0.68.2",
|
"react-native": "0.68.2",
|
||||||
"react-native-animated-spinkit": "1.5.2",
|
"react-native-animated-spinkit": "1.5.2",
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
"react-native-svg": "12.3.0",
|
"react-native-svg": "12.3.0",
|
||||||
"react-native-swipe-list-view": "3.2.9",
|
"react-native-swipe-list-view": "3.2.9",
|
||||||
"react-native-tab-view": "3.1.1",
|
"react-native-tab-view": "3.1.1",
|
||||||
"react-query": "3.39.0",
|
"react-query": "3.39.1",
|
||||||
"react-redux": "8.0.2",
|
"react-redux": "8.0.2",
|
||||||
"redux-persist": "6.0.0",
|
"redux-persist": "6.0.0",
|
||||||
"rn-placeholder": "3.0.3",
|
"rn-placeholder": "3.0.3",
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
"patch-package": "6.4.7",
|
"patch-package": "6.4.7",
|
||||||
"postinstall-postinstall": "2.1.0",
|
"postinstall-postinstall": "2.1.0",
|
||||||
"react-native-clean-project": "4.0.1",
|
"react-native-clean-project": "4.0.1",
|
||||||
"typescript": "4.7.2"
|
"typescript": "4.7.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "17.0.43",
|
"@types/react": "17.0.43",
|
||||||
|
|
15
src/App.tsx
15
src/App.tsx
|
@ -17,10 +17,9 @@ import {
|
||||||
} from '@utils/slices/settingsSlice'
|
} from '@utils/slices/settingsSlice'
|
||||||
import ThemeManager from '@utils/styles/ThemeManager'
|
import ThemeManager from '@utils/styles/ThemeManager'
|
||||||
import 'expo-asset'
|
import 'expo-asset'
|
||||||
import * as Notifications from 'expo-notifications'
|
|
||||||
import * as SplashScreen from 'expo-splash-screen'
|
import * as SplashScreen from 'expo-splash-screen'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { AppState, LogBox, Platform } from 'react-native'
|
import { LogBox, Platform } from 'react-native'
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||||
import 'react-native-image-keyboard'
|
import 'react-native-image-keyboard'
|
||||||
import { enableFreeze } from 'react-native-screens'
|
import { enableFreeze } from 'react-native-screens'
|
||||||
|
@ -44,18 +43,6 @@ const App: React.FC = () => {
|
||||||
log('log', 'App', 'rendering App')
|
log('log', 'App', 'rendering App')
|
||||||
const [localCorrupt, setLocalCorrupt] = useState<string>()
|
const [localCorrupt, setLocalCorrupt] = useState<string>()
|
||||||
|
|
||||||
const appStateEffect = useCallback(() => {
|
|
||||||
Notifications.setBadgeCountAsync(0)
|
|
||||||
Notifications.dismissAllNotificationsAsync()
|
|
||||||
}, [])
|
|
||||||
useEffect(() => {
|
|
||||||
const appStateListener = AppState.addEventListener('change', appStateEffect)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
appStateListener.remove()
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const delaySplash = async () => {
|
const delaySplash = async () => {
|
||||||
log('log', 'App', 'delay splash')
|
log('log', 'App', 'delay splash')
|
||||||
|
|
|
@ -29,6 +29,7 @@ export interface Props {
|
||||||
|
|
||||||
strokeWidth?: number
|
strokeWidth?: number
|
||||||
size?: 'S' | 'M' | 'L'
|
size?: 'S' | 'M' | 'L'
|
||||||
|
fontBold?: boolean
|
||||||
spacing?: 'XS' | 'S' | 'M' | 'L'
|
spacing?: 'XS' | 'S' | 'M' | 'L'
|
||||||
round?: boolean
|
round?: boolean
|
||||||
overlay?: boolean
|
overlay?: boolean
|
||||||
|
@ -48,6 +49,7 @@ const Button: React.FC<Props> = ({
|
||||||
disabled = false,
|
disabled = false,
|
||||||
strokeWidth,
|
strokeWidth,
|
||||||
size = 'M',
|
size = 'M',
|
||||||
|
fontBold = false,
|
||||||
spacing = 'S',
|
spacing = 'S',
|
||||||
round = false,
|
round = false,
|
||||||
overlay = false,
|
overlay = false,
|
||||||
|
@ -122,6 +124,7 @@ const Button: React.FC<Props> = ({
|
||||||
StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
StyleConstants.Font.Size[size] * (size === 'L' ? 1.25 : 1),
|
||||||
opacity: loading ? 0 : 1
|
opacity: loading ? 0 : 1
|
||||||
}}
|
}}
|
||||||
|
fontWeight={fontBold ? 'Bold' : 'Normal'}
|
||||||
children={content}
|
children={content}
|
||||||
testID='text'
|
testID='text'
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import Button from '@components/Button'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack'
|
||||||
|
import { RootStackParamList } from '@utils/navigation/navigators'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
sensitiveShown: boolean
|
||||||
|
text?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AttachmentAltText: React.FC<Props> = ({ sensitiveShown, text }) => {
|
||||||
|
if (!text) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigation = useNavigation<StackNavigationProp<RootStackParamList>>()
|
||||||
|
|
||||||
|
return !sensitiveShown ? (
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
right: StyleConstants.Spacing.S,
|
||||||
|
bottom: StyleConstants.Spacing.S
|
||||||
|
}}
|
||||||
|
overlay
|
||||||
|
size='S'
|
||||||
|
type='text'
|
||||||
|
content='ALT'
|
||||||
|
fontBold
|
||||||
|
onPress={() => {
|
||||||
|
navigation.navigate('Screen-Actions', { type: 'alt_text', text })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AttachmentAltText
|
|
@ -8,6 +8,7 @@ import { Audio } from 'expo-av'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
|
import AttachmentAltText from './AltText'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -127,6 +128,10 @@ const AttachmentAudio: React.FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
<AttachmentAltText
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
text={audio.description}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import GracefullyImage from '@components/GracefullyImage'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
|
import AttachmentAltText from './AltText'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -34,7 +35,9 @@ const AttachmentImage = ({
|
||||||
uri={{ original: image.preview_url, remote: image.remote_url }}
|
uri={{ original: image.preview_url, remote: image.remote_url }}
|
||||||
blurhash={image.blurhash}
|
blurhash={image.blurhash}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
analytics('timeline_shared_attachment_image_press', { id: image.id })
|
analytics('timeline_shared_attachment_image_press', {
|
||||||
|
id: image.id
|
||||||
|
})
|
||||||
navigateToImagesViewer(image.id)
|
navigateToImagesViewer(image.id)
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
@ -48,6 +51,10 @@ const AttachmentImage = ({
|
||||||
: image.meta.original.width / image.meta.original.height
|
: image.meta.original.width / image.meta.original.height
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<AttachmentAltText
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
text={image.description}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { View } from 'react-native'
|
import { View } from 'react-native'
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
|
import AttachmentAltText from './AltText'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -75,6 +76,10 @@ const AttachmentUnsupported: React.FC<Props> = ({
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
<AttachmentAltText
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
text={attachment.description}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,11 @@ import Button from '@components/Button'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
|
import { ResizeMode, Video, VideoFullscreenUpdate } from 'expo-av'
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import {
|
import { AppState, AppStateStatus, Pressable, View } from 'react-native'
|
||||||
AppState,
|
|
||||||
AppStateStatus,
|
|
||||||
Pressable,
|
|
||||||
StyleSheet,
|
|
||||||
View
|
|
||||||
} from 'react-native'
|
|
||||||
import { Blurhash } from 'react-native-blurhash'
|
import { Blurhash } from 'react-native-blurhash'
|
||||||
import attachmentAspectRatio from './aspectRatio'
|
import attachmentAspectRatio from './aspectRatio'
|
||||||
import analytics from '@components/analytics'
|
import analytics from '@components/analytics'
|
||||||
|
import AttachmentAltText from './AltText'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
total: number
|
total: number
|
||||||
|
@ -88,10 +83,12 @@ const AttachmentVideo: React.FC<Props> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={{
|
||||||
styles.base,
|
flex: 1,
|
||||||
{ aspectRatio: attachmentAspectRatio({ total, index }) }
|
flexBasis: '50%',
|
||||||
]}
|
padding: StyleConstants.Spacing.XS / 2,
|
||||||
|
aspectRatio: attachmentAspectRatio({ total, index })
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Video
|
<Video
|
||||||
accessibilityLabel={video.description}
|
accessibilityLabel={video.description}
|
||||||
|
@ -127,7 +124,17 @@ const AttachmentVideo: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Pressable style={styles.overlay} onPress={gifv ? playOnPress : null}>
|
<Pressable
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
onPress={gifv ? playOnPress : null}
|
||||||
|
>
|
||||||
{sensitiveShown ? (
|
{sensitiveShown ? (
|
||||||
video.blurhash ? (
|
video.blurhash ? (
|
||||||
<Blurhash
|
<Blurhash
|
||||||
|
@ -149,25 +156,13 @@ const AttachmentVideo: React.FC<Props> = ({
|
||||||
loading={videoLoading}
|
loading={videoLoading}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
<AttachmentAltText
|
||||||
|
sensitiveShown={sensitiveShown}
|
||||||
|
text={video.description}
|
||||||
|
/>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
base: {
|
|
||||||
flex: 1,
|
|
||||||
flexBasis: '50%',
|
|
||||||
padding: StyleConstants.Spacing.XS / 2
|
|
||||||
},
|
|
||||||
overlay: {
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default AttachmentVideo
|
export default AttachmentVideo
|
||||||
|
|
|
@ -91,7 +91,9 @@ const TimelineHeaderNotification = ({ queryKey, notification }: Props) => {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HeaderSharedCreated
|
<HeaderSharedCreated
|
||||||
created_at={notification.created_at}
|
created_at={
|
||||||
|
notification.status?.created_at || notification.created_at
|
||||||
|
}
|
||||||
edited_at={notification.status?.edited_at}
|
edited_at={notification.status?.edited_at}
|
||||||
/>
|
/>
|
||||||
{notification.status?.visibility ? (
|
{notification.status?.visibility ? (
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"content": {
|
"content": {
|
||||||
"button": {
|
"altText": {
|
||||||
"apply": "$t(common:buttons.apply)",
|
"heading": "Alternative Text"
|
||||||
"cancel": "$t(common:buttons.cancel)"
|
|
||||||
},
|
},
|
||||||
"notificationsFilter": {
|
"notificationsFilter": {
|
||||||
"heading": "Show notification types",
|
"heading": "Show notification types",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
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, { useCallback, useEffect, useMemo } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||||
import {
|
import {
|
||||||
|
@ -30,6 +30,7 @@ import {
|
||||||
} from 'react-native-safe-area-context'
|
} from 'react-native-safe-area-context'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import ActionsAccount from './Actions/Account'
|
import ActionsAccount from './Actions/Account'
|
||||||
|
import ActionsAltText from './Actions/AltText'
|
||||||
import ActionsDomain from './Actions/Domain'
|
import ActionsDomain from './Actions/Domain'
|
||||||
import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
||||||
import ActionsShare from './Actions/Share'
|
import ActionsShare from './Actions/Share'
|
||||||
|
@ -173,6 +174,8 @@ const ScreenActions = ({
|
||||||
)
|
)
|
||||||
case 'notifications_filter':
|
case 'notifications_filter':
|
||||||
return <ActionsNotificationsFilter />
|
return <ActionsNotificationsFilter />
|
||||||
|
case 'alt_text':
|
||||||
|
return <ActionsAltText text={params.text} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import Button from '@components/Button'
|
||||||
|
import MenuContainer from '@components/Menu/Container'
|
||||||
|
import MenuHeader from '@components/Menu/Header'
|
||||||
|
import CustomText from '@components/Text'
|
||||||
|
import { useNavigation } from '@react-navigation/native'
|
||||||
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Dimensions } from 'react-native'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActionsAltText: React.FC<Props> = ({ text }) => {
|
||||||
|
const navigation = useNavigation()
|
||||||
|
const { t } = useTranslation('screenActions')
|
||||||
|
const { colors } = useTheme()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuContainer>
|
||||||
|
<MenuHeader heading={t(`content.altText.heading`)} />
|
||||||
|
<ScrollView style={{ maxHeight: Dimensions.get('window').height / 2 }}>
|
||||||
|
<CustomText style={{ color: colors.primaryDefault }}>
|
||||||
|
{text}
|
||||||
|
</CustomText>
|
||||||
|
</ScrollView>
|
||||||
|
</MenuContainer>
|
||||||
|
<Button
|
||||||
|
type='text'
|
||||||
|
content={t('common:buttons.OK')}
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
style={{
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ActionsAltText
|
|
@ -10,7 +10,6 @@ import {
|
||||||
} from '@utils/slices/instancesSlice'
|
} from '@utils/slices/instancesSlice'
|
||||||
import { StyleConstants } from '@utils/styles/constants'
|
import { StyleConstants } from '@utils/styles/constants'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
import { StyleSheet } from 'react-native'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { useQueryClient } from 'react-query'
|
import { useQueryClient } from 'react-query'
|
||||||
|
@ -100,20 +99,16 @@ const ActionsNotificationsFilter: React.FC = () => {
|
||||||
</MenuContainer>
|
</MenuContainer>
|
||||||
<Button
|
<Button
|
||||||
type='text'
|
type='text'
|
||||||
content={t('content.button.apply')}
|
content={t('common:buttons.apply')}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
queryClient.resetQueries(queryKey)
|
queryClient.resetQueries(queryKey)
|
||||||
}}
|
}}
|
||||||
style={styles.button}
|
style={{
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default ActionsNotificationsFilter
|
export default ActionsNotificationsFilter
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
import {
|
import {
|
||||||
getVersionUpdate,
|
getVersionUpdate,
|
||||||
retriveVersionLatest
|
retriveVersionLatest
|
||||||
} from '@utils/slices/versionSlice'
|
} from '@utils/slices/appSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useEffect, useMemo } from 'react'
|
import React, { useCallback, useEffect, useMemo } from 'react'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import CustomText from '@components/Text'
|
import CustomText from '@components/Text'
|
||||||
import { useAppDispatch } from '@root/store'
|
import { useAppDispatch } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
|
import { getExpoToken } from '@utils/slices/appSlice'
|
||||||
import { updateInstancePush } from '@utils/slices/instances/updatePush'
|
import { updateInstancePush } from '@utils/slices/instances/updatePush'
|
||||||
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
|
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
|
||||||
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
|
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
|
||||||
|
@ -45,16 +46,12 @@ const TabMePush: React.FC = () => {
|
||||||
setPushEnabled(settings.granted)
|
setPushEnabled(settings.granted)
|
||||||
setPushCanAskAgain(settings.canAskAgain)
|
setPushCanAskAgain(settings.canAskAgain)
|
||||||
}
|
}
|
||||||
|
const expoToken = useSelector(getExpoToken)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDevelopment) {
|
if (isDevelopment) {
|
||||||
setPushAvailable(true)
|
setPushAvailable(true)
|
||||||
} else {
|
} else {
|
||||||
Notifications.getExpoPushTokenAsync({
|
setPushAvailable(!!expoToken)
|
||||||
experienceId: '@xmflsct/tooot',
|
|
||||||
applicationId: 'com.xmflsct.app.tooot'
|
|
||||||
})
|
|
||||||
.then(data => setPushAvailable(!!data))
|
|
||||||
.catch(() => setPushAvailable(false))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPush()
|
checkPush()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { MenuContainer, MenuRow } from '@components/Menu'
|
import { MenuContainer, MenuRow } from '@components/Menu'
|
||||||
import { getVersionUpdate } from '@utils/slices/versionSlice'
|
import { getVersionUpdate } from '@utils/slices/appSlice'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Linking, Platform } from 'react-native'
|
import { Linking, Platform } from 'react-native'
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { AnyAction, configureStore, Reducer } from '@reduxjs/toolkit'
|
||||||
import contextsMigration from '@utils/migrations/contexts/migration'
|
import contextsMigration from '@utils/migrations/contexts/migration'
|
||||||
import instancesMigration from '@utils/migrations/instances/migration'
|
import instancesMigration from '@utils/migrations/instances/migration'
|
||||||
import settingsMigration from '@utils/migrations/settings/migration'
|
import settingsMigration from '@utils/migrations/settings/migration'
|
||||||
|
import appSlice from '@utils/slices/appSlice'
|
||||||
import contextsSlice, { ContextsState } from '@utils/slices/contextsSlice'
|
import contextsSlice, { ContextsState } from '@utils/slices/contextsSlice'
|
||||||
import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice'
|
import instancesSlice, { InstancesState } from '@utils/slices/instancesSlice'
|
||||||
import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice'
|
import settingsSlice, { SettingsState } from '@utils/slices/settingsSlice'
|
||||||
import versionSlice from '@utils/slices/versionSlice'
|
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
|
||||||
import {
|
import {
|
||||||
|
@ -67,7 +67,7 @@ const store = configureStore({
|
||||||
SettingsState,
|
SettingsState,
|
||||||
AnyAction
|
AnyAction
|
||||||
>,
|
>,
|
||||||
version: versionSlice
|
app: appSlice
|
||||||
},
|
},
|
||||||
middleware: getDefaultMiddleware =>
|
middleware: getDefaultMiddleware =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
|
|
|
@ -20,6 +20,10 @@ export type RootStackParamList = {
|
||||||
| {
|
| {
|
||||||
type: 'notifications_filter'
|
type: 'notifications_filter'
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'alt_text'
|
||||||
|
text: string
|
||||||
|
}
|
||||||
'Screen-Announcements': { showAll: boolean }
|
'Screen-Announcements': { showAll: boolean }
|
||||||
'Screen-Compose':
|
'Screen-Compose':
|
||||||
| {
|
| {
|
||||||
|
|
|
@ -3,13 +3,15 @@ import apiTooot from '@api/tooot'
|
||||||
import { displayMessage } from '@components/Message'
|
import { displayMessage } from '@components/Message'
|
||||||
import navigationRef from '@helpers/navigationRef'
|
import navigationRef from '@helpers/navigationRef'
|
||||||
import { useAppDispatch } from '@root/store'
|
import { useAppDispatch } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
|
||||||
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
|
import { getExpoToken, retriveExpoToken } from '@utils/slices/appSlice'
|
||||||
import { disableAllPushes } from '@utils/slices/instancesSlice'
|
import { disableAllPushes } from '@utils/slices/instancesSlice'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { TFunction } from 'react-i18next'
|
import { TFunction } from 'react-i18next'
|
||||||
|
import { AppState } from 'react-native'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
export interface Params {
|
export interface Params {
|
||||||
t: TFunction<'screens'>
|
t: TFunction<'screens'>
|
||||||
|
@ -19,69 +21,84 @@ export interface Params {
|
||||||
const pushUseConnect = ({ t, instances }: Params) => {
|
const pushUseConnect = ({ t, instances }: Params) => {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(retriveExpoToken())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const expoToken = useSelector(getExpoToken)
|
||||||
|
|
||||||
|
const connect = () => {
|
||||||
|
apiTooot({
|
||||||
|
method: 'get',
|
||||||
|
url: `push/connect/${expoToken}`,
|
||||||
|
sentry: true
|
||||||
|
}).catch(error => {
|
||||||
|
if (error?.status == 404) {
|
||||||
|
displayMessage({
|
||||||
|
theme,
|
||||||
|
type: 'error',
|
||||||
|
duration: 'long',
|
||||||
|
message: t('pushError.message'),
|
||||||
|
description: t('pushError.description'),
|
||||||
|
onPress: () => {
|
||||||
|
navigationRef.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Me',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Me-Root'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
navigationRef.navigate('Screen-Tabs', {
|
||||||
|
screen: 'Tab-Me',
|
||||||
|
params: {
|
||||||
|
screen: 'Tab-Me-Settings'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch(disableAllPushes())
|
||||||
|
|
||||||
|
instances.forEach(instance => {
|
||||||
|
if (instance.push.global.value) {
|
||||||
|
apiGeneral<{}>({
|
||||||
|
method: 'delete',
|
||||||
|
domain: instance.url,
|
||||||
|
url: 'api/v1/push/subscription',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${instance.token}`
|
||||||
|
}
|
||||||
|
}).catch(() => console.log('error!!!'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushEnabled = instances.filter(instance => instance.push.global.value)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const appStateListener = AppState.addEventListener('change', state => {
|
||||||
|
console.log('changing state to', state)
|
||||||
|
if (expoToken && pushEnabled.length && state === 'active') {
|
||||||
|
Notifications.getBadgeCountAsync().then(count => {
|
||||||
|
if (count > 0) {
|
||||||
|
Notifications.setBadgeCountAsync(0)
|
||||||
|
connect()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
appStateListener.remove()
|
||||||
|
}
|
||||||
|
}, [expoToken, pushEnabled.length])
|
||||||
|
|
||||||
return useEffect(() => {
|
return useEffect(() => {
|
||||||
const connect = async () => {
|
if (expoToken && pushEnabled.length) {
|
||||||
const expoToken = isDevelopment
|
|
||||||
? 'DEVELOPMENT_TOKEN_1'
|
|
||||||
: (
|
|
||||||
await Notifications.getExpoPushTokenAsync({
|
|
||||||
experienceId: '@xmflsct/tooot',
|
|
||||||
applicationId: 'com.xmflsct.app.tooot'
|
|
||||||
})
|
|
||||||
).data
|
|
||||||
|
|
||||||
apiTooot({
|
|
||||||
method: 'get',
|
|
||||||
url: `push/connect/${expoToken}`,
|
|
||||||
sentry: true
|
|
||||||
}).catch(error => {
|
|
||||||
if (error?.status == 404) {
|
|
||||||
displayMessage({
|
|
||||||
theme,
|
|
||||||
type: 'error',
|
|
||||||
duration: 'long',
|
|
||||||
message: t('pushError.message'),
|
|
||||||
description: t('pushError.description'),
|
|
||||||
onPress: () => {
|
|
||||||
navigationRef.navigate('Screen-Tabs', {
|
|
||||||
screen: 'Tab-Me',
|
|
||||||
params: {
|
|
||||||
screen: 'Tab-Me-Root'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
navigationRef.navigate('Screen-Tabs', {
|
|
||||||
screen: 'Tab-Me',
|
|
||||||
params: {
|
|
||||||
screen: 'Tab-Me-Settings'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
dispatch(disableAllPushes())
|
|
||||||
|
|
||||||
instances.forEach(instance => {
|
|
||||||
if (instance.push.global.value) {
|
|
||||||
apiGeneral<{}>({
|
|
||||||
method: 'delete',
|
|
||||||
domain: instance.url,
|
|
||||||
url: 'api/v1/push/subscription',
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${instance.token}`
|
|
||||||
}
|
|
||||||
}).catch(() => console.log('error!!!'))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const pushEnabled = instances.filter(instance => instance.push.global.value)
|
|
||||||
if (pushEnabled.length) {
|
|
||||||
connect()
|
connect()
|
||||||
}
|
}
|
||||||
}, [instances])
|
}, [expoToken, pushEnabled.length])
|
||||||
}
|
}
|
||||||
|
|
||||||
export default pushUseConnect
|
export default pushUseConnect
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import apiGeneral from '@api/general'
|
||||||
|
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
|
||||||
|
import { RootState } from '@root/store'
|
||||||
|
import { isDevelopment } from '@utils/checkEnvironment'
|
||||||
|
import Constants from 'expo-constants'
|
||||||
|
import * as Notifications from 'expo-notifications'
|
||||||
|
|
||||||
|
export const retriveExpoToken = createAsyncThunk(
|
||||||
|
'app/expoToken',
|
||||||
|
async (): Promise<string> => {
|
||||||
|
if (isDevelopment) {
|
||||||
|
return 'DEVELOPMENT_TOKEN_1'
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await Notifications.getExpoPushTokenAsync({
|
||||||
|
experienceId: '@xmflsct/tooot',
|
||||||
|
applicationId: 'com.xmflsct.app.tooot'
|
||||||
|
})
|
||||||
|
return res.data
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const retriveVersionLatest = createAsyncThunk(
|
||||||
|
'app/versionUpdate',
|
||||||
|
async (): Promise<string> => {
|
||||||
|
const res = await apiGeneral<{ latest: string }>({
|
||||||
|
method: 'get',
|
||||||
|
domain: 'tooot.app',
|
||||||
|
url: 'version.json'
|
||||||
|
})
|
||||||
|
return res.body.latest
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export type AppState = {
|
||||||
|
expoToken?: string
|
||||||
|
versionUpdate: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appInitialState: AppState = {
|
||||||
|
expoToken: undefined,
|
||||||
|
versionUpdate: false
|
||||||
|
}
|
||||||
|
|
||||||
|
const appSlice = createSlice({
|
||||||
|
name: 'app',
|
||||||
|
initialState: appInitialState,
|
||||||
|
reducers: {},
|
||||||
|
extraReducers: builder => {
|
||||||
|
builder
|
||||||
|
.addCase(retriveExpoToken.fulfilled, (state, action) => {
|
||||||
|
if (action.payload) {
|
||||||
|
state.expoToken = action.payload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addCase(retriveVersionLatest.fulfilled, (state, action) => {
|
||||||
|
if (action.payload && Constants.manifest?.version) {
|
||||||
|
if (
|
||||||
|
parseFloat(action.payload) > parseFloat(Constants.manifest.version)
|
||||||
|
) {
|
||||||
|
state.versionUpdate = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const getExpoToken = (state: RootState) => state.app.expoToken
|
||||||
|
export const getVersionUpdate = (state: RootState) => state.app.versionUpdate
|
||||||
|
|
||||||
|
export default appSlice.reducer
|
|
@ -48,8 +48,9 @@ const pushRegister = async (
|
||||||
|
|
||||||
const accountId = instanceAccount.id
|
const accountId = instanceAccount.id
|
||||||
const accountFull = `@${instanceAccount.acct}@${instanceUri}`
|
const accountFull = `@${instanceAccount.acct}@${instanceUri}`
|
||||||
|
const randomPath = (Math.random() + 1).toString(36).substring(2)
|
||||||
|
|
||||||
const endpoint = `https://${TOOOT_API_DOMAIN}/push/send/${expoToken}/${instanceUrl}/${accountId}`
|
const endpoint = `https://${TOOOT_API_DOMAIN}/push/send/${expoToken}/${instanceUrl}/${accountId}/${randomPath}`
|
||||||
const auth = base64.encodeFromByteArray(Random.getRandomBytes(16))
|
const auth = base64.encodeFromByteArray(Random.getRandomBytes(16))
|
||||||
|
|
||||||
const alerts = instancePush.alerts
|
const alerts = instancePush.alerts
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
|
||||||
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import * as Notifications from 'expo-notifications'
|
|
||||||
import pushRegister from './push/register'
|
import pushRegister from './push/register'
|
||||||
import pushUnregister from './push/unregister'
|
import pushUnregister from './push/unregister'
|
||||||
|
|
||||||
|
@ -13,14 +11,10 @@ export const updateInstancePush = createAsyncThunk(
|
||||||
{ getState }
|
{ getState }
|
||||||
): Promise<InstanceLatest['push']['keys']['auth'] | undefined> => {
|
): Promise<InstanceLatest['push']['keys']['auth'] | undefined> => {
|
||||||
const state = getState() as RootState
|
const state = getState() as RootState
|
||||||
const expoToken = isDevelopment
|
const expoToken = state.app.expoToken
|
||||||
? 'DEVELOPMENT_TOKEN_1'
|
if (!expoToken) {
|
||||||
: (
|
return Promise.reject()
|
||||||
await Notifications.getExpoPushTokenAsync({
|
}
|
||||||
experienceId: '@xmflsct/tooot',
|
|
||||||
applicationId: 'com.xmflsct.app.tooot'
|
|
||||||
})
|
|
||||||
).data
|
|
||||||
|
|
||||||
if (disable) {
|
if (disable) {
|
||||||
return await pushRegister(state, expoToken)
|
return await pushRegister(state, expoToken)
|
||||||
|
|
|
@ -2,7 +2,6 @@ import apiTooot from '@api/tooot'
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit'
|
import { createAsyncThunk } from '@reduxjs/toolkit'
|
||||||
import i18n from '@root/i18n/i18n'
|
import i18n from '@root/i18n/i18n'
|
||||||
import { RootState } from '@root/store'
|
import { RootState } from '@root/store'
|
||||||
import { isDevelopment } from '@utils/checkEnvironment'
|
|
||||||
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
import { InstanceLatest } from '@utils/migrations/instances/migration'
|
||||||
import * as Notifications from 'expo-notifications'
|
import * as Notifications from 'expo-notifications'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
|
@ -21,14 +20,10 @@ export const updateInstancePushDecode = createAsyncThunk(
|
||||||
return Promise.reject()
|
return Promise.reject()
|
||||||
}
|
}
|
||||||
|
|
||||||
const expoToken = isDevelopment
|
const expoToken = state.app.expoToken
|
||||||
? 'DEVELOPMENT_TOKEN_1'
|
if (!expoToken) {
|
||||||
: (
|
return Promise.reject()
|
||||||
await Notifications.getExpoPushTokenAsync({
|
}
|
||||||
experienceId: '@xmflsct/tooot',
|
|
||||||
applicationId: 'com.xmflsct.app.tooot'
|
|
||||||
})
|
|
||||||
).data
|
|
||||||
|
|
||||||
await apiTooot({
|
await apiTooot({
|
||||||
method: 'put',
|
method: 'put',
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import apiGeneral from '@api/general'
|
|
||||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
|
|
||||||
import { RootState } from '@root/store'
|
|
||||||
import Constants from 'expo-constants'
|
|
||||||
|
|
||||||
export const retriveVersionLatest = createAsyncThunk(
|
|
||||||
'version/latest',
|
|
||||||
async () => {
|
|
||||||
const res = await apiGeneral<{ latest: string }>({
|
|
||||||
method: 'get',
|
|
||||||
domain: 'tooot.app',
|
|
||||||
url: 'version.json'
|
|
||||||
})
|
|
||||||
return res.body.latest
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
export type VersionState = {
|
|
||||||
update: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const versionInitialState = {
|
|
||||||
update: false
|
|
||||||
}
|
|
||||||
|
|
||||||
const versionSlice = createSlice({
|
|
||||||
name: 'version',
|
|
||||||
initialState: versionInitialState,
|
|
||||||
reducers: {},
|
|
||||||
extraReducers: builder => {
|
|
||||||
builder.addCase(retriveVersionLatest.fulfilled, (state, action) => {
|
|
||||||
if (action.payload && Constants.manifest?.version) {
|
|
||||||
if (parseFloat(action.payload) > parseFloat(Constants.manifest.version)) {
|
|
||||||
state.update = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const getVersionUpdate = (state: RootState) => state.version.update
|
|
||||||
|
|
||||||
export default versionSlice.reducer
|
|
32
yarn.lock
32
yarn.lock
|
@ -5285,10 +5285,10 @@ https-proxy-agent@^5.0.0:
|
||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
i18next@21.8.4:
|
i18next@21.8.8:
|
||||||
version "21.8.4"
|
version "21.8.8"
|
||||||
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.4.tgz#646e23065752036b38d9fda8898c18139b9e8ebe"
|
resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.8.8.tgz#725a05f2529689d059bce17221cf86fcdccf7665"
|
||||||
integrity sha512-b3LQ5n9V1juu8UItb5x1QTI4OTvNqsNs/wetwQlBvfijEqks+N5HKMKSoevf8w0/RGUrDQ7g4cvVzF8WBp9pUw==
|
integrity sha512-iN/5JuWStyivyBgmUgy5BRiFs0lZrgCRaeV9q4yVH/eR9NID7pZSMt3rpF8C16GplchoEjDP0JalKwZMJ1CJAA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.17.2"
|
"@babel/runtime" "^7.17.2"
|
||||||
|
|
||||||
|
@ -7440,10 +7440,10 @@ react-freeze@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.0.tgz#b21c65fe1783743007c8c9a2952b1c8879a77354"
|
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.0.tgz#b21c65fe1783743007c8c9a2952b1c8879a77354"
|
||||||
integrity sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw==
|
integrity sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw==
|
||||||
|
|
||||||
react-i18next@11.16.9:
|
react-i18next@11.17.0:
|
||||||
version "11.16.9"
|
version "11.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.16.9.tgz#890cdac0c49120e075d6c520b43dbad3f91bd2df"
|
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.17.0.tgz#44a0689dac7903352733e40303b743fa465eb797"
|
||||||
integrity sha512-euXxWvcEAvsY7ZVkwx9ztCq4butqtsGHEkpkuo0RMj8Ru09IF9o2KxCyN+zyv51Nr0aBh/elaTIiR6fMb8YfVg==
|
integrity sha512-ewq2S4bVUTRqOMAdM/XvzCn9xUPIryzeBQRghmJ8lC6VI/8Kp7z1GwoLyt8j7GB2ywhN2SjPk7LU4sHzVeu7aw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.14.5"
|
"@babel/runtime" "^7.14.5"
|
||||||
html-escaper "^2.0.2"
|
html-escaper "^2.0.2"
|
||||||
|
@ -7654,10 +7654,10 @@ react-native@0.68.2:
|
||||||
whatwg-fetch "^3.0.0"
|
whatwg-fetch "^3.0.0"
|
||||||
ws "^6.1.4"
|
ws "^6.1.4"
|
||||||
|
|
||||||
react-query@3.39.0:
|
react-query@3.39.1:
|
||||||
version "3.39.0"
|
version "3.39.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.0.tgz#0caca7b0da98e65008bbcd4df0d25618c2100050"
|
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.1.tgz#3876c0fdac7a3b5a84e195534e5fa8fbdd628847"
|
||||||
integrity sha512-Od0IkSuS79WJOhzWBx/ys0x13+7wFqgnn64vBqqAAnZ9whocVhl/y1padD5uuZ6EIkXbFbInax0qvY7zGM0thA==
|
integrity sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.5.5"
|
"@babel/runtime" "^7.5.5"
|
||||||
broadcast-channel "^3.4.1"
|
broadcast-channel "^3.4.1"
|
||||||
|
@ -8792,10 +8792,10 @@ typedarray-to-buffer@^3.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-typedarray "^1.0.0"
|
is-typedarray "^1.0.0"
|
||||||
|
|
||||||
typescript@4.7.2:
|
typescript@4.7.3:
|
||||||
version "4.7.2"
|
version "4.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.3.tgz#8364b502d5257b540f9de4c40be84c98e23a129d"
|
||||||
integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==
|
integrity sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==
|
||||||
|
|
||||||
ua-parser-js@^0.7.19, ua-parser-js@^0.7.30:
|
ua-parser-js@^0.7.19, ua-parser-js@^0.7.30:
|
||||||
version "0.7.31"
|
version "0.7.31"
|
||||||
|
|
Loading…
Reference in New Issue