1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
| import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { colors } from '../theme/colors';
import { fontSizes, fontWeights } from '../theme/typography';
import { type PullRequest } from '../api/client';
interface Props {
pull: PullRequest;
onPress: () => void;
}
function timeAgo(dateStr: string): string {
const diff = Date.now() - new Date(dateStr).getTime();
const minutes = Math.floor(diff / 60000);
if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24);
if (days < 30) return `${days}d ago`;
return `${Math.floor(days / 30)}mo ago`;
}
function stateColor(state: string): string {
switch (state) {
case 'open': return colors.green;
case 'merged': return colors.accent;
case 'closed': return colors.red;
default: return colors.textMuted;
}
}
function stateLabel(state: string): string {
switch (state) {
case 'open': return 'Open';
case 'merged': return 'Merged';
case 'closed': return 'Closed';
default: return state;
}
}
export function PullRow({ pull, onPress }: Props) {
return (
<TouchableOpacity style={styles.row} onPress={onPress} activeOpacity={0.75}>
<View style={[styles.stateDot, { backgroundColor: stateColor(pull.state) }]} />
<View style={styles.content}>
<Text style={styles.title} numberOfLines={2}>
{pull.title}
</Text>
<View style={styles.meta}>
<Text style={styles.number}>#{pull.number}</Text>
<View style={[styles.stateBadge, { backgroundColor: stateColor(pull.state) + '22', borderColor: stateColor(pull.state) }]}>
<Text style={[styles.stateText, { color: stateColor(pull.state) }]}>{stateLabel(pull.state)}</Text>
</View>
<Text style={styles.branches}>
{pull.headBranch} → {pull.baseBranch}
</Text>
</View>
<Text style={styles.time}>opened {timeAgo(pull.createdAt)}</Text>
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
row: {
flexDirection: 'row',
alignItems: 'flex-start',
padding: 14,
borderBottomWidth: 1,
borderBottomColor: colors.border,
gap: 12,
},
stateDot: {
width: 8,
height: 8,
borderRadius: 4,
marginTop: 5,
flexShrink: 0,
},
content: {
flex: 1,
gap: 4,
},
title: {
color: colors.text,
fontSize: fontSizes.base,
fontWeight: fontWeights.medium,
lineHeight: 20,
},
meta: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
flexWrap: 'wrap',
},
number: {
color: colors.textMuted,
fontSize: fontSizes.xs,
},
stateBadge: {
borderRadius: 4,
paddingHorizontal: 6,
paddingVertical: 2,
borderWidth: 1,
},
stateText: {
fontSize: fontSizes.xs,
fontWeight: fontWeights.medium,
},
branches: {
color: colors.textMuted,
fontSize: fontSizes.xs,
fontFamily: 'monospace',
},
time: {
color: colors.textMuted,
fontSize: fontSizes.xs,
},
});
|