@tscircuit/fake-snippets 0.0.77 → 0.0.79
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/formatbot.yml +63 -0
- package/bun-tests/fake-snippets-api/routes/accounts/get.test.ts +11 -0
- package/dist/bundle.js +258 -163
- package/fake-snippets-api/lib/db/autoload-packages.json +0 -28
- package/fake-snippets-api/routes/api/accounts/get.ts +2 -1
- package/fake-snippets-api/routes/api/packages/generate_from_jlcpcb.ts +111 -0
- package/package.json +1 -1
- package/src/App.tsx +0 -3
- package/src/components/CircuitJsonImportDialog.tsx +20 -3
- package/src/components/JLCPCBImportDialog.tsx +31 -27
- package/src/components/PackageBuildsPage/LogContent.tsx +25 -9
- package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +5 -5
- package/src/components/PackageBuildsPage/package-build-details-panel.tsx +2 -2
- package/src/components/PackageBuildsPage/package-build-header.tsx +1 -9
- package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +1 -0
- package/src/components/ViewPackagePage/components/sidebar-about-section.tsx +1 -0
- package/src/components/dialogs/edit-package-details-dialog.tsx +45 -11
- package/src/components/dialogs/rename-package-dialog.tsx +5 -0
- package/src/components/package-port/CodeEditor.tsx +28 -16
- package/src/hooks/use-current-package-release.ts +6 -2
- package/src/hooks/use-package-details-form.ts +9 -1
- package/src/hooks/use-package-release.ts +16 -3
- package/src/pages/latest.tsx +29 -87
- package/src/pages/quickstart.tsx +2 -2
- package/src/pages/user-profile.tsx +50 -15
- package/src/pages/beta.tsx +0 -367
|
@@ -9,18 +9,15 @@ import { Decoration, hoverTooltip, keymap } from "@codemirror/view"
|
|
|
9
9
|
import { getImportsFromCode } from "@tscircuit/prompt-benchmarks/code-runner-utils"
|
|
10
10
|
import type { ATABootstrapConfig } from "@typescript/ata"
|
|
11
11
|
import { setupTypeAcquisition } from "@typescript/ata"
|
|
12
|
+
import { linter } from "@codemirror/lint"
|
|
12
13
|
import { TSCI_PACKAGE_PATTERN } from "@/lib/constants"
|
|
13
14
|
import {
|
|
14
15
|
createDefaultMapFromCDN,
|
|
15
16
|
createSystem,
|
|
16
17
|
createVirtualTypeScriptEnvironment,
|
|
17
18
|
} from "@typescript/vfs"
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
tsFacet,
|
|
21
|
-
tsLinter,
|
|
22
|
-
tsSync,
|
|
23
|
-
} from "@valtown/codemirror-ts"
|
|
19
|
+
import { tsAutocomplete, tsFacet, tsSync } from "@valtown/codemirror-ts"
|
|
20
|
+
import { getLints } from "@valtown/codemirror-ts"
|
|
24
21
|
import { EditorView } from "codemirror"
|
|
25
22
|
import { useEffect, useMemo, useRef, useState } from "react"
|
|
26
23
|
import tsModule from "typescript"
|
|
@@ -75,6 +72,7 @@ export const CodeEditor = ({
|
|
|
75
72
|
const editorRef = useRef<HTMLDivElement>(null)
|
|
76
73
|
const viewRef = useRef<EditorView | null>(null)
|
|
77
74
|
const ataRef = useRef<ReturnType<typeof setupTypeAcquisition> | null>(null)
|
|
75
|
+
const lastReceivedTsFileTimeRef = useRef<number>(0)
|
|
78
76
|
const apiUrl = useSnippetsBaseApiUrl()
|
|
79
77
|
const codeCompletionApi = useCodeCompletionApi()
|
|
80
78
|
const [cursorPosition, setCursorPosition] = useState<number | null>(null)
|
|
@@ -218,16 +216,12 @@ export const CodeEditor = ({
|
|
|
218
216
|
receivedFile: (code: string, path: string) => {
|
|
219
217
|
fsMap.set(path, code)
|
|
220
218
|
env.createFile(path, code)
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
changes: {
|
|
224
|
-
from: 0,
|
|
225
|
-
to: viewRef.current.state.doc.length,
|
|
226
|
-
insert: viewRef.current.state.doc.toString(),
|
|
227
|
-
},
|
|
228
|
-
selection: viewRef.current.state.selection,
|
|
229
|
-
})
|
|
219
|
+
if (/\.tsx?$|\.d\.ts$/.test(path)) {
|
|
220
|
+
lastReceivedTsFileTimeRef.current = Date.now()
|
|
230
221
|
}
|
|
222
|
+
// Avoid dispatching a view update when ATA downloads files. Dispatching
|
|
223
|
+
// here caused the editor to reset the user's selection, which made text
|
|
224
|
+
// selection impossible while dependencies were loading.
|
|
231
225
|
},
|
|
232
226
|
},
|
|
233
227
|
}
|
|
@@ -292,7 +286,18 @@ export const CodeEditor = ({
|
|
|
292
286
|
: currentFile,
|
|
293
287
|
}),
|
|
294
288
|
tsSync(),
|
|
295
|
-
|
|
289
|
+
linter(async (view) => {
|
|
290
|
+
if (Date.now() - lastReceivedTsFileTimeRef.current < 3000) {
|
|
291
|
+
return []
|
|
292
|
+
}
|
|
293
|
+
const config = view.state.facet(tsFacet)
|
|
294
|
+
return config
|
|
295
|
+
? getLints({
|
|
296
|
+
...config,
|
|
297
|
+
diagnosticCodesToIgnore: [],
|
|
298
|
+
})
|
|
299
|
+
: []
|
|
300
|
+
}),
|
|
296
301
|
autocompletion({ override: [tsAutocomplete()] }),
|
|
297
302
|
hoverTooltip((view, pos) => {
|
|
298
303
|
const line = view.state.doc.lineAt(pos)
|
|
@@ -386,6 +391,13 @@ export const CodeEditor = ({
|
|
|
386
391
|
}
|
|
387
392
|
return false
|
|
388
393
|
},
|
|
394
|
+
keydown: (event) => {
|
|
395
|
+
if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
|
|
396
|
+
event.preventDefault()
|
|
397
|
+
return true
|
|
398
|
+
}
|
|
399
|
+
return false
|
|
400
|
+
},
|
|
389
401
|
}),
|
|
390
402
|
EditorView.theme({
|
|
391
403
|
".cm-tooltip-hover": {
|
|
@@ -3,7 +3,9 @@ import { useCurrentPackageId } from "./use-current-package-id"
|
|
|
3
3
|
import { useUrlParams } from "./use-url-params"
|
|
4
4
|
import { usePackageRelease } from "./use-package-release"
|
|
5
5
|
|
|
6
|
-
export const useCurrentPackageRelease = (
|
|
6
|
+
export const useCurrentPackageRelease = (options?: {
|
|
7
|
+
include_logs: boolean
|
|
8
|
+
}) => {
|
|
7
9
|
const { packageId } = useCurrentPackageId()
|
|
8
10
|
const urlParams = useUrlParams()
|
|
9
11
|
const { author, packageName } = useParams()
|
|
@@ -23,6 +25,8 @@ export const useCurrentPackageRelease = () => {
|
|
|
23
25
|
query = { package_id: packageId, is_latest: true }
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const { data: packageRelease, ...rest } = usePackageRelease(query
|
|
28
|
+
const { data: packageRelease, ...rest } = usePackageRelease(query, {
|
|
29
|
+
include_logs: options?.include_logs ?? false,
|
|
30
|
+
})
|
|
27
31
|
return { packageRelease, ...rest }
|
|
28
32
|
}
|
|
@@ -16,6 +16,7 @@ interface PackageDetailsForm {
|
|
|
16
16
|
license: string | null
|
|
17
17
|
visibility: string
|
|
18
18
|
defaultView: string
|
|
19
|
+
unscopedPackageName: string
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
interface UsePackageDetailsFormProps {
|
|
@@ -24,6 +25,7 @@ interface UsePackageDetailsFormProps {
|
|
|
24
25
|
initialLicense: string | null
|
|
25
26
|
initialVisibility: string
|
|
26
27
|
initialDefaultView: string
|
|
28
|
+
initialUnscopedPackageName: string
|
|
27
29
|
isDialogOpen: boolean
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -33,6 +35,7 @@ export const usePackageDetailsForm = ({
|
|
|
33
35
|
initialLicense,
|
|
34
36
|
initialVisibility,
|
|
35
37
|
initialDefaultView,
|
|
38
|
+
initialUnscopedPackageName,
|
|
36
39
|
isDialogOpen,
|
|
37
40
|
}: UsePackageDetailsFormProps) => {
|
|
38
41
|
const [formData, setFormData] = useState<PackageDetailsForm>({
|
|
@@ -41,6 +44,7 @@ export const usePackageDetailsForm = ({
|
|
|
41
44
|
license: initialLicense || null,
|
|
42
45
|
visibility: initialVisibility,
|
|
43
46
|
defaultView: initialDefaultView,
|
|
47
|
+
unscopedPackageName: initialUnscopedPackageName,
|
|
44
48
|
})
|
|
45
49
|
const [websiteError, setWebsiteError] = useState<string | null>(null)
|
|
46
50
|
|
|
@@ -52,6 +56,7 @@ export const usePackageDetailsForm = ({
|
|
|
52
56
|
license: initialLicense || null,
|
|
53
57
|
visibility: initialVisibility,
|
|
54
58
|
defaultView: initialDefaultView,
|
|
59
|
+
unscopedPackageName: initialUnscopedPackageName,
|
|
55
60
|
})
|
|
56
61
|
setWebsiteError(null)
|
|
57
62
|
}
|
|
@@ -62,6 +67,7 @@ export const usePackageDetailsForm = ({
|
|
|
62
67
|
initialLicense,
|
|
63
68
|
initialVisibility,
|
|
64
69
|
initialDefaultView,
|
|
70
|
+
initialUnscopedPackageName,
|
|
65
71
|
])
|
|
66
72
|
|
|
67
73
|
useEffect(() => {
|
|
@@ -93,7 +99,8 @@ export const usePackageDetailsForm = ({
|
|
|
93
99
|
formData.website !== initialWebsite ||
|
|
94
100
|
formData.license !== initialLicense ||
|
|
95
101
|
formData.visibility !== initialVisibility ||
|
|
96
|
-
formData.defaultView !== initialDefaultView
|
|
102
|
+
formData.defaultView !== initialDefaultView ||
|
|
103
|
+
formData.unscopedPackageName !== initialUnscopedPackageName,
|
|
97
104
|
[
|
|
98
105
|
formData,
|
|
99
106
|
initialDescription,
|
|
@@ -101,6 +108,7 @@ export const usePackageDetailsForm = ({
|
|
|
101
108
|
initialLicense,
|
|
102
109
|
initialVisibility,
|
|
103
110
|
initialDefaultView,
|
|
111
|
+
initialUnscopedPackageName,
|
|
104
112
|
],
|
|
105
113
|
)
|
|
106
114
|
|
|
@@ -18,15 +18,28 @@ type PackageReleaseQuery =
|
|
|
18
18
|
is_latest: boolean
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export const usePackageRelease = (
|
|
21
|
+
export const usePackageRelease = (
|
|
22
|
+
query: PackageReleaseQuery | null,
|
|
23
|
+
options?: { include_logs: boolean },
|
|
24
|
+
) => {
|
|
22
25
|
const axios = useAxios()
|
|
23
26
|
|
|
24
27
|
return useQuery<PackageRelease, Error & { status: number }>(
|
|
25
|
-
["packageRelease", query],
|
|
28
|
+
["packageRelease", query, options?.include_logs],
|
|
26
29
|
async () => {
|
|
27
30
|
if (!query) return
|
|
28
31
|
|
|
29
|
-
const { data } = await axios.post(
|
|
32
|
+
const { data } = await axios.post(
|
|
33
|
+
"/package_releases/get",
|
|
34
|
+
query,
|
|
35
|
+
options?.include_logs
|
|
36
|
+
? {
|
|
37
|
+
params: {
|
|
38
|
+
include_logs: true,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
: undefined,
|
|
42
|
+
)
|
|
30
43
|
|
|
31
44
|
if (!data.package_release) {
|
|
32
45
|
throw new Error("Package release not found")
|
package/src/pages/latest.tsx
CHANGED
|
@@ -4,17 +4,8 @@ import { useAxios } from "@/hooks/use-axios"
|
|
|
4
4
|
import { Package } from "fake-snippets-api/lib/db/schema"
|
|
5
5
|
import Header from "@/components/Header"
|
|
6
6
|
import Footer from "@/components/Footer"
|
|
7
|
-
import {
|
|
8
|
-
Search,
|
|
9
|
-
Tag,
|
|
10
|
-
Calendar,
|
|
11
|
-
Keyboard,
|
|
12
|
-
Cpu,
|
|
13
|
-
Layers,
|
|
14
|
-
LucideBellElectric,
|
|
15
|
-
} from "lucide-react"
|
|
7
|
+
import { Search, Keyboard, Cpu, Layers, LucideBellElectric } from "lucide-react"
|
|
16
8
|
import { Input } from "@/components/ui/input"
|
|
17
|
-
import { Badge } from "@/components/ui/badge"
|
|
18
9
|
import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
|
|
19
10
|
import {
|
|
20
11
|
Select,
|
|
@@ -23,8 +14,7 @@ import {
|
|
|
23
14
|
SelectTrigger,
|
|
24
15
|
SelectValue,
|
|
25
16
|
} from "@/components/ui/select"
|
|
26
|
-
import
|
|
27
|
-
import { PackageCard } from "@/components/PackageCard"
|
|
17
|
+
import PackageSearchResults from "@/components/PackageSearchResults"
|
|
28
18
|
|
|
29
19
|
const LatestPage: React.FC = () => {
|
|
30
20
|
const axios = useAxios()
|
|
@@ -48,22 +38,24 @@ const LatestPage: React.FC = () => {
|
|
|
48
38
|
},
|
|
49
39
|
)
|
|
50
40
|
|
|
51
|
-
const filteredPackages = packages
|
|
52
|
-
|
|
41
|
+
const filteredPackages = packages
|
|
42
|
+
?.filter((pkg) => {
|
|
43
|
+
if (!searchQuery) return true
|
|
53
44
|
|
|
54
|
-
|
|
45
|
+
const query = searchQuery.toLowerCase().trim()
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
const searchableFields = [
|
|
48
|
+
pkg.unscoped_name.toLowerCase(),
|
|
49
|
+
pkg.owner_github_username?.toLowerCase() ?? "",
|
|
50
|
+
(pkg.description || "").toLowerCase(),
|
|
51
|
+
]
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
return searchableFields.some((field) => {
|
|
54
|
+
const queryWords = query.split(/\s+/).filter((word) => word.length > 0)
|
|
55
|
+
return queryWords.every((word) => field.includes(word))
|
|
56
|
+
})
|
|
65
57
|
})
|
|
66
|
-
|
|
58
|
+
?.sort((a, b) => b.created_at.localeCompare(a.created_at))
|
|
67
59
|
|
|
68
60
|
return (
|
|
69
61
|
<div className="min-h-screen flex flex-col bg-gray-50">
|
|
@@ -71,7 +63,6 @@ const LatestPage: React.FC = () => {
|
|
|
71
63
|
<main className="flex-grow container mx-auto px-4 py-8">
|
|
72
64
|
<div className="mb-8 max-w-3xl">
|
|
73
65
|
<div className="flex items-center gap-2 mb-3">
|
|
74
|
-
<Calendar className="w-6 h-6 text-blue-500" />
|
|
75
66
|
<h1 className="text-4xl font-bold text-gray-900">
|
|
76
67
|
Latest Packages
|
|
77
68
|
</h1>
|
|
@@ -81,16 +72,6 @@ const LatestPage: React.FC = () => {
|
|
|
81
72
|
additions showcase new ideas and innovative approaches to circuit
|
|
82
73
|
design.
|
|
83
74
|
</p>
|
|
84
|
-
<div className="flex flex-wrap gap-3">
|
|
85
|
-
<Badge variant="secondary" className="px-3 py-1">
|
|
86
|
-
<Tag className="w-3.5 h-3.5 mr-1" />
|
|
87
|
-
<span>Latest Uploads</span>
|
|
88
|
-
</Badge>
|
|
89
|
-
<Badge variant="secondary" className="px-3 py-1">
|
|
90
|
-
<Calendar className="w-3.5 h-3.5 mr-1" />
|
|
91
|
-
<span>Most Recent First</span>
|
|
92
|
-
</Badge>
|
|
93
|
-
</div>
|
|
94
75
|
</div>
|
|
95
76
|
|
|
96
77
|
<div className="mb-6">
|
|
@@ -139,58 +120,19 @@ const LatestPage: React.FC = () => {
|
|
|
139
120
|
</Select>
|
|
140
121
|
</div>
|
|
141
122
|
</div>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<div>
|
|
156
|
-
<h3 className="text-lg font-semibold mb-2">
|
|
157
|
-
Error Loading Packages
|
|
158
|
-
</h3>
|
|
159
|
-
<p className="text-red-600">
|
|
160
|
-
We couldn't load the latest packages. Please try again later.
|
|
161
|
-
</p>
|
|
162
|
-
</div>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
) : filteredPackages?.length === 0 ? (
|
|
166
|
-
<div className="text-center py-12 px-4">
|
|
167
|
-
<div className="bg-slate-50 inline-flex rounded-full p-4 mb-4">
|
|
168
|
-
<Search className="w-8 h-8 text-slate-400" />
|
|
169
|
-
</div>
|
|
170
|
-
<h3 className="text-xl font-medium text-slate-900 mb-2">
|
|
171
|
-
No Matching Packages
|
|
172
|
-
</h3>
|
|
173
|
-
<p className="text-slate-500 max-w-md mx-auto mb-6">
|
|
174
|
-
{searchQuery
|
|
175
|
-
? `No packages match your search for "${searchQuery}".`
|
|
176
|
-
: category !== "all"
|
|
177
|
-
? `No ${category} packages found in the latest list.`
|
|
178
|
-
: "There are no new packages at the moment."}
|
|
179
|
-
</p>
|
|
180
|
-
</div>
|
|
181
|
-
) : (
|
|
182
|
-
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
183
|
-
{filteredPackages
|
|
184
|
-
?.sort((a, b) => b.created_at.localeCompare(a.created_at))
|
|
185
|
-
?.map((pkg) => (
|
|
186
|
-
<PackageCard
|
|
187
|
-
key={pkg.package_id}
|
|
188
|
-
pkg={pkg}
|
|
189
|
-
baseUrl={apiBaseUrl}
|
|
190
|
-
/>
|
|
191
|
-
))}
|
|
192
|
-
</div>
|
|
193
|
-
)}
|
|
123
|
+
<PackageSearchResults
|
|
124
|
+
isLoading={isLoading}
|
|
125
|
+
error={error}
|
|
126
|
+
filteredPackages={filteredPackages}
|
|
127
|
+
apiBaseUrl={apiBaseUrl}
|
|
128
|
+
emptyStateMessage={
|
|
129
|
+
searchQuery
|
|
130
|
+
? `No packages match your search for "${searchQuery}".`
|
|
131
|
+
: category !== "all"
|
|
132
|
+
? `No ${category} packages found in the latest list.`
|
|
133
|
+
: "There are no new packages at the moment."
|
|
134
|
+
}
|
|
135
|
+
/>
|
|
194
136
|
</main>
|
|
195
137
|
<Footer />
|
|
196
138
|
</div>
|
package/src/pages/quickstart.tsx
CHANGED
|
@@ -119,7 +119,7 @@ export const QuickstartPage = () => {
|
|
|
119
119
|
<div className="mt-12">
|
|
120
120
|
<h2 className="text-xl font-semibold mb-4">Import as Package</h2>
|
|
121
121
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
122
|
-
{[
|
|
122
|
+
{/* {[
|
|
123
123
|
{ name: "KiCad Footprint", type: "footprint" },
|
|
124
124
|
{ name: "KiCad Project", type: "board" },
|
|
125
125
|
{ name: "KiCad Module", type: "package" },
|
|
@@ -145,7 +145,7 @@ export const QuickstartPage = () => {
|
|
|
145
145
|
</Button>
|
|
146
146
|
</CardContent>
|
|
147
147
|
</Card>
|
|
148
|
-
))}
|
|
148
|
+
))} */}
|
|
149
149
|
<Card className="hover:shadow-md transition-shadow rounded-md flex flex-col">
|
|
150
150
|
<CardHeader className="p-4 pb-0">
|
|
151
151
|
<CardTitle className="text-lg flex items-center justify-between">
|
|
@@ -14,6 +14,7 @@ import type { Package } from "fake-snippets-api/lib/db/schema"
|
|
|
14
14
|
import type React from "react"
|
|
15
15
|
import { useState } from "react"
|
|
16
16
|
import { useQuery } from "react-query"
|
|
17
|
+
import NotFoundPage from "./404"
|
|
17
18
|
import { useParams } from "wouter"
|
|
18
19
|
import {
|
|
19
20
|
Select,
|
|
@@ -32,7 +33,28 @@ export const UserProfilePage = () => {
|
|
|
32
33
|
const [activeTab, setActiveTab] = useState("all")
|
|
33
34
|
const [filter, setFilter] = useState("most-recent") // Changed default from "newest" to "most-recent"
|
|
34
35
|
const session = useGlobalStore((s) => s.session)
|
|
35
|
-
const
|
|
36
|
+
const {
|
|
37
|
+
data: account,
|
|
38
|
+
error: accountError,
|
|
39
|
+
isLoading: isLoadingAccount,
|
|
40
|
+
} = useQuery<
|
|
41
|
+
{ account: { github_username: string } },
|
|
42
|
+
Error & { status: number }
|
|
43
|
+
>(
|
|
44
|
+
["account", username],
|
|
45
|
+
async () => {
|
|
46
|
+
const response = await axios.post("/accounts/get", {
|
|
47
|
+
github_username: username,
|
|
48
|
+
})
|
|
49
|
+
return response.data
|
|
50
|
+
},
|
|
51
|
+
{ retry: false },
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// use the username stored in the database so the correct case is displayed
|
|
55
|
+
const githubUsername = account?.account.github_username || username
|
|
56
|
+
const isCurrentUserProfile = githubUsername === session?.github_username
|
|
57
|
+
|
|
36
58
|
const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
|
|
37
59
|
useConfirmDeletePackageDialog()
|
|
38
60
|
const [packageToDelete, setPackageToDelete] = useState<Package | null>(null)
|
|
@@ -41,33 +63,42 @@ export const UserProfilePage = () => {
|
|
|
41
63
|
data: userPackages,
|
|
42
64
|
isLoading: isLoadingUserPackages,
|
|
43
65
|
refetch: refetchUserPackages,
|
|
44
|
-
} = useQuery<Package[]>(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
} = useQuery<Package[]>(
|
|
67
|
+
["userPackages", githubUsername],
|
|
68
|
+
async () => {
|
|
69
|
+
const response = await axios.post(`/packages/list`, {
|
|
70
|
+
owner_github_username: githubUsername,
|
|
71
|
+
})
|
|
72
|
+
return response.data.packages
|
|
73
|
+
},
|
|
74
|
+
{ enabled: Boolean(githubUsername) },
|
|
75
|
+
)
|
|
50
76
|
|
|
51
77
|
const { data: starredPackages, isLoading: isLoadingStarredPackages } =
|
|
52
78
|
useQuery<Package[]>(
|
|
53
|
-
["starredPackages",
|
|
79
|
+
["starredPackages", githubUsername],
|
|
54
80
|
async () => {
|
|
55
81
|
const response = await axios.post(`/packages/list`, {
|
|
56
|
-
starred_by:
|
|
82
|
+
starred_by: githubUsername,
|
|
57
83
|
})
|
|
58
84
|
return response.data.packages
|
|
59
85
|
},
|
|
60
86
|
{
|
|
61
|
-
enabled: activeTab === "starred",
|
|
87
|
+
enabled: activeTab === "starred" && Boolean(githubUsername),
|
|
62
88
|
},
|
|
63
89
|
)
|
|
64
90
|
|
|
65
91
|
const baseUrl = useSnippetsBaseApiUrl()
|
|
66
92
|
|
|
93
|
+
if (accountError) {
|
|
94
|
+
return <NotFoundPage heading="User Not Found" />
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
const packagesToShow =
|
|
68
98
|
activeTab === "starred" ? starredPackages : userPackages
|
|
69
99
|
const isLoading =
|
|
70
|
-
|
|
100
|
+
isLoadingAccount ||
|
|
101
|
+
(activeTab === "starred" ? isLoadingStarredPackages : isLoadingUserPackages)
|
|
71
102
|
|
|
72
103
|
const filteredPackages = packagesToShow
|
|
73
104
|
?.filter((pkg) => {
|
|
@@ -107,12 +138,16 @@ export const UserProfilePage = () => {
|
|
|
107
138
|
<div className="container mx-auto px-4 py-8">
|
|
108
139
|
<div className="flex items-center gap-4 mb-6">
|
|
109
140
|
<Avatar className="h-16 w-16">
|
|
110
|
-
<AvatarImage src={`https://github.com/${
|
|
111
|
-
<AvatarFallback>
|
|
141
|
+
<AvatarImage src={`https://github.com/${githubUsername}.png`} />
|
|
142
|
+
<AvatarFallback>
|
|
143
|
+
{githubUsername?.[0]?.toUpperCase()}
|
|
144
|
+
</AvatarFallback>
|
|
112
145
|
</Avatar>
|
|
113
146
|
<div>
|
|
114
147
|
<h1 className="text-3xl font-bold">
|
|
115
|
-
{isCurrentUserProfile
|
|
148
|
+
{isCurrentUserProfile
|
|
149
|
+
? "My Profile"
|
|
150
|
+
: `${githubUsername}'s Profile`}
|
|
116
151
|
</h1>
|
|
117
152
|
<div className="text-gray-600 mt-1">
|
|
118
153
|
{userPackages?.length || 0} packages
|
|
@@ -121,7 +156,7 @@ export const UserProfilePage = () => {
|
|
|
121
156
|
</div>
|
|
122
157
|
<div className="mb-6">
|
|
123
158
|
<a
|
|
124
|
-
href={`https://github.com/${
|
|
159
|
+
href={`https://github.com/${githubUsername}`}
|
|
125
160
|
target="_blank"
|
|
126
161
|
rel="noopener noreferrer"
|
|
127
162
|
className="inline-flex items-center"
|