@tscircuit/fake-snippets 0.0.108 → 0.0.109

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.
Files changed (57) hide show
  1. package/bun.lock +62 -19
  2. package/dist/bundle.js +3 -2
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.js +2 -1
  5. package/dist/schema.d.ts +8 -0
  6. package/dist/schema.js +2 -1
  7. package/fake-snippets-api/lib/db/schema.ts +1 -0
  8. package/package.json +7 -8
  9. package/src/App.tsx +0 -2
  10. package/src/components/DownloadButtonAndMenu.tsx +133 -35
  11. package/src/components/FileSidebar.tsx +31 -34
  12. package/src/components/Footer.tsx +0 -1
  13. package/src/components/HeaderLogin.tsx +1 -1
  14. package/src/components/HiddenFilesDropdown.tsx +0 -2
  15. package/src/components/PackageBuildsPage/PackageBuildDetailsPage.tsx +0 -2
  16. package/src/components/PackageBuildsPage/build-preview-content.tsx +34 -5
  17. package/src/components/PackageCard.tsx +0 -1
  18. package/src/components/ViewPackagePage/components/ShikiCodeViewer.tsx +20 -11
  19. package/src/components/ViewPackagePage/components/important-files-view.tsx +75 -59
  20. package/src/components/ViewPackagePage/components/main-content-header.tsx +4 -4
  21. package/src/components/ViewPackagePage/components/main-content-view-selector.tsx +0 -1
  22. package/src/components/ViewPackagePage/components/mobile-sidebar.tsx +0 -1
  23. package/src/components/ViewPackagePage/components/package-header.tsx +14 -17
  24. package/src/components/ViewPackagePage/components/preview-image-squares.tsx +0 -1
  25. package/src/components/ViewPackagePage/components/repo-page-content.tsx +21 -20
  26. package/src/components/ViewPackagePage/components/sidebar.tsx +0 -2
  27. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +18 -17
  28. package/src/components/ViewPackagePage/components/theme-toggle.tsx +0 -2
  29. package/src/components/ViewPackagePage/hooks/use-toast.tsx +0 -1
  30. package/src/components/package-port/CodeAndPreview.tsx +23 -40
  31. package/src/components/package-port/CodeEditor.tsx +24 -1
  32. package/src/components/package-port/CodeEditorHeader.tsx +5 -2
  33. package/src/components/preview/PackageReleasesDashboard.tsx +30 -25
  34. package/src/hooks/use-current-package-id.ts +5 -30
  35. package/src/hooks/use-current-package-info.ts +29 -5
  36. package/src/hooks/use-global-store.ts +1 -1
  37. package/src/hooks/useFileManagement.ts +153 -34
  38. package/src/hooks/useOptimizedPackageFilesLoader.ts +149 -0
  39. package/src/hooks/useUpdatePackageFilesMutation.ts +2 -0
  40. package/src/lib/download-fns/download-circuit-png.ts +11 -3
  41. package/src/lib/download-fns/download-gltf-from-circuit-json.ts +44 -0
  42. package/src/lib/utils/isComponentExported.ts +9 -0
  43. package/src/pages/authorize.tsx +0 -2
  44. package/src/pages/landing.tsx +0 -1
  45. package/src/pages/preview-release.tsx +14 -4
  46. package/src/pages/view-package.tsx +14 -13
  47. package/src/components/Footer2.tsx +0 -100
  48. package/src/components/ShippingInformationForm.tsx +0 -423
  49. package/src/components/StaticViewSnippetHeader.tsx +0 -70
  50. package/src/components/ViewPackagePage/components/file-explorer.tsx +0 -67
  51. package/src/components/ViewPackagePage/components/readme-view.tsx +0 -58
  52. package/src/components/ViewPackagePage/components/repo-header-button.tsx +0 -36
  53. package/src/components/ViewPackagePage/components/repo-header.tsx +0 -4
  54. package/src/components/ViewPackagePage/components/sidebar-contributors-section.tsx +0 -31
  55. package/src/components/ViewSnippetHeader.tsx +0 -181
  56. package/src/components/ui/input-otp.tsx +0 -69
  57. package/src/pages/settings.tsx +0 -25
