22
33import { useState , useEffect , useRef , useCallback } from "react" ;
44import { useRouter } from "next/navigation" ;
5- import { db , auth } from "@/lib/firebase" ;
5+ import { db } from "@/lib/firebase" ;
66import {
77 collection ,
88 setDoc ,
99 serverTimestamp ,
1010 doc ,
11+ getDoc ,
1112 getDocs ,
1213 Timestamp ,
1314} from "firebase/firestore" ;
@@ -31,13 +32,7 @@ import { convertImageToWebP } from "@/lib/image-utils";
3132import { useDropzone } from "react-dropzone" ;
3233import { useAutosave } from "@/hooks/use-autosave" ;
3334import { format } from "date-fns" ;
34-
35- // Type for an author document
36- interface Author {
37- id : string ;
38- name : string ;
39- uid : string ;
40- }
35+ import { useAuth } from "@/lib/auth-context" ;
4136
4237export default function NewArticlePage ( ) {
4338 // Generate article ID on mount for asset uploads
@@ -60,12 +55,7 @@ export default function NewArticlePage() {
6055 const [ previewHtml , setPreviewHtml ] = useState ( "" ) ;
6156 const contentRef = useRef < HTMLTextAreaElement > ( null ! ) ;
6257
63- // Author autocomplete state
6458 const [ authorName , setAuthorName ] = useState ( "" ) ;
65- const [ selectedAuthor , setSelectedAuthor ] = useState < Author | null > ( null ) ;
66- const [ authors , setAuthors ] = useState < Author [ ] > ( [ ] ) ;
67- const [ showSuggestions , setShowSuggestions ] = useState ( false ) ;
68- const suggestionsRef = useRef < HTMLDivElement > ( null ) ;
6959
7060 // Label autocomplete state
7161 const [ existingLabels , setExistingLabels ] = useState < string [ ] > ( [ ] ) ;
@@ -91,6 +81,7 @@ export default function NewArticlePage() {
9181
9282 const router = useRouter ( ) ;
9383 const { toast } = useToast ( ) ;
84+ const { user } = useAuth ( ) ;
9485
9586 // Custom thumbnail uploader using dropzone
9687 const { getRootProps, getInputProps, isDragActive } = useDropzone ( {
@@ -126,27 +117,35 @@ export default function NewArticlePage() {
126117 maxFiles : 1 ,
127118 } ) ;
128119
129- // Fetch authors on mount
120+ // Always attribute new articles to the signed-in author.
130121 useEffect ( ( ) => {
131- const fetchAuthors = async ( ) => {
122+ const fetchCurrentAuthor = async ( ) => {
123+ if ( ! user ) {
124+ setAuthorName ( "" ) ;
125+ return ;
126+ }
127+
132128 try {
133- const snap = await getDocs ( collection ( db , "authors" ) ) ;
134- const docs = snap . docs . map ( ( doc ) => ( {
135- id : doc . id ,
136- ...doc . data ( ) ,
137- } ) ) as Author [ ] ;
138- setAuthors ( docs ) ;
129+ const snap = await getDoc ( doc ( db , "authors" , user . uid ) ) ;
130+ const name = snap . data ( ) ?. name ;
131+
132+ if ( typeof name !== "string" || ! name . trim ( ) ) {
133+ throw new Error ( "Author profile has no name" ) ;
134+ }
135+
136+ setAuthorName ( name ) ;
139137 } catch ( error ) {
140- console . error ( "Error fetching authors:" , error ) ;
138+ console . error ( "Error fetching current author:" , error ) ;
139+ setAuthorName ( "" ) ;
141140 toast ( {
142141 title : "Error" ,
143- description : "Failed to load authors " ,
142+ description : "Failed to load your author profile " ,
144143 variant : "destructive" ,
145144 } ) ;
146145 }
147146 } ;
148- fetchAuthors ( ) ;
149- } , [ toast ] ) ;
147+ fetchCurrentAuthor ( ) ;
148+ } , [ user , toast ] ) ;
150149
151150 // Fetch existing labels from articles
152151 useEffect ( ( ) => {
@@ -176,32 +175,9 @@ export default function NewArticlePage() {
176175 }
177176 } , [ title ] ) ;
178177
179- // Update selected author if input changes
180- useEffect ( ( ) => {
181- if ( authorName . trim ( ) === "" ) {
182- setSelectedAuthor ( null ) ;
183- return ;
184- }
185- const match = authors . find ( ( a ) =>
186- a . name . toLowerCase ( ) . includes ( authorName . toLowerCase ( ) ) ,
187- ) ;
188-
189- if ( match ) {
190- setSelectedAuthor ( match ) ;
191- } else {
192- setSelectedAuthor ( null ) ;
193- }
194- } , [ authorName , authors ] ) ;
195-
196178 // Hide suggestions on click outside
197179 useEffect ( ( ) => {
198180 const handleClickOutside = ( event : MouseEvent ) => {
199- if (
200- suggestionsRef . current &&
201- ! suggestionsRef . current . contains ( event . target as Node )
202- ) {
203- setShowSuggestions ( false ) ;
204- }
205181 if (
206182 labelSuggestionsRef . current &&
207183 ! labelSuggestionsRef . current . contains ( event . target as Node )
@@ -304,15 +280,14 @@ export default function NewArticlePage() {
304280 }
305281 if ( ! authorName . trim ( ) ) {
306282 toast ( {
307- title : "Missing author " ,
308- description : "Please select an author for the article " ,
283+ title : "Author unavailable " ,
284+ description : "Your author profile could not be loaded " ,
309285 variant : "destructive" ,
310286 } ) ;
311287 throw new Error ( "Missing author" ) ;
312288 }
313289 }
314290
315- const user = auth . currentUser ;
316291 if ( ! user ) {
317292 if ( isManual ) {
318293 toast ( {
@@ -342,11 +317,9 @@ export default function NewArticlePage() {
342317 popularity,
343318 read : readTime ,
344319 slug,
345- authorName : selectedAuthor ? selectedAuthor . name : authorName ,
346- authorUID : selectedAuthor ? selectedAuthor . uid : user . uid ,
347- authorRef : selectedAuthor
348- ? doc ( db , "authors" , selectedAuthor . id )
349- : doc ( db , "authors" , user . uid ) ,
320+ authorName,
321+ authorUID : user . uid ,
322+ authorRef : doc ( db , "authors" , user . uid ) ,
350323
351324 // Set createdAt/date if not exists (merge will keep existing)
352325 // Actually serverTimestamp() will always update.
@@ -410,12 +383,12 @@ export default function NewArticlePage() {
410383 readTime ,
411384 slug ,
412385 authorName ,
413- selectedAuthor ,
414386 isPublished ,
415387 scheduledDate ,
416388 articleId ,
417389 router ,
418390 toast ,
391+ user ,
419392 ] ,
420393 ) ;
421394
@@ -487,43 +460,18 @@ export default function NewArticlePage() {
487460 </ p >
488461 </ div >
489462
490- { /* Author Name with Autocomplete */ }
491- < div className = "mb-6 relative " >
463+ { /* Author */ }
464+ < div className = "mb-6" >
492465 < label className = "block mb-2 font-medium" > Author:</ label >
493466 < Input
494467 value = { authorName }
495- onChange = { ( e ) => {
496- setAuthorName ( e . target . value ) ;
497- setShowSuggestions ( true ) ;
498- } }
499- onFocus = { ( ) => setShowSuggestions ( true ) }
500- placeholder = "Start typing author name..."
501- className = "w-full"
468+ disabled
469+ placeholder = "Loading your author profile..."
470+ className = "w-full opacity-70 cursor-not-allowed"
502471 />
503- { showSuggestions && authors . length > 0 && (
504- < div
505- ref = { suggestionsRef }
506- className = "absolute z-10 w-full border border-white/60 bg-[#1a1a1a] mt-1 max-h-48 overflow-y-auto"
507- >
508- { authors
509- . filter ( ( a ) =>
510- a . name . toLowerCase ( ) . includes ( authorName . toLowerCase ( ) ) ,
511- )
512- . map ( ( a ) => (
513- < div
514- key = { a . id }
515- className = "px-4 py-2 hover:bg-[#8a2be2]/20 cursor-pointer"
516- onClick = { ( ) => {
517- setAuthorName ( a . name ) ;
518- setSelectedAuthor ( a ) ;
519- setShowSuggestions ( false ) ;
520- } }
521- >
522- { a . name }
523- </ div >
524- ) ) }
525- </ div >
526- ) }
472+ < p className = "text-sm text-white/50 mt-1" >
473+ Automatically set from your signed-in account
474+ </ p >
527475 </ div >
528476
529477 { /* Toggle Editor/Preview/Assets for Content */ }
@@ -771,7 +719,7 @@ export default function NewArticlePage() {
771719 < div className = "flex flex-wrap gap-4 mt-8" >
772720 < Button
773721 onClick = { handleManualSave }
774- disabled = { creating }
722+ disabled = { creating || ! authorName }
775723 variant = "outline"
776724 >
777725 { creating && < Loader2 className = "mr-2 h-4 w-4 animate-spin" /> }
0 commit comments