Compare commits

..

25 Commits

Author SHA1 Message Date
995c7ce293 feat(auth): nsec login with url params 2024-10-07 19:17:19 +02:00
532cdaed8e refactor(auth): open nostr-login directly 2024-10-07 15:36:29 +02:00
67d545de2f fix: show import/export only for local 2024-10-07 13:44:04 +02:00
637e26bf35 refactor: init nostr-login, login method strategies, remove bunker 2024-10-07 13:36:45 +02:00
110621a125 feat: add nostrLoginAuthMethod to state 2024-10-07 13:36:45 +02:00
59e153595a refactor: z-index levels 2024-10-07 13:36:45 +02:00
0b79ebd909 refactor: z-index levels 2024-10-07 13:36:45 +02:00
e2dbed2b03 refactor: add nostr-login package, show not-allowed on disabled form fields 2024-10-07 13:36:45 +02:00
7c26edf84e chore(git): merge pull request from fixes-10-7 into staging
Reviewed-on: 
Reviewed-by: eugene <eugene@nostrdev.com>
2024-10-07 11:27:41 +00:00
2d7bb234f4 refactor: next on each mark, including the final one 2024-10-07 12:59:55 +02:00
c4d50293ff refactor: toolbox order 2024-10-07 12:53:43 +02:00
b
89971fb176 Merge pull request 'Add Sigit ID as a path param' () from issue-171 into staging
Reviewed-on: 
Reviewed-by: enes <enes@noreply.git.nostrdev.com>
2024-10-07 09:16:30 +00:00
b
acad24dc06 Merge branch 'staging' into issue-171 2024-10-07 09:15:38 +00:00
.
55abe814c9 fix: AGPL Licence, closes 2024-10-06 20:28:27 +01:00
b
e33996c1f9 Merge branch 'staging' into issue-171 2024-10-05 21:02:40 +00:00
6ba3b6ec89 Merge branch 'staging' into issue-171 2024-09-16 12:37:39 +00:00
b
aa8214d015 Merge branch 'staging' into issue-171 2024-09-13 10:07:24 +00:00
e48a396990 fix: verify/sign link 2024-09-11 17:29:47 +02:00
79e14d45a1 chore: comment fix 2024-09-11 15:41:42 +02:00
64e8ebba85 chore: renamed sigitKey to sigitCreateId 2024-09-11 13:30:39 +02:00
5dc8d53503 chore: sigitCreateId naming 2024-09-11 12:33:40 +02:00
86a16c13ce chore: comments and lint (typing) 2024-09-11 12:29:38 +02:00
7c027825cd style: lint fix 2024-09-11 12:03:23 +02:00
8e71592d88 fix: routing, removed useEffect 2024-09-11 11:59:12 +02:00
75a715d002 feat: Add Sigit ID as a path param 2024-09-10 16:00:48 +02:00
11 changed files with 143 additions and 85 deletions
src
components
AppBar
DisplaySigit
MarkFormField
layouts
pages
home
landing
settings/nostrLogin
sign
routes
utils

