Skip to content

Commit b0a5516

Browse files
committed
feat: follow people
1 parent c18d130 commit b0a5516

File tree

6 files changed

+177
-45
lines changed

6 files changed

+177
-45
lines changed

src/i18n/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
"profileLastModified": "Last Modified $。",
9191
"activeLatest": "Last actived at $ ",
9292
"favorited": "Favorited",
93-
"joinSinceTime": "Joined from",
93+
"v2exNumber": "V2EXer #$.",
94+
"joinSinceTime": "Joined from $.",
9495
"joinV2exSinceTime": "V2EXer #$, joined on $.",
9596
"createNodeSinceTime": "Created from $ .",
9697
"noLogin": "Not logged in",

src/i18n/locales/zh.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
"profileLastModified": "最近修改于 $。",
9191
"activeLatest": "最近活跃于 $。",
9292
"favorited": "收藏了",
93-
"joinSinceTime": "加入于",
93+
"v2exNumber": "第 $ 号会员。",
94+
"joinSinceTime": "加入于 $。",
9495
"joinV2exSinceTime": "第 $ 号会员,加入于 $。",
9596
"createNodeSinceTime": "节点从 $ 创建至今。",
9697
"noLogin": "未登陆",
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Created by leon<[email protected]> on 22/05/27.
3+
*/
4+
import { Placeholder, Spinner } from '@src/components'
5+
import { translate } from '@src/i18n'
6+
import { SylCommon, useTheme } from '@src/theme'
7+
import { ITheme, V2exObject } from '@src/types'
8+
import React from 'react'
9+
import { FlatList, StyleProp, View, ViewStyle } from 'react-native'
10+
import { BorderLine } from '../common'
11+
import SimpleProfileInfoCard from './SimpleProfileInfoCard'
12+
13+
export interface ProfileCardListProps {
14+
/**
15+
* container style
16+
*/
17+
containerStyle?: StyleProp<ViewStyle>
18+
19+
itemContainerStyle?: StyleProp<ViewStyle>
20+
21+
canLoadMoreContent?: boolean
22+
members?: Array<V2exObject.Member>
23+
onEndReached?: () => void
24+
refreshControl?: React.ReactElement
25+
searchIndicator?: boolean
26+
refreshCallback?: () => void
27+
28+
useFlatList?: boolean
29+
}
30+
31+
const ProfileCardList: React.FC<ProfileCardListProps> = ({
32+
useFlatList = true,
33+
containerStyle,
34+
itemContainerStyle,
35+
canLoadMoreContent,
36+
members,
37+
onEndReached,
38+
refreshControl,
39+
searchIndicator,
40+
refreshCallback
41+
}: ProfileCardListProps) => {
42+
const { theme } = useTheme()
43+
44+
const renderItemRow = ({ item }: { item: V2exObject.Member }) =>
45+
!item || item === null ? null : (
46+
<SimpleProfileInfoCard
47+
key={item.id}
48+
containerStyle={[styles.infoItemContainer(theme), itemContainerStyle]}
49+
info={item}
50+
/>
51+
)
52+
53+
const renderFooter = () => {
54+
if (canLoadMoreContent) {
55+
return <Spinner style={{ padding: theme.spacing.large }} />
56+
} else if (members && members.length > 0) {
57+
return (
58+
<Placeholder
59+
containerStyle={[{ backgroundColor: theme.colors.background }]}
60+
placeholderText={translate('tips.noMore')}
61+
/>
62+
)
63+
}
64+
return null
65+
}
66+
67+
const renderItemSeparator = () => <BorderLine />
68+
69+
const renderContent = () => {
70+
if (!members) {
71+
return <Spinner style={{ marginTop: 50 }} />
72+
}
73+
74+
if (members.length > 0) {
75+
return useFlatList ? (
76+
<FlatList
77+
refreshControl={refreshControl}
78+
style={styles.container(theme)}
79+
data={members}
80+
renderItem={renderItemRow}
81+
keyExtractor={(item, index) => index.toString()}
82+
onEndReached={onEndReached}
83+
onEndReachedThreshold={0.1}
84+
ListFooterComponent={renderFooter}
85+
numColumns={1}
86+
horizontal={false}
87+
key={'ONE COLUMN'}
88+
maxToRenderPerBatch={10}
89+
initialNumToRender={10}
90+
ItemSeparatorComponent={renderItemSeparator}
91+
/>
92+
) : (
93+
<View style={[styles.container(theme), containerStyle]}>{members.map((v) => renderItemRow({ item: v }))}</View>
94+
)
95+
}
96+
if (!searchIndicator) {
97+
return <Placeholder buttonText={translate('button.tryAgain')} buttonPress={refreshCallback} />
98+
}
99+
}
100+
101+
return <View style={[styles.container(theme), containerStyle]}>{renderContent()}</View>
102+
}
103+
104+
/**
105+
* @description styles settings
106+
*/
107+
const styles = {
108+
container: (theme: ITheme): ViewStyle => ({
109+
flex: 1
110+
}),
111+
infoItemContainer: (theme: ITheme): ViewStyle => ({
112+
...SylCommon.Card.container(theme),
113+
paddingTop: theme.spacing.medium
114+
})
115+
}
116+
117+
export default ProfileCardList

