Skip to content

Commit

Permalink
Fix cloud login/signup loading state (#11625)
Browse files Browse the repository at this point in the history
* Fix cloud loading state

* Use useInitAuthService for deps

* Fix use init service to not reinit on every render
  • Loading branch information
jamakase committed Mar 31, 2022
1 parent f028181 commit 57a6353
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 39 deletions.
97 changes: 61 additions & 36 deletions airbyte-webapp/src/packages/cloud/services/auth/AuthService.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useContext, useEffect, useMemo } from "react";
import React, { useCallback, useContext, useMemo, useRef } from "react";
import { useQueryClient } from "react-query";
import { useResetter } from "rest-hooks";
import { User as FbUser } from "firebase/auth";
import { useEffectOnce } from "react-use";

import { GoogleAuthService } from "packages/cloud/lib/auth/GoogleAuthService";
import useTypesafeReducer from "hooks/useTypesafeReducer";
Expand All @@ -17,6 +18,7 @@ import { useGetUserService } from "packages/cloud/services/users/UserService";
import { useAuth } from "packages/firebaseReact";
import { useAnalyticsService } from "hooks/services/Analytics";
import { getUtmFromStorage } from "utils/utmStorage";
import { useInitService } from "services/useInitService";

export type AuthUpdatePassword = (
email: string,
Expand Down Expand Up @@ -122,47 +124,62 @@ export const AuthenticationProvider: React.FC = ({ children }) => {
const auth = useAuth();
const userService = useGetUserService();
const analytics = useAnalyticsService();
const authService = useMemo(() => new GoogleAuthService(() => auth), [auth]);
const authService = useInitService(() => new GoogleAuthService(() => auth), [
auth,
]);

useEffect(() => {
return auth.onAuthStateChanged(async (currentUser) => {
if (state.currentUser === null && currentUser) {
let user: User | undefined;
const onAfterAuth = useCallback(
async (currentUser: FbUser) => {
let user: User | undefined;

try {
user = await userService.getByAuthId(
currentUser.uid,
AuthProviders.GoogleIdentityPlatform
);
} catch (err) {
if (currentUser.email) {
const encodedData = TempSignUpValuesProvider.get(currentUser);
user = await userService.create({
authProvider: AuthProviders.GoogleIdentityPlatform,
authUserId: currentUser.uid,
email: currentUser.email,
name: encodedData.name,
companyName: encodedData.companyName,
news: encodedData.news,
});
analytics.track("Airbyte.UI.User.Created", {
user_id: user.userId,
name: user.name,
email: user.email,
...getUtmFromStorage(),
});
}
try {
user = await userService.getByAuthId(
currentUser.uid,
AuthProviders.GoogleIdentityPlatform
);
} catch (err) {
if (currentUser.email) {
const encodedData = TempSignUpValuesProvider.get(currentUser);
user = await userService.create({
authProvider: AuthProviders.GoogleIdentityPlatform,
authUserId: currentUser.uid,
email: currentUser.email,
name: encodedData.name,
companyName: encodedData.companyName,
news: encodedData.news,
});
analytics.track("Airbyte.UI.User.Created", {
user_id: user.userId,
name: user.name,
email: user.email,
...getUtmFromStorage(),
});
}
}

if (user) {
loggedIn({ user, emailVerified: currentUser.emailVerified });
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[userService]
);

const stateRef = useRef(state);
stateRef.current = state;

if (user) {
loggedIn({ user, emailVerified: currentUser.emailVerified });
useEffectOnce(() => {
return auth.onAuthStateChanged(async (currentUser) => {
// We want to run this effect only once on initial page opening
if (!stateRef.current.inited) {
if (stateRef.current.currentUser === null && currentUser) {
await onAfterAuth(currentUser);
} else {
authInited();
}
} else {
authInited();
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state.currentUser, loggedIn, authInited]);
});

const queryClient = useQueryClient();
const reset = useResetter();
Expand All @@ -174,6 +191,10 @@ export const AuthenticationProvider: React.FC = ({ children }) => {
emailVerified: state.emailVerified,
async login(values: { email: string; password: string }): Promise<void> {
await authService.login(values.email, values.password);

if (auth.currentUser) {
await onAfterAuth(auth.currentUser);
}
},
async logout(): Promise<void> {
await authService.signOut();
Expand Down Expand Up @@ -226,11 +247,15 @@ export const AuthenticationProvider: React.FC = ({ children }) => {
});

await authService.sendEmailVerifiedLink();

if (auth.currentUser) {
await onAfterAuth(auth.currentUser);
}
},
user: state.currentUser,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[state, queryClient, userService]
[state, userService]
);

return <AuthContext.Provider value={ctx}>{children}</AuthContext.Provider>;
Expand Down
12 changes: 9 additions & 3 deletions airbyte-webapp/src/services/useInitService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ export function useInitService<T extends new (...args: unknown[]) => any>(
f: () => InstanceType<T>,
deps: ConstructorParameters<T>
): InstanceType<T> {
const service = useRef<InstanceType<T>>(f());
const service = useRef<InstanceType<T> | null>(null);

useEffect(() => {
service.current = f();
if (service.current !== null) {
service.current = f();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);

return service.current;
if (service.current === null) {
service.current = f();
}

return (service.current as unknown) as InstanceType<T>;
}

0 comments on commit 57a6353

Please sign in to comment.