Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
27552ad
Merge pull request #270 from alph-land/testing
xbabyx Jul 10, 2026
73ce727
Merge pull request #271 from alph-land/testing
xbabyx Jul 10, 2026
3bf687c
chore: add AlphScoundrel logo
alph-land-bot Jul 10, 2026
7327536
chore: add AlphScoundrel banner
alph-land-bot Jul 10, 2026
e7c9720
chore: add AlphScoundrel preview
alph-land-bot Jul 10, 2026
b0f227e
feat: add dApp AlphScoundrel
alph-land-bot Jul 10, 2026
d37e859
Merge pull request #272 from alph-land/submit/dapp-alphscoundrel-1783…
xbabyx Jul 10, 2026
3700bd7
chore: update dApp name placeholder example in submit form
YYBer Jul 13, 2026
73a7225
fix: upgrade npm CLI and Node version in publish workflow for OIDC su…
YYBer Jul 13, 2026
8875d7c
chore: allow manual trigger for publish-npm-package workflow
YYBer Jul 13, 2026
a9dba84
fix: bump Node version to 22 to support latest npm CLI
YYBer Jul 13, 2026
0f10138
fix: return 404 instead of blank page for unknown dapp routes
YYBer Jul 13, 2026
d69e52b
chore: add ALPHtris logo
alph-land-bot Jul 16, 2026
4ffc589
chore: add ALPHtris banner
alph-land-bot Jul 16, 2026
7decbc2
chore: add ALPHtris preview
alph-land-bot Jul 16, 2026
991bb9e
feat: add dApp ALPHtris
alph-land-bot Jul 16, 2026
3bdc4af
Merge pull request #273 from alph-land/submit/dapp-alphtris-178423098…
xbabyx Jul 17, 2026
2f901b4
fix: persist tiered reward settings when editing a bounty
YYBer Jul 20, 2026
5481553
fix: redirect god users to sponsor dashboard instead of landing page
YYBer Jul 20, 2026
bb8ef39
updated preview image for alphscoundrel
diomark Jul 20, 2026
72e9a1f
fix: add missing auth checks across bounty write endpoints, reject un…
YYBer Jul 21, 2026
40c9a45
fix: require god-user session for all /api/admin/* endpoints
YYBer Jul 27, 2026
0a673d5
test: consolidate tests under tests/, add admin auth coverage, run on…
YYBer Jul 27, 2026
ebbef20
Merge pull request #274 from diomark/develop
xbabyx Jul 27, 2026
640556d
feat: denominate bounty rewards in ALPH or USD, with a daily rate
YYBer Jul 27, 2026
82a893a
fix: stop a sponsor claiming another sponsor's profile URL
YYBer Jul 27, 2026
488ff52
refactor: route sponsors by a stored slug instead of matching two col…
YYBer Jul 27, 2026
2cf4fb8
fix: reject a transaction hash that already paid another submission
YYBer Jul 27, 2026
6e1401d
fix: create notifications server-side and lock down the endpoint
YYBer Jul 27, 2026
a334bf5
feat: store the winner placement and payout in columns
YYBer Jul 27, 2026
d4a7065
fix: do not record a transaction hash on a rejected submission
YYBer Jul 27, 2026
076ed70
fix: align the submission types with what the API returns
YYBer Jul 27, 2026
fadfb04
test: check the inlined submission columns against the schema dump
YYBer Jul 27, 2026
14dc9da
refactor: share one submission title reader
YYBer Jul 27, 2026
829afd9
fix: fetch the ALPH price from a chain of keyless sources
YYBer Jul 28, 2026
a63b315
feat: deduplicate and let users unsubscribe from email
YYBer Jul 28, 2026
2cb8822
feat: separate a bounty's draft and announced states from its status
YYBer Jul 28, 2026
24ae614
feat: give bounties a readable slug alongside the uuid
YYBer Jul 28, 2026
e49ad26
fix: authorize the sponsor dashboard endpoint and add a publish action
YYBer Jul 28, 2026
3792a98
feat: remind on deadlines and gate entry to bounties
YYBer Jul 28, 2026
1abd25a
fix: email sponsors on every account status change, not just approval
YYBer Jul 28, 2026
529f440
refactor: delete dead types and columns, and close a mute bypass
YYBer Jul 28, 2026
37d5ff1
refactor: drop the dead sponsor username column and two pre-025 compa…
YYBer Aug 4, 2026
9800139
chore: add Alephium Market Cap Ranking logo
alph-land-bot Aug 24, 2026
b715ca8
chore: add Alephium Market Cap Ranking banner
alph-land-bot Aug 24, 2026
fb6165c
chore: add Alephium Market Cap Ranking preview
alph-land-bot Aug 24, 2026
a128916
feat: add dApp Alephium Market Cap Ranking
alph-land-bot Aug 24, 2026
f8e4867
Update alephium-market-cap-ranking.json
xbabyx Sep 7, 2026
84e1c30
Merge pull request #275 from alph-land/submit/dapp-alephium-market-ca…
xbabyx Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/publish-npm-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
paths:
- "data/**"
- "packages/alphland-dapps/**"
workflow_dispatch:

jobs:
publish:
Expand All @@ -24,9 +25,12 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "18"
node-version: "22"
registry-url: "https://registry.npmjs.org"

- name: Update npm
run: npm install -g npm@latest

- name: Check if package version changed
id: version-check
run: |
Expand Down
38 changes: 38 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh

# Load nvm
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# Skip the test suite when the push only touches dApp content
# (data/*.json entries and public/dapps/** images) — e.g. a teammate
# submitting or removing a dApp listing, with no code changes.
zero="0000000000000000000000000000000000000000"
run_tests=0

while read -r local_ref local_sha remote_ref remote_sha; do
[ -z "$local_sha" ] && continue
[ "$local_sha" = "$zero" ] && continue # deleting a branch, nothing to check

if [ "$remote_sha" = "$zero" ]; then
base=$(git merge-base "$local_sha" origin/develop 2>/dev/null)
[ -z "$base" ] && base="$remote_sha"
else
base="$remote_sha"
fi

changed=$(git diff --name-only "$base" "$local_sha" 2>/dev/null)
[ -z "$changed" ] && continue

non_dapp_changes=$(printf '%s\n' "$changed" | grep -vE '^(data/[^/]+\.json|public/dapps/.*)$')
if [ -n "$non_dapp_changes" ]; then
run_tests=1
fi
done

if [ "$run_tests" = "0" ]; then
echo "pre-push: only dApp content changed (data/*.json, public/dapps/**) — skipping test suite"
exit 0
fi

npm run test:ci
3 changes: 3 additions & 0 deletions .lintstagedrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
],
"**/*.+(ts|tsx|js|jsx|css|json)": [
"prettier --write"
],
"src/features/bounty/**/*.+(ts|tsx)": [
"vitest related --run"
]
}
38 changes: 38 additions & 0 deletions data/alephium-market-cap-ranking.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "Alephium Market Cap Ranking",
"description": "Alephium Market Cap Ranking is an analytics website that tracks Alephium’s market capitalization ranking over time. It visualizes historical ranking data to help users follow how Alephium’s position changes in the broader crypto market.",
"short_description": "Track Alephium market cap ranking over time",
"tags": [
"Analytics"
],
"verified": false,
"dotw": false,
"councils_choice": false,
"links": {
"website": "https://spikeblack-alph.github.io/Alephium-MC-overtime/",
"careers": "",
"twitter": "",
"telegram": "https://t.me/Spike_Black",
"discord": "",
"docs": "",
"github": "https://github.com/SpikeBlack-Alph/Alephium-MC-overtime",
"youtube": "",
"medium": "",
"mirror": "",
"linkedin": ""
},
"twitterName": "",
"teamInfo": {
"contactEmail": "spikeblackalephium@gmail.com",
"founded": "2026-08-24T00:00:00.000Z",
"anonymous": false
},
"media": {
"logoUrl": "/dapps/alephium-market-cap-ranking/alephium-market-cap-ranking-logo.webp",
"bannerUrl": "/dapps/alephium-market-cap-ranking/alephium-market-cap-ranking-banner.webp",
"previewUrl": "/dapps/alephium-market-cap-ranking/alephium-market-cap-ranking-preview.webp"
},
"contracts": [],
"audits": [],
"tokens": []
}
38 changes: 38 additions & 0 deletions data/alphscoundrel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "AlphScoundrel",
"description": "AlphScoundrel is a deterministic dungeon card game built on Alephium. Players enter with 24 HP and work through a 44-card deck, fighting monsters, equipping weapons, banking potions, and maximizing a progress-based score. Guest practice runs stay local, while wallet-signed runs are transcript-verified before leaderboard submission; paid ALPH entries can follow a prize-eligible on-chain path.",
"short_description": "Scoundrel dungeon card game on Alephium",
"tags": [
"Games"
],
"verified": false,
"dotw": false,
"councils_choice": false,
"links": {
"website": "https://alphscoundrel.alehpium.org/",
"careers": "",
"twitter": "",
"telegram": "",
"discord": "",
"docs": "",
"github": "",
"youtube": "",
"medium": "",
"mirror": "",
"linkedin": ""
},
"twitterName": "",
"teamInfo": {
"contactEmail": "",
"founded": "",
"anonymous": true
},
"media": {
"logoUrl": "/dapps/alphscoundrel/alphscoundrel-logo.webp",
"bannerUrl": "/dapps/alphscoundrel/alphscoundrel-banner.webp",
"previewUrl": "/dapps/alphscoundrel/alphscoundrel-preview.webp"
},
"contracts": [],
"audits": [],
"tokens": []
}
38 changes: 38 additions & 0 deletions data/alphtris.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "ALPHtris",
"description": "ALPHtris is a neon block-stacking arcade game on Alephium. Players connect an Alephium wallet, pay an ALPH entry fee, play a run, and submit scores on chain to compete on current-round, global, and recent leaderboards. It's Tetris. On Alephium.",
"short_description": "on-chain Alphtris (Tetris)",
"tags": [
"Games"
],
"verified": false,
"dotw": false,
"councils_choice": false,
"links": {
"website": "https://alphtris.alehpium.org/",
"careers": "",
"twitter": "",
"telegram": "",
"discord": "",
"docs": "",
"github": "",
"youtube": "",
"medium": "",
"mirror": "",
"linkedin": ""
},
"twitterName": "",
"teamInfo": {
"contactEmail": "",
"founded": "",
"anonymous": true
},
"media": {
"logoUrl": "/dapps/alphtris/alphtris-logo.webp",
"bannerUrl": "/dapps/alphtris/alphtris-banner.webp",
"previewUrl": "/dapps/alphtris/alphtris-preview.webp"
},
"contracts": [],
"audits": [],
"tokens": []
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/dapps/alphtris/alphtris-banner.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/dapps/alphtris/alphtris-logo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/dapps/alphtris/alphtris-preview.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/submit/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ <h1>Submit Your dApp to Alphland</h1>

