Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
AppUms_Lecturer
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tungnq
AppUms_Lecturer
Commits
74d3ef42
Commit
74d3ef42
authored
Aug 12, 2025
by
nguyenquangtung004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TODO: Đã fix thành công SafeAreaView không bị khoảng trắng
parent
cd549183
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
171 additions
and
122 deletions
+171
-122
style.js
src/screens/home/style.js
+0
-1
view.js
src/screens/home/view.js
+171
-121
No files found.
src/screens/home/style.js
View file @
74d3ef42
...
@@ -6,7 +6,6 @@ const styles = StyleSheet.create({
...
@@ -6,7 +6,6 @@ const styles = StyleSheet.create({
containerBoxHeader
:
{
containerBoxHeader
:
{
flexDirection
:
"row"
,
flexDirection
:
"row"
,
marginHorizontal
:
15
,
marginHorizontal
:
15
,
marginTop
:
15
,
maxHeight
:
40
,
maxHeight
:
40
,
},
},
boxLogo
:{
boxLogo
:{
...
...
src/screens/home/view.js
View file @
74d3ef42
...
@@ -10,6 +10,8 @@ import {
...
@@ -10,6 +10,8 @@ import {
Keyboard
,
Keyboard
,
SafeAreaView
,
SafeAreaView
,
Image
,
Image
,
StatusBar
,
// FEATURE: Thêm StatusBar để control status bar
Platform
,
// FEATURE: Check platform để xử lý khác biệt iOS/Android
}
from
"react-native"
;
}
from
"react-native"
;
import
HeaderCus
from
"../home/header"
;
import
HeaderCus
from
"../home/header"
;
import
R
from
"../../assets/R"
;
import
R
from
"../../assets/R"
;
...
@@ -17,6 +19,7 @@ import ItemGrid from "./item";
...
@@ -17,6 +19,7 @@ import ItemGrid from "./item";
import
styles
from
"./style"
;
import
styles
from
"./style"
;
import
{
useNavigation
}
from
"@react-navigation/native"
;
import
{
useNavigation
}
from
"@react-navigation/native"
;
import
*
as
SCREENNAME
from
"../../routers/ScreenNames"
;
import
*
as
SCREENNAME
from
"../../routers/ScreenNames"
;
const
HomeView
=
(
props
)
=>
{
const
HomeView
=
(
props
)
=>
{
const
{
const
{
menuActivity
,
menuActivity
,
...
@@ -29,142 +32,188 @@ const HomeView = (props) => {
...
@@ -29,142 +32,188 @@ const HomeView = (props) => {
onSearchChange
,
onSearchChange
,
}
=
props
;
}
=
props
;
const
navigate
=
useNavigation
();
const
navigate
=
useNavigation
();
// FUNCTIONALITY: Render menu item với tối ưu performance
const
renderMenuItem
=
({
item
})
=>
{
const
renderMenuItem
=
({
item
})
=>
{
return
<
ItemGrid
item
=
{
item
}
onPress
=
{()
=>
onMenuItemPress
(
item
)}
/>
;
return
<
ItemGrid
item
=
{
item
}
onPress
=
{()
=>
onMenuItemPress
(
item
)}
/>
;
};
};
const
cardItemInfo
=
()
=>
{
return
<
View
style
=
{
styles
.
profileCard
}
>
// UI/UX: Card thông tin user với layout tối ưu
<
View
style
=
{
styles
.
profile_left
}
>
const
cardItemInfo
=
()
=>
{
{
/* Avatar */
}
return
(
<
View
style
=
{
styles
.
avatar
}
>
<
View
style
=
{
styles
.
profileCard
}
>
{
userProfile
?.
avatar
?
(
<
View
style
=
{
styles
.
profile_left
}
>
<
Image
{
/* FEATURE: Avatar với fallback placeholder */
}
source
=
{{
uri
:
userProfile
.
avatar
}}
<
View
style
=
{
styles
.
avatar
}
>
style
=
{
styles
.
avatar_image
}
{
userProfile
?.
avatar
?
(
resizeMode
=
"cover"
<
Image
/>
source
=
{{
uri
:
userProfile
.
avatar
}}
)
:
(
style
=
{
styles
.
avatar_image
}
<
View
style
=
{
styles
.
avatar_placeholder
}
>
resizeMode
=
"cover"
<
Text
style
=
{
styles
.
avatar_text
}
>
/>
{
userProfile
?.
name
?.
charAt
(
0
)}
)
:
(
<
View
style
=
{
styles
.
avatar_placeholder
}
>
<
Text
style
=
{
styles
.
avatar_text
}
>
{
userProfile
?.
name
?.
charAt
(
0
)
||
"U"
}
<
/Text
>
<
/View
>
)}
<
/View
>
{
/* FEATURE: Thông tin user với null safety */
}
<
View
style
=
{
styles
.
information
}
>
<
Text
style
=
{
styles
.
text_card_info
}
numberOfLines
=
{
1
}
ellipsizeMode
=
"tail"
>
{
userProfile
?.
name
??
"Không có dữ liệu"
}
<
/Text
>
<
Text
style
=
{
styles
.
text_card_info
}
numberOfLines
=
{
1
}
ellipsizeMode
=
"tail"
>
{
userProfile
?.
phone
??
"Không có dữ liệu"
}
<
/Text
>
<
/Text
>
<
/View
>
<
/View
>
)}
<
/View
>
<
/View
>
{
/* FEATURE: Button navigate tới profile */
}
{
/* Information */
}
<
TouchableOpacity
<
View
style
=
{
styles
.
information
}
>
style
=
{
styles
.
profile_btn
}
<
Text
onPress
=
{()
=>
navigate
.
navigate
(
SCREENNAME
.
PROFILE
)}
style
=
{
styles
.
text_card_info
}
activeOpacity
=
{
0.7
}
numberOfLines
=
{
1
}
ellipsizeMode
=
"tail"
>
{
userProfile
?.
name
??
"Không có dữ liệu"
}
<
/Text
>
<
Text
style
=
{
styles
.
text_card_info
}
numberOfLines
=
{
1
}
ellipsizeMode
=
"tail"
>
>
{
userProfile
?.
phone
??
"Không có dữ liệu"
}
<
Text
style
=
{
styles
.
btn_text
}
>
H
ồ
s
ơ
c
á
nh
â
n
<
/Text
>
<
/Text
>
<
View
style
=
{
styles
.
iconNext
}
>
<
Image
source
=
{
R
.
images
.
icNext
}
style
=
{{
width
:
5
,
height
:
10
}}
// OPTIMIZE: Dùng style thay maxWidth/maxHeight
/
>
<
/View
>
<
/TouchableOpacity
>
<
/View
>
<
/View
>
<
/View
>
);
};
<
TouchableOpacity
style
=
{
styles
.
profile_btn
}
onPress
=
{()
=>
navigate
.
navigate
(
SCREENNAME
.
PROFILE
)}
>
// FUNCTIONALITY: Render menu activity với FlatList tối ưu
<
Text
style
=
{
styles
.
btn_text
}
>
H
ồ
s
ơ
c
á
nh
â
n
<
/Text
>
const
renderMenuActivity
=
()
=>
{
<
View
style
=
{
styles
.
iconNext
}
>
return
(
<
Image
<
View
style
=
{
styles
.
menu_container
}
>
source
=
{
R
.
images
.
icNext
}
<
Text
style
=
{
styles
.
menu_title
}
>
Gi
ả
ng
d
ạ
y
<
/Text
>
maxWidth
=
{
5
}
<
FlatList
maxHeight
=
{
10
}
data
=
{
menuActivity
}
renderItem
=
{
renderMenuItem
}
numColumns
=
{
3
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
scrollEnabled
=
{
false
}
columnWrapperStyle
=
{
styles
.
row
}
ListEmptyComponent
=
{
renderItemEmpty
}
removeClippedSubviews
=
{
true
}
// PERFORMANCE: Tối ưu memory
maxToRenderPerBatch
=
{
9
}
// PERFORMANCE: Render tối đa 9 items/batch
/
>
/
>
<
/View
>
<
/View
>
<
/TouchableOpacity
>
);
<
/View
>
};
}
const
renderMenuActivity
=
()
=>
{
// FUNCTIONALITY: Render menu statistics
return
<
View
style
=
{
styles
.
menu_container
}
>
const
renderMenuStatistics
=
()
=>
{
<
Text
style
=
{
styles
.
menu_title
}
>
Gi
ả
ng
d
ạ
y
<
/Text
>
return
(
<
FlatList
<
View
style
=
{
styles
.
menu_container
}
>
data
=
{
menuActivity
}
<
Text
style
=
{
styles
.
menu_title
}
>
Th
ố
ng
k
ê
<
/Text
>
renderItem
=
{
renderMenuItem
}
<
FlatList
numColumns
=
{
3
}
data
=
{
menuStatistics
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
renderItem
=
{
renderMenuItem
}
scrollEnabled
=
{
false
}
numColumns
=
{
3
}
columnWrapperStyle
=
{
styles
.
row
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
ListEmptyComponent
=
{
renderItemEmpty
}
scrollEnabled
=
{
false
}
/
>
columnWrapperStyle
=
{
styles
.
row
}
<
/View
>
ListEmptyComponent
=
{
renderItemEmpty
}
}
removeClippedSubviews
=
{
true
}
const
renderMenuStatistics
=
()
=>
{
maxToRenderPerBatch
=
{
9
}
return
<
View
style
=
{
styles
.
menu_container
}
>
/
>
<
Text
style
=
{
styles
.
menu_title
}
>
Th
ố
ng
k
ê
<
/Text
>
<
/View
>
<
FlatList
);
data
=
{
menuStatistics
}
};
renderItem
=
{
renderMenuItem
}
numColumns
=
{
3
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
scrollEnabled
=
{
false
}
columnWrapperStyle
=
{
styles
.
row
}
ListEmptyComponent
=
{
renderItemEmpty
}
/
>
<
/View
>
}
const
renderMenuNotification
=
()
=>
{
return
<
View
style
=
{
styles
.
menu_container
}
>
<
Text
style
=
{
styles
.
menu_title
}
>
V
ă
n
b
ả
n
v
à
th
ô
ng
b
á
o
<
/Text
>
<
FlatList
data
=
{
menuNotification
}
renderItem
=
{
renderMenuItem
}
numColumns
=
{
3
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
scrollEnabled
=
{
false
}
columnWrapperStyle
=
{
styles
.
row
}
ListEmptyComponent
=
{
renderItemEmpty
}
/
>
<
/View
>
}
const
renderItemEmpty
=
()
=>
{
// FUNCTIONALITY: Render menu notification
return
<
View
style
=
{
styles
.
cardItemEmpty
}
>
const
renderMenuNotification
=
()
=>
{
<
Image
return
(
source
=
{
R
.
images
.
icNoData
}
<
View
style
=
{
styles
.
menu_container
}
>
maxWidth
=
{
50
}
<
Text
style
=
{
styles
.
menu_title
}
>
V
ă
n
b
ả
n
v
à
th
ô
ng
b
á
o
<
/Text
>
maxHeight
=
{
50
}
<
FlatList
/
>
data
=
{
menuNotification
}
<
Text
style
=
{[
styles
.
menu_text
,{
color
:
R
.
colors
.
red
}]}
>
Kh
ô
ng
c
ó
d
ữ
li
ệ
u
<
/Text
>
renderItem
=
{
renderMenuItem
}
<
/View
>
numColumns
=
{
3
}
}
keyExtractor
=
{(
item
)
=>
item
.
id
.
toString
()}
scrollEnabled
=
{
false
}
columnWrapperStyle
=
{
styles
.
row
}
ListEmptyComponent
=
{
renderItemEmpty
}
removeClippedSubviews
=
{
true
}
maxToRenderPerBatch
=
{
9
}
/
>
<
/View
>
);
};
// UI/UX: Empty state component
const
renderItemEmpty
=
()
=>
{
return
(
<
View
style
=
{
styles
.
cardItemEmpty
}
>
<
Image
source
=
{
R
.
images
.
icNoData
}
style
=
{{
width
:
50
,
height
:
50
}}
// OPTIMIZE: Dùng style object
/
>
<
Text
style
=
{[
styles
.
menu_text
,
{
color
:
R
.
colors
.
red
}]}
>
Kh
ô
ng
c
ó
d
ữ
li
ệ
u
<
/Text
>
<
/View
>
);
};
return
(
return
(
<
TouchableWithoutFeedback
onPress
=
{
Keyboard
.
dismiss
}
accessible
=
{
false
}
>
<>
<
SafeAreaView
style
=
{
styles
.
safeArea
}
>
{
/* FIXME: StatusBar để control màu status bar và remove white space */
}
<
View
style
=
{
styles
.
container_body
}
>
<
StatusBar
<
ImageBackground
barStyle
=
"light-content"
// Text trắng trên status bar
source
=
{
R
.
images
.
igBackground
}
backgroundColor
=
"transparent"
// Background trong suốt
style
=
{
styles
.
background_header
}
translucent
=
{
true
}
// Cho phép content hiển thị dưới status bar
>
/
>
<
HeaderCus
value
=
{
searchText
}
<
TouchableWithoutFeedback
onPress
=
{
Keyboard
.
dismiss
}
accessible
=
{
false
}
>
onChangeText
=
{
onSearchChange
}
{
/* FIXME: Dùng View thay SafeAreaView để loại bỏ white space */
}
/
>
<
View
style
=
{[
styles
.
safeArea
,
{
paddingTop
:
Platform
.
OS
===
'ios'
?
0
:
StatusBar
.
currentHeight
}]}
>
{
cardItemInfo
()}
<
View
style
=
{
styles
.
container_body
}
>
<
/ImageBackground
>
<
ImageBackground
<
ScrollView
source
=
{
R
.
images
.
igBackground
}
showsVerticalScrollIndicator
=
{
false
}
style
=
{[
styles
.
background_header
,
{
style
=
{
styles
.
scroll
}
>
// FIXME: Extend background lên trên để cover status bar
{
renderMenuActivity
()}
paddingTop
:
Platform
.
OS
===
'ios'
?
50
:
(
StatusBar
.
currentHeight
||
0
)
+
10
,
{
renderMenuStatistics
()}
}]}
{
renderMenuNotification
()}
>
<
/ScrollView
>
<
HeaderCus
value
=
{
searchText
}
onChangeText
=
{
onSearchChange
}
/
>
{
cardItemInfo
()}
<
/ImageBackground
>
<
ScrollView
showsVerticalScrollIndicator
=
{
false
}
style
=
{
styles
.
scroll
}
bounces
=
{
false
}
// OPTIMIZE: Tắt bounce effect
overScrollMode
=
"never"
// OPTIMIZE: Android không over scroll
>
{
renderMenuActivity
()}
{
renderMenuStatistics
()}
{
renderMenuNotification
()}
<
/ScrollView
>
<
/View
>
<
/View
>
<
/View
>
<
/
SafeAreaView
>
<
/
TouchableWithoutFeedback
>
<
/
TouchableWithoutFeedback
>
<
/
>
);
);
};
};
export
default
HomeView
;
export
default
HomeView
;
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment