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

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

RepoListScreen.tsxBlame146 lines · 1 contributor
c53cf00Claude1import React, { useContext, useState } from 'react';
2import {
3 View,
4 Text,
5 FlatList,
6 StyleSheet,
7 RefreshControl,
8 TextInput,
9} from 'react-native';
10import { SafeAreaView } from 'react-native-safe-area-context';
11import { type NativeStackNavigationProp } from '@react-navigation/native-stack';
12import { colors } from '../theme/colors';
13import { fontSizes, fontWeights } from '../theme/typography';
9d7a803Claude14import { AuthContext } from '../navigation/AuthContext';
c53cf00Claude15import { useUserRepos } from '../hooks/useRepo';
16import { RepoCard } from '../components/RepoCard';
17import { LoadingSpinner } from '../components/LoadingSpinner';
18import { ErrorState } from '../components/ErrorState';
19import { EmptyState } from '../components/EmptyState';
9d7a803Claude20import { type MainStackParamList } from '../navigation/types';
c53cf00Claude21
22interface Props {
23 navigation: NativeStackNavigationProp<MainStackParamList>;
24}
25
26export function RepoListScreen({ navigation }: Props) {
27 const { user } = useContext(AuthContext);
28 const { repos, loading, error, refresh } = useUserRepos(user?.username ?? null);
29 const [refreshing, setRefreshing] = useState(false);
30 const [query, setQuery] = useState('');
31
32 async function onRefresh() {
33 setRefreshing(true);
34 await refresh();
35 setRefreshing(false);
36 }
37
38 const filtered = query.trim()
39 ? repos.filter(
40 (r) =>
41 r.name.toLowerCase().includes(query.toLowerCase()) ||
42 (r.description && r.description.toLowerCase().includes(query.toLowerCase()))
43 )
44 : repos;
45
46 if (loading && !refreshing && repos.length === 0) {
47 return <LoadingSpinner fullScreen />;
48 }
49
50 if (error && repos.length === 0) {
51 return <ErrorState message={error} onRetry={refresh} />;
52 }
53
54 return (
55 <SafeAreaView style={styles.safe} edges={['top']}>
56 <View style={styles.header}>
57 <Text style={styles.title}>Repositories</Text>
58 <Text style={styles.count}>{repos.length} repos</Text>
59 </View>
60
61 <View style={styles.searchWrap}>
62 <TextInput
63 style={styles.search}
64 value={query}
65 onChangeText={setQuery}
66 placeholder="Search repositories..."
67 placeholderTextColor={colors.textMuted}
68 autoCapitalize="none"
69 autoCorrect={false}
70 clearButtonMode="while-editing"
71 />
72 </View>
73
74 <FlatList
75 data={filtered}
76 keyExtractor={(item) => item.id}
77 contentContainerStyle={styles.list}
78 renderItem={({ item }) => (
79 <RepoCard
80 repo={item}
81 onPress={() =>
82 navigation.navigate('RepoDetail', {
83 owner: user?.username ?? '',
84 repo: item.name,
85 })
86 }
87 />
88 )}
89 refreshControl={
90 <RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={colors.accent} />
91 }
92 ListEmptyComponent={
93 <EmptyState
94 title={query ? 'No matching repos' : 'No repositories'}
95 subtitle={query ? 'Try a different search term' : 'Create your first repo on gluecron.com'}
96 icon="⌥"
97 />
98 }
99 />
100 </SafeAreaView>
101 );
102}
103
104const styles = StyleSheet.create({
105 safe: {
106 flex: 1,
107 backgroundColor: colors.bg,
108 },
109 header: {
110 flexDirection: 'row',
111 justifyContent: 'space-between',
112 alignItems: 'center',
113 paddingHorizontal: 16,
114 paddingTop: 16,
115 paddingBottom: 8,
116 },
117 title: {
118 color: colors.text,
119 fontSize: fontSizes.xl,
120 fontWeight: fontWeights.bold,
121 },
122 count: {
123 color: colors.textMuted,
124 fontSize: fontSizes.sm,
125 },
126 searchWrap: {
127 paddingHorizontal: 16,
128 paddingBottom: 12,
129 },
130 search: {
131 backgroundColor: colors.bgSurface,
132 borderWidth: 1,
133 borderColor: colors.border,
134 borderRadius: 8,
135 paddingHorizontal: 12,
136 paddingVertical: 10,
137 color: colors.text,
138 fontSize: fontSizes.base,
139 minHeight: 40,
140 },
141 list: {
142 padding: 16,
143 paddingTop: 4,
144 paddingBottom: 32,
145 },
146});