diff --git a/.expo-shared/assets.json b/.expo-shared/assets.json deleted file mode 100644 index 1e6decfb..00000000 --- a/.expo-shared/assets.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true, - "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true -} diff --git a/src/components/Timelines/Timeline/Empty.tsx b/src/components/Timelines/Timeline/Empty.tsx index 6c0b5160..80241cec 100644 --- a/src/components/Timelines/Timeline/Empty.tsx +++ b/src/components/Timelines/Timeline/Empty.tsx @@ -1,8 +1,9 @@ -import { Feather } from '@expo/vector-icons' import React, { useMemo } from 'react' -import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' +import { StyleSheet, Text, View } from 'react-native' +import { Chase } from 'react-native-animated-spinkit' import { QueryStatus } from 'react-query' import Button from '@components/Button' +import { Feather } from '@expo/vector-icons' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' @@ -17,7 +18,9 @@ const TimelineEmpty: React.FC<Props> = ({ status, refetch }) => { const children = useMemo(() => { switch (status) { case 'loading': - return <ActivityIndicator /> + return ( + <Chase size={StyleConstants.Font.Size.L} color={theme.secondary} /> + ) case 'error': return ( <> diff --git a/src/components/Timelines/Timeline/Shared/End.tsx b/src/components/Timelines/Timeline/Shared/End.tsx index 3b04fa77..b7cceb78 100644 --- a/src/components/Timelines/Timeline/Shared/End.tsx +++ b/src/components/Timelines/Timeline/Shared/End.tsx @@ -1,6 +1,7 @@ -import { Feather } from '@expo/vector-icons' import React from 'react' -import { ActivityIndicator, StyleSheet, Text, View } from 'react-native' +import { StyleSheet, Text, View } from 'react-native' +import { Chase } from 'react-native-animated-spinkit' +import { Feather } from '@expo/vector-icons' import { StyleConstants } from '@utils/styles/constants' import { useTheme } from '@utils/styles/ThemeManager' @@ -14,7 +15,7 @@ const TimelineEnd: React.FC<Props> = ({ hasNextPage }) => { return ( <View style={styles.base}> {hasNextPage ? ( - <ActivityIndicator /> + <Chase size={StyleConstants.Font.Size.L} color={theme.secondary} /> ) : ( <Text style={[styles.text, { color: theme.secondary }]}> 居然刷到底了,喝杯{' '} diff --git a/src/components/Timelines/Timeline/Shared/HeaderNotification.tsx b/src/components/Timelines/Timeline/Shared/HeaderNotification.tsx index ae0edcc3..504c7868 100644 --- a/src/components/Timelines/Timeline/Shared/HeaderNotification.tsx +++ b/src/components/Timelines/Timeline/Shared/HeaderNotification.tsx @@ -1,21 +1,16 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { - ActivityIndicator, - Pressable, - StyleSheet, - Text, - View -} from 'react-native' +import { Pressable, StyleSheet, Text, View } from 'react-native' +import { Chase } from 'react-native-animated-spinkit' +import { useQuery } from 'react-query' +import client from '@api/client' import { Feather } from '@expo/vector-icons' import Emojis from '@components/Timelines/Timeline/Shared/Emojis' -import relativeTime from '@utils/relativeTime' -import { useTheme } from '@utils/styles/ThemeManager' -import { StyleConstants } from '@utils/styles/constants' -import { useQuery } from 'react-query' -import { relationshipFetch } from '@utils/fetches/relationshipFetch' -import client from '@api/client' import { toast } from '@components/toast' import openLink from '@root/utils/openLink' +import relativeTime from '@utils/relativeTime' +import { StyleConstants } from '@utils/styles/constants' +import { relationshipFetch } from '@utils/fetches/relationshipFetch' +import { useTheme } from '@utils/styles/ThemeManager' export interface Props { notification: Mastodon.Notification @@ -87,7 +82,9 @@ const TimelineHeaderNotification: React.FC<Props> = ({ notification }) => { switch (status) { case 'idle': case 'loading': - return <ActivityIndicator /> + return ( + <Chase size={StyleConstants.Font.Size.L} color={theme.secondary} /> + ) case 'success': return ( <Pressable onPress={relationshipOnPress}> diff --git a/src/components/Timelines/Timeline/Shared/Poll.tsx b/src/components/Timelines/Timeline/Shared/Poll.tsx index e03ca96a..72f0558a 100644 --- a/src/components/Timelines/Timeline/Shared/Poll.tsx +++ b/src/components/Timelines/Timeline/Shared/Poll.tsx @@ -162,12 +162,16 @@ const TimelinePoll: React.FC<Props> = ({ <View style={styles.optionSelected}> <Feather style={styles.voted} - name={poll.multiple ? 'check-square' : 'check-circle'} + name={ + `${poll.own_votes!.includes(index) ? 'check-' : ''}${ + poll.multiple ? 'square' : 'circle' + }` as any + } size={StyleConstants.Font.Size.M} color={ poll.own_votes!.includes(index) ? theme.primary - : theme.background + : theme.disabled } /> <View style={styles.contentSelected}> @@ -266,7 +270,7 @@ const styles = StyleSheet.create({ flexDirection: 'row' }, contentSelected: { - flexShrink: 1, + flex: 1, flexDirection: 'row', alignItems: 'center', paddingRight: StyleConstants.Spacing.S diff --git a/src/screens/Me/Lists.tsx b/src/screens/Me/Lists.tsx index 60e7eba2..fd7a2b7e 100644 --- a/src/screens/Me/Lists.tsx +++ b/src/screens/Me/Lists.tsx @@ -1,25 +1,18 @@ +import { MenuRow } from '@components/Menu' import { useNavigation } from '@react-navigation/native' -import React from 'react' -import { ActivityIndicator, Text } from 'react-native' -import { useQuery } from 'react-query' -import { MenuContainer, MenuRow } from '@components/Menu' - +import TimelineEmpty from '@root/components/Timelines/Timeline/Empty' import { listsFetch } from '@utils/fetches/listsFetch' +import React, { useMemo } from 'react' +import { StyleSheet } from 'react-native' +import { useQuery } from 'react-query' const ScreenMeLists: React.FC = () => { const navigation = useNavigation() - const { status, data } = useQuery(['Lists'], listsFetch) + const { status, data, refetch } = useQuery(['Lists'], listsFetch) - let lists - switch (status) { - case 'loading': - lists = <ActivityIndicator /> - break - case 'error': - lists = <Text>载入错误</Text> - break - case 'success': - lists = data?.map((d: Mastodon.List, i: number) => ( + const children = useMemo(() => { + if (status === 'success') { + return data?.map((d: Mastodon.List, i: number) => ( <MenuRow key={i} iconFront='list' @@ -32,10 +25,19 @@ const ScreenMeLists: React.FC = () => { } /> )) - break - } + } else { + return <TimelineEmpty status={status} refetch={refetch} /> + } + }, [status]) - return <MenuContainer>{lists}</MenuContainer> + return <>{children}</> } +const styles = StyleSheet.create({ + loading: { + flex: 1, + alignItems: 'center' + } +}) + export default ScreenMeLists diff --git a/src/screens/Shared/Account/Header.tsx b/src/screens/Shared/Account/Header.tsx index 91e24b11..c8357d1a 100644 --- a/src/screens/Shared/Account/Header.tsx +++ b/src/screens/Shared/Account/Header.tsx @@ -42,6 +42,8 @@ const AccountHeader: React.FC<Props> = ({ isMounted && setRatio(limitHeight ? accountState.headerRatio : height / width) }) + } else { + isMounted && setRatio(1 / 5) } }, [account, isMounted]) diff --git a/src/screens/Shared/Compose.tsx b/src/screens/Shared/Compose.tsx index d8abc3a2..249bc77c 100644 --- a/src/screens/Shared/Compose.tsx +++ b/src/screens/Shared/Compose.tsx @@ -528,7 +528,6 @@ const Compose: React.FC<Props> = ({ route: { params }, navigation }) => { const totalTextCount = (composeState.spoiler.active ? composeState.spoiler.count : 0) + composeState.text.count - // doesn't work const rawCount = composeState.text.raw.length const postButtonText = { diff --git a/src/screens/Shared/Compose/Root.tsx b/src/screens/Shared/Compose/Root.tsx index 546cd863..2ec9302d 100644 --- a/src/screens/Shared/Compose/Root.tsx +++ b/src/screens/Shared/Compose/Root.tsx @@ -1,40 +1,37 @@ -import { forEach, groupBy, sortBy } from 'lodash' -import React, { - Dispatch, - RefObject, - useCallback, - useContext, - useEffect, - useMemo, - useRef -} from 'react' -import { - View, - ActivityIndicator, - FlatList, - Pressable, - ProgressViewIOS, - StyleSheet, - Text, - TextInput, - Image -} from 'react-native' -import { useQuery } from 'react-query' import Emojis from '@components/Timelines/Timeline/Shared/Emojis' -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 { ComposeAction, ComposeState, ComposeContext } from '@screens/Shared/Compose' import ComposeActions from '@screens/Shared/Compose/Actions' -import updateText from './updateText' -import * as Permissions from 'expo-permissions' import ComposeRootFooter from '@screens/Shared/Compose/Root/Footer' import ComposeRootHeader from '@screens/Shared/Compose/Root/Header' +import updateText from '@screens/Shared/Compose/updateText' +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 * as Permissions from 'expo-permissions' +import { forEach, groupBy, sortBy } from 'lodash' +import React, { + Dispatch, + useCallback, + useContext, + useEffect, + useMemo +} from 'react' +import { + View, + FlatList, + Pressable, + ProgressViewIOS, + StyleSheet, + Text, + Image +} from 'react-native' +import { Chase } from 'react-native-animated-spinkit' +import { useQuery } from 'react-query' const ListItem = React.memo( ({ @@ -109,6 +106,8 @@ const ListItem = React.memo( ) const ComposeRoot: React.FC = () => { + const { theme } = useTheme() + const { composeState, composeDispatch } = useContext(ComposeContext) const { isFetching, isSuccess, data, refetch } = useQuery( @@ -159,7 +158,14 @@ const ComposeRoot: React.FC = () => { const listEmpty = useMemo(() => { if (isFetching) { - return <ActivityIndicator /> + return ( + <View style={styles.loading}> + <Chase + size={StyleConstants.Font.Size.M * 1.25} + color={theme.secondary} + /> + </View> + ) } }, [isFetching]) @@ -242,6 +248,10 @@ const styles = StyleSheet.create({ fontSize: StyleConstants.Font.Size.S, fontWeight: StyleConstants.Font.Weight.Bold, marginBottom: StyleConstants.Spacing.XS + }, + loading: { + flex: 1, + alignItems: 'center' } }) diff --git a/src/screens/Shared/Search.tsx b/src/screens/Shared/Search.tsx index 4665e92f..3e144d30 100644 --- a/src/screens/Shared/Search.tsx +++ b/src/screens/Shared/Search.tsx @@ -1,6 +1,7 @@ import { Feather } from '@expo/vector-icons' import { useNavigation } from '@react-navigation/native' import { HeaderRight } from '@root/components/Header' +import ParseContent from '@root/components/ParseContent' import Emojis from '@root/components/Timelines/Timeline/Shared/Emojis' import { searchFetch } from '@root/utils/fetches/searchFetch' import { StyleConstants } from '@root/utils/styles/constants' @@ -8,8 +9,8 @@ import { useTheme } from '@root/utils/styles/ThemeManager' import { debounce } from 'lodash' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { - ActivityIndicator, Image, + KeyboardAvoidingView, Pressable, SectionList, StyleSheet, @@ -17,6 +18,7 @@ import { TextInput, View } from 'react-native' +import { Chase } from 'react-native-animated-spinkit' import { SafeAreaView } from 'react-native-safe-area-context' import { useQuery } from 'react-query' @@ -71,46 +73,52 @@ const ScreenSharedSearch: React.FC = () => { }, [searchTerm]) const listEmpty = useMemo( - () => - status === 'loading' ? ( - <View style={styles.emptyBase}> - <ActivityIndicator /> - </View> - ) : ( - <View style={styles.emptyBase}> - <Text - style={[ - styles.emptyDefault, - styles.emptyFontSize, - { color: theme.primary } - ]} - > - 输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、 - <Text style={styles.emptyFontBold}>话题标签</Text>或者 - <Text style={styles.emptyFontBold}>嘟文</Text> - </Text> - <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> - 高级搜索格式 - </Text> - <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> - <Text style={{ color: theme.secondary }}>@username@domain</Text> - {' '} - 搜索用户 - </Text> - <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> - <Text style={{ color: theme.secondary }}>#example</Text> - {' '}搜索话题标签 - </Text> - <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> - <Text style={{ color: theme.secondary }}>URL</Text> - {' '}搜索指定嘟文 - </Text> - <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> - <Text style={{ color: theme.secondary }}>URL</Text> - {' '}搜索指定用户 - </Text> - </View> - ), + () => ( + <View style={styles.emptyBase}> + {status === 'loading' ? ( + <View style={styles.loading}> + <Chase + size={StyleConstants.Font.Size.M * 1.25} + color={theme.secondary} + /> + </View> + ) : ( + <> + <Text + style={[ + styles.emptyDefault, + styles.emptyFontSize, + { color: theme.primary } + ]} + > + 输入关键词搜索<Text style={styles.emptyFontBold}>用户</Text>、 + <Text style={styles.emptyFontBold}>话题标签</Text>或者 + <Text style={styles.emptyFontBold}>嘟文</Text> + </Text> + <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> + 高级搜索格式 + </Text> + <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> + <Text style={{ color: theme.secondary }}>@username@domain</Text> + {' '} + 搜索用户 + </Text> + <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> + <Text style={{ color: theme.secondary }}>#example</Text> + {' '}搜索话题标签 + </Text> + <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> + <Text style={{ color: theme.secondary }}>URL</Text> + {' '}搜索指定嘟文 + </Text> + <Text style={[styles.emptyAdvanced, { color: theme.primary }]}> + <Text style={{ color: theme.secondary }}>URL</Text> + {' '}搜索指定用户 + </Text> + </> + )} + </View> + ), [status] ) const sectionHeader = useCallback( @@ -204,63 +212,115 @@ const ScreenSharedSearch: React.FC = () => { </Pressable> ) case 'statuses': - return <Text>{item.id || 'empty'}</Text> + return ( + <Pressable + style={[ + styles.itemDefault, + styles.itemAccount, + { borderBottomColor: theme.border } + ]} + onPress={() => { + navigation.goBack() + navigation.push('Screen-Shared-Toot', { toot: item }) + }} + > + <Image + source={{ uri: item.account.avatar_static }} + style={styles.itemAccountAvatar} + /> + <View> + {item.account.emojis?.length ? ( + <Emojis + content={item.account.display_name || item.account.username} + emojis={item.account.emojis} + size={StyleConstants.Font.Size.S} + fontBold={true} + /> + ) : ( + <Text + style={[styles.nameWithoutEmoji, { color: theme.primary }]} + > + {item.account.display_name || item.account.username} + </Text> + )} + <Text + style={[styles.itemAccountAcct, { color: theme.secondary }]} + > + @{item.account.acct} + </Text> + {item.content && ( + <View style={styles.itemStatus}> + <ParseContent + content={item.content} + size='M' + emojis={item.emojis} + numberOfLines={2} + /> + </View> + )} + </View> + </Pressable> + ) default: return null } }, []) return ( - <SafeAreaView style={{ flex: 1 }} edges={['bottom']}> - <View style={styles.searchBar}> - <View - style={[styles.searchField, { borderBottomColor: theme.secondary }]} - > - <Feather - name='search' - color={theme.primary} - size={StyleConstants.Font.Size.M} - style={styles.searchIcon} - /> - <TextInput - style={[ - styles.textInput, - { - color: theme.primary + <KeyboardAvoidingView behavior='padding' style={{ flex: 1 }}> + <SafeAreaView style={{ flex: 1 }} edges={['bottom']}> + <View style={styles.searchBar}> + <View + style={[styles.searchField, { borderBottomColor: theme.secondary }]} + > + <Feather + name='search' + color={theme.primary} + size={StyleConstants.Font.Size.M} + style={styles.searchIcon} + /> + <TextInput + style={[ + styles.textInput, + { + color: theme.primary + } + ]} + autoFocus + onChangeText={onChangeText} + autoCapitalize='none' + autoCorrect={false} + clearButtonMode='never' + keyboardType='web-search' + onSubmitEditing={({ nativeEvent: { text } }) => + setSearchTerm(text) } - ]} - autoFocus - onChangeText={onChangeText} - autoCapitalize='none' - autoCorrect={false} - clearButtonMode='never' - keyboardType='web-search' - onSubmitEditing={({ nativeEvent: { text } }) => setSearchTerm(text)} - placeholder={'搜索些什么'} - placeholderTextColor={theme.secondary} - returnKeyType='go' - /> + placeholder={'搜索些什么'} + placeholderTextColor={theme.secondary} + returnKeyType='go' + /> + </View> + <View style={styles.searchCancel}> + <HeaderRight + type='text' + content='取消' + onPress={() => navigation.goBack()} + /> + </View> </View> - <View style={styles.searchCancel}> - <HeaderRight - type='text' - content='取消' - onPress={() => navigation.goBack()} - /> - </View> - </View> - <SectionList - style={styles.base} - renderItem={listItem} - stickySectionHeadersEnabled - sections={setctionData} - ListEmptyComponent={listEmpty} - keyboardShouldPersistTaps='always' - renderSectionHeader={sectionHeader} - renderSectionFooter={sectionFooter} - keyExtractor={(item, index) => item + index} - /> - </SafeAreaView> + <SectionList + style={styles.base} + renderItem={listItem} + stickySectionHeadersEnabled + sections={setctionData} + ListEmptyComponent={listEmpty} + keyboardShouldPersistTaps='always' + renderSectionHeader={sectionHeader} + renderSectionFooter={sectionFooter} + keyExtractor={(item, index) => item + index} + /> + </SafeAreaView> + </KeyboardAvoidingView> ) } @@ -303,6 +363,7 @@ const styles = StyleSheet.create({ StyleConstants.Spacing.M + StyleConstants.Spacing.S }, + loading: { flex: 1, alignItems: 'center' }, emptyFontSize: { fontSize: StyleConstants.Font.Size.S }, emptyFontBold: { fontWeight: StyleConstants.Font.Weight.Bold @@ -338,6 +399,7 @@ const styles = StyleSheet.create({ alignItems: 'center' }, itemAccountAvatar: { + alignSelf: 'flex-start', width: StyleConstants.Avatar.S, height: StyleConstants.Avatar.S, borderRadius: 6, @@ -350,6 +412,9 @@ const styles = StyleSheet.create({ itemAccountAcct: { marginTop: StyleConstants.Spacing.XS }, itemHashtag: { fontSize: StyleConstants.Font.Size.M + }, + itemStatus: { + marginTop: StyleConstants.Spacing.S } })