Skip to content

Commit b56f3ec

Browse files
committed
feat: add infinite scroll pagination and notification support
- Added pagination support with `fetchNextPage`, `hasNextPage`, and `isFetchingNextPage` to explore screens for both repositories and developers - Implemented infinite scroll with `onEndReached` and `onEndReachedThreshold` for better performance with large datasets - Added loading indicators during pagination with `ActivityIndicator` - Added `expo-notifications` dependency to app.json for future notification features - Updated FlatList components to use flattened data from paginated results - Enhanced artifact list in workflow runs to show empty state when no artifacts exist
1 parent 6464353 commit b56f3ec

12 files changed

Lines changed: 466 additions & 58 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## [0.0.10](https://github.com/involvex/awesome-github-app/compare/0.0.9...0.0.10) (2026-03-09)
2+
3+
### Features
4+
5+
- add infinite scroll pagination and notification support ([a7557fc](https://github.com/involvex/awesome-github-app/commit/a7557fc8fdb4e39e2ad034baab151da3453ab64d))
6+
17
## [0.0.9](https://github.com/involvex/awesome-github-app/compare/0.0.8...0.0.9) (2026-03-09)
28

39
## [0.0.8](https://github.com/involvex/awesome-github-app/compare/0.0.7...0.0.8) (2026-03-09)

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ android {
9292
applicationId 'com.involvex.awesomegithubapp'
9393
minSdkVersion rootProject.ext.minSdkVersion
9494
targetSdkVersion rootProject.ext.targetSdkVersion
95-
versionCode 9
96-
versionName "0.0.9"
95+
versionCode 10
96+
versionName "0.0.10"
9797

9898
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
9999
}

app.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"name": "awesome-github-app",
44
"slug": "awesome-github-app",
55
"owner": "involvex",
6-
"version": "0.0.9",
6+
"version": "0.0.10",
77
"orientation": "portrait",
88
"userInterfaceStyle": "automatic",
99
"githubUrl": "https://github.com/involvex/awesome-github-app.git",
@@ -18,7 +18,8 @@
1818
"expo-font",
1919
"expo-web-browser",
2020
"expo-image",
21-
"expo-secure-store"
21+
"expo-secure-store",
22+
"expo-notifications"
2223
],
2324
"android": {
2425
"package": "com.involvex.awesomegithubapp",
@@ -27,7 +28,7 @@
2728
"backgroundColor": "#ffffff"
2829
},
2930
"userInterfaceStyle": "automatic",
30-
"versionCode": 9
31+
"versionCode": 10
3132
},
3233
"web": {
3334
"output": "single",
@@ -36,7 +37,7 @@
3637
},
3738
"ios": {
3839
"bundleIdentifier": "com.involvex.awesomegithubapp",
39-
"buildNumber": "9",
40+
"buildNumber": "10",
4041
"infoPlist": {
4142
"ITSAppUsesNonExemptEncryption": false
4243
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "awesome-github-app",
3-
"version": "0.0.9",
3+
"version": "0.0.10",
44
"private": true,
55
"keywords": [
66
"github",

src/app/(tabs)/explore/developers.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ export default function DevelopersScreen() {
3434
language === "all"
3535
? "followers:>1000"
3636
: `language:${language} followers:>100`;
37-
const { data, isLoading } = useSearch(q, "users");
37+
const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } =
38+
useSearch(q, "users");
39+
const users = data?.pages.flatMap(p => p as SearchUserItem[]) ?? [];
3840

3941
return (
4042
<View style={[styles.container, { backgroundColor: theme.background }]}>
@@ -67,8 +69,17 @@ export default function DevelopersScreen() {
6769
/>
6870
) : (
6971
<FlatList
70-
data={data as SearchUserItem[]}
72+
data={users}
7173
keyExtractor={item => String(item.id)}
74+
onEndReached={() => {
75+
if (hasNextPage && !isFetchingNextPage) fetchNextPage();
76+
}}
77+
onEndReachedThreshold={0.3}
78+
ListFooterComponent={
79+
isFetchingNextPage ? (
80+
<ActivityIndicator style={{ padding: 16 }} />
81+
) : null
82+
}
7283
renderItem={({ item }) => (
7384
<Pressable
7485
style={[styles.row, { borderBottomColor: theme.border }]}

src/app/(tabs)/explore/index.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,13 @@ export default function ExploreScreen() {
137137
const activeFiltersCount =
138138
(sortBy !== "best-match" ? 1 : 0) + (language ? 1 : 0) + (starsMin ? 1 : 0);
139139

140-
const { data, isLoading } = useSearch(effectiveQuery, "repositories", {
141-
sort: sortBy === "best-match" ? undefined : sortBy,
142-
order: "desc",
143-
});
140+
const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } =
141+
useSearch(effectiveQuery, "repositories", {
142+
sort: sortBy === "best-match" ? undefined : sortBy,
143+
order: "desc",
144+
});
145+
146+
const repos = data?.pages.flatMap(p => p as SearchRepoItem[]) ?? [];
144147

145148
useEffect(() => {
146149
if (incomingQuery) {
@@ -298,13 +301,23 @@ export default function ExploreScreen() {
298301
/>
299302
) : (
300303
<FlatList
301-
data={data as SearchRepoItem[]}
304+
data={repos}
302305
keyExtractor={item => String(item.id)}
303306
renderItem={({ item }) => <RepoRow item={item} />}
304307
contentContainerStyle={
305-
(data as SearchRepoItem[] | undefined)?.length === 0
306-
? styles.listEmptyContainer
307-
: undefined
308+
repos.length === 0 ? styles.listEmptyContainer : undefined
309+
}
310+
onEndReached={() => {
311+
if (hasNextPage && !isFetchingNextPage) fetchNextPage();
312+
}}
313+
onEndReachedThreshold={0.3}
314+
ListFooterComponent={
315+
isFetchingNextPage ? (
316+
<ActivityIndicator
317+
style={{ paddingVertical: 16 }}
318+
color={theme.primary}
319+
/>
320+
) : null
308321
}
309322
ListEmptyComponent={
310323
<View style={styles.emptyState}>

src/app/repo/[owner]/[repo]/index.tsx

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
ActivityIndicator,
3+
FlatList,
34
Linking,
45
Pressable,
56
ScrollView,
@@ -14,6 +15,7 @@ import {
1415
useRepoReadme,
1516
useRepoContents,
1617
useCreateFork,
18+
useBranches,
1719
} from "../../../../lib/api/hooks";
1820
import { LanguageDot } from "../../../../components/ui/LanguageDot";
1921
import { Markdown } from "../../../../components/ui/Markdown";
@@ -298,6 +300,99 @@ function CodeTab({ owner, repo }: { owner: string; repo: string }) {
298300
);
299301
}
300302

303+
function BranchesTab({ owner, repo }: { owner: string; repo: string }) {
304+
const theme = useAppTheme();
305+
const { data: repoData } = useRepo(owner, repo);
306+
const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } =
307+
useBranches(owner, repo);
308+
309+
const branches = data?.pages.flatMap(p => p) ?? [];
310+
const defaultBranch = repoData?.default_branch;
311+
312+
if (isLoading) {
313+
return (
314+
<ActivityIndicator
315+
style={{ flex: 1, padding: 32 }}
316+
color={theme.primary}
317+
/>
318+
);
319+
}
320+
321+
return (
322+
<FlatList
323+
data={branches}
324+
keyExtractor={item => item.name}
325+
contentContainerStyle={styles.tabContent}
326+
onEndReached={() => {
327+
if (hasNextPage && !isFetchingNextPage) fetchNextPage();
328+
}}
329+
onEndReachedThreshold={0.3}
330+
ListFooterComponent={
331+
isFetchingNextPage ? (
332+
<ActivityIndicator
333+
style={{ paddingVertical: 16 }}
334+
color={theme.primary}
335+
/>
336+
) : null
337+
}
338+
ListEmptyComponent={
339+
<View style={styles.emptyState}>
340+
<Ionicons
341+
name="git-branch-outline"
342+
size={32}
343+
color={theme.muted}
344+
/>
345+
<Text style={[styles.emptyTitle, { color: theme.text }]}>
346+
No branches
347+
</Text>
348+
</View>
349+
}
350+
renderItem={({ item }) => (
351+
<View
352+
style={[
353+
styles.branchRow,
354+
{ backgroundColor: theme.surface, borderColor: theme.border },
355+
]}
356+
>
357+
<Ionicons
358+
name="git-branch-outline"
359+
size={16}
360+
color={theme.muted}
361+
/>
362+
<Text
363+
style={[styles.branchName, { color: theme.text }]}
364+
numberOfLines={1}
365+
>
366+
{item.name}
367+
</Text>
368+
{item.name === defaultBranch && (
369+
<View
370+
style={[
371+
styles.branchBadge,
372+
{
373+
backgroundColor: theme.primary + "20",
374+
borderColor: theme.primary,
375+
},
376+
]}
377+
>
378+
<Text style={[styles.branchBadgeText, { color: theme.primary }]}>
379+
default
380+
</Text>
381+
</View>
382+
)}
383+
{item.protected && (
384+
<Ionicons
385+
name="lock-closed-outline"
386+
size={14}
387+
color={theme.muted}
388+
/>
389+
)}
390+
</View>
391+
)}
392+
/>
393+
);
394+
}
395+
301396
function ComingSoonTab({
302397
name,
303398
icon,
@@ -646,9 +741,9 @@ export default function RepoDetailScreen() {
646741
/>
647742
)}
648743
{activeTab === "Branches" && (
649-
<ComingSoonTab
650-
name="Branches"
651-
icon="git-branch-outline"
744+
<BranchesTab
745+
owner={owner!}
746+
repo={repo!}
652747
/>
653748
)}
654749
</ScrollView>
@@ -803,6 +898,29 @@ const styles = StyleSheet.create({
803898
fontWeight: "800",
804899
letterSpacing: 1,
805900
},
901+
branchRow: {
902+
flexDirection: "row",
903+
alignItems: "center",
904+
gap: 8,
905+
paddingVertical: 12,
906+
paddingHorizontal: 16,
907+
borderBottomWidth: StyleSheet.hairlineWidth,
908+
},
909+
branchName: {
910+
flex: 1,
911+
fontSize: 14,
912+
fontWeight: "500",
913+
},
914+
branchBadge: {
915+
paddingHorizontal: 7,
916+
paddingVertical: 2,
917+
borderRadius: 10,
918+
borderWidth: 1,
919+
},
920+
branchBadgeText: {
921+
fontSize: 11,
922+
fontWeight: "600",
923+
},
806924
breadcrumb: { marginBottom: 8 },
807925
breadcrumbContent: { flexDirection: "row", alignItems: "center" },
808926
breadcrumbItem: { flexDirection: "row", alignItems: "center" },

0 commit comments

Comments
 (0)