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

Auth #15

Merged
merged 14 commits into from
Apr 4, 2024
Merged

Auth #15

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
2 changes: 1 addition & 1 deletion db/migrations/01-tables.sql
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
CREATE TABLE users(
user_id uuid PRIMARY KEY DEFAULT NULL,
email text DEFAULT NULL, -- TODO: email needs to be unique per account. But: not possible in electric-sql
auth_id uuid DEFAULT NULL,
label_replace_by_generated_column text DEFAULT NULL,
deleted boolean DEFAULT NULL
);
Expand Down Expand Up @@ -1364,6 +1363,7 @@ COMMENT ON COLUMN fields.level IS 'level of field if places or below: 1, 2';
CREATE TABLE ui_options(
user_id uuid PRIMARY KEY DEFAULT NULL REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
account_id uuid DEFAULT NULL REFERENCES accounts(account_id) ON DELETE CASCADE ON UPDATE CASCADE,
authenticated_email text DEFAULT NULL,
designing boolean DEFAULT NULL, -- FALSE,
breadcrumbs_overflowing boolean DEFAULT NULL, -- FALSE,
navs_overflowing boolean DEFAULT NULL, -- FALSE,
Expand Down
4 changes: 2 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ export default function App() {
ctx-name="uploadcare-uploader"
></lr-upload-ctx-provider>
<style dangerouslySetInnerHTML={{ __html: styleSheet() }} />
<SqlInitializer />
<Syncer />
<FluentProvider theme={lightTheme}>
<div style={routerContainerStyle}>
<SqlInitializer />
<Syncer />
<UploaderContext.Provider value={uploaderRef}>
<RouterProviderWithDb />
</UploaderContext.Provider>
Expand Down
8 changes: 8 additions & 0 deletions src/components/Auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CorbadoAuth } from '@corbado/react'

export const Auth = () => (
<CorbadoAuth
onLoggedIn={() => console.log('hi')}
customerSupportEmail="[email protected]"
/>
)
3 changes: 2 additions & 1 deletion src/components/Layout/Breadcrumbs/BreadcrumbForData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const isOdd = (num) => num % 2
const siblingStyle = {
marginLeft: 7,
}
const labelStyle = { userSelect: 'none' }

// forwarding refs is crucial for the overflow menu to work
// https://github.com/microsoft/fluentui/issues/27652#issuecomment-1520447241
Expand Down Expand Up @@ -162,7 +163,7 @@ export const BreadcrumbForData = forwardRef(
}
ref={ref}
>
<div className="text">{label}</div>
<div style={labelStyle}>{label}</div>
{!!sibling && <div style={siblingStyle}>{sibling}</div>}
<Menu navs={navs} />
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Layout/Breadcrumbs/BreadcrumbForFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { user_id } from '../../SqlInitializer'
const siblingStyle = {
marginLeft: 7,
}
const labelStyle = { userSelect: 'none' }

// forwarding refs is crucial for the overflow menu to work
// https://github.com/microsoft/fluentui/issues/27652#issuecomment-1520447241
Expand Down Expand Up @@ -138,7 +139,7 @@ export const BreadcrumbForFolder = forwardRef(
}
ref={ref}
>
<div className="text">{label}</div>
<div style={labelStyle}>{label}</div>
{!!sibling && <div style={siblingStyle}>{sibling}</div>}
<Menu navs={navs} />
</div>
Expand Down
182 changes: 89 additions & 93 deletions src/components/Layout/Header/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import {
} from '@fluentui/react-components'
import { FaCog } from 'react-icons/fa'
import { MdLogout, MdLogin } from 'react-icons/md'
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
import {
useNavigate,
useParams,
useSearchParams,
useLocation,
} from 'react-router-dom'
import { useLiveQuery } from 'electric-sql/react'
import { useCorbado } from '@corbado/react'

Expand Down Expand Up @@ -50,9 +55,10 @@ export const Menu = memo(() => {
const params = useParams()
const [searchParams] = useSearchParams()

const { isAuthenticated, logout } = useCorbado()
const { pathname } = useLocation()
const isHome = pathname === '/'

console.log('hello Menu, isAuthenticated:', isAuthenticated)
const { isAuthenticated, logout } = useCorbado()

const { db } = useElectric()!
// get ui_options.tabs
Expand All @@ -78,112 +84,90 @@ export const Menu = memo(() => {
})
}, [navigate, params.user_id, searchParams])

const onClickLogin = useCallback(() => navigate('/auth'), [navigate])
const onClickLogout = useCallback(() => {
logout()
navigate('/auth')
}, [logout, navigate])
const onClickLogout = useCallback(() => logout(), [logout])
const onClickEnter = useCallback(() => navigate('/projects'), [navigate])

const treeIsActive = tabs.includes('tree')
const dataIsActive = tabs.includes('data')
const filterIsActive = tabs.includes('filter')
const mapIsActive = tabs.includes('map')

console.log('hello Menu rendering', { isAuthenticated, isHome })

return (
<div style={controls}>
<Toolbar
aria-label="active tabs"
checkedValues={{ tabs }}
onCheckedValueChange={onChangeTabs}
>
<ToolbarToggleButton
aria-label="Tree"
name="tabs"
value="tree"
style={css(
buildButtonStyle({
prevIsActive: false,
nextIsActive: dataIsActive,
selfIsActive: treeIsActive,
}),
)}
>
Tree
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Data"
name="tabs"
value="data"
style={css(
buildButtonStyle({
prevIsActive: treeIsActive,
nextIsActive: filterIsActive,
selfIsActive: dataIsActive,
}),
)}
>
Data
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Filter"
name="tabs"
value="filter"
style={css(
buildButtonStyle({
prevIsActive: dataIsActive,
nextIsActive: mapIsActive,
selfIsActive: filterIsActive,
}),
)}
>
Filter
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Map"
name="tabs"
value="map"
style={css(
buildButtonStyle({
prevIsActive: filterIsActive,
nextIsActive: false,
selfIsActive: mapIsActive,
}),
)}
>
Map
</ToolbarToggleButton>
{!isHome && (
<>
<ToolbarToggleButton
aria-label="Tree"
name="tabs"
value="tree"
style={css(
buildButtonStyle({
prevIsActive: false,
nextIsActive: dataIsActive,
selfIsActive: treeIsActive,
}),
)}
>
Tree
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Data"
name="tabs"
value="data"
style={css(
buildButtonStyle({
prevIsActive: treeIsActive,
nextIsActive: filterIsActive,
selfIsActive: dataIsActive,
}),
)}
>
Data
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Filter"
name="tabs"
value="filter"
style={css(
buildButtonStyle({
prevIsActive: dataIsActive,
nextIsActive: mapIsActive,
selfIsActive: filterIsActive,
}),
)}
>
Filter
</ToolbarToggleButton>
<ToolbarToggleButton
aria-label="Map"
name="tabs"
value="map"
style={css(
buildButtonStyle({
prevIsActive: filterIsActive,
nextIsActive: false,
selfIsActive: mapIsActive,
}),
)}
>
Map
</ToolbarToggleButton>
</>
)}
</Toolbar>
<Button
size="medium"
icon={<FaCog />}
onClick={onClickOptions}
title="Options"
style={css({
backgroundColor: 'rgba(38, 82, 37, 0)',
border: 'none',
color: 'white',
on: ($) => [$('&:hover', { filter: 'brightness(85%)' })],
})}
/>
{isAuthenticated ? (
{!isHome && (
<Button
size="medium"
icon={<MdLogout />}
onClick={onClickLogout}
title="Log out"
style={css({
backgroundColor: 'rgba(38, 82, 37, 0)',
border: 'none',
color: 'white',
on: ($) => [$('&:hover', { filter: 'brightness(85%)' })],
})}
/>
) : (
<Button
size="medium"
icon={<MdLogin />}
onClick={onClickLogin}
title="Log in"
icon={<FaCog />}
onClick={onClickOptions}
title="Options"
style={css({
backgroundColor: 'rgba(38, 82, 37, 0)',
border: 'none',
Expand All @@ -192,6 +176,18 @@ export const Menu = memo(() => {
})}
/>
)}
<Button
size="medium"
icon={isAuthenticated && !isHome ? <MdLogout /> : <MdLogin />}
onClick={isAuthenticated && !isHome ? onClickLogout : onClickEnter}
title={!isAuthenticated ? 'Login' : isHome ? 'Enter' : 'Logout'}
style={css({
backgroundColor: 'rgba(38, 82, 37, 0)',
border: 'none',
color: 'white',
on: ($) => [$('&:hover', { filter: 'brightness(85%)' })],
})}
/>
</div>
)
})
2 changes: 1 addition & 1 deletion src/components/Layout/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const containerStyle = {
padding: '0 10px',
color: 'white',
}
const titleStyle = { fontSize: 'large' }
const titleStyle = { fontSize: 'large', userSelect: 'none' }

export const Header = () => {
return (
Expand Down
36 changes: 30 additions & 6 deletions src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
import { useSearchParams } from 'react-router-dom'
import { useSearchParams, useLocation, Outlet } from 'react-router-dom'

import { Breadcrumbs } from './Breadcrumbs'
import { Navs } from './Navs'
import { Header } from './Header'
import { Main } from './Main'
import { Notifications } from '../Notifications'
import { ProtectedRoute } from '../ProtectedRoute'

const homeOutletStyle = {
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'hidden',
position: 'relative',
}

export const Layout = () => {
const [searchParams] = useSearchParams()
const onlyForm = searchParams.get('onlyForm')

// console.log('Layout rendering')
const { pathname } = useLocation()
const isHome = pathname === '/'

console.log('hello Layout rendering, location:', pathname)

// this is used to show forms inside popups in the map
if (onlyForm) {
return <Main />
}
if (onlyForm) return <Main />

// TODO: not logged in visitors
// should see only simplified header and home page
if (isHome) {
return (
<>
<Header />
<div style={homeOutletStyle}>
<Outlet />
</div>
</>
)
}
return (
<>
<Header />
<Breadcrumbs />
<Navs />
<Main />
<ProtectedRoute>
<Main />
</ProtectedRoute>
<Notifications />
</>
)
Expand Down
24 changes: 10 additions & 14 deletions src/components/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { useCorbadoSession } from '@corbado/react'
import { useLocation, Navigate } from 'react-router-dom'
import { memo } from 'react'
import { useCorbado } from '@corbado/react'
import { Loading } from './shared/Loading'

// https://www.robinwieruch.de/react-router-authentication/
export const ProtectedRoute = ({ children }) => {
const { loading, isAuthenticated, user } = useCorbadoSession()
const location = useLocation()
import { Auth } from './Auth'

// console.warn('ProtectedRoute', { loading, isAuthenticated, user })
// https://www.robinwieruch.de/react-router-authentication/
export const ProtectedRoute = memo(({ children }) => {
const { loading, isAuthenticated } = useCorbado()
// console.log('hello ProtectedRoute', { loading, isAuthenticated })

if (loading) {
return <Loading label="Authenticating" />
}
if (loading) return <Loading label="Authenticating" />

if (!isAuthenticated || !user) {
return <Navigate to="/auth" replace state={{ from: location }} />
}
if (!isAuthenticated) return <Auth />

return children
}
})
Loading