Restyle login

This commit is contained in:
Zhiyuan Zheng 2020-12-13 21:09:21 +01:00
parent 48cab6053c
commit dc9870beaf
No known key found for this signature in database
GPG Key ID: 078A93AB607D85E0
16 changed files with 313 additions and 83 deletions

View File

@ -4,8 +4,8 @@ import { QueryCache, ReactQueryCacheProvider, setConsole } from 'react-query'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import { Index } from './src/Index'
import { persistor, store } from './src/store'
import { Index } from '@root/Index'
import { persistor, store } from '@root/store'
import ThemeManager from '@utils/styles/ThemeManager'
const queryCache = new QueryCache()

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

View File

@ -9,6 +9,7 @@ module.exports = function (api) {
{
root: ['./'],
alias: {
'@assets': './assets',
'@root': './src',
'@api': './src/api',
'@components': './src/components',

View File

@ -226,6 +226,32 @@ declare namespace Mastodon {
title: string
}
type Instance = {
// Base
uri: string
title: string
description: string
short_description: string
email: string
version: string
languages: string[]
registrations: boolean
approval_required: boolean
invites_enabled: boolean
urls: {
streaming_api: string
}
stats: {
user_count: number
status_count: number
domain_count: number
}
// Others
thumbnail?: string
contact_account?: Account
}
type Mention = {
// Base
id: string

View File

@ -18,6 +18,8 @@ import { useTheme } from '@utils/styles/ThemeManager'
import getCurrentTab from '@utils/getCurrentTab'
import { toastConfig } from '@components/toast'
import { useTranslation } from 'react-i18next'
import { useSelector } from 'react-redux'
import { getLocalUrl } from './utils/slices/instancesSlice'
enableScreens()
const Tab = createBottomTabNavigator<RootStackParamList>()
@ -31,6 +33,7 @@ export type RootStackParamList = {
}
export const Index: React.FC = () => {
const localInstance = useSelector(getLocalUrl)
const { i18n } = useTranslation()
const { mode, theme } = useTheme()
enum barStyle {
@ -46,12 +49,14 @@ export const Index: React.FC = () => {
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let name: any
let updateColor: string = color
switch (route.name) {
case 'Screen-Local':
name = 'home'
break
case 'Screen-Public':
name = 'globe'
!focused && (updateColor = theme.secondary)
break
case 'Screen-Post':
name = 'plus'
@ -61,30 +66,42 @@ export const Index: React.FC = () => {
break
case 'Screen-Me':
name = focused ? 'meh' : 'smile'
!focused && (updateColor = theme.secondary)
break
default:
name = 'alert-octagon'
break
}
return <Feather name={name} size={size} color={color} />
return <Feather name={name} size={size} color={updateColor} />
}
})}
tabBarOptions={{
activeTintColor: theme.primary,
inactiveTintColor: theme.secondary,
inactiveTintColor: localInstance ? theme.secondary : theme.disabled,
showLabel: false
}}
>
<Tab.Screen name='Screen-Local' component={ScreenLocal} />
<Tab.Screen
name='Screen-Local'
component={ScreenLocal}
listeners={{
tabPress: e => {
if (!localInstance) {
e.preventDefault()
}
}
}}
/>
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
<Tab.Screen
name='Screen-Post'
listeners={({ navigation, route }) => ({
tabPress: e => {
e.preventDefault()
navigation.navigate(getCurrentTab(navigation), {
screen: 'Screen-Shared-Compose'
})
localInstance &&
navigation.navigate(getCurrentTab(navigation), {
screen: 'Screen-Shared-Compose'
})
}
})}
>
@ -93,6 +110,13 @@ export const Index: React.FC = () => {
<Tab.Screen
name='Screen-Notifications'
component={ScreenNotifications}
listeners={{
tabPress: e => {
if (!localInstance) {
e.preventDefault()
}
}
}}
/>
<Tab.Screen name='Screen-Me' component={ScreenMe} />
</Tab.Navigator>

View File

@ -1,5 +1,5 @@
import axios from 'axios'
import { store, RootState } from '@root/store'
import { store, RootState } from 'src/store'
const client = async ({
method,

View File

@ -8,18 +8,17 @@ type PropsBase = {
onPress: () => void
disabled?: boolean
buttonSize?: 'S' | 'M'
size?: 'S' | 'M' | 'L'
}
export interface PropsText extends PropsBase {
text: string
icon?: any
size?: 'S' | 'M' | 'L'
}
export interface PropsIcon extends PropsBase {
text?: string
icon: any
size?: 'S' | 'M' | 'L'
}
const ButtonRow: React.FC<PropsText | PropsIcon> = ({
@ -71,16 +70,13 @@ const styles = StyleSheet.create({
button: {
paddingLeft: StyleConstants.Spacing.M,
paddingRight: StyleConstants.Spacing.M,
borderWidth: 1,
borderRadius: 100
borderWidth: 1.25,
borderRadius: 100,
alignItems: 'center'
},
text: {
textAlign: 'center'
}
})
export default React.memo(ButtonRow, (prev, next) => {
let skipUpdate = true
skipUpdate = prev.disabled === next.disabled
return skipUpdate
})
export default ButtonRow

View File

@ -1,8 +0,0 @@
import React from 'react'
import { Text } from 'react-native'
const PleaseLogin = () => {
return <Text></Text>
}
export default PleaseLogin

View File

@ -15,7 +15,6 @@ import {
import { useTheme } from '@utils/styles/ThemeManager'
import { useNavigation } from '@react-navigation/native'
import getCurrentTab from '@utils/getCurrentTab'
import PleaseLogin from '@components/PleaseLogin'
const Stack = createNativeStackNavigator()
@ -30,9 +29,7 @@ const Page = ({
<View style={{ width: Dimensions.get('window').width }}>
{localRegistered || page === 'RemotePublic' ? (
<Timeline page={page} />
) : (
<PleaseLogin />
)}
) : null}
</View>
)
}

View File

@ -28,13 +28,13 @@ const ScreenMe: React.FC = () => {
name='Screen-Me-Root'
component={ScreenMeRoot}
options={
localRegistered
? {
headerTranslucent: true,
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
headerCenter: () => <></>
}
: { headerTitle: t('headers.me.root') }
// localRegistered ?
{
headerTranslucent: true,
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
headerCenter: () => <></>
}
// : { headerTitle: t('meRoot:heading') }
}
/>
<Stack.Screen

View File

@ -14,7 +14,7 @@ const ScreenMeRoot: React.FC = () => {
const localRegistered = useSelector(getLocalUrl)
return (
<ScrollView>
<ScrollView keyboardShouldPersistTaps='handled'>
{localRegistered ? <MyInfo /> : <Login />}
{localRegistered && <Collections />}
<Settings />

View File

@ -1,5 +1,19 @@
import React, { useCallback, useEffect, useState } from 'react'
import { StyleSheet, Text, TextInput, View } from 'react-native'
import React, {
createRef,
useCallback,
useEffect,
useRef,
useState
} from 'react'
import {
Animated,
Dimensions,
Image,
StyleSheet,
Text,
TextInput,
View
} from 'react-native'
import { useQuery } from 'react-query'
import { debounce } from 'lodash'
@ -14,6 +28,9 @@ import { useTheme } from '@utils/styles/ThemeManager'
import { useTranslation } from 'react-i18next'
import { StyleConstants } from '@utils/styles/constants'
import { ButtonRow } from '@components/Button'
import ParseContent from '@root/components/ParseContent'
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
import { Feather } from '@expo/vector-icons'
const Login: React.FC = () => {
const { t } = useTranslation('meRoot')
@ -26,7 +43,7 @@ const Login: React.FC = () => {
clientSecret: string
}>()
const { isSuccess, refetch, data } = useQuery(
const { isSuccess, isFetching, refetch, data } = useQuery(
['Instance', { instance }],
instanceFetch,
{
@ -40,11 +57,13 @@ const Login: React.FC = () => {
text => {
setInstance(text)
setApplicationData(undefined)
refetch()
if (text) {
refetch()
}
},
1000,
{
leading: true
trailing: true
}
),
[]
@ -121,47 +140,217 @@ const Login: React.FC = () => {
})()
}, [response])
return (
<View style={styles.base}>
<TextInput
style={{
height: 50,
color: theme.primary,
borderColor: theme.border,
borderWidth: 1,
padding: StyleConstants.Spacing.M
}}
onChangeText={onChangeText}
autoCapitalize='none'
autoCorrect={false}
autoFocus
clearButtonMode='unless-editing'
keyboardType='url'
textContentType='URL'
onSubmitEditing={async () =>
isSuccess && data && data.uri && (await createApplication())
}
placeholder={t('content.login.server.placeholder')}
placeholderTextColor={theme.secondary}
returnKeyType='go'
/>
<ButtonRow
onPress={async () => await createApplication()}
text={t('content.login.button')}
disabled={!data?.uri}
/>
{isSuccess && data && data.uri && (
<View>
<Text style={{ color: theme.primary }}>{data.title}</Text>
const infoRef = createRef<ShimmerPlaceholder>()
const instanceInfo = useCallback(
({
header,
content,
parse
}: {
header: string
content: string
parse?: boolean
}) => {
if (isFetching) {
Animated.loop(infoRef.current?.getAnimated()!).start()
}
return (
<View style={styles.instanceInfo}>
<Text style={[styles.instanceInfoHeader, { color: theme.primary }]}>
{header}
</Text>
<ShimmerPlaceholder
visible={data?.uri}
stopAutoRun
width={
Dimensions.get('screen').width -
StyleConstants.Spacing.Global.PagePadding * 4
}
height={StyleConstants.Font.Size.M}
>
<Text
style={[styles.instanceInfoContent, { color: theme.primary }]}
>
{parse ? (
<ParseContent content={content} size={'M'} numberOfLines={5} />
) : (
content
)}
</Text>
</ShimmerPlaceholder>
</View>
)}
</View>
)
},
[data?.uri, isFetching]
)
return (
<>
<View style={{ flexDirection: 'row' }}>
<Image
source={require('assets/screens/meRoot/welcome.png')}
style={{ resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 }}
/>
</View>
<View style={styles.base}>
<View style={styles.inputRow}>
<TextInput
style={[
styles.textInput,
{
color: theme.primary,
borderBottomColor: theme.secondary
}
]}
onChangeText={onChangeText}
autoCapitalize='none'
autoCorrect={false}
autoFocus
clearButtonMode='unless-editing'
keyboardType='url'
textContentType='URL'
onSubmitEditing={async () =>
isSuccess && data && data.uri && (await createApplication())
}
placeholder={t('content.login.server.placeholder')}
placeholderTextColor={theme.secondary}
returnKeyType='go'
/>
<ButtonRow
onPress={async () => await createApplication()}
{...(isFetching
? { icon: 'loader' }
: { text: t('content.login.button') as string })}
disabled={!data?.uri}
/>
</View>
<View>
{instanceInfo({ header: '实例名称', content: data?.title })}
{instanceInfo({
header: '实例介绍',
content: data?.short_description,
parse: true
})}
<View style={styles.instanceStats}>
<View style={styles.instanceStat}>
<Text
style={[styles.instanceInfoHeader, { color: theme.primary }]}
>
</Text>
<ShimmerPlaceholder
visible={data?.stats?.user_count}
stopAutoRun
width={StyleConstants.Font.Size.M * 4}
height={StyleConstants.Font.Size.M}
>
<Text
style={[styles.instanceInfoContent, { color: theme.primary }]}
>
{data?.stats?.user_count}
</Text>
</ShimmerPlaceholder>
</View>
<View style={[styles.instanceStat, { alignItems: 'center' }]}>
<Text
style={[styles.instanceInfoHeader, { color: theme.primary }]}
>
</Text>
<ShimmerPlaceholder
visible={data?.stats?.user_count}
stopAutoRun
width={StyleConstants.Font.Size.M * 4}
height={StyleConstants.Font.Size.M}
>
<Text
style={[styles.instanceInfoContent, { color: theme.primary }]}
>
{data?.stats?.status_count}
</Text>
</ShimmerPlaceholder>
</View>
<View style={[styles.instanceStat, { alignItems: 'flex-end' }]}>
<Text
style={[styles.instanceInfoHeader, { color: theme.primary }]}
>
</Text>
<ShimmerPlaceholder
visible={data?.stats?.user_count}
stopAutoRun
width={StyleConstants.Font.Size.M * 4}
height={StyleConstants.Font.Size.M}
>
<Text
style={[styles.instanceInfoContent, { color: theme.primary }]}
>
{data?.stats?.domain_count}
</Text>
</ShimmerPlaceholder>
</View>
</View>
<Text style={[styles.disclaimer, { color: theme.secondary }]}>
<Feather
name='lock'
size={StyleConstants.Font.Size.M}
color={theme.secondary}
/>{' '}
</Text>
</View>
</View>
</>
)
}
const styles = StyleSheet.create({
base: {
padding: StyleConstants.Spacing.Global.PagePadding
},
inputRow: {
flex: 1,
flexDirection: 'row'
},
textInput: {
flex: 1,
borderBottomWidth: 1.25,
paddingTop: StyleConstants.Spacing.S - 1.5,
paddingBottom: StyleConstants.Spacing.S - 1.5,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding,
fontSize: StyleConstants.Font.Size.M,
marginRight: StyleConstants.Spacing.M
},
instanceInfo: {
marginTop: StyleConstants.Spacing.M,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding
},
instanceInfoHeader: {
fontSize: StyleConstants.Font.Size.S,
fontWeight: StyleConstants.Font.Weight.Bold,
marginBottom: StyleConstants.Spacing.XS
},
instanceInfoContent: { fontSize: StyleConstants.Font.Size.M },
instanceStats: {
flex: 1,
flexDirection: 'row',
marginTop: StyleConstants.Spacing.M,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding,
marginBottom: StyleConstants.Spacing.M
},
instanceStat: {
flex: 1
},
disclaimer: {
fontSize: StyleConstants.Font.Size.S,
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
paddingRight: StyleConstants.Spacing.Global.PagePadding,
marginBottom: StyleConstants.Spacing.M
}
})

View File

@ -6,11 +6,13 @@ import MenuButton from '@components/Menu/Button'
import { MenuContainer } from '@components/Menu'
import { useNavigation } from '@react-navigation/native'
import { useTranslation } from 'react-i18next'
import { useQueryCache } from 'react-query'
const Logout: React.FC = () => {
const { t } = useTranslation('meRoot')
const dispatch = useDispatch()
const navigation = useNavigation()
const queryCache = useQueryCache()
const alertOption = {
title: t('content.logout.alert.title'),
@ -20,6 +22,7 @@ const Logout: React.FC = () => {
text: t('content.logout.alert.buttons.logout'),
style: 'destructive' as const,
onPress: () => {
queryCache.clear()
dispatch(updateLocal({}))
navigation.navigate('Screen-Public', {
screen: 'Screen-Public-Root',

View File

@ -5,7 +5,6 @@ import Timeline from '@components/Timelines/Timeline'
import sharedScreens from '@screens/Shared/sharedScreens'
import { useSelector } from 'react-redux'
import { RootState } from '@root/store'
import PleaseLogin from '@components/PleaseLogin'
import { useTranslation } from 'react-i18next'
const Stack = createNativeStackNavigator()
@ -21,9 +20,7 @@ const ScreenNotifications: React.FC = () => {
screenOptions={{ headerTitle: t('notifications:heading') }}
>
<Stack.Screen name='Screen-Notifications-Root'>
{() =>
localRegistered ? <Timeline page='Notifications' /> : <PleaseLogin />
}
{() => (localRegistered ? <Timeline page='Notifications' /> : null)}
</Stack.Screen>
{sharedScreens(Stack)}

View File

@ -25,7 +25,11 @@ import { emojisFetch } from '@utils/fetches/emojisFetch'
import { searchFetch } from '@utils/fetches/searchFetch'
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
import { PostAction, ComposeState, ComposeContext } from '@screens/Shared/Compose'
import {
PostAction,
ComposeState,
ComposeContext
} from '@screens/Shared/Compose'
import ComposeActions from '@screens/Shared/Compose/Actions'
import updateText from './updateText'
import * as Permissions from 'expo-permissions'
@ -189,7 +193,7 @@ const ComposeRoot: React.FC = () => {
/>
<FlatList
keyboardShouldPersistTaps='handled'
ListHeaderComponent={<ComposeRootHeader textInputRef={textInputRef} />}
ListHeaderComponent={<ComposeRootHeader />}
ListFooterComponent={<ComposeRootFooter textInputRef={textInputRef} />}
ListEmptyComponent={listEmpty}
data={data}

View File

@ -11,6 +11,7 @@
"strict": true,
"baseUrl": "./",
"paths": {
// "@assets/*": ["./assets/*"],
"@root/*": ["./src/*"],
"@api/*": ["./src/api/*"],
"@components/*": ["./src/components/*"],