1
1
const cont = document . getElementById ( 'contributor' ) ;
2
+ let currentPage = 1 ; // Start from page 1
3
+ let isLoading = false ; // Flag to track loading state
4
+ let hasMore = true ; // Flag to track if there's more data to load
5
+
6
+ // Loading spinner element
7
+ const loadingSpinner = document . getElementById ( 'loading-spinner' ) ;
8
+
9
+ // Error message element
10
+ const errorMessage = document . getElementById ( 'error-message' ) ;
11
+
12
+ // Intersection Observer for infinite scroll
13
+ const observerOptions = {
14
+ root : null ,
15
+ rootMargin : '0px' ,
16
+ threshold : 0.1
17
+ } ;
18
+
19
+ // Use when one page is loaded, and then scroll down to load more
20
+ const intersectionObserver = new IntersectionObserver ( ( entries ) => {
21
+ entries . forEach ( entry => {
22
+ if ( entry . isIntersecting && ! isLoading && hasMore ) {
23
+ // If intersecting, not in loading state, and has more data to load
24
+ fetchContributors ( currentPage ) ;
25
+ }
26
+ } ) ;
27
+ } , observerOptions ) ;
28
+
29
+ // Lazy loading observer for avatars
30
+ const lazyLoadObserver = new IntersectionObserver ( ( entries ) => {
31
+ entries . forEach ( entry => {
32
+ if ( entry . isIntersecting ) {
33
+ const img = entry . target ;
34
+ img . src = img . dataset . src ;
35
+ img . classList . remove ( 'lazy' ) ;
36
+ lazyLoadObserver . unobserve ( img ) ;
37
+ }
38
+ } ) ;
39
+ } , observerOptions ) ;
2
40
41
+ // Loads a single page of contributors
3
42
async function fetchContributors ( pageNumber ) {
4
- const perPage = 100 ;
5
- const apiUrl = '/.netlify/functions/contributors' ; // Netlify serverless function path
43
+ if ( isLoading ) return ;
6
44
7
- const response = await fetch ( `${ apiUrl } ?page=${ pageNumber } &per_page=${ perPage } ` ) ;
8
-
9
- if ( ! response . ok ) {
10
- throw new Error ( `Failed to fetch the contributors data. Status code: ${ response . status } ` ) ;
11
- }
45
+ isLoading = true ;
46
+ const perPage = 20 ; // Number of items per page
47
+ const apiUrl = '/.netlify/functions/contributors' ;
12
48
13
- const contributorsData = await response . json ( ) ;
14
- return contributorsData ;
15
- }
49
+ try {
50
+ // Show loading spinner at the bottom
51
+ loadingSpinner . style . display = 'block' ;
16
52
17
- async function fetchAllContributors ( ) {
18
- let allContributors = [ ] ;
19
- let pageNumber = 1 ;
20
- const maxPages = 10 ; // Limiting the number of pages to avoid overload (can be adjusted)
53
+ const response = await fetch ( `${ apiUrl } ?page=${ pageNumber } &per_page=${ perPage } ` ) ;
21
54
22
- try {
23
- // Fetch all contributors in parallel using Promise.all()
24
- const fetchPromises = [ ] ;
55
+ if ( ! response . ok ) {
56
+ throw new Error ( `Failed to fetch contributors. Status: ${ response . status } ` ) ;
57
+ }
58
+
59
+ const contributorsData = await response . json ( ) ;
25
60
26
- // Fetch data for multiple pages concurrently
27
- for ( let i = 1 ; i <= maxPages ; i ++ ) {
28
- fetchPromises . push ( fetchContributors ( i ) ) ;
61
+ // Check if we have more data to load
62
+ hasMore = contributorsData . length === perPage ;
63
+
64
+ // Create and append contributor cards
65
+ await displayContributors ( contributorsData ) ;
66
+
67
+ currentPage ++ ;
68
+ } catch ( error ) {
69
+ errorMessage . style . display = 'block' ;
70
+ console . error ( 'Error fetching contributors:' , error ) ;
71
+ } finally {
72
+ isLoading = false ;
73
+ loadingSpinner . style . display = 'none' ;
74
+
75
+ // Add observer to the last card for infinite scroll
76
+ const allCards = cont . querySelectorAll ( '.contributor-card' ) ;
77
+ if ( allCards . length > 0 ) {
78
+ intersectionObserver . observe ( allCards [ allCards . length - 1 ] ) ;
29
79
}
80
+ }
81
+ }
30
82
31
- const contributorsArray = await Promise . all ( fetchPromises ) ;
83
+ // Displays the contributors on the page
84
+ async function displayContributors ( contributors ) {
85
+ const fragment = document . createDocumentFragment ( ) ;
32
86
33
- // Combine all the results
34
- contributorsArray . forEach ( contributorsData => {
35
- allContributors = allContributors . concat ( contributorsData ) ;
36
- } ) ;
87
+ for ( const contributor of contributors ) {
88
+ if ( contributor . login === 'Rakesh9100' ) continue ; // Skip owner
37
89
38
- // Display contributor cards
39
- allContributors . forEach ( ( contributor ) => {
40
- if ( contributor . login === 'Rakesh9100' ) return ; // Skip owner
90
+ const contributorCard = document . createElement ( 'div' ) ;
91
+ contributorCard . classList . add ( 'contributor-card' ) ;
41
92
42
- const contributorCard = document . createElement ( 'div' ) ;
43
- contributorCard . classList . add ( 'contributor-card' ) ;
93
+ // Create avatar with lazy loading
94
+ const avatarImg = document . createElement ( 'img' ) ;
95
+ avatarImg . classList . add ( 'lazy' ) ;
96
+ avatarImg . src = '../images/avatar.svg' ; // Add a placeholder image
97
+ avatarImg . dataset . src = contributor . avatar_url ;
98
+ avatarImg . alt = `${ contributor . login } 's Picture` ;
44
99
45
- const avatarImg = document . createElement ( 'img' ) ;
46
- avatarImg . src = contributor . avatar_url ;
47
- avatarImg . alt = `${ contributor . login } 's Picture` ;
100
+ const loginLink = document . createElement ( 'a' ) ;
101
+ loginLink . href = contributor . html_url ;
102
+ loginLink . target = '_blank' ;
103
+ loginLink . appendChild ( avatarImg ) ;
48
104
49
- const loginLink = document . createElement ( 'a' ) ;
50
- loginLink . href = contributor . html_url ;
51
- loginLink . target = '_blank' ;
52
- loginLink . appendChild ( avatarImg ) ;
105
+ const displayName = contributor . login ;
53
106
54
- // Fetch detailed info for the name
55
- fetch ( contributor . url )
56
- . then ( contributorDetails => contributorDetails . json ( ) )
57
- . then ( contributorData => {
58
- const displayName = contributorData . name || contributor . login ;
107
+ const nameDiv = document . createElement ( 'div' ) ;
108
+ nameDiv . classList . add ( 'contributor-name' ) ;
109
+ nameDiv . textContent = displayName ;
59
110
60
- const nameDiv = document . createElement ( 'div' ) ;
61
- nameDiv . classList . add ( 'contributor-name ' ) ;
62
- nameDiv . textContent = displayName ;
111
+ const contributionsCountBubbleDiv = document . createElement ( 'div' ) ;
112
+ contributionsCountBubbleDiv . classList . add ( 'contributions-count-bubble ' ) ;
113
+ contributionsCountBubbleDiv . textContent = contributor . contributions ;
63
114
64
- contributorCard . appendChild ( loginLink ) ;
65
- contributorCard . appendChild ( nameDiv ) ;
115
+ contributorCard . appendChild ( loginLink ) ;
116
+ contributorCard . appendChild ( nameDiv ) ;
117
+ contributorCard . appendChild ( contributionsCountBubbleDiv ) ;
118
+ fragment . appendChild ( contributorCard ) ;
66
119
67
- cont . appendChild ( contributorCard ) ;
68
- } )
69
- . catch ( error => console . error ( 'Error fetching the contributor details:' , error ) ) ;
70
- } ) ;
71
- } catch ( error ) {
72
- console . error ( 'Error fetching the contributors:' , error ) ;
120
+ // Observe the image for lazy loading
121
+ lazyLoadObserver . observe ( avatarImg ) ;
73
122
}
123
+
124
+ cont . appendChild ( fragment ) ;
74
125
}
75
126
76
- fetchAllContributors ( ) ;
127
+ // Initial load
128
+ fetchContributors ( currentPage ) ;
0 commit comments