1
1
import { useRouter } from 'next/router' ;
2
- import { useMemo } from 'react' ;
3
2
import { AiOutlineBell , AiOutlineQuestionCircle } from 'react-icons/ai' ;
4
3
5
4
import { useLoginContext } from '@/hooks/useLoginContext' ;
6
5
7
6
import { NoPrefetchLink } from '@/components/links/CustomLink' ;
7
+ import { LevelRender } from '@/components/user/Common' ;
8
8
9
9
import { DEFAULT_AVATAR } from '@/utils/constants' ;
10
10
11
11
import SideLoginButton from './SideLoginButton' ;
12
12
13
13
import { SideProps } from '@/types/home' ;
14
14
15
- export default function UserStatus ( { t } : SideProps ) {
15
+ const UserAvatar = ( { uid, avatar } : { uid : string ; avatar : string } ) => (
16
+ < NoPrefetchLink href = { `/user/${ uid } ` } >
17
+ < a >
18
+ < div className = 'bg-img top-0 left-0 h-10 w-10 shrink-0 grow-0 cursor-pointer rounded-lg object-cover' >
19
+ < img
20
+ className = 'rounded'
21
+ width = '40'
22
+ height = '40'
23
+ src = { avatar || DEFAULT_AVATAR }
24
+ alt = 'side_avatar'
25
+ />
26
+ </ div >
27
+ </ a >
28
+ </ NoPrefetchLink >
29
+ ) ;
30
+
31
+ const NotificationBell = ( { unreadCount } : { unreadCount : number } ) => (
32
+ < span className = 'relative inline-block' >
33
+ < AiOutlineBell
34
+ size = { 20 }
35
+ className = 'text-gray-500 hover:text-blue-500 dark:text-gray-400 dark:hover:text-blue-500'
36
+ />
37
+ { unreadCount > 0 && (
38
+ < span className = 'absolute top-0.5 right-0 flex h-1.5 w-1.5 translate-x-1/2 -translate-y-1/2' >
39
+ < span className = 'absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75' />
40
+ < span className = 'relative inline-flex h-1.5 w-1.5 rounded-full bg-blue-500' />
41
+ </ span >
42
+ ) }
43
+ </ span >
44
+ ) ;
45
+
46
+ const LevelProgress = ( {
47
+ contribute,
48
+ nextLevelScore,
49
+ t,
50
+ } : {
51
+ contribute : number ;
52
+ nextLevelScore : number | null ;
53
+ t : ( key : string ) => string ;
54
+ } ) => (
55
+ < div className = 'mt-1' >
56
+ < div className = 'flex justify-between text-sm' >
57
+ < div className = 'cursor-pointer text-gray-400' >
58
+ < NoPrefetchLink href = '/help/level' >
59
+ < a className = 'align-[-5px] text-xs' >
60
+ < span className = 'mr-0.5' > { t ( 'user_side.contribute_label' ) } </ span >
61
+ < AiOutlineQuestionCircle className = 'inline-block align-[-2px]' />
62
+ </ a >
63
+ </ NoPrefetchLink >
64
+ </ div >
65
+ < span className = 'text-xl text-blue-500' >
66
+ { contribute }
67
+ < span className = 'mx-0.5' > /</ span >
68
+ { nextLevelScore || 'Max' }
69
+ </ span >
70
+ </ div >
71
+ < div className = 'flex h-1.5 w-full overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700' >
72
+ < div
73
+ className = 'flex flex-col justify-center overflow-hidden bg-blue-500'
74
+ style = { {
75
+ width : `${
76
+ ! nextLevelScore ? 100 : ( contribute / nextLevelScore ) * 100
77
+ } %`,
78
+ } }
79
+ />
80
+ </ div >
81
+ </ div >
82
+ ) ;
83
+
84
+ const UserFooter = ( {
85
+ uid,
86
+ isAdmin,
87
+ logout,
88
+ t,
89
+ } : {
90
+ uid : string ;
91
+ isAdmin : boolean ;
92
+ logout : ( ) => void ;
93
+ t : ( key : string ) => string ;
94
+ } ) => (
95
+ < div className = 'mt-3 flex justify-between border-t text-xs dark:border-gray-700' >
96
+ < NoPrefetchLink href = { `/user/${ uid } ` } >
97
+ < a className = 'pl-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline' >
98
+ { t ( 'user_side.profile' ) }
99
+ </ a >
100
+ </ NoPrefetchLink >
101
+ { isAdmin ? (
102
+ < a href = '/taichi/' >
103
+ < div className = 'pr-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline' >
104
+ { t ( 'user_side.admin' ) }
105
+ </ div >
106
+ </ a >
107
+ ) : (
108
+ < div
109
+ className = 'cursor-pointer pr-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline'
110
+ onClick = { logout }
111
+ >
112
+ { t ( 'user_side.logout' ) }
113
+ </ div >
114
+ ) }
115
+ </ div >
116
+ ) ;
117
+
118
+ export default function UserStatus ( { t, i18n_lang } : SideProps ) {
16
119
const router = useRouter ( ) ;
17
120
const { userInfo, isLogin, logout } = useLoginContext ( ) ;
18
- const levelPercent = useMemo ( ( ) => {
19
- if (
20
- typeof userInfo ?. contribute === 'number' &&
21
- typeof userInfo ?. next_level_score === 'number'
22
- ) {
23
- return ( userInfo . contribute / userInfo ?. next_level_score ) * 100 ;
24
- }
25
- // next_level_score 为 null 时则达到了最大等级
26
- return ! userInfo ?. next_level_score ? 100 : 0 ;
27
- } , [ userInfo ] ) ;
28
121
29
122
if ( ! isLogin || ! userInfo ?. success ) {
30
123
return < SideLoginButton text = { t ( 'user_side.login' ) } /> ;
@@ -33,99 +126,43 @@ export default function UserStatus({ t }: SideProps) {
33
126
return (
34
127
< div className = 'relative' >
35
128
< div className = 'flex' >
36
- < NoPrefetchLink href = { `/user/${ userInfo . uid } ` } >
37
- < a >
38
- < div className = 'bg-img top-0 left-0 h-10 w-10 shrink-0 grow-0 cursor-pointer rounded-lg object-cover' >
39
- < img
40
- className = 'rounded'
41
- width = '40'
42
- height = '40'
43
- src = { userInfo ?. avatar || DEFAULT_AVATAR }
44
- alt = 'side_avatar'
45
- />
46
- </ div >
47
- </ a >
48
- </ NoPrefetchLink >
129
+ < UserAvatar uid = { userInfo . uid } avatar = { userInfo . avatar } />
49
130
< div className = 'ml-2 w-full' >
50
131
< div className = 'relative flex h-5 items-center' >
51
132
< div className = 'block w-14 truncate align-baseline font-medium dark:text-gray-300 lg:w-24' >
52
133
{ userInfo . nickname }
53
134
</ div >
54
135
< div className = 'flex-grow' />
55
- < div className = 'justify-end' >
56
- < div
57
- className = 'flex cursor-pointer flex-row'
58
- onClick = { ( ) => {
59
- router . push ( '/notification' ) ;
60
- } }
61
- >
62
- < span className = 'relative inline-block' >
63
- < AiOutlineBell
64
- size = { 20 }
65
- className = 'text-gray-500 hover:text-blue-500 dark:text-gray-400 dark:hover:text-blue-500'
66
- />
67
- { userInfo ?. unread . total > 0 && (
68
- < span className = 'absolute top-0.5 right-0 flex h-1.5 w-1.5 translate-x-1/2 -translate-y-1/2' >
69
- < span className = 'absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75' />
70
- < span className = 'relative inline-flex h-1.5 w-1.5 rounded-full bg-blue-500' />
71
- </ span >
72
- ) }
73
- </ span >
74
- </ div >
136
+ < div
137
+ className = 'cursor-pointer justify-end'
138
+ onClick = { ( ) => router . push ( '/notification' ) }
139
+ >
140
+ < NotificationBell unreadCount = { userInfo ?. unread . total || 0 } />
75
141
</ div >
76
142
</ div >
77
- < div className = 'text-sm font-bold text-blue-500' >
78
- Lv.{ userInfo . level }
79
- </ div >
80
- </ div >
81
- </ div >
82
- { /* 等级展示 */ }
83
- < div className = 'mt-1' >
84
- < div className = 'flex justify-between text-sm' >
85
- < div className = 'cursor-pointer text-gray-400' >
86
- < NoPrefetchLink href = '/help/level' >
87
- < a className = 'align-[-5px] text-xs' >
88
- < span className = 'mr-0.5' >
89
- { t ( 'user_side.contribute_label' ) }
90
- </ span >
91
- < AiOutlineQuestionCircle className = 'inline-block align-[-2px]' />
92
- </ a >
93
- </ NoPrefetchLink >
94
- </ div >
95
- < span className = 'text-xl text-blue-500' >
96
- { userInfo . contribute }
97
- < span className = 'mx-0.5' > /</ span >
98
- { userInfo . next_level_score || 'Max' }
99
- </ span >
100
- </ div >
101
- < div className = 'flex h-1.5 w-full overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700' >
102
- < div
103
- className = 'flex flex-col justify-center overflow-hidden bg-blue-500'
104
- style = { { width : `${ levelPercent } %` } }
105
- />
106
- </ div >
107
- </ div >
108
- < div className = 'mt-3 flex justify-between border-t text-xs dark:border-gray-700' >
109
- < NoPrefetchLink href = { `/user/${ userInfo . uid } ` } >
110
- < a className = 'pl-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline' >
111
- { t ( 'user_side.profile' ) }
112
- </ a >
113
- </ NoPrefetchLink >
114
- { userInfo . permission ?. code == 'super' ? (
115
- < a href = '/taichi/' >
116
- < div className = 'pr-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline' >
117
- { t ( 'user_side.admin' ) }
143
+ < div className = 'flex items-center justify-between' >
144
+ < div className = 'text-sm font-bold text-blue-500' >
145
+ Lv.{ userInfo . level }
146
+ </ div >
147
+ < div className = 'text-xs' >
148
+ { LevelRender ( userInfo . level , false , i18n_lang ) }
118
149
</ div >
119
- </ a >
120
- ) : (
121
- < div
122
- className = 'cursor-pointer pr-1 pt-2 pb-1 text-gray-400 hover:text-blue-500 hover:underline'
123
- onClick = { logout }
124
- >
125
- { t ( 'user_side.logout' ) }
126
150
</ div >
127
- ) }
151
+ </ div >
128
152
</ div >
153
+
154
+ < LevelProgress
155
+ contribute = { userInfo . contribute }
156
+ nextLevelScore = { userInfo . next_level_score }
157
+ t = { t }
158
+ />
159
+
160
+ < UserFooter
161
+ uid = { userInfo . uid }
162
+ isAdmin = { userInfo . permission ?. code === 'super' }
163
+ logout = { logout }
164
+ t = { t }
165
+ />
129
166
</ div >
130
167
) ;
131
168
}
0 commit comments