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: allow login with oauth providers set in the sites configuration #740

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
018e85a
feat: Allow users to login with Email link
prathameshkurunkar7 Feb 22, 2024
82ef663
fix: replace toast with callout on success
prathameshkurunkar7 Feb 22, 2024
7c2f818
feat: created custom callout component
prathameshkurunkar7 Feb 23, 2024
c1f1e8b
refactor: login with email link component separated
prathameshkurunkar7 Feb 23, 2024
d661960
feat(mobile): Allow users to login with email link
prathameshkurunkar7 Feb 24, 2024
64e1b0b
fix(mobile): refresh messages when user reacts
nikkothari22 Feb 22, 2024
cd23da2
fix: API path for creating a DM channel via search
nikkothari22 Feb 22, 2024
f2cfab3
fix: only show enabled users on mentions and add members
nikkothari22 Feb 22, 2024
e43ae72
fix: show error message if users have a role profile attached to them
nikkothari22 Feb 22, 2024
11ff086
fix: autofocus tiptap editor
nikkothari22 Feb 22, 2024
f9b3506
fix: permission checks + read channel type from cache
nikkothari22 Feb 23, 2024
1461cd3
fix(ui): width of user avatar in message preview of mobile
nikkothari22 Feb 23, 2024
5531a8d
fix(ui): wrap reactions on mobile app
nikkothari22 Feb 23, 2024
37ee58f
fix: create raven user for website users if role is assigned to them
nikkothari22 Feb 23, 2024
3d353c7
fix: scroll to current selected item in mention list
nikkothari22 Feb 23, 2024
d04abde
fix: do not fetch unread count for archived channels
nikkothari22 Feb 23, 2024
25e1569
fix: mention list on mobile
nikkothari22 Feb 23, 2024
2dd688d
chore: bumped version to v1.4.1
nikkothari22 Feb 23, 2024
9445c41
feat: Raven Mentions child table
TITANiumRox Feb 23, 2024
dea7b67
feat: mentions
TITANiumRox Feb 23, 2024
a6c13d9
fix: moved vite to dependencies
sumitjain236 Feb 24, 2024
664e712
chore: bumped version to 1.4.2
sumitjain236 Feb 24, 2024
afd1526
fix: added install_depedencies
sumitjain236 Feb 24, 2024
317c74b
fix: updated import for error callout
prathameshkurunkar7 Feb 27, 2024
4e3a9c3
Merge branch 'develop' into 692-allow-users-to-login-with-email-link-…
prathameshkurunkar7 Feb 27, 2024
7f7e33d
fix: reply block link rendering
nikkothari22 Feb 27, 2024
7d1b487
fix: do not open two tabs on link click
nikkothari22 Feb 27, 2024
7f9798d
fix: user mentions highlighting
nikkothari22 Feb 27, 2024
f9a380e
fix: remove line clamp on links
nikkothari22 Feb 27, 2024
32d170c
chore: bumped version to v1.4.3
nikkothari22 Feb 27, 2024
1d5ccac
chore: downgraded to vite 4
nikkothari22 Feb 28, 2024
581398a
merge: do not escape underscores in url while copying #738
prathameshkurunkar7 Mar 1, 2024
2883d58
feat: Allow login with OAuth providers set in the site's configuratio…
prathameshkurunkar7 Mar 1, 2024
b02f224
fix: repaired imports and replace post call
prathameshkurunkar7 Mar 1, 2024
46b50db
fix(mobile): repaired imports and used frappe post call hook
prathameshkurunkar7 Mar 1, 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
22 changes: 22 additions & 0 deletions mobile/src/components/common/SuccessCallout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import { PropsWithChildren } from "react";

export type CalloutObject = {
state: boolean;
message: string;
};

