Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

PullRow.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

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