34 const [tokenError, setTokenError] = useState<string | null>(null);
35 const [username, setUsername] = useState<string>("");
36 // Flag to track if we've already tried to fetch data
37 const [initialFetchDone, setInitialFetchDone] = useState(false);
38 // Editor key for forcing re-render
39 const [editorKey, setEditorKey] = useState<string>("editor-0");
57 }, [apiToken]);
58
59 // Fetch user information to get username
60 const fetchUserInfo = useCallback(async () => {
61 if (!apiToken) return;
62
63 try {
64 const response = await fetch(`/api/user`, {
65 headers: { "Authorization": `Bearer ${apiToken}` },
66 });
74 }
75 } catch (error) {
76 console.warn("Could not fetch user info:", error);
77 // Non-critical error, don't show to user
78 }
79 }, [apiToken]);
80
81 const fetchVals = useCallback(async () => {
82 if (!apiToken) {
83 setError("Val Town API Key is required to list your vals.");
84 setLoading(false);
85 setVals([]);
86 setInitialFetchDone(true);
87 return;
88 }
97 try {
98 const queryParams = filterPrivacy !== "all" ? `?privacy=${filterPrivacy}` : "";
99 const response = await fetch(`/api/vals${queryParams}`, {
100 headers: { "Authorization": `Bearer ${apiToken}` },
101 });
108 setVals([]);
109 } else {
110 setError(`Failed to fetch vals: ${errorData.error || response.statusText}`);
111 }
112 throw new Error(`Failed to fetch vals (Status: ${response.status})`);
113 }
114 const data = await response.json();
121 }
122 } catch (err) {
123 console.error(`Error fetching vals:`, err);
124 if (!error && !tokenError) {
125 setError(`Failed to load vals. Please try again later.`);
127 } finally {
128 setLoading(false);
129 setInitialFetchDone(true);
130 }
131 }, [apiToken, filterPrivacy, initialFetchDone, error, tokenError]);
132
133 // Run initial fetch only once when API token is available
134 useEffect(() => {
135 if (apiToken && !initialFetchDone && !loading) {
136 fetchUserInfo();
137 fetchVals();
138 }
139 }, [apiToken, initialFetchDone, loading, fetchUserInfo, fetchVals]);
140
141 const handleValSelect = async (val: Val) => {
165 try {
166 const valId = val.id;
167 const response = await fetch(`/api/vals/${valId}/moi`);
168 if (!response.ok) {
169 const errorData = await response.json().catch(() => ({ error: `HTTP error ${response.status}` }));
170 throw new Error(errorData.error || "Failed to fetch moi.md");
171 }
172 const data: MoiFile = await response.json();
179 pendingSaveContentRef.current = initialContent;
180 } catch (err) {
181 console.error("Error fetching moi.md:", err);
182 setItemError(`Failed to fetch moi.md content: ${err instanceof Error ? err.message : String(err)}`);
183 if (selectedVal) {
184 const defaultContent = generateDefaultMoiContent(selectedVal);
274 console.log("Updating existing moi.md file");
275 // Update existing moi.md file using the current API endpoint
276 const response = await fetch(`/api/vals/${valId}/moi`, {
277 method: "POST",
278 headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiToken}` },
296 console.log("Creating new moi.md file");
297 // Create new moi.md file for vals that don't have one yet
298 const response = await fetch(`/api/vals/${valId}/file`, {
299 method: "POST",
300 headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiToken}` },
327
328 // Refresh the vals list to show updated status
329 fetchVals();
330
331 setTimeout(() => setSaveSuccess(false), 3000);
351 setApiToken(newToken);
352
353 // Reset the initialFetchDone flag if the token changes
354 if (newToken !== apiToken) {
355 setInitialFetchDone(false);
356 }
357 };
443 {/* Refresh Button: Aqua background, Black text */}
444 <button
445 onClick={fetchVals}
446 disabled={loading || !apiToken}
447 className={`px-3 py-1.5 rounded-md text-sm transition-colors text-[#000000] ${