mirror of
https://github.com/tauri-apps/tauri-docs.git
synced 2026-01-31 00:35:16 +01:00
redo
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,3 +19,4 @@ pnpm-debug.log*
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
_openCollectiveData.json
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
interface Props {
|
||||
sponsor: Sponsor;
|
||||
}
|
||||
|
||||
export type Sponsor = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
profileUrl?: string;
|
||||
tier?: Tier;
|
||||
};
|
||||
|
||||
export type Tier = 'platinum' | 'gold' | 'silver' | 'bronze';
|
||||
|
||||
export const IMAGE_DIMENSION = 128;
|
||||
|
||||
const { sponsor } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
sponsor.tier == 'platinum' && (
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Image
|
||||
src={sponsor.avatarUrl}
|
||||
alt={sponsor.name}
|
||||
width={IMAGE_DIMENSION}
|
||||
height={IMAGE_DIMENSION}
|
||||
class="image"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
sponsor.tier == 'gold' && (
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Image
|
||||
src={sponsor.avatarUrl}
|
||||
alt={sponsor.name}
|
||||
width={IMAGE_DIMENSION}
|
||||
height={IMAGE_DIMENSION}
|
||||
class="image"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
sponsor.tier == 'silver' && (
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer">
|
||||
{sponsor.name}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
{sponsor.tier == 'bronze' && sponsor.name}
|
||||
|
||||
<style define:vars={{ dimension: `${IMAGE_DIMENSION}px` }}>
|
||||
.image {
|
||||
width: var(--dimension);
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -1,201 +0,0 @@
|
||||
---
|
||||
import { type Sponsor as SponsorType, IMAGE_DIMENSION, type Tier } from './Sponsor.astro';
|
||||
import Sponsor from './Sponsor.astro';
|
||||
const GITHUB_TOKEN = import.meta.env.GITHUB_TOKEN;
|
||||
const IS_PRODUCTION = import.meta.env.NETLIFY != undefined;
|
||||
|
||||
let gitHubSponsors: SponsorType[] = [];
|
||||
|
||||
if (GITHUB_TOKEN || IS_PRODUCTION) {
|
||||
gitHubSponsors = await getGitHubSponsors();
|
||||
}
|
||||
|
||||
const gitHubSponsorsLoaded = gitHubSponsors.length > 0;
|
||||
|
||||
const openCollectiveSponsors = await getOpenCollectiveSponsors();
|
||||
|
||||
async function getGitHubSponsors(): Promise<SponsorType[]> {
|
||||
if (!GITHUB_TOKEN)
|
||||
throw Error('Error generator sponsor list: GITHUB_TOKEN is invalid or not set');
|
||||
|
||||
// https://docs.github.com/graphql
|
||||
const gitHubQuery = `query {
|
||||
organization(login:"tauri-apps") {
|
||||
sponsors(first: 100) {
|
||||
nodes {
|
||||
... on Actor {
|
||||
login,
|
||||
avatarUrl(size: ${IMAGE_DIMENSION})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const gitHubSponsorResponse = await fetch('https://api.github.com/graphql', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: gitHubQuery }),
|
||||
headers: {
|
||||
Authorization: `bearer ${GITHUB_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!gitHubSponsorResponse.ok)
|
||||
throw Error(
|
||||
`There was an issue with the GitHub sponsors query: ${gitHubSponsorResponse.status}: ${gitHubSponsorResponse.statusText}`
|
||||
);
|
||||
|
||||
const gitHubSponsorData = (await gitHubSponsorResponse.json()).data;
|
||||
return gitHubSponsorData.organization.sponsors.nodes
|
||||
.map(
|
||||
(node: any): SponsorType => ({
|
||||
id: node.login,
|
||||
name: node.login,
|
||||
avatarUrl: node.avatarUrl,
|
||||
})
|
||||
)
|
||||
.sort((a: SponsorType, b: SponsorType) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
async function getOpenCollectiveSponsors(): Promise<SponsorType[]> {
|
||||
const filteredSlugs = ['github-sponsors'];
|
||||
|
||||
// Documentation at https://graphql-docs-v2.opencollective.com/welcome
|
||||
const openCollectiveQuery = `query account {
|
||||
collective(slug: "tauri") {
|
||||
contributors(limit: 1000) {
|
||||
nodes {
|
||||
name
|
||||
image(height: ${IMAGE_DIMENSION})
|
||||
totalAmountDonated
|
||||
collectiveSlug
|
||||
isIncognito
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const openCollectiveResponse = await fetch('https://api.opencollective.com/graphql/v2', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: openCollectiveQuery }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!openCollectiveResponse.ok)
|
||||
throw Error(
|
||||
`There was an issue with the Open Collective sponsors query: ${openCollectiveResponse.status} ${openCollectiveResponse.statusText}`
|
||||
);
|
||||
|
||||
const openCollectiveData = (await openCollectiveResponse.json()).data;
|
||||
return openCollectiveData.collective.contributors.nodes
|
||||
.filter(
|
||||
(node: any) =>
|
||||
!node.isIncognito &&
|
||||
node.totalAmountDonated > 0 &&
|
||||
!filteredSlugs.includes(node.collectiveSlug) &&
|
||||
node.name != 'Guest'
|
||||
)
|
||||
.sort((a: any, b: any) => b.totalAmountDonated - a.totalAmountDonated)
|
||||
.map((node: any): SponsorType => {
|
||||
let tier: Tier;
|
||||
let amount = node.totalAmountDonated / 100;
|
||||
if (amount >= 5_000) {
|
||||
tier = 'platinum';
|
||||
} else if (amount >= 500) {
|
||||
tier = 'gold';
|
||||
} else if (amount >= 100) {
|
||||
tier = 'silver';
|
||||
} else {
|
||||
tier = 'bronze';
|
||||
}
|
||||
return {
|
||||
name: node.name,
|
||||
id: node.name,
|
||||
avatarUrl: node.image,
|
||||
profileUrl: `https://opencollective.com/${node.collectiveSlug}`,
|
||||
tier,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
const openCollectiveSilverOpt1 = openCollectiveSponsors.filter(
|
||||
(sponsor) => sponsor.tier == 'silver'
|
||||
);
|
||||
const openCollectiveSilverOpt2 = openCollectiveSponsors
|
||||
.filter((sponsor) => sponsor.tier == 'silver')
|
||||
.map((sponsor) => sponsor.name)
|
||||
.join(', ');
|
||||
|
||||
const openCollectiveBronze = openCollectiveSponsors
|
||||
.filter((sponsor) => sponsor.tier == 'bronze')
|
||||
.map((sponsor) => sponsor.name)
|
||||
.join(', ');
|
||||
---
|
||||
|
||||
<h1 id="sponsors">Sponsors</h1>
|
||||
<h2>Open Collective</h2>
|
||||
<div class="sponsor-grid">
|
||||
{
|
||||
openCollectiveSponsors
|
||||
.filter((sponsor) => sponsor.tier == 'platinum')
|
||||
.map((sponsor) => <Sponsor {sponsor} />)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="sponsor-grid">
|
||||
{
|
||||
openCollectiveSponsors
|
||||
.filter((sponsor) => sponsor.tier == 'gold')
|
||||
.map((sponsor) => <Sponsor {sponsor} />)
|
||||
}
|
||||
</div>
|
||||
<!-- <div>
|
||||
{
|
||||
openCollectiveSilverOpt1.map((sponsor) => (
|
||||
<>
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer">
|
||||
{sponsor.name}
|
||||
</a>{' '}
|
||||
</>
|
||||
))
|
||||
}
|
||||
</div> -->
|
||||
<div>
|
||||
{openCollectiveSilverOpt2}
|
||||
</div>
|
||||
<!-- <div>
|
||||
{openCollectiveBronze}
|
||||
</div> -->
|
||||
<h2>GitHub</h2>
|
||||
{
|
||||
!gitHubSponsorsLoaded && (
|
||||
<p>
|
||||
<code>GITHUB_TOKEN</code> environment variable not set so GitHub sponsors could not be loaded.
|
||||
</p>
|
||||
)
|
||||
}
|
||||
{
|
||||
gitHubSponsorsLoaded && (
|
||||
<ul class="sponsor-grid">
|
||||
{gitHubSponsors.map((sponsor) => (
|
||||
<li>
|
||||
<Sponsor {sponsor} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
.sponsor-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0 1rem;
|
||||
}
|
||||
|
||||
.sponsor-grid > li {
|
||||
list-style: none;
|
||||
}
|
||||
</style>
|
||||
79
src/components/sponsors/GitHub/Main.astro
Normal file
79
src/components/sponsors/GitHub/Main.astro
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
const GITHUB_TOKEN = import.meta.env.GITHUB_TOKEN;
|
||||
|
||||
let gitHubSponsors = [];
|
||||
|
||||
export type Sponsor = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
profileUrl?: string;
|
||||
tier?: Tier;
|
||||
};
|
||||
|
||||
export const IMAGE_DIMENSION = 64;
|
||||
|
||||
const IS_PRODUCTION = import.meta.env.NETLIFY != undefined;
|
||||
|
||||
export type Tier = 'platinum' | 'gold' | 'silver' | 'bronze';
|
||||
|
||||
if (GITHUB_TOKEN || IS_PRODUCTION) {
|
||||
gitHubSponsors = await getGitHubSponsors();
|
||||
}
|
||||
|
||||
const gitHubSponsorsLoaded = gitHubSponsors.length > 0;
|
||||
|
||||
async function getGitHubSponsors(): Promise<any[]> {
|
||||
if (!GITHUB_TOKEN)
|
||||
throw Error('Error generator sponsor list: GITHUB_TOKEN is invalid or not set');
|
||||
|
||||
// https://docs.github.com/graphql
|
||||
const gitHubQuery = `query {
|
||||
organization(login:"tauri-apps") {
|
||||
sponsors(first: 100) {
|
||||
nodes {
|
||||
... on Actor {
|
||||
login,
|
||||
avatarUrl(size: ${IMAGE_DIMENSION})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const gitHubSponsorResponse = await fetch('https://api.github.com/graphql', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query: gitHubQuery }),
|
||||
headers: {
|
||||
Authorization: `bearer ${GITHUB_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!gitHubSponsorResponse.ok)
|
||||
throw Error(
|
||||
`There was an issue with the GitHub sponsors query: ${gitHubSponsorResponse.status}: ${gitHubSponsorResponse.statusText}`
|
||||
);
|
||||
|
||||
const gitHubSponsorData = (await gitHubSponsorResponse.json()).data;
|
||||
return gitHubSponsorData.organization.sponsors.nodes
|
||||
.map(
|
||||
(node: any): Sponsor => ({
|
||||
id: node.login,
|
||||
name: node.login,
|
||||
avatarUrl: node.avatarUrl,
|
||||
})
|
||||
)
|
||||
.sort((a: Sponsor, b: Sponsor) => a.name.localeCompare(b.name));
|
||||
}
|
||||
---
|
||||
|
||||
{!gitHubSponsorsLoaded && <p>_error_loading_</p>}
|
||||
{
|
||||
gitHubSponsorsLoaded && (
|
||||
<div class="sponsor-grid github">
|
||||
{gitHubSponsors.map((sponsor) => (
|
||||
<div class="sponsor" />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
122
src/components/sponsors/OpenCollective/Contributor.astro
Normal file
122
src/components/sponsors/OpenCollective/Contributor.astro
Normal file
@@ -0,0 +1,122 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
import { IMAGE_DIMENSION, type Sponsor } from './_types';
|
||||
|
||||
interface Props {
|
||||
sponsor: Sponsor;
|
||||
|
||||
needComma?: boolean;
|
||||
}
|
||||
|
||||
const { sponsor, needComma } = Astro.props;
|
||||
|
||||
const roundingStyle: Record<Sponsor['type'], string> = {
|
||||
ORGANIZATION: 'rounded-lg',
|
||||
INDIVIDUAL: 'rounded-full',
|
||||
};
|
||||
---
|
||||
|
||||
{
|
||||
(sponsor.tier === 'platinum' || sponsor.tier === 'gold' || sponsor.tier === 'silver') && (
|
||||
<div class="image-container">
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer">
|
||||
<Image
|
||||
src={sponsor.avatarUrl}
|
||||
alt={sponsor.name}
|
||||
width={IMAGE_DIMENSION}
|
||||
height={IMAGE_DIMENSION}
|
||||
class={`image ${sponsor.tier} ${roundingStyle[sponsor.type]}`}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!sponsor.tier && sponsor.avatarUrl && (
|
||||
<div class="image-container">
|
||||
fallback
|
||||
<a
|
||||
href={sponsor.profileUrl || `https://github.com/${sponsor.name}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
src={sponsor.avatarUrl}
|
||||
alt={sponsor.name}
|
||||
width={IMAGE_DIMENSION}
|
||||
height={IMAGE_DIMENSION}
|
||||
class={`image ${roundingStyle[sponsor.type]}`}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
sponsor.tier === 'bronze' && (
|
||||
<>
|
||||
<a href={sponsor.profileUrl} target="_blank" rel="noopener noreferrer" class="bronze-sponsor">
|
||||
{sponsor.name}
|
||||
</a>
|
||||
{needComma && <span class="bronze-separator">, </span>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<style define:vars={{ dimension: `${IMAGE_DIMENSION}px` }}>
|
||||
.rounded-full {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.rounded-lg {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.image {
|
||||
object-fit: cover;
|
||||
background-color: white;
|
||||
border: 2px solid var(--sl-color-gray-1);
|
||||
}
|
||||
|
||||
.image-container {
|
||||
aspect-ratio: 1;
|
||||
width: fit-content;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 1s ease;
|
||||
}
|
||||
|
||||
.image-container:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.platinum {
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
}
|
||||
|
||||
.gold {
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.silver {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
|
||||
.bronze-sponsor {
|
||||
filter: brightness(0.8);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.bronze-sponsor:hover {
|
||||
filter: brightness(1.2);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.bronze-separator {
|
||||
color: var(--sl-color-text-muted);
|
||||
padding-inline-end: 2px;
|
||||
}
|
||||
</style>
|
||||
113
src/components/sponsors/OpenCollective/Main.astro
Normal file
113
src/components/sponsors/OpenCollective/Main.astro
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import Contributor from './Contributor.astro';
|
||||
import {
|
||||
fetchOpenCollectiveData,
|
||||
GOLD_THRESHOLD,
|
||||
PLATINUM_THRESHOLD,
|
||||
SILVER_THRESHOLD,
|
||||
} from '@utils/fetchOpenCollectiveData';
|
||||
import type { Sponsor } from './_types';
|
||||
|
||||
const DATA_FILE = path.resolve('./src/components/sponsors/_openCollectiveData.json');
|
||||
|
||||
let openCollectiveSponsors = [];
|
||||
if (fs.existsSync(DATA_FILE)) {
|
||||
openCollectiveSponsors = JSON.parse(fs.readFileSync(DATA_FILE, 'utf-8'));
|
||||
} else {
|
||||
openCollectiveSponsors = await fetchOpenCollectiveData();
|
||||
fs.writeFileSync(DATA_FILE, JSON.stringify(openCollectiveSponsors, null, 2));
|
||||
}
|
||||
|
||||
const sponsorsByTier = {
|
||||
platinum: openCollectiveSponsors.filter((sponsor: Sponsor) => sponsor.tier === 'platinum'),
|
||||
gold: openCollectiveSponsors.filter((sponsor: Sponsor) => sponsor.tier === 'gold'),
|
||||
silver: openCollectiveSponsors.filter((sponsor: Sponsor) => sponsor.tier === 'silver'),
|
||||
bronze: openCollectiveSponsors.filter((sponsor: Sponsor) => sponsor.tier === 'bronze'),
|
||||
};
|
||||
|
||||
const platinumSponsors = sponsorsByTier.platinum;
|
||||
const goldSponsors = sponsorsByTier.gold;
|
||||
const silverSponsors = sponsorsByTier.silver;
|
||||
const bronzeSponsors = sponsorsByTier.bronze;
|
||||
---
|
||||
|
||||
{
|
||||
platinumSponsors.length > 0 && (
|
||||
<div class="tier-section">
|
||||
<h4>Platinum Sponsors (${PLATINUM_THRESHOLD.toLocaleString()}+)</h4>
|
||||
<div class="sponsor-container platinum">
|
||||
{platinumSponsors.map((sponsor) => (
|
||||
<Contributor {sponsor} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
goldSponsors.length > 0 && (
|
||||
<div class="tier-section">
|
||||
<h4>Gold Sponsors (${GOLD_THRESHOLD.toLocaleString()}+)</h4>
|
||||
<div class="sponsor-container gold">
|
||||
{goldSponsors.map((sponsor) => (
|
||||
<Contributor {sponsor} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
silverSponsors.length > 0 && (
|
||||
<div class="tier-section">
|
||||
<h4>Silver Sponsors (${SILVER_THRESHOLD.toLocaleString()}+)</h4>
|
||||
<div class="sponsor-container silver">
|
||||
{silverSponsors.map((sponsor) => (
|
||||
<Contributor {sponsor} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
bronzeSponsors.length > 0 && (
|
||||
<div class="tier-section">
|
||||
<h4>Bronze Sponsors</h4>
|
||||
<div class="sponsor-list">
|
||||
{bronzeSponsors.map((sponsor, index) => (
|
||||
<Contributor {sponsor} needComma={index < bronzeSponsors.length - 1} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
.sponsor-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sponsor-container.silver {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.sponsor-container.gold {
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.sponsor-container.platinum {
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tier-section {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: var(--sl-color-text-muted);
|
||||
}
|
||||
</style>
|
||||
11
src/components/sponsors/OpenCollective/_types.ts
Normal file
11
src/components/sponsors/OpenCollective/_types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export type Sponsor = {
|
||||
id: string;
|
||||
name: string;
|
||||
avatarUrl: string;
|
||||
profileUrl?: string;
|
||||
tier?: Tier;
|
||||
type: 'ORGANIZATION' | 'INDIVIDUAL';
|
||||
};
|
||||
|
||||
export type Tier = 'platinum' | 'gold' | 'silver' | 'bronze';
|
||||
export const IMAGE_DIMENSION = 256;
|
||||
62
src/components/sponsors/ServiceProviders/Main.astro
Normal file
62
src/components/sponsors/ServiceProviders/Main.astro
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
import { Image } from 'astro:assets';
|
||||
|
||||
interface ServiceProvider {
|
||||
name: string;
|
||||
image?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
const serviceProviders: ServiceProvider[] = [
|
||||
{
|
||||
name: 'Netlify',
|
||||
url: 'https://www.netlify.com/',
|
||||
},
|
||||
{
|
||||
name: 'Meilisearch',
|
||||
url: 'https://www.meilisearch.com/',
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{
|
||||
serviceProviders.length > 0 && (
|
||||
<div class="service-providers">
|
||||
{serviceProviders.map((provider) => (
|
||||
<div class="service-provider not-content">
|
||||
<a href={provider.url} target="_blank" rel="noopener noreferrer">
|
||||
{provider.name}
|
||||
</a>
|
||||
{provider.image && <Image src={provider.image} inferSize alt={provider.name} />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
.service-providers {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.service-provider {
|
||||
padding: 1rem;
|
||||
border: 4px solid var(--sl-color-gray-5);
|
||||
border-radius: 8px;
|
||||
background: var(--sl-color-bg-nav);
|
||||
}
|
||||
|
||||
.service-provider a {
|
||||
font-weight: 600;
|
||||
color: var(--sl-color-text);
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.service-provider a:hover {
|
||||
color: var(--sl-color-accent);
|
||||
}
|
||||
</style>
|
||||
48
src/components/sponsors/SponsorList.astro
Normal file
48
src/components/sponsors/SponsorList.astro
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
import OpenCollective from './OpenCollective/Main.astro';
|
||||
import GitHub from './GitHub/Main.astro';
|
||||
import ServiceProvider from './ServiceProviders/Main.astro';
|
||||
|
||||
// const codeContributors = [
|
||||
// // todo: Populate this list with actual code contributors, pull top 10 for each repo?
|
||||
// ];
|
||||
---
|
||||
|
||||
<div class="not-content">
|
||||
<h2>Sponsors</h2>
|
||||
|
||||
<section class="sponsors-section">
|
||||
<div class="funding-source">
|
||||
<h4>GitHub</h4>
|
||||
<GitHub />
|
||||
</div>
|
||||
|
||||
<div class="funding-source">
|
||||
<h4>Open Collective</h4>
|
||||
<OpenCollective />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="sponsors-section">
|
||||
<h4>Partners</h4>
|
||||
<!-- todo CrabNebula -->
|
||||
<h4>Services</h4>
|
||||
<ServiceProvider />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* todo: add styles for headings h2, h3, h4 */
|
||||
h4 {
|
||||
/* border 50% opacity */
|
||||
border-bottom: 1px solid var(--sl-color-gray-4);
|
||||
}
|
||||
|
||||
.sponsors-section {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.funding-source {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,8 @@ hero:
|
||||
|
||||
import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components';
|
||||
import Cta from '@fragments/cta.mdx';
|
||||
import SponsorList from '@components/sponsors/SponsorList.astro';
|
||||
|
||||
|
||||
<div class="hero-bg">
|
||||
<div class="bg-logo"></div>
|
||||
@@ -67,6 +69,7 @@ import Cta from '@fragments/cta.mdx';
|
||||
</Card>
|
||||
</CardGrid> */}
|
||||
|
||||
import SponsorList from '@components/SponsorList.astro';
|
||||
|
||||
<SponsorList />
|
||||
|
||||
|
||||
|
||||
84
src/utils/fetchOpenCollectiveData.ts
Normal file
84
src/utils/fetchOpenCollectiveData.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
IMAGE_DIMENSION,
|
||||
type Sponsor,
|
||||
type Tier,
|
||||
} from '@components/sponsors/OpenCollective/_types';
|
||||
|
||||
export const PLATINUM_THRESHOLD = 5_000;
|
||||
export const GOLD_THRESHOLD = 500;
|
||||
export const SILVER_THRESHOLD = 100;
|
||||
|
||||
export async function fetchOpenCollectiveData() {
|
||||
const filteredSlugs = ['github-sponsors'];
|
||||
|
||||
// Documentation at https://graphql-docs-v2.opencollective.com/welcome
|
||||
const query = `query account {
|
||||
collective(slug: "tauri") {
|
||||
contributors(limit: 1000) {
|
||||
nodes {
|
||||
account {
|
||||
name
|
||||
type
|
||||
imageUrl(height: ${IMAGE_DIMENSION})
|
||||
slug
|
||||
isIncognito
|
||||
}
|
||||
totalAmountContributed {
|
||||
value
|
||||
currency
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const res = await fetch('https://api.opencollective.com/graphql/v2', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ query }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw Error(
|
||||
`Open Collective query failed: ${res.status} ${res.statusText} \n ${JSON.stringify(await res.json(), null, 2)}, `
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: handle currency
|
||||
|
||||
const openCollectiveData = (await res.json()).data;
|
||||
return openCollectiveData.collective.contributors.nodes
|
||||
.filter(
|
||||
(node: any) =>
|
||||
!node.account.isIncognito &&
|
||||
node.totalAmountContributed.value > 0 &&
|
||||
!filteredSlugs.includes(node.account.slug) &&
|
||||
node.account.name != 'Guest'
|
||||
)
|
||||
.sort((a: any, b: any) => b.totalAmountContributed.value - a.totalAmountContributed.value)
|
||||
.map((node: any): Sponsor => {
|
||||
let tier: Tier;
|
||||
let amount = node.totalAmountContributed.value;
|
||||
if (amount >= PLATINUM_THRESHOLD) {
|
||||
tier = 'platinum';
|
||||
} else if (amount >= GOLD_THRESHOLD) {
|
||||
tier = 'gold';
|
||||
} else if (amount >= SILVER_THRESHOLD) {
|
||||
tier = 'silver';
|
||||
} else {
|
||||
tier = 'bronze';
|
||||
}
|
||||
const { slug, name, type, isIncognito, imageUrl } = node.account;
|
||||
|
||||
return {
|
||||
name,
|
||||
id: name,
|
||||
avatarUrl: imageUrl,
|
||||
profileUrl: `https://opencollective.com/${slug}`,
|
||||
tier,
|
||||
type,
|
||||
};
|
||||
});
|
||||
}
|
||||
46
tauri-docs.code-workspace
Normal file
46
tauri-docs.code-workspace
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".",
|
||||
"name": "docs"
|
||||
},
|
||||
{
|
||||
"path": "packages",
|
||||
"name": "packages",
|
||||
"folders": [
|
||||
{
|
||||
"path": "js-api-generator",
|
||||
},
|
||||
{
|
||||
"path": "config-generator",
|
||||
},
|
||||
{
|
||||
"path": "cli-generator",
|
||||
},
|
||||
{
|
||||
"path": "releases-generator",
|
||||
},
|
||||
{
|
||||
"path": "compatibility-table",
|
||||
}
|
||||
]
|
||||
},
|
||||
// todo: fix paths so that we can see docs, packages and submodules
|
||||
{
|
||||
"path": "packages",
|
||||
"name": "submodules",
|
||||
"folders": [
|
||||
{
|
||||
"path": "awesome-tauri",
|
||||
},
|
||||
{
|
||||
"path": "tauri",
|
||||
},
|
||||
{
|
||||
"path": "plugins-workspace",
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
@@ -5,7 +5,8 @@
|
||||
"paths": {
|
||||
"@components/*": ["src/components/*"],
|
||||
"@assets/*": ["src/assets/*"],
|
||||
"@fragments/*": ["src/content/docs/_fragments/*"]
|
||||
"@fragments/*": ["src/content/docs/_fragments/*"],
|
||||
"@utils/*": ["src/utils/*"]
|
||||
}
|
||||
},
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
|
||||
Reference in New Issue
Block a user