feat: tambah fitur tandai terbaca per notifikasi dan ekstrak styles
- Tambah fungsi handleMarkOneRead untuk tandai satu notifikasi terbaca tanpa navigasi - Tambah tombol "Tandai dibaca" pada tiap notifikasi yang belum terbaca - Buat notification.styles.ts dengan 8 class styles untuk notification screen - Daftarkan NotificationStyles ke constants/styles/index.ts
This commit is contained in:
@@ -136,6 +136,17 @@ export default function Notification() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleMarkOneRead(id: string) {
|
||||||
|
try {
|
||||||
|
const hasil = await decryptToken(String(token?.current))
|
||||||
|
await apiReadOneNotification({ user: hasil, id: id })
|
||||||
|
await queryClient.invalidateQueries({ queryKey: ['notifications'] })
|
||||||
|
dispatch(setUpdateNotification(!updateNotification))
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
|
<SafeAreaView style={[Styles.flex1, { backgroundColor: colors.background }]}>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -174,7 +185,8 @@ export default function Notification() {
|
|||||||
onCancel={() => setShowConfirm(false)}
|
onCancel={() => setShowConfirm(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={[Styles.flex1, Styles.ph15, { paddingTop: 10 }]}>
|
|
||||||
|
<View style={[Styles.flex1, Styles.ph15, Styles.notifContainer]}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
[0, 1, 2, 3, 4].map((_, i) => <SkeletonTwoItem key={i} />)
|
[0, 1, 2, 3, 4].map((_, i) => <SkeletonTwoItem key={i} />)
|
||||||
) : flatData.length === 0 ? (
|
) : flatData.length === 0 ? (
|
||||||
@@ -203,11 +215,11 @@ export default function Notification() {
|
|||||||
renderItem={({ item }) => {
|
renderItem={({ item }) => {
|
||||||
if (item._type === 'header') {
|
if (item._type === 'header') {
|
||||||
return (
|
return (
|
||||||
<View style={[Styles.rowItemsCenter, { marginTop: 16, marginBottom: 8 }]}>
|
<View style={[Styles.rowItemsCenter, Styles.notifHeaderRow]}>
|
||||||
<Text style={{ fontSize: 11, fontWeight: '600', color: colors.dimmed, letterSpacing: 0.6, textTransform: 'uppercase' }}>
|
<Text style={[Styles.notifDateText, { color: colors.dimmed }]}>
|
||||||
{item.date}
|
{item.date}
|
||||||
</Text>
|
</Text>
|
||||||
<View style={{ flex: 1, height: 1, backgroundColor: colors.icon + '20', marginLeft: 8 }} />
|
<View style={[Styles.notifDateSeparator, { backgroundColor: colors.icon + '20' }]} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -217,37 +229,20 @@ export default function Notification() {
|
|||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => handleReadNotification(item.id, item.category, item.idContent)}
|
onPress={() => handleReadNotification(item.id, item.category, item.idContent)}
|
||||||
style={({ pressed }) => [{
|
style={({ pressed }) => [Styles.notifItemRow, {
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderRadius: 10,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.icon + '20',
|
borderColor: colors.icon + '20',
|
||||||
backgroundColor: pressed
|
backgroundColor: pressed
|
||||||
? colors.icon + '10'
|
? colors.icon + '10'
|
||||||
: item.isRead
|
: item.isRead
|
||||||
? colors.icon + '10'
|
? colors.icon + '10'
|
||||||
: colors.card,
|
: colors.card,
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 10,
|
|
||||||
marginBottom: 6,
|
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
{/* Colored icon */}
|
<View style={[Styles.notifIconContainer, { backgroundColor: color + '20' }]}>
|
||||||
<View style={{
|
|
||||||
width: 42,
|
|
||||||
height: 42,
|
|
||||||
borderRadius: 21,
|
|
||||||
backgroundColor: color + '20',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
flexShrink: 0,
|
|
||||||
}}>
|
|
||||||
<Feather name={icon} size={20} color={color} />
|
<Feather name={icon} size={20} color={color} />
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Content */}
|
<View style={[Styles.flex1, Styles.notifContent]}>
|
||||||
<View style={[Styles.flex1, { marginLeft: 10 }]}>
|
|
||||||
<View style={[Styles.rowSpaceBetween, Styles.itemsCenter]}>
|
<View style={[Styles.rowSpaceBetween, Styles.itemsCenter]}>
|
||||||
<View style={[Styles.flex1, Styles.mr10]}>
|
<View style={[Styles.flex1, Styles.mr10]}>
|
||||||
<Text
|
<Text
|
||||||
@@ -257,6 +252,20 @@ export default function Notification() {
|
|||||||
{item.title}
|
{item.title}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
{!item.isRead && (
|
||||||
|
<Pressable
|
||||||
|
onPress={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
handleMarkOneRead(item.id)
|
||||||
|
}}
|
||||||
|
hitSlop={8}
|
||||||
|
style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1, flexShrink: 0 })}
|
||||||
|
>
|
||||||
|
<Text style={Styles.notifMarkReadText}>
|
||||||
|
Tandai dibaca
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text
|
||||||
style={[Styles.textMediumNormal, { color: item.isRead ? colors.dimmed : colors.text, opacity: item.isRead ? 0.7 : 1 }]}
|
style={[Styles.textMediumNormal, { color: item.isRead ? colors.dimmed : colors.text, opacity: item.isRead ? 0.7 : 1 }]}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import CardStyles from './card.styles';
|
|||||||
import ModalStyles from './modal.styles';
|
import ModalStyles from './modal.styles';
|
||||||
import HeaderStyles from './header.styles';
|
import HeaderStyles from './header.styles';
|
||||||
import ComponentStyles from './component.styles';
|
import ComponentStyles from './component.styles';
|
||||||
|
import NotificationStyles from './notification.styles';
|
||||||
|
|
||||||
const Styles = StyleSheet.create({
|
const Styles = StyleSheet.create({
|
||||||
...SpacingStyles,
|
...SpacingStyles,
|
||||||
@@ -21,6 +22,7 @@ const Styles = StyleSheet.create({
|
|||||||
...ModalStyles,
|
...ModalStyles,
|
||||||
...HeaderStyles,
|
...HeaderStyles,
|
||||||
...ComponentStyles,
|
...ComponentStyles,
|
||||||
|
...NotificationStyles,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Styles;
|
export default Styles;
|
||||||
|
|||||||
29
constants/styles/notification.styles.ts
Normal file
29
constants/styles/notification.styles.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
|
||||||
|
const NotificationStyles = StyleSheet.create({
|
||||||
|
notifContainer: { paddingTop: 10 },
|
||||||
|
notifHeaderRow: { marginTop: 16, marginBottom: 8 },
|
||||||
|
notifDateSeparator: { flex: 1, height: 1, marginLeft: 8 },
|
||||||
|
notifDateText: { fontSize: 11, fontWeight: '600', letterSpacing: 0.6, textTransform: 'uppercase' },
|
||||||
|
notifItemRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 10,
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
notifIconContainer: {
|
||||||
|
width: 42,
|
||||||
|
height: 42,
|
||||||
|
borderRadius: 21,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexShrink: 0,
|
||||||
|
},
|
||||||
|
notifContent: { marginLeft: 10 },
|
||||||
|
notifMarkReadText: { fontSize: 11, color: '#3B82F6', fontWeight: '600' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default NotificationStyles;
|
||||||
Reference in New Issue
Block a user