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
6bb07a98
Commit
6bb07a98
authored
Sep 05, 2025
by
tungnq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TODO: Đã hoàn thiện giao diện email home
parent
078dcd66
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
332 additions
and
17 deletions
+332
-17
index.js
src/screens/email/index.js
+140
-3
style.js
src/screens/email/style.js
+100
-5
view.js
src/screens/email/view.js
+92
-9
No files found.
src/screens/email/index.js
View file @
6bb07a98
import
React
from
'react'
;
import
{
Text
,
View
,
StyleSheet
}
from
'react-native'
;
import
React
,
{
useState
,
useEffect
}
from
'react'
;
import
EmailHomeView
from
'./view'
;
const
EmailHome
=
(
props
)
=>
{
const
[
emails
,
setEmails
]
=
useState
([]);
const
[
loading
,
setLoading
]
=
useState
(
true
);
// Fake API data
const
generateFakeEmails
=
()
=>
{
const
today
=
new
Date
();
const
yesterday
=
new
Date
(
today
);
yesterday
.
setDate
(
yesterday
.
getDate
()
-
1
);
const
weekAgo
=
new
Date
(
today
);
weekAgo
.
setDate
(
weekAgo
.
getDate
()
-
7
);
const
monthAgo
=
new
Date
(
today
);
monthAgo
.
setMonth
(
monthAgo
.
getMonth
()
-
1
);
return
[
{
id
:
1
,
sender
:
'Nguyễn Minh Đức'
,
subject
:
'Bảng lương tháng 10'
,
preview
:
'Kính gửi nhân viên ưu tú đây là bảng lương tháng 10 của bạn hãy kiểm tra nếu có thắc...'
,
date
:
today
,
isRead
:
true
,
hasAttachment
:
false
,
avatar
:
'https://via.placeholder.com/40'
},
{
id
:
2
,
sender
:
'Vũ Công Bình'
,
subject
:
'Re: ĐƠN XIN ỨNG LƯƠNG'
,
preview
:
'Kính gửi nhân viên ưu tú đây là bảng lương tháng 10 của bạn hãy kiểm tra nếu có thắc...'
,
date
:
yesterday
,
isRead
:
false
,
hasAttachment
:
true
,
avatar
:
'https://via.placeholder.com/40'
},
{
id
:
3
,
sender
:
'Vũ Công Bình'
,
subject
:
'Re: ĐƠN XIN ỨNG LƯƠNG'
,
preview
:
'Kính gửi nhân viên ưu tú đây là bảng lương tháng 10 của bạn hãy kiểm tra nếu có thắc...'
,
date
:
yesterday
,
isRead
:
false
,
hasAttachment
:
true
,
avatar
:
'https://via.placeholder.com/40'
},
{
id
:
4
,
sender
:
'Trần Thị Mai'
,
subject
:
'Thông báo họp tuần'
,
preview
:
'Cuộc họp tuần sẽ được tổ chức vào thứ 2 tuần tới lúc 9h00 sáng tại phòng họp A...'
,
date
:
weekAgo
,
isRead
:
true
,
hasAttachment
:
false
,
avatar
:
'https://via.placeholder.com/40'
},
{
id
:
5
,
sender
:
'Phòng Nhân Sự'
,
subject
:
'Thông báo nghỉ lễ'
,
preview
:
'Công ty thông báo lịch nghỉ lễ Quốc Khánh 2/9 từ ngày 31/8 đến 3/9...'
,
date
:
monthAgo
,
isRead
:
true
,
hasAttachment
:
false
,
avatar
:
'https://via.placeholder.com/40'
}
];
};
// Logic để format ngày hiển thị
const
formatEmailDate
=
(
emailDate
)
=>
{
const
today
=
new
Date
();
const
yesterday
=
new
Date
(
today
);
yesterday
.
setDate
(
yesterday
.
getDate
()
-
1
);
// Reset time để so sánh chỉ ngày
const
emailDateOnly
=
new
Date
(
emailDate
.
getFullYear
(),
emailDate
.
getMonth
(),
emailDate
.
getDate
());
const
todayOnly
=
new
Date
(
today
.
getFullYear
(),
today
.
getMonth
(),
today
.
getDate
());
const
yesterdayOnly
=
new
Date
(
yesterday
.
getFullYear
(),
yesterday
.
getMonth
(),
yesterday
.
getDate
());
if
(
emailDateOnly
.
getTime
()
===
todayOnly
.
getTime
())
{
return
'Hôm nay'
;
}
else
if
(
emailDateOnly
.
getTime
()
===
yesterdayOnly
.
getTime
())
{
return
'Tuần trước'
;
}
else
{
// Format: dd/mm/yyyy
const
day
=
emailDate
.
getDate
().
toString
().
padStart
(
2
,
'0'
);
const
month
=
(
emailDate
.
getMonth
()
+
1
).
toString
().
padStart
(
2
,
'0'
);
const
year
=
emailDate
.
getFullYear
();
return
`
${
day
}
/
${
month
}
/
${
year
}
`
;
}
};
//Nhóm các email trong danh sách theo ngày trả về một đối tượng trong đó các khoá là ngày
const
groupEmailsByDate
=
(
emailList
)
=>
{
const
grouped
=
{};
emailList
.
forEach
(
email
=>
{
const
dateLabel
=
formatEmailDate
(
email
.
date
);
if
(
!
grouped
[
dateLabel
])
{
grouped
[
dateLabel
]
=
[];
}
grouped
[
dateLabel
].
push
(
email
);
});
return
grouped
;
};
useEffect
(()
=>
{
setTimeout
(()
=>
{
const
fakeEmails
=
generateFakeEmails
();
setEmails
(
fakeEmails
);
setLoading
(
false
);
},
1000
);
},
[]);
const
handleEmailPress
=
(
email
)
=>
{
console
.
log
(
'Email pressed:'
,
email
.
subject
);
};
const
handleRefresh
=
()
=>
{
setLoading
(
true
);
setTimeout
(()
=>
{
const
fakeEmails
=
generateFakeEmails
();
setEmails
(
fakeEmails
);
setLoading
(
false
);
},
1000
);
};
const
groupedEmails
=
groupEmailsByDate
(
emails
);
return
(
<
EmailHomeView
/>
<
EmailHomeView
emails
=
{
emails
}
groupedEmails
=
{
groupedEmails
}
loading
=
{
loading
}
onEmailPress
=
{
handleEmailPress
}
onRefresh
=
{
handleRefresh
}
formatEmailDate
=
{
formatEmailDate
}
/
>
);
};
...
...
src/screens/email/style.js
View file @
6bb07a98
import
{
StyleSheet
,
Text
,
View
}
from
'react-native'
import
{
StyleSheet
}
from
'react-native'
import
R
from
'../../assets/R'
const
styles
=
StyleSheet
.
create
({
container
:
{
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
flex
:
1
,
backgroundColor
:
R
.
colors
.
white
},
body
:
{
loadingContainer
:
{
flex
:
1
,
justifyContent
:
'center'
,
alignItems
:
'center'
,
backgroundColor
:
R
.
colors
.
white
},
loadingText
:
{
marginTop
:
10
,
fontSize
:
R
.
fontsize
.
fontSizeSubTitle
,
fontFamily
:
R
.
fonts
.
fontMedium
,
color
:
R
.
colors
.
gray2
},
listContainer
:
{
},
dateSection
:
{
marginBottom
:
10
,
},
dateSectionHeader
:
{
fontSize
:
R
.
fontsize
.
fontSizeSubTitle
,
fontWeight
:
'600'
,
fontFamily
:
R
.
fonts
.
fontMedium
,
color
:
R
.
colors
.
gray2
,
paddingHorizontal
:
15
,
paddingVertical
:
10
,
},
emailItem
:
{
flexDirection
:
'row'
,
paddingHorizontal
:
15
,
paddingVertical
:
10
,
backgroundColor
:
R
.
colors
.
white
,
alignItems
:
'flex-start'
},
unreadEmail
:
{
backgroundColor
:
R
.
colors
.
grayBorderInputTextHeader
},
avatarContainer
:
{
marginRight
:
10
,
marginTop
:
5
},
avatar
:
{
width
:
40
,
height
:
40
,
borderRadius
:
20
,
backgroundColor
:
R
.
colors
.
blue
,
justifyContent
:
'center'
,
alignItems
:
'center'
},
avatarText
:
{
color
:
R
.
colors
.
white
,
fontSize
:
16
,
fontWeight
:
'bold'
},
emailContent
:
{
flex
:
1
,
marginRight
:
10
},
emailHeader
:
{
flexDirection
:
'row'
,
justifyContent
:
'space-between'
,
alignItems
:
'center'
,
},
senderName
:
{
fontSize
:
R
.
fontsize
.
fontsSizeTitle
,
color
:
R
.
colors
.
black
,
flex
:
1
,
marginRight
:
10
},
unreadText
:
{
fontWeight
:
'600'
,
fontFamily
:
R
.
fonts
.
fontMedium
,
color
:
R
.
colors
.
black
},
emailDate
:
{
fontSize
:
R
.
fontsize
.
fontSizeContent
,
color
:
R
.
colors
.
gray2
},
emailSubject
:
{
fontSize
:
R
.
fontsize
.
fontSizeBtn
,
color
:
R
.
colors
.
black
,
},
emailPreview
:
{
fontSize
:
R
.
fontsize
.
fontSizeContent
,
color
:
R
.
colors
.
gray2
,
},
attachmentIndicator
:
{
width
:
20
,
height
:
20
,
borderRadius
:
10
,
backgroundColor
:
R
.
colors
.
blue
,
justifyContent
:
'center'
,
alignItems
:
'center'
,
marginTop
:
4
},
attachmentCount
:
{
color
:
R
.
colors
.
white
,
fontSize
:
R
.
fontsize
.
fontSizeContent
,
fontWeight
:
'bold'
}
})
export
default
styles
\ No newline at end of file
src/screens/email/view.js
View file @
6bb07a98
import
React
from
'react'
;
import
{
Text
,
View
,
TouchableOpacity
,
StyleSheet
}
from
'react-native'
;
import
{
Text
,
View
,
TouchableOpacity
,
FlatList
,
Image
,
ActivityIndicator
,
RefreshControl
,
}
from
'react-native'
;
import
styles
from
'./style'
;
import
Header
from
'../../components/Header/Header'
;
const
EmailHomeView
=
(
props
)
=>
{
const
{
}
=
props
;
return
(
<
View
style
=
{
styles
.
container
}
>
<
View
>
const
EmailHomeView
=
props
=>
{
const
{
groupedEmails
,
loading
,
onEmailPress
,
onRefresh
,
formatEmailDate
}
=
props
;
const
renderEmailItem
=
({
item
})
=>
(
<
TouchableOpacity
style
=
{[
styles
.
emailItem
,
!
item
.
isRead
&&
styles
.
unreadEmail
]}
onPress
=
{()
=>
onEmailPress
(
item
)}
>
<
View
style
=
{
styles
.
avatarContainer
}
>
<
View
style
=
{
styles
.
avatar
}
>
<
Text
style
=
{
styles
.
avatarText
}
>
{
item
.
sender
.
charAt
(
0
).
toUpperCase
()}
<
/Text
>
<
/View
>
<
/View
>
<
View
style
=
{
styles
.
emailContent
}
>
<
View
style
=
{
styles
.
emailHeader
}
>
<
Text
style
=
{[
styles
.
senderName
,
!
item
.
isRead
&&
styles
.
unreadText
]}
>
{
item
.
sender
}
<
/Text
>
<
/View
>
<
Text
style
=
{[
styles
.
emailSubject
,
!
item
.
isRead
&&
styles
.
unreadText
]}
numberOfLines
=
{
1
}
>
{
item
.
subject
}
<
/Text
>
<
Text
style
=
{
styles
.
emailPreview
}
numberOfLines
=
{
2
}
>
{
item
.
preview
}
<
/Text
>
<
/View
>
<
View
style
=
{{
justifyContent
:
'center'
,
alignItems
:
'flex-end'
,
position
:
'absolute'
,
right
:
15
,
top
:
10
}}
>
<
Text
style
=
{
styles
.
emailDate
}
>
{
`
${
item
.
date
.
getDate
().
toString
().
padStart
(
2
,
'0'
)}
/
${(
item
.
date
.
getMonth
()
+
1
).
toString
().
padStart
(
2
,
'0'
)}
/
${
item
.
date
.
getFullYear
()}
`
}
<
/Text
>
{
item
.
hasAttachment
&&
(
<
View
style
=
{
styles
.
attachmentIndicator
}
>
<
Text
style
=
{
styles
.
attachmentCount
}
>
2
<
/Text
>
<
/View
>
)}
<
/View
>
<
/TouchableOpacity
>
);
const
renderDateSection
=
(
dateLabel
,
emailsForDate
)
=>
(
<
View
key
=
{
dateLabel
}
style
=
{
styles
.
dateSection
}
>
<
Text
style
=
{
styles
.
dateSectionHeader
}
>
{
dateLabel
}
<
/Text
>
{
emailsForDate
.
map
(
email
=>
(
<
View
key
=
{
email
.
id
}
>
{
renderEmailItem
({
item
:
email
})}
<
/View
>
))}
<
/View
>
);
if
(
loading
)
{
return
(
<
View
style
=
{
styles
.
loadingContainer
}
>
<
ActivityIndicator
size
=
"large"
color
=
"#007AFF"
/>
<
Text
style
=
{
styles
.
loadingText
}
>
Đ
ang
t
ả
i
email
...
<
/Text
>
<
/View
>
);
}
return
(
<
View
style
=
{
styles
.
container
}
>
<
FlatList
data
=
{
Object
.
keys
(
groupedEmails
)}
keyExtractor
=
{
item
=>
item
}
renderItem
=
{({
item
:
dateLabel
})
=>
renderDateSection
(
dateLabel
,
groupedEmails
[
dateLabel
])
}
refreshControl
=
{
<
RefreshControl
refreshing
=
{
loading
}
onRefresh
=
{
onRefresh
}
colors
=
{[
'#007AFF'
]}
/
>
}
showsVerticalScrollIndicator
=
{
false
}
contentContainerStyle
=
{
styles
.
listContainer
}
/
>
<
/View
>
);
};
...
...
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