1- import { useState } from 'react'
1+ import { useEffect , useState } from 'react'
22import { useRouter } from 'next/router'
33import Head from 'next/head'
44import { PageBase } from 'web/components/page-base'
@@ -15,19 +15,64 @@ import {db} from 'web/lib/supabase/db'
1515import { ProfileInfo } from 'web/components/profile/profile-info'
1616import { User } from 'common/user'
1717import { getUserForStaticProps } from 'common/supabase/users'
18- import { type GetStaticProps } from 'next'
18+ import { GetStaticPropsContext } from 'next'
1919import { CompassLoadingIndicator } from "web/components/widgets/loading-indicator" ;
2020import Custom404 from '../404'
2121import { sleep } from "common/util/time" ;
22+ import { isNativeMobile } from "web/lib/util/webview" ;
2223
23- export const getStaticProps : GetStaticProps <
24- UserPageProps ,
25- { username : string }
26- > = async ( props ) => {
27- // console.log('Starting getStaticProps in /[username]')
24+ async function getUser ( username : string ) {
25+ const user = await getUserForStaticProps ( db , username )
26+ return user
27+ }
28+
29+ async function getProfile ( userId : string ) {
30+ let profile
31+ let i = 0
32+ while ( ! profile ) {
33+ profile = await getProfileRow ( userId , db )
34+ if ( i > 0 ) await sleep ( 500 )
35+ i ++
36+ if ( i >= 8 ) {
37+ break
38+ }
39+ }
40+ console . debug ( `Profile loaded after ${ i } tries` )
41+ return profile ;
42+ }
43+
44+ // getServerSideProps is a Next.js function that can be used to fetch data and render the contents of a page at request time.
45+ // export async function getServerSideProps(context: any) {
46+ // if (!isNativeMobile()) {
47+ // // Not mobile → let SSG handle it
48+ // return {notFound: true};
49+ // }
50+ //
51+ // // Mobile path: server-side fetch
52+ // const username = context.params.username;
53+ // const user = await getUser(username);
54+ //
55+ // if (!user) {
56+ // return {props: {notFoundCustomText: 'User not found'}};
57+ // }
58+ //
59+ // const profile = await getProfile(user.id);
60+ //
61+ // console.log('getServerSideProps', {user, profile, username})
62+ //
63+ // return {props: {user, profile, username}};
64+ // }
65+
66+ // SSG: static site generation
67+ // Next.js will pre-render this page at build time using the props returned by getStaticProps
68+ export const getStaticProps = async ( props : GetStaticPropsContext < {
69+ username : string
70+ } > ) => {
2871 const { username} = props . params !
2972
30- const user = await getUserForStaticProps ( db , username )
73+ console . log ( 'Starting getStaticProps in /[username]' )
74+
75+ const user = await getUser ( username )
3176
3277 console . debug ( 'getStaticProps' , { user} )
3378
@@ -57,24 +102,13 @@ export const getStaticProps: GetStaticProps<
57102 console . debug ( 'User deleted' )
58103 return {
59104 props : {
60- user : false ,
61105 username,
62106 } ,
63107 revalidate : 15 ,
64108 }
65109 }
66110
67- let profile
68- let i = 0
69- while ( ! profile ) {
70- profile = await getProfileRow ( user . id , db )
71- if ( i > 0 ) await sleep ( 500 )
72- i ++
73- if ( i >= 8 ) {
74- break
75- }
76- }
77- console . debug ( `Profile loaded after ${ i } tries` )
111+ const profile = await getProfile ( user . id )
78112
79113 if ( ! profile ) {
80114 console . debug ( 'No profile' , `${ user . username } hasn't created a profile yet.` )
@@ -96,32 +130,77 @@ export const getStaticProps: GetStaticProps<
96130 }
97131}
98132
133+ // Runs at build time for SSG (native mobile), or at runtime for ISR fallback in web
99134export const getStaticPaths = ( ) => {
135+ console . log ( 'Starting getStaticPaths in /[username]' , isNativeMobile ( ) )
100136 return { paths : [ ] , fallback : 'blocking' }
101137}
102138
103- type UserPageProps = DeletedUserPageProps | ActiveUserPageProps | NotFoundPageProps
104-
105- type DeletedUserPageProps = {
106- user : false
107- username : string
139+ type UserPageProps = Partial < ActiveUserPageProps > & {
140+ username : string // to override Partial’s optional
141+ notFoundCustomText ?: string
108142}
143+
109144type ActiveUserPageProps = {
110- user : User
111145 username : string
146+ user : User
112147 profile : ProfileRow
113148}
114- type NotFoundPageProps = {
115- notFoundCustomText : string
116- }
117149
118150export default function UserPage ( props : UserPageProps ) {
119- // console.debug('Starting UserPage in /[username]')
120- if ( 'notFoundCustomText' in props ) {
121- return < Custom404 customText = { props . notFoundCustomText } />
151+ // console.log('Starting UserPage in /[username]')
152+
153+ const nativeMobile = isNativeMobile ( )
154+ const router = useRouter ( )
155+ const username = ( nativeMobile ? router . query . username : props . username ) as string
156+ const [ user , setUser ] = useState < User | undefined > ( props . user )
157+ const [ profile , setProfile ] = useState < ProfileRow | undefined > ( props . profile )
158+ const [ notFoundCustomText , setNotFoundCustomText ] = useState ( props . notFoundCustomText )
159+ const [ loading , setLoading ] = useState ( nativeMobile )
160+
161+ console . log ( 'UserPage state:' , { username, user, profile, notFoundCustomText, loading, nativeMobile} )
162+
163+ useEffect ( ( ) => {
164+ if ( nativeMobile ) {
165+ // Mobile/WebView scenario: fetch profile dynamically
166+ async function load ( ) {
167+ setLoading ( true )
168+ const fetchedUser = await getUser ( username )
169+ if ( ! fetchedUser ) return
170+ if ( fetchedUser . username !== username ) {
171+ console . debug ( 'Found a case-insensitive match for native mobile' )
172+ await router . push ( `/${ fetchedUser . username } ` )
173+ }
174+ setUser ( fetchedUser )
175+ console . debug ( 'fetched user for native mobile:' , fetchedUser )
176+ const fetchedProfile = await getProfile ( fetchedUser . id )
177+ if ( ! fetchedProfile ) {
178+ setNotFoundCustomText ( `${ username } hasn't created a profile yet.` )
179+ } else {
180+ setProfile ( fetchedProfile )
181+ console . debug ( 'fetched profile for native mobile:' , fetchedProfile )
182+ }
183+ setLoading ( false )
184+ }
185+
186+ load ( )
187+ }
188+ // On web, initialProfile from SSR/ISR is already loaded
189+ } , [ username , nativeMobile ] ) ;
190+
191+ if ( loading ) {
192+ return < PageBase
193+ trackPageView = { 'user page' }
194+ >
195+ < CompassLoadingIndicator />
196+ </ PageBase >
122197 }
123198
124- if ( ! props . user ) {
199+ if ( notFoundCustomText ) {
200+ return < Custom404 customText = { notFoundCustomText } />
201+ }
202+
203+ if ( ! user ) {
125204 return < PageBase
126205 trackPageView = { 'user page' }
127206 className = { 'relative p-2 sm:pt-0' }
@@ -134,7 +213,7 @@ export default function UserPage(props: UserPageProps) {
134213 </ PageBase >
135214 }
136215
137- if ( props . user . isBannedFromPosting ) {
216+ if ( user . isBannedFromPosting ) {
138217 return < PageBase
139218 trackPageView = { 'user page' }
140219 className = { 'relative p-2 sm:pt-0' }
@@ -147,7 +226,24 @@ export default function UserPage(props: UserPageProps) {
147226 </ PageBase >
148227 }
149228
150- return < UserPageInner { ...props } />
229+ if ( ! profile ) {
230+ return < PageBase
231+ trackPageView = { 'user page' }
232+ className = { 'relative p-2 sm:pt-0' }
233+ >
234+ < Col className = "items-center justify-center h-full" >
235+ < div className = "text-xl font-semibold text-center mt-8" >
236+ This user hasn't created a profile yet.
237+ </ div >
238+ </ Col >
239+ </ PageBase >
240+ }
241+
242+ return < UserPageInner
243+ user = { user }
244+ username = { username }
245+ profile = { profile }
246+ />
151247}
152248
153249function UserPageInner ( props : ActiveUserPageProps ) {
0 commit comments