<div class="field" id="field-name">
<label>dApp Name <span class="required">*</span></label>
<input type="text" id="name" placeholder="e.g. Ayin DEX" maxlength="100" />
<input type="text" id="name" placeholder="e.g. Powfi" maxlength="100" />
<div class="field-error">dApp name is required</div>
</div>

Expand Down
3 changes: 2 additions & 1 deletion src/features/bounty/components/BookmarksSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface BookmarkItem {
status: string;
end_date: string;
sponsor_name: string;
sponsor_slug?: string;
sponsor_logo_url: string | null;
}

Expand Down Expand Up @@ -208,7 +209,7 @@ export function BookmarksSection({
by{" "}
{bookmark.sponsor_id && bookmark.sponsor_name ? (
<Link
href={`/bounty/sponsor/${sponsorSlug(bookmark.sponsor_name)}`}
href={`/bounty/sponsor/${bookmark.sponsor_slug || sponsorSlug(bookmark.sponsor_name)}`}
>
<a
className="hover:text-orange transition-colors"
Expand Down
7 changes: 6 additions & 1 deletion src/features/bounty/components/BountyCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ interface BountyCardProps {
logo?: string;
title: string;
company: string;
/** Stored routing key. Falls back to the name only for rows predating it. */
companySlug?: string;
reward: string;
tags: string[];
sponsorVerified?: boolean;
Expand Down Expand Up @@ -54,6 +56,7 @@ export function BountyCard({
logo,
title,
company,
companySlug,
reward,
tags,
sponsorVerified = false,
Expand Down Expand Up @@ -92,7 +95,9 @@ export function BountyCard({
</h3>
<p className="text-sm text-light-charcoal dark:text-lightgrey mb-3 flex items-center gap-1">
{sponsorId && company ? (
<Link href={`/bounty/sponsor/${sponsorSlug(company)}`}>
<Link
href={`/bounty/sponsor/${companySlug || sponsorSlug(company)}`}
>
<a
className="hover:text-orange transition-colors"
onClick={(e) => e.stopPropagation()}
Expand Down
51 changes: 3 additions & 48 deletions src/features/bounty/components/CommentSection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"use client";

import { notificationService } from "../services/notificationService";
import { apiClient, BountyComment } from "@/lib/api-client";
import { containsProfanity } from "@/lib/profanity-filter";
import Link from "next/link";
Expand Down Expand Up @@ -115,12 +114,6 @@ function SingleComment({

// Notify comment owner (if not liking own comment)
if (comment.user_id !== currentUserId) {
await notificationService.notifyCommentLike(
comment.user_id,
bountyId,
bountyTitle,
currentUsername,
);
}
}
} catch (error) {
Expand Down Expand Up @@ -392,47 +385,9 @@ export function CommentSection({
parent_comment_id: replyingTo || undefined,
});

// Send notifications
if (replyingTo) {
// Find the parent comment to get its author
const findComment = (
comments: CommentWithReplies[],
id: string,
): CommentWithReplies | null => {
for (const c of comments) {
if (c.id === id) return c;
if (c.replies) {
const found = findComment(c.replies, id);
if (found) return found;
}
}
return null;
};
const parentComment = findComment(comments, replyingTo);
if (parentComment && parentComment.user_id !== currentUserId) {
await notificationService.notifyCommentReply(
parentComment.user_id,
bountyId,
bountyTitle,
currentUsername,
);
}
} else if (sponsorUserId && sponsorUserId !== currentUserId) {
// Notify sponsor of new comment (if not muted)
const shouldNotify = await notificationService.shouldNotify(
sponsorUserId,
bountyId,
"comments",
);
if (shouldNotify) {
await notificationService.notifyNewComment(
sponsorUserId,
bountyId,
bountyTitle,
currentUsername,
);
}
}
// Notifications are written by the worker when the comment is created --
// it knows the parent author and the sponsor, and it honours the bounty
// mute for replies too, which this path never did.

setNewComment("");
setReplyingTo(null);
Expand Down
Loading
Loading