@@ -12,14 +12,27 @@ import {
1212 __experimentalUseBorderProps as useBorderProps ,
1313} from '@wordpress/block-editor' ;
1414import { __ } from '@wordpress/i18n' ;
15- import { useMemo } from '@wordpress/element' ;
15+ import { createContext , useContext , useMemo } from '@wordpress/element' ;
1616import { PanelBody , RangeControl , ToggleControl } from '@wordpress/components' ;
1717import { addQueryArgs , removeQueryArgs } from '@wordpress/url' ;
1818/**
1919 * Internal dependencies
2020 */
2121import { useUserAvatar , usePostAuthors } from './hooks' ;
2222import { useCustomByline , extractAuthorIdsFromByline } from '../../shared/hooks/use-custom-byline' ;
23+
24+ /**
25+ * Fallback context that always returns null.
26+ * Used when the shared AuthorContext from newspack-blocks is not available.
27+ */
28+ const FallbackAuthorContext = createContext ( null ) ;
29+
30+ /**
31+ * Get the shared AuthorContext from newspack-blocks if available, otherwise use fallback.
32+ * This allows the avatar block to be used inside the Author Profile block's nested mode.
33+ */
34+ const SharedAuthorContext = typeof window !== 'undefined' && window . NewspackAuthorContext ? window . NewspackAuthorContext : FallbackAuthorContext ;
35+
2336const AvatarInspectorControls = ( { setAttributes, attributes } ) => (
2437 < InspectorControls >
2538 < PanelBody title = { __ ( 'Settings' , 'newspack-plugin' ) } >
@@ -120,14 +133,58 @@ const AvatarWrapper = ( { avatar, size, attributes, placeholder = false } ) => {
120133} ;
121134
122135const Edit = ( { attributes, context, setAttributes } ) => {
136+ const blockProps = useBlockProps ( ) ;
137+
138+ // Check for parent block context first (nested mode - single author).
139+ const authorFromBlockContext = context [ 'newspack-blocks/author' ] ;
140+ const authorFromReactContext = useContext ( SharedAuthorContext ) ;
141+ const authorFromParent = authorFromBlockContext || authorFromReactContext ;
142+
143+ // Hooks must be called unconditionally per React rules.
123144 const { postId, postType } = context ;
124145 const avatar = useUserAvatar ( { userId : attributes ?. userId , postId, postType } ) ;
125146 const allAuthors = usePostAuthors ( { postId, postType } ) ;
126147 const { bylineActive, bylineContent } = useCustomByline ( postId , postType ) ;
127- const blockProps = useBlockProps ( ) ;
128148
129- // Text-only custom byline (no [Author] shortcodes) — show placeholder .
149+ // Memoize author ID extraction to avoid running regex on every render .
130150 const authorIds = useMemo ( ( ) => extractAuthorIdsFromByline ( bylineContent ) , [ bylineContent ] ) ;
151+
152+ const renderAvatar = ( currentAvatar , key ) => (
153+ < AvatarWrapper key = { key } avatar = { currentAvatar } size = { attributes . size } attributes = { attributes } />
154+ ) ;
155+
156+ // Nested mode: render single author from parent context.
157+ if ( authorFromParent ) {
158+ let avatarUrl = '' ;
159+ if ( authorFromParent . avatar ) {
160+ if ( authorFromParent . avatar . includes ( '<img' ) ) {
161+ const match = authorFromParent . avatar . match ( / s r c = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / ) ;
162+ avatarUrl = match ?. [ 1 ] || '' ;
163+ } else {
164+ avatarUrl = authorFromParent . avatar ;
165+ }
166+ }
167+
168+ if ( ! avatarUrl ) {
169+ return null ;
170+ }
171+
172+ const parentAvatar = {
173+ src : avatarUrl ,
174+ alt : authorFromParent . name || '' ,
175+ minSize : 16 ,
176+ maxSize : 128 ,
177+ } ;
178+
179+ return (
180+ < >
181+ < AvatarInspectorControls attributes = { attributes } setAttributes = { setAttributes } />
182+ < div { ...blockProps } > { renderAvatar ( parentAvatar , 'nested-author' ) } </ div >
183+ </ >
184+ ) ;
185+ }
186+
187+ // Text-only custom byline (no [Author] shortcodes) — show placeholder.
131188 const isTextOnlyByline = bylineActive && ( ! bylineContent || authorIds . length === 0 ) ;
132189 if ( isTextOnlyByline ) {
133190 return (
@@ -138,28 +195,28 @@ const Edit = ( { attributes, context, setAttributes } ) => {
138195 ) ;
139196 }
140197
198+ // Standalone mode: get authors from post context.
141199 const authors = allAuthors ?. length ? allAuthors : null ;
142200
143201 // Wait until we have something to render
144202 if ( ! avatar ?. src && ! authors ?. length ) {
145203 return < div { ...blockProps } > { __ ( 'Loading avatar…' , 'newspack-plugin' ) } </ div > ;
146204 }
147205
148- const renderAvatar = ( currentAvatar , key ) => (
149- < AvatarWrapper key = { key } avatar = { currentAvatar } size = { attributes . size } attributes = { attributes } />
150- ) ;
151206 return (
152207 < >
153208 < AvatarInspectorControls attributes = { attributes } setAttributes = { setAttributes } />
154- { authors ?. length
155- ? authors . map ( ( author , index ) => {
156- const currentAvatar = {
157- src : author . avatarSrc ,
158- alt : author ?. name || author ?. display_name || '' ,
159- } ;
160- return renderAvatar ( currentAvatar , author . id || index ) ;
161- } )
162- : renderAvatar ( avatar , 'single-author' ) }
209+ < div { ...blockProps } >
210+ { authors ?. length
211+ ? authors . map ( ( author , index ) => {
212+ const currentAvatar = {
213+ src : author . avatarSrc ,
214+ alt : author ?. name || author ?. display_name || '' ,
215+ } ;
216+ return renderAvatar ( currentAvatar , author . id || index ) ;
217+ } )
218+ : renderAvatar ( avatar , 'single-author' ) }
219+ </ div >
163220 </ >
164221 ) ;
165222} ;
0 commit comments