export const SuccessCallout = ({
children,
...props
}: PropsWithChildren<{ message?: string }>) => {
return (
<div
key="success"
className="ion-margin bg-zinc-900 rounded-md border-2 border-green-4 00 p-2"
role="complementary"
>
<p className="font-normal text-green-400">{props.message}</p>
</div>
);
};
2 changes: 1 addition & 1 deletion mobile/src/components/layout/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Profile } from "../../pages/profile"
import { PropsWithChildren, useContext } from "react"
import { UserContext } from "../../utils/auth/UserProvider"
import { FullPageLoader } from "./loaders/FullPageLoader"
import { Login } from "../../pages/auth"
import { Login } from "../../pages/auth/Login"

export const Navbar = () => {
const { currentUser, isLoading } = useContext(UserContext)
Expand Down
55 changes: 45 additions & 10 deletions mobile/src/pages/auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IonButton, IonContent, IonHeader, IonImg, IonInput, IonItem, IonPage, IonSpinner, IonTitle, IonToolbar } from '@ionic/react'
import { IonButton, IonContent, IonHeader, IonImg, IonInput, IonItem, IonPage, IonSpinner, IonTitle, IonToolbar, IonText, IonRow } from '@ionic/react'
import { ErrorBanner } from '../../components/layout'
import {SuccessCallout, CalloutObject} from '@/components/common/SuccessCallout'
import raven_logo from '../../assets/raven_logo.png'
import { useContext, useState } from 'react'
import { UserContext } from '../../utils/auth/UserProvider'
import { Controller, set, useForm } from 'react-hook-form'
import { isEmailValid } from '../../utils/validations/validations'
import { Controller, useForm } from 'react-hook-form'
import { LoginWithEmail } from '@/pages/auth/LoginWithEmail'

