@@ -25,7 +25,7 @@ interface Props extends ScrollViewProps {
2525const SCROLL_HALT_THRESHOLD = 100
2626
2727export const RealtimeTimeline = ( props : Props ) => {
28- const { client } = useClient ( )
28+ const { client, isDomainOffline } = useClient ( )
2929
3030 const loadingRef = useRef ( true )
3131 const [ loading , setLoading ] = useState ( true )
@@ -48,6 +48,33 @@ export const RealtimeTimeline = (props: Props) => {
4848 newArrivalsRef . current = newArrivals
4949 } , [ newArrivals ] )
5050
51+ // 自ドメインがオフラインでも、単一タイムラインならそのホストから直接読める。
52+ // reader再生成のトリガーは解決済みのhostOverride値の変化のみにする
53+ // (isDomainOffline自体を依存にすると、一時的なオフライン遷移のたびに表示中のリーダーが破棄されてしまう)
54+ const [ hostOverride , setHostOverride ] = useState < string | undefined > ( undefined )
55+ useEffect ( ( ) => {
56+ if ( ! client || ! isDomainOffline || props . timelines . length !== 1 ) {
57+ setHostOverride ( undefined )
58+ return
59+ }
60+ let isCancelled = false
61+ const owner = URL . parse ( props . timelines [ 0 ] ) ?. host
62+ if ( ! owner ) {
63+ setHostOverride ( undefined )
64+ return
65+ }
66+ client . api
67+ . resolveDomain ( owner )
68+ . catch ( ( ) => undefined )
69+ . then ( ( fqdn ) => {
70+ if ( isCancelled ) return
71+ setHostOverride ( fqdn === client . api . defaultHost ? undefined : fqdn )
72+ } )
73+ return ( ) => {
74+ isCancelled = true
75+ }
76+ } , [ client , isDomainOffline , props . timelines ] )
77+
5178 useEffect ( ( ) => {
5279 console . log ( 'Initializing timeline reader for timelines:' , props . timelines )
5380 let isCancelled = false
@@ -56,53 +83,62 @@ export const RealtimeTimeline = (props: Props) => {
5683 const request = async ( ) => {
5784 if ( ! client ) return
5885
59- return client . newTimelineReader ( ) . then ( ( t ) => {
60- if ( isCancelled ) return
61- t . haltUpdate = false
62- t . onUpdate = ( ) => {
63- startTransition ( ( ) => {
64- update ( )
65- } )
66- }
67-
68- t . onNewItem = ( item ) => {
86+ return client
87+ . newTimelineReader ( { withoutSocket : false , hostOverride } )
88+ . catch ( ( ) => client . newTimelineReader ( { withoutSocket : true , hostOverride } ) )
89+ . then ( ( t ) => {
6990 if ( isCancelled ) return
70- if ( ! t . haltUpdate ) return
71- if ( ! item . href ) return
91+ t . haltUpdate = false
92+ t . onUpdate = ( ) => {
93+ startTransition ( ( ) => {
94+ update ( )
95+ } )
96+ }
7297
73- client . getMessage ( item . href ) . then ( ( msg ) => {
98+ t . onNewItem = ( item ) => {
7499 if ( isCancelled ) return
75- if ( ! msg ) return
76- const icon = msg . authorProfile ?. avatar
77- if ( ! icon ) return
78- setNewArrivals ( ( prev ) => {
79- if ( prev . find ( ( e ) => e . src === icon ) ) return prev
80- return [ { id : item . href ! , author : msg . author , src : icon } , ...prev ]
100+ if ( ! t . haltUpdate ) return
101+ if ( ! item . href ) return
102+
103+ client . getMessage ( item . href ) . then ( ( msg ) => {
104+ if ( isCancelled ) return
105+ if ( ! msg ) return
106+ const icon = msg . authorProfile ?. avatar
107+ if ( ! icon ) return
108+ setNewArrivals ( ( prev ) => {
109+ if ( prev . find ( ( e ) => e . src === icon ) ) return prev
110+ return [ { id : item . href ! , author : msg . author , src : icon } , ...prev ]
111+ } )
81112 } )
82- } )
83- }
113+ }
84114
85- reader . current = t
86- t . listen ( props . timelines )
87- . then ( ( hasMoreData ) => {
88- setHasMoreData ( hasMoreData )
89- } )
90- . finally ( ( ) => {
91- loadingRef . current = false
92- setLoading ( false )
93- setInitialLoaded ( true )
94- } )
95- return t
96- } )
115+ reader . current = t
116+ t . listen ( props . timelines )
117+ . then ( ( hasMoreData ) => {
118+ setHasMoreData ( hasMoreData )
119+ } )
120+ . finally ( ( ) => {
121+ loadingRef . current = false
122+ setLoading ( false )
123+ setInitialLoaded ( true )
124+ } )
125+ return t
126+ } )
97127 }
98- const mt = request ( )
128+ const mt = request ( ) . catch ( ( err ) => {
129+ console . error ( 'Failed to initialize timeline reader:' , err )
130+ loadingRef . current = false
131+ setLoading ( false )
132+ setInitialLoaded ( true )
133+ return undefined
134+ } )
99135 return ( ) => {
100136 isCancelled = true
101137 mt . then ( ( t ) => {
102138 t ?. dispose ( )
103139 } )
104140 }
105- } , [ client , reader , props . timelines , update ] )
141+ } , [ client , reader , props . timelines , update , hostOverride ] )
106142
107143 const scrollRef = useRef < HTMLDivElement > ( null )
108144
0 commit comments