Skip to content
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

feat: Implement Email/SSO UI #206

Merged
merged 40 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
cdf08f3
wip
elcharitas Jan 16, 2024
eb93d72
add store updates
elcharitas Jan 16, 2024
31cd85b
wip
elcharitas Jan 16, 2024
57a997a
wip
elcharitas Jan 17, 2024
e5a96b2
move containers
elcharitas Jan 17, 2024
adb37ac
improve dialog
elcharitas Jan 17, 2024
515c599
show container
elcharitas Jan 17, 2024
a9f9f80
further styling
elcharitas Jan 18, 2024
306dc49
socials icons
elcharitas Jan 18, 2024
4d258f8
add slice to retrieve state
elcharitas Jan 18, 2024
a438475
create migration
elcharitas Jan 18, 2024
47b8c8d
bump storage
elcharitas Jan 18, 2024
2cee6b1
more work on email
elcharitas Jan 18, 2024
7420560
method selection
elcharitas Jan 18, 2024
3b08587
add backdrop for modals
elcharitas Jan 18, 2024
c7b6be7
fix error modal
elcharitas Jan 18, 2024
db5c947
Merge branch 'dev' into ALPHA-4462
elcharitas Jan 19, 2024
a8f42e9
minor cleanup
elcharitas Jan 19, 2024
1004900
use hook
elcharitas Jan 19, 2024
8746634
create pager for page titles
elcharitas Jan 19, 2024
401847f
paged layouts
elcharitas Jan 19, 2024
63e78a0
mobile auth
elcharitas Jan 19, 2024
d139098
mobile auth wip
elcharitas Jan 19, 2024
4d16f28
align
elcharitas Jan 22, 2024
f2da14c
Merge branch 'dev' into ALPHA-4462
elcharitas Jan 23, 2024
40a7bb2
Merge branch 'dev' into ALPHA-4462
elcharitas Jan 24, 2024
ceabd98
final work
elcharitas Jan 25, 2024
2ad3ead
Merge branch 'dev' into ALPHA-4462
elcharitas Jan 26, 2024
f8a10b0
cleanup and style improvement
elcharitas Jan 26, 2024
8d09081
fixes
elcharitas Jan 26, 2024
4a75faa
wip
elcharitas Jan 30, 2024
92df511
wip
elcharitas Jan 30, 2024
836b692
login mut
elcharitas Jan 30, 2024
f816107
finish up google auth
elcharitas Jan 31, 2024
133b073
Merge branch 'dev' into ALPHA-4462
elcharitas Jan 31, 2024
1b50c4c
final cleanups
elcharitas Feb 1, 2024
3e61f3c
final migration fix
elcharitas Feb 1, 2024
82ff2a7
add env vars
elcharitas Feb 1, 2024
2a95622
PR Feedbacks
elcharitas Feb 2, 2024
941f9f8
Merge branch 'dev' into ALPHA-4462
elcharitas Feb 5, 2024
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
1 change: 1 addition & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ jobs:
VITE_FIRE_APP_ID: ${{ secrets.DEV__REACT_APP_FIRE_APP_ID }}
VITE_SENTRY_DSN: ${{ secrets.PROD__VITE_SENTRY_DSN }}
VITE_CLARITY_PROJECT_ID: ${{ secrets.DEV__REACT_APP_CLARITY_PROJECT_ID }}
VITE_OAUTH_ID_GOOGLE: ${{ secrets.DEV__REACT_APP_OAUTH_ID_GOOGLE }}

NODE_OPTIONS: "--max_old_space_size=4096"

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy-epsilon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
VITE_FIRE_APP_ID: ${{ secrets.DEV__REACT_APP_FIRE_APP_ID }}
VITE_SENTRY_DSN: ${{ secrets.PROD__VITE_SENTRY_DSN }}
VITE_CLARITY_PROJECT_ID: ${{ secrets.DEV__REACT_APP_CLARITY_PROJECT_ID }}
VITE_OAUTH_ID_GOOGLE: ${{ secrets.DEV__REACT_APP_OAUTH_ID_GOOGLE }}

NODE_OPTIONS: "--max_old_space_size=4096"

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ jobs:
VITE_FIRE_APP_ID: ${{ secrets.PROD__REACT_APP_FIRE_APP_ID }}
VITE_SENTRY_DSN: ${{ secrets.PROD__VITE_SENTRY_DSN }}
VITE_CLARITY_PROJECT_ID: ${{ secrets.PROD__VITE_CLARITY_PROJECT_ID }}
VITE_OAUTH_ID_GOOGLE: ${{ secrets.PROD__REACT_APP_OAUTH_ID_GOOGLE }}

