├── apps
├── web-app
│ ├── .prettierrc
│ ├── src
│ │ ├── vite-env.d.ts
│ │ ├── routes
│ │ │ ├── __root.tsx
│ │ │ ├── app
│ │ │ │ ├── index.tsx
│ │ │ │ ├── explore.tsx
│ │ │ │ └── profile.tsx
│ │ │ ├── status
│ │ │ │ └── success.tsx
│ │ │ └── index.tsx
│ │ ├── user
│ │ │ ├── user.service.ts
│ │ │ ├── user.controller.ts
│ │ │ └── user.module.ts
│ │ ├── components
│ │ │ ├── pages
│ │ │ │ ├── settings
│ │ │ │ │ └── update-profile.tsx
│ │ │ │ ├── homepage.tsx
│ │ │ │ └── explore
│ │ │ │ │ ├── potatoe-users.tsx
│ │ │ │ │ └── general-users.tsx
│ │ │ ├── ui
│ │ │ │ ├── skeleton.tsx
│ │ │ │ ├── sonner.tsx
│ │ │ │ ├── input.tsx
│ │ │ │ ├── avatar.tsx
│ │ │ │ ├── badge.tsx
│ │ │ │ ├── card.tsx
│ │ │ │ ├── tabs.tsx
│ │ │ │ ├── tooltip.tsx
│ │ │ │ ├── slider.tsx
│ │ │ │ ├── button.tsx
│ │ │ │ └── table.tsx
│ │ │ ├── misc
│ │ │ │ ├── ProductHuntCard.tsx
│ │ │ │ └── product-hunt-badge.tsx
│ │ │ ├── fallbacks
│ │ │ │ ├── noDataFound.tsx
│ │ │ │ └── wallet-no-connect.tsx
│ │ │ ├── wallet
│ │ │ │ └── walletProfilePanel..tsx
│ │ │ ├── profile
│ │ │ │ ├── TipBadge.tsx
│ │ │ │ ├── profilePanel.tsx
│ │ │ │ └── addressAndBadge.tsx
│ │ │ ├── popups
│ │ │ │ ├── drawer
│ │ │ │ │ └── index.tsx
│ │ │ │ └── modals
│ │ │ │ │ └── index.tsx
│ │ │ ├── github
│ │ │ │ ├── GithubUserCardSkeleton.tsx
│ │ │ │ └── GithubUserCard.tsx
│ │ │ ├── search
│ │ │ │ └── GithubUserSearch.tsx
│ │ │ ├── typography
│ │ │ │ └── index.tsx
│ │ │ └── views
│ │ │ │ └── settings-drawer-view.tsx
│ │ ├── services
│ │ │ ├── index.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── user.service.ts
│ │ │ ├── wallet.service.ts
│ │ │ └── transaction.service.ts
│ │ ├── lib
│ │ │ ├── utils.ts
│ │ │ └── supabase.ts
│ │ ├── data
│ │ │ └── dashboardData.ts
│ │ ├── enums
│ │ │ └── API_ENUM.ts
│ │ ├── util
│ │ │ ├── extract-wallet-from-user.ts
│ │ │ ├── content-utils.ts
│ │ │ └── api.ts
│ │ ├── pages
│ │ │ ├── embed
│ │ │ │ └── tip-badge.tsx
│ │ │ ├── main
│ │ │ │ └── index.tsx
│ │ │ ├── explore
│ │ │ │ └── ExploreUsers.tsx
│ │ │ ├── status
│ │ │ │ └── success-page.tsx
│ │ │ ├── profile
│ │ │ │ └── userProfileCard.tsx
│ │ │ └── usersPage.tsx
│ │ ├── main.tsx
│ │ ├── App.tsx
│ │ ├── interface
│ │ │ └── users.interface.ts
│ │ ├── constant
│ │ │ └── index.ts
│ │ ├── providers
│ │ │ ├── indexProvider.tsx
│ │ │ └── walletConnectProvider.tsx
│ │ ├── hooks
│ │ │ ├── useProfile.ts
│ │ │ ├── connect-wallet.ts
│ │ │ ├── connect-wallet.tsx
│ │ │ ├── extract-user-wallet.ts
│ │ │ ├── useSolanaTip.ts
│ │ │ └── useAuth.ts
│ │ ├── configs
│ │ │ └── axios.ts
│ │ ├── sections
│ │ │ ├── auth.tsx
│ │ │ └── hero.tsx
│ │ ├── header
│ │ │ ├── dashboardHeader.tsx
│ │ │ └── nav.tsx
│ │ ├── button
│ │ │ ├── connectWalletButton.tsx
│ │ │ ├── auth.tsx
│ │ │ └── index.tsx
│ │ ├── store
│ │ │ ├── use-fetch-github-data.store.ts
│ │ │ └── user.store.ts
│ │ ├── misc
│ │ │ └── github_users_card.tsx
│ │ ├── dashboard
│ │ │ └── dashboardBottomTab.tsx
│ │ └── layouts
│ │ │ └── dashboard.tsx
│ ├── vercel.json
│ ├── public
│ │ ├── logo
│ │ │ └── logo.png
│ │ ├── favicon_io
│ │ │ ├── favicon.ico
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ ├── about.txt
│ │ │ └── site.webmanifest
│ │ ├── asset
│ │ │ └── sounds
│ │ │ │ └── magic_sound.mp3
│ │ └── vite.svg
│ ├── dev-dist
│ │ └── registerSW.js
│ ├── tsconfig.json
│ ├── .gitignore
│ ├── components.json
│ ├── tsconfig.node.json
│ ├── eslint.config.js
│ ├── tsconfig.app.json
│ ├── store
│ │ └── user.ts
│ ├── vite.config.ts
│ ├── index.html
│ ├── README.md
│ └── package.json
├── extension
│ ├── src
│ │ ├── vite-env.d.ts
│ │ ├── constants
│ │ │ └── index.ts
│ │ ├── images
│ │ │ ├── 128.png
│ │ │ ├── 16.png
│ │ │ └── 48.png
│ │ ├── interface
│ │ │ └── index.ts
│ │ ├── popup.tsx
│ │ ├── popup.html
│ │ ├── components
│ │ │ └── ui
│ │ │ │ └── button.tsx
│ │ ├── pages
│ │ │ ├── welcome.tsx
│ │ │ └── index.tsx
│ │ ├── content.tsx
│ │ └── manifest.json
│ ├── public
│ │ ├── icon
│ │ │ ├── 16.png
│ │ │ ├── 32.png
│ │ │ ├── 48.png
│ │ │ ├── 96.png
│ │ │ └── 128.png
│ │ └── icon-with-shadow.svg
│ ├── postcss.config.js
│ ├── tsconfig.node.json
│ ├── libs
│ │ └── supabase.ts
│ ├── .gitignore
│ ├── tailwind.config.js
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── package.json
├── server
│ ├── .dockerignore
│ ├── bun.lockb
│ ├── drizzle
│ │ ├── 0001_motionless_loki.sql
│ │ ├── 0003_past_kitty_pryde.sql
│ │ ├── 0002_easy_shen.sql
│ │ ├── meta
│ │ │ └── _journal.json
│ │ └── 0000_kind_gamora.sql
│ ├── tsconfig.json
│ ├── .nixpacks.toml
│ ├── src
│ │ ├── db
│ │ │ ├── index.ts
│ │ │ └── schema.ts
│ │ ├── utils
│ │ │ ├── content-format.ts
│ │ │ └── telegram-notification.ts
│ │ ├── types
│ │ │ └── env.d.ts
│ │ ├── types.ts
│ │ ├── enums
│ │ │ └── index.ts
│ │ ├── constants
│ │ │ └── index.ts
│ │ ├── config
│ │ │ └── telegraf.ts
│ │ ├── middleware
│ │ │ └── auth.ts
│ │ ├── routes
│ │ │ ├── user.ts
│ │ │ ├── wallets.ts
│ │ │ └── tx-records.ts
│ │ └── index.ts
│ ├── README.md
│ ├── .prettierrc
│ ├── drizzle.config.ts
│ ├── Dockerfile
│ ├── .gitignore
│ ├── scripts
│ │ └── push-db.ts
│ ├── fly.toml
│ └── package.json
└── github-bot
│ ├── .gitignore
│ ├── .dockerignore
│ ├── Dockerfile
│ ├── src
│ ├── controllers
│ │ ├── index.ts
│ │ ├── bounty.controller.ts
│ │ ├── issues.controller.ts
│ │ └── commands.controller.ts
│ ├── enums
│ │ └── commands.ts
│ └── index.ts
│ ├── vitest.config.ts
│ ├── .env.example
│ ├── test
│ ├── fixtures
│ │ ├── issues.opened.json
│ │ └── mock-cert.pem
│ └── index.test.ts
│ ├── tsconfig.json
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ └── CONTRIBUTING.md
├── .DS_Store
├── libs
├── common
│ ├── .travis.yml
│ ├── src
│ │ ├── .eslintrc
│ │ ├── index.test.tsx
│ │ ├── styles.module.css
│ │ ├── index.tsx
│ │ └── typings.d.ts
│ ├── .eslintignore
│ ├── example
│ │ ├── src
│ │ │ ├── react-app-env.d.ts
│ │ │ ├── index.tsx
│ │ │ ├── App.tsx
│ │ │ ├── setupTests.ts
│ │ │ ├── App.test.tsx
│ │ │ └── index.css
│ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── manifest.json
│ │ │ └── index.html
│ │ ├── README.md
│ │ ├── tsconfig.json
│ │ └── package.json
│ ├── tsconfig.test.json
│ ├── .editorconfig
│ ├── .prettierrc
│ ├── .gitignore
│ ├── tsconfig.json
│ ├── README.md
│ ├── .eslintrc
│ └── package.json
└── supabase
│ └── index.ts
├── pnpm-workspace.yaml
├── constants
└── index.ts
├── program
├── programs
│ └── program
│ │ ├── Xargo.toml
│ │ ├── src
│ │ └── lib.rs
│ │ └── Cargo.toml
├── .gitignore
├── .prettierignore
├── tsconfig.json
├── Cargo.toml
├── Anchor.toml
├── migrations
│ └── deploy.ts
├── package.json
└── tests
│ └── program.ts
├── .gitignore
├── .prettierrc
├── enums
└── index.ts
├── interface
└── index.interface.ts
├── .github
├── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── custom-issue-template-📝.md
│ ├── feature-request-✨.md
│ └── bug-report-🐞.md
└── workflows
│ └── greetings.yml
├── .idea
├── .gitignore
├── modules.xml
├── vcs.xml
├── potato-monorepo.iml
└── dataSources.xml
├── config
└── axios.ts
├── webpack.config.js
├── README.md
├── package.json
└── LICENSE
/apps/web-app/.prettierrc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web-app/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/apps/extension/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/.DS_Store
--------------------------------------------------------------------------------
/apps/extension/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export const GITHUB_HTML_IDENTIFIER = "";
2 |
--------------------------------------------------------------------------------
/libs/common/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 12
4 | - 10
5 |
--------------------------------------------------------------------------------
/libs/common/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/libs/common/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | node_modules/
4 | .snapshots/
5 | *.min.js
--------------------------------------------------------------------------------
/libs/common/example/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 | - "libs/*"
4 | - "packages/*"
5 |
--------------------------------------------------------------------------------
/constants/index.ts:
--------------------------------------------------------------------------------
1 | export const TOKEN = "";
2 | export const AUTH_TOKEN = "access_token";
3 |
--------------------------------------------------------------------------------
/apps/server/.dockerignore:
--------------------------------------------------------------------------------
1 | fly.toml
2 | Dockerfile
3 | .dockerignore
4 | node_modules
5 | .git
6 |
--------------------------------------------------------------------------------
/apps/server/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/server/bun.lockb
--------------------------------------------------------------------------------
/apps/web-app/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "routes": [{ "src": "/[^.]+", "dest": "/", "status": 200 }]
3 | }
4 |
--------------------------------------------------------------------------------
/apps/server/drizzle/0001_motionless_loki.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "wallets" DROP CONSTRAINT "wallets_address_unique";
--------------------------------------------------------------------------------
/program/programs/program/Xargo.toml:
--------------------------------------------------------------------------------
1 | [target.bpfel-unknown-unknown.dependencies.std]
2 | features = []
3 |
--------------------------------------------------------------------------------
/program/.gitignore:
--------------------------------------------------------------------------------
1 | .anchor
2 | .DS_Store
3 | target
4 | **/*.rs.bk
5 | node_modules
6 | test-ledger
7 | .yarn
8 |
--------------------------------------------------------------------------------
/program/.prettierignore:
--------------------------------------------------------------------------------
1 | .anchor
2 | .DS_Store
3 | target
4 | node_modules
5 | dist
6 | build
7 | test-ledger
8 |
--------------------------------------------------------------------------------
/apps/server/drizzle/0003_past_kitty_pryde.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "transaction_records" ALTER COLUMN "tx_hash" DROP NOT NULL;
--------------------------------------------------------------------------------
/apps/github-bot/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | *.pem
4 | !mock-cert.pem
5 | .env
6 | coverage
7 | lib
8 |
--------------------------------------------------------------------------------
/apps/extension/public/icon/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/public/icon/16.png
--------------------------------------------------------------------------------
/apps/extension/public/icon/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/public/icon/32.png
--------------------------------------------------------------------------------
/apps/extension/public/icon/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/public/icon/48.png
--------------------------------------------------------------------------------
/apps/extension/public/icon/96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/public/icon/96.png
--------------------------------------------------------------------------------
/apps/extension/src/images/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/src/images/128.png
--------------------------------------------------------------------------------
/apps/extension/src/images/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/src/images/16.png
--------------------------------------------------------------------------------
/apps/extension/src/images/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/src/images/48.png
--------------------------------------------------------------------------------
/apps/web-app/public/logo/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/logo/logo.png
--------------------------------------------------------------------------------
/apps/extension/public/icon/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/extension/public/icon/128.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | /node_modules
3 | apps/extension/node_modules
4 | apps/*/node_modules
5 | apps/*/.env
6 | .env
7 | */.env
8 |
--------------------------------------------------------------------------------
/apps/extension/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/libs/common/example/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/libs/common/example/public/favicon.ico
--------------------------------------------------------------------------------
/apps/web-app/src/routes/__root.tsx:
--------------------------------------------------------------------------------
1 | import { createRootRoute } from "@tanstack/react-router";
2 | export const Route = createRootRoute();
3 |
--------------------------------------------------------------------------------
/apps/web-app/src/user/user.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 |
3 | @Injectable()
4 | export class UserService {}
5 |
--------------------------------------------------------------------------------
/libs/common/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "printWidth": 80,
4 | "arrowParens": "always",
5 | "semi": true,
6 | "bracketSpacing": true
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/favicon.ico
--------------------------------------------------------------------------------
/apps/web-app/src/user/user.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller } from "@nestjs/common";
2 |
3 | @Controller("user")
4 | export class UserController {}
5 |
--------------------------------------------------------------------------------
/apps/web-app/public/asset/sounds/magic_sound.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/asset/sounds/magic_sound.mp3
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/favicon-16x16.png
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/favicon-32x32.png
--------------------------------------------------------------------------------
/apps/server/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "jsx": "react-jsx",
5 | "jsxImportSource": "hono/jsx"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/apple-touch-icon.png
--------------------------------------------------------------------------------
/apps/web-app/src/components/pages/settings/update-profile.tsx:
--------------------------------------------------------------------------------
1 | const UpdateProfile = () => {
2 | return <>profile>;
3 | };
4 | export default UpdateProfile;
5 |
--------------------------------------------------------------------------------
/enums/index.ts:
--------------------------------------------------------------------------------
1 | import { AUTH_TOKEN } from "../constants";
2 |
3 | export enum Secrete_Enums {}
4 |
5 | export const TOKEN_ENUMS = {
6 | AUTH_TOKEN,
7 | };
8 |
--------------------------------------------------------------------------------
/apps/server/.nixpacks.toml:
--------------------------------------------------------------------------------
1 | [phases.setup]
2 | nixPkgs = ["bun"]
3 |
4 | [phases.build]
5 | cmds = ["bun install"]
6 |
7 | [start]
8 | cmd = "bun run src/index.ts"
9 |
--------------------------------------------------------------------------------
/apps/web-app/src/services/index.ts:
--------------------------------------------------------------------------------
1 | import { AuthService } from "./auth.service";
2 | import UserService from "./user.service";
3 | export { AuthService, UserService };
4 |
--------------------------------------------------------------------------------
/interface/index.interface.ts:
--------------------------------------------------------------------------------
1 | export interface IGithubUserData {
2 | provider_id: string;
3 | user_name: string;
4 | email: string;
5 | avatar_url: string;
6 | }
7 |
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/android-chrome-192x192.png
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yhoungdev/potatoe-squeezy/HEAD/apps/web-app/public/favicon_io/android-chrome-512x512.png
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
--------------------------------------------------------------------------------
/apps/server/src/db/index.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import { drizzle } from 'drizzle-orm/node-postgres';
3 |
4 | const db = drizzle(process.env.DATABASE_URL!);
5 | export { db };
6 |
--------------------------------------------------------------------------------
/apps/server/src/utils/content-format.ts:
--------------------------------------------------------------------------------
1 | const escapeMarkdown = (text: string) => {
2 | return text.replace(/[_*[\]()~`>#+\-=|{}.!]/g, '\\$&');
3 | };
4 |
5 | export { escapeMarkdown };
6 |
--------------------------------------------------------------------------------
/apps/web-app/dev-dist/registerSW.js:
--------------------------------------------------------------------------------
1 | if ("serviceWorker" in navigator)
2 | navigator.serviceWorker.register("/dev-sw.js?dev-sw", {
3 | scope: "/",
4 | type: "classic",
5 | });
6 |
--------------------------------------------------------------------------------
/apps/server/README.md:
--------------------------------------------------------------------------------
1 | To install dependencies:
2 |
3 | ```sh
4 | bun install
5 | ```
6 |
7 | To run:
8 |
9 | ```sh
10 | bun run dev
11 | ```
12 |
13 | open http://localhost:3000
14 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/pages/homepage.tsx:
--------------------------------------------------------------------------------
1 | import AuthComponent from "../auth";
2 |
3 | const Homepage = () => {
4 | return ;
5 | };
6 |
7 | export default Homepage;
8 |
--------------------------------------------------------------------------------
/apps/server/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "bracketSameLine": false,
4 | "bracketSpacing": true,
5 | "printWidth": 80,
6 | "tabWidth": 2,
7 | "singleQuote": true
8 | }
9 |
--------------------------------------------------------------------------------
/apps/server/src/types/env.d.ts:
--------------------------------------------------------------------------------
1 | type Bindings = {
2 | GITHUB_CLIENT_ID: string;
3 | GITHUB_CLIENT_SECRET: string;
4 | JWT_SECRET: string;
5 | DB: any;
6 | };
7 |
8 | export type Env = Bindings;
9 |
--------------------------------------------------------------------------------
/libs/common/src/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { ExampleComponent } from '.'
2 |
3 | describe('ExampleComponent', () => {
4 | it('is truthy', () => {
5 | expect(ExampleComponent).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom-issue-template-📝.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Custom issue template \U0001F4DD"
3 | about: Describe this issue template's purpose here.
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
--------------------------------------------------------------------------------
/libs/common/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/apps/web-app/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/libs/common/example/src/index.tsx:
--------------------------------------------------------------------------------
1 | import './index.css'
2 |
3 | import React from 'react'
4 | import ReactDOM from 'react-dom'
5 | import App from './App'
6 |
7 | ReactDOM.render( , document.getElementById('root'))
8 |
--------------------------------------------------------------------------------
/apps/github-bot/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules/
2 | **/.git
3 | **/README.md
4 | **/LICENSE
5 | **/.vscode
6 | **/npm-debug.log
7 | **/coverage
8 | **/.env
9 | **/.editorconfig
10 | **/dist
11 | **/*.pem
12 | Dockerfile
13 |
--------------------------------------------------------------------------------
/libs/common/src/styles.module.css:
--------------------------------------------------------------------------------
1 | /* add css module styles here (optional) */
2 |
3 | .test {
4 | margin: 2em;
5 | padding: 0.5em;
6 | border: 2px solid #000;
7 | font-size: 2em;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/apps/extension/src/interface/index.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | export interface IUser {
3 | email: string;
4 | }
5 |
6 | export interface IButtonProps {
7 | children?: React.ReactNode;
8 | className?: string;
9 | }
10 |
--------------------------------------------------------------------------------
/apps/github-bot/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-slim
2 | WORKDIR /usr/src/app
3 | COPY package.json package-lock.json ./
4 | RUN npm ci --production
5 | RUN npm cache clean --force
6 | ENV NODE_ENV="production"
7 | COPY . .
8 | CMD [ "npm", "start" ]
9 |
--------------------------------------------------------------------------------
/apps/web-app/src/routes/app/index.tsx:
--------------------------------------------------------------------------------
1 | import IndexDashboardPage from "@/pages/main";
2 | import { createFileRoute } from "@tanstack/react-router";
3 |
4 | export const Route = createFileRoute("/app/")({
5 | component: IndexDashboardPage,
6 | });
7 |
--------------------------------------------------------------------------------
/apps/extension/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/apps/github-bot/src/controllers/index.ts:
--------------------------------------------------------------------------------
1 | import { handleIssueOpened, handleIssueComment } from "./issues.controller.ts";
2 | import bountyController from "./bounty.controller.ts";
3 |
4 | export { handleIssueOpened, handleIssueComment, bountyController };
5 |
--------------------------------------------------------------------------------
/apps/github-bot/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vitest/config";
2 |
3 | export default defineConfig({
4 | test: {
5 | include: ["test/**/*.test.ts"],
6 | coverage: {
7 | provider: "v8",
8 | },
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/apps/web-app/src/routes/app/explore.tsx:
--------------------------------------------------------------------------------
1 | import ExploreUsers from "@/pages/explore/ExploreUsers";
2 | import { createFileRoute } from "@tanstack/react-router";
3 |
4 | export const Route = createFileRoute("/app/explore")({
5 | component: ExploreUsers,
6 | });
7 |
--------------------------------------------------------------------------------
/apps/web-app/src/routes/status/success.tsx:
--------------------------------------------------------------------------------
1 | import SuccessPage from "@/pages/status/success-page";
2 | import { createFileRoute } from "@tanstack/react-router";
3 |
4 | export const Route = createFileRoute("/status/success")({
5 | component: SuccessPage,
6 | });
7 |
--------------------------------------------------------------------------------
/apps/extension/libs/supabase.ts:
--------------------------------------------------------------------------------
1 | import initializeSupabase from "../../../libs/supabase";
2 |
3 | // @ts-ignore
4 | const env = import.meta.env;
5 | export const supabaseObject = initializeSupabase(
6 | env.VITE_APP_PROJECT_URL,
7 | env.VITE_APP_PRIVATE_KEY,
8 | );
9 |
--------------------------------------------------------------------------------
/libs/common/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "jsxSingleQuote": true,
4 | "semi": false,
5 | "tabWidth": 2,
6 | "bracketSpacing": true,
7 | "jsxBracketSameLine": false,
8 | "arrowParens": "always",
9 | "trailingComma": "none"
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web-app/src/data/dashboardData.ts:
--------------------------------------------------------------------------------
1 | export const DASHBOARDNAV = [
2 | {
3 | title: "Dashboard",
4 | icon: "🛖",
5 | path: "/app",
6 | },
7 |
8 | {
9 | title: "Explore",
10 | icon: "🌍",
11 | path: "/app/explore",
12 | },
13 | ];
14 |
--------------------------------------------------------------------------------
/program/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "types": ["mocha", "chai"],
4 | "typeRoots": ["./node_modules/@types"],
5 | "lib": ["es2015"],
6 | "module": "commonjs",
7 | "target": "es6",
8 | "esModuleInterop": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/libs/common/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { ExampleComponent } from 'common'
4 | import 'common/dist/index.css'
5 |
6 | const App = () => {
7 | return
8 | }
9 |
10 | export default App
11 |
--------------------------------------------------------------------------------
/config/axios.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | interface IAxiosProps {
4 | baseUrl: string;
5 | }
6 | export const defaultAxios = axios.create({
7 | baseURL: "http://localhost:5000",
8 | timeout: 10000,
9 | timeoutErrorMessage: "Service timeout please try again later",
10 | });
11 |
--------------------------------------------------------------------------------
/libs/common/example/README.md:
--------------------------------------------------------------------------------
1 | This example was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
2 |
3 | It is linked to the common package in the parent directory for development purposes.
4 |
5 | You can run `yarn install` and then `yarn start` to test your package.
6 |
--------------------------------------------------------------------------------
/program/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "programs/*"
4 | ]
5 | resolver = "2"
6 |
7 | [profile.release]
8 | overflow-checks = true
9 | lto = "fat"
10 | codegen-units = 1
11 | [profile.release.build-override]
12 | opt-level = 3
13 | incremental = false
14 | codegen-units = 1
15 |
--------------------------------------------------------------------------------
/apps/github-bot/.env.example:
--------------------------------------------------------------------------------
1 | # The ID of your GitHub App
2 | APP_ID=
3 | WEBHOOK_SECRET=development
4 |
5 | # Use `trace` to get verbose logging or `info` to show less
6 | LOG_LEVEL=debug
7 |
8 | # Go to https://smee.io/new set this to the URL that you are redirected to.
9 | WEBHOOK_PROXY_URL=
10 |
--------------------------------------------------------------------------------
/libs/common/example/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom/extend-expect'
6 |
--------------------------------------------------------------------------------
/libs/common/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import styles from './styles.module.css'
3 |
4 | interface Props {
5 | text: string
6 | }
7 |
8 | export const ExampleComponent = ({ text }: Props) => {
9 | return
Example Component: {text}
10 | }
11 |
--------------------------------------------------------------------------------
/apps/web-app/src/user/user.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { UserService } from "./user.service";
3 | import { UserController } from "./user.controller";
4 |
5 | @Module({
6 | providers: [UserService],
7 | controllers: [UserController],
8 | })
9 | export class UserModule {}
10 |
--------------------------------------------------------------------------------
/libs/common/example/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | it('renders without crashing', () => {
6 | const div = document.createElement('div')
7 | ReactDOM.render( , div)
8 | ReactDOM.unmountComponentAtNode(div)
9 | })
10 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require("webpack");
2 |
3 | module.exports = {
4 | resolve: {
5 | fallback: {
6 | buffer: require.resolve("buffer"),
7 | },
8 | },
9 | plugins: [
10 | new webpack.ProvidePlugin({
11 | Buffer: ["buffer", "Buffer"],
12 | }),
13 | ],
14 | };
15 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/apps/server/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import 'dotenv/config';
2 | import { defineConfig } from 'drizzle-kit';
3 |
4 | export default defineConfig({
5 | out: './drizzle',
6 | schema: './src/db/schema.ts',
7 | dialect: 'postgresql',
8 | dbCredentials: {
9 | url: process.env.DATABASE_URL!,
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/apps/server/src/types.ts:
--------------------------------------------------------------------------------
1 | import { InferSelectModel } from 'drizzle-orm';
2 | import { users, wallets } from './db/schema';
3 |
4 | export type User = InferSelectModel;
5 | export type Wallet = InferSelectModel;
6 |
7 | export interface UserWithWallets extends User {
8 | wallets: Wallet[];
9 | }
10 |
--------------------------------------------------------------------------------
/apps/extension/src/popup.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import Welcome from "./pages/welcome";
4 | import SignIn from "./pages";
5 |
6 | ReactDOM.createRoot(document.body).render(
7 |
8 | {/* */}
9 |
10 | ,
11 | );
12 |
--------------------------------------------------------------------------------
/apps/web-app/src/enums/API_ENUM.ts:
--------------------------------------------------------------------------------
1 | enum API_ENDPOINTS {
2 | GITHUB_AUTH = "/auth/login",
3 | GOOGLE_AUTH = "/auth/google",
4 | SIGN_OUT = "/auth/logout",
5 |
6 | USER_PROFILE = "/user/profile",
7 | USER_ALL = "/user/all",
8 | USER_WALLET = "/wallet",
9 |
10 | TRANSACTION_RECORDS = "/tx-records",
11 | }
12 |
13 | export default API_ENDPOINTS;
14 |
--------------------------------------------------------------------------------
/apps/web-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ],
11 | "compilerOptions": {
12 | "baseUrl": ".",
13 | "paths": {
14 | "@/*": ["./src/*"]
15 | },
16 | "jsx": "react-jsx"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/github-bot/test/fixtures/issues.opened.json:
--------------------------------------------------------------------------------
1 | {
2 | "action": "opened",
3 | "issue": {
4 | "number": 1,
5 | "user": {
6 | "login": "hiimbex"
7 | }
8 | },
9 | "repository": {
10 | "name": "testing-things",
11 | "owner": {
12 | "login": "hiimbex"
13 | }
14 | },
15 | "installation": {
16 | "id": 2
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/apps/web-app/src/util/extract-wallet-from-user.ts:
--------------------------------------------------------------------------------
1 | import { UserService } from "@/services";
2 |
3 | const extractWalletFromUser = async (userName: string) => {
4 | try {
5 | const response = await UserService.fetchAllPotatoeUsers();
6 | return response;
7 | } catch (error) {
8 | return null;
9 | }
10 | };
11 |
12 | export { extractWalletFromUser };
13 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import { cn } from "@/lib/utils";
2 |
3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4 | return (
5 |
10 | );
11 | }
12 |
13 | export { Skeleton };
14 |
--------------------------------------------------------------------------------
/libs/common/example/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "common",
3 | "name": "common",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/embed/tip-badge.tsx:
--------------------------------------------------------------------------------
1 | import { TipBadge } from "@/components/profile/TipBadge";
2 |
3 | export default function TipBadgeEmbed() {
4 | const searchParams = new URLSearchParams(window.location.search);
5 | const username = searchParams.get("user");
6 |
7 | if (!username) {
8 | return null;
9 | }
10 |
11 | return ;
12 | }
13 |
--------------------------------------------------------------------------------
/apps/web-app/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 | .vercel
26 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/libs/common/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | .rpt2_cache
11 |
12 | # misc
13 | .DS_Store
14 | .env
15 | .env.local
16 | .env.development.local
17 | .env.test.local
18 | .env.production.local
19 |
20 | npm-debug.log*
21 | yarn-debug.log*
22 | yarn-error.log*
23 |
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/about.txt:
--------------------------------------------------------------------------------
1 | This favicon was generated using the following graphics from Twitter Twemoji:
2 |
3 | - Graphics Title: 1f35f.svg
4 | - Graphics Author: Copyright 2020 Twitter, Inc and other contributors (https://github.com/twitter/twemoji)
5 | - Graphics Source: https://github.com/twitter/twemoji/blob/master/assets/svg/1f35f.svg
6 | - Graphics License: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
7 |
--------------------------------------------------------------------------------
/program/programs/program/src/lib.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | declare_id!("8kfy8YGqzB1EQf87dqhrfuNaagqpm3yVSZG5T7QnQ294");
4 |
5 | #[program]
6 | pub mod program {
7 | use super::*;
8 |
9 | pub fn initialize(ctx: Context) -> Result<()> {
10 | msg!("Greetings from: {:?}", ctx.program_id);
11 | Ok(())
12 | }
13 | }
14 |
15 | #[derive(Accounts)]
16 | pub struct Initialize {}
17 |
--------------------------------------------------------------------------------
/apps/extension/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | # Config files
27 | .webextrc
28 | .webextrc.*
29 |
--------------------------------------------------------------------------------
/apps/github-bot/src/controllers/bounty.controller.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "probot";
2 | const bountyController = async (context: Context) => {
3 | try {
4 | const comment = context.issue({
5 | body: "💰 This issue has been labeled as a bounty",
6 | });
7 |
8 | await context.octokit.issues.createComment(comment);
9 | } catch (err) {
10 | console.error(err);
11 | }
12 | };
13 |
14 | export default bountyController;
15 |
--------------------------------------------------------------------------------
/apps/server/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20
2 |
3 | WORKDIR /app
4 |
5 | RUN apt-get update && apt-get install -y unzip
6 |
7 | RUN curl -fsSL https://bun.sh/install | bash && \
8 | echo "export PATH=\"/root/.bun/bin:\$PATH\"" >> /root/.bashrc
9 |
10 | ENV PATH="/root/.bun/bin:$PATH"
11 |
12 | COPY package.json ./
13 | COPY bun.lock* ./
14 |
15 | RUN bun install --frozen-lockfile
16 |
17 | COPY . .
18 |
19 | CMD ["bun", "run", "src/index.ts"]
20 |
--------------------------------------------------------------------------------
/apps/extension/src/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Popup
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/program/Anchor.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | package_manager = "yarn"
3 |
4 | [features]
5 | resolution = true
6 | skip-lint = false
7 |
8 | [programs.localnet]
9 | program = "8kfy8YGqzB1EQf87dqhrfuNaagqpm3yVSZG5T7QnQ294"
10 |
11 | [registry]
12 | url = "https://api.apr.dev"
13 |
14 | [provider]
15 | cluster = "localnet"
16 | wallet = "~/.config/solana/id.json"
17 |
18 | [scripts]
19 | test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
20 |
--------------------------------------------------------------------------------
/program/programs/program/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "program"
3 | version = "0.1.0"
4 | description = "Created with Anchor"
5 | edition = "2021"
6 |
7 | [lib]
8 | crate-type = ["cdylib", "lib"]
9 | name = "program"
10 |
11 | [features]
12 | default = []
13 | cpi = ["no-entrypoint"]
14 | no-entrypoint = []
15 | no-idl = []
16 | no-log-ix-name = []
17 | idl-build = ["anchor-lang/idl-build"]
18 |
19 |
20 | [dependencies]
21 | anchor-lang = "0.31.0"
22 |
23 |
--------------------------------------------------------------------------------
/apps/extension/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { IButtonProps } from "../../interface";
3 |
4 | const Button: React.FC = ({
5 | children,
6 | className,
7 | }: IButtonProps) => {
8 | const customClass = `py-2
9 | px-2 bg-primary-100
10 | rounded-md text-white font-bold`;
11 | return {children} ;
12 | };
13 |
14 | export default Button;
15 |
--------------------------------------------------------------------------------
/libs/common/example/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | font-family:
5 | -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
6 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
7 | -webkit-font-smoothing: antialiased;
8 | -moz-osx-font-smoothing: grayscale;
9 | }
10 |
11 | code {
12 | font-family:
13 | source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 🚀 Features
2 |
3 | - 🔥 **Zap GitHub Users** – Instantly tip developers with SOL for their contributions.
4 | - ⚡ **Get Zapped** – Receive tips from other users for your open-source work.
5 | - 🔗 **Seamless GitHub Integration** – Connect your GitHub profile and start tipping effortlessly.
6 | - 🛠 **Fast & Low-Cost Transactions** – Built on Solana for lightning-fast and cheap payments.
7 | - 🎉 **Support Open Source** – Encourage innovation by rewarding developers directly.
8 |
--------------------------------------------------------------------------------
/apps/web-app/src/routes/app/profile.tsx:
--------------------------------------------------------------------------------
1 | import DefaultDashboard from "@/layouts/dashboard";
2 | import UserProfileCard from "@/pages/profile/userProfileCard";
3 | import { createFileRoute } from "@tanstack/react-router";
4 |
5 | export const Route = createFileRoute("/app/profile")({
6 | component: RouteComponent,
7 | });
8 |
9 | function RouteComponent() {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
--------------------------------------------------------------------------------
/apps/server/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | node_modules/
3 | .pnp
4 | .pnp.js
5 |
6 | # testing
7 | coverage/
8 |
9 | # production
10 | build/
11 | dist/
12 | .output/
13 |
14 | # misc
15 | .DS_Store
16 | *.pem
17 | .env
18 | .env.local
19 | .env.*
20 | !.env.example
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # IDE
28 | .idea/
29 | .vscode/
30 | *.swp
31 | *.swo
32 |
33 | # Bun
34 | bun.lockb
35 |
36 | # Logs
37 | logs
38 | *.log
39 |
--------------------------------------------------------------------------------
/apps/web-app/public/favicon_io/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "",
3 | "short_name": "",
4 | "icons": [
5 | {
6 | "src": "/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#ffffff",
17 | "background_color": "#ffffff",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/apps/web-app/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from "react";
2 | import { createRoot } from "react-dom/client";
3 | import App from "./App.tsx";
4 | import IndexProdivder from "./providers/indexProvider.tsx";
5 | import { Toaster } from "sonner";
6 |
7 | createRoot(document.getElementById("root")!).render(
8 |
9 |
10 |
11 |
12 |
13 | ,
14 | );
15 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/main/index.tsx:
--------------------------------------------------------------------------------
1 | import WalletTransactionTable from "@/components/tables/transactionTable.tsx";
2 | import DefaultDashboard from "@/layouts/dashboard.tsx";
3 | import WalletProfilePanel from "@/components/wallet/walletProfilePanel.";
4 | function IndexDashboardPage() {
5 | return (
6 |
7 |
8 |
9 |
10 | );
11 | }
12 |
13 | export default IndexDashboardPage;
14 |
--------------------------------------------------------------------------------
/program/migrations/deploy.ts:
--------------------------------------------------------------------------------
1 | // Migrations are an early feature. Currently, they're nothing more than this
2 | // single deploy script that's invoked from the CLI, injecting a provider
3 | // configured from the workspace's Anchor.toml.
4 |
5 | import * as anchor from "@coral-xyz/anchor";
6 |
7 | module.exports = async function (provider: anchor.AnchorProvider) {
8 | // Configure client to use the provider.
9 | anchor.setProvider(provider);
10 |
11 | // Add your deploy script here.
12 | };
13 |
--------------------------------------------------------------------------------
/apps/github-bot/src/enums/commands.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description
3 | * Yoo, so i Probot does'nt support Ts enums ye
4 | * I am not stupid for using object, it is just for the main time.
5 | * I will change it to enum if Probot supports it.
6 | * For now lets be squashing some potatoes 🍟
7 | */
8 | const COMMANDS = {
9 | BOUNTY: "/bounty",
10 | CLAIM: "/claim",
11 | CANCEL_BOUNTY: "/cancel-bounty",
12 | EDIT_BOUNTY: "/edit-bounty",
13 | HELP: "/help",
14 | } as const;
15 |
16 | export default COMMANDS;
17 |
--------------------------------------------------------------------------------
/.idea/potato-monorepo.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/apps/server/scripts/push-db.ts:
--------------------------------------------------------------------------------
1 | import { drizzle } from 'drizzle-orm/neon-http';
2 | import { neon } from '@neondatabase/serverless';
3 | import * as dotenv from 'dotenv';
4 |
5 | dotenv.config();
6 |
7 | async function push() {
8 | try {
9 | const sql = neon(process.env.DATABASE_URL!);
10 | const db = drizzle(sql);
11 |
12 | console.log('Connected to database');
13 | console.log('Schema push completed');
14 | } catch (error) {
15 | console.error('Failed to connect:', error);
16 | }
17 | }
18 |
19 | push();
20 |
--------------------------------------------------------------------------------
/apps/web-app/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": false,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "",
8 | "css": "src/App.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
22 |
--------------------------------------------------------------------------------
/apps/github-bot/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Probot } from "probot";
2 | import { handleIssueOpened, handleIssueComment } from "./controllers/index.ts";
3 | import { bountyController } from "./controllers/index.ts";
4 |
5 | export default (app: Probot) => {
6 | app.log.info("App started 🎉");
7 | app.on("issues.opened", handleIssueOpened);
8 | app.on("issue_comment.created", async (context) => {
9 | if (context.isBot) return;
10 | await handleIssueComment(context);
11 | });
12 | app.on("issues.labeled", bountyController);
13 | };
14 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/misc/ProductHuntCard.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ProductHuntCard: React.FC = () => {
4 | return (
5 |
6 |
15 |
16 | );
17 | };
18 |
19 | export default ProductHuntCard;
20 |
--------------------------------------------------------------------------------
/apps/server/src/enums/index.ts:
--------------------------------------------------------------------------------
1 | enum NOTIFICATION_TYPE {
2 | NEW_USER = '🎉 A new user has signed up.',
3 | NEW_MESSAGE = '📩 You have received a new message.',
4 | TRANSACTION_NOTIFICATION = '💳 Your transaction has been processed successfully.',
5 | TIP_NOTIFICATION = '💰 You have received a new tip.',
6 | REMINDER = '⏰ This is a reminder for your upcoming event.',
7 | }
8 |
9 | enum NOTIFICATION_STATUS {
10 | SENT = 'sent',
11 | FAILED = 'failed',
12 | PENDING = 'pending',
13 | }
14 |
15 | export { NOTIFICATION_TYPE, NOTIFICATION_STATUS };
16 |
--------------------------------------------------------------------------------
/program/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "license": "ISC",
3 | "scripts": {
4 | "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
5 | "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
6 | },
7 | "dependencies": {
8 | "@coral-xyz/anchor": "^0.31.0"
9 | },
10 | "devDependencies": {
11 | "chai": "^4.3.4",
12 | "mocha": "^9.0.3",
13 | "ts-mocha": "^10.0.0",
14 | "@types/bn.js": "^5.1.0",
15 | "@types/chai": "^4.3.0",
16 | "@types/mocha": "^9.0.0",
17 | "typescript": "^5.7.3",
18 | "prettier": "^2.6.2"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/apps/web-app/src/lib/supabase.ts:
--------------------------------------------------------------------------------
1 | import { createClient } from "@supabase/supabase-js";
2 |
3 | const supabaseUrl = import.meta.env.VITE_PROJECT_URL;
4 | const supabaseKey = import.meta.env.VITE_APP_PRIVATE_KEY;
5 |
6 | if (!supabaseUrl || !supabaseKey) {
7 | throw new Error("Missing Supabase environment variables");
8 | }
9 |
10 | export const supabase = createClient(supabaseUrl, supabaseKey, {
11 | auth: {
12 | autoRefreshToken: true,
13 | persistSession: true,
14 | detectSessionInUrl: true,
15 | },
16 | });
17 |
18 | export default supabase;
19 |
--------------------------------------------------------------------------------
/libs/common/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Default CSS definition for typescript,
3 | * will be overridden with file-specific definitions by rollup
4 | */
5 | declare module '*.css' {
6 | const content: { [className: string]: string }
7 | export default content
8 | }
9 |
10 | interface SvgrComponent
11 | extends React.StatelessComponent> {}
12 |
13 | declare module '*.svg' {
14 | const svgUrl: string
15 | const svgComponent: SvgrComponent
16 | export default svgUrl
17 | export { svgComponent as ReactComponent }
18 | }
19 |
--------------------------------------------------------------------------------
/.idea/dataSources.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | postgresql
6 | true
7 | org.postgresql.Driver
8 | jdbc:postgresql:///potatoe
9 | $ProjectFileDir$
10 |
11 |
12 |
--------------------------------------------------------------------------------
/apps/extension/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
4 | theme: {
5 | extend: {
6 | colors: {
7 | primary: {
8 | 100: "#f4a929",
9 | },
10 | },
11 | container: {
12 | padding: {
13 | DEFAULT: "1rem",
14 | sm: "2rem",
15 | lg: "4rem",
16 | xl: "5rem",
17 | "2xl": "6rem",
18 | },
19 | },
20 | },
21 | },
22 | plugins: [require("daisyui")],
23 | };
24 |
--------------------------------------------------------------------------------
/apps/web-app/src/App.tsx:
--------------------------------------------------------------------------------
1 | import "./App.css";
2 | import { RouterProvider, createRouter } from "@tanstack/react-router";
3 | import { routeTree } from "./routeTree.gen";
4 | import { Analytics } from "@vercel/analytics/react";
5 |
6 | const router = createRouter({ routeTree });
7 |
8 | declare module "@tanstack/react-router" {
9 | interface Register {
10 | router: typeof router;
11 | }
12 | }
13 | function App() {
14 | return (
15 | <>
16 |
17 |
18 | >
19 | );
20 | }
21 |
22 | export default App;
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "potato-monorepo",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "workspaces": [],
7 | "scripts": {
8 | "format": "prettier --write ./",
9 | "start": "concurrently \"cd apps/web-app && pnpm dev\" \"cd apps/server && pnpm dev\""
10 | },
11 | "private": true,
12 | "dependencies": {
13 | "@radix-ui/react-alert-dialog": "^1.1.6",
14 | "dotenv": "^16.4.7",
15 | "lucid": "^2.21.0",
16 | "prettier": "^3.4.2"
17 | },
18 | "devDependencies": {
19 | "concurrently": "^9.1.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/apps/web-app/src/interface/users.interface.ts:
--------------------------------------------------------------------------------
1 | interface IPotatoeUser {
2 | id: number;
3 | githubId: string;
4 | username: string;
5 | email: string | null;
6 | name: string;
7 | avatarUrl: string;
8 | createdAt: string;
9 | updatedAt: string;
10 | }
11 |
12 | interface IWallet {
13 | id: number;
14 | userId: number;
15 | address: string;
16 | createdAt: string;
17 | updatedAt: string;
18 | }
19 |
20 | interface IPotatoeUserData {
21 | users: IPotatoeUser;
22 | wallets: IWallet;
23 | }
24 |
25 | export type { IPotatoeUser, IWallet, IPotatoeUserData };
26 |
--------------------------------------------------------------------------------
/program/tests/program.ts:
--------------------------------------------------------------------------------
1 | import * as anchor from "@coral-xyz/anchor";
2 | import { Program } from "@coral-xyz/anchor";
3 | import { Program } from "../target/types/program";
4 |
5 | describe("program", () => {
6 | // Configure the client to use the local cluster.
7 | anchor.setProvider(anchor.AnchorProvider.env());
8 |
9 | const program = anchor.workspace.program as Program;
10 |
11 | it("Is initialized!", async () => {
12 | // Add your test here.
13 | const tx = await program.methods.initialize().rpc();
14 | console.log("Your transaction signature", tx);
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/apps/web-app/src/constant/index.ts:
--------------------------------------------------------------------------------
1 | export const BASE_API_URL = import.meta.env.VITE_API_BASE_URL;
2 | export const TAB_STATE = `cursor-pointer !rounded-none
3 | data-[state=active]:bg-transparent
4 | data-[state=active]:border-b-2
5 | data-[state=active]:text-red-400`;
6 |
7 | export const RPC_KEY = import.meta.env.VITE_RPC_KEY;
8 | const RPC_ENDPOINT = `https://mainnet.helius-rpc.com/?api-key=${RPC_KEY}`;
9 | export const RPC_URL =
10 | import.meta.env.MODE === "development"
11 | ? "https://api.devnet.solana.com"
12 | : RPC_ENDPOINT;
13 |
--------------------------------------------------------------------------------
/apps/web-app/src/providers/indexProvider.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 | import WalletConnectProvider from "./walletConnectProvider";
3 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4 |
5 | const IndexProdivder = ({ children }: { children: ReactNode }) => {
6 | const queryClient = new QueryClient();
7 | return (
8 | <>
9 |
10 |
11 | {children}
12 |
13 |
14 | >
15 | );
16 | };
17 | export default IndexProdivder;
18 |
--------------------------------------------------------------------------------
/apps/server/fly.toml:
--------------------------------------------------------------------------------
1 | # fly.toml app configuration file generated for potatoe-squeezy on 2025-03-16T01:06:35+01:00
2 | #
3 | # See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4 | #
5 |
6 | app = 'potatoe-squeezy'
7 | primary_region = 'jnb'
8 |
9 | [build]
10 |
11 | [env]
12 | PORT = '8080'
13 |
14 | [http_service]
15 | internal_port = 8080
16 | force_https = true
17 | auto_stop_machines = 'stop'
18 | auto_start_machines = true
19 | min_machines_running = 0
20 | processes = ['app']
21 |
22 | [[vm]]
23 | memory = '1gb'
24 | cpu_kind = 'shared'
25 | cpus = 1
26 |
--------------------------------------------------------------------------------
/apps/web-app/src/services/auth.service.ts:
--------------------------------------------------------------------------------
1 | import API_ENDPOINTS from "@/enums/API_ENUM";
2 | import ApiClient from "@/util/api";
3 |
4 | class AuthService {
5 | static async signInWithGithub(): Promise {
6 | const response = await ApiClient.get<{ url: string }>(
7 | API_ENDPOINTS.GITHUB_AUTH,
8 | );
9 | return response;
10 | }
11 |
12 | static async signInWithGoogle(): Promise {
13 | return await ApiClient.get(API_ENDPOINTS.GOOGLE_AUTH);
14 | }
15 |
16 | static async signOut(): Promise {
17 | await ApiClient.post(API_ENDPOINTS.SIGN_OUT);
18 | }
19 | }
20 |
21 | export { AuthService };
22 |
--------------------------------------------------------------------------------
/apps/web-app/src/services/user.service.ts:
--------------------------------------------------------------------------------
1 | import API_ENDPOINTS from "@/enums/API_ENUM";
2 | import ApiClient from "@/util/api";
3 |
4 | class UserService {
5 | static async fetchUserProfile() {
6 | const response = await ApiClient.get(API_ENDPOINTS.USER_PROFILE);
7 | return response;
8 | }
9 |
10 | static async fetchUserWallets() {
11 | const response = await ApiClient.get(API_ENDPOINTS.USER_WALLET);
12 | return response;
13 | }
14 |
15 | static async fetchAllPotatoeUsers() {
16 | const response = await ApiClient.get(API_ENDPOINTS.USER_ALL);
17 | return response;
18 | }
19 | }
20 | export default UserService;
21 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/fallbacks/noDataFound.tsx:
--------------------------------------------------------------------------------
1 | import { WalletSvg } from "@/assets/svg";
2 |
3 | interface NoDataFoundProps {
4 | message?: string;
5 | submessage?: string;
6 | }
7 |
8 | export const NoDataFound = ({
9 | message = "No transactions yet",
10 | submessage = "Your transaction history will appear here",
11 | }: NoDataFoundProps) => {
12 | return (
13 |
14 |
15 |
{message}
16 |
{submessage}
17 |
18 | );
19 | };
20 |
--------------------------------------------------------------------------------
/apps/extension/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on: [pull_request_target, issues]
4 |
5 | jobs:
6 | greeting:
7 | runs-on: ubuntu-latest
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 | steps:
12 | - uses: actions/first-interaction@v1
13 | with:
14 | repo-token: ${{ secrets.GITHUB_TOKEN }}
15 | issue-message: "🎉 Thank you for opening your first issue! We appreciate your contribution and will review it soon. Stay tuned for updates! 🚀"
16 | pr-message: "👏 Amazing! You've submitted your first pull request! Our team will review it shortly. Thanks for helping improve the project! 🎯"
17 |
--------------------------------------------------------------------------------
/apps/extension/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import react from "@vitejs/plugin-react";
3 | import webExtension, { readJsonFile } from "vite-plugin-web-extension";
4 |
5 | function generateManifest() {
6 | const manifest = readJsonFile("src/manifest.json");
7 | const pkg = readJsonFile("package.json");
8 | return {
9 | name: pkg.name,
10 | description: pkg.description,
11 | version: pkg.version,
12 | ...manifest,
13 | };
14 | }
15 |
16 | // https://vitejs.dev/config/
17 | export default defineConfig({
18 | plugins: [
19 | react(),
20 | webExtension({
21 | manifest: generateManifest,
22 | }),
23 | ],
24 | });
25 |
--------------------------------------------------------------------------------
/libs/common/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "jsx": "react",
8 | "sourceMap": true,
9 | "declaration": true,
10 | "esModuleInterop": true,
11 | "noImplicitReturns": true,
12 | "noImplicitThis": true,
13 | "noImplicitAny": true,
14 | "strictNullChecks": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "allowSyntheticDefaultImports": true
19 | },
20 | "include": ["src"],
21 | "exclude": ["node_modules", "build"]
22 | }
23 |
--------------------------------------------------------------------------------
/libs/common/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "dist",
4 | "module": "esnext",
5 | "lib": ["dom", "esnext"],
6 | "moduleResolution": "node",
7 | "jsx": "react",
8 | "sourceMap": true,
9 | "declaration": true,
10 | "esModuleInterop": true,
11 | "noImplicitReturns": true,
12 | "noImplicitThis": true,
13 | "noImplicitAny": true,
14 | "strictNullChecks": true,
15 | "suppressImplicitAnyIndexErrors": true,
16 | "noUnusedLocals": true,
17 | "noUnusedParameters": true,
18 | "allowSyntheticDefaultImports": true
19 | },
20 | "include": ["src"],
21 | "exclude": ["node_modules", "dist", "example"]
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | export const GITHUB_CLIENT_ID = process.env.GITHUB_CLIENT_ID!;
2 | export const GITHUB_CLIENT_SECRET = process.env.GITHUB_CLIENT_SECRET!;
3 | export const JWT_SECRET = process.env.JWT_SECRET!;
4 | export const DATABASE_URL = process.env.DATABASE_URL!;
5 | export const GITHUB_CALLBACK_URL =
6 | process.env.NODE_ENV === 'production'
7 | ? 'https://potatoe-app-production.up.railway.app/auth/callback'
8 | : 'http://localhost:3000/auth/callback';
9 | export const FRONTEND_APP_URL = process.env.FRONTEND_APP_URL!;
10 | export const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN!;
11 | export const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID!;
12 |
--------------------------------------------------------------------------------
/libs/common/README.md:
--------------------------------------------------------------------------------
1 | # common
2 |
3 | > french fries
4 |
5 | [](https://www.npmjs.com/package/common) [](https://standardjs.com)
6 |
7 | ## Install
8 |
9 | ```bash
10 | npm install --save common
11 | ```
12 |
13 | ## Usage
14 |
15 | ```tsx
16 | import React, { Component } from 'react'
17 |
18 | import MyComponent from 'common'
19 | import 'common/dist/index.css'
20 |
21 | class Example extends Component {
22 | render() {
23 | return
24 | }
25 | }
26 | ```
27 |
28 | ## License
29 |
30 | MIT © [yhoungdev](https://github.com/yhoungdev)
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request-✨.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request ✨
3 | about: Suggest an idea for this project
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/libs/supabase/index.ts:
--------------------------------------------------------------------------------
1 | import { createClient, SupabaseClient } from "@supabase/supabase-js";
2 |
3 | /**
4 | * @param {string} supabaseUrl - The Supabase project URL.
5 | * @param {string} supabaseAnonKey - The Supabase public anonymous key.
6 | * @returns {SupabaseClient} The initialized Supabase client.
7 | */
8 | const initializeSupabase = (
9 | supabaseUrl: string,
10 | supabaseAnonKey: string,
11 | ): SupabaseClient => {
12 | if (!supabaseUrl || !supabaseAnonKey) {
13 | throw new Error(
14 | "Supabase URL and Anon Key are required to initialize the client.",
15 | );
16 | }
17 | return createClient(supabaseUrl, supabaseAnonKey);
18 | };
19 |
20 | export default initializeSupabase;
21 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/wallet/walletProfilePanel..tsx:
--------------------------------------------------------------------------------
1 | import ProfilePanel from "../profile/profilePanel";
2 | import { useUserStore } from "@/store/user.store";
3 |
4 | function WalletProfilePanel() {
5 | const userStore = useUserStore();
6 | const users = userStore?.user?.users ?? {};
7 | const address = userStore?.user?.wallets?.address ?? "";
8 |
9 | const { avatarUrl, username, name } = users;
10 |
11 | return (
12 |
21 | );
22 | }
23 |
24 | export default WalletProfilePanel;
25 |
--------------------------------------------------------------------------------
/apps/web-app/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4 | "target": "ES2022",
5 | "lib": ["ES2023"],
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 |
9 | /* Bundler mode */
10 | "moduleResolution": "bundler",
11 | "allowImportingTsExtensions": true,
12 | "isolatedModules": true,
13 | "moduleDetection": "force",
14 | "noEmit": true,
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true,
21 | "noUncheckedSideEffectImports": true
22 | },
23 | "include": ["vite.config.ts"]
24 | }
25 |
--------------------------------------------------------------------------------
/apps/github-bot/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "incremental": true,
4 | "target": "es2022",
5 | "module": "Node16",
6 | "declaration": true,
7 | "sourceMap": true,
8 | "outDir": "./lib",
9 |
10 | /* Strict Type-Checking Options */
11 | "strict": true,
12 |
13 | /* Additional Checks */
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "noFallthroughCasesInSwitch": true,
18 | "allowImportingTsExtensions": true,
19 |
20 | "moduleResolution": "Node16",
21 | "esModuleInterop": true,
22 | "forceConsistentCasingInFileNames": true
23 | },
24 | "include": ["src/"],
25 | "compileOnSave": false
26 | }
27 |
--------------------------------------------------------------------------------
/apps/server/src/config/telegraf.ts:
--------------------------------------------------------------------------------
1 | import { Telegraf } from 'telegraf';
2 | import { TELEGRAM_BOT_TOKEN } from '../constants';
3 |
4 | const telegram_bot = new Telegraf(TELEGRAM_BOT_TOKEN);
5 |
6 | let isBotLaunched = false;
7 |
8 | const launchBot = async () => {
9 | if (!isBotLaunched) {
10 | try {
11 | await telegram_bot.launch();
12 | isBotLaunched = true;
13 | console.log('✅ Telegram bot launched successfully!');
14 | } catch (error) {
15 | console.error('🚫 Failed to launch Telegram bot:', error);
16 | }
17 |
18 | process.once('SIGINT', () => telegram_bot.stop('SIGINT'));
19 | process.once('SIGTERM', () => telegram_bot.stop('SIGTERM'));
20 | }
21 | };
22 |
23 | export { telegram_bot, launchBot };
24 |
--------------------------------------------------------------------------------
/apps/github-bot/src/controllers/issues.controller.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "probot";
2 |
3 | export async function handleIssueOpened(context: Context) {
4 | try {
5 | const issueComment = context.issue({
6 | body: "Thanks for opening this issue!",
7 | });
8 | await context.octokit.issues.createComment(issueComment);
9 | } catch (error) {
10 | console.error("Error handling opened issue:", error);
11 | }
12 | }
13 |
14 | export async function handleIssueComment(context: Context) {
15 | try {
16 | // Add your issue comment handling logic here
17 | const comment = context.payload.comment;
18 | // Process the comment as needed
19 | } catch (error) {
20 | console.error("Error handling issue comment:", error);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/apps/server/src/utils/telegram-notification.ts:
--------------------------------------------------------------------------------
1 | import { telegram_bot } from '../config/telegraf';
2 |
3 | const sendTelegramMessage = async (chatId: string, message: string) => {
4 | try {
5 | await telegram_bot.telegram.sendMessage(chatId, message);
6 | } catch (error) {
7 | console.error(`Failed to send message to chat ${chatId}:`, error);
8 | }
9 | };
10 |
11 | const sendTelegramNotification = async (chatId: string, message: string) => {
12 | try {
13 | await telegram_bot.telegram.sendMessage(chatId, message, {
14 | parse_mode: 'MarkdownV2',
15 | });
16 | } catch (error) {
17 | console.error(`Failed to send notification to chat ${chatId}:`, error);
18 | }
19 | };
20 |
21 | export { sendTelegramNotification, sendTelegramMessage };
22 |
--------------------------------------------------------------------------------
/apps/github-bot/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2025, obiabo
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 |
--------------------------------------------------------------------------------
/apps/server/drizzle/0002_easy_shen.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "transaction_records" (
2 | "id" serial PRIMARY KEY NOT NULL,
3 | "amount" numeric NOT NULL,
4 | "sender_address" text NOT NULL,
5 | "sender_id" integer,
6 | "recipient_address" text NOT NULL,
7 | "recipient_id" integer,
8 | "tx_hash" text NOT NULL,
9 | "note" text,
10 | "created_at" timestamp DEFAULT now()
11 | );
12 | --> statement-breakpoint
13 | ALTER TABLE "transaction_records" ADD CONSTRAINT "transaction_records_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
14 | ALTER TABLE "transaction_records" ADD CONSTRAINT "transaction_records_recipient_id_users_id_fk" FOREIGN KEY ("recipient_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
--------------------------------------------------------------------------------
/apps/server/drizzle/meta/_journal.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "7",
3 | "dialect": "postgresql",
4 | "entries": [
5 | {
6 | "idx": 0,
7 | "version": "7",
8 | "when": 1742018780546,
9 | "tag": "0000_kind_gamora",
10 | "breakpoints": true
11 | },
12 | {
13 | "idx": 1,
14 | "version": "7",
15 | "when": 1744089206973,
16 | "tag": "0001_motionless_loki",
17 | "breakpoints": true
18 | },
19 | {
20 | "idx": 2,
21 | "version": "7",
22 | "when": 1745093521013,
23 | "tag": "0002_easy_shen",
24 | "breakpoints": true
25 | },
26 | {
27 | "idx": 3,
28 | "version": "7",
29 | "when": 1745093718119,
30 | "tag": "0003_past_kitty_pryde",
31 | "breakpoints": true
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/apps/github-bot/README.md:
--------------------------------------------------------------------------------
1 | # github-bot
2 |
3 | > A GitHub App built with [Probot](https://github.com/probot/probot) that Reward contributors from PR and comment with 🍟 potatoe bot
4 |
5 | ## Setup
6 |
7 | ```sh
8 | # Install dependencies
9 | npm install
10 |
11 | # Run the bot
12 | npm start
13 | ```
14 |
15 | ## Docker
16 |
17 | ```sh
18 | # 1. Build container
19 | docker build -t github-bot .
20 |
21 | # 2. Start container
22 | docker run -e APP_ID= -e PRIVATE_KEY= github-bot
23 | ```
24 |
25 | ## Contributing
26 |
27 | If you have suggestions for how github-bot could be improved, or want to report a bug, open an issue! We'd love all and any contributions.
28 |
29 | For more, check out the [Contributing Guide](CONTRIBUTING.md).
30 |
31 | ## License
32 |
33 | [ISC](LICENSE) © 2025 obiabo
34 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/profile/TipBadge.tsx:
--------------------------------------------------------------------------------
1 | interface TipBadgeProps {
2 | username: string;
3 | }
4 |
5 | export function TipBadge({ username }: TipBadgeProps) {
6 | const currentUrl =
7 | typeof window !== "undefined" ? window.location.origin : "";
8 |
9 | return (
10 |
16 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/apps/extension/src/pages/welcome.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Button from "../components/ui/button";
3 |
4 | function Welcome() {
5 | return (
6 |
12 |
13 |
🥔
14 |
15 | Your GitHub just got crispy. When life hands you potatoes, squeeze out
16 | Bitcoin instead! 🎉🥔.
17 |
18 |
19 |
Get Started
20 |
21 | );
22 | }
23 |
24 | export default Welcome;
25 |
--------------------------------------------------------------------------------
/libs/common/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "extends": [
4 | "standard",
5 | "standard-react",
6 | "plugin:prettier/recommended",
7 | "prettier/standard",
8 | "prettier/react",
9 | "plugin:@typescript-eslint/eslint-recommended"
10 | ],
11 | "env": {
12 | "node": true
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 2020,
16 | "ecmaFeatures": {
17 | "legacyDecorators": true,
18 | "jsx": true
19 | }
20 | },
21 | "settings": {
22 | "react": {
23 | "version": "16"
24 | }
25 | },
26 | "rules": {
27 | "space-before-function-paren": 0,
28 | "react/prop-types": 0,
29 | "react/jsx-handler-names": 0,
30 | "react/jsx-fragments": 0,
31 | "react/no-unused-prop-types": 0,
32 | "import/export": 0
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/popups/drawer/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Sheet,
3 | SheetContent,
4 | SheetHeader,
5 | SheetTitle,
6 | SheetTrigger,
7 | } from "@/components/ui/sheet";
8 |
9 | interface IDrawer {
10 | trigger: React.ReactNode;
11 | children: React.ReactNode;
12 | title: string;
13 | side?: "top" | "right" | "bottom" | "left";
14 | }
15 |
16 | const Drawer = ({ trigger, children, title, side = "right" }: IDrawer) => {
17 | return (
18 |
19 | {trigger}
20 |
21 |
22 | {title}
23 |
24 | {children}
25 |
26 |
27 | );
28 | };
29 |
30 | export default Drawer;
31 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/github/GithubUserCardSkeleton.tsx:
--------------------------------------------------------------------------------
1 | import { Skeleton } from "@/components/ui/skeleton";
2 |
3 | export function GithubUserCardSkeleton() {
4 | return (
5 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/apps/server/drizzle/0000_kind_gamora.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "users" (
2 | "id" serial PRIMARY KEY NOT NULL,
3 | "github_id" text NOT NULL,
4 | "username" text NOT NULL,
5 | "email" text,
6 | "name" text,
7 | "avatar_url" text,
8 | "created_at" timestamp DEFAULT now(),
9 | "updated_at" timestamp DEFAULT now(),
10 | CONSTRAINT "users_github_id_unique" UNIQUE("github_id")
11 | );
12 | --> statement-breakpoint
13 | CREATE TABLE "wallets" (
14 | "id" serial PRIMARY KEY NOT NULL,
15 | "user_id" integer NOT NULL,
16 | "address" text NOT NULL,
17 | "created_at" timestamp DEFAULT now(),
18 | "updated_at" timestamp DEFAULT now(),
19 | CONSTRAINT "wallets_address_unique" UNIQUE("address")
20 | );
21 | --> statement-breakpoint
22 | ALTER TABLE "wallets" ADD CONSTRAINT "wallets_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
--------------------------------------------------------------------------------
/apps/web-app/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from "@eslint/js";
2 | import globals from "globals";
3 | import reactHooks from "eslint-plugin-react-hooks";
4 | import reactRefresh from "eslint-plugin-react-refresh";
5 | import tseslint from "typescript-eslint";
6 |
7 | export default tseslint.config(
8 | { ignores: ["dist"] },
9 | {
10 | extends: [js.configs.recommended, ...tseslint.configs.recommended],
11 | files: ["**/*.{ts,tsx}"],
12 | languageOptions: {
13 | ecmaVersion: 2020,
14 | globals: globals.browser,
15 | },
16 | plugins: {
17 | "react-hooks": reactHooks,
18 | "react-refresh": reactRefresh,
19 | },
20 | rules: {
21 | ...reactHooks.configs.recommended.rules,
22 | "react-refresh/only-export-components": [
23 | "warn",
24 | { allowConstantExport: true },
25 | ],
26 | },
27 | },
28 | );
29 |
--------------------------------------------------------------------------------
/apps/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "scripts": {
4 | "dev": "bun run --hot src/index.ts",
5 | "start": "bun run --hot src/index.ts",
6 | "db:generate": "drizzle-kit generate",
7 | "push": "bun run scripts/push-db.ts",
8 | "db:studio": "drizzle-kit studio",
9 | "db:push": "npx drizzle-kit push",
10 | "format": "prettier --write ."
11 | },
12 | "dependencies": {
13 | "@hono-dev/auth-github": "^0.0.1",
14 | "@neondatabase/serverless": "^0.10.4",
15 | "dotenv": "^16.4.7",
16 | "drizzle-orm": "^0.40.0",
17 | "hono": "^4.7.2",
18 | "pg": "^8.14.0",
19 | "prettier": "^3.5.3",
20 | "telegraf": "^4.16.3",
21 | "telegramify-markdown": "^1.3.0"
22 | },
23 | "devDependencies": {
24 | "@types/bun": "latest",
25 | "@types/pg": "^8.11.11",
26 | "drizzle-kit": "^0.30.5",
27 | "tsx": "^4.19.3"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/useProfile.ts:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { UserService } from "@/services";
3 | import { useUserStore } from "@/store/user.store";
4 | import Cookies from "js-cookie";
5 |
6 | export function useProfile() {
7 | const setUser = useUserStore((state) => state.setUser);
8 | const { setWallet } = useUserStore();
9 | const token = Cookies.get("auth-token");
10 |
11 | const { data: profile, isLoading } = useQuery({
12 | queryKey: ["userProfile", token],
13 | queryFn: async () => {
14 | const response = await UserService.fetchUserProfile();
15 | setUser(response);
16 | setWallet(response?.wallets?.address);
17 |
18 | return response;
19 | },
20 | enabled: !!token,
21 | staleTime: 1000 * 60 * 5,
22 | retry: 1,
23 | });
24 |
25 | return {
26 | profile,
27 | isLoading,
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/apps/web-app/src/services/wallet.service.ts:
--------------------------------------------------------------------------------
1 | import API_ENDPOINTS from "@/enums/API_ENUM";
2 | import ApiClient from "@/util/api";
3 |
4 | interface UpdateWalletPayload {
5 | address: string;
6 | }
7 |
8 | class WalletService {
9 | static async getWalletAddress() {
10 | const response = await ApiClient.get<{ address: string }>(
11 | API_ENDPOINTS.USER_WALLET,
12 | );
13 | return response;
14 | }
15 |
16 | static async addWallet(payload: any) {
17 | const response = await ApiClient.post<{ address: string }>(
18 | API_ENDPOINTS.USER_WALLET,
19 | payload,
20 | );
21 | return response;
22 | }
23 |
24 | static async updateWallet(payload: any) {
25 | const response = await ApiClient.put<{ address: string }>(
26 | API_ENDPOINTS.USER_WALLET,
27 | payload,
28 | );
29 | return response;
30 | }
31 | }
32 |
33 | export default WalletService;
34 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/connect-wallet.ts:
--------------------------------------------------------------------------------
1 | import { useWallet } from "@solana/wallet-adapter-react";
2 | import { useWalletModal } from "@solana/wallet-adapter-react-ui";
3 |
4 | export const useWalletConnection = () => {
5 | const { setVisible } = useWalletModal();
6 | const { connected, connecting } = useWallet();
7 |
8 | const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
9 |
10 | const connectWallet = async () => {
11 | if (isMobile) {
12 | const isSolanaAvailable =
13 | !!(window as any).solana || !!(window as any).solflare;
14 |
15 | if (!isSolanaAvailable) {
16 | const universalLink = `https://solana.com/portal`;
17 | window.location.href = universalLink;
18 | return;
19 | }
20 | }
21 |
22 | setVisible(true);
23 | };
24 |
25 | return {
26 | connectWallet,
27 | connected,
28 | connecting,
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/connect-wallet.tsx:
--------------------------------------------------------------------------------
1 | import { useWallet } from "@solana/wallet-adapter-react";
2 | import { useWalletModal } from "@solana/wallet-adapter-react-ui";
3 |
4 | export const useWalletConnection = () => {
5 | const { setVisible } = useWalletModal();
6 | const { connected, connecting } = useWallet();
7 |
8 | const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
9 |
10 | const connectWallet = async () => {
11 | if (isMobile) {
12 | const isSolanaAvailable =
13 | !!(window as any).solana || !!(window as any).solflare;
14 |
15 | if (!isSolanaAvailable) {
16 | const universalLink = `https://solana.com/portal`;
17 | window.location.href = universalLink;
18 | return;
19 | }
20 | }
21 |
22 | setVisible(true);
23 | };
24 |
25 | return {
26 | connectWallet,
27 | connected,
28 | connecting,
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/apps/web-app/src/configs/axios.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import Cookies from "js-cookie";
3 |
4 | const DEFAULT_AXIOS = axios.create({
5 | baseURL: import.meta.env.VITE_API_BASE_URL,
6 | timeout: 10000,
7 | timeoutErrorMessage: "There was a timeout, try again",
8 | });
9 |
10 | DEFAULT_AXIOS.interceptors.request.use(
11 | (config) => {
12 | const token = Cookies.get("auth-token");
13 | if (token) {
14 | config.headers.Authorization = `Bearer ${token}`;
15 | }
16 | return config;
17 | },
18 | (error) => {
19 | return Promise.reject(error);
20 | },
21 | );
22 |
23 | DEFAULT_AXIOS.interceptors.response.use(
24 | (response) => response,
25 | (error) => {
26 | if (error.response?.status === 401) {
27 | Cookies.remove("auth-token");
28 | window.location.href = "/";
29 | }
30 | return Promise.reject(error);
31 | },
32 | );
33 |
34 | export default DEFAULT_AXIOS;
35 |
--------------------------------------------------------------------------------
/apps/web-app/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ESNext",
6 | "useDefineForClassFields": true,
7 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
8 | "module": "ESNext",
9 | "skipLibCheck": true,
10 | "moduleResolution": "Node",
11 | "esModuleInterop": true,
12 |
13 | "allowImportingTsExtensions": true,
14 | "isolatedModules": true,
15 | "moduleDetection": "force",
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 |
19 | "strict": true,
20 | "noUnusedLocals": true,
21 | "noUnusedParameters": true,
22 | "noFallthroughCasesInSwitch": true,
23 | "noUncheckedSideEffectImports": true,
24 | "baseUrl": ".",
25 | "paths": {
26 | "@/*": ["./src/*"]
27 | }
28 | },
29 | "include": ["src"],
30 | "exclude": ["node_modules"]
31 | }
32 |
--------------------------------------------------------------------------------
/apps/web-app/src/util/content-utils.ts:
--------------------------------------------------------------------------------
1 | const truncateContext = (text: string, maxLength: number): string => {
2 | if (text.length <= maxLength) {
3 | return text;
4 | }
5 | return text.slice(0, maxLength) + "...";
6 | };
7 |
8 | const truncateText = (text: string, maxLength: number): string => {
9 | if (text.length <= maxLength) {
10 | return text;
11 | }
12 | const truncated = text.slice(0, maxLength);
13 | const lastSpaceIndex = truncated.lastIndexOf(" ");
14 | return truncated.slice(0, lastSpaceIndex) + "...";
15 | };
16 | const truncateTextWithEllipsis = (text: string, maxLength: number): string => {
17 | if (text.length <= maxLength) {
18 | return text;
19 | }
20 | const truncated = text.slice(0, maxLength);
21 | const lastSpaceIndex = truncated.lastIndexOf(" ");
22 | return truncated.slice(0, lastSpaceIndex) + "...";
23 | };
24 |
25 | export { truncateContext, truncateText, truncateTextWithEllipsis };
26 |
--------------------------------------------------------------------------------
/apps/extension/src/content.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { Toaster, toast } from "sonner";
4 | import TipGithubUser from "./components/Views/tipGithubUser";
5 |
6 | const ID = ".user-profile-bio";
7 |
8 | console.log("Potatoe squeezed communicating successfully with Github");
9 | if (document.location.hostname === "github.com") {
10 | const formElement = document.querySelector(ID);
11 | if (formElement) {
12 | const App = () => {
13 | return ;
14 | };
15 |
16 | const container = document.createElement("div");
17 | formElement.appendChild(container);
18 |
19 | const root = ReactDOM.createRoot(container);
20 | root.render(
21 | <>
22 |
23 |
24 | >,
25 | );
26 | } else {
27 | console.error(`Element with ID "${ID}" not found.`);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/apps/server/src/middleware/auth.ts:
--------------------------------------------------------------------------------
1 | import { Context, Next } from 'hono';
2 | import { verify } from 'hono/jwt';
3 | import type { Env } from '../types/env';
4 |
5 | export const auth = async (c: Context<{ Bindings: Env }>, next: Next) => {
6 | const authHeader = c.req.header('Authorization');
7 |
8 | if (!authHeader || !authHeader.startsWith('Bearer ')) {
9 | return c.json({ error: 'Unauthorized' }, 401);
10 | }
11 |
12 | const token = authHeader.split(' ')[1];
13 | console.log('Received Token:', token);
14 |
15 | try {
16 | const payload = await verify(token, c.env.JWT_SECRET);
17 | console.log('Decoded Payload:', payload);
18 | c.set('user', payload);
19 | c.set('userId', payload.userId || payload.sub); // Ensure userId is set correctly
20 | await next();
21 | } catch (error) {
22 | console.error('Token Verification Error:', error);
23 | return c.json({ error: 'Invalid token', details: error.message }, 401);
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/apps/github-bot/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "github-bot",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "Reward contributors from PR and comment with 🍟 potatoe bot",
6 | "author": "obiabo",
7 | "license": "ISC",
8 | "homepage": "https://github.com//",
9 | "keywords": [
10 | "probot",
11 | "github",
12 | "probot-app"
13 | ],
14 | "scripts": {
15 | "build": "tsc",
16 | "start": "probot run ./src/index.ts",
17 | "start:dev": "ts-node-esm --loader ts-node/esm ./src/index.ts",
18 | "dev": "nodemon",
19 | "test": "vitest"
20 | },
21 | "dependencies": {
22 | "nodemon": "^3.1.9",
23 | "prettier": "^3.5.3",
24 | "probot": "^13.0.1"
25 | },
26 | "devDependencies": {
27 | "@types/node": "^20.0.0",
28 | "nock": "^14.0.0-beta.5",
29 | "smee-client": "^2.0.0",
30 | "typescript": "^5.3.3",
31 | "vitest": "^1.3.1"
32 | },
33 | "engines": {
34 | "node": ">= 18"
35 | },
36 | "type": "module"
37 | }
38 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | import { useTheme } from "next-themes";
2 | import { Toaster as Sonner, ToasterProps } from "sonner";
3 |
4 | const Toaster = ({ ...props }: ToasterProps) => {
5 | const { theme = "system" } = useTheme();
6 |
7 | return (
8 |
24 | );
25 | };
26 |
27 | export { Toaster };
28 |
--------------------------------------------------------------------------------
/apps/web-app/src/sections/auth.tsx:
--------------------------------------------------------------------------------
1 | import Nav from "@/header/nav.astro";
2 | import AuthButton from "../button/auth";
3 |
4 | const Auth = () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
🍟
11 |
12 |
Continue with Github
13 |
14 |
15 |
16 | When life gives you potatoes, don't make chips—squeeze out Bitcoin
17 | instead! With PotatoSqueezy, get zapped for your awesome projects
18 | and make your birthdays even cooler!
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default Auth;
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report-🐞.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "Bug report \U0001F41E"
3 | about: Create a report to help us improve
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 |
28 | - OS: [e.g. iOS]
29 | - Browser [e.g. chrome, safari]
30 | - Version [e.g. 22]
31 |
32 | **Smartphone (please complete the following information):**
33 |
34 | - Device: [e.g. iPhone6]
35 | - OS: [e.g. iOS8.1]
36 | - Browser [e.g. stock browser, safari]
37 | - Version [e.g. 22]
38 |
39 | **Additional context**
40 | Add any other context about the problem here.
41 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6 | return (
7 |
18 | );
19 | }
20 |
21 | export { Input };
22 |
--------------------------------------------------------------------------------
/apps/web-app/src/header/dashboardHeader.tsx:
--------------------------------------------------------------------------------
1 | import { Settings } from "lucide-react";
2 | import Drawer from "@/components/popups/drawer";
3 | import SettingsDrawerView from "@/components/views/settings-drawer-view.tsx";
4 |
5 | function DashboardHeader() {
6 | return (
7 |
8 |
13 |
18 |
19 |
20 |
24 |
25 |
26 | }
27 | >
28 |
29 |
30 |
31 |
32 |
33 | );
34 | }
35 |
36 | export default DashboardHeader;
37 |
--------------------------------------------------------------------------------
/apps/web-app/src/services/transaction.service.ts:
--------------------------------------------------------------------------------
1 | import API_ENDPOINTS from "@/enums/API_ENUM";
2 | import ApiClient from "@/util/api";
3 |
4 | interface TransactionRecord {
5 | id: number;
6 | amount: number;
7 | senderAddress: string;
8 | senderId: number | null;
9 | recipientAddress: string;
10 | recipientId: number | null;
11 | txHash: string;
12 | note: string | null;
13 | createdAt: string;
14 | }
15 |
16 | class TransactionService {
17 | static async getTransactionRecords(): Promise {
18 | const response = await ApiClient.get(
19 | API_ENDPOINTS.TRANSACTION_RECORDS,
20 | );
21 | return response;
22 | }
23 |
24 | static async createTransactionRecord(
25 | data: Omit,
26 | ) {
27 | const response = await ApiClient.post(
28 | API_ENDPOINTS.TRANSACTION_RECORDS,
29 | data as any,
30 | );
31 | return response;
32 | }
33 | }
34 |
35 | export default TransactionService;
36 | export type { TransactionRecord };
37 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/extract-user-wallet.ts:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { UserService } from "@/services";
3 |
4 | const useExtractUserWallet = (userName: string) => {
5 | const [wallet, setWallet] = useState(null);
6 | const [isLoading, setIsLoading] = useState(true);
7 | const [error, setError] = useState(null);
8 |
9 | useEffect(() => {
10 | const fetchUserWallet = async () => {
11 | try {
12 | const users = await UserService.fetchAllPotatoeUsers();
13 | const user = users?.find((data: any) => {
14 | const userData = data?.users;
15 |
16 | return userData?.username === userName;
17 | });
18 | setWallet(user?.wallets || null);
19 | } catch (err) {
20 | setError("Failed to fetch user wallet");
21 | } finally {
22 | setIsLoading(false);
23 | }
24 | };
25 |
26 | fetchUserWallet();
27 | }, [userName]);
28 |
29 | return { wallet, isLoading, error };
30 | };
31 |
32 | export default useExtractUserWallet;
33 |
--------------------------------------------------------------------------------
/apps/extension/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "potato-squeezy",
3 | "private": true,
4 | "version": "1.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build"
9 | },
10 | "dependencies": {
11 | "@headlessui/react": "^2.2.0",
12 | "@solana/pay": "^0.2.5",
13 | "@supabase/supabase-js": "^2.47.10",
14 | "axios": "^1.7.9",
15 | "canvas-confetti": "^1.9.3",
16 | "lucid": "^2.21.0",
17 | "react": "^18.2.0",
18 | "react-dom": "^18.2.0",
19 | "react-icons": "^5.4.0",
20 | "sonner": "^1.7.1",
21 | "supabase": "^2.1.1"
22 | },
23 | "devDependencies": {
24 | "@types/react": "^18.0.26",
25 | "@types/react-dom": "^18.0.9",
26 | "@types/webextension-polyfill": "^0.10.0",
27 | "@vitejs/plugin-react": "^4.2.1",
28 | "autoprefixer": "^10.4.20",
29 | "daisyui": "^4.12.23",
30 | "postcss": "^8.4.49",
31 | "tailwindcss": "^3.4.17",
32 | "typescript": "~5.6.3",
33 | "vite": "^5.0.0",
34 | "vite-plugin-web-extension": "^4.0.0",
35 | "webextension-polyfill": "^0.10.0"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 O514B0
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/apps/web-app/src/button/connectWalletButton.tsx:
--------------------------------------------------------------------------------
1 | import { WalletMultiButton } from "@solana/wallet-adapter-react-ui";
2 | import { Wallet2 } from "lucide-react";
3 | import { ReactNode } from "react";
4 | import { useWallet } from "@solana/wallet-adapter-react";
5 |
6 | const ConnectWalletButton = ({ children }: { children: ReactNode }) => {
7 | const { connected } = useWallet();
8 | return (
9 |
25 | {!connected ? (
26 | <>
27 | Connect Wallet
28 | >
29 | ) : (
30 | "Connected"
31 | )}
32 |
33 | );
34 | };
35 |
36 | export default ConnectWalletButton;
37 |
--------------------------------------------------------------------------------
/apps/web-app/src/store/use-fetch-github-data.store.ts:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 | import { persist, devtools } from "zustand/middleware";
3 |
4 | interface GithubUser {
5 | id: number;
6 | login: string;
7 | avatar_url: string;
8 | html_url: string;
9 | name: string | null;
10 | email: string | null;
11 | bio: string | null;
12 | }
13 |
14 | interface GithubDataState {
15 | githubUser: GithubUser | null;
16 | setGithubUser: (user: GithubUser) => void;
17 | clearGithubUser: () => void;
18 | }
19 |
20 | export const useFetchGithubDataStore = create()(
21 | devtools(
22 | persist(
23 | (set) => ({
24 | githubUser: null,
25 | setGithubUser: (user) =>
26 | set({ githubUser: user }, false, "setGithubUser"),
27 | clearGithubUser: () =>
28 | set({ githubUser: null }, false, "clearGithubUser"),
29 | }),
30 | {
31 | name: "github-user-storage",
32 | partialize: (state) => ({
33 | githubUser: state.githubUser,
34 | }),
35 | },
36 | ),
37 | {
38 | name: "FetchGithubDataStore",
39 | enabled: process.env.NODE_ENV === "development",
40 | },
41 | ),
42 | );
43 |
--------------------------------------------------------------------------------
/apps/extension/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "{{chrome}}.manifest_version": 3,
3 | "{{firefox}}.manifest_version": 2,
4 | "description": "Turn potatoes into Bitcoin! Zap anyone on GitHub as sponsorship.",
5 | "icons": {
6 | "16": "icon/16.png",
7 | "32": "icon/32.png",
8 | "48": "icon/48.png",
9 | "96": "icon/96.png",
10 | "128": "icon/128.png"
11 | },
12 | "permissions": [
13 | "sidePanel",
14 | "identity",
15 | "cookies",
16 | "activeTab",
17 | "storage",
18 | "tabs"
19 | ],
20 | "{{chrome}}.action": {
21 | "default_popup": "src/popup.html"
22 | },
23 | "{{firefox}}.browser_action": {
24 | "default_popup": "src/popup.html"
25 | },
26 | "background": {
27 | "{{chrome}}.service_worker": "src/background.ts",
28 | "{{firefox}}.scripts": ["src/background.ts"]
29 | },
30 | "content_scripts": [
31 | {
32 | "matches": ["https://github.com/*"],
33 | "js": ["src/content.tsx"]
34 | }
35 | ],
36 | "{{chrome}}.side_panel": {
37 | "default_path": "src/popup.html"
38 | },
39 | "host_permissions": [
40 | "https://www.potatoesqueezy.xyz/*",
41 | "http://localhost:4321/*"
42 | ],
43 | "oauth2": {
44 | "client_id": "Ov23liATCckJVguF3n2G",
45 | "scopes": ["read:user"]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as AvatarPrimitive from "@radix-ui/react-avatar";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | function Avatar({
7 | className,
8 | ...props
9 | }: React.ComponentProps) {
10 | return (
11 |
19 | );
20 | }
21 |
22 | function AvatarImage({
23 | className,
24 | ...props
25 | }: React.ComponentProps) {
26 | return (
27 |
32 | );
33 | }
34 |
35 | function AvatarFallback({
36 | className,
37 | ...props
38 | }: React.ComponentProps) {
39 | return (
40 |
48 | );
49 | }
50 |
51 | export { Avatar, AvatarImage, AvatarFallback };
52 |
--------------------------------------------------------------------------------
/apps/web-app/src/button/auth.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import supabase from "@/lib/supabase";
3 | import useAuth from "@/hooks/useAuth";
4 | function AuthButton() {
5 | const webSupabase = supabase;
6 | const { session, loading, error } = useAuth();
7 |
8 | useEffect(() => {
9 | if (session && typeof window !== "undefined") {
10 | window.location.href = "/app";
11 | }
12 | }, [session]);
13 |
14 | const signInWithGithub = async () => {
15 | try {
16 | const { error } = await webSupabase.auth.signInWithOAuth({
17 | provider: "github",
18 | });
19 |
20 | if (error) {
21 | console.error("Error signing in:", error.message);
22 | alert("Failed to sign in. Please try again.");
23 | }
24 | } catch (err) {
25 | console.error("Unexpected error during sign-in:", err);
26 | alert("An unexpected error occurred. Please try again.");
27 | }
28 | };
29 |
30 | if (loading) return Loading...
;
31 | if (error) return Error: {error.message}
;
32 |
33 | return (
34 |
35 |
39 | Sign in with Github
40 |
41 |
42 | );
43 | }
44 |
45 | export default AuthButton;
46 |
--------------------------------------------------------------------------------
/apps/web-app/src/button/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | type TVariant =
4 | | "primary"
5 | | "secondary"
6 | | "danger"
7 | | "default"
8 | | "success"
9 | | "warning";
10 |
11 | interface IProps {
12 | children: React.ReactNode;
13 | className?: string;
14 | variant?: TVariant;
15 | onClick?: () => void;
16 | type?: "button" | "submit" | "reset";
17 | disabled?: boolean;
18 | }
19 |
20 | const DefaultButton: React.FC = ({
21 | children,
22 | className = "",
23 | variant = "primary",
24 | onClick,
25 | type = "button",
26 | disabled = false,
27 | }): React.ReactNode => {
28 | const variantStyles: Record = {
29 | primary: "bg-orange-500 text-white hover:bg-orange-600",
30 | secondary: "bg-gray-500 text-white hover:bg-gray-600",
31 | danger: "bg-red-500 text-white hover:bg-red-600",
32 | default:
33 | "bg-[#212830] border-[#80808054] border text-white hover:bg-red-600",
34 | };
35 |
36 | return (
37 |
45 | {children}
46 |
47 | );
48 | };
49 |
50 | export default DefaultButton;
51 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/fallbacks/wallet-no-connect.tsx:
--------------------------------------------------------------------------------
1 | import { WalletSvg } from "@/assets/svg";
2 | import ConnectWalletButton from "@/button/connectWalletButton";
3 | import React from "react";
4 | import { motion } from "framer-motion";
5 |
6 | const WalletNotConnected = () => {
7 | return (
8 |
14 |
19 |
20 |
21 |
27 | Wallet Not Connected
28 |
29 |
35 | Connect Wallet
36 |
37 |
38 | );
39 | };
40 |
41 | export default WalletNotConnected;
42 |
--------------------------------------------------------------------------------
/apps/web-app/store/user.ts:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 | import { persist } from "zustand/middleware";
3 |
4 | interface UserData {
5 | id: number;
6 | githubId: string;
7 | username: string;
8 | email: string | null;
9 | name: string;
10 | avatarUrl: string;
11 | createdAt: string;
12 | updatedAt: string;
13 | }
14 |
15 | interface WalletData {
16 | id: number;
17 | userId: number;
18 | address: string;
19 | createdAt: string;
20 | updatedAt: string;
21 | }
22 |
23 | interface UserState {
24 | users: UserData | null;
25 | wallets: WalletData | null;
26 | isAuthenticated: boolean;
27 | setUserData: (data: { users: UserData; wallets: WalletData }) => void;
28 | updateWallet: (wallet: WalletData) => void;
29 | clearUserData: () => void;
30 | }
31 |
32 | const initialState = {
33 | users: null,
34 | wallets: null,
35 | isAuthenticated: false,
36 | };
37 |
38 | export const useUserStore = create()(
39 | persist(
40 | (set) => ({
41 | ...initialState,
42 |
43 | setUserData: (data) =>
44 | set({
45 | users: data.users,
46 | wallets: data.wallets,
47 | isAuthenticated: true,
48 | }),
49 |
50 | updateWallet: (wallet) => set((state) => ({ ...state, wallets: wallet })),
51 |
52 | clearUserData: () => set(initialState),
53 | }),
54 | {
55 | name: "user-storage",
56 | },
57 | ),
58 | );
59 |
--------------------------------------------------------------------------------
/apps/web-app/src/header/nav.tsx:
--------------------------------------------------------------------------------
1 | const Nav = () => {
2 | return (
3 |
4 |
33 |
34 | );
35 | };
36 |
37 | export default Nav;
38 |
--------------------------------------------------------------------------------
/apps/web-app/src/providers/walletConnectProvider.tsx:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react";
2 | import { clusterApiUrl } from "@solana/web3.js";
3 | import {
4 | ConnectionProvider,
5 | WalletProvider,
6 | } from "@solana/wallet-adapter-react";
7 | import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
8 | import {
9 | CoinbaseWalletAdapter,
10 | PhantomWalletAdapter,
11 | SolflareWalletAdapter,
12 | TorusWalletAdapter,
13 | } from "@solana/wallet-adapter-wallets";
14 | import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
15 | import "@solana/wallet-adapter-react-ui/styles.css";
16 | import { ReactNode } from "@tanstack/react-router";
17 |
18 | const WalletAdapterProvider = ({ children }: { children: ReactNode }) => {
19 | const network = WalletAdapterNetwork.Mainnet;
20 |
21 | const endpoint = useMemo(() => clusterApiUrl(network), [network]);
22 |
23 | const wallets = useMemo(
24 | () => [
25 | new PhantomWalletAdapter(),
26 | new CoinbaseWalletAdapter(),
27 | new SolflareWalletAdapter({ network }),
28 | new TorusWalletAdapter(),
29 | ],
30 | [network],
31 | );
32 |
33 | return (
34 |
35 |
36 | {children}
37 |
38 |
39 | );
40 | };
41 |
42 | export default WalletAdapterProvider;
43 |
--------------------------------------------------------------------------------
/apps/web-app/src/util/api.ts:
--------------------------------------------------------------------------------
1 | import DEFAULT_AXIOS from "@/configs/axios";
2 | import { BASE_API_URL } from "@/constant";
3 |
4 | class ApiClient {
5 | private static readonly baseUrl = BASE_API_URL;
6 | private static readonly axiosInstance = DEFAULT_AXIOS;
7 |
8 | static async get(
9 | path: string,
10 | params?: Record,
11 | ): Promise {
12 | const response = await this.axiosInstance.get(`${this.baseUrl}${path}`, {
13 | params,
14 | });
15 | return response.data;
16 | }
17 |
18 | static async post(
19 | path: string,
20 | data?: Record,
21 | ): Promise {
22 | const response = await this.axiosInstance.post(
23 | `${this.baseUrl}${path}`,
24 | data,
25 | );
26 | return response.data;
27 | }
28 |
29 | static async put(path: string, data?: Record): Promise {
30 | const response = await this.axiosInstance.put(
31 | `${this.baseUrl}${path}`,
32 | data,
33 | );
34 | return response.data;
35 | }
36 |
37 | static async delete(path: string): Promise {
38 | const response = await this.axiosInstance.delete(`${this.baseUrl}${path}`);
39 | return response.data;
40 | }
41 |
42 | static async patch(path: string): Promise {
43 | const response = await this.axiosInstance.patch(`${this.baseUrl}${path}`);
44 | return response.data;
45 | }
46 | }
47 |
48 | export default ApiClient;
49 |
--------------------------------------------------------------------------------
/apps/web-app/src/misc/github_users_card.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import DefaultButton from "../button";
3 | import Button from "../button";
4 |
5 | interface IGithubUserCardProps {
6 | user_avatar_url: string;
7 | user_name: string;
8 | github_username: string;
9 | html_url?: string;
10 | }
11 |
12 | function GithubUsersCard({
13 | user_name,
14 | user_avatar_url,
15 | github_username,
16 | html_url,
17 | }: IGithubUserCardProps) {
18 | const goToProfile = () => {
19 | if (typeof window !== "undefined") {
20 | window.location.href = `/app/profile?user=${github_username}`;
21 | }
22 | };
23 |
24 | return (
25 |
32 |
37 |
38 |
{user_name}
39 |
@{github_username}
40 |
41 |
42 | 🍟 Zap User ⚡
43 |
44 |
45 | );
46 | }
47 |
48 | export default GithubUsersCard;
49 |
--------------------------------------------------------------------------------
/apps/web-app/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/apps/web-app/src/sections/hero.tsx:
--------------------------------------------------------------------------------
1 | import Nav from "@/header/nav.astro";
2 |
3 | const Hero = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
🍟
10 |
11 |
12 | Coming Soon 🥳🥳
13 |
14 |
15 | GitHub Just Got Starchier — Turn Potatoes into Bitcoin!
16 |
17 |
18 |
19 |
20 | When life gives you potatoes, don't make chips—squeeze out Bitcoin
21 | instead! With PotatoSqueezy, get zapped for your awesome projects
22 | and make your birthdays even cooler!
23 |
24 |
25 |
26 |
31 |
32 | Get Start
33 |
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default Hero;
42 |
--------------------------------------------------------------------------------
/apps/web-app/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 | import tailwindcss from "@tailwindcss/vite";
3 | import react from "@vitejs/plugin-react";
4 | import { defineConfig } from "vite";
5 | import { TanStackRouterVite } from "@tanstack/router-plugin/vite";
6 | import { nodePolyfills } from "vite-plugin-node-polyfills";
7 | import { VitePWA } from "vite-plugin-pwa";
8 |
9 | // https://vite.dev/config/
10 | export default defineConfig({
11 | root: ".",
12 | plugins: [
13 | TanStackRouterVite({ target: "react", autoCodeSplitting: true }),
14 | react({
15 | babel: {
16 | plugins: [
17 | [
18 | "@locator/babel-jsx/dist",
19 | {
20 | env: "development",
21 | },
22 | ],
23 | ],
24 | },
25 | }),
26 | VitePWA({
27 | registerType: "autoUpdate",
28 | injectRegister: "auto",
29 |
30 | devOptions: {
31 | enabled: true,
32 | },
33 | manifest: {
34 | name: "Sui Bulma",
35 | short_name: "Sui Bulma",
36 | description:
37 | "SUI Bulma - A Bitcoin-centric DeFi platform on Sui, enabling lending, stablecoins, perps, and synthetic assets to enhance BTC utility",
38 | theme_color: "#0B0B14",
39 | },
40 | }),
41 | nodePolyfills(),
42 | tailwindcss(),
43 | ],
44 | resolve: {
45 | alias: {
46 | "@": path.resolve(__dirname, "./src"),
47 | },
48 | },
49 | define: {
50 | global: "globalThis",
51 | },
52 | });
53 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/useSolanaTip.ts:
--------------------------------------------------------------------------------
1 | import { useWallet } from "@solana/wallet-adapter-react";
2 | import {
3 | LAMPORTS_PER_SOL,
4 | PublicKey,
5 | Transaction,
6 | SystemProgram,
7 | } from "@solana/web3.js";
8 | import { useState } from "react";
9 | import { toast } from "sonner";
10 |
11 | export function useSolanaTip() {
12 | const { publicKey, sendTransaction } = useWallet();
13 | const [isSending, setIsSending] = useState(false);
14 |
15 | const sendTip = async (recipientWalletAddress: string) => {
16 | if (!publicKey || !recipientWalletAddress) {
17 | toast.error("Please connect your wallet first");
18 | return;
19 | }
20 |
21 | try {
22 | setIsSending(true);
23 | const recipientAddress = new PublicKey(recipientWalletAddress);
24 | const transaction = new Transaction().add(
25 | SystemProgram.transfer({
26 | fromPubkey: publicKey,
27 | toPubkey: recipientAddress,
28 | lamports: 0.1 * LAMPORTS_PER_SOL,
29 | }),
30 | );
31 |
32 | const signature = await sendTransaction(transaction, connection);
33 | await connection.confirmTransaction(signature, "confirmed");
34 |
35 | toast.success("Tip sent successfully! 🎉");
36 | } catch (error) {
37 | console.error("Error sending tip:", error);
38 | toast.error("Failed to send tip");
39 | } finally {
40 | setIsSending(false);
41 | }
42 | };
43 |
44 | return {
45 | sendTip,
46 | isSending,
47 | isWalletConnected: !!publicKey,
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/apps/web-app/src/hooks/useAuth.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import Cookies from "js-cookie";
3 | import { useUserStore } from "@/store/user.store";
4 | import { useNavigate } from "@tanstack/react-router";
5 |
6 | function useAuth() {
7 | const [isAuthenticated, setIsAuthenticated] = useState(() => {
8 | const token = Cookies.get("auth-token");
9 | return !!token;
10 | });
11 |
12 | const { user, clearUser } = useUserStore();
13 | const navigate = useNavigate();
14 |
15 | useEffect(() => {
16 | const token = Cookies.get("auth-token");
17 | if (!token && isAuthenticated) {
18 | handleLogout();
19 | }
20 | }, [isAuthenticated]);
21 |
22 | const handleLogin = (token: string) => {
23 | Cookies.set("auth-token", token, {
24 | secure: true,
25 | sameSite: "lax",
26 | expires: 7,
27 | });
28 | setIsAuthenticated(true);
29 | };
30 |
31 | const handleLogout = () => {
32 | Cookies.remove("auth-token");
33 | clearUser();
34 | setIsAuthenticated(false);
35 | navigate({ to: "/" });
36 | };
37 |
38 | const checkAuthStatus = () => {
39 | const token = Cookies.get("auth-token");
40 | const isValid = !!token;
41 | setIsAuthenticated(isValid);
42 |
43 | if (!isValid) {
44 | handleLogout();
45 | return false;
46 | }
47 | return true;
48 | };
49 |
50 | return {
51 | isAuthenticated,
52 | user,
53 | login: handleLogin,
54 | logout: handleLogout,
55 | checkAuthStatus,
56 | };
57 | }
58 |
59 | export default useAuth;
60 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/profile/profilePanel.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from "framer-motion";
2 | import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
3 | import Typography from "../typography";
4 | interface ProfilePanelProps {
5 | name: string;
6 | username: string;
7 | withAction?: boolean;
8 | onUpdateAddress?: () => void;
9 | avatar: string;
10 | userBio?: string;
11 | walletAddress?: string;
12 | }
13 |
14 | function ProfilePanel({
15 | name,
16 | username,
17 | withAction = false,
18 | avatar,
19 | userBio = "",
20 | }: ProfilePanelProps) {
21 | return (
22 |
23 |
28 |
29 |
30 |
31 |
32 |
33 | {username?.slice(0, 2).toUpperCase()}
34 |
35 |
36 |
{name}
37 |
38 | @{username}
39 |
40 |
41 |
{userBio}
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
49 | export default ProfilePanel;
50 |
--------------------------------------------------------------------------------
/apps/server/src/routes/user.ts:
--------------------------------------------------------------------------------
1 | import { Hono } from 'hono';
2 | import { db } from '../db';
3 | import { UserWithWallets } from '../types';
4 | import { auth } from '../middleware/auth';
5 | import type { Env } from '../types/env';
6 | import { eq } from 'drizzle-orm';
7 | import { users, wallets } from '../db/schema';
8 | const userRoute = new Hono<{ Bindings: Env }>();
9 |
10 | userRoute.get('/profile', auth, async (c) => {
11 | try {
12 | const userId = c.get('userId');
13 |
14 | const user = await db
15 | .select()
16 | .from(users)
17 | .where(eq(users.id, parseInt(userId)))
18 | .leftJoin(wallets, eq(users.id, wallets.userId))
19 | .execute();
20 |
21 | if (!user || user.length === 0) {
22 | return c.json({ error: 'User not found' }, 404);
23 | }
24 |
25 | return c.json(user[0]);
26 | } catch (error) {
27 | console.error('Error fetching user profile:', error);
28 | return c.json({ error: 'Internal server error' }, 500);
29 | }
30 | });
31 |
32 | userRoute.get('/all', async (c) => {
33 | try {
34 | const usersList = await db
35 | .select()
36 | .from(users)
37 | .leftJoin(wallets, eq(users.id, wallets.userId))
38 | .execute();
39 |
40 | if (!usersList || usersList.length === 0) {
41 | return c.json({ error: 'No users found' }, 404);
42 | }
43 |
44 | return c.json(usersList);
45 | } catch (error) {
46 | console.error('Error fetching all users:', error);
47 | return c.json({ error: 'Internal server error' }, 500);
48 | }
49 | });
50 |
51 | export { userRoute };
52 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/popups/modals/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AlertDialog,
3 | AlertDialogAction,
4 | AlertDialogCancel,
5 | AlertDialogContent,
6 | AlertDialogFooter,
7 | AlertDialogHeader,
8 | AlertDialogTitle,
9 | AlertDialogTrigger,
10 | } from "@/components/ui/alert-dialog";
11 | import { X } from "lucide-react";
12 |
13 | interface IModalLayout {
14 | trigger?: React.ReactNode;
15 | children: React.ReactNode;
16 | title: string;
17 | onClose?: () => void;
18 | open?: boolean;
19 | closeOnOverlayClick?: boolean;
20 | }
21 |
22 | const ModalLayout = ({
23 | trigger,
24 | children,
25 | title,
26 | onClose,
27 | open,
28 | closeOnOverlayClick = true,
29 | }: IModalLayout) => {
30 | return (
31 |
32 | {trigger && {trigger} }
33 | {
35 | if (!closeOnOverlayClick) {
36 | e.preventDefault();
37 | }
38 | }}
39 | >
40 |
41 |
42 |
{title}
43 | {(closeOnOverlayClick || !open) && (
44 |
45 |
46 |
47 | )}
48 |
49 | {children}
50 |
51 |
52 |
53 | );
54 | };
55 |
56 | export default ModalLayout;
57 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/misc/product-hunt-badge.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ProductHuntBadge: React.FC = () => {
4 | return (
5 |
33 | );
34 | };
35 |
36 | export default ProductHuntBadge;
37 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/search/GithubUserSearch.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { SearchIcon, Loader2 } from "lucide-react";
3 | import { Button } from "../ui/button";
4 | import { Input } from "../ui/input";
5 |
6 | interface GithubUserSearchProps {
7 | searchQuery: string;
8 | onSearchChange: (value: string) => void;
9 | onSearch: () => void;
10 | loading: boolean;
11 | }
12 |
13 | export function GithubUserSearch({
14 | searchQuery,
15 | onSearchChange,
16 | onSearch,
17 | loading,
18 | }: GithubUserSearchProps) {
19 | const handleKeyPress = (e: React.KeyboardEvent) => {
20 | if (e.key === "Enter") {
21 | onSearch();
22 | }
23 | };
24 |
25 | return (
26 |
27 |
28 |
29 | onSearchChange(e.target.value)}
33 | onKeyPress={handleKeyPress}
34 | placeholder="Search GitHub username..."
35 | className="w-full !p-5 bg-white/5 rounded-xl pl-11 border border-white/10
36 | focus:outline-none focus:ring-2 transition-all"
37 | />
38 |
39 |
40 | {loading ? (
41 |
42 |
43 | Searching...
44 |
45 | ) : (
46 | "Search"
47 | )}
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/libs/common/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "common-example",
3 | "homepage": ".",
4 | "version": "0.0.0",
5 | "private": true,
6 | "scripts": {
7 | "start": "node ../node_modules/react-scripts/bin/react-scripts.js start",
8 | "build": "node ../node_modules/react-scripts/bin/react-scripts.js build",
9 | "test": "node ../node_modules/react-scripts/bin/react-scripts.js test",
10 | "eject": "node ../node_modules/react-scripts/bin/react-scripts.js eject"
11 | },
12 | "dependencies": {
13 | "@testing-library/jest-dom": "link:../node_modules/@testing-library/jest-dom",
14 | "@testing-library/react": "link:../node_modules/@testing-library/react",
15 | "@testing-library/user-event": "link:../node_modules/@testing-library/user-event",
16 | "@types/jest": "link:../node_modules/@types/jest",
17 | "@types/node": "link:../node_modules/@types/node",
18 | "@types/react": "link:../node_modules/@types/react",
19 | "@types/react-dom": "link:../node_modules/@types/react-dom",
20 | "react": "link:../node_modules/react",
21 | "react-dom": "link:../node_modules/react-dom",
22 | "react-scripts": "link:../node_modules/react-scripts",
23 | "typescript": "link:../node_modules/typescript",
24 | "common": "link:.."
25 | },
26 | "devDependencies": {
27 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3"
28 | },
29 | "eslintConfig": {
30 | "extends": "react-app"
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/apps/github-bot/test/fixtures/mock-cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEAli7V49NdZe+XYC1pLaHM0te8kiDmZBJ1u2HJHN8GdbROB6NO
3 | VpC3xK7NxQn6xpvZ9ux20NvcDvGle+DOptZztBH+np6h2jZQ1/kD1yG1eQvVH4th
4 | /9oqHuIjmIfO8lIe4Hyd5Fw5xHkGqVETTGR+0c7kdZIlHmkOregUGtMYZRUi4YG+
5 | q0w+uFemiHpGKXbeCIAvkq7aIkisEzvPWfSyYdA6WJHpxFk7tD7D8VkzABLVRHCq
6 | AuyqPG39BhGZcGLXx5rGK56kDBJkyTR1t3DkHpwX+JKNG5UYNwOG4LcQj1fteeta
7 | TdkYUMjIyWbanlMYyC+dq7B5fe7el99jXQ1gXwIDAQABAoIBADKfiPOpzKLOtzzx
8 | MbHzB0LO+75aHq7+1faayJrVxqyoYWELuB1P3NIMhknzyjdmU3t7S7WtVqkm5Twz
9 | lBUC1q+NHUHEgRQ4GNokExpSP4SU63sdlaQTmv0cBxmkNarS6ZuMBgDy4XoLvaYX
10 | MSUf/uukDLhg0ehFS3BteVFtdJyllhDdTenF1Nb1rAeN4egt8XLsE5NQDr1szFEG
11 | xH5lb+8EDtzgsGpeIddWR64xP0lDIKSZWst/toYKWiwjaY9uZCfAhvYQ1RsO7L/t
12 | sERmpYgh+rAZUh/Lr98EI8BPSPhzFcSHmtqzzejvC5zrZPHcUimz0CGA3YBiLoJX
13 | V1OrxmECgYEAxkd8gpmVP+LEWB3lqpSvJaXcGkbzcDb9m0OPzHUAJDZtiIIf0UmO
14 | nvL68/mzbCHSj+yFjZeG1rsrAVrOzrfDCuXjAv+JkEtEx0DIevU1u60lGnevOeky
15 | r8Be7pmymFB9/gzQAd5ezIlTv/COgoO986a3h1yfhzrrzbqSiivw308CgYEAwecI
16 | aZZwqH3GifR+0+Z1B48cezA5tC8LZt5yObGzUfxKTWy30d7lxe9N59t0KUVt/QL5
17 | qVkd7mqGzsUMyxUN2U2HVnFTWfUFMhkn/OnCnayhILs8UlCTD2Xxoy1KbQH/9FIr
18 | xf0pbMNJLXeGfyRt/8H+BzSZKBw9opJBWE4gqfECgYBp9FdvvryHuBkt8UQCRJPX
19 | rWsRy6pY47nf11mnazpZH5Cmqspv3zvMapF6AIxFk0leyYiQolFWvAv+HFV5F6+t
20 | Si1mM8GCDwbA5zh6pEBDewHhw+UqMBh63HSeUhmi1RiOwrAA36CO8i+D2Pt+eQHv
21 | ir52IiPJcs4BUNrv5Q1BdwKBgBHgVNw3LGe8QMOTMOYkRwHNZdjNl2RPOgPf2jQL
22 | d/bFBayhq0jD/fcDmvEXQFxVtFAxKAc+2g2S8J67d/R5Gm/AQAvuIrsWZcY6n38n
23 | pfOXaLt1x5fnKcevpFlg4Y2vM4O416RHNLx8PJDehh3Oo/2CSwMrDDuwbtZAGZok
24 | icphAoGBAI74Tisfn+aeCZMrO8KxaWS5r2CD1KVzddEMRKlJvSKTY+dOCtJ+XKj1
25 | OsZdcDvDC5GtgcywHsYeOWHldgDWY1S8Z/PUo4eK9qBXYBXp3JEZQ1dqzFdz+Txi
26 | rBn2WsFLsxV9j2/ugm0PqWVBcU2bPUCwvaRu3SOms2teaLwGCkhr
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/libs/common/example/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
16 |
17 |
18 |
27 | common
28 |
29 |
30 |
31 | You need to enable JavaScript to run this app.
32 |
33 |
34 |
35 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const badgeVariants = cva(
8 | "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
14 | secondary:
15 | "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
16 | destructive:
17 | "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
18 | outline:
19 | "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
20 | },
21 | },
22 | defaultVariants: {
23 | variant: "default",
24 | },
25 | },
26 | );
27 |
28 | function Badge({
29 | className,
30 | variant,
31 | asChild = false,
32 | ...props
33 | }: React.ComponentProps<"span"> &
34 | VariantProps & { asChild?: boolean }) {
35 | const Comp = asChild ? Slot : "span";
36 |
37 | return (
38 |
43 | );
44 | }
45 |
46 | export { Badge, badgeVariants };
47 |
--------------------------------------------------------------------------------
/apps/web-app/src/dashboard/dashboardBottomTab.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import { DASHBOARDNAV } from "@/data/dashboardData.ts";
3 | import { Link, useRouter } from "@tanstack/react-router";
4 |
5 | function DashboardBottomTab() {
6 | const router = useRouter();
7 | const [currentPath, setCurrentPath] = useState("");
8 |
9 | useEffect(() => {
10 | setCurrentPath(window.location.pathname);
11 | }, []);
12 |
13 | return (
14 |
15 |
19 | {DASHBOARDNAV.map((item) => {
20 | const isActive = currentPath === item.path;
21 |
22 | return (
23 |
37 |
38 | {item.icon}
39 |
40 |
41 | {item.title}
42 |
43 |
44 | );
45 | })}
46 |
47 |
48 | );
49 | }
50 |
51 | export default DashboardBottomTab;
52 |
--------------------------------------------------------------------------------
/apps/web-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 | Potatoe - Support GitHub Contributors with Crypto
12 |
16 |
20 |
24 |
28 |
29 |
30 |
31 |
36 |
37 |
41 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | import { cn } from "@/lib/utils";
4 |
5 | function Card({ className, ...props }: React.ComponentProps<"div">) {
6 | return (
7 |
15 | );
16 | }
17 |
18 | function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19 | return (
20 |
25 | );
26 | }
27 |
28 | function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
29 | return (
30 |
35 | );
36 | }
37 |
38 | function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
39 | return (
40 |
45 | );
46 | }
47 |
48 | function CardContent({ className, ...props }: React.ComponentProps<"div">) {
49 | return (
50 |
55 | );
56 | }
57 |
58 | function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
59 | return (
60 |
65 | );
66 | }
67 |
68 | export {
69 | Card,
70 | CardHeader,
71 | CardFooter,
72 | CardTitle,
73 | CardDescription,
74 | CardContent,
75 | };
76 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as TabsPrimitive from "@radix-ui/react-tabs";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | function Tabs({
7 | className,
8 | ...props
9 | }: React.ComponentProps) {
10 | return (
11 |
16 | );
17 | }
18 |
19 | function TabsList({
20 | className,
21 | ...props
22 | }: React.ComponentProps) {
23 | return (
24 |
32 | );
33 | }
34 |
35 | function TabsTrigger({
36 | className,
37 | ...props
38 | }: React.ComponentProps) {
39 | return (
40 |
48 | );
49 | }
50 |
51 | function TabsContent({
52 | className,
53 | ...props
54 | }: React.ComponentProps) {
55 | return (
56 |
61 | );
62 | }
63 |
64 | export { Tabs, TabsList, TabsTrigger, TabsContent };
65 |
--------------------------------------------------------------------------------
/apps/web-app/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import { createFileRoute } from "@tanstack/react-router";
2 | import Homepage from "@/components/pages/homepage";
3 | import { useEffect } from "react";
4 | import Cookies from "js-cookie";
5 | import { useProfile } from "@/hooks/useProfile";
6 | import ProductHuntBadge from "@/components/misc/product-hunt-badge";
7 |
8 | export const Route = createFileRoute("/")({
9 | validateSearch: (search: Record) => {
10 | return {
11 | token: search.token as string | undefined,
12 | };
13 | },
14 | component: IndexRoute,
15 | });
16 |
17 | function IndexRoute() {
18 | const { token } = Route.useSearch();
19 | useProfile();
20 |
21 | useEffect(() => {
22 | if (token) {
23 | Cookies.set("auth-token", token, {
24 | secure: true,
25 | sameSite: "lax",
26 | expires: 7,
27 | });
28 | }
29 | }, [token]);
30 |
31 | return (
32 | <>
33 |
34 |
35 |
39 | {/* */}
44 |
45 |
60 | >
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/apps/server/src/routes/wallets.ts:
--------------------------------------------------------------------------------
1 | import { Hono } from 'hono';
2 | import { eq } from 'drizzle-orm';
3 | import { db } from '../db';
4 | import { wallets } from '../db/schema';
5 |
6 | const walletsRoute = new Hono();
7 |
8 | walletsRoute.post('/', async (c) => {
9 | try {
10 | const { userId, address } = await c.req.json();
11 |
12 | if (!userId || !address) {
13 | return c.json({ error: 'User ID and wallet address are required' }, 400);
14 | }
15 |
16 | const newWallet = await db
17 | .insert(wallets)
18 | .values({
19 | userId,
20 | address,
21 | updatedAt: new Date(),
22 | })
23 | .returning();
24 |
25 | return c.json(newWallet[0]);
26 | } catch (error) {
27 | console.error('Error adding wallet:', error);
28 | return c.json({ error: 'Internal server error' }, 500);
29 | }
30 | });
31 |
32 | walletsRoute.put('/', async (c) => {
33 | try {
34 | const { walletId, address } = await c.req.json();
35 | if (!walletId || !address) {
36 | return c.json({ error: 'Wallet ID and address are required' }, 400);
37 | }
38 | const updatedWallet = await db
39 | .update(wallets)
40 | .set({
41 | address,
42 | updatedAt: new Date(),
43 | })
44 | .where(eq(wallets.id, walletId))
45 | .returning();
46 | return c.json(updatedWallet[0]);
47 | } catch (error) {
48 | console.error('Error updating wallet:', error);
49 | return c.json({ error: 'Internal server error' }, 500);
50 | }
51 | });
52 |
53 | walletsRoute.get('/user/:userId', async (c) => {
54 | try {
55 | const userId = parseInt(c.req.param('userId'));
56 | const userWallets = await db.query.wallets.findMany({
57 | where: eq(wallets.userId, userId),
58 | });
59 |
60 | return c.json(userWallets);
61 | } catch (error) {
62 | console.error('Error fetching wallets:', error);
63 | return c.json({ error: 'Internal server error' }, 500);
64 | }
65 | });
66 |
67 | export default walletsRoute;
68 |
--------------------------------------------------------------------------------
/apps/server/src/db/schema.ts:
--------------------------------------------------------------------------------
1 | import {
2 | pgTable,
3 | serial,
4 | text,
5 | timestamp,
6 | integer,
7 | numeric,
8 | } from 'drizzle-orm/pg-core';
9 | import { relations } from 'drizzle-orm';
10 |
11 | export const users = pgTable('users', {
12 | id: serial('id').primaryKey(),
13 | githubId: text('github_id').notNull().unique(),
14 | username: text('username').notNull(),
15 | email: text('email'),
16 | name: text('name'),
17 | avatarUrl: text('avatar_url'),
18 | createdAt: timestamp('created_at').defaultNow(),
19 | updatedAt: timestamp('updated_at').defaultNow(),
20 | });
21 |
22 | export const wallets = pgTable('wallets', {
23 | id: serial('id').primaryKey(),
24 | userId: integer('user_id')
25 | .notNull()
26 | .references(() => users.id),
27 | address: text('address').notNull(),
28 | createdAt: timestamp('created_at').defaultNow(),
29 | updatedAt: timestamp('updated_at').defaultNow(),
30 | });
31 |
32 | export const transactionRecords = pgTable('transaction_records', {
33 | id: serial('id').primaryKey(),
34 | amount: numeric('amount').notNull(),
35 | senderAddress: text('sender_address').notNull(),
36 | senderId: integer('sender_id').references(() => users.id),
37 | recipientAddress: text('recipient_address').notNull(),
38 | recipientId: integer('recipient_id').references(() => users.id),
39 | txHash: text('tx_hash'),
40 | note: text('note'),
41 | createdAt: timestamp('created_at').defaultNow(),
42 | });
43 |
44 | export const walletsRelations = relations(wallets, ({ one }) => ({
45 | user: one(users, {
46 | fields: [wallets.userId],
47 | references: [users.id],
48 | }),
49 | }));
50 |
51 | export const transactionRecordsRelations = relations(
52 | transactionRecords,
53 | ({ one }) => ({
54 | sender: one(users, {
55 | fields: [transactionRecords.senderId],
56 | references: [users.id],
57 | }),
58 | recipient: one(users, {
59 | fields: [transactionRecords.recipientId],
60 | references: [users.id],
61 | }),
62 | }),
63 | );
64 |
--------------------------------------------------------------------------------
/apps/server/src/routes/tx-records.ts:
--------------------------------------------------------------------------------
1 | import { Hono } from 'hono';
2 | import { db } from '../db';
3 | import { transactionRecords, users } from '../db/schema';
4 | import { desc, eq } from 'drizzle-orm';
5 |
6 | const txRecordsRoute = new Hono();
7 |
8 | txRecordsRoute.get('/', async (c) => {
9 | try {
10 | const records = await db
11 | .select({
12 | id: transactionRecords.id,
13 | amount: transactionRecords.amount,
14 | senderAddress: transactionRecords.senderAddress,
15 | recipientAddress: transactionRecords.recipientAddress,
16 | createdAt: transactionRecords.createdAt,
17 | sender: {
18 | username: users.username,
19 | avatarUrl: users.avatarUrl,
20 | },
21 | })
22 | .from(transactionRecords)
23 | .leftJoin(users, eq(users.id, transactionRecords.senderId))
24 | .orderBy(desc(transactionRecords.createdAt))
25 | .limit(50);
26 |
27 | return c.json(records);
28 | } catch (error) {
29 | console.error('Error fetching transaction records:', error);
30 | return c.json({ error: 'Internal server error' }, 500);
31 | }
32 | });
33 |
34 | txRecordsRoute.post('/', async (c) => {
35 | try {
36 | const { amount, senderAddress, senderId, recipientAddress, recipientId } =
37 | await c.req.json();
38 |
39 | if (!amount || !senderAddress || !recipientAddress) {
40 | return c.json(
41 | {
42 | error: 'Amount, sender address, and recipient address are required',
43 | },
44 | 400,
45 | );
46 | }
47 |
48 | const newRecord = await db
49 | .insert(transactionRecords)
50 | .values({
51 | amount,
52 | senderAddress,
53 | senderId,
54 | recipientAddress,
55 | recipientId,
56 | })
57 | .returning();
58 |
59 | return c.json(newRecord[0]);
60 | } catch (error) {
61 | console.error('Error creating transaction record:', error);
62 | return c.json({ error: 'Internal server error' }, 500);
63 | }
64 | });
65 |
66 | export default txRecordsRoute;
67 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/explore/ExploreUsers.tsx:
--------------------------------------------------------------------------------
1 | import DefaultDashboard from "@/layouts/dashboard";
2 | import { motion } from "framer-motion";
3 | import { TAB_STATE } from "@/constant/index";
4 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
5 | import GeneralGithubUsers from "@/components/pages/explore/general-users";
6 | import PotatoeUsers from "@/components/pages/explore/potatoe-users";
7 |
8 | const tabs = [
9 | { value: "account", label: "Potatoe Users 🍟", content: },
10 | { value: "general", label: "General", content: },
11 | ];
12 |
13 | export default function ExploreUsers() {
14 | return (
15 |
16 |
21 |
22 |
23 |
24 | Find GitHub Users
25 |
26 |
27 | Search and tip your favorite GitHub contributors
28 |
29 |
30 |
31 |
32 |
33 | {tabs.map((tab) => (
34 |
39 | {tab.label}
40 |
41 | ))}
42 |
43 | {tabs.map((tab) => (
44 |
45 | {tab.content}
46 |
47 | ))}
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/apps/web-app/src/store/user.store.ts:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 | import { persist, devtools } from "zustand/middleware";
3 |
4 | interface Wallet {
5 | id: number;
6 | userId: number;
7 | address: string;
8 | createdAt: string;
9 | updatedAt: string;
10 | }
11 |
12 | interface User {
13 | id: number;
14 | githubId: string;
15 | username: string;
16 | email: string | null;
17 | name: string | null;
18 | avatarUrl: string;
19 | createdAt: string;
20 | updatedAt: string;
21 | }
22 |
23 | interface UserState {
24 | user: User | null;
25 | wallet: Wallet | null;
26 | isLoading: boolean;
27 | setUser: (user: User) => void;
28 | setWallet: (wallet: Wallet) => void;
29 | clearUser: () => void;
30 | setLoading: (status: boolean) => void;
31 | updateWalletAddress: (address: string) => void;
32 | }
33 |
34 | export const useUserStore = create()(
35 | devtools(
36 | persist(
37 | (set, get) => ({
38 | user: null,
39 | wallet: null,
40 | isLoading: false,
41 | setUser: (user) => set({ user }, false, "setUser"),
42 | setWallet: (wallet) => set({ wallet }, false, "setWallet"),
43 | clearUser: () => set({ user: null, wallet: null }, false, "clearUser"),
44 | setLoading: (status) => set({ isLoading: status }, false, "setLoading"),
45 | updateWalletAddress: (address: string) =>
46 | set(
47 | (state) => ({
48 | wallet: state.wallet
49 | ? {
50 | ...state.wallet,
51 | address,
52 | updatedAt: new Date().toISOString(),
53 | }
54 | : null,
55 | }),
56 | false,
57 | "updateWalletAddress",
58 | ),
59 | }),
60 | {
61 | name: "user-storage",
62 | partialize: (state) => ({
63 | user: state.user,
64 | wallet: state.wallet,
65 | }),
66 | },
67 | ),
68 | {
69 | name: "UserStore",
70 | enabled: process.env.NODE_ENV === "development",
71 | },
72 | ),
73 | );
74 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function TooltipProvider({
9 | delayDuration = 0,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
18 | );
19 | }
20 |
21 | function Tooltip({
22 | ...props
23 | }: React.ComponentProps) {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | function TooltipTrigger({
32 | ...props
33 | }: React.ComponentProps) {
34 | return ;
35 | }
36 |
37 | function TooltipContent({
38 | className,
39 | sideOffset = 0,
40 | children,
41 | ...props
42 | }: React.ComponentProps) {
43 | return (
44 |
45 |
54 | {children}
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
62 |
--------------------------------------------------------------------------------
/apps/web-app/README.md:
--------------------------------------------------------------------------------
1 | # React + TypeScript + Vite
2 |
3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4 |
5 | Currently, two official plugins are available:
6 |
7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9 |
10 | ## Expanding the ESLint configuration
11 |
12 | If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13 |
14 | ```js
15 | export default tseslint.config({
16 | extends: [
17 | // Remove ...tseslint.configs.recommended and replace with this
18 | ...tseslint.configs.recommendedTypeChecked,
19 | // Alternatively, use this for stricter rules
20 | ...tseslint.configs.strictTypeChecked,
21 | // Optionally, add this for stylistic rules
22 | ...tseslint.configs.stylisticTypeChecked,
23 | ],
24 | languageOptions: {
25 | // other options...
26 | parserOptions: {
27 | project: ["./tsconfig.node.json", "./tsconfig.app.json"],
28 | tsconfigRootDir: import.meta.dirname,
29 | },
30 | },
31 | });
32 | ```
33 |
34 | You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
35 |
36 | ```js
37 | // eslint.config.js
38 | import reactX from "eslint-plugin-react-x";
39 | import reactDom from "eslint-plugin-react-dom";
40 |
41 | export default tseslint.config({
42 | plugins: {
43 | // Add the react-x and react-dom plugins
44 | "react-x": reactX,
45 | "react-dom": reactDom,
46 | },
47 | rules: {
48 | // other rules...
49 | // Enable its recommended typescript rules
50 | ...reactX.configs["recommended-typescript"].rules,
51 | ...reactDom.configs.recommended.rules,
52 | },
53 | });
54 | ```
55 |
--------------------------------------------------------------------------------
/apps/github-bot/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | [fork]: /fork
4 | [pr]: /compare
5 | [code-of-conduct]: CODE_OF_CONDUCT.md
6 |
7 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
8 |
9 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
10 |
11 | ## Issues and PRs
12 |
13 | If you have suggestions for how this project could be improved, or want to report a bug, open an issue! We'd love all and any contributions. If you have questions, too, we'd love to hear them.
14 |
15 | We'd also love PRs. If you're thinking of a large PR, we advise opening up an issue first to talk about it, though! Look at the links below if you're not sure how to open a PR.
16 |
17 | ## Submitting a pull request
18 |
19 | 1. [Fork][fork] and clone the repository.
20 | 1. Configure and install the dependencies: `npm install`.
21 | 1. Make sure the tests pass on your machine: `npm test`, note: these tests also apply the linter, so there's no need to lint separately.
22 | 1. Create a new branch: `git checkout -b my-branch-name`.
23 | 1. Make your change, add tests, and make sure the tests still pass.
24 | 1. Push to your fork and [submit a pull request][pr].
25 | 1. Pat your self on the back and wait for your pull request to be reviewed and merged.
26 |
27 | Here are a few things you can do that will increase the likelihood of your pull request being accepted:
28 |
29 | - Write and update tests.
30 | - Keep your changes as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
31 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
32 |
33 | Work in Progress pull requests are also welcome to get feedback early on, or if there is something blocked you.
34 |
35 | ## Resources
36 |
37 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
38 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
39 | - [GitHub Help](https://help.github.com)
40 |
--------------------------------------------------------------------------------
/libs/common/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "common",
3 | "version": "1.0.0",
4 | "description": "french fries",
5 | "author": "yhoungdev",
6 | "license": "MIT",
7 | "repository": "yhoungdev/common",
8 | "main": "dist/index.js",
9 | "module": "dist/index.modern.js",
10 | "source": "src/index.tsx",
11 | "engines": {
12 | "node": ">=10"
13 | },
14 | "scripts": {
15 | "build": "microbundle-crl --no-compress --format modern,cjs",
16 | "start": "microbundle-crl watch --no-compress --format modern,cjs",
17 | "prepare": "run-s build",
18 | "test": "run-s test:unit test:lint test:build",
19 | "test:build": "run-s build",
20 | "test:lint": "eslint .",
21 | "test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
22 | "test:watch": "react-scripts test --env=jsdom",
23 | "predeploy": "cd example && yarn install && yarn run build",
24 | "deploy": "gh-pages -d example/build"
25 | },
26 | "peerDependencies": {
27 | "react": "^16.0.0"
28 | },
29 | "devDependencies": {
30 | "@testing-library/jest-dom": "^4.2.4",
31 | "@testing-library/react": "^9.5.0",
32 | "@testing-library/user-event": "^7.2.1",
33 | "@types/jest": "^25.1.4",
34 | "@types/node": "^12.12.38",
35 | "@types/react": "^16.9.27",
36 | "@types/react-dom": "^16.9.7",
37 | "@typescript-eslint/eslint-plugin": "^2.26.0",
38 | "@typescript-eslint/parser": "^2.26.0",
39 | "microbundle-crl": "^0.13.10",
40 | "babel-eslint": "^10.0.3",
41 | "cross-env": "^7.0.2",
42 | "eslint": "^6.8.0",
43 | "eslint-config-prettier": "^6.7.0",
44 | "eslint-config-standard": "^14.1.0",
45 | "eslint-config-standard-react": "^9.2.0",
46 | "eslint-plugin-import": "^2.18.2",
47 | "eslint-plugin-node": "^11.0.0",
48 | "eslint-plugin-prettier": "^3.1.1",
49 | "eslint-plugin-promise": "^4.2.1",
50 | "eslint-plugin-react": "^7.17.0",
51 | "eslint-plugin-standard": "^4.0.1",
52 | "gh-pages": "^2.2.0",
53 | "npm-run-all": "^4.1.5",
54 | "prettier": "^2.0.4",
55 | "react": "^16.13.1",
56 | "react-dom": "^16.13.1",
57 | "react-scripts": "^3.4.1",
58 | "typescript": "^3.7.5"
59 | },
60 | "files": [
61 | "dist"
62 | ]
63 | }
64 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/status/success-page.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "@/components/ui/button";
2 | import { Link } from "@tanstack/react-router";
3 | import Confetti from "react-confetti";
4 | import { useEffect, useRef } from "react";
5 |
6 | const SuccessPage = () => {
7 | const audioRef = useRef(null);
8 |
9 | const params = new URLSearchParams(window.location.search);
10 | const txnHash = params.get("txnHash");
11 | useEffect(() => {
12 | const handleInteraction = () => {
13 | if (audioRef.current) {
14 | audioRef.current
15 | .play()
16 | .then(() => {
17 | console.log("✅ Audio played successfully after user interaction.");
18 | })
19 | .catch((err) => {
20 | console.error("❌ Still couldn’t play audio:", err);
21 | });
22 | }
23 |
24 | window.removeEventListener("click", handleInteraction);
25 | };
26 |
27 | window.addEventListener("click", handleInteraction);
28 |
29 | return () => {
30 | window.removeEventListener("click", handleInteraction);
31 | };
32 | }, []);
33 |
34 | return (
35 |
36 |
37 |
42 |
43 |
🎉
44 |
Tip Successful!
45 |
46 | Thank you for your generosity. Your tip has been successfully processed.
47 |
48 |
58 |
59 | );
60 | };
61 |
62 | export default SuccessPage;
63 |
--------------------------------------------------------------------------------
/apps/web-app/src/layouts/dashboard.tsx:
--------------------------------------------------------------------------------
1 | import DashboardHeader from "@/header/dashboardHeader.tsx";
2 | import DashboardBottomTab from "@/dashboard/dashboardBottomTab.tsx";
3 | import useAuth from "@/hooks/useAuth";
4 | import { useEffect } from "react";
5 | import { useNavigate } from "@tanstack/react-router";
6 | import ModalLayout from "@/components/popups/modals";
7 | import AddOrUpdateAddress from "@/components/pages/settings/add-or-update-address.tsx";
8 | import { useUserStore } from "../../store/user.ts";
9 |
10 | interface IDashboardProps {
11 | children: React.ReactNode;
12 | title?: string;
13 | }
14 |
15 | const DefaultDashboard = ({ children }: IDashboardProps): React.JSX.Element => {
16 | const { isAuthenticated, checkAuthStatus } = useAuth();
17 | const navigate = useNavigate();
18 | const { wallet } = useUserStore();
19 |
20 | useEffect(() => {
21 | const isValid = checkAuthStatus();
22 | if (!isValid) {
23 | navigate({ to: "/" });
24 | }
25 | }, []);
26 |
27 | console.log(wallet);
28 |
29 | if (!isAuthenticated) {
30 | return null;
31 | }
32 |
33 | return (
34 |
35 | {wallet === undefined && (
36 |
43 |
47 | Please add your Solana wallet address.{" "}
48 |
49 | Click here.
50 |
51 |
52 | }
53 | //closeOnOverlayClick={!!address}
54 | >
55 |
56 |
57 |
58 | )}
59 |
60 |
61 |
{children}
62 |
63 |
64 |
65 | );
66 | };
67 |
68 | export default DefaultDashboard;
69 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as SliderPrimitive from "@radix-ui/react-slider";
5 |
6 | import { cn } from "@/lib/utils";
7 |
8 | function Slider({
9 | className,
10 | defaultValue,
11 | value,
12 | min = 0,
13 | max = 100,
14 | ...props
15 | }: React.ComponentProps) {
16 | const _values = React.useMemo(
17 | () =>
18 | Array.isArray(value)
19 | ? value
20 | : Array.isArray(defaultValue)
21 | ? defaultValue
22 | : [min, max],
23 | [value, defaultValue, min, max],
24 | );
25 |
26 | return (
27 |
39 |
45 |
51 |
52 | {Array.from({ length: _values.length }, (_, index) => (
53 |
58 | ))}
59 |
60 | );
61 | }
62 |
63 | export { Slider };
64 |
--------------------------------------------------------------------------------
/apps/server/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Hono } from 'hono';
2 | import { cors } from 'hono/cors';
3 | import { authRouter } from './routes/auth';
4 | import walletsRoute from './routes/wallets';
5 | import txRecordsRoute from './routes/tx-records';
6 | import { db } from './db';
7 | import { sql } from 'drizzle-orm';
8 | import type { Env } from './types/env';
9 | import { logger } from 'hono/logger';
10 | import { prettyJSON } from 'hono/pretty-json';
11 | import { userRoute } from './routes/user';
12 | import { sendTelegramMessage } from './utils/telegram-notification';
13 | import { TELEGRAM_CHAT_ID } from './constants';
14 | import { launchBot, telegram_bot } from './config/telegraf';
15 | const app = new Hono<{ Bindings: Env }>();
16 |
17 | app.use(logger());
18 | app.use(prettyJSON());
19 | app.use('*', async (c, next) => {
20 | c.env = {
21 | GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID!,
22 | GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET!,
23 | JWT_SECRET: process.env.JWT_SECRET!,
24 | DB: db,
25 | };
26 | await next();
27 | });
28 |
29 | app.use('/*', cors());
30 |
31 | app.get('/', (c) => {
32 | sendTelegramMessage(TELEGRAM_CHAT_ID, 'Potatoe API is up and running!');
33 | return c.json({
34 | name: 'Potatoe GitHub Users API',
35 | version: '1.0.0',
36 | description: '⚡ Instantly tip developers with SOL for their contributions',
37 | });
38 | });
39 |
40 | app.get('/db-test', async (c) => {
41 | try {
42 | const result = await c.env.DB.query(sql`SELECT NOW()`);
43 | return c.json({
44 | status: 'Connected',
45 | timestamp: result[0].now,
46 | message: 'Database connection successful!',
47 | });
48 | } catch (error) {
49 | return c.json(
50 | {
51 | status: 'Error',
52 |
53 | message:
54 | error instanceof Error ? error.message : 'An unknown error occurred',
55 | },
56 | 500,
57 | );
58 | }
59 | });
60 |
61 | const routes = [
62 | { path: '/auth', handler: authRouter },
63 | { path: '/wallet', handler: walletsRoute },
64 | { path: '/user', handler: userRoute },
65 | { path: '/tx-records', handler: txRecordsRoute },
66 | ];
67 |
68 | routes.forEach(({ path, handler }) => {
69 | app.route(path, handler);
70 | });
71 |
72 | launchBot();
73 | logger();
74 |
75 | export default app;
76 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/typography/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 | import { cn } from "@/lib/utils";
4 |
5 | const typographyVariants = cva("", {
6 | variants: {
7 | variant: {
8 | h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
9 | h2: "scroll-m-20 text-3xl font-semibold tracking-tight",
10 | h3: "scroll-m-20 text-2xl font-semibold tracking-tight",
11 | h4: "scroll-m-20 text-xl font-semibold tracking-tight",
12 | h5: "scroll-m-20 text-lg font-semibold tracking-tight",
13 | h6: "scroll-m-20 text-base font-semibold tracking-tight",
14 | p: "leading-7 [&:not(:first-child)]:mt-6",
15 | blockquote: "mt-6 border-l-2 pl-6 italic",
16 | list: "my-6 ml-6 list-disc [&>li]:mt-2",
17 | body1: "text-base leading-7",
18 | body2: "text-sm leading-6",
19 | caption: "text-xs leading-5",
20 | muted: "text-sm text-muted-foreground",
21 | },
22 | color: {
23 | default: "",
24 | primary: "text-primary",
25 | secondary: "text-secondary",
26 | muted: "text-muted-foreground",
27 | accent: "text-accent-foreground",
28 | destructive: "text-destructive",
29 | },
30 | weight: {
31 | normal: "font-normal",
32 | medium: "font-medium",
33 | semibold: "font-semibold",
34 | bold: "font-bold",
35 | extrabold: "font-extrabold",
36 | },
37 | },
38 | defaultVariants: {
39 | variant: "body1",
40 | color: "default",
41 | weight: "normal",
42 | },
43 | });
44 |
45 | interface TypographyProps
46 | extends React.HTMLAttributes,
47 | VariantProps {
48 | as?: keyof JSX.IntrinsicElements;
49 | children: React.ReactNode;
50 | weight?: "normal" | "medium" | "semibold" | "bold" | "extrabold";
51 | }
52 |
53 | export const Typography = ({
54 | as: Component = "p",
55 | children,
56 | variant,
57 | color,
58 | weight,
59 | className,
60 | ...props
61 | }: TypographyProps) => {
62 | return (
63 |
67 | {children}
68 |
69 | );
70 | };
71 |
72 | export default Typography;
73 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | `inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap
9 | rounded-md text-sm font-medium transition-all disabled:pointer-events-none
10 | disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4
11 | shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50
12 | focus-visible:ring-[3px] aria-invalid:ring-destructive/20
13 | dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive`,
14 | {
15 | variants: {
16 | variant: {
17 | default:
18 | "bg-red-400 text-primary-foreground shadow-xs hover:bg-primary/90",
19 | destructive:
20 | "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
21 | outline:
22 | "border bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
23 | secondary:
24 | "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
25 | ghost:
26 | "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
27 | link: "text-primary underline-offset-4 hover:underline",
28 | },
29 | size: {
30 | default: "h-9 px-4 py-2 has-[>svg]:px-3",
31 | sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
32 | lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
33 | icon: "size-9",
34 | },
35 | },
36 | defaultVariants: {
37 | variant: "default",
38 | size: "default",
39 | },
40 | },
41 | );
42 |
43 | function Button({
44 | className,
45 | variant,
46 | size,
47 | asChild = false,
48 | ...props
49 | }: React.ComponentProps<"button"> &
50 | VariantProps & {
51 | asChild?: boolean;
52 | }) {
53 | const Comp = asChild ? Slot : "button";
54 |
55 | return (
56 |
61 | );
62 | }
63 |
64 | export { Button, buttonVariants };
65 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/pages/explore/potatoe-users.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { UserService } from "@/services";
3 | import { GithubUserCardSkeleton } from "@/components/github/GithubUserCardSkeleton";
4 | import { GithubUserCard } from "@/components/github/GithubUserCard";
5 | import { GithubUserSearch } from "@/components/search/GithubUserSearch";
6 | import { useQuery } from "@tanstack/react-query";
7 | import { IPotatoeUserData } from "@/interface/users.interface";
8 |
9 | const fetchPotatoeUsers = async () => {
10 | const response = await UserService.fetchAllPotatoeUsers();
11 | return response;
12 | };
13 |
14 | const PotatoeUsers = () => {
15 | const [searchQuery, setSearchQuery] = useState("");
16 |
17 | const {
18 | data: users,
19 | isLoading,
20 | isError,
21 | } = useQuery({
22 | queryKey: ["potatoe-users"],
23 | queryFn: fetchPotatoeUsers,
24 | staleTime: 1000 * 60 * 5,
25 | });
26 |
27 | const filteredUsers = users?.filter(({ users: user }) =>
28 | user.username.toLowerCase().includes(searchQuery.toLowerCase()),
29 | );
30 |
31 | return (
32 |
33 |
{}}
37 | loading={isLoading}
38 | />
39 |
40 |
41 | {isLoading ? (
42 | Array.from({ length: 6 }).map((_, index) => (
43 |
44 | ))
45 | ) : isError ? (
46 |
Failed to load users
47 | ) : filteredUsers?.length > 0 ? (
48 | filteredUsers?.map((data: IPotatoeUserData, index: number) => {
49 | const users = data.users;
50 |
51 | const userData = {
52 | name: users.name,
53 | avatar_url: users.avatarUrl,
54 | login: users.username,
55 | email: users.email,
56 | };
57 | return (
58 |
63 | );
64 | })
65 | ) : (
66 |
No users found
67 | )}
68 |
69 |
70 | );
71 | };
72 |
73 | export default PotatoeUsers;
74 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/pages/explore/general-users.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { toast } from "sonner";
3 | import { GithubUserCardSkeleton } from "@/components/github/GithubUserCardSkeleton";
4 | import { GithubUserCard } from "@/components/github/GithubUserCard";
5 | import { GithubUserSearch } from "@/components/search/GithubUserSearch";
6 |
7 | interface GitHubUser {
8 | login: string;
9 | avatar_url: string;
10 | name: string;
11 | bio: string;
12 | }
13 |
14 | const GeneralGithubUsers = () => {
15 | const [searchQuery, setSearchQuery] = useState("");
16 | const [users, setUsers] = useState([]);
17 | const [loading, setLoading] = useState(false);
18 |
19 | const searchGithubUser = async () => {
20 | if (!searchQuery) {
21 | toast.error("Please enter a GitHub username");
22 | return;
23 | }
24 |
25 | setLoading(true);
26 | try {
27 | const response = await fetch(
28 | `https://api.github.com/search/users?q=${searchQuery}&per_page=10`,
29 | );
30 | const data = await response.json();
31 |
32 | if (response.ok) {
33 | const detailedUsers = await Promise.all(
34 | data.items.map(async (user: any) => {
35 | const userResponse = await fetch(
36 | `https://api.github.com/users/${user.login}`,
37 | );
38 | return userResponse.json();
39 | }),
40 | );
41 |
42 | setUsers(detailedUsers);
43 | toast.success(`Found ${detailedUsers.length} users!`);
44 | } else {
45 | setUsers([]);
46 | toast.error("No users found");
47 | }
48 | } catch (error) {
49 | toast.error("Error fetching GitHub users");
50 | console.error("Error:", error);
51 | } finally {
52 | setLoading(false);
53 | }
54 | };
55 | return (
56 |
57 |
63 |
64 |
65 | {loading ? (
66 | <>
67 | {Array.from({ length: 6 }).map((_, index) => (
68 |
69 | ))}
70 | >
71 | ) : (
72 | users?.map((user) => )
73 | )}
74 |
75 |
76 | );
77 | };
78 |
79 | export default GeneralGithubUsers;
80 |
--------------------------------------------------------------------------------
/apps/extension/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { supabaseObject } from "../../libs/supabase";
3 | import { FaGithub } from "react-icons/fa";
4 | import browser from "webextension-polyfill";
5 |
6 | function SignIn() {
7 | const signInWithGithub = async () => {
8 | try {
9 | const { data, error } = await supabaseObject.auth.signInWithOAuth({
10 | provider: "github",
11 | options: {
12 | redirectTo: browser.identity.getRedirectURL(),
13 | },
14 | });
15 |
16 | if (error) {
17 | console.error("Error during GitHub sign-in:", error.message);
18 | throw error;
19 | }
20 |
21 | if (data?.url) {
22 | await browser.tabs.create({ url: data.url });
23 | console.log("GitHub sign-in initiated, redirected to:", data.url);
24 | }
25 | } catch (err) {
26 | console.error("Failed to sign in with GitHub:", err);
27 | }
28 | };
29 |
30 | const getUser = async () => {
31 | try {
32 | const {
33 | data: { user },
34 | error,
35 | } = await supabaseObject.auth.getUser();
36 |
37 | if (error) {
38 | console.error("Error fetching user:", error.message);
39 | throw error;
40 | }
41 |
42 | console.log("Logged-in user:", user);
43 | } catch (err) {
44 | console.error("Failed to fetch user:", err);
45 | }
46 | };
47 |
48 | return (
49 |
55 |
56 |
57 |
Welcome Back
58 |
Sign in to your account using GitHub
59 |
60 |
61 |
65 |
66 | Sign in with GitHub
67 |
68 |
69 | {/*
*/}
73 | {/* Get Current User*/}
74 | {/* */}
75 |
76 |
77 | );
78 | }
79 |
80 | export default SignIn;
81 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/github/GithubUserCard.tsx:
--------------------------------------------------------------------------------
1 | import { motion } from "framer-motion";
2 | import { GithubIcon, ExternalLinkIcon } from "lucide-react";
3 | import { Button } from "@/components/ui/button";
4 | import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
5 | import { truncateText } from "@/util/content-utils";
6 | interface GitHubUser {
7 | login: string;
8 | avatar_url: string;
9 | name: string;
10 | bio: string;
11 | }
12 |
13 | interface GithubUserCardProps {
14 | user: GitHubUser;
15 | }
16 |
17 | export function GithubUserCard({ user }: GithubUserCardProps) {
18 | return (
19 |
24 |
25 |
26 |
27 |
28 | {user?.login?.slice(0, 2).toUpperCase()}
29 |
30 |
31 |
32 |
33 |
34 | {user.name || user.login}
35 |
36 |
48 |
49 | {user.bio && (
50 |
51 | {truncateText(user.bio, 30)}
52 |
53 | )}
54 |
{
57 | const currentUrl = window.location.origin;
58 | window.location.href = `${currentUrl}/app/profile?user=${user.login}`;
59 | console.log(currentUrl);
60 | }}
61 | >
62 | Tip User 🍟
63 |
64 |
65 |
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/apps/github-bot/test/index.test.ts:
--------------------------------------------------------------------------------
1 | // You can import your modules
2 | // import index from '../src/index'
3 |
4 | import nock from "nock";
5 | // Requiring our app implementation
6 | import myProbotApp from "../src/index.js";
7 | import { Probot, ProbotOctokit } from "probot";
8 | // Requiring our fixtures
9 | //import payload from "./fixtures/issues.opened.json" with { "type": "json"};
10 | import fs from "fs";
11 | import path from "path";
12 | import { fileURLToPath } from "url";
13 | import { describe, beforeEach, afterEach, test, expect } from "vitest";
14 |
15 | const issueCreatedBody = { body: "Thanks for opening this issue!" };
16 |
17 | const __dirname = path.dirname(fileURLToPath(import.meta.url));
18 |
19 | const privateKey = fs.readFileSync(
20 | path.join(__dirname, "fixtures/mock-cert.pem"),
21 | "utf-8",
22 | );
23 |
24 | const payload = JSON.parse(
25 | fs.readFileSync(path.join(__dirname, "fixtures/issues.opened.json"), "utf-8"),
26 | );
27 |
28 | describe("My Probot app", () => {
29 | let probot: any;
30 |
31 | beforeEach(() => {
32 | nock.disableNetConnect();
33 | probot = new Probot({
34 | appId: 123,
35 | privateKey,
36 | // disable request throttling and retries for testing
37 | Octokit: ProbotOctokit.defaults({
38 | retry: { enabled: false },
39 | throttle: { enabled: false },
40 | }),
41 | });
42 | // Load our app into probot
43 | probot.load(myProbotApp);
44 | });
45 |
46 | test("creates a comment when an issue is opened", async () => {
47 | const mock = nock("https://api.github.com")
48 | // Test that we correctly return a test token
49 | .post("/app/installations/2/access_tokens")
50 | .reply(200, {
51 | token: "test",
52 | permissions: {
53 | issues: "write",
54 | },
55 | })
56 |
57 | // Test that a comment is posted
58 | .post("/repos/hiimbex/testing-things/issues/1/comments", (body: any) => {
59 | expect(body).toMatchObject(issueCreatedBody);
60 | return true;
61 | })
62 | .reply(200);
63 |
64 | // Receive a webhook event
65 | await probot.receive({ name: "issues", payload });
66 |
67 | expect(mock.pendingMocks()).toStrictEqual([]);
68 | });
69 |
70 | afterEach(() => {
71 | nock.cleanAll();
72 | nock.enableNetConnect();
73 | });
74 | });
75 |
76 | // For more information about testing with Jest see:
77 | // https://facebook.github.io/jest/
78 |
79 | // For more information about using TypeScript in your tests, Jest recommends:
80 | // https://github.com/kulshekhar/ts-jest
81 |
82 | // For more information about testing with Nock see:
83 | // https://github.com/nock/nock
84 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/views/settings-drawer-view.tsx:
--------------------------------------------------------------------------------
1 | import AddressAndBadge from "../profile/addressAndBadge";
2 | import ProductHuntBadge from "../misc/product-hunt-badge";
3 | import { useUserStore } from "@/store/user.store";
4 | import { toast } from "sonner";
5 | import { Power } from "lucide-react";
6 | import ModalLayout from "@/components/popups/modals";
7 | import { DialogDescription } from "@/components/ui/dialog.tsx";
8 | import useAuth from "@/hooks/useAuth.ts";
9 | import { Button } from "@/components/ui/button.tsx";
10 |
11 | const SettingsDrawerView = () => {
12 | const { user } = useUserStore() || {};
13 |
14 | const { username: profile_name } = user?.users || {};
15 | const { address } = user?.wallets || {};
16 |
17 | const { logout } = useAuth();
18 |
19 | const copyBadgeCode = () => {
20 | const badgeCode = `
21 |
22 |
29 |
30 | `.trim();
31 | navigator.clipboard.writeText(badgeCode);
32 | toast.success("Badge code copied to clipboard!");
33 | };
34 | return (
35 |
36 |
{}}
39 | onCopyBadge={copyBadgeCode}
40 | currentAddress={address}
41 | />
42 |
56 | }
57 | title={"Log Out"}
58 | >
59 |
60 | Your account would be logged out
61 |
62 |
63 |
64 | Continue
65 |
66 |
67 |
68 |
69 | );
70 | };
71 | export default SettingsDrawerView;
72 |
--------------------------------------------------------------------------------
/apps/github-bot/src/controllers/commands.controller.ts:
--------------------------------------------------------------------------------
1 | import { Context } from "probot";
2 | import COMMANDS from "../enums/commands.ts";
3 |
4 | class BountyBot {
5 | private context: Context;
6 |
7 | constructor(context: Context) {
8 | this.context = context;
9 | }
10 |
11 | async handleComment() {
12 | const { comment: commentContext, sender } = this.context.payload;
13 | const { body } = commentContext;
14 | console.log(body);
15 |
16 | if (body.startsWith(COMMANDS.BOUNTY)) {
17 | const bountyMatch = body.match(/^\/bounty\s+(\d+(?:\.\d+)?)\s+(\w+)$/);
18 | if (bountyMatch) {
19 | const [_, amount, currency] = bountyMatch;
20 | await this.registerBounty(amount, currency);
21 | } else {
22 | await this.registerBounty();
23 | }
24 | }
25 |
26 | if (body.startsWith(COMMANDS.CLAIM)) {
27 | await this.claimBounty();
28 | }
29 |
30 | if (body.startsWith(COMMANDS.EDIT_BOUNTY)) {
31 | await this.editBounty();
32 | }
33 |
34 | if (body.startsWith(COMMANDS.HELP)) {
35 | await this.sendHelp();
36 | }
37 | }
38 |
39 | private async registerBounty(amount?: string, currency?: string) {
40 | if (!amount || !currency) {
41 | const comment = this.context.issue({
42 | body: `❌ Invalid bounty format. Please use: \`/bounty \`\nExample: \`/bounty 10 USDC\``,
43 | });
44 | await this.context.octokit.issues.createComment(comment);
45 | return;
46 | }
47 |
48 | const comment = this.context.issue({
49 | body: `💰 Bounty registered: ${amount} ${currency}`,
50 | });
51 | await this.context.octokit.issues.createComment(comment);
52 | }
53 |
54 | private async rewardBounty() {
55 | const comment = this.context.issue({
56 | body: `💰 Bounty registered!`,
57 | });
58 | await this.context.octokit.issues.createComment(comment);
59 | }
60 |
61 | private async claimBounty() {
62 | const sender = this.context.payload.sender.login;
63 | const comment = this.context.issue({
64 | body: `🛠️ @${sender} is claiming this bounty.`,
65 | });
66 | await this.context.octokit.issues.createComment(comment);
67 | }
68 |
69 | private async editBounty() {
70 | const comment = this.context.issue({
71 | body: `✏️ Bounty updated.`,
72 | });
73 | await this.context.octokit.issues.createComment(comment);
74 | }
75 |
76 | private async sendHelp() {
77 | const comment = this.context.issue({
78 | body: `
79 | 🧠 **Potatoe Squeezy Bot Help**
80 | - \`/bounty 10 USDC\`
81 | - \`/claim\`
82 | - \`/edit-bounty 15 USDC\`
83 | - \`/cancel-bounty\`
84 | - \`/help\`
85 | `,
86 | });
87 | await this.context.octokit.issues.createComment(comment);
88 | }
89 | }
90 |
91 | export default BountyBot;
92 |
--------------------------------------------------------------------------------
/apps/extension/public/icon-with-shadow.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/profile/userProfileCard.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 | import CelebrateUser from "./celebrateUser";
3 | import { Skeleton } from "@/components/ui/skeleton";
4 | import ProfilePanel from "@/components/profile/profilePanel";
5 | import WalletNotConnected from "@/components/fallbacks/wallet-no-connect";
6 | import { useWallet } from "@solana/wallet-adapter-react";
7 | import useExtractUserWallet from "@/hooks/extract-user-wallet";
8 |
9 | interface GitHubUser {
10 | login: string;
11 | name: string;
12 | bio: string;
13 | avatar_url: string;
14 | }
15 |
16 | function UserProfileCard() {
17 | const [userData, setUserData] = useState(null);
18 | const [loading, setLoading] = useState(true);
19 |
20 | const searchParams = new URLSearchParams(window.location.search);
21 | const username = searchParams.get("user");
22 |
23 | const { wallet } = useExtractUserWallet(username || "");
24 | const { address } = wallet || {};
25 |
26 | const { connected } = useWallet();
27 |
28 | useEffect(() => {
29 | const fetchUserData = async () => {
30 | const searchParams = new URLSearchParams(window.location.search);
31 | const username = searchParams.get("user");
32 |
33 | if (!username) {
34 | setLoading(false);
35 | return;
36 | }
37 |
38 | try {
39 | const response = await fetch(
40 | `https://api.github.com/users/${username}`,
41 | );
42 | const data = await response.json();
43 |
44 | if (response.ok) {
45 | setUserData(data);
46 | } else {
47 | setUserData(null);
48 | }
49 | } catch (error) {
50 | console.error("Failed to fetch user:", error);
51 | setUserData(null);
52 | } finally {
53 | setLoading(false);
54 | }
55 | };
56 |
57 | fetchUserData();
58 | }, []);
59 |
60 | if (loading) {
61 | return (
62 |
63 |
64 |
65 | );
66 | }
67 |
68 | if (!userData) {
69 | return (
70 |
71 | User not found
72 |
73 | );
74 | }
75 |
76 | if (!connected) {
77 | return ;
78 | }
79 |
80 | return (
81 |
91 | );
92 | }
93 |
94 | export default UserProfileCard;
95 |
--------------------------------------------------------------------------------
/apps/web-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "web-app",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "lint": "eslint .",
10 | "preview": "vite preview",
11 | "format": "prettier --write ."
12 | },
13 | "dependencies": {
14 | "@radix-ui/react-alert-dialog": "^1.1.14",
15 | "@radix-ui/react-avatar": "^1.1.10",
16 | "@radix-ui/react-dialog": "^1.1.14",
17 | "@radix-ui/react-slider": "^1.3.5",
18 | "@radix-ui/react-slot": "^1.2.3",
19 | "@radix-ui/react-tabs": "^1.1.12",
20 | "@radix-ui/react-tooltip": "^1.2.7",
21 | "@solana-mobile/wallet-adapter-mobile": "2.2.0",
22 | "@solana/wallet-adapter-base": "^0.9.26",
23 | "@solana/wallet-adapter-react": "^0.15.38",
24 | "@solana/wallet-adapter-react-ui": "^0.9.38",
25 | "@solana/wallet-adapter-wallets": "^0.19.36",
26 | "@solana/web3.js": "1.98.2",
27 | "@supabase/supabase-js": "^2.49.9",
28 | "@tailwindcss/vite": "^4.1.8",
29 | "@tanstack/react-query": "^5.80.0",
30 | "@tanstack/react-router": "^1.120.15",
31 | "@types/js-cookie": "^3.0.6",
32 | "@vercel/analytics": "^1.5.0",
33 | "@walletconnect/solana-adapter": "^0.0.8",
34 | "avatar": "^0.1.0",
35 | "axios": "^1.9.0",
36 | "boring-avatars": "^1.11.2",
37 | "card": "^2.5.4",
38 | "class-variance-authority": "^0.7.1",
39 | "clsx": "^2.1.1",
40 | "date-fns": "^4.1.0",
41 | "framer-motion": "^12.16.0",
42 | "js-cookie": "^3.0.5",
43 | "lucid": "^2.21.0",
44 | "lucide-react": "^0.511.0",
45 | "motion": "^12.16.0",
46 | "next-themes": "^0.4.6",
47 | "react": "^19.1.0",
48 | "react-confetti": "^6.4.0",
49 | "react-dom": "^19.1.0",
50 | "react-hot-toast": "^2.5.2",
51 | "react-share": "^5.2.2",
52 | "rpc-websockets": "^10.0.0",
53 | "skeleton": "^1.0.0",
54 | "sonner": "^2.0.5",
55 | "tailwind-merge": "^3.3.0",
56 | "tailwindcss": "4.1.8",
57 | "tailwindcss-animate": "^1.0.7",
58 | "vite-plugin-node-polyfills": "^0.23.0",
59 | "zod": "^3.25.49",
60 | "zustand": "^5.0.5"
61 | },
62 | "devDependencies": {
63 | "@eslint/js": "^9.28.0",
64 | "@locator/babel-jsx": "^0.4.4",
65 | "@tanstack/router-cli": "^1.120.15",
66 | "@tanstack/router-devtools": "^1.120.15",
67 | "@tanstack/router-plugin": "^1.120.15",
68 | "@types/node": "^22.15.29",
69 | "@types/react": "^19.1.6",
70 | "@types/react-dom": "^19.1.5",
71 | "@vitejs/plugin-react": "^4.5.1",
72 | "@vitejs/plugin-react-swc": "^3.10.1",
73 | "eslint": "^9.28.0",
74 | "eslint-plugin-react-hooks": "^5.2.0",
75 | "eslint-plugin-react-refresh": "^0.4.20",
76 | "globals": "^16.2.0",
77 | "tailwindcss-motion": "^1.1.0",
78 | "typescript": "~5.8.3",
79 | "typescript-eslint": "^8.33.1",
80 | "vite": "^6.3.5",
81 | "vite-plugin-pwa": "^1.0.0"
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/profile/addressAndBadge.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from "../ui/button";
2 | import { ChevronRight, CopyIcon } from "lucide-react";
3 | import { TipBadge } from "./TipBadge";
4 | import Typography from "../typography";
5 | import ModalLayout from "../popups/modals";
6 | import AddOrUpdateAddress from "../pages/settings/add-or-update-address";
7 | import UpdateProfile from "../pages/settings/update-profile";
8 |
9 | interface AddressAndBadgeProps {
10 | username: string;
11 | onCopyBadge: () => void;
12 | }
13 |
14 | const AddressAndBadge = ({ username, onCopyBadge }: AddressAndBadgeProps) => {
15 | return (
16 |
17 |
21 |
22 | 💼 Add/Update Wallet Address
23 |
24 |
25 |
26 | }
27 | >
28 |
29 |
30 |
31 |
35 | ✨ Generate Badge
36 |
37 |
38 | }
39 | >
40 |
41 |
45 | Your Tip Badge ✨
46 |
47 |
48 |
49 |
50 |
54 | Copy Badge Code
55 |
56 |
57 |
58 |
59 |
63 | 👤 Update Profile
64 |
65 |
66 | }
67 | >
68 |
69 |
70 |
71 | );
72 | };
73 |
74 | export default AddressAndBadge;
75 |
--------------------------------------------------------------------------------
/apps/web-app/src/pages/usersPage.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import GithubUsersCard from "../misc/github_users_card.tsx";
3 | import { Search } from "lucide-react";
4 | import DefaultButton from "../button/index.tsx";
5 |
6 | interface GitHubUser {
7 | id: number;
8 | login: string;
9 | avatar_url: string;
10 | name: string;
11 | bio?: string;
12 | }
13 |
14 | function UsersPage() {
15 | const [users, setUsers] = useState([]);
16 | const [searchQuery, setSearchQuery] = useState("");
17 | const [loading, setLoading] = useState(false);
18 |
19 | const searchGithubUsers = async () => {
20 | if (!searchQuery) return;
21 |
22 | setLoading(true);
23 | try {
24 | const response = await fetch(
25 | `https://api.github.com/search/users?q=${searchQuery}&per_page=10`,
26 | );
27 | const data = await response.json();
28 |
29 | const detailedUsers = await Promise.all(
30 | data.items.map(async (user: GitHubUser) => {
31 | const userResponse = await fetch(
32 | `https://api.github.com/users/${user.login}`,
33 | );
34 | return userResponse.json();
35 | }),
36 | );
37 |
38 | setUsers(detailedUsers);
39 | } catch (error) {
40 | console.error("Error fetching GitHub users:", error);
41 | } finally {
42 | setLoading(false);
43 | }
44 | };
45 |
46 | const handleSearch = (e: React.FormEvent) => {
47 | e.preventDefault();
48 | searchGithubUsers();
49 | };
50 |
51 | return (
52 |
53 |
71 |
72 |
73 | {users.map((user) => (
74 |
81 | ))}
82 |
83 |
84 | {users.length === 0 && !loading && (
85 |
86 | Search for GitHub users to get started
87 |
88 | )}
89 |
90 | );
91 | }
92 |
93 | export default UsersPage;
94 |
--------------------------------------------------------------------------------
/apps/web-app/src/components/ui/table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { cn } from "@/lib/utils";
3 |
4 | function Table({ className, ...props }: React.ComponentProps<"table">) {
5 | return (
6 |
16 | );
17 | }
18 |
19 | function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
20 | return (
21 |
26 | );
27 | }
28 |
29 | function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
30 | return (
31 |
36 | );
37 | }
38 |
39 | function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
40 | return (
41 | tr]:last:border-b-0",
44 | className,
45 | )}
46 | data-slot="table-footer"
47 | {...props}
48 | />
49 | );
50 | }
51 |
52 | function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
53 | return (
54 |
62 | );
63 | }
64 |
65 | function TableHead({ className, ...props }: React.ComponentProps<"th">) {
66 | return (
67 | [role=checkbox]]:translate-y-[2px]",
70 | className,
71 | )}
72 | data-slot="table-head"
73 | {...props}
74 | />
75 | );
76 | }
77 |
78 | function TableCell({ className, ...props }: React.ComponentProps<"td">) {
79 | return (
80 | [role=checkbox]]:translate-y-[2px]",
83 | className,
84 | )}
85 | data-slot="table-cell"
86 | {...props}
87 | />
88 | );
89 | }
90 |
91 | function TableCaption({
92 | className,
93 | ...props
94 | }: React.ComponentProps<"caption">) {
95 | return (
96 |
101 | );
102 | }
103 |
104 | export {
105 | Table,
106 | TableHeader,
107 | TableBody,
108 | TableFooter,
109 | TableHead,
110 | TableRow,
111 | TableCell,
112 | TableCaption,
113 | };
114 |
--------------------------------------------------------------------------------