type Inputs = {
email: string,
Expand All @@ -14,9 +15,18 @@ type Inputs = {
export const Login = () => {

const [error, setError] = useState<Error | null>(null)
const { login, isLoading } = useContext(UserContext)
const { login } = useContext(UserContext)
const { control, handleSubmit, formState: { errors } } = useForm<Inputs>()
const [loading, setLoading] = useState(false)
const [callout, setCallout] = useState<CalloutObject | null>(null)
const [isLoginWithEmailLink, setIsLoginWithEmailLink] = useState<boolean>(false)

// to show/unshow login with email section
const onClickLoginWithEmail = () =>{
setError(null)
setCallout(null)
setIsLoginWithEmailLink(!isLoginWithEmailLink)
}

async function onSubmit(values: Inputs) {
setError(null)
Expand All @@ -39,7 +49,16 @@ export const Login = () => {
</IonToolbar>
</IonHeader>
{error && <ErrorBanner overrideHeading={error.message} />}
<form onSubmit={handleSubmit(onSubmit)}>
{ callout && <SuccessCallout message={callout.message}/> }
{
isLoginWithEmailLink ?
<LoginWithEmail
setCallout={setCallout}
setError={setError}
onClickLoginWithEmail={onClickLoginWithEmail}
/>:
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<IonItem>
<Controller
name="email"
Expand All @@ -50,7 +69,7 @@ export const Login = () => {
render={({ field }) => <IonInput
onIonChange={(e) => field.onChange(e.detail.value)}
required
placeholder='sally@example.com'
placeholder='jane@example.com'
className={!!errors?.email ? 'ion-invalid ion-touched' : ''}
label='Email/Username'
errorText={errors?.email?.message}
Expand Down Expand Up @@ -81,13 +100,29 @@ export const Login = () => {
<IonButton
type="submit"
className='ion-margin-top'
expand="block">
expand="block"
>
{loading ? <IonSpinner name="crescent" /> : "Login"}
</IonButton>
</form>
</div>
</IonContent>
<IonRow class="ion-justify-content-center ion-margin-top ion-margin-bottom">
<IonText color="medium" >
or
</IonText>
</IonRow>
<IonButton
type="button"
onClick={onClickLoginWithEmail}
expand="block"
>
{loading ? <IonSpinner name="crescent" /> : "Login With Email Link"}
</IonButton>
</div>
}

</div>
</IonContent>

</IonPage>
)
}
}
109 changes: 109 additions & 0 deletions mobile/src/pages/auth/LoginWithEmail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
IonButton,
IonInput,
IonItem,
IonSpinner,
} from "@ionic/react";
import { useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { useFrappePostCall } from "frappe-react-sdk";
import { CalloutObject } from '@/components/common/SuccessCallout'

type Inputs = {
email: string;
password: string;
};

export type LoginInputs = {
email: string;
password: string;
};

type Props = {
setError: (err: any) => void;
setCallout: (callout: CalloutObject) => void;
onClickLoginWithEmail: () => void;
};

export const LoginWithEmail = (props: Props) => {
const {
control,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<Inputs>();
const [loading, setLoading] = useState(false);

// POST Call to send login link (settings for social logins, email link etc)
const { call } = useFrappePostCall('frappe.www.login.send_login_link')

async function sendEmailLink(values: LoginInputs) {
return call({
email: values.email,
})
.then((result) => {
props.setCallout({
state: true,
message: "Login Link sent on Email",
});
}).catch((err)=>{
props.setError(err)
})

}


return (
<div>
<form onSubmit={handleSubmit(sendEmailLink)}>
<IonItem>
<Controller
name="email"
control={control}
rules={{
required: "Email is required",
}}
render={({ field }) => (
<IonInput
onIonChange={(e) =>
field.onChange(e.detail.value)
}
required
placeholder="jane@example.com"
className={
!!errors?.email
? "ion-invalid ion-touched"
: ""
}
label="Email"
errorText={errors?.email?.message}
inputMode="email"
labelPlacement="stacked"
/>
)}
/>
</IonItem>

<IonButton
type="submit"
className="ion-margin-top"
expand="block"
disabled={isSubmitting}
>
{loading ? (
<IonSpinner name="crescent" />
) : (
"Send Login Link"
)}
</IonButton>
</form>
<IonButton
type="button"
onClick={props.onClickLoginWithEmail}
expand="block"
fill="clear"
>
{loading ? <IonSpinner name="crescent" /> : "Back to Login"}
</IonButton>
</div>
);
};
1 change: 0 additions & 1 deletion mobile/src/pages/auth/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion raven-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ProtectedRoute } from './utils/auth/ProtectedRoute'
import { UserProvider } from './utils/auth/UserProvider'
import { ChannelRedirect } from './utils/channel/ChannelRedirect'
import "cal-sans";
import { useState } from 'react'
import { ThemeProvider } from './ThemeProvider'
import { Toaster } from './components/common/Toast/Toaster'
import { FullPageLoader } from './components/layout/Loaders'
Expand All @@ -16,6 +15,7 @@ const router = createBrowserRouter(
createRoutesFromElements(
<>
<Route path='/login' lazy={() => import('@/pages/auth/Login')} />
<Route path='/login-with-email' lazy={()=> import('@/pages/auth/LoginWithEmail')} />
<Route path="/" element={<ProtectedRoute />}>
<Route index element={<ChannelRedirect />} />
<Route path="channel" element={<MainPage />} >
Expand Down
36 changes: 36 additions & 0 deletions raven-app/src/components/common/Callouts/CustomCallout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Callout } from "@radix-ui/themes";
import {
CalloutIconProps,
CalloutRootProps,
CalloutTextProps,
} from "@radix-ui/themes/dist/cjs/components/callout";
import { PropsWithChildren } from "react";

export type CalloutObject = {
state: boolean,
message: string,
}

export type CustomCalloutProps = {
rootProps?: CalloutRootProps;
iconProps?: CalloutIconProps;
iconChildren?: React.ReactNode;
textProps?: CalloutTextProps;
textChildren?: React.ReactNode;
};

export const CustomCallout = ({
rootProps,
iconProps,
textProps,
textChildren,
iconChildren,
}: PropsWithChildren<CustomCalloutProps>) => {
return (
<Callout.Root {...rootProps}>
<Callout.Icon {...iconProps}>{iconChildren}</Callout.Icon>
<Callout.Text {...textProps}>{textChildren}</Callout.Text>
</Callout.Root>
);
};

12 changes: 12 additions & 0 deletions raven-app/src/components/common/Callouts/ErrorCallouts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { PropsWithChildren } from "react"
import { FiAlertTriangle } from "react-icons/fi"
import { CustomCallout } from "./CustomCallout"


export const ErrorCallout = ({ children,...props }: PropsWithChildren<{message?: string}>) => {
return (<CustomCallout
rootProps={{color: "red"}}
iconChildren = {<FiAlertTriangle size="18"/>}
textChildren = { props.message }
/>)
}
13 changes: 13 additions & 0 deletions raven-app/src/components/common/Callouts/SuccessCallout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { BiCheckCircle } from "react-icons/bi";
import { PropsWithChildren } from "react";
import { CustomCallout } from "./CustomCallout";

export const SuccessCallout = ({ children,...props}: PropsWithChildren<{message?: string}>) => {
return (
<CustomCallout
rootProps={{color: "green"}}
iconChildren = {<BiCheckCircle size="18"/>}
textChildren = { props.message }
/>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Button, Dialog, Em, Flex, Strong, Text, TextField } from "@radix-ui/the
import { Loader } from "@/components/common/Loader"
import { BiSearch } from "react-icons/bi"
import { useToast } from "@/hooks/useToast"
import { ErrorCallout } from "@/components/layout/AlertBanner/ErrorBanner"
import { ErrorCallout } from "@/components/common/Callouts/ErrorCallouts"

interface AddUsersResponse {
failed_users: User[],
Expand Down
17 changes: 2 additions & 15 deletions raven-app/src/components/layout/AlertBanner/ErrorBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { FrappeError } from 'frappe-react-sdk'
import { PropsWithChildren, useMemo } from 'react'
import { useMemo } from 'react'
import React from 'react'
import { Callout } from '@radix-ui/themes'
import { FiAlertTriangle } from 'react-icons/fi'
import { ErrorCallout } from "@/components/common/Callouts/ErrorCallouts"

interface ErrorBannerProps {
error?: FrappeError | null,
Expand Down Expand Up @@ -73,15 +72,3 @@ export const ErrorBanner = ({ error, overrideHeading, children }: ErrorBannerPro
{children}
</ErrorCallout>)
}


export const ErrorCallout = ({ children }: PropsWithChildren) => {
return (<Callout.Root color="red" role="alert">
<Callout.Icon>
<FiAlertTriangle size='18' />
</Callout.Icon>
<Callout.Text>
{children}
</Callout.Text>
</Callout.Root>)
}
36 changes: 36 additions & 0 deletions raven-app/src/components/layout/AuthContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PropsWithChildren, useContext } from 'react'
import { Box, Flex, Text } from '@radix-ui/themes';
import { FullPageLoader } from "./Loaders";
import { Link } from 'react-router-dom';
import { UserContext } from '@/utils/auth/UserProvider';


const AuthContainer = ({ children, ...props }: PropsWithChildren) => {
const { isLoading } = useContext(UserContext)

return (
<Box className={'min-h-screen'}>
<Flex justify='center' align='center' className={'h-screen w-full'}>
{
isLoading ? <FullPageLoader /> :
<Box className={'w-full max-w-lg'}>
<Flex direction='column' gap='6' className={'w-full bg-white rounded-lg shadow dark:border dark:bg-gray-900 dark:border-gray-700 p-8'}>

<Link to="/" tabIndex={-1}>
<Flex justify="center">
<Text as='span' size='9' className='cal-sans'>raven</Text>
</Flex>
</Link>

{children}

</Flex>
</Box>
}
</Flex>
</Box>
)

}

export default AuthContainer;
Loading
Loading