diff --git a/common/src/envs/constants.ts b/common/src/envs/constants.ts index 28138eca..7b51defa 100644 --- a/common/src/envs/constants.ts +++ b/common/src/envs/constants.ts @@ -45,6 +45,7 @@ export const LOCAL_WEB_DOMAIN = `localhost:3000` export const LOCAL_BACKEND_DOMAIN = `${IS_LOCAL_ANDROID ? '10.0.2.2' : 'localhost'}:8088` export const DOMAIN = IS_LOCAL ? LOCAL_WEB_DOMAIN : ENV_CONFIG.domain +export const DEPLOYED_WEB_URL = `https://www.${ENV_CONFIG.domain}` export const WEB_URL = IS_LOCAL ? `http://${LOCAL_WEB_DOMAIN}` : `https://${DOMAIN}` export const BACKEND_DOMAIN = IS_LOCAL ? LOCAL_BACKEND_DOMAIN : ENV_CONFIG.backendDomain export const FIREBASE_CONFIG = ENV_CONFIG.firebaseConfig diff --git a/web/lib/util/page-data.ts b/web/lib/util/page-data.ts new file mode 100644 index 00000000..bfd8c963 --- /dev/null +++ b/web/lib/util/page-data.ts @@ -0,0 +1,20 @@ +import {DEPLOYED_WEB_URL} from "common/envs/constants"; + +export const getBuildId = async () => { + const res = await fetch(`${DEPLOYED_WEB_URL}/api/build-id`) + if (!res.ok) throw new Error('Failed to fetch build manifest') + const data = await res.json() + return data.buildId +} + +export const getPageData = async (route = '/') => { + const buildId = await getBuildId() + const cleanRoute = route.startsWith('/') ? route.slice(1) : route + const url = `${DEPLOYED_WEB_URL}/api/proxy-data?path=${buildId}/${cleanRoute || 'index'}.json` + console.log('Fetching data from:', url) + const res = await fetch(url, {cache: 'force-cache'}) as any + if (!res.ok) throw new Error(`Failed to fetch data for ${route}`) + const result = await res.json() as any + console.log('Fetched Page data:', result) + return result.pageProps +} \ No newline at end of file diff --git a/web/pages/[username]/index.tsx b/web/pages/[username]/index.tsx index f4c0c370..0e0d9521 100644 --- a/web/pages/[username]/index.tsx +++ b/web/pages/[username]/index.tsx @@ -16,11 +16,12 @@ import {ProfileInfo} from 'web/components/profile/profile-info' import {User} from 'common/user' import {getUserForStaticProps} from 'common/supabase/users' import {GetStaticPropsContext} from 'next' -import {CompassLoadingIndicator} from "web/components/widgets/loading-indicator"; +import {CompassLoadingIndicator} from "web/components/widgets/loading-indicator" import Custom404 from '../404' -import {sleep} from "common/util/time"; -import {isNativeMobile} from "web/lib/util/webview"; -import {getPixelHeight} from "web/lib/util/css"; +import {sleep} from "common/util/time" +import {isNativeMobile} from "web/lib/util/webview" +import {getPixelHeight} from "web/lib/util/css" +import {getPageData} from "web/lib/util/page-data"; async function getUser(username: string) { const user = await getUserForStaticProps(db, username) @@ -39,29 +40,29 @@ async function getProfile(userId: string) { } } console.debug(`Profile loaded after ${i} tries`) - return profile; + return profile } // getServerSideProps is a Next.js function that can be used to fetch data and render the contents of a page at request time. // export async function getServerSideProps(context: any) { // if (!isNativeMobile()) { // // Not mobile → let SSG handle it -// return {notFound: true}; +// return {notFound: true} // } // // // Mobile path: server-side fetch -// const username = context.params.username; -// const user = await getUser(username); +// const username = context.params.username +// const user = await getUser(username) // // if (!user) { -// return {props: {notFoundCustomText: 'User not found'}}; +// return {props: {notFoundCustomText: 'User not found'}} // } // -// const profile = await getProfile(user.id); +// const profile = await getProfile(user.id) // // console.log('getServerSideProps', {user, profile, username}) // -// return {props: {user, profile, username}}; +// return {props: {user, profile, username}} // } // SSG: static site generation @@ -154,49 +155,37 @@ export default function UserPage(props: UserPageProps) { useEffect(() => { console.log('safe-area-inset-bottom:', getPixelHeight('safe-area-inset-bottom')) console.log('safe-area-inset-top:', getPixelHeight('safe-area-inset-top')) - }, []); + }, []) const nativeMobile = isNativeMobile() const router = useRouter() const username = (nativeMobile ? router.query.username : props.username) as string - const [user, setUser] = useState(props.user) - const [profile, setProfile] = useState(props.profile) - const [notFoundCustomText, setNotFoundCustomText] = useState(props.notFoundCustomText) + const [fetchedProps, setFetchedProps] = useState(props) const [loading, setLoading] = useState(nativeMobile) - console.log('UserPage state:', {username, user, profile, notFoundCustomText, loading, nativeMobile}) + console.log('UserPage state:', {username, fetchedProps, loading, nativeMobile}) useEffect(() => { if (nativeMobile) { - // Mobile/WebView scenario: fetch profile dynamically + // Mobile/WebView scenario: fetch profile dynamically from the remote web server (to benefit from SSR and ISR) async function load() { setLoading(true) - const fetchedUser = await getUser(username) - if (!fetchedUser) return - if (fetchedUser.username !== username) { - console.debug('Found a case-insensitive match for native mobile') - await router.push(`/${fetchedUser.username}`) - } - setUser(fetchedUser) - console.debug('fetched user for native mobile:', fetchedUser) - const fetchedProfile = await getProfile(fetchedUser.id) - if (!fetchedProfile) { - setNotFoundCustomText(`${username} hasn't created a profile yet.`) - } else { - setProfile(fetchedProfile) - console.debug('fetched profile for native mobile:', fetchedProfile) + try { + const _props = await getPageData(username) + setFetchedProps(_props) + } catch (e) { + console.error('Failed to fetch profile for native mobile', e) + setFetchedProps({username, notFoundCustomText: 'Failed to fetch profile.'}) } setLoading(false) } load() } else { - setUser(props.user) - setProfile(props.profile) - setNotFoundCustomText(props.notFoundCustomText) + setFetchedProps(props) } // On web, initialProfile from SSR/ISR is already loaded - }, [username, nativeMobile]); + }, [username, nativeMobile]) if (loading) { return } - if (notFoundCustomText) { - return + if (fetchedProps.notFoundCustomText) { + return } - if (!user) { + if (!fetchedProps.user) { return } - if (user.isBannedFromPosting) { + if (fetchedProps.user?.isBannedFromPosting) { return } - if (!profile) { + if (!fetchedProps.profile) { return } - return + return } function UserPageInner(props: ActiveUserPageProps) {