Skip to content

Feat/membership frontend #379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 64 additions & 66 deletions context/AppContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
"use client";

import 'react-toastify/dist/ReactToastify.css';

import { useTranslations } from 'next-intl';
import {
createContext,
useState,
SetStateAction,
ReactNode,
useEffect
useEffect,
} from 'react';

import { Pi } from '@pinetwork-js/sdk';
import axiosClient, {setAuthToken} from '@/config/client';
import axiosClient from '@/config/client';
import { onIncompletePaymentFound } from '@/utils/auth';
import { AuthResult } from '@/constants/pi';
import { IUser } from '@/constants/types';

import logger from '../logger.config.mjs';

interface IAppContextProps {
currentUser: IUser | null;
setCurrentUser: React.Dispatch<SetStateAction<IUser | null>>;
registerUser: () => void;
autoLoginUser: ()=> void;
autoLoginUser: () => void;
isSigningInUser: boolean;
reload: boolean;
alertMessage: string | null;
Expand All @@ -37,16 +35,16 @@ interface IAppContextProps {
const initialState: IAppContextProps = {
currentUser: null,
setCurrentUser: () => {},
registerUser: () => { },
autoLoginUser: ()=> {},
registerUser: () => {},
autoLoginUser: () => {},
isSigningInUser: false,
reload: false,
alertMessage: null,
setAlertMessage: () => {},
showAlert: () => {},
setReload: () => {},
isSaveLoading: false,
setIsSaveLoading: () => {}
setIsSaveLoading: () => {},
};

export const AppContext = createContext<IAppContextProps>(initialState);
Expand All @@ -61,90 +59,90 @@ const AppContextProvider = ({ children }: AppContextProviderProps) => {
const [isSigningInUser, setIsSigningInUser] = useState(false);
const [reload, setReload] = useState(false);
const [isSaveLoading, setIsSaveLoading] = useState(false);

const [alertMessage, setAlertMessage] = useState<string | null>(null);

const showAlert = (message: string) => {
setAlertMessage(message);
setTimeout(() => {
setAlertMessage(null); // Clear alert after 5 seconds
}, 5000);
setTimeout(() => setAlertMessage(null), 5000);
};

const registerUser = async () => {
logger.info('Initializing Pi SDK for user registration.');
logger.info('Initializing Pi SDK');

await Pi.init({ version: '2.0', sandbox: process.env.NODE_ENV === 'development' });
let isInitiated = Pi.initialized;

if (isInitiated) {
try {
setIsSigningInUser(true);
const pioneerAuth: AuthResult = await window.Pi.authenticate(['username', 'payments'], onIncompletePaymentFound);
const res = await axiosClient.post(
"/users/authenticate",
{}, // empty body
{
headers: {
Authorization: `Bearer ${pioneerAuth.accessToken}`,
},
}
);

if (res.status === 200) {
setAuthToken(res.data?.token);
setCurrentUser(res.data.user);
logger.info('User authenticated successfully.');
setTimeout(() => {
setIsSigningInUser(false); // hide the splash screen after the delay
}, 2500);
} else if (res.status === 500) {
setCurrentUser(null);
logger.error('User authentication failed.');
setIsSigningInUser(false);
}
} catch (error) {
logger.error('Error during user registration:', error);
setIsSigningInUser(false);
const isInitiated = Pi.initialized;

if (!isInitiated) {
logger.error("Pi SDK failed to initialize.");
return;
}

try {
setIsSigningInUser(true);

const pioneerAuth: AuthResult = await window.Pi.authenticate(
['username', 'payments'],
onIncompletePaymentFound
);

const res = await axiosClient.post(
"/users/authenticate",
{},
{
headers: {
Authorization: `Bearer ${pioneerAuth.accessToken}`,
},
}
);

if (res.status === 200) {
setCurrentUser(res.data.user);
logger.info("User authenticated.");
} else {
setCurrentUser(null);
logger.error("User authentication failed.");
}
} else {
logger.error('PI SDK failed to initialize.');

} catch (err) {
logger.error("registerUser failed:", err);
} finally {
setIsSigningInUser(false);
}
};

const autoLoginUser = async () => {
logger.info('Attempting to auto-login user.');
try {
setIsSigningInUser(true);
const res = await axiosClient.get('/users/me');
const res = await axiosClient.get("/users/me", { withCredentials: true });

if (res.status === 200) {
logger.info('Auto-login successful.');
setCurrentUser(res.data);
setTimeout(() => {
setIsSigningInUser(false); // hide the splash screen after the delay
}, 2500);
} else {
setCurrentUser(null);
logger.warn('Auto-login failed.');
setIsSigningInUser(false);
}
} catch (error) {
logger.error('Auto login unresolved; attempting Pi SDK authentication:', error);
await registerUser();
}
}
};

useEffect(() => {
logger.info('AppContextProvider mounted.');
if (!currentUser) {
registerUser();
} else {
autoLoginUser();
}
autoLoginUser();
}, []);

return (
<AppContext.Provider value={{ currentUser, setCurrentUser, registerUser, autoLoginUser, isSigningInUser, reload, setReload, showAlert, alertMessage, setAlertMessage, isSaveLoading, setIsSaveLoading }}>
<AppContext.Provider
value={{
currentUser,
setCurrentUser,
registerUser,
autoLoginUser,
isSigningInUser,
reload,
setReload,
showAlert,
alertMessage,
setAlertMessage,
isSaveLoading,
setIsSaveLoading,
}}
>
{children}
</AppContext.Provider>
);
Expand Down
99 changes: 12 additions & 87 deletions src/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export const dynamic = 'force-dynamic';
const lato = Lato({ weight: '400', subsets: ['latin'], display: 'swap' });

export async function generateStaticParams() {
return locales.map((locale) => { locale })
};
return locales.map((loc:any) => ({ locale: loc }));
}

export default function LocaleLayout({
children,
Expand All @@ -21,12 +21,10 @@ export default function LocaleLayout({
children: React.ReactNode;
params: { locale: string };
}) {
// Enable static rendering
// Enable static rendering
setRequestLocale(locale);

// Receive messages provided in `i18n.ts`
const messages = useMessages();

// log the locale and messages loading
logger.info(`Rendering LocaleLayout for locale: ${locale}`);
if (messages) {
Expand All @@ -36,86 +34,13 @@ export default function LocaleLayout({
}

return (
<html lang={locale} suppressHydrationWarning={true}>
<head>
<meta charSet="utf-8" />
<title>Map of Pi</title>
<base href="/" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<meta
property="og:title"
content="Map of Pi, Searchable places accepting Pi on a map"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://mapofpi.concretecode.ch" />
<meta
property="og:image"
content="https://mapofpi.concretecode.ch/assets/images/logo.svg"
/>
<meta
name="description"
content="Map of Pi is a mobile application developed to help Pi community members easily locate local businesses that accept Pi as payment"
/>
<meta name="keywords" content="map, pi, business, app" />
<meta name="author" content="Map of Pi Team" />
<meta
httpEquiv="Content-Security-Policy"
content="font-src 'self' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/https://fonts.gstatic.com/;"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.css"
/>
<link
rel="stylesheet"
href="https://unpkg.com/[email protected]/dist/leaflet.css"
/>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />

{/* Google tag (gtag.js) */}
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SVNC88Q13K"></script>
<script dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-SVNC88Q13K');
`,
}} />
</head>
<body
className={`bg-background text-black ${lato.className}`}>
<NextIntlClientProvider locale={locale} messages={messages}>
<Providers>
<Navbar />
<div className='pt-[80px]'>{children}</div>
</Providers>
</NextIntlClientProvider>
</body>
</html>
<NextIntlClientProvider locale={locale} messages={messages}>
<Providers>
<Navbar />
<div className={`pt-[80px] bg-background text-black ${lato.className}`}>
{children}
</div>
</Providers>
</NextIntlClientProvider>
);
}
}
12 changes: 12 additions & 0 deletions src/app/[locale]/membership/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use client"

import React, {useContext} from "react";
import MembershipScreen from "../../../components/shared/Membership/membershipScreen";
import { AppContext } from "../../../../context/AppContextProvider";

const MembershipPage: React.FC = () => {
const context = useContext(AppContext);
return <MembershipScreen />;
}

export default MembershipPage;
Loading