Skip to content

Commit

Permalink
feat: implement session management and login flow
Browse files Browse the repository at this point in the history
  • Loading branch information
OKendigelyan authored and ajinkyaraj-23 committed Feb 26, 2025
1 parent 718638a commit 373f573
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 17 deletions.
27 changes: 16 additions & 11 deletions apps/web/src/components/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { useCurrentAccount } from "@umami/state";
import { useHandleSession } from "@umami/state";

import { Layout } from "../../Layout";
import { SessionLogin } from "../../views/SessionLogin/SessionLogin";
import { Welcome } from "../../views/Welcome";
import { BeaconProvider } from "../beacon";
import { WalletConnectProvider } from "../WalletConnect/WalletConnectProvider";

export const App = () => {
const currentAccount = useCurrentAccount();
const { isSessionActive, isOnboarded } = useHandleSession();

return currentAccount ? (
<BeaconProvider>
<WalletConnectProvider>
<Layout />
</WalletConnectProvider>
</BeaconProvider>
) : (
<Welcome />
);
if (!isOnboarded()) {
return <Welcome />;
} else if (!isSessionActive) {
return <SessionLogin />;
} else {
return (
<BeaconProvider>
<WalletConnectProvider>
<Layout />
</WalletConnectProvider>
</BeaconProvider>
);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Text,
} from "@chakra-ui/react";
import { useMultiForm } from "@umami/components";
import { useIsPasswordSet, useSessionTimeout } from "@umami/state";
import { useHandleSession, useIsPasswordSet } from "@umami/state";
import { defaultDerivationPathTemplate } from "@umami/tezos";
import { FormProvider } from "react-hook-form";

Expand Down Expand Up @@ -72,7 +72,7 @@ export const SetupPassword = ({ mode }: SetupPasswordProps) => {
const color = useColor();
const { onSubmit, isLoading } = useGetSetupPasswordSubmitHandler(mode);
const isPasswordSet = useIsPasswordSet();
const { setupSessionTimeout } = useSessionTimeout();
const { setupSessionTimeout } = useHandleSession();

const form = useMultiForm<FormFields>({
mode: "all",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/providers/ReduxStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ export const ReduxStore = ({ children }: PropsWithChildren) => (

const ReduxStoreContent = ({ children }: PropsWithChildren) => {
const nonce = getOrCreateUserNonce();

if (!nonce) {
return <>{children}</>;
}
if (!persistor) {
const { persistor } = initializePersistence(store, nonce);
setupPersistor(persistor);

return (
<PersistGate loading={null} persistor={persistor}>
{children}
Expand Down
67 changes: 67 additions & 0 deletions apps/web/src/views/SessionLogin/SessionLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Button, Center, Flex, type FlexProps, Heading, Icon, Text } from "@chakra-ui/react";
import { type FieldValues, FormProvider, useForm } from "react-hook-form";

import { LogoLightIcon, TezosLogoIcon } from "../../assets/icons";
import { PasswordInput } from "../../components/PasswordInput";
import { useColor } from "../../styles/useColor";

export const SessionLogin = () => {
const color = useColor();

const form = useForm({
defaultValues: {
password: "",
},
mode: "onBlur",
});

const onSubmit = (data: FieldValues) => {
console.log(data);
};

return (
<FormProvider {...form}>
<Flex alignItems={{ base: "end", md: "center" }} justifyContent="center" height="100vh">
<Flex
alignItems="center"
alignContent="start"
flexDirection="column"
gap="36px"
width={{ base: "full", md: "510px" }}
padding={{ base: "36px", md: "36px 42px" }}
color={color("700")}
fontSize="14px"
border="1px solid"
borderColor={color("100")}
borderTopRadius="30px"
borderBottomRadius={{ base: 0, md: "30px" }}
backgroundColor={color("white")}
>
<Flex alignItems="center" justifyContent="end" flexDirection="column" gridArea="header">
<Icon as={LogoLightIcon} width="42px" height="42px" />
<Heading marginTop="18px" color={color("900")} size={{ base: "2xl", md: "3xl" }}>
Welcome back!
</Heading>
<Text marginTop="6px" color={color("600")} size={{ base: "md", md: "lg" }}>
You need to sign back in to use your wallet.
</Text>
</Flex>
<PasswordInput inputName="password" />
<Button width="full" onClick={form.handleSubmit(onSubmit)} size="lg" variant="primary">
Unlock
</Button>
<Logo alignSelf="end" gridArea="tezos-logo" />
</Flex>
</Flex>
</FormProvider>
);
};

const Logo = (props: FlexProps) => (
<Center gap="10px" width="full" {...props}>
<Text color="gray.400" size="sm">
Powered by
</Text>
<Icon as={TezosLogoIcon} width="auto" height={{ base: "24px", md: "36px" }} color="gray.400" />
</Center>
);
1 change: 1 addition & 0 deletions apps/web/src/views/SessionLogin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./SessionLogin";
15 changes: 12 additions & 3 deletions packages/state/src/hooks/session.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { useAppDispatch } from "./useAppDispatch";
import { useAppSelector } from "./useAppSelector";
import { setHasSession } from "../slices/session";

const SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes from login

export const useSessionTimeout = () => {
export const useHandleSession = () => {
const isOnboarded = () => !!localStorage.getItem("user_requirements_nonce");
const isSessionActive = useAppSelector(state => state.session.hasSession);
const dispatch = useAppDispatch();

const setupSessionTimeout = () => {
try {
const timeoutId = window.setTimeout(() => {
const timeoutId = setTimeout(() => {
sessionStorage.clear();
dispatch(setHasSession(false));
}, SESSION_TIMEOUT);

// Store timeout ID in case we need to clear it
Expand All @@ -14,5 +23,5 @@ export const useSessionTimeout = () => {
}
};

return { setupSessionTimeout };
return { setupSessionTimeout, isSessionActive, isOnboarded };
};
4 changes: 3 additions & 1 deletion packages/state/src/reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { errorsSlice } from "./slices/errors";
import { multisigsSlice } from "./slices/multisigs";
import { networksSlice } from "./slices/networks";
import { protocolSettingsSlice } from "./slices/protocolSettings";
import { sessionSlice } from "./slices/session";
import { tokensSlice } from "./slices/tokens";

let TEST_STORAGE: Storage | undefined;
Expand Down Expand Up @@ -47,6 +48,7 @@ export const makeReducer = () => {
networks: networksSlice.reducer,
protocolSettings: protocolSettingsSlice.reducer,
tokens: tokensSlice.reducer,
session: sessionSlice.reducer,
});

return (state: any, action: Action) => {
Expand All @@ -68,7 +70,7 @@ export const makePersistConfigs = (storage_: Storage | undefined, password?: str
key: "root",
version: VERSION,
storage,
blacklist: ["accounts", "assets", "announcement", "tokens", "protocolSettings"],
blacklist: ["accounts", "assets", "announcement", "tokens", "protocolSettings", "session"],
migrate: createAsyncMigrate(mainStoreMigrations, { debug: false }),
transforms: [
encryptTransform(
Expand Down
15 changes: 15 additions & 0 deletions packages/state/src/slices/session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSlice } from "@reduxjs/toolkit";

export const sessionSlice = createSlice({
name: "session",
initialState: {
hasSession: false,
},
reducers: {
setHasSession: (state, action) => {
state.hasSession = action.payload;
},
},
});

export const { setHasSession } = sessionSlice.actions;
2 changes: 2 additions & 0 deletions packages/state/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type Storage, persistReducer, persistStore } from "redux-persist";

import { makePersistConfigs, makeReducer } from "./reducer";
import { accountsSlice } from "./slices/accounts/accounts";
import { setHasSession } from "./slices/session";

// Create initial store without persistence
export const makeStore = () => {
Expand Down Expand Up @@ -55,6 +56,7 @@ export const initializePersistence = (

// Update store's reducer
store.replaceReducer(finalReducer);
store.dispatch(setHasSession(true));

const persistor = persistStore(store);
return { persistor };
Expand Down

0 comments on commit 373f573

Please sign in to comment.