Merge pull request #316 from tooot-app/main

Test v4.0.4
This commit is contained in:
xmflsct 2022-06-03 23:29:25 +02:00 committed by GitHub
commit 6a1f8b3c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 330 additions and 211 deletions

View File

@ -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",

View File

@ -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')

View File

@ -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'
/> />

View File

@ -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

View File

@ -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>
) )
} }

View File

@ -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>
) )
} }

View File

@ -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>
) )
} }

View File

@ -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

View File

@ -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 ? (

View File

@ -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",

View File

@ -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} />
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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()

View File

@ -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'

View File

@ -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({

View File

@ -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':
| { | {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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',

View File

@ -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

View File

@ -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"