@@ -47,34 +47,55 @@ export function ProfilePage() {
4747 // Convert file to base64
4848 const reader = new FileReader ( ) ;
4949 reader . onload = async ( e ) => {
50- const base64 = e . target ?. result as string ;
51-
52- // Generate unique filename
53- const fileName = `avatar-${ user . username } -${ Date . now ( ) } .${ file . type . split ( '/' ) [ 1 ] } ` ;
54- const path = `avatars/${ fileName } ` ;
55-
56- // Upload to GitHub
57- await putFile ( path , base64 . split ( ',' ) [ 1 ] , `Upload avatar for ${ user . username } ` , true ) ;
58-
59- // Update user profile
60- const updatedUser = {
61- ...user ,
62- avatar : `https://raw.githubusercontent.com/feekool/feekool.github.io/master/avatars/${ fileName } `
63- } ;
64-
65- const content = stringifyFrontmatter ( updatedUser , '' ) ;
66-
67- // Get the current file with SHA to avoid 422 error
68- const userPath = `users/${ user . username } .md` ;
69- const existingFile = await getFile ( userPath ) ;
70-
71- await putFile ( userPath , content , `Update profile for ${ user . username } ` , false , existingFile ?. sha ) ;
72-
73- // Update local storage
74- localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
75-
76- // Reload page to update avatar in UI
77- window . location . reload ( ) ;
50+ try {
51+ const base64 = e . target ?. result as string ;
52+
53+ // Generate unique filename
54+ const fileName = `avatar-${ user . username } -${ Date . now ( ) } .${ file . type . split ( '/' ) [ 1 ] } ` ;
55+ const path = `avatars/${ fileName } ` ;
56+
57+ // Upload to GitHub
58+ await putFile ( path , base64 . split ( ',' ) [ 1 ] , `Upload avatar for ${ user . username } ` , true ) ;
59+
60+ // Update user profile
61+ const updatedUser = {
62+ ...user ,
63+ avatar : `https://raw.githubusercontent.com/feekool/feekool.github.io/master/avatars/${ fileName } `
64+ } ;
65+
66+ const content = stringifyFrontmatter ( updatedUser , '' ) ;
67+
68+ // Get the current file with SHA to avoid 422 error
69+ const userPath = `users/${ user . username } .md` ;
70+ let existingFile = null ;
71+ try {
72+ existingFile = await getFile ( userPath ) ;
73+ } catch ( error : any ) {
74+ if ( error . status !== 401 && error . status !== 404 ) {
75+ throw error ;
76+ }
77+ }
78+
79+ try {
80+ await putFile ( userPath , content , `Update profile for ${ user . username } ` , false , existingFile ?. sha ) ;
81+ } catch ( error : any ) {
82+ if ( error . status === 401 ) {
83+ alert ( 'GitHub token not configured. Avatar saved locally only. Set VITE_API_KEY to sync to GitHub.' ) ;
84+ } else {
85+ throw error ;
86+ }
87+ }
88+
89+ // Update local storage
90+ localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
91+
92+ // Reload page to update avatar in UI
93+ window . location . reload ( ) ;
94+ } catch ( error ) {
95+ safeLogError ( 'Error uploading avatar:' , error ) ;
96+ alert ( 'Failed to upload avatar. Make sure VITE_API_KEY is set.' ) ;
97+ setIsUploading ( false ) ;
98+ }
7899 } ;
79100
80101 reader . readAsDataURL ( file ) ;
@@ -100,9 +121,24 @@ export function ProfilePage() {
100121
101122 // Get the current file with SHA to avoid 422 error
102123 const userPath = `users/${ user . username } .md` ;
103- const existingFile = await getFile ( userPath ) ;
124+ let existingFile = null ;
125+ try {
126+ existingFile = await getFile ( userPath ) ;
127+ } catch ( error : any ) {
128+ if ( error . status !== 401 && error . status !== 404 ) {
129+ throw error ;
130+ }
131+ }
104132
105- await putFile ( userPath , content , `Update profile for ${ user . username } ` , false , existingFile ?. sha ) ;
133+ try {
134+ await putFile ( userPath , content , `Update profile for ${ user . username } ` , false , existingFile ?. sha ) ;
135+ } catch ( error : any ) {
136+ if ( error . status === 401 ) {
137+ alert ( 'GitHub token not configured. Profile saved locally only. Set VITE_API_KEY to sync to GitHub.' ) ;
138+ } else {
139+ throw error ;
140+ }
141+ }
106142
107143 // Update local storage
108144 localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
@@ -131,9 +167,24 @@ export function ProfilePage() {
131167
132168 // Get the current file with SHA to avoid 422 error
133169 const userPath = `users/${ user . username } .md` ;
134- const existingFile = await getFile ( userPath ) ;
170+ let existingFile = null ;
171+ try {
172+ existingFile = await getFile ( userPath ) ;
173+ } catch ( error : any ) {
174+ if ( error . status !== 401 && error . status !== 404 ) {
175+ throw error ;
176+ }
177+ }
135178
136- await putFile ( userPath , content , `Reset avatar to Gravatar for ${ user . username } ` , false , existingFile ?. sha ) ;
179+ try {
180+ await putFile ( userPath , content , `Reset avatar to Gravatar for ${ user . username } ` , false , existingFile ?. sha ) ;
181+ } catch ( error : any ) {
182+ if ( error . status === 401 ) {
183+ alert ( 'GitHub token not configured. Avatar reset locally only. Set VITE_API_KEY to sync to GitHub.' ) ;
184+ } else {
185+ throw error ;
186+ }
187+ }
137188 localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
138189 window . location . reload ( ) ;
139190 } catch ( error ) {
@@ -291,8 +342,24 @@ export function ProfilePage() {
291342 try {
292343 const updatedUser = { ...user ! , lang : newLang } ;
293344 const content = stringifyFrontmatter ( updatedUser , '' ) ;
294- const existingFile = await getFile ( `users/${ user ! . username } .md` ) ;
295- await putFile ( `users/${ user ! . username } .md` , content , `Update language for ${ user ! . username } ` , false , existingFile ?. sha ) ;
345+ let existingFile = null ;
346+ try {
347+ existingFile = await getFile ( `users/${ user ! . username } .md` ) ;
348+ } catch ( error : any ) {
349+ if ( error . status !== 401 && error . status !== 404 ) {
350+ throw error ;
351+ }
352+ }
353+
354+ try {
355+ await putFile ( `users/${ user ! . username } .md` , content , `Update language for ${ user ! . username } ` , false , existingFile ?. sha ) ;
356+ } catch ( error : any ) {
357+ if ( error . status === 401 ) {
358+ console . warn ( 'Language saved locally only - GitHub token not configured.' ) ;
359+ } else {
360+ throw error ;
361+ }
362+ }
296363 localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
297364 localStorage . setItem ( 'lang' , newLang ) ;
298365 window . location . reload ( ) ; // Reload to apply language
@@ -324,8 +391,24 @@ export function ProfilePage() {
324391 try {
325392 const updatedUser = { ...user ! , theme : newTheme } ;
326393 const content = stringifyFrontmatter ( updatedUser , '' ) ;
327- const existingFile = await getFile ( `users/${ user ! . username } .md` ) ;
328- await putFile ( `users/${ user ! . username } .md` , content , `Update theme for ${ user ! . username } ` , false , existingFile ?. sha ) ;
394+ let existingFile = null ;
395+ try {
396+ existingFile = await getFile ( `users/${ user ! . username } .md` ) ;
397+ } catch ( error : any ) {
398+ if ( error . status !== 401 && error . status !== 404 ) {
399+ throw error ;
400+ }
401+ }
402+
403+ try {
404+ await putFile ( `users/${ user ! . username } .md` , content , `Update theme for ${ user ! . username } ` , false , existingFile ?. sha ) ;
405+ } catch ( error : any ) {
406+ if ( error . status === 401 ) {
407+ console . warn ( 'Theme saved locally only - GitHub token not configured.' ) ;
408+ } else {
409+ throw error ;
410+ }
411+ }
329412 localStorage . setItem ( 'user' , JSON . stringify ( updatedUser ) ) ;
330413 } catch ( error ) {
331414 safeLogError ( 'Error saving theme:' , error ) ;
@@ -335,6 +418,15 @@ export function ProfilePage() {
335418 updateUserTheme ( ) ;
336419 } }
337420 className = "px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700"
421+ >
422+ } catch ( error ) {
423+ safeLogError ( 'Error saving theme:' , error ) ;
424+ alert ( 'Failed to save theme. Please try again.' ) ;
425+ }
426+ } ;
427+ updateUserTheme ( ) ;
428+ } }
429+ className = "px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700"
338430 >
339431 < option value = "light" > Light</ option >
340432 < option value = "dark" > Dark < / o p t i o n >
0 commit comments