NODE_OPTIONS: "--max_old_space_size=4096"

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
VITE_FIRE_APP_ID: ${{ secrets.STAGING__REACT_APP_FIRE_APP_ID }}
VITE_SENTRY_DSN: ${{ secrets.PROD__VITE_SENTRY_DSN }}
VITE_CLARITY_PROJECT_ID: ${{ secrets.STAGING__VITE_CLARITY_PROJECT_ID }}
VITE_OAUTH_ID_GOOGLE: ${{ secrets.STAGING__REACT_APP_OAUTH_ID_GOOGLE }}

NODE_OPTIONS: "--max_old_space_size=4096"

Expand Down
1 change: 1 addition & 0 deletions packages/frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ dist/

# testing
/coverage
*.keystore

# production
/dist
Expand Down
6 changes: 3 additions & 3 deletions packages/frontend/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ android {
applicationId "com.alphaday"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 0
versionName "0.0.1"
versionCode 1
versionName "1.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
Expand All @@ -25,7 +25,7 @@ android {
}

repositories {
flatDir{
flatDir {
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@capacitor/status-bar": "5.0.6",
"@ionic/react": "7.6.1",
"@ionic/react-router": "7.1.2",
"@react-oauth/google": "0.12.1",
"@reduxjs/toolkit": "1.8.0",
"@sentry/react": "7.8.1",
"@sentry/tracing": "7.8.1",
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/src/MobileApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import PreloaderPage from "./pages/preloader";
import "@alphaday/ui-kit/global.scss";

const SuperfeedPage = lazyRetry(() => import("./mobile-pages/superfeed"));
const AuthPage = lazyRetry(() => import("./mobile-pages/auth"));

const App: React.FC = () => {
useAppInit();
Expand All @@ -16,6 +17,7 @@ const App: React.FC = () => {
<Switch>
<Suspense fallback={<PreloaderPage />}>
<Route path="/" exact component={SuperfeedPage} />
<Route path="/auth*" exact component={AuthPage} />
{/* Add more routes as needed */}
</Suspense>
</Switch>
Expand Down
1 change: 1 addition & 0 deletions packages/frontend/src/api/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ export * from "./useKeyPress";
export * from "./useFeatureFlags";
export * from "./useWalletViewStateUpdater";
export * from "./useWidgetHeight";
export * from "./useAuth";
123 changes: 123 additions & 0 deletions packages/frontend/src/api/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { useCallback } from "react";
import { useGoogleLogin } from "@react-oauth/google";
import { useHistory } from "react-router";
import {
useRequestCodeMutation,
useVerifyTokenMutation,
useSsoLoginMutation,
} from "../services";
import { useAppDispatch, useAppSelector } from "../store/hooks";
import * as userStore from "../store/slices/user";
import { EAuthMethod, EAuthState, TUserAccess } from "../types";
import { Logger } from "../utils/logging";

interface IUseAuth {
isAuthenticated: boolean;
authState: TUserAccess;
openAuthModal: () => void;
resetAuthState: () => void;
requestCode: (email: string) => Promise<void>;
verifyToken: (email: string, code: string) => Promise<void>;
ssoLogin: (provider: EAuthMethod) => void;
}

/**
* useAuth
*
* @returns
*/
export const useAuth = (): IUseAuth => {
const dispatch = useAppDispatch();
const authState = useAppSelector(userStore.selectAuthState);
const isAuthenticated = useAppSelector(userStore.selectIsAuthenticated);
const navigate = useHistory();

const [requestCodeMut] = useRequestCodeMutation();
const [verifyTokenMut] = useVerifyTokenMutation();
const [ssoLoginMut] = useSsoLoginMutation();

const openAuthModal = useCallback(() => {
dispatch(userStore.initAuthMethodSelection());
}, [dispatch]);

const requestCode = useCallback(
async (email: string) => {
try {
await requestCodeMut({ email }).unwrap();
dispatch(userStore.setAuthState(EAuthState.VerifyingEmail));
} catch (e) {
Logger.error(
"useAuth::requestCode: error sending OTP to email",
e
);
}
},
[dispatch, requestCodeMut]
);

const verifyToken = useCallback(
async (email: string, code: string) => {
try {
const verifyResp = await verifyTokenMut({
email,
code,
}).unwrap();

dispatch(
userStore.setAuthToken({ value: verifyResp.accessToken })
);
} catch (e) {
Logger.error("useAuth::verifyToken: error verifying OTP", e);
}
},
[dispatch, verifyTokenMut]
);

const googleSSOCallback = useCallback(
({ access_token }: Record<string, string>) => {
ssoLoginMut({
accessToken: access_token,
provider: EAuthMethod.Google,
})
.unwrap()
.then((r) => {
Logger.debug("useAuth::googleSSOLogin: success", r);
dispatch(userStore.setAuthToken({ value: r.accessToken }));
dispatch(userStore.setAuthState(EAuthState.Verified));
Logger.debug("useAuth::googleSSOLogin: redirecting");
navigate.push("/");
})
.catch((e) =>
Logger.error("useAuth::googleSSOLogin: error", e)
);
},
[ssoLoginMut, dispatch, navigate]
);

const googleSSOLogin = useGoogleLogin({
onSuccess: googleSSOCallback,
redirect_uri: `${window.location.origin}/auth/google_callback/`,
});
const ssoLogin = useCallback(
(provider: EAuthMethod) => {
if (provider === EAuthMethod.Google) {
googleSSOLogin();
}
},
[googleSSOLogin]
);

const resetAuthState = useCallback(() => {
dispatch(userStore.resetAuthState());
}, [dispatch]);

return {
isAuthenticated,
authState,
openAuthModal,
resetAuthState,
requestCode,
verifyToken,
ssoLogin,
};
};
91 changes: 91 additions & 0 deletions packages/frontend/src/api/services/auth/authEndpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { EAuthMethod } from "src/api/types";
import { Logger } from "src/api/utils/logging";
import CONFIG from "src/config";
import { alphadayApi } from "../alphadayApi";
import {
TVerificationCodeRequest,
TVerificationCodeResponse,
TVerifyEmailRequest,
TVerifyEmailRawResponse,
TVerifyEmailResponse,
TSSOLoginRequest,
TSSOLoginRawResponse,
TSSOLoginResponse,
} from "./types";

const { AUTH } = CONFIG.API.DEFAULT.ROUTES;

export const authApi = alphadayApi.injectEndpoints({
endpoints: (builder) => ({
requestCode: builder.mutation<
TVerificationCodeResponse,
TVerificationCodeRequest
>({
query: (req) => {
const path = `${AUTH.BASE}${AUTH.VERIFY_EMAIL}`;
Logger.debug("requestCode: body", JSON.stringify(req));
Logger.debug("requestCode: querying", path);
return {
url: path,
body: req,
method: "POST",
};
},
}),
verifyToken: builder.mutation<
TVerifyEmailResponse,
TVerifyEmailRequest
>({
query: (req) => {
const path = `${AUTH.BASE}${AUTH.VERIFY_TOKEN}`;
Logger.debug("verifyToken: body", JSON.stringify(req));
Logger.debug("verifyToken: querying", path);
return {
url: path,
body: req,
method: "POST",
};
},
transformResponse: (rawResult: TVerifyEmailRawResponse) => {
return {
accessToken: rawResult.access_token,
refreshToken: rawResult.refresh_token,
};
},
}),
ssoLogin: builder.mutation<TSSOLoginResponse, TSSOLoginRequest>({
query: (req) => {
const path = `${AUTH.BASE}${AUTH.CONVERT_TOKEN}`;
Logger.debug("ssoLogin: body", JSON.stringify(req));
Logger.debug("ssoLogin: querying", path);
return {
url: path,
body: {
grant_type: "convert_token",
backend:
req.provider === EAuthMethod.Google
? "google-oauth2"
: "apple",
token: req.accessToken,
client_id: CONFIG.APP.X_APP_ID,
client_secret: CONFIG.APP.X_APP_SECRET,
},
method: "POST",
};
},
transformResponse: (rawResult: TSSOLoginRawResponse) => {
return {
accessToken: rawResult.access_token,
refreshToken: rawResult.refresh_token,
};
},
}),
}),
overrideExisting: false,
});

export const {
useRequestCodeMutation,
useVerifyTokenMutation,
useSsoLoginMutation,
} = authApi;
39 changes: 39 additions & 0 deletions packages/frontend/src/api/services/auth/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { EAuthMethod } from "src/api/types";

export type TVerificationCodeRequest = {
email: string;
};

export type TVerificationCodeResponse = void;

export type TVerifyEmailRequest = {
email: string;
code: string;
};

export type TVerifyEmailResponse = {
accessToken: string;
refreshToken: string;
};

export type TVerifyEmailRawResponse = {
access_token: string;
refresh_token: string;
};

export type TSSOLoginRequest = {
accessToken: string;
provider: EAuthMethod;
idToken?: string;
code?: string;
};

export type TSSOLoginRawResponse = {
access_token: string;
refresh_token: string;
};

export type TSSOLoginResponse = {
accessToken: string;
refreshToken: string;
};
3 changes: 3 additions & 0 deletions packages/frontend/src/api/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ export * from "./lens-feed/types";
export * from "./features/featuresEndpoints";
export * from "./features/types";

export * from "./auth/authEndpoints";
export * from "./auth/types";

/**
* alphadayApi export should be last
*/
Expand Down
Loading
Loading