diff --git a/.github/workflows/create-discussions.yml b/.github/workflows/create-discussions.yml new file mode 100644 index 0000000..a30a890 --- /dev/null +++ b/.github/workflows/create-discussions.yml @@ -0,0 +1,108 @@ +name: Create GitHub Discussions + +on: + pull_request: + types: [opened] + paths: + - 'src/content/apps/**' + # Allow manual trigger to create discussions for existing apps + workflow_dispatch: + inputs: + createForExisting: + description: 'Create discussions for existing apps' + required: true + default: 'true' + type: boolean + +jobs: + create-discussion: + runs-on: ubuntu-latest + permissions: + discussions: write + pull-requests: read + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Create Discussion for PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + + // Get the changed files from the PR + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number + }); + + // Find new app submissions + const appFiles = files.filter(file => + file.status === 'added' && + file.filename.startsWith('src/content/apps/') && + !file.filename.endsWith('_template.md') + ); + + for (const file of appFiles) { + const content = Buffer.from((await github.rest.repos.getContent({ + owner: context.repo.owner, + repo: context.repo.repo, + path: file.filename, + ref: context.payload.pull_request.head.sha + })).data.content, 'base64').toString(); + + await createDiscussion(content); + } + + - name: Create Discussions for Existing Apps + if: github.event_name == 'workflow_dispatch' && inputs.createForExisting + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const path = require('path'); + const yaml = require('js-yaml'); + + // Get all existing app files + const appsDir = 'src/content/apps'; + const files = fs.readdirSync(appsDir) + .filter(file => file.endsWith('.md') && file !== '_template.md'); + + for (const file of files) { + const content = fs.readFileSync(path.join(appsDir, file), 'utf8'); + await createDiscussion(content); + } + + async function createDiscussion(content) { + // Parse the frontmatter + const frontmatter = content.split('---')[1]; + const appData = yaml.load(frontmatter); + + // Check if discussion already exists + const { data: discussions } = await github.rest.discussions.list({ + owner: context.repo.owner, + repo: context.repo.repo, + category_id: process.env.DISCUSSION_CATEGORY_ID + }); + + const existingDiscussion = discussions.find(d => + d.title === `Vote: ${appData.name} by ${appData.author}` + ); + + if (!existingDiscussion) { + // Create a discussion + await github.rest.discussions.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Vote: ${appData.name} by ${appData.author}`, + body: `🗳️ **Vote for this app by giving it a 👍 reaction!**\n\n${appData.description}`, + category_id: process.env.DISCUSSION_CATEGORY_ID + }); + console.log(`Created discussion for ${appData.name}`); + } else { + console.log(`Discussion already exists for ${appData.name}`); + } + } diff --git a/.github/workflows/update-votes.yml b/.github/workflows/update-votes.yml new file mode 100644 index 0000000..f78539c --- /dev/null +++ b/.github/workflows/update-votes.yml @@ -0,0 +1,59 @@ +name: Update Vote Counts + +on: + schedule: + - cron: '0 */6 * * *' # Run every 6 hours + workflow_dispatch: # Allow manual triggers + +jobs: + update-votes: + runs-on: ubuntu-latest + permissions: + contents: write + discussions: read + steps: + - uses: actions/checkout@v4 + + - name: Update Vote Counts + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs').promises; + const path = require('path'); + const yaml = require('js-yaml'); + + // Get all discussions in the Flutter of the Year category + const discussions = await github.paginate(github.rest.discussions.listForRepo, { + owner: context.repo.owner, + repo: context.repo.repo, + category_id: process.env.DISCUSSION_CATEGORY_ID + }); + + // Create votes.json with the current vote counts + const votes = {}; + for (const discussion of discussions) { + if (discussion.title.startsWith('Vote: ')) { + const appName = discussion.title.replace('Vote: ', '').split(' by ')[0]; + // Count thumbs up reactions + const reactions = await github.rest.reactions.listForDiscussion({ + owner: context.repo.owner, + repo: context.repo.repo, + discussion_number: discussion.number + }); + votes[appName] = reactions.data.filter(r => r.content === '+1').length; + } + } + + // Write the votes to a JSON file + await fs.writeFile( + 'src/data/votes.json', + JSON.stringify(votes, null, 2) + ); + + // Commit and push the changes + const date = new Date().toISOString(); + await exec.exec('git', ['config', 'user.name', 'github-actions[bot]']); + await exec.exec('git', ['config', 'user.email', 'github-actions[bot]@users.noreply.github.com']); + await exec.exec('git', ['add', 'src/data/votes.json']); + await exec.exec('git', ['commit', '-m', `Update vote counts - ${date}`]); + await exec.exec('git', ['push']); diff --git a/src/data/votes.json b/src/data/votes.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/data/votes.json @@ -0,0 +1 @@ +{} diff --git a/src/pages/index.astro b/src/pages/index.astro index dd9ee21..852718b 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -2,9 +2,18 @@ import { getCollection } from 'astro:content'; import { Image } from 'astro:assets'; import Layout from '../layouts/Layout.astro'; +import { getVotes, type VoteData } from '../utils/votes'; const currentYear = new Date().getFullYear(); const allApps = await getCollection('apps'); +const votes: VoteData = getVotes(); + +// Sort apps by vote count (descending) +const sortedApps = allApps.sort((a, b) => { + const votesA = votes[a.data.name] || 0; + const votesB = votes[b.data.name] || 0; + return votesB - votesA; +}); --- @@ -51,7 +60,7 @@ const allApps = await getCollection('apps');