mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-13 06:13:03 -05:00
feat: add github stats
This commit is contained in:
@@ -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>
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import VideoButton from './VideoButton.astro';
|
import VideoButton from './VideoButton.astro';
|
||||||
import ClipboardCopy from './ClipboardCopy.astro';
|
import ClipboardCopy from './ClipboardCopy.astro';
|
||||||
import { getEntry } from 'astro:content';
|
import { getEntry } from 'astro:content';
|
||||||
|
import AnsweredUser from './github/AnsweredUser.svelte';
|
||||||
|
|
||||||
const { lang } = Astro.props;
|
const { lang } = Astro.props;
|
||||||
const { author, challengeNumber, title, blogLink, videoLink, command } = Astro.props.entry.data;
|
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 communityLink = `https://github.com/tomalaforge/angular-challenges/pulls?q=label%3A${challengeNumber}+label%3Aanswer+sort%3Areactions-%2B1-desc`;
|
||||||
const npxCommand = `npx nx serve ${command}`;
|
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>
|
<div class="separator"></div>
|
||||||
@@ -71,7 +51,7 @@ while (true) {
|
|||||||
<div class="article-footer">
|
<div class="article-footer">
|
||||||
<a
|
<a
|
||||||
href={communityLink}>
|
href={communityLink}>
|
||||||
❖ {items.length > 0 ? items.length : ''} {data['challenge.footer.communityAnswers']}*
|
❖ {data['challenge.footer.communityAnswers']}*
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
href={authorLink}>
|
href={authorLink}>
|
||||||
@@ -92,20 +72,7 @@ while (true) {
|
|||||||
<VideoButton {...videoLink} {...Astro.props} />}
|
<VideoButton {...videoLink} {...Astro.props} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error ? null :
|
<AnsweredUser client:load {challengeNumber} />
|
||||||
<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>}
|
|
||||||
|
|
||||||
<div class="footer-note">
|
<div class="footer-note">
|
||||||
* {data['challenge.footer.upvoteAnswer']}
|
* {data['challenge.footer.upvoteAnswer']}
|
||||||
@@ -131,16 +98,5 @@ while (true) {
|
|||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.solution-container {
|
|
||||||
margin-top: 3rem;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.5rem;
|
|
||||||
|
|
||||||
img {
|
|
||||||
border-radius: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Author from './Author.astro';
|
|||||||
import ChallengeFooter from './ChallengeFooter.astro';
|
import ChallengeFooter from './ChallengeFooter.astro';
|
||||||
import CommentSection from './CommentSection.astro';
|
import CommentSection from './CommentSection.astro';
|
||||||
import ContributorsFooter from './ContributorsFooter.astro';
|
import ContributorsFooter from './ContributorsFooter.astro';
|
||||||
import AnswerNumber from './AnswerNumber.astro';
|
import AnswerNumber from './github/AnswerNumber.svelte';
|
||||||
|
|
||||||
const { lang } = Astro.props;
|
const { lang } = Astro.props;
|
||||||
const { data } = await getEntry('i18n', lang);
|
const { data } = await getEntry('i18n', lang);
|
||||||
@@ -18,7 +18,7 @@ const renderCommentSection = !Astro.props.entry.data.noCommentSection;
|
|||||||
|
|
||||||
{ challengeNumber ? <div class="header-container">
|
{ challengeNumber ? <div class="header-container">
|
||||||
{ author ? <Author {...author.data} {data} /> : null}
|
{ author ? <Author {...author.data} {data} /> : null}
|
||||||
<AnswerNumber {challengeNumber}/>
|
<AnswerNumber client:load/>
|
||||||
</div> :null}
|
</div> :null}
|
||||||
|
|
||||||
<Default {...Astro.props}>
|
<Default {...Astro.props}>
|
||||||
|
|||||||
62
docs/src/components/GitHubStats.svelte
Normal file
62
docs/src/components/GitHubStats.svelte
Normal 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>
|
||||||
@@ -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 Default from '@astrojs/starlight/components/SiteTitle.astro';
|
||||||
import MyIcon from './MyIcon.astro';
|
import GitHubStats from './GitHubStats.svelte';
|
||||||
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();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -19,34 +8,6 @@ const { stargazers_count, forks } = await response.json();
|
|||||||
<slot />
|
<slot />
|
||||||
</Default>
|
</Default>
|
||||||
|
|
||||||
<div class="github">
|
<GitHubStats client:load />
|
||||||
<a class="category" href="https://github.com/tomalaforge/angular-challenges">
|
|
||||||
<MyIcon name="star" />
|
|
||||||
<div>{stargazers_count}</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
|
|||||||
15
docs/src/components/github/AnswerNumber.svelte
Normal file
15
docs/src/components/github/AnswerNumber.svelte
Normal 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>
|
||||||
66
docs/src/components/github/AnsweredUser.svelte
Normal file
66
docs/src/components/github/AnsweredUser.svelte
Normal 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>
|
||||||
13
docs/src/components/github/github-store.ts
Normal file
13
docs/src/components/github/github-store.ts
Normal 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,
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user