@@ -5,7 +5,6 @@ import { useParams, useRouter } from 'next/navigation';
55import { useAuth } from '@/lib/auth' ;
66import { useNostrIdentity } from '@/hooks/useNostrIdentity' ;
77import {
8- createMomentComment ,
98 loadMomentEngagement ,
109 parseMomentRouteId ,
1110 toggleMomentLike ,
@@ -18,7 +17,7 @@ import { UsersService } from '@/lib/services/users';
1817import { fetchNostrEventById } from '@/lib/nostr/thread' ;
1918import type { NostrEvent } from '@/lib/nostr/nostr' ;
2019import {
21- ArrowLeft , Globe , Heart , Lock , MessageCircle ,
20+ ArrowLeft , Globe , Heart , Lock , MessageCircle , PenLine ,
2221 Repeat2 , Shield , Zap , X , Share2
2322} from 'lucide-react' ;
2423import { useUnifiedDrawer } from '@/context/UnifiedDrawerContext' ;
@@ -234,7 +233,6 @@ export function PostViewClient({
234233 const [ reposts , setReposts ] = useState ( 0 ) ;
235234 const [ liked , setLiked ] = useState ( false ) ;
236235 const [ loading , setLoading ] = useState ( ! preview ?. content ) ;
237- const [ replyContent , setReplyContent ] = useState ( '' ) ;
238236 const [ busy , setBusy ] = useState ( false ) ;
239237
240238 // For Nostr event kind detection
@@ -372,42 +370,50 @@ export function PostViewClient({
372370 } finally { setBusy ( false ) ; }
373371 } ;
374372
375- const sendReply = async ( ) => {
376- const text = replyContent . trim ( ) ;
377- if ( ! momentId || ! text || busy ) return ;
373+ const openReplyComposer = ( ) => {
374+ if ( ! momentId ) return ;
378375 if ( source === 'nostr' && ( isVaultLocked || ! identity ) ) {
379- toast . error ( 'Unlock vault to comment on Nostr' ) ; void unlockAndLoad ( ) ; return ;
376+ toast . error ( 'Unlock vault to reply on Nostr' ) ;
377+ void unlockAndLoad ( ) ;
378+ return ;
380379 }
381- if ( source === 'ecosystem' && ! user ) return ;
382- setBusy ( true ) ;
383- try {
384- const words = `${ text } ${ moment ?. caption || moment ?. content || '' } ` . toLowerCase ( ) . match ( / # ? \w { 3 , } / g) || [ ] ;
385- const topics = Array . from ( new Set ( words . slice ( 0 , 10 ) ) ) ;
386- void import ( '@/lib/connect/feed-settings' ) . then ( ( { recordFeedInteraction } ) =>
387- recordFeedInteraction ( { topics, searchWeight : 3 , isConsciousAction : true } ) ,
388- ) ;
389-
390- const created = await createMomentComment ( {
391- source,
392- id : momentId ,
393- content : text ,
394- userId : user ?. $id ,
395- privateKeyBytes : identity ?. privateKeyBytes ,
396- nsec : identity ?. nsec ,
397- rootPubkey : moment ?. pubkey || nostrEvent ?. pubkey ,
398- nostrId : ( moment as any ) ?. nostrId ,
380+ if ( source === 'ecosystem' && ! user ) {
381+ openUnifiedDrawer ( 'login' , {
382+ title : 'Reply to moment' ,
383+ subtitle : 'Sign in to reply with Kylie assist.' ,
384+ objectKind : 'moment' ,
399385 } ) ;
400- setReplyContent ( '' ) ;
401- if ( created ) {
402- setReplies ( prev => [ ...prev , created ] ) ;
386+ return ;
387+ }
388+ const snippet = String ( moment ?. caption || moment ?. content || preview ?. content || '' ) . trim ( ) ;
389+ openUnifiedDrawer ( 'moment-composer' , {
390+ mode : 'reply' ,
391+ parentMomentId : momentId ,
392+ source,
393+ parentSnippet : snippet . slice ( 0 , 120 ) ,
394+ rootPubkey : moment ?. pubkey || nostrEvent ?. pubkey ,
395+ nostrId : ( moment as any ) ?. nostrId || ( source === 'nostr' ? momentId : undefined ) ,
396+ } ) ;
397+ } ;
398+
399+ useEffect ( ( ) => {
400+ const onReply = ( ev : Event ) => {
401+ const detail = ( ev as CustomEvent ) . detail || { } ;
402+ if ( String ( detail . parentMomentId || '' ) !== String ( momentId ) ) return ;
403+ if ( detail . comment ) {
404+ setReplies ( ( prev ) => {
405+ if ( prev . some ( ( r ) => r . id === detail . comment . id ) ) return prev ;
406+ return [ ...prev , detail . comment ] ;
407+ } ) ;
403408 } else {
404- const refreshed = await loadMomentEngagement ( { source, id : momentId , userId : user ?. $id } ) ;
405- setReplies ( refreshed . comments ) ;
409+ void loadMomentEngagement ( { source, id : momentId , userId : user ?. $id } ) . then ( ( eng ) => {
410+ setReplies ( eng . comments ) ;
411+ } ) ;
406412 }
407- } catch ( e ) {
408- console . error ( e ) ; toast . error ( 'Could not post reply' ) ;
409- } finally { setBusy ( false ) ; }
410- } ;
413+ } ;
414+ window . addEventListener ( 'kylrix:moment- reply-created' , onReply ) ;
415+ return ( ) => window . removeEventListener ( 'kylrix:moment-reply-created' , onReply ) ;
416+ } , [ momentId , source , user ?. $id ] ) ;
411417
412418 const handleShare = ( ) => {
413419 if ( ! momentId ) return ;
@@ -500,14 +506,9 @@ export function PostViewClient({
500506 </ div >
501507 </ article >
502508
503- { /* Reply composer skeleton */ }
504- < div className = "flex gap-2 items-end min-w-0 max-w-full" >
505- < div className = "min-w-0 flex-1 h-[42px] rounded-xl bg-[#000000] border-2 border-white/20 px-4 flex items-center text-sm text-white/50 font-satoshi" >
506- Write a reply…
507- </ div >
508- < div className = "shrink-0 h-[42px] px-4 rounded-xl bg-[#F59E0B]/30 text-black/70 font-extrabold text-sm flex items-center" >
509- Reply
510- </ div >
509+ { /* Reply FAB skeleton */ }
510+ < div className = "flex justify-end pt-2" >
511+ < div className = "h-14 w-14 rounded-2xl bg-[#F59E0B]/40 border-2 border-white/10" />
511512 </ div >
512513 </ div >
513514 </ div >
@@ -710,59 +711,25 @@ export function PostViewClient({
710711 </ article >
711712 ) }
712713
713- { /* Reply composer — shown for posts and replies, not reactions/reposts */ }
714+ { /* Reply FAB → same create-moment drawer (Kylie assist) */ }
714715 { ! isReaction && ! isRepost && (
715- < div className = "flex gap-2 items-end min-w-0 max-w-full" >
716- < textarea
717- value = { replyContent }
718- onChange = { e => {
719- setReplyContent ( e . target . value ) ;
720- const target = e . target ;
721- target . style . height = 'auto' ;
722- target . style . height = `${ Math . min ( target . scrollHeight , 140 ) } px` ;
723- } }
724- onKeyDown = { e => {
725- if ( e . key === 'Enter' && ! e . shiftKey ) {
726- e . preventDefault ( ) ;
727- void sendReply ( ) ;
728- }
729- } }
730- rows = { 1 }
731- autoComplete = "off"
732- autoCorrect = "off"
733- autoCapitalize = "none"
734- spellCheck = { false }
735- data-gramm = "false"
736- placeholder = { source === 'nostr' && isVaultLocked ? 'Unlock vault to reply…' : 'Write a reply…' }
737- className = "min-w-0 flex-1 rounded-xl bg-[#000000] border-2 border-white/20 px-4 py-2.5 text-sm outline-none focus:border-white/40 resize-none max-h-[140px] leading-relaxed text-white font-satoshi placeholder:text-white/40"
738- />
739- { source === 'nostr' && isVaultLocked ? (
740- < button
741- type = "button"
742- onClick = { ( ) => void unlockAndLoad ( ) }
743- className = "shrink-0 h-[42px] rounded-xl bg-[#F59E0B]/15 text-[#F59E0B] font-bold text-sm px-3 inline-flex items-center gap-1.5 border-2 border-[#F59E0B]/40"
744- >
745- < Lock size = { 14 } /> Unlock
746- </ button >
747- ) : (
748- < button
749- type = "button"
750- disabled = { busy || ! replyContent . trim ( ) || ( source === 'ecosystem' && ! user ) }
751- onClick = { ( ) => void sendReply ( ) }
752- className = "shrink-0 h-[42px] rounded-xl bg-[#F59E0B] text-black font-extrabold text-sm px-4 disabled:opacity-40 transition-opacity cursor-pointer"
753- >
754- Reply
755- </ button >
756- ) }
757- </ div >
716+ < button
717+ type = "button"
718+ onClick = { openReplyComposer }
719+ className = "fixed bottom-6 right-5 z-[40] h-14 w-14 rounded-2xl bg-[#F59E0B] text-black shadow-[0_8px_24px_rgba(245,158,11,0.35)] border-2 border-black/20 inline-flex items-center justify-center hover:scale-105 active:scale-95 transition-transform cursor-pointer"
720+ title = "Reply"
721+ aria-label = "Reply with assist"
722+ >
723+ < PenLine size = { 22 } strokeWidth = { 2.5 } />
724+ </ button >
758725 ) }
759726
760727 { /* Replies list */ }
761728 { ! isReaction && ! isRepost && (
762- < ul className = "space-y-2 min-w-0 max-w-full list-none p-0 m-0" >
729+ < ul className = "space-y-2 min-w-0 max-w-full list-none p-0 m-0 pb-24 " >
763730 { replies . length === 0 ? (
764731 < li className = "rounded-[18px] border-2 border-white/20 bg-[#000000] px-4 py-8 text-center text-sm text-white/50" >
765- No comments yet
732+ No comments yet — tap the reply button to write with Kylie assist
766733 </ li >
767734 ) : (
768735 replies . map ( r => (
0 commit comments