Skip to content

Commit 61dd331

Browse files
committed
Use SSR/ISR only for web app (load client side on mobile app)
1 parent 817640c commit 61dd331

File tree

1 file changed

+131
-35
lines changed

1 file changed

+131
-35
lines changed

web/pages/[username]/index.tsx

Lines changed: 131 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useState} from 'react'
1+
import {useEffect, useState} from 'react'
22
import {useRouter} from 'next/router'
33
import Head from 'next/head'
44
import {PageBase} from 'web/components/page-base'
@@ -15,19 +15,64 @@ import {db} from 'web/lib/supabase/db'
1515
import {ProfileInfo} from 'web/components/profile/profile-info'
1616
import {User} from 'common/user'
1717
import {getUserForStaticProps} from 'common/supabase/users'
18-
import {type GetStaticProps} from 'next'
18+
import {GetStaticPropsContext} from 'next'
1919
import {CompassLoadingIndicator} from "web/components/widgets/loading-indicator";
2020
import Custom404 from '../404'
2121
import {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
99134
export 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+
109144
type ActiveUserPageProps = {
110-
user: User
111145
username: string
146+
user: User
112147
profile: ProfileRow
113148
}
114-
type NotFoundPageProps = {
115-
notFoundCustomText: string
116-
}
117149

118150
export 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

153249
function UserPageInner(props: ActiveUserPageProps) {

0 commit comments

Comments
 (0)