diff --git a/app/components/OpenSourceStats.tsx b/app/components/OpenSourceStats.tsx new file mode 100644 index 00000000..a85b68c4 --- /dev/null +++ b/app/components/OpenSourceStats.tsx @@ -0,0 +1,149 @@ +import { convexQuery } from '@convex-dev/react-query' +import { useNpmDownloadCounter } from '@erquhart/convex-oss-stats/react' +import NumberFlow from '@number-flow/react' +import { useSuspenseQuery } from '@tanstack/react-query' +import { api } from 'convex/_generated/api' +import { FaCube, FaStar, FaUsers } from 'react-icons/fa' +import { FaDownload } from 'react-icons/fa' +import convexImageWhite from '~/images/convex-white.svg' +import convexImageDark from '~/images/convex-dark.svg' + +const counterIntervalMs = 500 + +const StableCounter = ({ value }: { value?: number }) => { + const dummyString = Number( + Array(value?.toString().length ?? 1) + .fill('8') + .join('') + ).toLocaleString() + + return ( + <> + {/* Dummy span to prevent layout shift */} + {dummyString} + + + + + ) +} + +const NpmDownloadCounter = ({ + npmData, +}: { + npmData: Parameters[0] +}) => { + const liveNpmDownloadCount = useNpmDownloadCounter(npmData, { + intervalMs: counterIntervalMs, + }) + return +} + +export default function OssStats() { + const { data: github } = useSuspenseQuery( + convexQuery(api.stats.getGithubOwner, { + owner: 'tanstack', + }) + ) + const { data: npm } = useSuspenseQuery( + convexQuery(api.stats.getNpmOrg, { + name: 'tanstack', + }) + ) + + return ( +
+
+ + +
+
+ +
+
+ NPM Downloads +
+
+
+ + +
+
+ +
+
+ Stars on Github +
+
+
+
+ +
+
+ +
+
+ Contributors on GitHub +
+
+
+
+ +
+
+ +
+
+ Dependents on GitHub +
+
+
+
+
+ +
+
+ + Powered by + + Convex Logo + Convex Logo +
+
+
+
+ ) +} diff --git a/app/routes/_libraries/index.tsx b/app/routes/_libraries/index.tsx index 31d7bd93..ab137087 100644 --- a/app/routes/_libraries/index.tsx +++ b/app/routes/_libraries/index.tsx @@ -5,19 +5,12 @@ import { createFileRoute, getRouteApi, } from '@tanstack/react-router' -import { useSuspenseQuery } from '@tanstack/react-query' -import { convexQuery } from '@convex-dev/react-query' -import { useNpmDownloadCounter } from '@erquhart/convex-oss-stats/react' -import NumberFlow from '@number-flow/react' -import { api } from '../../../convex/_generated/api' import { Carbon } from '~/components/Carbon' import { twMerge } from 'tailwind-merge' import { CgSpinner } from 'react-icons/cg' import { Footer } from '~/components/Footer' import SponsorPack from '~/components/SponsorPack' import discordImage from '~/images/discord-logo-white.svg' -import convexImageWhite from '~/images/convex-white.svg' -import convexImageDark from '~/images/convex-dark.svg' import { useMutation } from '~/hooks/useMutation' import { sample } from '~/utils/utils' import { libraries } from '~/libraries' @@ -27,7 +20,7 @@ import bytesImage from '~/images/bytes.svg' // import waves from '~/images/waves.png' // import background from '~/images/background.jpg' import { partners } from '../../utils/partners' -import { FaCube, FaDownload, FaStar, FaUsers } from 'react-icons/fa' +import OpenSourceStats from '~/components/OpenSourceStats' export const textColors = [ `text-rose-500`, @@ -79,143 +72,6 @@ async function bytesSignupServerFn({ email }: { email: string }) { const librariesRouteApi = getRouteApi('/_libraries') -const StableCounter = ({ value }: { value?: number }) => { - const dummyString = Number( - Array(value?.toString().length ?? 1) - .fill('8') - .join('') - ).toLocaleString() - - return ( - <> - {/* Dummy span to prevent layout shift */} - {dummyString} - - - - - ) -} - -const NpmDownloadCounter = ({ - npmData, -}: { - npmData: Parameters[0] -}) => { - const liveNpmDownloadCount = useNpmDownloadCounter(npmData) - return -} - -const OssStats = () => { - const { data: github } = useSuspenseQuery( - convexQuery(api.stats.getGithubOwner, { - owner: 'tanstack', - }) - ) - console.log('github', github) - const { data: npm } = useSuspenseQuery( - convexQuery(api.stats.getNpmOrg, { - name: 'tanstack', - }) - ) - - return ( -
-
- - -
-
- -
-
- NPM Downloads -
-
-
- - -
-
- -
-
- Stars on Github -
-
-
-
- -
-
- -
-
- Contributors on GitHub -
-
-
-
- -
-
- -
-
- Dependents on GitHub -
-
-
-
- -
- ) -} - function Index() { const bytesSignupMutation = useMutation({ fn: bytesSignupServerFn, @@ -281,7 +137,7 @@ function Index() {
- +
diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index b5546345..934e324a 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -41,45 +41,37 @@ export declare const internal: FilterApi< export declare const components: { ossStats: { - lib: { - getGithubOwner: FunctionReference< + github: { + getGithubOwners: FunctionReference< "query", "internal", - { owner: string }, - any + { owners: Array }, + Array >; - getNpmOrg: FunctionReference<"query", "internal", { name: string }, any>; - sync: FunctionReference< - "action", + updateGithubOwner: FunctionReference< + "mutation", "internal", - { - githubAccessToken: string; - githubOwners: Array; - minStars: number; - npmOrgs: Array; - }, + { name: string }, any >; - updateGithubOwner: FunctionReference< - "mutation", + updateGithubOwnerStats: FunctionReference< + "action", "internal", - { - contributorCount?: number; - dependentCount?: number; - owner: string; - starCount?: number; - }, + { githubAccessToken: string; owner: string; page?: number }, any >; updateGithubRepoStars: FunctionReference< "mutation", "internal", - { - githubAccessToken: string; - name: string; - owner: string; - starCount?: number; - }, + { name: string; owner: string; starCount: number }, any >; updateGithubRepos: FunctionReference< @@ -89,7 +81,6 @@ export declare const components: { repos: Array<{ contributorCount: number; dependentCount: number; - dependentCountPrevious?: { count: number; updatedAt: number }; name: string; owner: string; starCount: number; @@ -97,20 +88,73 @@ export declare const components: { }, any >; - updateNpmOrg: FunctionReference< + }; + lib: { + clearAndSync: FunctionReference< + "action", + "internal", + { + githubAccessToken: string; + githubOwners: Array; + minStars: number; + npmOrgs: Array; + }, + any + >; + clearPage: FunctionReference< "mutation", "internal", + { tableName: "githubRepos" | "npmPackages" }, + { isDone: boolean } + >; + clearTable: FunctionReference< + "action", + "internal", + { tableName: "githubRepos" | "npmPackages" }, + null + >; + sync: FunctionReference< + "action", + "internal", { + githubAccessToken: string; + githubOwners: Array; + minStars: number; + npmOrgs: Array; + }, + null + >; + }; + npm: { + getNpmOrgs: FunctionReference< + "query", + "internal", + { names: Array }, + Array; downloadCount: number; + downloadCountUpdatedAt: number; name: string; - }, + updatedAt: number; + }> + >; + updateNpmOrg: FunctionReference< + "mutation", + "internal", + { name: string }, + any + >; + updateNpmOrgStats: FunctionReference< + "action", + "internal", + { org: string; page?: number }, any >; - updateNpmPackages: FunctionReference< + updateNpmPackagesForOrg: FunctionReference< "mutation", "internal", { + org: string; packages: Array<{ dayOfWeekAverages: Array; downloadCount: number; diff --git a/convex/stats.ts b/convex/stats.ts index 99a681ab..750343ad 100644 --- a/convex/stats.ts +++ b/convex/stats.ts @@ -6,4 +6,4 @@ export const ossStats = new OssStats(components.ossStats, { npmOrgs: ['tanstack'], }) -export const { getGithubOwner, getNpmOrg, sync } = ossStats.api() +export const { getGithubOwner, getNpmOrg, sync, clearAndSync } = ossStats.api() diff --git a/package.json b/package.json index 16282afa..9221cd66 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "@convex-dev/react-query": "0.0.0-alpha.8", "@docsearch/css": "^3.5.2", "@docsearch/react": "^3.5.2", - "@erquhart/convex-oss-stats": "^0.3.3", + "@erquhart/convex-oss-stats": "0.3.5", "@headlessui/react": "1.7.18", "@number-flow/react": "^0.4.1", "@octokit/graphql": "^7.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 047754ab..d7bde316 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: ^3.5.2 version: 3.6.0(@algolia/client-search@5.17.0)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.17.3) '@erquhart/convex-oss-stats': - specifier: ^0.3.3 - version: 0.3.3(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 0.3.5 + version: 0.3.5(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8) '@headlessui/react': specifier: 1.7.18 version: 1.7.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1096,8 +1096,8 @@ packages: '@emotion/weak-memoize@0.2.5': resolution: {integrity: sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==} - '@erquhart/convex-oss-stats@0.3.3': - resolution: {integrity: sha512-pE7rBaVEyfhKmTaueFGe1rdGeA76WNMguXnjquqc11uwfiKofP3ZUKthgpIdcwb2cS27vQniEdIpd87qiU+DOA==} + '@erquhart/convex-oss-stats@0.3.5': + resolution: {integrity: sha512-30ptwWfbgMDwmBIYWV/FmhvVepJpgCU445tONaoc7qf5DtEkbIvh0q8q34VWU+InISk0ryhJDHDmBfLC+rtqZg==} peerDependencies: convex: ~1.16.5 || ~1.17.0 react: ^17.0.2 || ^18.0.0 || ^19.0.0-0 @@ -3533,6 +3533,22 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + convex-helpers@0.1.67: + resolution: {integrity: sha512-nnb0W2FYKbqJt00UH6raZ/J0A1+CbcFdqmFJTDvvSR1P4RhwBj1TBzS3/B1G1nMYebX3gguSwOWfO0s2rcB/wg==} + hasBin: true + peerDependencies: + convex: ^1.13.0 + hono: ^4.0.5 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + zod: ^3.22.4 + peerDependenciesMeta: + hono: + optional: true + react: + optional: true + zod: + optional: true + convex@1.17.2: resolution: {integrity: sha512-h12LChRZLGSGTIiqtBMyM7BT+zHxpUjtA1YI2vhFza7x+9E1CYzP8c2En6wKnZ9Dr2PYE4vWqUg8EHkBr0hI5A==} engines: {node: '>=18.0.0', npm: '>=7.0.0'} @@ -7776,13 +7792,14 @@ snapshots: '@emotion/weak-memoize@0.2.5': {} - '@erquhart/convex-oss-stats@0.3.3(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@erquhart/convex-oss-stats@0.3.5(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.23.8)': dependencies: '@convex-dev/crons': 0.1.5(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@octokit/graphql': 8.1.1 '@octokit/graphql-schema': 15.25.0 cheerio: 1.0.0 convex: 1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + convex-helpers: 0.1.67(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(zod@3.23.8) date-fns: 4.1.0 framer-motion: 11.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nano: 10.1.4 @@ -7795,7 +7812,9 @@ snapshots: - '@emotion/is-prop-valid' - debug - encoding + - hono - react-dom + - zod '@esbuild/aix-ppc64@0.20.2': optional: true @@ -10281,6 +10300,13 @@ snapshots: convert-source-map@2.0.0: {} + convex-helpers@0.1.67(convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(zod@3.23.8): + dependencies: + convex: 1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + react: 18.3.1 + zod: 3.23.8 + convex@1.17.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: esbuild: 0.23.0