@ -28,6 +28,8 @@ import { faClose } from '@fortawesome/free-solid-svg-icons'
import useMediaQuery from '@mui/material/useMediaQuery'
import { useLogout } from '../../hooks/useLogout'
import { launch as launchNostrLoginDialog } from 'nostr-login'
export const AppBar = () => {
const navigate = useNavigate()
const logout = useLogout()
@ -128,7 +130,7 @@ export const AppBar = () => {
<Button
startIcon={<ButtonIcon />}
onClick={() => {
navigate(appPublicRoutes.nostr)
launchNostrLoginDialog('welcome')
}}
variant="contained"
>

@ -22,11 +22,16 @@ import { useSigitMeta } from '../../hooks/useSigitMeta'
import { extractFileExtensions } from '../../utils/file'
type SigitProps = {
sigitCreateId: string
meta: Meta
parsedMeta: SigitCardDisplayInfo
}
export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
export const DisplaySigit = ({
meta,
parsedMeta,
sigitCreateId: sigitCreateId
}: SigitProps) => {
const { title, createdAt, submittedBy, signers, signedStatus, isValid } =
parsedMeta
@ -35,15 +40,19 @@ export const DisplaySigit = ({ meta, parsedMeta }: SigitProps) => {
return (
<div className={styles.itemWrapper}>
<Link
to={
signedStatus === SigitStatus.Complete
? appPublicRoutes.verify
: appPrivateRoutes.sign
}
state={{ meta }}
className={styles.insetLink}
></Link>
{signedStatus === SigitStatus.Complete && (
<Link
to={appPublicRoutes.verify}
state={{ meta }}
className={styles.insetLink}
></Link>
)}
{signedStatus !== SigitStatus.Complete && (
<Link
to={`${appPrivateRoutes.sign}/${sigitCreateId}`}
className={styles.insetLink}
></Link>
)}
<p className={`line-clamp-2 ${styles.title}`}>{title}</p>
<div className={styles.users}>
{submittedBy && (

@ -1,7 +1,7 @@
import { CurrentUserMark } from '../../types/mark.ts'
import styles from './style.module.scss'
import { MARK_TYPE_TRANSLATION, NEXT, SIGN } from '../../utils/const.ts'
import { MARK_TYPE_TRANSLATION } from '../../utils/const.ts'
import {
findNextIncompleteCurrentUserMark,
isCurrentUserMarksComplete,
@ -32,7 +32,6 @@ const MarkFormField = ({
handleCurrentUserMarkChange
}: MarkFormFieldProps) => {
const [displayActions, setDisplayActions] = useState(true)
const getSubmitButtonText = () => (isReadyToSign() ? SIGN : NEXT)
const isReadyToSign = () =>
isCurrentUserMarksComplete(currentUserMarks) ||
isCurrentValueLast(currentUserMarks, selectedMark, selectedMarkValue)
@ -61,6 +60,7 @@ const MarkFormField = ({
onClick={toggleActions}
className={styles.triggerBtn}
type="button"
title="Toggle"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -93,7 +93,7 @@ const MarkFormField = ({
/>
<div className={styles.actionsBottom}>
<button type="submit" className={styles.submitButton}>
{getSubmitButtonText()}
NEXT
</button>
</div>
</form>

@ -1,6 +1,5 @@
import { Event, kinds } from 'nostr-tools'
import { Event, getPublicKey, kinds, nip19 } from 'nostr-tools'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
import { AppBar } from '../components/AppBar/AppBar'
import { LoadingSpinner } from '../components/LoadingSpinner'
@ -12,12 +11,11 @@ import {
import {
restoreState,
setMetadataEvent,
updateKeyPair,
updateLoginMethod,
updateNostrLoginAuthMethod,
updateUserAppData
} from '../store/actions'
import { State } from '../store/rootReducer'
import { Dispatch } from '../store/store'
import { setUserRobotImage } from '../store/userRobotImage/action'
import {
getRoboHashPicture,
@ -25,7 +23,7 @@ import {
loadState,
subscribeForSigits
} from '../utils'
import { useAppSelector } from '../hooks'
import { useAppDispatch, useAppSelector } from '../hooks'
import styles from './style.module.scss'
import { useLogout } from '../hooks/useLogout'
import { LoginMethod } from '../store/auth/types'
@ -33,13 +31,13 @@ import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
import { init as initNostrLogin } from 'nostr-login'
export const MainLayout = () => {
const [searchParams] = useSearchParams()
const [searchParams, setSearchParams] = useSearchParams()
const navigate = useNavigate()
const dispatch: Dispatch = useDispatch()
const dispatch = useAppDispatch()
const logout = useLogout()
const [isLoading, setIsLoading] = useState(true)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState(`Loading App`)
const authState = useSelector((state: State) => state.auth)
const authState = useAppSelector((state) => state.auth)
const usersAppData = useAppSelector((state) => state.userAppData)
// Ref to track if `subscribeForSigits` has been called
@ -125,7 +123,7 @@ export const MainLayout = () => {
}, [])
useEffect(() => {
if (authState.loggedIn && usersAppData) {
if (authState && authState.loggedIn && usersAppData) {
const pubkey = authState.usersPubkey || authState.keyPair?.public
if (pubkey && !hasSubscribed.current) {
@ -164,6 +162,53 @@ export const MainLayout = () => {
}
}, [authState, dispatch])
useEffect(() => {
// Developer login with ?nsec= (not recommended)
const nsec = searchParams.get('nsec')
if (!nsec) return
// Clear nsec from the url immediately
searchParams.delete('nsec')
setSearchParams(searchParams)
if (!authState?.loggedIn) {
if (!nsec.startsWith('nsec')) {
console.error('Invalid format, use private key (nsec)')
return
}
try {
const privateKey = nip19.decode(nsec).data as Uint8Array
if (!privateKey) {
console.error('Failed to convert the private key.')
return
}
const publickey = getPublicKey(privateKey)
dispatch(
updateKeyPair({
private: nsec,
public: publickey
})
)
dispatch(updateLoginMethod(LoginMethod.privateKey))
;(async () => {
const authController = new AuthController()
await authController
.authAndGetMetadataAndRelaysMap(publickey)
.catch((err) => {
console.error('Error occurred in authentication: ' + err)
return null
})
})()
} catch (err) {
console.error(`Error decoding the nsec. ${err}`)
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch, searchParams])
if (isLoading) return <LoadingSpinner desc={loadingSpinnerDesc} />
const isDev = import.meta.env.MODE === 'development'

@ -257,6 +257,7 @@ export const HomePage = () => {
.map((key) => (
<DisplaySigit
key={`sigit-${key}`}
sigitCreateId={key}
parsedMeta={parsedSigits[key]}
meta={sigits[key]}
/>

@ -1,7 +1,6 @@
import { Box, Button } from '@mui/material'
import { useEffect } from 'react'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'
import { appPublicRoutes } from '../../routes'
import { Outlet, useLocation } from 'react-router-dom'
import { saveVisitedLink } from '../../utils'
import { CardComponent } from '../../components/Landing/CardComponent/CardComponent'
import { Container } from '../../components/Container'
@ -20,13 +19,13 @@ import {
} from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIconStack } from '../../components/FontAwesomeIconStack'
import { Footer } from '../../components/Footer/Footer'
import { launch as launchNostrLoginDialog } from 'nostr-login'
export const LandingPage = () => {
const navigate = useNavigate()
const location = useLocation()
const onSignInClick = async () => {
navigate(appPublicRoutes.nostr)
launchNostrLoginDialog('welcome')
}
const cards = [
@ -35,7 +34,7 @@ export const LandingPage = () => {
title: <>Open Source</>,
description: (
<>
Code is MIT licenced and available at{' '}
Code is AGPL licenced and available at{' '}
<a href="https://git.nostrdev.com/sigit/sigit.io">
https://git.nostrdev.com/sigit/sigit.io
</a>

@ -10,9 +10,14 @@ import { launch as launchNostrLoginDialog } from 'nostr-login'
import { Container } from '../../../components/Container'
import PeopleIcon from '@mui/icons-material/People'
import ImportExportIcon from '@mui/icons-material/ImportExport'
import { useAppSelector } from '../../../hooks/store'
import { NostrLoginAuthMethod } from '../../../store/auth/types'
export const NostrLoginPage = () => {
const theme = useTheme()
const nostrLoginAuthMethod = useAppSelector(
(state) => state.auth?.nostrLoginAuthMethod
)
return (
<Container>
@ -50,21 +55,23 @@ export const NostrLoginPage = () => {
}}
/>
</ListItemButton>
<ListItemButton
onClick={() => {
launchNostrLoginDialog('import')
}}
>
<ListItemIcon>
<ImportExportIcon />
</ListItemIcon>
<ListItemText
primary={'Import / Export Keys'}
sx={{
color: theme.palette.text.primary
{nostrLoginAuthMethod === NostrLoginAuthMethod.Local && (
<ListItemButton
onClick={() => {
launchNostrLoginDialog('import')
}}
/>
</ListItemButton>
>
<ListItemIcon>
<ImportExportIcon />
</ListItemIcon>
<ListItemText
primary={'Import / Export Keys'}
sx={{
color: theme.palette.text.primary
}}
/>
</ListItemButton>
)}
</List>
</Container>
)

@ -7,7 +7,7 @@ import { MuiFileInput } from 'mui-file-input'
import { Event, verifyEvent } from 'nostr-tools'
import { useCallback, useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom'
import { useLocation, useNavigate, useParams } from 'react-router-dom'
import { toast } from 'react-toastify'
import { LoadingSpinner } from '../../components/LoadingSpinner'
import { NostrController } from '../../controllers'
@ -54,6 +54,7 @@ import {
SigitFile
} from '../../utils/file.ts'
import { ARRAY_BUFFER, DEFLATE } from '../../utils/const.ts'
import { useAppSelector } from '../../hooks/store.ts'
enum SignedStatus {
Fully_Signed,
User_Is_Next_Signer,
@ -63,17 +64,39 @@ enum SignedStatus {
export const SignPage = () => {
const navigate = useNavigate()
const location = useLocation()
const params = useParams()
const usersAppData = useAppSelector((state) => state.userAppData)
/**
* Received from `location.state`
*
* uploadedZip will be received from home page when a user uploads a sigit zip wrapper that contains keys.json
* arrayBuffer will be received in navigation from create page in offline mode
* meta will be received in navigation from create & home page in online mode
* arrayBuffer (decryptedArrayBuffer) will be received in navigation from create page in offline mode
* meta (metaInNavState) will be received in navigation from create & home page in online mode
*/
const {
meta: metaInNavState,
arrayBuffer: decryptedArrayBuffer,
uploadedZip
} = location.state || {}
let metaInNavState = location?.state?.meta || undefined
const { arrayBuffer: decryptedArrayBuffer, uploadedZip } = location.state || {
decryptedArrayBuffer: undefined,
uploadedZip: undefined
}
/**
* If userAppData (redux) is available, and we have the route param (sigit id)
* which is actually a `createEventId`, we will fetch a `sigit`
* based on the provided route ID and set fetched `sigit` to the `metaInNavState`
*/
if (usersAppData) {
const sigitCreateId = params.id
if (sigitCreateId) {
const sigit = usersAppData.sigits[sigitCreateId]
if (sigit) {
metaInNavState = sigit
}
}
}
const [displayInput, setDisplayInput] = useState(false)

@ -1,11 +1,7 @@
import { Modal } from '../layouts/modal'
import { CreatePage } from '../pages/create'
import { HomePage } from '../pages/home'
import { LandingPage } from '../pages/landing'
import { Login } from '../pages/login'
import { Nostr } from '../pages/nostr'
import { ProfilePage } from '../pages/profile'
import { Register } from '../pages/register'
import { SettingsPage } from '../pages/settings/Settings'
import { CacheSettingsPage } from '../pages/settings/cache'
import { NostrLoginPage } from '../pages/settings/nostrLogin'
@ -87,29 +83,7 @@ export const publicRoutes: PublicRouteProps[] = [
{
path: appPublicRoutes.landingPage,
hiddenWhenLoggedIn: true,
element: <LandingPage />,
children: [
{
element: <Modal />,
children: [
{
path: appPublicRoutes.login,
hiddenWhenLoggedIn: true,
element: <Login />
},
{
path: appPublicRoutes.register,
hiddenWhenLoggedIn: true,
element: <Register />
},
{
path: appPublicRoutes.nostr,
hiddenWhenLoggedIn: true,
element: <Nostr />
}
]
}
]
element: <LandingPage />
},
{
path: appPublicRoutes.profile,
@ -131,7 +105,7 @@ export const privateRoutes = [
element: <CreatePage />
},
{
path: appPrivateRoutes.sign,
path: `${appPrivateRoutes.sign}/:id?`,
element: <SignPage />
},
{

@ -4,8 +4,6 @@ export const EMPTY: string = ''
export const MARK_TYPE_TRANSLATION: { [key: string]: string } = {
[MarkType.FULLNAME.valueOf()]: 'Full Name'
}
export const SIGN: string = 'Sign'
export const NEXT: string = 'Next'
export const ARRAY_BUFFER = 'arraybuffer'
export const DEFLATE = 'DEFLATE'

@ -153,6 +153,11 @@ const findOtherUserMarks = (marks: Mark[], pubkey: string): Mark[] => {
}
export const DEFAULT_TOOLBOX: DrawTool[] = [
{
identifier: MarkType.TEXT,
icon: faT,
label: 'Text'
},
{
identifier: MarkType.FULLNAME,
icon: faIdCard,
@ -177,11 +182,6 @@ export const DEFAULT_TOOLBOX: DrawTool[] = [
label: 'Date Time',
isComingSoon: true
},
{
identifier: MarkType.TEXT,
icon: faT,
label: 'Text'
},
{
identifier: MarkType.NUMBER,
icon: fa1,