Merge pull request #124 from tooot-app/main

Nightly 210511
This commit is contained in:
xmflsct 2021-05-11 21:39:48 +02:00 committed by GitHub
commit 27a9f9fdc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 234 additions and 186 deletions

View File

@ -151,4 +151,8 @@ declare namespace Nav {
fields?: Mastodon.Source['fields'] fields?: Mastodon.Source['fields']
} }
} }
type TabMePushStackParamList = {
'Tab-Me-Push-Root': undefined
}
} }

View File

@ -86,6 +86,7 @@ export interface Props {
start: number start: number
end: number end: number
}> }>
maxLength?: number
} }
const ComponentEmojis: React.FC<Props> = ({ const ComponentEmojis: React.FC<Props> = ({
@ -93,6 +94,7 @@ const ComponentEmojis: React.FC<Props> = ({
value, value,
setValue, setValue,
selectionRange, selectionRange,
maxLength,
children children
}) => { }) => {
const { reduceMotionEnabled } = useAccessibility() const { reduceMotionEnabled } = useAccessibility()
@ -125,9 +127,13 @@ const ComponentEmojis: React.FC<Props> = ({
const newTextWithSpace = ` ${emojiShortcode}${ const newTextWithSpace = ` ${emojiShortcode}${
whiteSpaceRear ? '' : ' ' whiteSpaceRear ? '' : ' '
}` }`
setValue([contentFront, newTextWithSpace, contentRear].join('')) setValue(
[contentFront, newTextWithSpace, contentRear]
.join('')
.slice(0, maxLength)
)
} else { } else {
setValue(`${emojiShortcode} `) setValue(`${emojiShortcode} `.slice(0, maxLength))
} }
}, },
[value, selectionRange.current?.start, selectionRange.current?.end] [value, selectionRange.current?.start, selectionRange.current?.end]

View File

@ -107,6 +107,7 @@ const Input: React.FC<Props> = ({
value={value} value={value}
setValue={setValue} setValue={setValue}
selectionRange={selectionRange} selectionRange={selectionRange}
maxLength={options?.maxLength}
> >
<View <View
style={[ style={[

View File

@ -115,15 +115,10 @@ const TabMe = React.memo(
<Stack.Screen <Stack.Screen
name='Tab-Me-Push' name='Tab-Me-Push'
component={TabMePush} component={TabMePush}
options={({ navigation }: any) => ({ options={{
headerTitle: t('me.stacks.push.name'), stackPresentation: 'modal',
...(Platform.OS === 'android' && { headerShown: false
headerCenter: () => ( }}
<HeaderCenter content={t('me.stacks.push.name')} />
)
}),
headerLeft: () => <HeaderLeft onPress={() => navigation.pop(1)} />
})}
/> />
<Stack.Screen <Stack.Screen
name='Tab-Me-Settings' name='Tab-Me-Settings'

View File

@ -6,10 +6,10 @@ import { useTranslation } from 'react-i18next'
import { KeyboardAvoidingView, Platform } from 'react-native' import { KeyboardAvoidingView, Platform } from 'react-native'
import FlashMessage from 'react-native-flash-message' import FlashMessage from 'react-native-flash-message'
import { createNativeStackNavigator } from 'react-native-screens/native-stack' import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import ScreenMeProfileFields from './Profile/Fields' import TabMeProfileFields from './Profile/Fields'
import ScreenMeProfileName from './Profile/Name' import TabMeProfileName from './Profile/Name'
import ScreenMeProfileNote from './Profile/Note' import TabMeProfileNote from './Profile/Note'
import ScreenMeProfileRoot from './Profile/Root' import TabMeProfileRoot from './Profile/Root'
const Stack = createNativeStackNavigator<Nav.TabMeProfileStackParamList>() const Stack = createNativeStackNavigator<Nav.TabMeProfileStackParamList>()
@ -30,7 +30,7 @@ const TabMeProfile: React.FC<StackScreenProps<
> >
<Stack.Screen <Stack.Screen
name='Tab-Me-Profile-Root' name='Tab-Me-Profile-Root'
component={ScreenMeProfileRoot} component={TabMeProfileRoot}
options={{ options={{
headerTitle: t('me.stacks.profile.name'), headerTitle: t('me.stacks.profile.name'),
...(Platform.OS === 'android' && { ...(Platform.OS === 'android' && {
@ -58,7 +58,7 @@ const TabMeProfile: React.FC<StackScreenProps<
}} }}
> >
{({ route, navigation }) => ( {({ route, navigation }) => (
<ScreenMeProfileName <TabMeProfileName
messageRef={messageRef} messageRef={messageRef}
route={route} route={route}
navigation={navigation} navigation={navigation}
@ -77,7 +77,7 @@ const TabMeProfile: React.FC<StackScreenProps<
}} }}
> >
{({ route, navigation }) => ( {({ route, navigation }) => (
<ScreenMeProfileNote <TabMeProfileNote
messageRef={messageRef} messageRef={messageRef}
route={route} route={route}
navigation={navigation} navigation={navigation}
@ -96,7 +96,7 @@ const TabMeProfile: React.FC<StackScreenProps<
}} }}
> >
{({ route, navigation }) => ( {({ route, navigation }) => (
<ScreenMeProfileFields <TabMeProfileFields
messageRef={messageRef} messageRef={messageRef}
route={route} route={route}
navigation={navigation} navigation={navigation}

View File

@ -24,7 +24,7 @@ const prepareFields = (
}) })
} }
const ScreenMeProfileFields: React.FC<StackScreenProps< const TabMeProfileFields: React.FC<StackScreenProps<
Nav.TabMeProfileStackParamList, Nav.TabMeProfileStackParamList,
'Tab-Me-Profile-Fields' 'Tab-Me-Profile-Fields'
> & { messageRef: RefObject<FlashMessage> }> = ({ > & { messageRef: RefObject<FlashMessage> }> = ({
@ -112,7 +112,7 @@ const ScreenMeProfileFields: React.FC<StackScreenProps<
}, [mode, i18n.language, dirty, status, newFields]) }, [mode, i18n.language, dirty, status, newFields])
return ( return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='handled'> <ScrollView style={styles.base} keyboardShouldPersistTaps='always'>
<View style={{ marginBottom: StyleConstants.Spacing.L * 2 }}> <View style={{ marginBottom: StyleConstants.Spacing.L * 2 }}>
{Array.from(Array(4).keys()).map(index => ( {Array.from(Array(4).keys()).map(index => (
<View key={index} style={styles.group}> <View key={index} style={styles.group}>
@ -168,4 +168,4 @@ const styles = StyleSheet.create({
} }
}) })
export default ScreenMeProfileFields export default TabMeProfileFields

View File

@ -11,7 +11,7 @@ import { Alert, StyleSheet } from 'react-native'
import FlashMessage from 'react-native-flash-message' import FlashMessage from 'react-native-flash-message'
import { ScrollView } from 'react-native-gesture-handler' import { ScrollView } from 'react-native-gesture-handler'
const ScreenMeProfileName: React.FC<StackScreenProps< const TabMeProfileName: React.FC<StackScreenProps<
Nav.TabMeProfileStackParamList, Nav.TabMeProfileStackParamList,
'Tab-Me-Profile-Name' 'Tab-Me-Profile-Name'
> & { messageRef: RefObject<FlashMessage> }> = ({ > & { messageRef: RefObject<FlashMessage> }> = ({
@ -94,7 +94,7 @@ const ScreenMeProfileName: React.FC<StackScreenProps<
}, [mode, i18n.language, dirty, status, displayName]) }, [mode, i18n.language, dirty, status, displayName])
return ( return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='handled'> <ScrollView style={styles.base} keyboardShouldPersistTaps='always'>
<Input <Input
value={displayName} value={displayName}
setValue={setDisplayName} setValue={setDisplayName}
@ -116,4 +116,4 @@ const styles = StyleSheet.create({
} }
}) })
export default ScreenMeProfileName export default TabMeProfileName

View File

@ -11,7 +11,7 @@ import { Alert, StyleSheet, View } from 'react-native'
import FlashMessage from 'react-native-flash-message' import FlashMessage from 'react-native-flash-message'
import { ScrollView } from 'react-native-gesture-handler' import { ScrollView } from 'react-native-gesture-handler'
const ScreenMeProfileNote: React.FC<StackScreenProps< const TabMeProfileNote: React.FC<StackScreenProps<
Nav.TabMeProfileStackParamList, Nav.TabMeProfileStackParamList,
'Tab-Me-Profile-Note' 'Tab-Me-Profile-Note'
> & { messageRef: RefObject<FlashMessage> }> = ({ > & { messageRef: RefObject<FlashMessage> }> = ({
@ -94,7 +94,7 @@ const ScreenMeProfileNote: React.FC<StackScreenProps<
}, [mode, i18n.language, dirty, status, newNote]) }, [mode, i18n.language, dirty, status, newNote])
return ( return (
<ScrollView style={styles.base} keyboardShouldPersistTaps='handled'> <ScrollView style={styles.base} keyboardShouldPersistTaps='always'>
<View style={{ marginBottom: StyleConstants.Spacing.XL * 2 }}> <View style={{ marginBottom: StyleConstants.Spacing.XL * 2 }}>
<Input <Input
value={newNote} value={newNote}
@ -114,4 +114,4 @@ const styles = StyleSheet.create({
} }
}) })
export default ScreenMeProfileNote export default TabMeProfileNote

View File

@ -6,7 +6,7 @@ import React, { useCallback } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler' import { ScrollView } from 'react-native-gesture-handler'
const ScreenMeProfileRoot: React.FC<StackScreenProps< const TabMeProfileRoot: React.FC<StackScreenProps<
Nav.TabMeProfileStackParamList, Nav.TabMeProfileStackParamList,
'Tab-Me-Profile-Root' 'Tab-Me-Profile-Root'
>> = ({ navigation }) => { >> = ({ navigation }) => {
@ -180,4 +180,4 @@ const ScreenMeProfileRoot: React.FC<StackScreenProps<
) )
} }
export default ScreenMeProfileRoot export default TabMeProfileRoot

View File

@ -1,162 +1,42 @@
import { MenuContainer, MenuRow } from '@components/Menu' import { HeaderCenter, HeaderLeft } from '@components/Header'
import { updateInstancePush } from '@utils/slices/instances/updatePush'
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
import {
clearPushLoading,
getInstanceAccount,
getInstancePush,
getInstanceUri
} from '@utils/slices/instancesSlice'
import * as WebBrowser from 'expo-web-browser'
import * as Notifications from 'expo-notifications'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler'
import { useDispatch, useSelector } from 'react-redux'
import layoutAnimation from '@utils/styles/layoutAnimation'
import Button from '@components/Button'
import { StyleConstants } from '@utils/styles/constants'
import { AppState, Linking } from 'react-native'
import { StackScreenProps } from '@react-navigation/stack' import { StackScreenProps } from '@react-navigation/stack'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Platform } from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import TabMePushRoot from './Push/Root'
const Stack = createNativeStackNavigator<Nav.TabMePushStackParamList>()
const TabMePush: React.FC<StackScreenProps< const TabMePush: React.FC<StackScreenProps<
Nav.TabMeStackParamList, Nav.TabMeStackParamList,
'Tab-Me-Push' 'Tab-Me-Push'
>> = () => { >> = ({ navigation }) => {
const { t } = useTranslation('screenTabs') const { t } = useTranslation('screenTabs')
const instanceAccount = useSelector(
getInstanceAccount,
(prev, next) => prev?.acct === next?.acct
)
const instanceUri = useSelector(getInstanceUri)
const dispatch = useDispatch()
const instancePush = useSelector(getInstancePush)
const [pushEnabled, setPushEnabled] = useState<boolean>()
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
const checkPush = async () => {
const settings = await Notifications.getPermissionsAsync()
layoutAnimation()
setPushEnabled(settings.granted)
setPushCanAskAgain(settings.canAskAgain)
}
useEffect(() => {
checkPush()
AppState.addEventListener('change', checkPush)
return () => {
AppState.removeEventListener('change', checkPush)
}
}, [])
useEffect(() => {
dispatch(clearPushLoading())
}, [])
const isLoading = instancePush?.global.loading || instancePush?.decode.loading
const alerts = useMemo(() => {
return instancePush?.alerts
? (['follow', 'favourite', 'reblog', 'mention', 'poll'] as [
'follow',
'favourite',
'reblog',
'mention',
'poll'
]).map(alert => (
<MenuRow
key={alert}
title={t(`me.push.${alert}.heading`)}
switchDisabled={
!pushEnabled || !instancePush.global.value || isLoading
}
switchValue={instancePush?.alerts[alert].value}
switchOnValueChange={() =>
dispatch(
updateInstancePushAlert({
changed: alert,
alerts: {
...instancePush?.alerts,
[alert]: {
...instancePush?.alerts[alert],
value: !instancePush?.alerts[alert].value
}
}
})
)
}
/>
))
: null
}, [pushEnabled, instancePush?.global, instancePush?.alerts, isLoading])
return ( return (
<ScrollView> <Stack.Navigator
{pushEnabled === false ? ( screenOptions={{ headerHideShadow: true, headerTopInsetEnabled: false }}
<MenuContainer> >
<Button <Stack.Screen
type='text' name='Tab-Me-Push-Root'
content={ component={TabMePushRoot}
pushCanAskAgain options={{
? t('me.push.enable.direct') headerTitle: t('me.stacks.push.name'),
: t('me.push.enable.settings') ...(Platform.OS === 'android' && {
} headerCenter: () => (
style={{ <HeaderCenter content={t('me.stacks.push.name')} />
marginTop: StyleConstants.Spacing.Global.PagePadding, )
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2 }),
}} headerLeft: () => (
onPress={async () => { <HeaderLeft
if (pushCanAskAgain) { content='ChevronDown'
const result = await Notifications.requestPermissionsAsync() onPress={() => navigation.goBack()}
setPushEnabled(result.granted) />
setPushCanAskAgain(result.canAskAgain) )
} else { }}
Linking.openSettings() />
} </Stack.Navigator>
}}
/>
</MenuContainer>
) : null}
<MenuContainer>
<MenuRow
title={t('me.push.global.heading', {
acct: `@${instanceAccount?.acct}@${instanceUri}`
})}
description={t('me.push.global.description')}
loading={instancePush?.global.loading}
switchDisabled={!pushEnabled || isLoading}
switchValue={
pushEnabled === false ? false : instancePush?.global.value
}
switchOnValueChange={() =>
dispatch(updateInstancePush(!instancePush?.global.value))
}
/>
</MenuContainer>
<MenuContainer>
<MenuRow
title={t('me.push.decode.heading')}
description={t('me.push.decode.description')}
loading={instancePush?.decode.loading}
switchDisabled={
!pushEnabled || !instancePush?.global.value || isLoading
}
switchValue={instancePush?.decode.value}
switchOnValueChange={() =>
dispatch(updateInstancePushDecode(!instancePush?.decode.value))
}
/>
<MenuRow
title={t('me.push.howitworks')}
iconBack='ExternalLink'
onPress={() =>
WebBrowser.openBrowserAsync('https://tooot.app/how-push-works')
}
/>
</MenuContainer>
<MenuContainer>{alerts}</MenuContainer>
</ScrollView>
) )
} }

View File

@ -0,0 +1,163 @@
import { MenuContainer, MenuRow } from '@components/Menu'
import { updateInstancePush } from '@utils/slices/instances/updatePush'
import { updateInstancePushAlert } from '@utils/slices/instances/updatePushAlert'
import { updateInstancePushDecode } from '@utils/slices/instances/updatePushDecode'
import {
clearPushLoading,
getInstanceAccount,
getInstancePush,
getInstanceUri
} from '@utils/slices/instancesSlice'
import * as WebBrowser from 'expo-web-browser'
import * as Notifications from 'expo-notifications'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ScrollView } from 'react-native-gesture-handler'
import { useDispatch, useSelector } from 'react-redux'
import layoutAnimation from '@utils/styles/layoutAnimation'
import Button from '@components/Button'
import { StyleConstants } from '@utils/styles/constants'
import { AppState, Linking } from 'react-native'
import { StackScreenProps } from '@react-navigation/stack'
const TabMePushRoot: React.FC<StackScreenProps<
Nav.TabMeStackParamList,
'Tab-Me-Push'
>> = () => {
const { t } = useTranslation('screenTabs')
const instanceAccount = useSelector(
getInstanceAccount,
(prev, next) => prev?.acct === next?.acct
)
const instanceUri = useSelector(getInstanceUri)
const dispatch = useDispatch()
const instancePush = useSelector(getInstancePush)
const [pushEnabled, setPushEnabled] = useState<boolean>()
const [pushCanAskAgain, setPushCanAskAgain] = useState<boolean>()
const checkPush = async () => {
const settings = await Notifications.getPermissionsAsync()
layoutAnimation()
setPushEnabled(settings.granted)
setPushCanAskAgain(settings.canAskAgain)
}
useEffect(() => {
checkPush()
AppState.addEventListener('change', checkPush)
return () => {
AppState.removeEventListener('change', checkPush)
}
}, [])
useEffect(() => {
dispatch(clearPushLoading())
}, [])
const isLoading = instancePush?.global.loading || instancePush?.decode.loading
const alerts = useMemo(() => {
return instancePush?.alerts
? (['follow', 'favourite', 'reblog', 'mention', 'poll'] as [
'follow',
'favourite',
'reblog',
'mention',
'poll'
]).map(alert => (
<MenuRow
key={alert}
title={t(`me.push.${alert}.heading`)}
switchDisabled={
!pushEnabled || !instancePush.global.value || isLoading
}
switchValue={instancePush?.alerts[alert].value}
switchOnValueChange={() =>
dispatch(
updateInstancePushAlert({
changed: alert,
alerts: {
...instancePush?.alerts,
[alert]: {
...instancePush?.alerts[alert],
value: !instancePush?.alerts[alert].value
}
}
})
)
}
/>
))
: null
}, [pushEnabled, instancePush?.global, instancePush?.alerts, isLoading])
return (
<ScrollView>
{pushEnabled === false ? (
<MenuContainer>
<Button
type='text'
content={
pushCanAskAgain
? t('me.push.enable.direct')
: t('me.push.enable.settings')
}
style={{
marginTop: StyleConstants.Spacing.Global.PagePadding,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding * 2
}}
onPress={async () => {
if (pushCanAskAgain) {
const result = await Notifications.requestPermissionsAsync()
setPushEnabled(result.granted)
setPushCanAskAgain(result.canAskAgain)
} else {
Linking.openSettings()
}
}}
/>
</MenuContainer>
) : null}
<MenuContainer>
<MenuRow
title={t('me.push.global.heading', {
acct: `@${instanceAccount?.acct}@${instanceUri}`
})}
description={t('me.push.global.description')}
loading={instancePush?.global.loading}
switchDisabled={!pushEnabled || isLoading}
switchValue={
pushEnabled === false ? false : instancePush?.global.value
}
switchOnValueChange={() =>
dispatch(updateInstancePush(!instancePush?.global.value))
}
/>
</MenuContainer>
<MenuContainer>
<MenuRow
title={t('me.push.decode.heading')}
description={t('me.push.decode.description')}
loading={instancePush?.decode.loading}
switchDisabled={
!pushEnabled || !instancePush?.global.value || isLoading
}
switchValue={instancePush?.decode.value}
switchOnValueChange={() =>
dispatch(updateInstancePushDecode(!instancePush?.decode.value))
}
/>
<MenuRow
title={t('me.push.howitworks')}
iconBack='ExternalLink'
onPress={() =>
WebBrowser.openBrowserAsync('https://tooot.app/how-push-works')
}
/>
</MenuContainer>
<MenuContainer>{alerts}</MenuContainer>
</ScrollView>
)
}
export default TabMePushRoot

View File

@ -4,7 +4,7 @@ import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { KeyboardAvoidingView, Platform } from 'react-native' import { KeyboardAvoidingView, Platform } from 'react-native'
import { createNativeStackNavigator } from 'react-native-screens/native-stack' import { createNativeStackNavigator } from 'react-native-screens/native-stack'
import ScreenMeSwitchRoot from './Switch/Root' import TabMeSwitchRoot from './Switch/Root'
const Stack = createNativeStackNavigator() const Stack = createNativeStackNavigator()
@ -23,7 +23,7 @@ const TabMeSwitch: React.FC<StackScreenProps<
> >
<Stack.Screen <Stack.Screen
name='Screen-Me-Switch-Root' name='Screen-Me-Switch-Root'
component={ScreenMeSwitchRoot} component={TabMeSwitchRoot}
options={{ options={{
headerTitle: t('me.stacks.switch.name'), headerTitle: t('me.stacks.switch.name'),
...(Platform.OS === 'android' && { ...(Platform.OS === 'android' && {

View File

@ -11,7 +11,6 @@ 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 { groupBy } from 'lodash'
import React, { useRef } from 'react' import React, { useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { StyleSheet, Text, View } from 'react-native' import { StyleSheet, Text, View } from 'react-native'
@ -48,7 +47,7 @@ const AccountButton: React.FC<Props> = ({ instance, selected = false }) => {
) )
} }
const ScreenMeSwitchRoot: React.FC = () => { const TabMeSwitchRoot: React.FC = () => {
const { t } = useTranslation('screenTabs') const { t } = useTranslation('screenTabs')
const { theme } = useTheme() const { theme } = useTheme()
const instances = useSelector(getInstances, () => true) const instances = useSelector(getInstances, () => true)
@ -137,4 +136,4 @@ const styles = StyleSheet.create({
} }
}) })
export default ScreenMeSwitchRoot export default TabMeSwitchRoot