Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
- Skills: keep global sorting across pagination on `/skills` (thanks @CodeBBakGoSu, #98).
- Skills: allow updating skill description/summary from frontmatter on subsequent publishes (#312) (thanks @ianalloway).
- Skills/Web: prevent filtered pagination dead-ends and loading-state flicker on `/skills`; move highlighted browse filtering into server list query (#339) (thanks @Marvae).
- Web: align `/skills` total count with public visibility and format header count (thanks @rknoche6, #76).

## 0.6.1 - 2026-02-13

Expand Down
7 changes: 0 additions & 7 deletions convex/crons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,6 @@ crons.interval(
{},
)

crons.interval(
'global-stats-update',
{ minutes: 60 },
internal.statsMaintenance.updateGlobalStatsInternal,
{},
)

crons.interval('vt-pending-scans', { minutes: 5 }, internal.vt.pollPendingScans, { batchSize: 100 })

crons.interval('vt-cache-backfill', { minutes: 30 }, internal.vt.backfillActiveSkillsVTCache, {
Expand Down
7 changes: 0 additions & 7 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,6 @@ const skillStatBackfillState = defineTable({
updatedAt: v.number(),
}).index('by_key', ['key'])

const globalStats = defineTable({
key: v.string(),
activeSkillsCount: v.number(),
updatedAt: v.number(),
}).index('by_key', ['key'])

const skillStatEvents = defineTable({
skillId: v.id('skills'),
kind: v.union(
Expand Down Expand Up @@ -580,7 +574,6 @@ export default defineSchema({
skillDailyStats,
skillLeaderboards,
skillStatBackfillState,
globalStats,
skillStatEvents,
skillStatUpdateCursors,
comments,
Expand Down
12 changes: 0 additions & 12 deletions convex/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1632,18 +1632,6 @@ function isCursorParseError(error: unknown) {
return false
}

export const countPublicSkills = query({
args: {},
handler: async (ctx) => {
const stats = await ctx.db
.query('globalStats')
.withIndex('by_key', (q) => q.eq('key', 'default'))
.unique()
// Null means global stats haven't been initialized yet.
return stats?.activeSkillsCount ?? null
},
})

function sortToIndex(
sort: 'downloads' | 'stars' | 'installsCurrent' | 'installsAllTime',
):
Expand Down
43 changes: 0 additions & 43 deletions convex/statsMaintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@ import { internal } from './_generated/api'
import type { Doc } from './_generated/dataModel'
import type { ActionCtx } from './_generated/server'
import { internalAction, internalMutation, internalQuery } from './_generated/server'
import { toPublicSkill } from './lib/public'

const DEFAULT_BATCH_SIZE = 200
const MAX_BATCH_SIZE = 1000
const DEFAULT_MAX_BATCHES = 5
const MAX_MAX_BATCHES = 50
const BACKFILL_STATE_KEY = 'default'
const GLOBAL_STATS_PAGE_SIZE = 500

export const backfillSkillStatFieldsInternal = internalMutation({
args: {
Expand Down Expand Up @@ -301,44 +299,3 @@ export const runReconcileSkillStarCountsInternal = internalAction({
function clampInt(value: number, min: number, max: number) {
return Math.min(Math.max(value, min), max)
}

export const updateGlobalStatsInternal = internalMutation({
args: {},
handler: async (ctx) => {
let count = 0
let cursor: string | null = null

while (true) {
const { page, isDone, continueCursor } = await ctx.db
.query('skills')
.withIndex('by_active_updated', (q) => q.eq('softDeletedAt', undefined))
.order('asc')
.paginate({ cursor, numItems: GLOBAL_STATS_PAGE_SIZE })

for (const skill of page) {
if (toPublicSkill(skill)) {
count += 1
}
}

if (isDone) break
cursor = continueCursor
}

const now = Date.now()
const existing = await ctx.db
.query('globalStats')
.withIndex('by_key', (q) => q.eq('key', 'default'))
.unique()

if (existing) {
await ctx.db.patch(existing._id, { activeSkillsCount: count, updatedAt: now })
} else {
await ctx.db.insert('globalStats', {
key: 'default',
activeSkillsCount: count,
updatedAt: now,
})
}
},
})
4 changes: 0 additions & 4 deletions src/__tests__/skills-index-load-more.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { SkillsIndex } from '../routes/skills/index'

const navigateMock = vi.fn()
const useActionMock = vi.fn()
const useQueryMock = vi.fn()
const usePaginatedQueryMock = vi.fn()
let searchMock: Record<string, unknown> = {}

Expand All @@ -22,19 +21,16 @@ vi.mock('@tanstack/react-router', () => ({

vi.mock('convex/react', () => ({
useAction: (...args: unknown[]) => useActionMock(...args),
useQuery: (...args: unknown[]) => useQueryMock(...args),
usePaginatedQuery: (...args: unknown[]) => usePaginatedQueryMock(...args),
}))

describe('SkillsIndex load-more observer', () => {
beforeEach(() => {
usePaginatedQueryMock.mockReset()
useActionMock.mockReset()
useQueryMock.mockReset()
navigateMock.mockReset()
searchMock = {}
useActionMock.mockReturnValue(() => Promise.resolve([]))
useQueryMock.mockReturnValue(null)
})

afterEach(() => {
Expand Down
4 changes: 0 additions & 4 deletions src/__tests__/skills-index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { SkillsIndex } from '../routes/skills/index'

const navigateMock = vi.fn()
const useActionMock = vi.fn()
const useQueryMock = vi.fn()
const usePaginatedQueryMock = vi.fn()
let searchMock: Record<string, unknown> = {}

Expand All @@ -22,19 +21,16 @@ vi.mock('@tanstack/react-router', () => ({

vi.mock('convex/react', () => ({
useAction: (...args: unknown[]) => useActionMock(...args),
useQuery: (...args: unknown[]) => useQueryMock(...args),
usePaginatedQuery: (...args: unknown[]) => usePaginatedQueryMock(...args),
}))

describe('SkillsIndex', () => {
beforeEach(() => {
usePaginatedQueryMock.mockReset()
useActionMock.mockReset()
useQueryMock.mockReset()
navigateMock.mockReset()
searchMock = {}
useActionMock.mockReturnValue(() => Promise.resolve([]))
useQueryMock.mockReturnValue(null)
// Default: return empty results with Exhausted status
usePaginatedQueryMock.mockReturnValue({
results: [],
Expand Down
6 changes: 0 additions & 6 deletions src/routes/skills/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { createFileRoute, redirect } from '@tanstack/react-router'
import { useQuery } from 'convex/react'
import { useRef } from 'react'
import { api } from '../../../convex/_generated/api'
import { parseSort } from './-params'
import { SkillsResults } from './-SkillsResults'
import { SkillsToolbar } from './-SkillsToolbar'
Expand Down Expand Up @@ -51,9 +49,6 @@ export function SkillsIndex() {
const navigate = Route.useNavigate()
const search = Route.useSearch()
const searchInputRef = useRef<HTMLInputElement>(null)
const totalSkills = useQuery(api.skills.countPublicSkills)
const totalSkillsText =
typeof totalSkills === 'number' ? totalSkills.toLocaleString('en-US') : null

const model = useSkillsBrowseModel({
navigate,
Expand All @@ -66,7 +61,6 @@ export function SkillsIndex() {
<header className="skills-header-top">
<h1 className="section-title" style={{ marginBottom: 8 }}>
Skills
{totalSkillsText && <span style={{ opacity: 0.55 }}>{` (${totalSkillsText})`}</span>}
</h1>
<p className="section-subtitle" style={{ marginBottom: 0 }}>
{model.isLoadingSkills
Expand Down