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

PullListScreen.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.

PullListScreen.tsxBlame134 lines · 1 contributor
c53cf00Claude1import React, { useState } from 'react';
2import {
3 View,
4 Text,
5 FlatList,
6 StyleSheet,
7 RefreshControl,
8 TouchableOpacity,
9} from 'react-native';
10import { SafeAreaView } from 'react-native-safe-area-context';
11import { type NativeStackNavigationProp } from '@react-navigation/native-stack';
12import { type RouteProp } from '@react-navigation/native';
13import { colors } from '../theme/colors';
14import { fontSizes, fontWeights } from '../theme/typography';
15import { usePulls } from '../hooks/usePulls';
16import { PullRow } from '../components/PullRow';
17import { LoadingSpinner } from '../components/LoadingSpinner';
18import { ErrorState } from '../components/ErrorState';
19import { EmptyState } from '../components/EmptyState';
9d7a803Claude20import { type MainStackParamList } from '../navigation/types';
c53cf00Claude21
22type PullState = 'open' | 'closed' | 'merged';
23
24type Props = {
25 navigation: NativeStackNavigationProp<MainStackParamList, 'PullList'>;
26 route: RouteProp<MainStackParamList, 'PullList'>;
27};
28
29const STATE_LABELS: PullState[] = ['open', 'merged', 'closed'];
30
31export function PullListScreen({ navigation, route }: Props) {
32 const { owner, repo } = route.params;
33 const [stateFilter, setStateFilter] = useState<PullState>('open');
34 const [refreshing, setRefreshing] = useState(false);
35 const { pulls, loading, error, refresh } = usePulls(owner, repo, stateFilter);
36
37 async function onRefresh() {
38 setRefreshing(true);
39 await refresh();
40 setRefreshing(false);
41 }
42
43 if (loading && !refreshing && pulls.length === 0) {
44 return <LoadingSpinner fullScreen />;
45 }
46
47 if (error && pulls.length === 0) {
48 return <ErrorState message={error} onRetry={refresh} />;
49 }
50
51 return (
52 <SafeAreaView style={styles.safe} edges={['top']}>
53 <View style={styles.filterBar}>
54 {STATE_LABELS.map((s) => (
55 <TouchableOpacity
56 key={s}
57 style={[styles.filterBtn, stateFilter === s && styles.filterBtnActive]}
58 onPress={() => setStateFilter(s)}
59 activeOpacity={0.75}
60 >
61 <Text style={[styles.filterText, stateFilter === s && styles.filterTextActive]}>
62 {s.charAt(0).toUpperCase() + s.slice(1)}
63 </Text>
64 </TouchableOpacity>
65 ))}
66 <Text style={styles.repoBadge}>{owner}/{repo}</Text>
67 </View>
68
69 <FlatList
70 data={pulls}
71 keyExtractor={(item) => item.id}
72 renderItem={({ item }) => (
73 <PullRow
74 pull={item}
75 onPress={() =>
76 navigation.navigate('PullDetail', {
77 owner,
78 repo,
79 number: item.number,
80 })
81 }
82 />
83 )}
84 refreshControl={
85 <RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={colors.accent} />
86 }
87 ListEmptyComponent={
88 <EmptyState
89 title={`No ${stateFilter} pull requests`}
90 subtitle="Pull requests will appear here when created"
91 icon="⑂"
92 />
93 }
94 />
95 </SafeAreaView>
96 );
97}
98
99const styles = StyleSheet.create({
100 safe: { flex: 1, backgroundColor: colors.bg },
101 filterBar: {
102 flexDirection: 'row',
103 alignItems: 'center',
104 padding: 12,
105 gap: 6,
106 borderBottomWidth: 1,
107 borderBottomColor: colors.border,
108 flexWrap: 'wrap',
109 },
110 filterBtn: {
111 paddingHorizontal: 12,
112 paddingVertical: 6,
113 borderRadius: 20,
114 borderWidth: 1,
115 borderColor: 'transparent',
116 },
117 filterBtnActive: {
118 borderColor: colors.border,
119 backgroundColor: colors.bgSurface,
120 },
121 filterText: {
122 color: colors.textMuted,
123 fontSize: fontSizes.sm,
124 fontWeight: fontWeights.medium,
125 },
126 filterTextActive: {
127 color: colors.text,
128 },
129 repoBadge: {
130 marginLeft: 'auto',
131 color: colors.textMuted,
132 fontSize: fontSizes.xs,
133 },
134});