feat: add github stats

This commit is contained in:
thomas
2024-03-28 09:52:18 +01:00
parent 00ff67f93b
commit bb4d1b14a0
8 changed files with 163 additions and 105 deletions

View File

@@ -1,15 +0,0 @@
---
const { challengeNumber } = Astro.props;
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:"${challengeNumber}"+label:"answer"&per_page=1`);
const { total_count } = await response.json();
---
{total_count ? <div class="answer-text">Answered by {total_count} people</div> : null }
<style>
.answer-text {
font-size: var(--sl-text-xs);
color: var(--sl-color-gray-3);
width: max-content;
}
</style>

View File

@@ -2,6 +2,7 @@
import VideoButton from './VideoButton.astro';
import ClipboardCopy from './ClipboardCopy.astro';
import { getEntry } from 'astro:content';
import AnsweredUser from './github/AnsweredUser.svelte';
const { lang } = Astro.props;
const { author, challengeNumber, title, blogLink, videoLink, command } = Astro.props.entry.data;
@@ -11,27 +12,6 @@ const authorLink = `https://github.com/tomalaforge/angular-challenges/pulls?q=la
const communityLink = `https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A${challengeNumber}+label%3Aanswer+sort%3Areactions-%2B1-desc`;
const npxCommand = `npx nx serve ${command}`;
let items = [];
let page = 1;
let error = false;
try {
while (true) {
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:"${challengeNumber}"+label:"answer"&per_page=100&page=${page}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const { items: new_items, total_count } = await response.json();
if (!new_items || new_items.length === 0) break;
items.push(...new_items);
if(total_count < page * 100) break;
page++;
}
} catch (e) {
error = true;
}
---
<div class="separator"></div>
@@ -71,7 +51,7 @@ while (true) {
<div class="article-footer">
<a
href={communityLink}>
{items.length > 0 ? items.length : ''} {data['challenge.footer.communityAnswers']}*
❖ {data['challenge.footer.communityAnswers']}*
</a>
<a
href={authorLink}>
@@ -92,20 +72,7 @@ while (true) {
<VideoButton {...videoLink} {...Astro.props} />}
</div>
{error ? null :
<div class="solution-container">
<div>Answered by</div>
{(items ?? []).map((item) => (
<img
loading="lazy"
src={item.user.avatar_url}
width="30"
height="30"
alt=""
class="avatar"
/>
))}
</div>}
<AnsweredUser client:load {challengeNumber} />
<div class="footer-note">
* {data['challenge.footer.upvoteAnswer']}
@@ -131,16 +98,5 @@ while (true) {
margin-top: 3rem;
}
.solution-container {
margin-top: 3rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
img {
border-radius: 50%;
}
}
</style>

View File

@@ -6,7 +6,7 @@ import Author from './Author.astro';
import ChallengeFooter from './ChallengeFooter.astro';
import CommentSection from './CommentSection.astro';
import ContributorsFooter from './ContributorsFooter.astro';
import AnswerNumber from './AnswerNumber.astro';
import AnswerNumber from './github/AnswerNumber.svelte';
const { lang } = Astro.props;
const { data } = await getEntry('i18n', lang);
@@ -18,7 +18,7 @@ const renderCommentSection = !Astro.props.entry.data.noCommentSection;
{ challengeNumber ? <div class="header-container">
{ author ? <Author {...author.data} {data} /> : null}
<AnswerNumber {challengeNumber}/>
<AnswerNumber client:load/>
</div> :null}
<Default {...Astro.props}>

View File

@@ -0,0 +1,62 @@
<script>
import MyIcon from './MyIcon.astro';
import { onMount } from 'svelte';
let error = false;
let loading = true;
let stargazersCount = 0;
let forksCount = 0;
async function fetchStats() {
try {
const response = await fetch(`https://api.github.com/repos/tomalaforge/angular-challenges`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const { stargazers_count, forks } = await response.json();
stargazersCount = stargazers_count;
forksCount = forks;
} catch (e) {
error = true;
} finally {
loading = false;
}
}
onMount(() => {
fetchStats();
});
</script>
{#if !error && !loading}
<div class="github">
<a class="category" href="https://github.com/tomalaforge/angular-challenges">
<MyIcon name="star" />
<div>{stargazersCount}</div>
</a>
<div class="category fork">
<MyIcon name="fork" viewBox="0 0 16 16" />
<div>{forksCount}</div>
</div>
</div>
{/if}
<style>
.github {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: var(--sl-nav-gap)
}
.category {
display: flex;
align-items: center;
font-size: 12px;
gap: 0.25rem;
}
</style>

View File

@@ -1,17 +1,6 @@
---
import { getEntry } from 'astro:content';
import type { Props } from '@astrojs/starlight/props';
import Default from '@astrojs/starlight/components/SiteTitle.astro';
import MyIcon from './MyIcon.astro';
const { challengeNumber } = Astro.props.entry.data;
const { lang } = Astro.props;
const { data } = await getEntry('i18n', lang);
const response = await fetch(`https://api.github.com/repos/tomalaforge/angular-challenges`);
const { stargazers_count, forks } = await response.json();
import GitHubStats from './GitHubStats.svelte';
---
@@ -19,34 +8,6 @@ const { stargazers_count, forks } = await response.json();
<slot />
</Default>
<div class="github">
<a class="category" href="https://github.com/tomalaforge/angular-challenges">
<MyIcon name="star" />
<div>{stargazers_count}</div>
</a>
<GitHubStats client:load />
<div class="category fork">
<MyIcon name="fork" viewBox="0 0 16 16" />
<div>{forks}</div>
</div>
</div>
<style>
.github {
display: flex;
flex-direction: column;
align-items: flex-start;
margin-left: var(--sl-nav-gap)
}
.category {
display: flex;
align-items: center;
font-size: 12px;
gap: 0.25rem;
}
.fork {
//margin-top: -5px;
}
</style>

View File

@@ -0,0 +1,15 @@
<script>
import { isLoaded, totalCount } from './github-store';
</script>
{#if isLoaded}
<div class="answer-text">Answered by {$totalCount} people</div>
{/if}
<style>
.answer-text {
font-size: var(--sl-text-xs);
color: var(--sl-color-gray-3);
width: max-content;
}
</style>

View File

@@ -0,0 +1,66 @@
<script>
import { onMount } from 'svelte';
import { data, error, isLoaded, isLoading, totalCount } from './github-store';
export let challengeNumber;
let page = 1;
async function fetchTotalCount() {
isLoading.set(true);
try {
while (true) {
const response = await fetch(`https://api.github.com/search/issues?q=repo:tomalaforge/angular-challenges+is:pr+label:"${challengeNumber}"+label:"answer"&per_page=100&page=${page}`);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const { items: new_items, total_count } = await response.json();
if (!new_items || new_items.length === 0) break;
data.update(items => [...items, ...new_items]);
totalCount.set(total_count);
if(total_count < page * 100) break;
page++;
}
} catch (e) {
error.set(true);
} finally {
isLoading.set(false);
}
}
onMount(() => {
fetchTotalCount();
});
</script>
{#if $isLoaded}
<div class="solution-container">
<div>Answered by</div>
{#each $data as { user }}
<img
loading="lazy"
src={user.avatar_url}
width="30"
height="30"
alt=""
class="avatar"
/>
{/each}
</div>
{/if}
<style>
.solution-container {
margin-top: 3rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.avatar {
border-radius: 50%;
}
</style>

View File

@@ -0,0 +1,13 @@
import { derived, writable } from 'svelte/store';
export const count = writable(0);
export const isLoading = writable(true);
export const error = writable(false);
export const data = writable<any[]>([]);
export const totalCount = writable(0);
export const isLoaded = derived(
[isLoading, error],
([$isLoading, $error]) => !$isLoading && !$error,
);