Bluesky app fork with some witchin' additions 馃挮
at main 143 lines 5.0 kB view raw
1import {useCallback, useState} from 'react' 2import {LayoutAnimation, Pressable, View} from 'react-native' 3import {msg} from '@lingui/core/macro' 4import {useLingui} from '@lingui/react' 5import {Trans} from '@lingui/react/macro' 6import {useFocusEffect} from '@react-navigation/native' 7 8import {useGetTimeAgo} from '#/lib/hooks/useTimeAgo' 9import { 10 type CommonNavigatorParams, 11 type NativeStackScreenProps, 12} from '#/lib/routes/types' 13import {getEntries} from '#/logger/logDump' 14import {useSetMinimalShellMode, useTickEveryMinute} from '#/state/shell' 15import {atoms as a, useTheme} from '#/alf' 16import { 17 ChevronBottom_Stroke2_Corner0_Rounded as ChevronBottomIcon, 18 ChevronTop_Stroke2_Corner0_Rounded as ChevronTopIcon, 19} from '#/components/icons/Chevron' 20import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo' 21import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' 22import * as Layout from '#/components/Layout' 23import {Text} from '#/components/Typography' 24 25export function LogScreen({}: NativeStackScreenProps< 26 CommonNavigatorParams, 27 'Log' 28>) { 29 const t = useTheme() 30 const {_} = useLingui() 31 const setMinimalShellMode = useSetMinimalShellMode() 32 const [expanded, setExpanded] = useState<string[]>([]) 33 const timeAgo = useGetTimeAgo() 34 const tick = useTickEveryMinute() 35 36 useFocusEffect( 37 useCallback(() => { 38 setMinimalShellMode(false) 39 }, [setMinimalShellMode]), 40 ) 41 42 const toggler = (id: string) => () => { 43 LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 44 if (expanded.includes(id)) { 45 setExpanded(expanded.filter(v => v !== id)) 46 } else { 47 setExpanded([...expanded, id]) 48 } 49 } 50 51 return ( 52 <Layout.Screen> 53 <Layout.Header.Outer> 54 <Layout.Header.BackButton /> 55 <Layout.Header.Content> 56 <Layout.Header.TitleText> 57 <Trans>System log</Trans> 58 </Layout.Header.TitleText> 59 </Layout.Header.Content> 60 <Layout.Header.Slot /> 61 </Layout.Header.Outer> 62 <Layout.Content> 63 {getEntries() 64 .slice(0) 65 .map(entry => { 66 return ( 67 <View key={`entry-${entry.id}`}> 68 <Pressable 69 style={[ 70 a.flex_row, 71 a.align_center, 72 a.py_md, 73 a.px_sm, 74 a.border_b, 75 t.atoms.border_contrast_low, 76 t.atoms.bg, 77 a.gap_sm, 78 ]} 79 onPress={toggler(entry.id)} 80 accessibilityLabel={_(msg`View debug entry`)} 81 accessibilityHint={_( 82 msg`Opens additional details for a debug entry`, 83 )}> 84 {entry.level === 'warn' || entry.level === 'error' ? ( 85 <WarningIcon size="sm" fill={t.palette.negative_500} /> 86 ) : ( 87 <CircleInfoIcon size="sm" /> 88 )} 89 <View 90 style={[ 91 a.flex_1, 92 a.flex_row, 93 a.justify_start, 94 a.align_center, 95 a.gap_sm, 96 ]}> 97 {entry.context && ( 98 <Text style={[t.atoms.text_contrast_medium]}> 99 ({String(entry.context)}) 100 </Text> 101 )} 102 <Text>{String(entry.message)}</Text> 103 </View> 104 {entry.metadata && 105 Object.keys(entry.metadata).length > 0 && 106 (expanded.includes(entry.id) ? ( 107 <ChevronTopIcon 108 size="sm" 109 style={[t.atoms.text_contrast_low]} 110 /> 111 ) : ( 112 <ChevronBottomIcon 113 size="sm" 114 style={[t.atoms.text_contrast_low]} 115 /> 116 ))} 117 <Text style={[{minWidth: 40}, t.atoms.text_contrast_medium]}> 118 {timeAgo(entry.timestamp, tick)} 119 </Text> 120 </Pressable> 121 {expanded.includes(entry.id) && ( 122 <View 123 style={[ 124 t.atoms.bg_contrast_25, 125 a.rounded_xs, 126 a.p_sm, 127 a.border_b, 128 t.atoms.border_contrast_low, 129 ]}> 130 <View style={[a.px_sm, a.py_xs]}> 131 <Text style={[a.leading_snug, {fontFamily: 'monospace'}]}> 132 {JSON.stringify(entry.metadata, null, 2)} 133 </Text> 134 </View> 135 </View> 136 )} 137 </View> 138 ) 139 })} 140 </Layout.Content> 141 </Layout.Screen> 142 ) 143}