src/screens/components/profile/SimpleProfileInfoCard.tsx

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export interface SimpleProfileInfoCardProps {
3232
const SimpleProfileInfoCard: React.FC<SimpleProfileInfoCardProps> = ({
3333
info,
3434
containerStyle,
35-
withArrow = false
35+
withArrow = true
3636
}: SimpleProfileInfoCardProps) => {
3737
const { theme } = useTheme()
3838

@@ -56,19 +56,35 @@ const SimpleProfileInfoCard: React.FC<SimpleProfileInfoCardProps> = ({
5656
<Text
5757
style={[
5858
ProfileInfoStyle.baseRightItem(theme),
59+
styles.baseRightItem(theme),
5960
theme.typography.subheadingText,
6061
{ color: theme.colors.secondary }
6162
]}>
62-
{info?.username ?? translate('label.goLogin')}
63+
{info?.username}
6364
</Text>
6465
{info?.tagline ? (
65-
<Text style={[ProfileInfoStyle.baseRightItem(theme), theme.typography.bodyText]}>{info.tagline}</Text>
66+
<Text
67+
style={[
68+
ProfileInfoStyle.baseRightItem(theme),
69+
styles.baseRightItem(theme),
70+
theme.typography.bodyText
71+
]}>
72+
{info.tagline}
73+
</Text>
74+
) : null}
75+
{info ? (
76+
<Text
77+
style={[
78+
ProfileInfoStyle.baseRightItem(theme),
79+
styles.baseRightItem(theme),
80+
theme.typography.captionText
81+
]}>
82+
{translate('label.v2exNumber').replace('$', info?.id.toString())}
83+
</Text>
6684
) : null}
6785
{info?.created ? (
6886
<Text style={[ProfileInfoStyle.infoItem(theme), theme.typography.captionText]}>
69-
{translate('label.joinV2exSinceTime')
70-
.replace('$', info?.id.toString())
71-
.replace('$', dayjs(info?.created * 1000).format())}
87+
{translate('label.joinSinceTime').replace('$', dayjs(info?.created * 1000).format())}
7288
</Text>
7389
) : null}
7490
</View>
@@ -86,4 +102,10 @@ const SimpleProfileInfoCard: React.FC<SimpleProfileInfoCardProps> = ({
86102
return renderContent()
87103
}
88104

105+
const styles = {
106+
baseRightItem: (theme: ITheme): ViewStyle => ({
107+
paddingBottom: theme.spacing.tiny
108+
})
109+
}
110+
89111
export default SimpleProfileInfoCard

src/screens/components/profile/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { default as ProfileGrid } from './ProfileGrid'
44
export { default as ProfileInfo } from './ProfileInfo'
55
export { default as ProfileTopics } from './ProfileTopics'
66
export { default as SimpleProfileInfoCard } from './SimpleProfileInfoCard'
7+
export { default as ProfileCardList } from './ProfileCardList'

src/screens/my/Following.tsx

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,36 @@
1-
import React, { useState } from 'react'
1+
/**
2+
* Created by leon<[email protected]> on 22/04/07.
3+
*/
4+
import { Placeholder } from '@src/components'
5+
import { FollowingScreenProps as ScreenProps } from '@src/navigation'
6+
import { RootState } from '@src/store'
7+
import { SylCommon, useTheme } from '@src/theme'
8+
import { V2exObject } from '@src/types'
9+
import React from 'react'
10+
import { View } from 'react-native'
211
import { connect } from 'react-redux'
3-
import { StyleSheet, View, ViewStyle, TextStyle } from 'react-native'
4-
5-
import { translate } from '@src/i18n'
6-
import { useTheme, SylCommon } from '@src/theme'
7-
import { IState, ITheme, V2exObject } from '@src/types'
8-
import * as CompS from '../components'
9-
import { Text, Spinner, Placeholder } from '@src/components'
10-
import { FollowingScreenProps as ScreenProps, ROUTES } from '@src/navigation'
12+
import { ProfileCardList } from '../components'
1113

12-
const Following = ({ route, navigation, loading }: ScreenProps) => {
14+
const Following = ({
15+
followPeoples
16+
}: ScreenProps & {
17+
followPeoples: V2exObject.Member[]
18+
}) => {
1319
const { theme } = useTheme()
14-
return (
15-
<View style={[SylCommon.Layout.fill, SylCommon.View.background(theme)]}>
16-
<Placeholder
17-
displayType="icon"
18-
icon={theme.assets.images.icons.placeholder.construction}
19-
placeholderText={translate(`router.${ROUTES.Following}`) + translate('label.underConstruction')}
20-
/>
21-
</View>
22-
)
23-
}
24-
25-
/**
26-
* @description styles settings
27-
*/
28-
const styles = {
29-
container: (theme: ITheme): ViewStyle => ({
30-
flex: 1
31-
})
32-
}
20+
const renderContent = () => {
21+
if (!followPeoples) {
22+
return <Placeholder />
23+
}
24+
return <ProfileCardList members={[...followPeoples].reverse()} canLoadMoreContent={false} searchIndicator={false} />
25+
}
3326

34-
/**
35-
* default props
36-
*/
37-
Following.defaultProps = {
38-
loading: false
27+
return <View style={[SylCommon.Layout.fill, SylCommon.View.background(theme)]}>{renderContent()}</View>
3928
}
4029

41-
const mapStateToProps = ({ ui: { login } }: { ui: IState.UIState }) => {
42-
const { error, success, loading } = login
43-
return { error, success, loading }
30+
const mapStateToProps = ({ member: { followPeoples } }: RootState) => {
31+
return {
32+
followPeoples
33+
}
4434
}
4535

4636
export default connect(mapStateToProps)(Following)

0 commit comments

Comments
 (0)