mirror of https://github.com/tooot-app/app
Fixed #471
This commit is contained in:
parent
fbfae52627
commit
18e7262f6f
|
@ -475,7 +475,8 @@ declare namespace Mastodon {
|
||||||
// Base
|
// Base
|
||||||
name: string
|
name: string
|
||||||
url: string
|
url: string
|
||||||
// history: types
|
history: { day: string; accounts: string; uses: string }[]
|
||||||
|
following: boolean // Since v4.0
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebSocketStream =
|
type WebSocketStream =
|
||||||
|
|
17
src/App.tsx
17
src/App.tsx
|
@ -20,6 +20,7 @@ import * as SplashScreen from 'expo-splash-screen'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { 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 { SafeAreaProvider } from 'react-native-safe-area-context'
|
||||||
import { enableFreeze } from 'react-native-screens'
|
import { enableFreeze } from 'react-native-screens'
|
||||||
import { QueryClientProvider } from 'react-query'
|
import { QueryClientProvider } from 'react-query'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from 'react-redux'
|
||||||
|
@ -95,13 +96,15 @@ const App: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionSheetProvider>
|
<SafeAreaProvider>
|
||||||
<AccessibilityManager>
|
<ActionSheetProvider>
|
||||||
<ThemeManager>
|
<AccessibilityManager>
|
||||||
<Screens localCorrupt={localCorrupt} />
|
<ThemeManager>
|
||||||
</ThemeManager>
|
<Screens localCorrupt={localCorrupt} />
|
||||||
</AccessibilityManager>
|
</ThemeManager>
|
||||||
</ActionSheetProvider>
|
</AccessibilityManager>
|
||||||
|
</ActionSheetProvider>
|
||||||
|
</SafeAreaProvider>
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
|
|
|
@ -4,10 +4,8 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import { getColors, Theme } from '@utils/styles/themes'
|
import { getColors, Theme } from '@utils/styles/themes'
|
||||||
import React, { RefObject } from 'react'
|
import React, { RefObject } from 'react'
|
||||||
import { AccessibilityInfo } from 'react-native'
|
import { AccessibilityInfo } from 'react-native'
|
||||||
import FlashMessage, {
|
import FlashMessage, { hideMessage, showMessage } from 'react-native-flash-message'
|
||||||
hideMessage,
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
showMessage
|
|
||||||
} from 'react-native-flash-message'
|
|
||||||
import haptics from './haptics'
|
import haptics from './haptics'
|
||||||
|
|
||||||
const displayMessage = ({
|
const displayMessage = ({
|
||||||
|
@ -112,6 +110,7 @@ const removeMessage = () => {
|
||||||
|
|
||||||
const Message = React.forwardRef<FlashMessage>((_, ref) => {
|
const Message = React.forwardRef<FlashMessage>((_, ref) => {
|
||||||
const { colors, theme } = useTheme()
|
const { colors, theme } = useTheme()
|
||||||
|
const insets = useSafeAreaInsets()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlashMessage
|
<FlashMessage
|
||||||
|
@ -125,7 +124,8 @@ const Message = React.forwardRef<FlashMessage>((_, ref) => {
|
||||||
shadowOffset: { width: 0, height: 0 },
|
shadowOffset: { width: 0, height: 0 },
|
||||||
shadowOpacity: theme === 'light' ? 0.16 : 0.24,
|
shadowOpacity: theme === 'light' ? 0.16 : 0.24,
|
||||||
shadowRadius: 4,
|
shadowRadius: 4,
|
||||||
paddingRight: StyleConstants.Spacing.M * 2
|
paddingRight: StyleConstants.Spacing.M * 2,
|
||||||
|
marginTop: insets.top
|
||||||
}}
|
}}
|
||||||
titleStyle={{
|
titleStyle={{
|
||||||
color: colors.primaryDefault,
|
color: colors.primaryDefault,
|
||||||
|
|
|
@ -23,5 +23,10 @@
|
||||||
"feature": "notification_types_positive_filter",
|
"feature": "notification_types_positive_filter",
|
||||||
"version": 3.5,
|
"version": 3.5,
|
||||||
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v3.5.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"feature": "follow_tags",
|
||||||
|
"version": 4.0,
|
||||||
|
"reference": "https://github.com/mastodon/mastodon/releases/tag/v4.0.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -310,6 +310,13 @@
|
||||||
"attachments": {
|
"attachments": {
|
||||||
"name": "<0 /><1>\"s media</1>"
|
"name": "<0 /><1>\"s media</1>"
|
||||||
},
|
},
|
||||||
|
"hashtag": {
|
||||||
|
"follow": "Follow",
|
||||||
|
"unfollow": "Unfollow"
|
||||||
|
},
|
||||||
|
"history": {
|
||||||
|
"name": "Edit History"
|
||||||
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"header": {
|
"header": {
|
||||||
"prefix": "Searching",
|
"prefix": "Searching",
|
||||||
|
@ -346,9 +353,6 @@
|
||||||
"reblogged_by": "{{count}} boosted",
|
"reblogged_by": "{{count}} boosted",
|
||||||
"favourited_by": "{{count}} favourited"
|
"favourited_by": "{{count}} favourited"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"history": {
|
|
||||||
"name": "Edit History"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,7 +9,6 @@ import * as VideoThumbnails from 'expo-video-thumbnails'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FlatList, Image, ScrollView, View } from 'react-native'
|
import { FlatList, Image, ScrollView, View } from 'react-native'
|
||||||
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const Share = ({
|
const Share = ({
|
||||||
|
@ -76,9 +75,7 @@ const Share = ({
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<Image source={{ uri: item }} style={{ width: 88, height: 88 }} />
|
<Image source={{ uri: item }} style={{ width: 88, height: 88 }} />
|
||||||
)}
|
)}
|
||||||
ItemSeparatorComponent={() => (
|
ItemSeparatorComponent={() => <View style={{ width: StyleConstants.Spacing.S }} />}
|
||||||
<View style={{ width: StyleConstants.Spacing.S }} />
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
@ -99,64 +96,60 @@ const ScreenAccountSelection = ({
|
||||||
const instances = useSelector(getInstances, () => true)
|
const instances = useSelector(getInstances, () => true)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<ScrollView
|
||||||
<ScrollView
|
style={{ marginBottom: StyleConstants.Spacing.L * 2 }}
|
||||||
style={{ marginBottom: StyleConstants.Spacing.L * 2 }}
|
keyboardShouldPersistTaps='always'
|
||||||
keyboardShouldPersistTaps='always'
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<View
|
{share ? <Share {...share} /> : null}
|
||||||
|
<CustomText
|
||||||
|
fontStyle='M'
|
||||||
|
fontWeight='Bold'
|
||||||
style={{
|
style={{
|
||||||
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
|
textAlign: 'center',
|
||||||
|
marginTop: StyleConstants.Spacing.L,
|
||||||
|
marginBottom: StyleConstants.Spacing.S,
|
||||||
|
color: colors.primaryDefault
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{share ? <Share {...share} /> : null}
|
{t('content.select_account')}
|
||||||
<CustomText
|
</CustomText>
|
||||||
fontStyle='M'
|
<View
|
||||||
fontWeight='Bold'
|
style={{
|
||||||
style={{
|
flex: 1,
|
||||||
textAlign: 'center',
|
flexDirection: 'row',
|
||||||
marginTop: StyleConstants.Spacing.L,
|
flexWrap: 'wrap',
|
||||||
marginBottom: StyleConstants.Spacing.S,
|
marginTop: StyleConstants.Spacing.M
|
||||||
color: colors.primaryDefault
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{instances.length
|
||||||
{t('content.select_account')}
|
? instances
|
||||||
</CustomText>
|
.slice()
|
||||||
<View
|
.sort((a, b) =>
|
||||||
style={{
|
`${a.uri}${a.account.acct}`.localeCompare(`${b.uri}${b.account.acct}`)
|
||||||
flex: 1,
|
)
|
||||||
flexDirection: 'row',
|
.map((instance, index) => {
|
||||||
flexWrap: 'wrap',
|
return (
|
||||||
marginTop: StyleConstants.Spacing.M
|
<AccountButton
|
||||||
}}
|
key={index}
|
||||||
>
|
instance={instance}
|
||||||
{instances.length
|
additionalActions={() => {
|
||||||
? instances
|
navigationRef.navigate('Screen-Compose', {
|
||||||
.slice()
|
type: 'share',
|
||||||
.sort((a, b) =>
|
...share
|
||||||
`${a.uri}${a.account.acct}`.localeCompare(
|
})
|
||||||
`${b.uri}${b.account.acct}`
|
}}
|
||||||
)
|
/>
|
||||||
)
|
)
|
||||||
.map((instance, index) => {
|
})
|
||||||
return (
|
: null}
|
||||||
<AccountButton
|
|
||||||
key={index}
|
|
||||||
instance={instance}
|
|
||||||
additionalActions={() => {
|
|
||||||
navigationRef.navigate('Screen-Compose', {
|
|
||||||
type: 'share',
|
|
||||||
...share
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
: null}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</View>
|
||||||
</SafeAreaProvider>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,7 @@ import { StyleConstants } from '@utils/styles/constants'
|
||||||
import { useTheme } from '@utils/styles/ThemeManager'
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
import React, { useCallback, useEffect } from 'react'
|
import React, { useCallback, useEffect } from 'react'
|
||||||
import { Dimensions, StyleSheet, View } from 'react-native'
|
import { Dimensions, StyleSheet, View } from 'react-native'
|
||||||
import {
|
import { PanGestureHandler, State, TapGestureHandler } from 'react-native-gesture-handler'
|
||||||
PanGestureHandler,
|
|
||||||
State,
|
|
||||||
TapGestureHandler
|
|
||||||
} from 'react-native-gesture-handler'
|
|
||||||
import Animated, {
|
import Animated, {
|
||||||
Extrapolate,
|
Extrapolate,
|
||||||
interpolate,
|
interpolate,
|
||||||
|
@ -17,10 +13,7 @@ import Animated, {
|
||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming
|
withTiming
|
||||||
} from 'react-native-reanimated'
|
} from 'react-native-reanimated'
|
||||||
import {
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
SafeAreaProvider,
|
|
||||||
useSafeAreaInsets
|
|
||||||
} from 'react-native-safe-area-context'
|
|
||||||
import ActionsAltText from './Actions/AltText'
|
import ActionsAltText from './Actions/AltText'
|
||||||
import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
import ActionsNotificationsFilter from './Actions/NotificationsFilter'
|
||||||
|
|
||||||
|
@ -39,12 +32,7 @@ const ScreenActions = ({
|
||||||
}, [])
|
}, [])
|
||||||
const styleTop = useAnimatedStyle(() => {
|
const styleTop = useAnimatedStyle(() => {
|
||||||
return {
|
return {
|
||||||
bottom: interpolate(
|
bottom: interpolate(panY.value, [0, screenHeight], [0, -screenHeight], Extrapolate.CLAMP)
|
||||||
panY.value,
|
|
||||||
[0, screenHeight],
|
|
||||||
[0, -screenHeight],
|
|
||||||
Extrapolate.CLAMP
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const dismiss = useCallback(() => {
|
const dismiss = useCallback(() => {
|
||||||
|
@ -73,45 +61,35 @@ const ScreenActions = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<Animated.View style={{ flex: 1 }}>
|
||||||
<Animated.View style={{ flex: 1 }}>
|
<TapGestureHandler
|
||||||
<TapGestureHandler
|
onHandlerStateChange={({ nativeEvent }) => {
|
||||||
onHandlerStateChange={({ nativeEvent }) => {
|
if (nativeEvent.state === State.ACTIVE) {
|
||||||
if (nativeEvent.state === State.ACTIVE) {
|
dismiss()
|
||||||
dismiss()
|
}
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
|
<Animated.View
|
||||||
|
style={[styles.overlay, { backgroundColor: colors.backgroundOverlayInvert }]}
|
||||||
>
|
>
|
||||||
<Animated.View
|
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
||||||
style={[
|
<Animated.View
|
||||||
styles.overlay,
|
style={[
|
||||||
{ backgroundColor: colors.backgroundOverlayInvert }
|
styles.container,
|
||||||
]}
|
styleTop,
|
||||||
>
|
{
|
||||||
<PanGestureHandler onGestureEvent={onGestureEvent}>
|
backgroundColor: colors.backgroundDefault,
|
||||||
<Animated.View
|
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
||||||
style={[
|
}
|
||||||
styles.container,
|
]}
|
||||||
styleTop,
|
>
|
||||||
{
|
<View style={[styles.handle, { backgroundColor: colors.primaryOverlay }]} />
|
||||||
backgroundColor: colors.backgroundDefault,
|
{actions()}
|
||||||
paddingBottom: insets.bottom || StyleConstants.Spacing.L
|
</Animated.View>
|
||||||
}
|
</PanGestureHandler>
|
||||||
]}
|
</Animated.View>
|
||||||
>
|
</TapGestureHandler>
|
||||||
<View
|
</Animated.View>
|
||||||
style={[
|
|
||||||
styles.handle,
|
|
||||||
{ backgroundColor: colors.primaryOverlay }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
{actions()}
|
|
||||||
</Animated.View>
|
|
||||||
</PanGestureHandler>
|
|
||||||
</Animated.View>
|
|
||||||
</TapGestureHandler>
|
|
||||||
</Animated.View>
|
|
||||||
</SafeAreaProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { Directions, Gesture, LongPressGestureHandler } from 'react-native-gestu
|
||||||
import { LiveTextImageView } from 'react-native-live-text-image-view'
|
import { LiveTextImageView } from 'react-native-live-text-image-view'
|
||||||
import { runOnJS, useSharedValue } from 'react-native-reanimated'
|
import { runOnJS, useSharedValue } from 'react-native-reanimated'
|
||||||
import { Zoom, createZoomListComponent } from 'react-native-reanimated-zoom'
|
import { Zoom, createZoomListComponent } from 'react-native-reanimated-zoom'
|
||||||
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||||
import saveImage from './ImageViewer/save'
|
import saveImage from './ImageViewer/save'
|
||||||
|
|
||||||
const ZoomFlatList = createZoomListComponent(FlatList)
|
const ZoomFlatList = createZoomListComponent(FlatList)
|
||||||
|
@ -153,7 +153,7 @@ const ScreenImagesViewer = ({
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider style={{ backgroundColor: 'black' }}>
|
<View style={{ backgroundColor: 'black' }}>
|
||||||
<StatusBar hidden />
|
<StatusBar hidden />
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
|
@ -232,7 +232,7 @@ const ScreenImagesViewer = ({
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</LongPressGestureHandler>
|
</LongPressGestureHandler>
|
||||||
</SafeAreaProvider>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import ContextMenu from 'react-native-context-menu-view'
|
import ContextMenu from 'react-native-context-menu-view'
|
||||||
import TabSharedRoot from './Shared/Root'
|
import TabShared from './Shared'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabLocalStackParamList>()
|
const Stack = createNativeStackNavigator<TabLocalStackParamList>()
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ const TabLocal = React.memo(
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{TabSharedRoot({ Stack })}
|
{TabShared({ Stack })}
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,7 @@ import TabMeSettings from './Me/Settings'
|
||||||
import TabMeSettingsFontsize from './Me/SettingsFontsize'
|
import TabMeSettingsFontsize from './Me/SettingsFontsize'
|
||||||
import TabMeSettingsLanguage from './Me/SettingsLanguage'
|
import TabMeSettingsLanguage from './Me/SettingsLanguage'
|
||||||
import TabMeSwitch from './Me/Switch'
|
import TabMeSwitch from './Me/Switch'
|
||||||
import TabSharedRoot from './Shared/Root'
|
import TabShared from './Shared'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabMeStackParamList>()
|
const Stack = createNativeStackNavigator<TabMeStackParamList>()
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ const TabMe = React.memo(
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{TabSharedRoot({ Stack })}
|
{TabShared({ Stack })}
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import React, { useCallback, useMemo } from 'react'
|
import React, { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import TabSharedRoot from './Shared/Root'
|
import TabShared from './Shared'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabNotificationsStackParamList>()
|
const Stack = createNativeStackNavigator<TabNotificationsStackParamList>()
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ const TabNotifications = React.memo(
|
||||||
children={children}
|
children={children}
|
||||||
options={screenOptionsRoot}
|
options={screenOptionsRoot}
|
||||||
/>
|
/>
|
||||||
{TabSharedRoot({ Stack })}
|
{TabShared({ Stack })}
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@ import React, { useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Dimensions } from 'react-native'
|
import { Dimensions } from 'react-native'
|
||||||
import { TabView } from 'react-native-tab-view'
|
import { TabView } from 'react-native-tab-view'
|
||||||
import TabSharedRoot from './Shared/Root'
|
import TabShared from './Shared'
|
||||||
|
|
||||||
const Stack = createNativeStackNavigator<TabPublicStackParamList>()
|
const Stack = createNativeStackNavigator<TabPublicStackParamList>()
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ const TabPublic = React.memo(
|
||||||
return (
|
return (
|
||||||
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
<Stack.Navigator screenOptions={{ headerShadowVisible: false }}>
|
||||||
<Stack.Screen name='Tab-Public-Root' options={screenOptionsRoot} children={children} />
|
<Stack.Screen name='Tab-Public-Root' options={screenOptionsRoot} children={children} />
|
||||||
{TabSharedRoot({ Stack })}
|
{TabShared({ Stack })}
|
||||||
</Stack.Navigator>
|
</Stack.Navigator>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,24 +1,78 @@
|
||||||
|
import haptics from '@components/haptics'
|
||||||
|
import { HeaderRight } from '@components/Header'
|
||||||
|
import { displayMessage } from '@components/Message'
|
||||||
import Timeline from '@components/Timeline'
|
import Timeline from '@components/Timeline'
|
||||||
import TimelineDefault from '@components/Timeline/Default'
|
import TimelineDefault from '@components/Timeline/Default'
|
||||||
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
import { TabSharedStackScreenProps } from '@utils/navigation/navigators'
|
||||||
|
import { useTagsMutation, useTagsQuery } from '@utils/queryHooks/tags'
|
||||||
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
|
||||||
import React from 'react'
|
import { checkInstanceFeature } from '@utils/slices/instancesSlice'
|
||||||
|
import { useTheme } from '@utils/styles/ThemeManager'
|
||||||
|
import React, { useEffect } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
const TabSharedHashtag: React.FC<
|
const TabSharedHashtag: React.FC<TabSharedStackScreenProps<'Tab-Shared-Hashtag'>> = ({
|
||||||
TabSharedStackScreenProps<'Tab-Shared-Hashtag'>
|
navigation,
|
||||||
> = ({
|
|
||||||
route: {
|
route: {
|
||||||
params: { hashtag }
|
params: { hashtag }
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
|
const queryKey: QueryKeyTimeline = ['Timeline', { page: 'Hashtag', hashtag }]
|
||||||
|
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
|
const canFollowTags = useSelector(checkInstanceFeature('follow_tags'))
|
||||||
|
const { data, isFetching, refetch } = useTagsQuery({
|
||||||
|
tag: hashtag,
|
||||||
|
options: { enabled: canFollowTags }
|
||||||
|
})
|
||||||
|
const mutation = useTagsMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
haptics('Success')
|
||||||
|
refetch()
|
||||||
|
},
|
||||||
|
onError: (err: any, { to }) => {
|
||||||
|
displayMessage({
|
||||||
|
theme,
|
||||||
|
type: 'error',
|
||||||
|
message: t('common:message.error.message', {
|
||||||
|
function: to ? t('shared.hashtag.follow') : t('shared.hashtag.unfollow')
|
||||||
|
}),
|
||||||
|
...(err.status &&
|
||||||
|
typeof err.status === 'number' &&
|
||||||
|
err.data &&
|
||||||
|
err.data.error &&
|
||||||
|
typeof err.data.error === 'string' && {
|
||||||
|
description: err.data.error
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canFollowTags) return
|
||||||
|
|
||||||
|
navigation.setOptions({
|
||||||
|
headerRight: () => (
|
||||||
|
<HeaderRight
|
||||||
|
loading={isFetching || mutation.isLoading}
|
||||||
|
type='text'
|
||||||
|
content={data?.following ? t('shared.hashtag.unfollow') : t('shared.hashtag.follow')}
|
||||||
|
onPress={() =>
|
||||||
|
typeof data?.following === 'boolean' &&
|
||||||
|
mutation.mutate({ tag: hashtag, type: 'follow', to: !data.following })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}, [canFollowTags, data?.following, isFetching])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Timeline
|
<Timeline
|
||||||
queryKey={queryKey}
|
queryKey={queryKey}
|
||||||
customProps={{
|
customProps={{
|
||||||
renderItem: ({ item }) => (
|
renderItem: ({ item }) => <TimelineDefault item={item} queryKey={queryKey} />
|
||||||
<TimelineDefault item={item} queryKey={queryKey} />
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -20,11 +20,7 @@ import { Trans, useTranslation } from 'react-i18next'
|
||||||
import { Platform, TextInput, View } from 'react-native'
|
import { Platform, TextInput, View } from 'react-native'
|
||||||
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
|
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view'
|
||||||
|
|
||||||
const TabSharedRoot = ({
|
const TabShared = ({ Stack }: { Stack: ReturnType<typeof createNativeStackNavigator> }) => {
|
||||||
Stack
|
|
||||||
}: {
|
|
||||||
Stack: ReturnType<typeof createNativeStackNavigator>
|
|
||||||
}) => {
|
|
||||||
const { colors, mode } = useTheme()
|
const { colors, mode } = useTheme()
|
||||||
const { t } = useTranslation('screenTabs')
|
const { t } = useTranslation('screenTabs')
|
||||||
|
|
||||||
|
@ -50,9 +46,7 @@ const TabSharedRoot = ({
|
||||||
backgroundColor: `rgba(255, 255, 255, 0)`
|
backgroundColor: `rgba(255, 255, 255, 0)`
|
||||||
},
|
},
|
||||||
title: '',
|
title: '',
|
||||||
headerLeft: () => (
|
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} background />,
|
||||||
<HeaderLeft onPress={() => navigation.goBack()} background />
|
|
||||||
),
|
|
||||||
headerRight: () => {
|
headerRight: () => {
|
||||||
const actions: ContextMenuAction[] = []
|
const actions: ContextMenuAction[] = []
|
||||||
|
|
||||||
|
@ -77,13 +71,10 @@ const TabSharedRoot = ({
|
||||||
dropdownMenuMode
|
dropdownMenuMode
|
||||||
>
|
>
|
||||||
<HeaderRight
|
<HeaderRight
|
||||||
accessibilityLabel={t(
|
accessibilityLabel={t('shared.account.actions.accessibilityLabel', {
|
||||||
'shared.account.actions.accessibilityLabel',
|
user: account.acct
|
||||||
{ user: account.acct }
|
})}
|
||||||
)}
|
accessibilityHint={t('shared.account.actions.accessibilityHint')}
|
||||||
accessibilityHint={t(
|
|
||||||
'shared.account.actions.accessibilityHint'
|
|
||||||
)}
|
|
||||||
content='MoreHorizontal'
|
content='MoreHorizontal'
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
background
|
background
|
||||||
|
@ -132,9 +123,7 @@ const TabSharedRoot = ({
|
||||||
key='Tab-Shared-Hashtag'
|
key='Tab-Shared-Hashtag'
|
||||||
name='Tab-Shared-Hashtag'
|
name='Tab-Shared-Hashtag'
|
||||||
component={TabSharedHashtag}
|
component={TabSharedHashtag}
|
||||||
options={({
|
options={({ route }: TabSharedStackScreenProps<'Tab-Shared-Hashtag'>) => ({
|
||||||
route
|
|
||||||
}: TabSharedStackScreenProps<'Tab-Shared-Hashtag'>) => ({
|
|
||||||
title: `#${decodeURIComponent(route.params.hashtag)}`
|
title: `#${decodeURIComponent(route.params.hashtag)}`
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
@ -150,24 +139,16 @@ const TabSharedRoot = ({
|
||||||
key='Tab-Shared-Search'
|
key='Tab-Shared-Search'
|
||||||
name='Tab-Shared-Search'
|
name='Tab-Shared-Search'
|
||||||
component={TabSharedSearch}
|
component={TabSharedSearch}
|
||||||
options={({
|
options={({ navigation }: TabSharedStackScreenProps<'Tab-Shared-Search'>) => ({
|
||||||
navigation
|
|
||||||
}: TabSharedStackScreenProps<'Tab-Shared-Search'>) => ({
|
|
||||||
...(Platform.OS === 'ios'
|
...(Platform.OS === 'ios'
|
||||||
? {
|
? {
|
||||||
headerLeft: () => (
|
headerLeft: () => <HeaderLeft onPress={() => navigation.goBack()} />
|
||||||
<HeaderLeft onPress={() => navigation.goBack()} />
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
: { headerLeft: () => null }),
|
: { headerLeft: () => null }),
|
||||||
headerTitle: () => {
|
headerTitle: () => {
|
||||||
const onChangeText = debounce(
|
const onChangeText = debounce((text: string) => navigation.setParams({ text }), 1000, {
|
||||||
(text: string) => navigation.setParams({ text }),
|
trailing: true
|
||||||
1000,
|
})
|
||||||
{
|
|
||||||
trailing: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
|
@ -199,9 +180,7 @@ const TabSharedRoot = ({
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
clearButtonMode='never'
|
clearButtonMode='never'
|
||||||
keyboardType='web-search'
|
keyboardType='web-search'
|
||||||
onSubmitEditing={({ nativeEvent: { text } }) =>
|
onSubmitEditing={({ nativeEvent: { text } }) => navigation.setParams({ text })}
|
||||||
navigation.setParams({ text })
|
|
||||||
}
|
|
||||||
placeholder={t('shared.search.header.placeholder')}
|
placeholder={t('shared.search.header.placeholder')}
|
||||||
placeholderTextColor={colors.secondary}
|
placeholderTextColor={colors.secondary}
|
||||||
returnKeyType='go'
|
returnKeyType='go'
|
||||||
|
@ -216,9 +195,7 @@ const TabSharedRoot = ({
|
||||||
key='Tab-Shared-Toot'
|
key='Tab-Shared-Toot'
|
||||||
name='Tab-Shared-Toot'
|
name='Tab-Shared-Toot'
|
||||||
component={TabSharedToot}
|
component={TabSharedToot}
|
||||||
options={{
|
options={{ title: t('shared.toot.name') }}
|
||||||
title: t('shared.toot.name')
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
@ -233,9 +210,7 @@ const TabSharedRoot = ({
|
||||||
title: t(`shared.users.${reference}.${type}`, { count }),
|
title: t(`shared.users.${reference}.${type}`, { count }),
|
||||||
...(Platform.OS === 'android' && {
|
...(Platform.OS === 'android' && {
|
||||||
headerCenter: () => (
|
headerCenter: () => (
|
||||||
<HeaderCenter
|
<HeaderCenter content={t(`shared.users.${reference}.${type}`, { count })} />
|
||||||
content={t(`shared.users.${reference}.${type}`, { count })}
|
|
||||||
/>
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})}
|
})}
|
||||||
|
@ -244,4 +219,4 @@ const TabSharedRoot = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TabSharedRoot
|
export default TabShared
|
|
@ -0,0 +1,59 @@
|
||||||
|
import apiInstance from '@api/instance'
|
||||||
|
import { AxiosError } from 'axios'
|
||||||
|
import {
|
||||||
|
QueryFunctionContext,
|
||||||
|
useMutation,
|
||||||
|
UseMutationOptions,
|
||||||
|
useQuery,
|
||||||
|
UseQueryOptions
|
||||||
|
} from 'react-query'
|
||||||
|
|
||||||
|
type QueryKeyFollowedTags = ['FollowedTags']
|
||||||
|
const useFollowedTagsQuery = ({
|
||||||
|
options
|
||||||
|
}: {
|
||||||
|
options?: UseQueryOptions<Mastodon.Tag, AxiosError>
|
||||||
|
}) => {
|
||||||
|
const queryKey: QueryKeyFollowedTags = ['FollowedTags']
|
||||||
|
return useQuery(
|
||||||
|
queryKey,
|
||||||
|
async ({ pageParam }: QueryFunctionContext<QueryKeyFollowedTags>) => {
|
||||||
|
const params: { [key: string]: string } = { ...pageParam }
|
||||||
|
const res = await apiInstance<Mastodon.Tag>({ method: 'get', url: `followed_tags`, params })
|
||||||
|
return res.body
|
||||||
|
},
|
||||||
|
options
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryKeyTags = ['Tags', { tag: string }]
|
||||||
|
const queryFunction = ({ queryKey }: QueryFunctionContext<QueryKeyTags>) => {
|
||||||
|
const { tag } = queryKey[1]
|
||||||
|
|
||||||
|
return apiInstance<Mastodon.Tag>({ method: 'get', url: `tags/${tag}` }).then(res => res.body)
|
||||||
|
}
|
||||||
|
const useTagsQuery = ({
|
||||||
|
options,
|
||||||
|
...queryKeyParams
|
||||||
|
}: QueryKeyTags[1] & {
|
||||||
|
options?: UseQueryOptions<Mastodon.Tag, AxiosError>
|
||||||
|
}) => {
|
||||||
|
const queryKey: QueryKeyTags = ['Tags', { ...queryKeyParams }]
|
||||||
|
return useQuery(queryKey, queryFunction, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MutationVarsAnnouncement = { tag: string; type: 'follow'; to: boolean }
|
||||||
|
const mutationFunction = async ({ tag, type, to }: MutationVarsAnnouncement) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'follow':
|
||||||
|
return apiInstance<{}>({
|
||||||
|
method: 'post',
|
||||||
|
url: `tags/${tag}/${to ? 'follow' : 'unfollow'}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const useTagsMutation = (options: UseMutationOptions<{}, AxiosError, MutationVarsAnnouncement>) => {
|
||||||
|
return useMutation(mutationFunction, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useFollowedTagsQuery, useTagsQuery, useTagsMutation }
|
|
@ -29,10 +29,7 @@ const instancesSlice = createSlice({
|
||||||
name: 'instances',
|
name: 'instances',
|
||||||
initialState: instancesInitialState,
|
initialState: instancesInitialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
updateInstanceActive: (
|
updateInstanceActive: ({ instances }, action: PayloadAction<InstanceLatest>) => {
|
||||||
{ instances },
|
|
||||||
action: PayloadAction<InstanceLatest>
|
|
||||||
) => {
|
|
||||||
instances = instances.map(instance => {
|
instances = instances.map(instance => {
|
||||||
instance.active =
|
instance.active =
|
||||||
instance.url === action.payload.url &&
|
instance.url === action.payload.url &&
|
||||||
|
@ -43,9 +40,7 @@ const instancesSlice = createSlice({
|
||||||
},
|
},
|
||||||
updateInstanceAccount: (
|
updateInstanceAccount: (
|
||||||
{ instances },
|
{ instances },
|
||||||
action: PayloadAction<
|
action: PayloadAction<Pick<InstanceLatest['account'], 'acct' & 'avatarStatic'>>
|
||||||
Pick<InstanceLatest['account'], 'acct' & 'avatarStatic'>
|
|
||||||
>
|
|
||||||
) => {
|
) => {
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].account = {
|
instances[activeIndex].account = {
|
||||||
|
@ -60,10 +55,7 @@ const instancesSlice = createSlice({
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].notifications_filter = action.payload
|
instances[activeIndex].notifications_filter = action.payload
|
||||||
},
|
},
|
||||||
updateInstanceDraft: (
|
updateInstanceDraft: ({ instances }, action: PayloadAction<ComposeStateDraft>) => {
|
||||||
{ instances },
|
|
||||||
action: PayloadAction<ComposeStateDraft>
|
|
||||||
) => {
|
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
const draftIndex = instances[activeIndex].drafts.findIndex(
|
const draftIndex = instances[activeIndex].drafts.findIndex(
|
||||||
({ timestamp }) => timestamp === action.payload.timestamp
|
({ timestamp }) => timestamp === action.payload.timestamp
|
||||||
|
@ -74,10 +66,7 @@ const instancesSlice = createSlice({
|
||||||
instances[activeIndex].drafts[draftIndex] = action.payload
|
instances[activeIndex].drafts[draftIndex] = action.payload
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
removeInstanceDraft: (
|
removeInstanceDraft: ({ instances }, action: PayloadAction<ComposeStateDraft['timestamp']>) => {
|
||||||
{ instances },
|
|
||||||
action: PayloadAction<ComposeStateDraft['timestamp']>
|
|
||||||
) => {
|
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
instances[activeIndex].drafts = instances[activeIndex].drafts?.filter(
|
instances[activeIndex].drafts = instances[activeIndex].drafts?.filter(
|
||||||
draft => draft.timestamp !== action.payload
|
draft => draft.timestamp !== action.payload
|
||||||
|
@ -126,9 +115,7 @@ const instancesSlice = createSlice({
|
||||||
action: PayloadAction<InstanceLatest['frequentEmojis'][0]['emoji']>
|
action: PayloadAction<InstanceLatest['frequentEmojis'][0]['emoji']>
|
||||||
) => {
|
) => {
|
||||||
const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week
|
const HALF_LIFE = 60 * 60 * 24 * 7 // 1 week
|
||||||
const calculateScore = (
|
const calculateScore = (emoji: InstanceLatest['frequentEmojis'][0]): number => {
|
||||||
emoji: InstanceLatest['frequentEmojis'][0]
|
|
||||||
): number => {
|
|
||||||
var seconds = (new Date().getTime() - emoji.lastUsed) / 1000
|
var seconds = (new Date().getTime() - emoji.lastUsed) / 1000
|
||||||
var score = emoji.count + 1
|
var score = emoji.count + 1
|
||||||
var order = Math.log(Math.max(score, 1)) / Math.LN10
|
var order = Math.log(Math.max(score, 1)) / Math.LN10
|
||||||
|
@ -137,9 +124,7 @@ const instancesSlice = createSlice({
|
||||||
}
|
}
|
||||||
const activeIndex = findInstanceActive(instances)
|
const activeIndex = findInstanceActive(instances)
|
||||||
const foundEmojiIndex = instances[activeIndex].frequentEmojis?.findIndex(
|
const foundEmojiIndex = instances[activeIndex].frequentEmojis?.findIndex(
|
||||||
e =>
|
e => e.emoji.shortcode === action.payload.shortcode && e.emoji.url === action.payload.url
|
||||||
e.emoji.shortcode === action.payload.shortcode &&
|
|
||||||
e.emoji.url === action.payload.url
|
|
||||||
)
|
)
|
||||||
let newEmojisSort: InstanceLatest['frequentEmojis']
|
let newEmojisSort: InstanceLatest['frequentEmojis']
|
||||||
if (foundEmojiIndex > -1) {
|
if (foundEmojiIndex > -1) {
|
||||||
|
@ -147,11 +132,11 @@ const instancesSlice = createSlice({
|
||||||
.map((e, i) =>
|
.map((e, i) =>
|
||||||
i === foundEmojiIndex
|
i === foundEmojiIndex
|
||||||
? {
|
? {
|
||||||
...e,
|
...e,
|
||||||
score: calculateScore(e),
|
score: calculateScore(e),
|
||||||
count: e.count + 1,
|
count: e.count + 1,
|
||||||
lastUsed: new Date().getTime()
|
lastUsed: new Date().getTime()
|
||||||
}
|
}
|
||||||
: e
|
: e
|
||||||
)
|
)
|
||||||
.sort((a, b) => b.score - a.score)
|
.sort((a, b) => b.score - a.score)
|
||||||
|
@ -218,8 +203,7 @@ const instancesSlice = createSlice({
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
state.instances.length &&
|
state.instances.length && (state.instances[state.instances.length - 1].active = true)
|
||||||
(state.instances[state.instances.length - 1].active = true)
|
|
||||||
|
|
||||||
analytics('logout')
|
analytics('logout')
|
||||||
})
|
})
|
||||||
|
@ -250,8 +234,7 @@ const instancesSlice = createSlice({
|
||||||
.addCase(updateConfiguration.fulfilled, (state, action) => {
|
.addCase(updateConfiguration.fulfilled, (state, action) => {
|
||||||
const activeIndex = findInstanceActive(state.instances)
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
state.instances[activeIndex].version = action.payload?.version || '0'
|
state.instances[activeIndex].version = action.payload?.version || '0'
|
||||||
state.instances[activeIndex].configuration =
|
state.instances[activeIndex].configuration = action.payload.configuration
|
||||||
action.payload.configuration
|
|
||||||
})
|
})
|
||||||
.addCase(updateConfiguration.rejected, (_, action) => {
|
.addCase(updateConfiguration.rejected, (_, action) => {
|
||||||
console.error(action.error)
|
console.error(action.error)
|
||||||
|
@ -291,22 +274,16 @@ const instancesSlice = createSlice({
|
||||||
// Update Instance Push Individual Alert
|
// Update Instance Push Individual Alert
|
||||||
.addCase(updateInstancePushAlert.fulfilled, (state, action) => {
|
.addCase(updateInstancePushAlert.fulfilled, (state, action) => {
|
||||||
const activeIndex = findInstanceActive(state.instances)
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
state.instances[activeIndex].push.alerts[
|
state.instances[activeIndex].push.alerts[action.meta.arg.changed].loading = false
|
||||||
action.meta.arg.changed
|
|
||||||
].loading = false
|
|
||||||
state.instances[activeIndex].push.alerts = action.payload
|
state.instances[activeIndex].push.alerts = action.payload
|
||||||
})
|
})
|
||||||
.addCase(updateInstancePushAlert.rejected, (state, action) => {
|
.addCase(updateInstancePushAlert.rejected, (state, action) => {
|
||||||
const activeIndex = findInstanceActive(state.instances)
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
state.instances[activeIndex].push.alerts[
|
state.instances[activeIndex].push.alerts[action.meta.arg.changed].loading = false
|
||||||
action.meta.arg.changed
|
|
||||||
].loading = false
|
|
||||||
})
|
})
|
||||||
.addCase(updateInstancePushAlert.pending, (state, action) => {
|
.addCase(updateInstancePushAlert.pending, (state, action) => {
|
||||||
const activeIndex = findInstanceActive(state.instances)
|
const activeIndex = findInstanceActive(state.instances)
|
||||||
state.instances[activeIndex].push.alerts[
|
state.instances[activeIndex].push.alerts[action.meta.arg.changed].loading = true
|
||||||
action.meta.arg.changed
|
|
||||||
].loading = true
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Check if frequently used emojis still exist
|
// Check if frequently used emojis still exist
|
||||||
|
@ -317,8 +294,7 @@ const instancesSlice = createSlice({
|
||||||
activeIndex
|
activeIndex
|
||||||
].frequentEmojis?.filter(emoji => {
|
].frequentEmojis?.filter(emoji => {
|
||||||
return action.payload?.find(
|
return action.payload?.find(
|
||||||
e =>
|
e => e.shortcode === emoji.emoji.shortcode && e.url === emoji.emoji.url
|
||||||
e.shortcode === emoji.emoji.shortcode && e.url === emoji.emoji.url
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -331,8 +307,7 @@ const instancesSlice = createSlice({
|
||||||
export const getInstanceActive = ({ instances: { instances } }: RootState) =>
|
export const getInstanceActive = ({ instances: { instances } }: RootState) =>
|
||||||
findInstanceActive(instances)
|
findInstanceActive(instances)
|
||||||
|
|
||||||
export const getInstances = ({ instances: { instances } }: RootState) =>
|
export const getInstances = ({ instances: { instances } }: RootState) => instances
|
||||||
instances
|
|
||||||
|
|
||||||
export const getInstance = ({ instances: { instances } }: RootState) =>
|
export const getInstance = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]
|
instances[findInstanceActive(instances)]
|
||||||
|
@ -350,42 +325,30 @@ export const getInstanceVersion = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.version
|
instances[findInstanceActive(instances)]?.version
|
||||||
export const checkInstanceFeature =
|
export const checkInstanceFeature =
|
||||||
(feature: string) =>
|
(feature: string) =>
|
||||||
({ instances: { instances } }: RootState): Boolean => {
|
({ instances: { instances } }: RootState): boolean => {
|
||||||
return (
|
return (
|
||||||
features
|
features
|
||||||
.filter(f => f.feature === feature)
|
.filter(f => f.feature === feature)
|
||||||
.filter(
|
.filter(f => parseFloat(instances[findInstanceActive(instances)]?.version) >= f.version)
|
||||||
f =>
|
?.length > 0
|
||||||
parseFloat(instances[findInstanceActive(instances)]?.version) >=
|
)
|
||||||
f.version
|
}
|
||||||
)?.length > 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get Instance Configuration */
|
/* Get Instance Configuration */
|
||||||
export const getInstanceConfigurationStatusMaxChars = ({
|
export const getInstanceConfigurationStatusMaxChars = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.configuration?.statuses.max_characters || 500
|
||||||
}: RootState) =>
|
|
||||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
|
||||||
.max_characters || 500
|
|
||||||
|
|
||||||
export const getInstanceConfigurationStatusMaxAttachments = ({
|
export const getInstanceConfigurationStatusMaxAttachments = ({
|
||||||
instances: { instances }
|
instances: { instances }
|
||||||
}: RootState) =>
|
}: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
instances[findInstanceActive(instances)]?.configuration?.statuses.max_media_attachments || 4
|
||||||
.max_media_attachments || 4
|
|
||||||
|
|
||||||
export const getInstanceConfigurationStatusCharsURL = ({
|
export const getInstanceConfigurationStatusCharsURL = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.configuration?.statuses.characters_reserved_per_url ||
|
||||||
}: RootState) =>
|
23
|
||||||
instances[findInstanceActive(instances)]?.configuration?.statuses
|
|
||||||
.characters_reserved_per_url || 23
|
|
||||||
|
|
||||||
export const getInstanceConfigurationMediaAttachments = ({
|
export const getInstanceConfigurationMediaAttachments = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.configuration?.media_attachments || {
|
||||||
}: RootState) =>
|
|
||||||
instances[findInstanceActive(instances)]?.configuration
|
|
||||||
?.media_attachments || {
|
|
||||||
supported_mime_types: [
|
supported_mime_types: [
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
'image/png',
|
'image/png',
|
||||||
|
@ -418,9 +381,7 @@ export const getInstanceConfigurationMediaAttachments = ({
|
||||||
video_matrix_limit: 2304000
|
video_matrix_limit: 2304000
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getInstanceConfigurationPoll = ({
|
export const getInstanceConfigurationPoll = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
|
||||||
}: RootState) =>
|
|
||||||
instances[findInstanceActive(instances)]?.configuration?.polls || {
|
instances[findInstanceActive(instances)]?.configuration?.polls || {
|
||||||
max_options: 4,
|
max_options: 4,
|
||||||
max_characters_per_option: 50,
|
max_characters_per_option: 50,
|
||||||
|
@ -432,16 +393,14 @@ export const getInstanceConfigurationPoll = ({
|
||||||
export const getInstanceAccount = ({ instances: { instances } }: RootState) =>
|
export const getInstanceAccount = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.account
|
instances[findInstanceActive(instances)]?.account
|
||||||
|
|
||||||
export const getInstanceNotificationsFilter = ({
|
export const getInstanceNotificationsFilter = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.notifications_filter
|
||||||
}: RootState) => instances[findInstanceActive(instances)]?.notifications_filter
|
|
||||||
|
|
||||||
export const getInstancePush = ({ instances: { instances } }: RootState) =>
|
export const getInstancePush = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.push
|
instances[findInstanceActive(instances)]?.push
|
||||||
|
|
||||||
export const getInstanceTimelinesLookback = ({
|
export const getInstanceTimelinesLookback = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.timelinesLookback
|
||||||
}: RootState) => instances[findInstanceActive(instances)]?.timelinesLookback
|
|
||||||
|
|
||||||
export const getInstanceMePage = ({ instances: { instances } }: RootState) =>
|
export const getInstanceMePage = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.mePage
|
instances[findInstanceActive(instances)]?.mePage
|
||||||
|
@ -449,9 +408,8 @@ export const getInstanceMePage = ({ instances: { instances } }: RootState) =>
|
||||||
export const getInstanceDrafts = ({ instances: { instances } }: RootState) =>
|
export const getInstanceDrafts = ({ instances: { instances } }: RootState) =>
|
||||||
instances[findInstanceActive(instances)]?.drafts
|
instances[findInstanceActive(instances)]?.drafts
|
||||||
|
|
||||||
export const getInstanceFrequentEmojis = ({
|
export const getInstanceFrequentEmojis = ({ instances: { instances } }: RootState) =>
|
||||||
instances: { instances }
|
instances[findInstanceActive(instances)]?.frequentEmojis
|
||||||
}: RootState) => instances[findInstanceActive(instances)]?.frequentEmojis
|
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
updateInstanceActive,
|
updateInstanceActive,
|
||||||
|
|
Loading…
Reference in New Issue