@@ -1,11 +1,10 @@
1
- "use client"
2
-
3
1
  import { Skeleton } from "@/components/ui/skeleton"
4
2
  import { FileText, Folder } from "lucide-react"
5
3
  import { useMemo, useState } from "react"
6
4
  import { isHiddenFile } from "../../utils/is-hidden-file"
7
5
  import { isWithinDirectory } from "../../utils/is-within-directory"
8
6
  import HiddenFilesDropdown from "@/components/HiddenFilesDropdown"
7
+ import type { PackageFile as ApiPackageFile } from "fake-snippets-api/lib/db/schema"
9
8
 
10
9
  interface Directory {
11
10
  type: "directory"
@@ -21,24 +20,20 @@ interface File {
21
20
  created_at: string
22
21
  }
23
22
 
24
- interface PackageFile {
25
- package_file_id: string
26
- package_release_id: string
27
- file_path: string
28
- file_content: string
29
- content_text?: string // Keep for backward compatibility
30
- created_at: string // iso-8601
23
+ interface PackageFile extends ApiPackageFile {
24
+ file_content?: string
25
+ content_text?: string | null // Keep for backward compatibility
31
26
  }
32
27
 
33
28
  interface FilesViewProps {
34
29
  packageFiles?: PackageFile[]
35
- isLoading?: boolean
36
30
  onFileClicked?: (file: PackageFile) => void
31
+ arePackageFilesFetched?: boolean
37
32
  }
38
33
 
39
34
  export default function FilesView({
40
35
  packageFiles = [],
41
- isLoading = false,
36
+ arePackageFilesFetched = false,
42
37
  onFileClicked,
43
38
  }: FilesViewProps) {
44
39
  const [activeDir, setActiveDir] = useState("")
@@ -106,12 +101,19 @@ export default function FilesView({
106
101
  }, [packageFiles, showHiddenFiles])
107
102
  // Format date for display
108
103
  const formatDate = (dateString: string) => {
109
- const date = new Date(dateString)
104
+ const parsedDate = new Date(dateString)
105
+ if (Number.isNaN(parsedDate.getTime())) return ""
106
+
110
107
  const now = new Date()
111
- const diffTime = Math.abs(now.getTime() - date.getTime())
112
- const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
108
+ const diffMs = now.getTime() - parsedDate.getTime()
109
+ const oneDayMs = 1000 * 60 * 60 * 24
110
+
111
+ // Treat future dates as today
112
+ if (diffMs <= 0) return "today"
113
113
 
114
- if (diffDays < 1) return "today"
114
+ if (diffMs < oneDayMs) return "today"
115
+
116
+ const diffDays = Math.floor(diffMs / oneDayMs)
115
117
  if (diffDays === 1) return "yesterday"
116
118
  if (diffDays < 7) return `${diffDays} days ago`
117
119
  if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`
@@ -160,7 +162,7 @@ export default function FilesView({
160
162
 
161
163
  const toggleHiddenFiles = () => setShowHiddenFiles((prev) => !prev)
162
164
 
163
- if (isLoading) {
165
+ if (!arePackageFilesFetched) {
164
166
  return (
165
167
  <div className="mb-4 border border-gray-200 dark:border-[#30363d] rounded-md overflow-hidden">
166
168
  <div className="flex items-center px-4 py-2 md:py-3 bg-gray-100 dark:bg-[#161b22] border-b border-gray-200 dark:border-[#30363d]">
@@ -225,7 +227,6 @@ export default function FilesView({
225
227
  </div>
226
228
  </div>
227
229
  </div>
228
-
229
230
  {/* Files and Directories */}
230
231
  <div className="bg-white dark:bg-[#0d1117]">
231
232
  {items.length === 0 && !activeDir ? (
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import { useTheme } from "next-themes"
4
2
  import { Moon, Sun } from "lucide-react"
5
3
  import { Button } from "@/components/ui/button"
@@ -1,4 +1,3 @@
1
- "use client"
2
1
  import toastLibrary, { Toaster, type Toast } from "react-hot-toast"
3
2
  import React from "react"
4
3
 
@@ -7,7 +7,6 @@ import useWarnUserOnPageChange from "@/hooks/use-warn-user-on-page-change"
7
7
  import { getSnippetTemplate } from "@/lib/get-snippet-template"
8
8
  import { cn } from "@/lib/utils"
9
9
  import type { Package } from "fake-snippets-api/lib/db/schema"
10
- import { Loader2 } from "lucide-react"
11
10
  import { useMemo, useState } from "react"
12
11
  import EditorNav from "@/components/package-port/EditorNav"
13
12
  import { SuspenseRunFrame } from "../SuspenseRunFrame"
@@ -15,8 +14,7 @@ import { applyEditEventsToManualEditsFile } from "@tscircuit/core"
15
14
  import { toastManualEditConflicts } from "@/lib/utils/toastManualEditConflicts"
16
15
  import { ManualEditEvent } from "@tscircuit/props"
17
16
  import { useFileManagement } from "@/hooks/useFileManagement"
18
- import { DEFAULT_CODE } from "@/lib/utils/package-utils"
19
- import { useGlobalStore } from "@/hooks/use-global-store"
17
+ import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
20
18
 
21
19
  interface Props {
22
20
  pkg?: Package
@@ -39,13 +37,13 @@ export interface CodeAndPreviewState {
39
37
 
40
38
  export function CodeAndPreview({ pkg, projectUrl }: Props) {
41
39
  const { toast } = useToast()
42
- const session = useGlobalStore((s) => s.session)
43
40
  const urlParams = useUrlParams()
44
41
 
45
42
  const templateFromUrl = useMemo(
46
43
  () => (urlParams.template ? getSnippetTemplate(urlParams.template) : null),
47
44
  [urlParams.template],
48
45
  )
46
+
49
47
  const [state, setState] = useState<CodeAndPreviewState>({
50
48
  showPreview: true,
51
49
  fullScreen: false,
@@ -69,20 +67,26 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
69
67
  isSaving,
70
68
  currentFile,
71
69
  fsMap,
70
+ priorityFileFetched,
72
71
  isLoading,
73
72
  createFile,
73
+ mainComponentPath,
74
74
  deleteFile,
75
+ isFullyLoaded,
75
76
  onFileSelect,
77
+ totalFilesCount,
76
78
  saveFiles,
77
79
  setLocalFiles,
80
+ loadedFilesCount,
78
81
  localFiles,
82
+ currentFileCode,
79
83
  initialFiles,
80
84
  renameFile,
81
85
  packageFilesMeta,
82
86
  } = useFileManagement({
83
87
  templateCode: templateFromUrl?.code,
84
88
  currentPackage: pkg,
85
- fileChoosen: urlParams.file_path ?? null,
89
+ urlParams,
86
90
  openNewPackageSaveDialog,
87
91
  updateLastUpdated: () => {
88
92
  setState((prev) => ({ ...prev, lastSavedAt: Date.now() }))
@@ -94,6 +98,7 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
94
98
  (!isSaving &&
95
99
  Date.now() - state.lastSavedAt > 1000 &&
96
100
  localFiles.some((file) => {
101
+ if (isHiddenFile(file.path)) return false
97
102
  const initialFile = initialFiles.find((x) => x.path === file.path)
98
103
  return initialFile?.content !== file.content
99
104
  })) ||
@@ -103,29 +108,6 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
103
108
 
104
109
  useWarnUserOnPageChange({ hasUnsavedChanges })
105
110
 
106
- const currentFileCode = useMemo(
107
- () =>
108
- localFiles.find((x) => x.path === currentFile)?.content ??
109
- state.defaultComponentFile ??
110
- DEFAULT_CODE,
111
- [localFiles, currentFile],
112
- )
113
-
114
- const mainComponentPath = useMemo(() => {
115
- const isReactComponentExported =
116
- /export function\s+\w+/.test(currentFileCode) ||
117
- /export const\s+\w+\s*=/.test(currentFileCode) ||
118
- /export default\s+\w+/.test(currentFileCode) ||
119
- /export default\s+function\s*(\w*)\s*\(/.test(currentFileCode) ||
120
- /export default\s*\(\s*\)\s*=>/.test(currentFileCode)
121
-
122
- return (currentFile?.endsWith(".tsx") || currentFile?.endsWith(".ts")) &&
123
- !!localFiles.some((x) => x.path == currentFile) &&
124
- isReactComponentExported
125
- ? currentFile
126
- : state.defaultComponentFile
127
- }, [currentFile, localFiles, currentFileCode])
128
-
129
111
  const handleEditEvent = (event: ManualEditEvent) => {
130
112
  const parsedManualEdits = JSON.parse(
131
113
  localFiles.find((x) => x.path === "manual-edits.json")?.content || "{}",
@@ -171,17 +153,10 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
171
153
  })
172
154
  }
173
155
 
174
- if (urlParams.package_id && (!pkg || isLoading)) {
175
- return (
176
- <div className="flex items-center justify-center h-[80vh]">
177
- <div className="flex flex-col items-center justify-center">
178
- <div className="text-lg text-gray-500 mb-4">Loading</div>
179
- <Loader2 className="w-16 h-16 animate-spin text-gray-400" />
180
- </div>
181
- </div>
182
- )
183
- }
184
-
156
+ const finalfsMap = useMemo(
157
+ () => (Object.keys(fsMap).length > 0 ? fsMap : {}),
158
+ [fsMap],
159
+ )
185
160
  return (
186
161
  <div className="flex flex-col min-h-[50vh]">
187
162
  <EditorNav
@@ -213,8 +188,14 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
213
188
  <CodeEditor
214
189
  isSaving={isSaving}
215
190
  handleCreateFile={createFile}
191
+ totalFilesCount={totalFilesCount}
192
+ loadedFilesCount={loadedFilesCount}
193
+ isFullyLoaded={isFullyLoaded}
216
194
  handleDeleteFile={deleteFile}
217
195
  handleRenameFile={renameFile}
196
+ isPriorityFileFetched={
197
+ !priorityFileFetched && Boolean(urlParams.package_id)
198
+ }
218
199
  pkg={pkg}
219
200
  currentFile={currentFile}
220
201
  onFileSelect={onFileSelect}
@@ -255,7 +236,7 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
255
236
  onEditEvent={(event) => {
256
237
  handleEditEvent(event)
257
238
  }}
258
- fsMap={fsMap ?? {}}
239
+ fsMap={finalfsMap}
259
240
  projectUrl={projectUrl}
260
241
  />
261
242
  </div>
@@ -265,3 +246,5 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
265
246
  </div>
266
247
  )
267
248
  }
249
+
250
+ export default CodeAndPreview
@@ -50,6 +50,7 @@ import {
50
50
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
51
51
  import { inlineCopilot } from "codemirror-copilot"
52
52
  import { useViewTsFilesDialog } from "@/components/dialogs/view-ts-files-dialog"
53
+ import { Loader2 } from "lucide-react"
53
54
 
54
55
  const defaultImports = `
55
56
  import React from "@types/react/jsx-runtime"
@@ -59,6 +60,7 @@ import type { CommonLayoutProps } from "@tscircuit/props"
59
60
 
60
61
  export const CodeEditor = ({
61
62
  onCodeChange,
63
+ isPriorityFileFetched,
62
64
  readOnly = false,
63
65
  files = [],
64
66
  isSaving = false,
@@ -72,9 +74,13 @@ export const CodeEditor = ({
72
74
  handleCreateFile,
73
75
  handleDeleteFile,
74
76
  pkg,
77
+ isFullyLoaded = false,
78
+ totalFilesCount = 0,
79
+ loadedFilesCount = 0,
75
80
  }: {
76
81
  onCodeChange: (code: string, filename?: string) => void
77
82
  files: PackageFile[]
83
+ isPriorityFileFetched: boolean
78
84
  isSaving?: boolean
79
85
  handleCreateFile: (props: ICreateFileProps) => ICreateFileResult
80
86
  handleDeleteFile: (props: IDeleteFileProps) => IDeleteFileResult
@@ -87,6 +93,9 @@ export const CodeEditor = ({
87
93
  onFileContentChanged?: (path: string, content: string) => void
88
94
  currentFile: string | null
89
95
  onFileSelect: (path: string, lineNumber?: number) => void
96
+ isFullyLoaded?: boolean
97
+ totalFilesCount?: number
98
+ loadedFilesCount?: number
90
99
  }) => {
91
100
  const editorRef = useRef<HTMLDivElement>(null)
92
101
  const viewRef = useRef<EditorView | null>(null)
@@ -120,10 +129,13 @@ export const CodeEditor = ({
120
129
  const [sidebarOpen, setSidebarOpen] = useState(false)
121
130
  const [isCreatingFile, setIsCreatingFile] = useState(false)
122
131
 
123
- // Set current file on component mount
132
+ // Set current file on component mount - only when explicitly requested via URL
124
133
  useEffect(() => {
125
134
  if (files.length === 0 || !pkgFilesLoaded || currentFile) return
126
135
 
136
+ // Only run this if there's an explicit file_path in URL - don't auto-select files
137
+ if (!filePathFromUrl) return
138
+
127
139
  const targetFile = findTargetFile(files, filePathFromUrl)
128
140
  if (targetFile) {
129
141
  const lineNumber = lineNumberFromUrl
@@ -825,10 +837,15 @@ export const CodeEditor = ({
825
837
  isCreatingFile={isCreatingFile}
826
838
  setIsCreatingFile={setIsCreatingFile}
827
839
  pkg={pkg}
840
+ isLoadingFiles={!isFullyLoaded}
841
+ loadingProgress={
842
+ totalFilesCount > 0 ? `${loadedFilesCount}/${totalFilesCount}` : null
843
+ }
828
844
  />
829
845
  <div className="flex flex-col flex-1 w-full min-w-0 h-full">
830
846
  {showImportAndFormatButtons && (
831
847
  <CodeEditorHeader
848
+ isLoadingFiles={!isFullyLoaded}
832
849
  entrypointFileName={entryPointFileName}
833
850
  appendNewFile={(path: string, content: string) => {
834
851
  onFileContentChanged?.(path, content)
@@ -854,7 +871,13 @@ export const CodeEditor = ({
854
871
  className={
855
872
  "flex-1 overflow-auto [&_.cm-editor]:h-full [&_.cm-scroller]:!h-full"
856
873
  }
874
+ style={{ display: isPriorityFileFetched ? "none" : "block" }}
857
875
  />
876
+ {isPriorityFileFetched && (
877
+ <div className="grid place-items-center h-full">
878
+ <Loader2 className="w-16 h-16 animate-spin text-gray-400" />
879
+ </div>
880
+ )}
858
881
  </div>
859
882
  {showQuickOpen && (
860
883
  <QuickOpen
@@ -41,6 +41,7 @@ interface CodeEditorHeaderProps {
41
41
  handleFileChange: (filename: FileName) => void
42
42
  entrypointFileName?: string
43
43
  appendNewFile: (path: string, content: string) => void
44
+ isLoadingFiles: boolean
44
45
  createFile: (props: ICreateFileProps) => ICreateFileResult
45
46
  aiAutocompleteState: [boolean, React.Dispatch<React.SetStateAction<boolean>>]
46
47
  }
@@ -49,8 +50,8 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
49
50
  currentFile,
50
51
  files,
51
52
  updateFileContent,
52
- appendNewFile,
53
53
  fileSidebarState,
54
+ isLoadingFiles = true,
54
55
  handleFileChange,
55
56
  entrypointFileName = "index.tsx",
56
57
  createFile,
@@ -230,7 +231,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
230
231
  (filename) => !isHiddenFile(filename),
231
232
  ).length > 0
232
233
  ? "Select file"
233
- : "No files"
234
+ : isLoadingFiles
235
+ ? "Loading files..."
236
+ : "No files"
234
237
  }
235
238
  />
236
239
  </SelectTrigger>
@@ -91,8 +91,9 @@ export const PackageReleasesDashboard = ({
91
91
  </div>
92
92
  <div className="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 mt-2 text-sm text-gray-600">
93
93
  <div
94
- className="flex cursor-pointer items-center gap-1"
94
+ className={`flex items-center gap-1 ${pkg.github_repo_full_name ? "cursor-pointer" : ""}`}
95
95
  onClick={() =>
96
+ pkg.github_repo_full_name &&
96
97
  window?.open(
97
98
  `https://github.com/${pkg.github_repo_full_name}/tree/${latestRelease?.branch_name || "main"}`,
98
99
  "_blank",
@@ -121,21 +122,23 @@ export const PackageReleasesDashboard = ({
121
122
  </div>
122
123
 
123
124
  <div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-2">
124
- <Button
125
- variant="outline"
126
- size="sm"
127
- className="flex items-center gap-2 justify-center min-w-[120px] h-9"
128
- onClick={() =>
129
- window.open(
130
- `https://github.com/${pkg.github_repo_full_name}`,
131
- "_blank",
132
- )
133
- }
134
- >
135
- <GitHubLogoIcon className="w-4 h-4" />
136
- <span className="hidden sm:inline">Repository</span>
137
- <span className="sm:hidden">Repository</span>
138
- </Button>
125
+ {pkg.github_repo_full_name && (
126
+ <Button
127
+ variant="outline"
128
+ size="sm"
129
+ className="flex items-center gap-2 justify-center min-w-[120px] h-9"
130
+ onClick={() =>
131
+ window.open(
132
+ `https://github.com/${pkg.github_repo_full_name}`,
133
+ "_blank",
134
+ )
135
+ }
136
+ >
137
+ <GitHubLogoIcon className="w-4 h-4" />
138
+ <span className="hidden sm:inline">Repository</span>
139
+ <span className="sm:hidden">Repository</span>
140
+ </Button>
141
+ )}
139
142
  {latestBuild && (
140
143
  <Button
141
144
  variant="outline"
@@ -164,15 +167,17 @@ export const PackageReleasesDashboard = ({
164
167
  </Button>
165
168
  </DropdownMenuTrigger>
166
169
  <DropdownMenuContent align="end">
167
- <DropdownMenuItem asChild>
168
- <a
169
- href={`https://github.com/${pkg.github_repo_full_name}`}
170
- target="_blank"
171
- rel="noopener noreferrer"
172
- >
173
- View Source
174
- </a>
175
- </DropdownMenuItem>
170
+ {pkg.github_repo_full_name && (
171
+ <DropdownMenuItem asChild>
172
+ <a
173
+ href={`https://github.com/${pkg.github_repo_full_name}`}
174
+ target="_blank"
175
+ rel="noopener noreferrer"
176
+ >
177
+ View Source
178
+ </a>
179
+ </DropdownMenuItem>
180
+ )}
176
181
  <DropdownMenuItem asChild>
177
182
  <a href="#" download>
178
183
  Download Build
@@ -1,41 +1,16 @@
1
- import { useEffect, useState } from "react"
2
- import { useParams } from "wouter"
3
- import { usePackageById } from "./use-package-by-package-id"
4
- import { usePackageByName } from "./use-package-by-package-name"
5
- import { useUrlParams } from "./use-url-params"
1
+ import { useCurrentPackageInfo } from "./use-current-package-info"
6
2
 
7
3
  export const useCurrentPackageId = (): {
8
4
  packageId: string | null
9
5
  isLoading: boolean
10
6
  error: (Error & { status: number }) | null
11
7
  } => {
12
- const urlParams = useUrlParams()
13
- const urlPackageId = urlParams.package_id
14
- const wouter = useParams()
15
- const [packageIdFromUrl, setPackageId] = useState<string | null>(urlPackageId)
16
-
17
- useEffect(() => {
18
- if (urlPackageId) {
19
- setPackageId(urlPackageId)
20
- }
21
- }, [urlPackageId])
22
-
23
- const packageName =
24
- wouter.author && wouter.packageName
25
- ? `${wouter.author}/${wouter.packageName}`
26
- : null
27
-
28
- const {
29
- data: packageByName,
30
- isLoading: isLoadingPackageByName,
31
- error: errorPackageByName,
32
- } = usePackageByName(packageName)
33
-
34
- const packageId = packageIdFromUrl ?? packageByName?.package_id ?? null
8
+ const { packageInfo, isLoading, error } = useCurrentPackageInfo()
9
+ const packageId = packageInfo?.package_id ?? null
35
10
 
36
11
  return {
37
12
  packageId,
38
- isLoading: isLoadingPackageByName,
39
- error: errorPackageByName,
13
+ isLoading,
14
+ error,
40
15
  }
41
16
  }
@@ -1,8 +1,32 @@
1
- import { useCurrentPackageId } from "./use-current-package-id"
1
+ import { useParams } from "wouter"
2
2
  import { usePackageById } from "./use-package-by-package-id"
3
+ import { usePackageByName } from "./use-package-by-package-name"
4
+ import { useUrlParams } from "./use-url-params"
5
+ import type { Package } from "fake-snippets-api/lib/db/schema"
3
6
 
4
- export const useCurrentPackageInfo = () => {
5
- const { packageId } = useCurrentPackageId()
6
- const { data: packageInfo, ...rest } = usePackageById(packageId)
7
- return { packageInfo, ...rest }
7
+ export const useCurrentPackageInfo = (): {
8
+ packageInfo: Package | undefined
9
+ isLoading: boolean
10
+ error: (Error & { status: number }) | null
11
+ refetch: () => Promise<unknown>
12
+ } => {
13
+ const urlParams = useUrlParams()
14
+ const packageIdFromQuery = urlParams.package_id ?? null
15
+
16
+ const { author, packageName } = useParams()
17
+ const packageSlug = author && packageName ? `${author}/${packageName}` : null
18
+
19
+ const queryById = usePackageById(packageIdFromQuery)
20
+ const queryByName = usePackageByName(packageSlug)
21
+
22
+ const data = queryById.data ?? queryByName.data
23
+ const isLoading = queryById.isLoading || queryByName.isLoading
24
+ const error =
25
+ (queryById.error as (Error & { status: number }) | null) ??
26
+ (queryByName.error as (Error & { status: number }) | null) ??
27
+ null
28
+
29
+ const refetch = packageIdFromQuery ? queryById.refetch : queryByName.refetch
30
+
31
+ return { packageInfo: data, isLoading, error, refetch }
8
32
  }
@@ -28,7 +28,7 @@ export const useGlobalStore = create<Store>()(
28
28
  ),
29
29
  )
30
30
 
31
- useGlobalStore.subscribe((state, prevState) => {
31
+ useGlobalStore.subscribe((state) => {
32
32
  ;(window as any).globalStore = state
33
33
  window.TSCIRCUIT_REGISTRY_TOKEN = state.session?.token ?? null
34
34
  })