@tscircuit/fake-snippets 0.0.109 → 0.0.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bun-formatcheck.yml +2 -2
- package/.github/workflows/bun-pver-release.yml +3 -3
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +2 -2
- package/.github/workflows/update-snapshots.yml +1 -1
- package/README.md +4 -0
- package/api/generated-index.js +37 -3
- package/biome.json +2 -1
- package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +32 -3
- package/bun-tests/fake-snippets-api/fixtures/preload.ts +18 -0
- package/bun-tests/fake-snippets-api/routes/orgs/add_member.test.ts +26 -0
- package/bun-tests/fake-snippets-api/routes/orgs/create.test.ts +37 -0
- package/bun-tests/fake-snippets-api/routes/orgs/get.test.ts +52 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list.test.ts +17 -0
- package/bun-tests/fake-snippets-api/routes/orgs/list_members.test.ts +23 -0
- package/bun-tests/fake-snippets-api/routes/orgs/remove_member.test.ts +81 -0
- package/bun-tests/fake-snippets-api/routes/orgs/update.test.ts +151 -0
- package/bun-tests/fake-snippets-api/routes/package_builds/get.test.ts +1 -1
- package/bun-tests/fake-snippets-api/routes/package_files/create.test.ts +15 -13
- package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +26 -24
- package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +9 -9
- package/bun-tests/fake-snippets-api/routes/package_files/download.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_files/get.test.ts +38 -28
- package/bun-tests/fake-snippets-api/routes/package_files/list.test.ts +23 -15
- package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +33 -0
- package/bun-tests/fake-snippets-api/routes/package_releases/get.test.ts +4 -4
- package/bun-tests/fake-snippets-api/routes/package_releases/get_image_generation_fields.test.ts +38 -0
- package/bun-tests/fake-snippets-api/routes/packages/create.test.ts +19 -0
- package/bun-tests/fake-snippets-api/routes/packages/fork.test.ts +3 -4
- package/bun-tests/fake-snippets-api/routes/packages/get.test.ts +30 -0
- package/bun-tests/fake-snippets-api/routes/packages/images.test.ts +4 -2
- package/bun-tests/fake-snippets-api/routes/packages/list-1.test.ts +34 -0
- package/bun.lock +361 -453
- package/bunfig.toml +2 -1
- package/dist/bundle.js +1313 -639
- package/dist/index.d.ts +313 -6
- package/dist/index.js +328 -24
- package/dist/schema.d.ts +290 -1
- package/dist/schema.js +54 -1
- package/fake-snippets-api/lib/db/autoload-dev-packages.ts +31 -20
- package/fake-snippets-api/lib/db/db-client.ts +219 -4
- package/fake-snippets-api/lib/db/schema.ts +63 -1
- package/fake-snippets-api/lib/db/seed.ts +100 -0
- package/fake-snippets-api/lib/middleware/with-session-auth.ts +60 -8
- package/fake-snippets-api/lib/package_file/get-package-file-id-from-file-descriptor.ts +2 -2
- package/fake-snippets-api/lib/public-mapping/public-map-org.ts +33 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-build.ts +10 -0
- package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +17 -0
- package/fake-snippets-api/routes/api/orgs/add_member.ts +52 -0
- package/fake-snippets-api/routes/api/orgs/create.ts +48 -0
- package/fake-snippets-api/routes/api/orgs/get.ts +39 -0
- package/fake-snippets-api/routes/api/orgs/list.ts +31 -0
- package/fake-snippets-api/routes/api/orgs/list_members.ts +60 -0
- package/fake-snippets-api/routes/api/orgs/remove_member.ts +46 -0
- package/fake-snippets-api/routes/api/orgs/update.ts +118 -0
- package/fake-snippets-api/routes/api/package_files/get.ts +3 -6
- package/fake-snippets-api/routes/api/package_files/list.ts +7 -4
- package/fake-snippets-api/routes/api/packages/create.ts +57 -10
- package/fake-snippets-api/routes/api/packages/get.ts +23 -0
- package/fake-snippets-api/routes/api/packages/images/[owner_github_username]/[unscoped_name]/[view_format].ts +13 -11
- package/fake-snippets-api/routes/api/packages/list.ts +29 -2
- package/fake-snippets-api/routes/api/packages/update_ai_description.ts +37 -0
- package/package.json +25 -19
- package/renovate.json +1 -1
- package/scripts/generate-sitemap.ts +1 -1
- package/src/App.tsx +27 -8
- package/src/ContextProviders.tsx +25 -2
- package/src/components/CircuitJsonImportDialog.tsx +1 -1
- package/src/components/CmdKMenu.tsx +281 -247
- package/src/components/DownloadButtonAndMenu.tsx +17 -5
- package/src/components/FileSidebar.tsx +11 -17
- package/src/components/Footer.tsx +8 -9
- package/src/components/Header.tsx +19 -32
- package/src/components/Header2.tsx +16 -32
- package/src/components/HeaderDropdown.tsx +13 -8
- package/src/components/HeaderLogin.tsx +43 -15
- package/src/components/NotFound.tsx +5 -5
- package/src/components/PackageBreadcrumb.tsx +6 -12
- package/src/components/PackageSearchResults.tsx +1 -1
- package/src/components/PrefetchPageLink.tsx +7 -1
- package/src/components/ProfileRouter.tsx +32 -0
- package/src/components/SearchComponent.tsx +12 -8
- package/src/components/SentryNotFoundReporter.tsx +44 -0
- package/src/components/UserCard.tsx +80 -0
- package/src/components/ViewPackagePage/components/build-status.tsx +1 -1
- package/src/components/ViewPackagePage/components/important-files-view.tsx +105 -34
- package/src/components/ViewPackagePage/components/main-content-header.tsx +10 -6
- package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +1 -1
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +54 -19
- package/src/components/ViewPackagePage/components/package-header.tsx +25 -33
- package/src/components/ViewPackagePage/components/preview-image-squares.tsx +11 -18
- package/src/components/ViewPackagePage/components/repo-page-content.tsx +12 -5
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +16 -10
- package/src/components/ViewPackagePage/components/sidebar-releases-section.tsx +11 -11
- package/src/components/ViewPackagePage/components/tab-views/pcb-view.tsx +1 -2
- package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +2 -1
- package/src/components/dialogs/GitHubRepositorySelector.tsx +56 -49
- package/src/components/dialogs/edit-package-details-dialog.tsx +5 -6
- package/src/components/dialogs/import-component-dialog.tsx +16 -9
- package/src/components/dialogs/import-package-dialog.tsx +3 -2
- package/src/components/dialogs/new-package-save-prompt-dialog.tsx +190 -0
- package/src/components/organization/OrganizationCard.tsx +206 -0
- package/src/components/organization/OrganizationCardSkeleton.tsx +55 -0
- package/src/components/organization/OrganizationHeader.tsx +154 -0
- package/src/components/organization/OrganizationMembers.tsx +146 -0
- package/src/components/package-port/CodeAndPreview.tsx +15 -12
- package/src/components/package-port/CodeEditor.tsx +4 -30
- package/src/components/package-port/CodeEditorHeader.tsx +123 -61
- package/src/components/package-port/EditorNav.tsx +32 -49
- package/src/components/preview/ConnectedPackagesList.tsx +8 -8
- package/src/components/preview/ConnectedRepoOverview.tsx +102 -2
- package/src/components/preview/PackageReleasesDashboard.tsx +23 -11
- package/src/components/ui/tree-view.tsx +6 -3
- package/src/hooks/use-add-org-member-mutation.ts +51 -0
- package/src/hooks/use-create-org-mutation.ts +38 -0
- package/src/hooks/use-create-package-mutation.ts +3 -0
- package/src/hooks/use-current-package-release.ts +4 -3
- package/src/hooks/use-download-zip.ts +2 -2
- package/src/hooks/use-global-store.ts +6 -4
- package/src/hooks/use-hydration.ts +30 -0
- package/src/hooks/use-jlcpcb-component-import.tsx +164 -0
- package/src/hooks/use-list-org-members.ts +27 -0
- package/src/hooks/use-list-user-orgs.ts +25 -0
- package/src/hooks/use-org-by-github-handle.ts +26 -0
- package/src/hooks/use-org.ts +24 -0
- package/src/hooks/use-organization.ts +42 -0
- package/src/hooks/use-package-as-snippet.ts +4 -2
- package/src/hooks/use-package-builds.ts +6 -2
- package/src/hooks/use-package-files.ts +5 -3
- package/src/hooks/use-package-release-by-id-or-version.ts +29 -20
- package/src/hooks/use-package-release-images.ts +105 -0
- package/src/hooks/use-package-release.ts +2 -2
- package/src/hooks/use-package-stars.ts +80 -4
- package/src/hooks/use-preview-images.ts +6 -3
- package/src/hooks/use-remove-org-member-mutation.ts +32 -0
- package/src/hooks/use-update-ai-description-mutation.ts +42 -0
- package/src/hooks/use-update-org-mutation.ts +41 -0
- package/src/hooks/use-warn-user-on-page-change.ts +71 -4
- package/src/hooks/useFileManagement.ts +51 -22
- package/src/hooks/useOptimizedPackageFilesLoader.ts +11 -24
- package/src/hooks/usePackageFilesLoader.ts +2 -2
- package/src/hooks/useUpdatePackageFilesMutation.ts +13 -1
- package/src/lib/download-fns/download-gltf-from-circuit-json.ts +1 -1
- package/src/lib/download-fns/download-kicad-files.ts +22 -11
- package/src/lib/download-fns/download-step.ts +12 -0
- package/src/lib/normalize-svg-for-tile.ts +50 -0
- package/src/lib/posthog.ts +11 -9
- package/src/lib/react-query-api-failure-tracking.ts +148 -0
- package/src/lib/sentry.ts +14 -0
- package/src/lib/templates/blank-circuit-board-template.ts +0 -4
- package/src/lib/ts-lib-cache.ts +122 -7
- package/src/lib/utils/checkIfManualEditsImported.ts +4 -4
- package/src/lib/utils/findTargetFile.ts +45 -10
- package/src/lib/utils/isComponentExported.ts +2 -1
- package/src/main.tsx +2 -1
- package/src/pages/create-organization.tsx +169 -0
- package/src/pages/dashboard.tsx +38 -6
- package/src/pages/datasheet.tsx +1 -1
- package/src/pages/datasheets.tsx +3 -3
- package/src/pages/editor.tsx +4 -6
- package/src/pages/landing.tsx +6 -6
- package/src/pages/latest.tsx +3 -0
- package/src/pages/organization-profile.tsx +199 -0
- package/src/pages/organization-settings.tsx +569 -0
- package/src/pages/package-editor.tsx +21 -21
- package/src/pages/preview-release.tsx +75 -145
- package/src/pages/quickstart.tsx +159 -123
- package/src/pages/release-detail.tsx +119 -31
- package/src/pages/search.tsx +197 -57
- package/src/pages/settings-redirect.tsx +44 -0
- package/src/pages/trending.tsx +29 -20
- package/src/pages/user-profile.tsx +58 -7
- package/src/pages/user-settings.tsx +161 -0
- package/src/pages/view-package.tsx +30 -16
- package/vite.config.ts +9 -0
- package/fake-snippets-api/routes/api/autocomplete/create_autocomplete.ts +0 -133
- package/src/components/JLCPCBImportDialog.tsx +0 -280
- package/src/components/PackageBuildsPage/LogContent.tsx +0 -72
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -113
- package/src/components/PackageBuildsPage/build-preview-content.tsx +0 -56
- package/src/components/PackageBuildsPage/collapsible-section.tsx +0 -63
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +0 -166
- package/src/components/PackageBuildsPage/package-build-header.tsx +0 -79
- package/src/components/PageSearchComponent.tsx +0 -148
- package/src/pages/package-builds.tsx +0 -33
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React, { useCallback } from "react"
|
|
2
|
+
import { useToast } from "@/hooks/use-toast"
|
|
3
|
+
import { useGlobalStore } from "@/hooks/use-global-store"
|
|
4
|
+
import { useLocation } from "wouter"
|
|
5
|
+
import { useCreatePackageMutation } from "@/hooks/use-create-package-mutation"
|
|
6
|
+
import { useCreatePackageReleaseMutation } from "@/hooks/use-create-package-release-mutation"
|
|
7
|
+
import { useCreatePackageFilesMutation } from "@/hooks/use-create-package-files-mutation"
|
|
8
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
9
|
+
import { JlcpcbComponentTsxLoadedPayload } from "@tscircuit/runframe/runner"
|
|
10
|
+
|
|
11
|
+
export const useJlcpcbComponentImport = () => {
|
|
12
|
+
const { toastLibrary } = useToast()
|
|
13
|
+
const session = useGlobalStore((s) => s.session)
|
|
14
|
+
const [, navigate] = useLocation()
|
|
15
|
+
const axios = useAxios()
|
|
16
|
+
const createPackageMutation = useCreatePackageMutation()
|
|
17
|
+
const createReleaseMutation = useCreatePackageReleaseMutation()
|
|
18
|
+
const createFilesMutation = useCreatePackageFilesMutation()
|
|
19
|
+
|
|
20
|
+
const runImport = useCallback(
|
|
21
|
+
async ({ result, tsx }: JlcpcbComponentTsxLoadedPayload) => {
|
|
22
|
+
if (!session) {
|
|
23
|
+
throw new Error("You must be logged in to import from JLCPCB")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const partNumber = result.component.partNumber || "component"
|
|
27
|
+
|
|
28
|
+
const normalizePartNumber = (input: string) =>
|
|
29
|
+
input
|
|
30
|
+
.replace(/^@/, "")
|
|
31
|
+
.trim()
|
|
32
|
+
.replace(/\s+/g, "-")
|
|
33
|
+
.replace(/[^a-zA-Z0-9-_/]/g, "-")
|
|
34
|
+
.replace(/--+/g, "-")
|
|
35
|
+
.replace(/-+$/g, "")
|
|
36
|
+
.replace(/^-+/g, "") || "component"
|
|
37
|
+
|
|
38
|
+
const componentSlug = normalizePartNumber(partNumber)
|
|
39
|
+
const packageName = `${session.github_username}/${componentSlug}`
|
|
40
|
+
|
|
41
|
+
const fetchExistingPackage = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const { data } = await axios.post("/packages/get", {
|
|
44
|
+
name: packageName,
|
|
45
|
+
})
|
|
46
|
+
return data.package
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
const status = error?.response?.status || error?.status
|
|
49
|
+
if (status === 404) return null
|
|
50
|
+
throw error
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const existingPackage = await fetchExistingPackage()
|
|
55
|
+
if (existingPackage) {
|
|
56
|
+
navigate(`/editor?package_id=${existingPackage.package_id}`)
|
|
57
|
+
return {
|
|
58
|
+
partNumber,
|
|
59
|
+
packageId: existingPackage.package_id,
|
|
60
|
+
existing: true,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const description =
|
|
65
|
+
result.component.description ||
|
|
66
|
+
`Generated from JLCPCB part number ${partNumber}`
|
|
67
|
+
|
|
68
|
+
let newPackage
|
|
69
|
+
try {
|
|
70
|
+
newPackage = await createPackageMutation.mutateAsync({
|
|
71
|
+
name: packageName,
|
|
72
|
+
description,
|
|
73
|
+
})
|
|
74
|
+
} catch (error) {
|
|
75
|
+
const fallbackPackage = await fetchExistingPackage()
|
|
76
|
+
if (fallbackPackage) {
|
|
77
|
+
navigate(`/editor?package_id=${fallbackPackage.package_id}`)
|
|
78
|
+
return {
|
|
79
|
+
partNumber,
|
|
80
|
+
packageId: fallbackPackage.package_id,
|
|
81
|
+
existing: true,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const release = await createReleaseMutation.mutateAsync({
|
|
88
|
+
package_id: newPackage.package_id,
|
|
89
|
+
version: "0.1.0",
|
|
90
|
+
is_latest: true,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
await createFilesMutation.mutateAsync({
|
|
94
|
+
file_path: "index.tsx",
|
|
95
|
+
content_text: tsx,
|
|
96
|
+
package_release_id: release.package_release_id,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
navigate(`/editor?package_id=${newPackage.package_id}`)
|
|
100
|
+
return {
|
|
101
|
+
partNumber,
|
|
102
|
+
packageId: newPackage.package_id,
|
|
103
|
+
existing: false,
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
[
|
|
107
|
+
createFilesMutation,
|
|
108
|
+
createPackageMutation,
|
|
109
|
+
createReleaseMutation,
|
|
110
|
+
axios,
|
|
111
|
+
navigate,
|
|
112
|
+
session,
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const importComponent = useCallback(
|
|
117
|
+
async (payload: JlcpcbComponentTsxLoadedPayload) => {
|
|
118
|
+
const importPromise = runImport(payload)
|
|
119
|
+
|
|
120
|
+
toastLibrary.promise(importPromise, {
|
|
121
|
+
loading: "Importing component...",
|
|
122
|
+
success: ({ partNumber, existing }) => (
|
|
123
|
+
<p>
|
|
124
|
+
{existing
|
|
125
|
+
? `Component ${partNumber} already exists. Opening package in the editor.`
|
|
126
|
+
: `Component ${partNumber} imported successfully. Opening package in the editor.`}
|
|
127
|
+
</p>
|
|
128
|
+
),
|
|
129
|
+
error: (error) => (
|
|
130
|
+
<p>
|
|
131
|
+
{error instanceof Error
|
|
132
|
+
? error.message
|
|
133
|
+
: "Failed to import component"}
|
|
134
|
+
</p>
|
|
135
|
+
),
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
await importPromise
|
|
139
|
+
},
|
|
140
|
+
[runImport, toastLibrary],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
importComponent,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export const openJlcpcbImportIssue = (
|
|
149
|
+
partNumber: string,
|
|
150
|
+
errorMessage: string,
|
|
151
|
+
) => {
|
|
152
|
+
const url = getJlcpcbImportIssueUrl(partNumber, errorMessage)
|
|
153
|
+
window.open(url, "_blank")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const getJlcpcbImportIssueUrl = (partNumber: string, errorMessage: string) => {
|
|
157
|
+
const issueTitle = `[${partNumber}] Failed to import from JLCPCB`
|
|
158
|
+
const issueBody = `I tried to import the part number ${partNumber} from JLCPCB, but it failed. Here's the error I got:\n\`\`\`\n${errorMessage}\n\`\`\`\n\nCould be an issue in \`fetchEasyEDAComponent\` or \`convertRawEasyEdaToTs\``
|
|
159
|
+
const issueLabels = "snippets,good first issue"
|
|
160
|
+
const url = `https://github.com/tscircuit/easyeda-converter/issues/new?title=${encodeURIComponent(
|
|
161
|
+
issueTitle,
|
|
162
|
+
)}&body=${encodeURIComponent(issueBody)}&labels=${encodeURIComponent(issueLabels)}`
|
|
163
|
+
return url
|
|
164
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { Account } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export const useListOrgMembers = ({
|
|
6
|
+
orgId,
|
|
7
|
+
orgName,
|
|
8
|
+
}: { orgId?: string; orgName?: string }) => {
|
|
9
|
+
const axios = useAxios()
|
|
10
|
+
return useQuery<Account[], Error & { status: number }>(
|
|
11
|
+
["orgs", "members", orgId || orgName],
|
|
12
|
+
async () => {
|
|
13
|
+
if (!orgId && !orgName) {
|
|
14
|
+
throw new Error("Organization ID or name is required")
|
|
15
|
+
}
|
|
16
|
+
const params = orgId ? { org_id: orgId } : { name: orgName }
|
|
17
|
+
const { data } = await axios.get("/orgs/list_members", { params })
|
|
18
|
+
return data.members
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
enabled: Boolean(orgId || orgName),
|
|
22
|
+
retry: false,
|
|
23
|
+
refetchOnWindowFocus: false,
|
|
24
|
+
keepPreviousData: true,
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { useGlobalStore } from "./use-global-store"
|
|
5
|
+
|
|
6
|
+
export const useListUserOrgs = (githubHandle?: string) => {
|
|
7
|
+
const axios = useAxios()
|
|
8
|
+
const session = useGlobalStore((s) => s.session)
|
|
9
|
+
const github_handle = githubHandle || session?.github_username
|
|
10
|
+
|
|
11
|
+
return useQuery<PublicOrgSchema[], Error & { status: number }>(
|
|
12
|
+
["orgs", "list", github_handle],
|
|
13
|
+
async () => {
|
|
14
|
+
const { data } = await axios.get("/orgs/list", {
|
|
15
|
+
...(github_handle && { params: { github_handle } }),
|
|
16
|
+
})
|
|
17
|
+
return data.orgs
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
retry: false,
|
|
21
|
+
refetchOnWindowFocus: false,
|
|
22
|
+
enabled: Boolean(github_handle),
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
import { useGlobalStore } from "./use-global-store"
|
|
5
|
+
|
|
6
|
+
export const useOrgByGithubHandle = (githubHandle: string | null) => {
|
|
7
|
+
const axios = useAxios()
|
|
8
|
+
const session = useGlobalStore((s) => s.session)
|
|
9
|
+
return useQuery<PublicOrgSchema, Error & { status: number }>(
|
|
10
|
+
["orgs", "by-github-handle", githubHandle],
|
|
11
|
+
async () => {
|
|
12
|
+
if (!githubHandle) {
|
|
13
|
+
throw new Error("GitHub handle is required")
|
|
14
|
+
}
|
|
15
|
+
const { data } = await axios.get("/orgs/get", {
|
|
16
|
+
params: { github_handle: githubHandle },
|
|
17
|
+
})
|
|
18
|
+
return data.org
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
enabled: Boolean(githubHandle),
|
|
22
|
+
retry: false,
|
|
23
|
+
refetchOnWindowFocus: false,
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export const useOrg = ({ orgName }: { orgName: string | null }) => {
|
|
6
|
+
const axios = useAxios()
|
|
7
|
+
return useQuery<PublicOrgSchema, Error & { status: number }>(
|
|
8
|
+
["orgs", orgName],
|
|
9
|
+
async () => {
|
|
10
|
+
if (!orgName) {
|
|
11
|
+
throw new Error("Organization name is required")
|
|
12
|
+
}
|
|
13
|
+
const { data } = await axios.get("/orgs/get", {
|
|
14
|
+
params: { org_name: orgName },
|
|
15
|
+
})
|
|
16
|
+
return data.org
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
enabled: Boolean(orgName),
|
|
20
|
+
retry: false,
|
|
21
|
+
refetchOnWindowFocus: false,
|
|
22
|
+
},
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useQuery } from "react-query"
|
|
2
|
+
import { useAxios } from "@/hooks/use-axios"
|
|
3
|
+
import type { PublicOrgSchema } from "fake-snippets-api/lib/db/schema"
|
|
4
|
+
|
|
5
|
+
export const useOrganization = ({
|
|
6
|
+
orgId,
|
|
7
|
+
orgName,
|
|
8
|
+
github_handle,
|
|
9
|
+
}: { orgId?: string; orgName?: string; github_handle?: string }) => {
|
|
10
|
+
const axios = useAxios()
|
|
11
|
+
|
|
12
|
+
const orgQuery = useQuery<PublicOrgSchema, Error & { status: number }>(
|
|
13
|
+
["orgs", "get", orgId || orgName || github_handle],
|
|
14
|
+
async () => {
|
|
15
|
+
if (!orgId && !orgName && !github_handle) {
|
|
16
|
+
throw new Error("Organization ID, name, or GitHub handle is required")
|
|
17
|
+
}
|
|
18
|
+
const params = orgId
|
|
19
|
+
? { org_id: orgId }
|
|
20
|
+
: orgName
|
|
21
|
+
? { org_name: orgName }
|
|
22
|
+
: { github_handle }
|
|
23
|
+
const { data } = await axios.get("/orgs/get", { params })
|
|
24
|
+
return data.org
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
enabled: Boolean(orgId || orgName),
|
|
28
|
+
retry: false,
|
|
29
|
+
refetchOnWindowFocus: false,
|
|
30
|
+
keepPreviousData: true,
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
organization: orgQuery.data,
|
|
36
|
+
membersCount: orgQuery.data?.member_count || 0,
|
|
37
|
+
packagesCount: orgQuery.data?.package_count || 0,
|
|
38
|
+
isLoading: orgQuery.isLoading,
|
|
39
|
+
isError: orgQuery.isError,
|
|
40
|
+
error: orgQuery.error,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -32,8 +32,10 @@ export const usePackageAsSnippet = (packageId: string | null) => {
|
|
|
32
32
|
if (!packageQuery.data?.latest_package_release_id) {
|
|
33
33
|
throw new Error("No latest release ID available")
|
|
34
34
|
}
|
|
35
|
-
const { data } = await axios.
|
|
36
|
-
|
|
35
|
+
const { data } = await axios.get("/package_files/list", {
|
|
36
|
+
params: {
|
|
37
|
+
package_release_id: packageQuery.data.latest_package_release_id,
|
|
38
|
+
},
|
|
37
39
|
})
|
|
38
40
|
return data.package_files
|
|
39
41
|
},
|
|
@@ -56,11 +56,14 @@ export const usePackageBuildsByReleaseId = (releaseId?: string | null) => {
|
|
|
56
56
|
return usePackageBuilds(releaseId ? { package_release_id: releaseId } : null)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export const usePackageBuild = (
|
|
59
|
+
export const usePackageBuild = (
|
|
60
|
+
packageBuildId: string | null,
|
|
61
|
+
options?: { include_logs?: boolean },
|
|
62
|
+
) => {
|
|
60
63
|
const axios = useAxios()
|
|
61
64
|
|
|
62
65
|
return useQuery<PackageBuild, Error & { status: number }>(
|
|
63
|
-
["packageBuild", packageBuildId],
|
|
66
|
+
["packageBuild", packageBuildId, options?.include_logs],
|
|
64
67
|
async () => {
|
|
65
68
|
if (!packageBuildId) {
|
|
66
69
|
throw new Error("package_build_id is required")
|
|
@@ -69,6 +72,7 @@ export const usePackageBuild = (packageBuildId: string | null) => {
|
|
|
69
72
|
const { data } = await axios.get("/package_builds/get", {
|
|
70
73
|
params: {
|
|
71
74
|
package_build_id: packageBuildId,
|
|
75
|
+
include_logs: options?.include_logs,
|
|
72
76
|
},
|
|
73
77
|
})
|
|
74
78
|
|
|
@@ -36,7 +36,9 @@ export const usePackageFile = (
|
|
|
36
36
|
async () => {
|
|
37
37
|
if (!query) return
|
|
38
38
|
|
|
39
|
-
const { data } = await axios.
|
|
39
|
+
const { data } = await axios.get("/package_files/get", {
|
|
40
|
+
params: query,
|
|
41
|
+
})
|
|
40
42
|
|
|
41
43
|
if (!data.package_file) {
|
|
42
44
|
throw new Error("Package file not found")
|
|
@@ -98,8 +100,8 @@ export const usePackageFiles = (packageReleaseId?: string | null) => {
|
|
|
98
100
|
if (!packageReleaseId) return []
|
|
99
101
|
|
|
100
102
|
try {
|
|
101
|
-
const { data } = await axios.
|
|
102
|
-
package_release_id: packageReleaseId,
|
|
103
|
+
const { data } = await axios.get("/package_files/list", {
|
|
104
|
+
params: { package_release_id: packageReleaseId },
|
|
103
105
|
})
|
|
104
106
|
|
|
105
107
|
if (!data.package_files) {
|
|
@@ -1,36 +1,45 @@
|
|
|
1
|
-
import {
|
|
2
|
-
usePackageReleaseById,
|
|
3
|
-
usePackageReleaseByNameAndVersion,
|
|
4
|
-
} from "./use-package-release"
|
|
1
|
+
import { usePackageRelease } from "./use-package-release"
|
|
5
2
|
import { isUuid } from "@/lib/utils/isUuid"
|
|
6
3
|
|
|
7
4
|
export const usePackageReleaseByIdOrVersion = (
|
|
8
5
|
releaseIdOrVersion: string | null,
|
|
9
6
|
packageName?: string | null,
|
|
7
|
+
options?: {
|
|
8
|
+
include_logs?: boolean
|
|
9
|
+
include_ai_review?: boolean
|
|
10
|
+
refetchInterval?:
|
|
11
|
+
| number
|
|
12
|
+
| false
|
|
13
|
+
| ((
|
|
14
|
+
data:
|
|
15
|
+
| import("fake-snippets-api/lib/db/schema").PackageRelease
|
|
16
|
+
| undefined,
|
|
17
|
+
) => number | false)
|
|
18
|
+
},
|
|
10
19
|
) => {
|
|
11
20
|
const isReleaseIdUuid = releaseIdOrVersion
|
|
12
21
|
? isUuid(releaseIdOrVersion)
|
|
13
22
|
: false
|
|
14
23
|
|
|
15
|
-
|
|
16
|
-
const releaseByIdQuery = usePackageReleaseById(
|
|
17
|
-
isReleaseIdUuid ? releaseIdOrVersion : null,
|
|
18
|
-
)
|
|
24
|
+
let query: Parameters<typeof usePackageRelease>[0] = null
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
? `${packageName}@${releaseIdOrVersion}`
|
|
26
|
+
if (isReleaseIdUuid) {
|
|
27
|
+
query = releaseIdOrVersion
|
|
28
|
+
? { package_release_id: releaseIdOrVersion }
|
|
24
29
|
: null
|
|
30
|
+
} else if (packageName && releaseIdOrVersion) {
|
|
31
|
+
query = {
|
|
32
|
+
package_name_with_version: `${packageName}@${releaseIdOrVersion}`,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
if (query && options?.include_logs !== undefined) {
|
|
37
|
+
query.include_logs = options.include_logs
|
|
38
|
+
}
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return releaseByIdQuery
|
|
33
|
-
} else {
|
|
34
|
-
return releaseByVersionQuery
|
|
40
|
+
if (query && options?.include_ai_review !== undefined) {
|
|
41
|
+
query.include_ai_review = options.include_ai_review
|
|
35
42
|
}
|
|
43
|
+
|
|
44
|
+
return usePackageRelease(query, { refetchInterval: options?.refetchInterval })
|
|
36
45
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { useMemo, useCallback } from "react"
|
|
2
|
+
import { useQueries } from "react-query"
|
|
3
|
+
import { useAxios } from "./use-axios"
|
|
4
|
+
import { useParams } from "wouter"
|
|
5
|
+
import { useApiBaseUrl } from "./use-packages-base-api-url"
|
|
6
|
+
|
|
7
|
+
interface UsePackageReleaseImagesProps {
|
|
8
|
+
packageReleaseId?: string | null
|
|
9
|
+
availableFilePaths?: string[] | undefined
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ViewConfig {
|
|
13
|
+
id: string
|
|
14
|
+
label: string
|
|
15
|
+
backgroundClass: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const VIEWS: ViewConfig[] = [
|
|
19
|
+
{
|
|
20
|
+
id: "3d",
|
|
21
|
+
label: "3D",
|
|
22
|
+
backgroundClass: "bg-gray-100",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "pcb",
|
|
26
|
+
label: "PCB",
|
|
27
|
+
backgroundClass: "bg-black",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "schematic",
|
|
31
|
+
label: "Schematic",
|
|
32
|
+
backgroundClass: "bg-[#F5F1ED]",
|
|
33
|
+
},
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
export function usePackageReleaseImages({
|
|
37
|
+
packageReleaseId,
|
|
38
|
+
}: UsePackageReleaseImagesProps) {
|
|
39
|
+
const apiurl = useApiBaseUrl()
|
|
40
|
+
const axios = useAxios()
|
|
41
|
+
const { author, packageName } = useParams()
|
|
42
|
+
|
|
43
|
+
const createQueryFn = useCallback(
|
|
44
|
+
(viewId: string) => async () => {
|
|
45
|
+
if (!packageReleaseId || !author || !packageName) return null
|
|
46
|
+
|
|
47
|
+
const imageUrl = `${apiurl}/packages/images/${author}/${packageName}/${viewId}.png?package_release_id=${packageReleaseId}`
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const response = await axios.head(imageUrl)
|
|
51
|
+
return response.status === 200 ? imageUrl : null
|
|
52
|
+
} catch {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
[packageReleaseId, author, packageName, apiurl, axios],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
const queryConfigs = useMemo(
|
|
60
|
+
() =>
|
|
61
|
+
VIEWS.map((view) => ({
|
|
62
|
+
queryKey: [
|
|
63
|
+
"packageReleaseImage",
|
|
64
|
+
packageReleaseId,
|
|
65
|
+
view.id,
|
|
66
|
+
author,
|
|
67
|
+
packageName,
|
|
68
|
+
],
|
|
69
|
+
queryFn: createQueryFn(view.id),
|
|
70
|
+
enabled: Boolean(packageReleaseId && author && packageName),
|
|
71
|
+
retry: false,
|
|
72
|
+
refetchOnWindowFocus: false,
|
|
73
|
+
refetchOnMount: false,
|
|
74
|
+
refetchOnReconnect: false,
|
|
75
|
+
staleTime: Infinity,
|
|
76
|
+
cacheTime: Infinity,
|
|
77
|
+
})),
|
|
78
|
+
[packageReleaseId, author, packageName, createQueryFn],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
const queries = useQueries(queryConfigs)
|
|
82
|
+
|
|
83
|
+
const availableViews = useMemo(() => {
|
|
84
|
+
const result = []
|
|
85
|
+
|
|
86
|
+
for (let i = 0; i < VIEWS.length; i++) {
|
|
87
|
+
const view = VIEWS[i]
|
|
88
|
+
const query = queries[i]
|
|
89
|
+
|
|
90
|
+
if (query.isLoading || query.data) {
|
|
91
|
+
result.push({
|
|
92
|
+
id: view.id,
|
|
93
|
+
label: view.label,
|
|
94
|
+
imageUrl: query.data || "",
|
|
95
|
+
isLoading: query.isLoading,
|
|
96
|
+
backgroundClass: view.backgroundClass,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
}, [queries])
|
|
103
|
+
|
|
104
|
+
return { availableViews }
|
|
105
|
+
}
|
|
@@ -40,8 +40,8 @@ export const usePackageRelease = (
|
|
|
40
40
|
|
|
41
41
|
const { data } = await axios.post("/package_releases/get", query, {
|
|
42
42
|
params: {
|
|
43
|
-
include_logs: query.include_logs,
|
|
44
|
-
include_ai_review: query.include_ai_review,
|
|
43
|
+
include_logs: Boolean(query.include_logs),
|
|
44
|
+
include_ai_review: Boolean(query.include_ai_review),
|
|
45
45
|
},
|
|
46
46
|
})
|
|
47
47
|
|
|
@@ -38,25 +38,77 @@ export const usePackageStarMutation = (query: PackageStarQuery) => {
|
|
|
38
38
|
const axios = useAxios()
|
|
39
39
|
const queryClient = useQueryClient()
|
|
40
40
|
|
|
41
|
-
const addStar = useMutation
|
|
41
|
+
const addStar = useMutation<
|
|
42
|
+
any,
|
|
43
|
+
Error,
|
|
44
|
+
void,
|
|
45
|
+
{ previousStars?: PackageStarResponse }
|
|
46
|
+
>(
|
|
42
47
|
async () => {
|
|
43
48
|
const { data } = await axios.post("/packages/add_star", query)
|
|
44
49
|
return data
|
|
45
50
|
},
|
|
46
51
|
{
|
|
47
|
-
|
|
52
|
+
onMutate: async () => {
|
|
53
|
+
await queryClient.cancelQueries(["packageStars", query])
|
|
54
|
+
const previousStars = queryClient.getQueryData<PackageStarResponse>([
|
|
55
|
+
"packageStars",
|
|
56
|
+
query,
|
|
57
|
+
])
|
|
58
|
+
const optimistic: PackageStarResponse = {
|
|
59
|
+
is_starred: true,
|
|
60
|
+
star_count: (previousStars?.star_count ?? 0) + 1,
|
|
61
|
+
}
|
|
62
|
+
queryClient.setQueryData(["packageStars", query], optimistic)
|
|
63
|
+
return { previousStars }
|
|
64
|
+
},
|
|
65
|
+
onError: (_error, _vars, context) => {
|
|
66
|
+
if (context?.previousStars) {
|
|
67
|
+
queryClient.setQueryData(
|
|
68
|
+
["packageStars", query],
|
|
69
|
+
context.previousStars,
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
onSettled: () => {
|
|
48
74
|
queryClient.invalidateQueries(["packageStars", query])
|
|
49
75
|
},
|
|
50
76
|
},
|
|
51
77
|
)
|
|
52
78
|
|
|
53
|
-
const removeStar = useMutation
|
|
79
|
+
const removeStar = useMutation<
|
|
80
|
+
any,
|
|
81
|
+
Error,
|
|
82
|
+
void,
|
|
83
|
+
{ previousStars?: PackageStarResponse }
|
|
84
|
+
>(
|
|
54
85
|
async () => {
|
|
55
86
|
const { data } = await axios.post("/packages/remove_star", query)
|
|
56
87
|
return data
|
|
57
88
|
},
|
|
58
89
|
{
|
|
59
|
-
|
|
90
|
+
onMutate: async () => {
|
|
91
|
+
await queryClient.cancelQueries(["packageStars", query])
|
|
92
|
+
const previousStars = queryClient.getQueryData<PackageStarResponse>([
|
|
93
|
+
"packageStars",
|
|
94
|
+
query,
|
|
95
|
+
])
|
|
96
|
+
const optimistic: PackageStarResponse = {
|
|
97
|
+
is_starred: false,
|
|
98
|
+
star_count: Math.max(0, (previousStars?.star_count ?? 1) - 1),
|
|
99
|
+
}
|
|
100
|
+
queryClient.setQueryData(["packageStars", query], optimistic)
|
|
101
|
+
return { previousStars }
|
|
102
|
+
},
|
|
103
|
+
onError: (_error, _vars, context) => {
|
|
104
|
+
if (context?.previousStars) {
|
|
105
|
+
queryClient.setQueryData(
|
|
106
|
+
["packageStars", query],
|
|
107
|
+
context.previousStars,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
onSettled: () => {
|
|
60
112
|
queryClient.invalidateQueries(["packageStars", query])
|
|
61
113
|
},
|
|
62
114
|
},
|
|
@@ -84,3 +136,27 @@ export const usePackageStarMutationById = (packageId: string) => {
|
|
|
84
136
|
export const usePackageStarMutationByName = (packageName: string) => {
|
|
85
137
|
return usePackageStarMutation({ name: packageName })
|
|
86
138
|
}
|
|
139
|
+
|
|
140
|
+
// High-level hook that exposes current star state and a single toggle action
|
|
141
|
+
export const usePackageStarring = (query: PackageStarQuery | null) => {
|
|
142
|
+
const starsQuery = usePackageStars(query)
|
|
143
|
+
const mutations = usePackageStarMutation(query ?? { name: "" })
|
|
144
|
+
|
|
145
|
+
const toggleStar = async () => {
|
|
146
|
+
if (!query) return
|
|
147
|
+
const currentlyStarred = starsQuery.data?.is_starred ?? false
|
|
148
|
+
if (currentlyStarred) await mutations.removeStar.mutateAsync()
|
|
149
|
+
else await mutations.addStar.mutateAsync()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
isStarred: starsQuery.data?.is_starred ?? false,
|
|
154
|
+
starCount: starsQuery.data?.star_count ?? 0,
|
|
155
|
+
isPending: mutations.addStar.isLoading || mutations.removeStar.isLoading,
|
|
156
|
+
toggleStar,
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const usePackageStarringByName = (packageName: string | null) => {
|
|
161
|
+
return usePackageStarring(packageName ? { name: packageName } : null)
|
|
162
|
+
}
|