Skip to content

Commit 288b8a8

Browse files
committed
mit
fix: reqs
1 parent 7343422 commit 288b8a8

4 files changed

Lines changed: 94 additions & 58 deletions

File tree

src/lib/auth.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { createContext, useContext, useState, useEffect } from 'react';
22
import { getFile, putFile } from './github';
33
import { parseFrontmatter, stringifyFrontmatter, generateGravatarUrl } from './utils';
44
import { swManager } from './serviceWorker';
5+
import { DEFAULT_ACCENT_COLOR } from './userSettings';
56

67
export interface User {
78
username: string;
@@ -46,11 +47,13 @@ export function AuthProvider({ children }: {children: React.ReactNode;}) {
4647
const file = await getFile(path);
4748

4849
let userData: User;
50+
let isNewUser = false;
4951

5052
if (file) {
5153
const { data } = parseFrontmatter<User>(file.content);
5254
userData = { ...data, username };
5355
} else {
56+
isNewUser = true;
5457
const defaultAvatarUrl = await generateGravatarUrl(username);
5558
userData = {
5659
username,
@@ -64,6 +67,19 @@ export function AuthProvider({ children }: {children: React.ReactNode;}) {
6467
await putFile(path, content, `Create user ${username}`);
6568
}
6669

70+
// Create default settings file if it doesn't exist
71+
const settingsPath = `users/${username}-settings.md`;
72+
const settingsFile = await getFile(settingsPath);
73+
74+
if (!settingsFile) {
75+
const defaultSettings = {
76+
accentColor: DEFAULT_ACCENT_COLOR,
77+
updatedAt: new Date().toISOString()
78+
};
79+
const settingsContent = stringifyFrontmatter(defaultSettings, '');
80+
await putFile(settingsPath, settingsContent, `Create settings for ${username}`);
81+
}
82+
6783
setUser(userData);
6884
localStorage.setItem('user', JSON.stringify(userData));
6985
} finally {

src/lib/forum.ts

Lines changed: 54 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getFile, putFile, listFiles, isFileCached } from './github';
1+
import { getFile, putFile, listFiles } from './github';
22
import { parseFrontmatter, stringifyFrontmatter, generateId, transliterate, normalizeText, slugify } from './utils';
33

44
export interface Forum {
@@ -45,30 +45,25 @@ function matchesSearch(item: { author: string; body: string }, options?: { autho
4545
return true;
4646
}
4747

48-
const cacheFilePaths = async (files: Array<{ name: string; path?: string }>) => {
49-
await Promise.all(
50-
files
51-
.filter(file => file.name.endsWith('.md') && file.path && !isFileCached(file.path))
52-
.map(file => getFile(file.path!))
53-
);
54-
};
55-
5648
export async function listForums(): Promise<Forum[]> {
5749
try {
5850
const files = await listFiles('forums');
5951
const forums: Forum[] = [];
6052

61-
await cacheFilePaths(files);
62-
63-
for (const file of files) {
64-
if (file.name.endsWith('.md') && file.path) {
65-
const fileData = await getFile(file.path);
66-
if (fileData) {
67-
const { data } = parseFrontmatter<Forum>(fileData.content);
68-
forums.push({ ...data, slug: file.name.replace('.md', '') });
69-
}
53+
// Load all files in parallel, cache them, no duplicate requests
54+
const fileDataPromises = files
55+
.filter(file => file.name.endsWith('.md') && file.path)
56+
.map(file => getFile(file.path!));
57+
58+
const fileDataArray = await Promise.all(fileDataPromises);
59+
60+
fileDataArray.forEach((fileData, index) => {
61+
if (fileData) {
62+
const file = files[index];
63+
const { data } = parseFrontmatter<Forum>(fileData.content);
64+
forums.push({ ...data, slug: file.name.replace('.md', '') });
7065
}
71-
}
66+
});
7267

7368
return forums.sort((a, b) => Number(a.order || 0) - Number(b.order || 0));
7469
} catch (error: any) {
@@ -114,26 +109,29 @@ export async function listTopics(forumSlug: string, options?: { author?: string;
114109
const files = await listFiles('topics');
115110
const topics: Topic[] = [];
116111

117-
await cacheFilePaths(files);
118-
119-
for (const file of files) {
120-
if (file.name.endsWith('.md') && file.path) {
121-
const fileData = await getFile(file.path);
122-
if (fileData) {
123-
const { data, content } = parseFrontmatter<Topic>(fileData.content);
124-
if (data.forumSlug === forumSlug) {
125-
const topic = {
126-
...data,
127-
id: file.name.replace('.md', ''),
128-
body: content
129-
};
130-
if (matchesSearch(topic, options)) {
131-
topics.push(topic);
132-
}
112+
// Load all files in parallel, cache them, no duplicate requests
113+
const fileDataPromises = files
114+
.filter(file => file.name.endsWith('.md') && file.path)
115+
.map(file => getFile(file.path!));
116+
117+
const fileDataArray = await Promise.all(fileDataPromises);
118+
const mdFiles = files.filter(file => file.name.endsWith('.md') && file.path);
119+
120+
fileDataArray.forEach((fileData, index) => {
121+
if (fileData) {
122+
const { data, content } = parseFrontmatter<Topic>(fileData.content);
123+
if (data.forumSlug === forumSlug) {
124+
const topic = {
125+
...data,
126+
id: mdFiles[index].name.replace('.md', ''),
127+
body: content
128+
};
129+
if (matchesSearch(topic, options)) {
130+
topics.push(topic);
133131
}
134132
}
135133
}
136-
}
134+
});
137135

138136
return topics.sort(
139137
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
@@ -188,26 +186,29 @@ export async function listPosts(topicId: string, options?: { author?: string; te
188186
const files = await listFiles('posts');
189187
const posts: Post[] = [];
190188

191-
await cacheFilePaths(files);
192-
193-
for (const file of files) {
194-
if (file.name.endsWith('.md') && file.path) {
195-
const fileData = await getFile(file.path);
196-
if (fileData) {
197-
const { data, content } = parseFrontmatter<Post>(fileData.content);
198-
if (data.topicId === topicId) {
199-
const post = {
200-
...data,
201-
id: file.name.replace('.md', ''),
202-
body: content
203-
};
204-
if (matchesSearch(post, options)) {
205-
posts.push(post);
206-
}
189+
// Load all files in parallel, cache them, no duplicate requests
190+
const fileDataPromises = files
191+
.filter(file => file.name.endsWith('.md') && file.path)
192+
.map(file => getFile(file.path!));
193+
194+
const fileDataArray = await Promise.all(fileDataPromises);
195+
const mdFiles = files.filter(file => file.name.endsWith('.md') && file.path);
196+
197+
fileDataArray.forEach((fileData, index) => {
198+
if (fileData) {
199+
const { data, content } = parseFrontmatter<Post>(fileData.content);
200+
if (data.topicId === topicId) {
201+
const post = {
202+
...data,
203+
id: mdFiles[index].name.replace('.md', ''),
204+
body: content
205+
};
206+
if (matchesSearch(post, options)) {
207+
posts.push(post);
207208
}
208209
}
209210
}
210-
}
211+
});
211212

212213
return posts.sort(
213214
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()

src/lib/userSettings.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ export async function saveUserSettings(username: string, settings: UserSettings)
4747
try {
4848
const path = `users/${username}-settings.md`;
4949
const content = stringifyFrontmatter(settings, '');
50-
await putFile(path, content, `Update settings for ${username}`);
50+
51+
// Get the current file with SHA to avoid 422 error
52+
const existingFile = await getFile(path);
53+
54+
await putFile(path, content, `Update settings for ${username}`, false, existingFile?.sha);
5155

5256
// Clear cache to ensure fresh data on next load
5357
if (typeof clearGitHubApiCache === 'function') {

src/pages/ProfilePage.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom';
33
import { ArrowLeft, Camera, Save, User as UserIcon, Calendar, Globe } from 'lucide-react';
44
import { useAuth } from '../lib/auth';
55
import { useTranslation } from '../lib/i18n';
6-
import { putFile } from '../lib/github';
6+
import { putFile, getFile } from '../lib/github';
77
import { stringifyFrontmatter, generateGravatarUrl, safeLogError } from '../lib/utils';
88
import { ColorPicker } from '../components/ColorPicker';
99

@@ -61,7 +61,12 @@ export function ProfilePage() {
6161
};
6262

6363
const content = stringifyFrontmatter(updatedUser, '');
64-
await putFile(`users/${user.username}.md`, content, `Update profile for ${user.username}`);
64+
65+
// Get the current file with SHA to avoid 422 error
66+
const userPath = `users/${user.username}.md`;
67+
const existingFile = await getFile(userPath);
68+
69+
await putFile(userPath, content, `Update profile for ${user.username}`, false, existingFile?.sha);
6570

6671
// Update local storage
6772
localStorage.setItem('user', JSON.stringify(updatedUser));
@@ -90,7 +95,12 @@ export function ProfilePage() {
9095
};
9196

9297
const content = stringifyFrontmatter(updatedUser, '');
93-
await putFile(`users/${user.username}.md`, content, `Update profile for ${user.username}`);
98+
99+
// Get the current file with SHA to avoid 422 error
100+
const userPath = `users/${user.username}.md`;
101+
const existingFile = await getFile(userPath);
102+
103+
await putFile(userPath, content, `Update profile for ${user.username}`, false, existingFile?.sha);
94104

95105
// Update local storage
96106
localStorage.setItem('user', JSON.stringify(updatedUser));
@@ -116,7 +126,12 @@ export function ProfilePage() {
116126
avatar: defaultAvatarUrl
117127
};
118128
const content = stringifyFrontmatter(updatedUser, '');
119-
await putFile(`users/${user.username}.md`, content, `Reset avatar to Gravatar for ${user.username}`);
129+
130+
// Get the current file with SHA to avoid 422 error
131+
const userPath = `users/${user.username}.md`;
132+
const existingFile = await getFile(userPath);
133+
134+
await putFile(userPath, content, `Reset avatar to Gravatar for ${user.username}`, false, existingFile?.sha);
120135
localStorage.setItem('user', JSON.stringify(updatedUser));
121136
window.location.reload();
122137
} catch (error) {

0 commit comments

Comments
 (0)