chore(git): Merge branch 'staging' into issue-274
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 45s
All checks were successful
Open PR on Staging / audit_and_check (pull_request) Successful in 45s
This commit is contained in:
commit
18c07556ea
@ -33,7 +33,7 @@ const App = () => {
|
|||||||
window.location.href.split(`${window.location.origin}/#`)[1]
|
window.location.href.split(`${window.location.origin}/#`)[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
return `${appPublicRoutes.login}?callbackPath=${callbackPathEncoded}`
|
return `${appPublicRoutes.landingPage}?callbackPath=${callbackPathEncoded}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
// Hide route only if loggedIn and r.hiddenWhenLoggedIn are both true
|
||||||
|
@ -17,7 +17,7 @@ interface MarkFormFieldProps {
|
|||||||
handleCurrentUserMarkChange: (mark: CurrentUserMark) => void
|
handleCurrentUserMarkChange: (mark: CurrentUserMark) => void
|
||||||
handleSelectedMarkValueChange: (value: string) => void
|
handleSelectedMarkValueChange: (value: string) => void
|
||||||
handleSubmit: (event: React.MouseEvent<HTMLButtonElement>) => void
|
handleSubmit: (event: React.MouseEvent<HTMLButtonElement>) => void
|
||||||
selectedMark: CurrentUserMark
|
selectedMark: CurrentUserMark | null
|
||||||
selectedMarkValue: string
|
selectedMarkValue: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,26 +34,23 @@ const MarkFormField = ({
|
|||||||
}: MarkFormFieldProps) => {
|
}: MarkFormFieldProps) => {
|
||||||
const [displayActions, setDisplayActions] = useState(true)
|
const [displayActions, setDisplayActions] = useState(true)
|
||||||
const [complete, setComplete] = useState(false)
|
const [complete, setComplete] = useState(false)
|
||||||
|
|
||||||
const isReadyToSign = () =>
|
const isReadyToSign = () =>
|
||||||
isCurrentUserMarksComplete(currentUserMarks) ||
|
isCurrentUserMarksComplete(currentUserMarks) ||
|
||||||
isCurrentValueLast(currentUserMarks, selectedMark, selectedMarkValue)
|
isCurrentValueLast(currentUserMarks, selectedMark, selectedMarkValue)
|
||||||
const isCurrent = (currentMark: CurrentUserMark) =>
|
const isCurrent = (currentMark: CurrentUserMark) =>
|
||||||
currentMark.id === selectedMark.id && !complete
|
currentMark.id === selectedMark?.id && !complete
|
||||||
const isDone = (currentMark: CurrentUserMark) =>
|
const isDone = (currentMark: CurrentUserMark) =>
|
||||||
isCurrent(currentMark) ? !!selectedMarkValue : currentMark.isCompleted
|
isCurrent(currentMark) ? !!selectedMarkValue : currentMark.isCompleted
|
||||||
const findNext = () => {
|
const findNext = () => {
|
||||||
return (
|
return (
|
||||||
currentUserMarks[selectedMark.id] ||
|
currentUserMarks[selectedMark!.id] ||
|
||||||
findNextIncompleteCurrentUserMark(currentUserMarks)
|
findNextIncompleteCurrentUserMark(currentUserMarks)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
const handleFormSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
console.log('handle form submit runs...')
|
|
||||||
|
|
||||||
// Without this line, we lose mark values when switching
|
// Without this line, we lose mark values when switching
|
||||||
handleCurrentUserMarkChange(selectedMark)
|
handleCurrentUserMarkChange(selectedMark!)
|
||||||
|
|
||||||
if (!complete) {
|
if (!complete) {
|
||||||
isReadyToSign()
|
isReadyToSign()
|
||||||
@ -63,15 +60,16 @@ const MarkFormField = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const toggleActions = () => setDisplayActions(!displayActions)
|
const toggleActions = () => setDisplayActions(!displayActions)
|
||||||
const markLabel = getToolboxLabelByMarkType(selectedMark.mark.type)
|
const markLabel = selectedMark
|
||||||
|
? getToolboxLabelByMarkType(selectedMark.mark.type)
|
||||||
|
: ''
|
||||||
const handleCurrentUserMarkClick = (mark: CurrentUserMark) => {
|
const handleCurrentUserMarkClick = (mark: CurrentUserMark) => {
|
||||||
setComplete(false)
|
setComplete(false)
|
||||||
handleCurrentUserMarkChange(mark)
|
handleCurrentUserMarkChange(mark)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectCompleteMark = () => {
|
const handleSelectCompleteMark = () => {
|
||||||
handleCurrentUserMarkChange(selectedMark)
|
if (currentUserMarks.length) handleCurrentUserMarkChange(selectedMark!)
|
||||||
setComplete(true)
|
setComplete(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +104,15 @@ const MarkFormField = ({
|
|||||||
<div className={styles.actionsWrapper}>
|
<div className={styles.actionsWrapper}>
|
||||||
<div className={styles.actionsTop}>
|
<div className={styles.actionsTop}>
|
||||||
<div className={styles.actionsTopInfo}>
|
<div className={styles.actionsTopInfo}>
|
||||||
{!complete && (
|
{!complete && selectedMark ? (
|
||||||
<p className={styles.actionsTopInfoText}>Add {markLabel}</p>
|
<p className={styles.actionsTopInfoText}>Add {markLabel}</p>
|
||||||
|
) : (
|
||||||
|
<p className={styles.actionsTopInfoText}>Finish</p>
|
||||||
)}
|
)}
|
||||||
{complete && <p className={styles.actionsTopInfoText}>Finish</p>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.inputWrapper}>
|
<div className={styles.inputWrapper}>
|
||||||
{!complete && (
|
{!complete && selectedMark ? (
|
||||||
<form onSubmit={(e) => handleFormSubmit(e)}>
|
<form onSubmit={(e) => handleFormSubmit(e)}>
|
||||||
<MarkInput
|
<MarkInput
|
||||||
markType={selectedMark.mark.type}
|
markType={selectedMark.mark.type}
|
||||||
@ -129,9 +128,7 @@ const MarkFormField = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
) : (
|
||||||
|
|
||||||
{complete && (
|
|
||||||
<div className={styles.actionsBottom}>
|
<div className={styles.actionsBottom}>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleSignAndComplete}
|
onClick={handleSignAndComplete}
|
||||||
|
@ -101,21 +101,22 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
|||||||
*/
|
*/
|
||||||
const handleSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
|
const handleSubmit = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (!selectedMarkValue || !selectedMark) return
|
if (selectedMarkValue && selectedMark) {
|
||||||
|
const updatedMark: CurrentUserMark = getUpdatedMark(
|
||||||
|
selectedMark,
|
||||||
|
selectedMarkValue
|
||||||
|
)
|
||||||
|
|
||||||
const updatedMark: CurrentUserMark = getUpdatedMark(
|
setSelectedMarkValue(EMPTY)
|
||||||
selectedMark,
|
const updatedCurrentUserMarks = updateCurrentUserMarks(
|
||||||
selectedMarkValue
|
currentUserMarks,
|
||||||
)
|
updatedMark
|
||||||
|
)
|
||||||
|
setCurrentUserMarks(updatedCurrentUserMarks)
|
||||||
|
setSelectedMark(null)
|
||||||
|
setUpdatedMarks(updatedMark.mark)
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedMarkValue(EMPTY)
|
|
||||||
const updatedCurrentUserMarks = updateCurrentUserMarks(
|
|
||||||
currentUserMarks,
|
|
||||||
updatedMark
|
|
||||||
)
|
|
||||||
setCurrentUserMarks(updatedCurrentUserMarks)
|
|
||||||
setSelectedMark(null)
|
|
||||||
setUpdatedMarks(updatedMark.mark)
|
|
||||||
handleSign()
|
handleSign()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,28 +153,24 @@ const PdfMarking = (props: PdfMarkingProps) => {
|
|||||||
centerIcon={faPen}
|
centerIcon={faPen}
|
||||||
rightIcon={faCircleInfo}
|
rightIcon={faCircleInfo}
|
||||||
>
|
>
|
||||||
{currentUserMarks?.length > 0 && (
|
<PdfView
|
||||||
<PdfView
|
currentFile={currentFile}
|
||||||
currentFile={currentFile}
|
files={files}
|
||||||
files={files}
|
handleMarkClick={handleMarkClick}
|
||||||
handleMarkClick={handleMarkClick}
|
|
||||||
selectedMarkValue={selectedMarkValue}
|
|
||||||
selectedMark={selectedMark}
|
|
||||||
currentUserMarks={currentUserMarks}
|
|
||||||
otherUserMarks={otherUserMarks}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</StickySideColumns>
|
|
||||||
{selectedMark !== null && (
|
|
||||||
<MarkFormField
|
|
||||||
handleSubmit={handleSubmit}
|
|
||||||
handleSelectedMarkValueChange={handleChange}
|
|
||||||
selectedMark={selectedMark}
|
|
||||||
selectedMarkValue={selectedMarkValue}
|
selectedMarkValue={selectedMarkValue}
|
||||||
|
selectedMark={selectedMark}
|
||||||
currentUserMarks={currentUserMarks}
|
currentUserMarks={currentUserMarks}
|
||||||
handleCurrentUserMarkChange={handleCurrentUserMarkChange}
|
otherUserMarks={otherUserMarks}
|
||||||
/>
|
/>
|
||||||
)}
|
</StickySideColumns>
|
||||||
|
<MarkFormField
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
handleSelectedMarkValueChange={handleChange}
|
||||||
|
selectedMark={selectedMark}
|
||||||
|
selectedMarkValue={selectedMarkValue}
|
||||||
|
currentUserMarks={currentUserMarks}
|
||||||
|
handleCurrentUserMarkChange={handleCurrentUserMarkChange}
|
||||||
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import { EventTemplate, UnsignedEvent } from 'nostr-tools'
|
import { EventTemplate, UnsignedEvent } from 'nostr-tools'
|
||||||
import { WindowNostr } from 'nostr-tools/nip07'
|
|
||||||
import { EventEmitter } from 'tseep'
|
import { EventEmitter } from 'tseep'
|
||||||
import store from '../store/store'
|
import store from '../store/store'
|
||||||
import { SignedEvent } from '../types'
|
import { SignedEvent } from '../types'
|
||||||
import { LoginMethodContext } from '../services/LoginMethodStrategy/loginMethodContext'
|
import { LoginMethodContext } from '../services/LoginMethodStrategy/loginMethodContext'
|
||||||
|
import { clear, unixNow } from '../utils'
|
||||||
|
import { LoginMethod } from '../store/auth/types'
|
||||||
|
import { logout as nostrLogout } from 'nostr-login'
|
||||||
|
import { userLogOutAction } from '../store/actions'
|
||||||
|
|
||||||
export class NostrController extends EventEmitter {
|
export class NostrController extends EventEmitter {
|
||||||
private static instance: NostrController
|
private static instance: NostrController
|
||||||
@ -11,13 +14,6 @@ export class NostrController extends EventEmitter {
|
|||||||
private constructor() {
|
private constructor() {
|
||||||
super()
|
super()
|
||||||
}
|
}
|
||||||
private getNostrObject = () => {
|
|
||||||
if (window.nostr) return window.nostr as WindowNostr
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`window.nostr object not present. Make sure you have an nostr extension installed/working properly.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getInstance(): NostrController {
|
public static getInstance(): NostrController {
|
||||||
if (!NostrController.instance) {
|
if (!NostrController.instance) {
|
||||||
@ -72,7 +68,22 @@ export class NostrController extends EventEmitter {
|
|||||||
const loginMethod = store.getState().auth.loginMethod
|
const loginMethod = store.getState().auth.loginMethod
|
||||||
const context = new LoginMethodContext(loginMethod)
|
const context = new LoginMethodContext(loginMethod)
|
||||||
|
|
||||||
return await context.signEvent(event)
|
const authkey = store.getState().auth.usersPubkey
|
||||||
|
const signedEvent = await context.signEvent(event)
|
||||||
|
const pubkey = signedEvent.pubkey
|
||||||
|
|
||||||
|
// Forcefully log out the user if we detect missmatch between pubkeys
|
||||||
|
// Allow undefined authkey, intial log in
|
||||||
|
if (authkey && authkey !== pubkey) {
|
||||||
|
if (loginMethod === LoginMethod.nostrLogin) {
|
||||||
|
nostrLogout()
|
||||||
|
}
|
||||||
|
store.dispatch(userLogOutAction())
|
||||||
|
clear()
|
||||||
|
throw new Error('User missmatch.\n\nPlease log in again.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return signedEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
nip04Encrypt = async (receiver: string, content: string): Promise<string> => {
|
nip04Encrypt = async (receiver: string, content: string): Promise<string> => {
|
||||||
@ -97,23 +108,37 @@ export class NostrController extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function will capture the public key from the nostr extension or if no extension present
|
* Function will capture the public key from signedEvent
|
||||||
* function wil capture the public key from the local storage
|
|
||||||
*/
|
*/
|
||||||
capturePublicKey = async (): Promise<string> => {
|
capturePublicKey = async (): Promise<string> => {
|
||||||
const nostr = this.getNostrObject()
|
try {
|
||||||
const pubKey = await nostr.getPublicKey().catch((err: unknown) => {
|
const timestamp = unixNow()
|
||||||
if (err instanceof Error) {
|
const { href } = window.location
|
||||||
return Promise.reject(err.message)
|
|
||||||
} else {
|
const authEvent: EventTemplate = {
|
||||||
return Promise.reject(JSON.stringify(err))
|
kind: 27235,
|
||||||
|
tags: [
|
||||||
|
['u', href],
|
||||||
|
['method', 'GET']
|
||||||
|
],
|
||||||
|
content: '',
|
||||||
|
created_at: timestamp
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
if (!pubKey) {
|
const signedAuthEvent = await this.signEvent(authEvent)
|
||||||
return Promise.reject('Error getting public key, user canceled')
|
const pubkey = signedAuthEvent.pubkey
|
||||||
|
|
||||||
|
if (!pubkey) {
|
||||||
|
return Promise.reject('Error getting public key, user canceled')
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(pubkey)
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return Promise.reject(error.message)
|
||||||
|
} else {
|
||||||
|
return Promise.reject(JSON.stringify(error))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(pubKey)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,12 +58,12 @@ export const MainLayout = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const login = useCallback(async () => {
|
const login = useCallback(async () => {
|
||||||
|
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
||||||
|
|
||||||
const nostrController = NostrController.getInstance()
|
const nostrController = NostrController.getInstance()
|
||||||
const authController = new AuthController()
|
const authController = new AuthController()
|
||||||
const pubkey = await nostrController.capturePublicKey()
|
const pubkey = await nostrController.capturePublicKey()
|
||||||
|
|
||||||
dispatch(updateLoginMethod(LoginMethod.nostrLogin))
|
|
||||||
|
|
||||||
const redirectPath =
|
const redirectPath =
|
||||||
await authController.authAndGetMetadataAndRelaysMap(pubkey)
|
await authController.authAndGetMetadataAndRelaysMap(pubkey)
|
||||||
|
|
||||||
|
@ -123,15 +123,19 @@ const isLast = <T>(index: number, arr: T[]) => index === arr.length - 1
|
|||||||
|
|
||||||
const isCurrentValueLast = (
|
const isCurrentValueLast = (
|
||||||
currentUserMarks: CurrentUserMark[],
|
currentUserMarks: CurrentUserMark[],
|
||||||
selectedMark: CurrentUserMark,
|
selectedMark: CurrentUserMark | null,
|
||||||
selectedMarkValue: string
|
selectedMarkValue: string
|
||||||
) => {
|
) => {
|
||||||
const filteredMarks = currentUserMarks.filter(
|
if (selectedMark && currentUserMarks.length > 0) {
|
||||||
(mark) => mark.id !== selectedMark.id
|
const filteredMarks = currentUserMarks.filter(
|
||||||
)
|
(mark) => mark.id !== selectedMark.id
|
||||||
return (
|
)
|
||||||
isCurrentUserMarksComplete(filteredMarks) && selectedMarkValue.length > 0
|
return (
|
||||||
)
|
isCurrentUserMarksComplete(filteredMarks) && selectedMarkValue.length > 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUpdatedMark = (
|
const getUpdatedMark = (
|
||||||
|
@ -365,11 +365,21 @@ export const createWrap = (unsignedEvent: UnsignedEvent, receiver: string) => {
|
|||||||
* @returns The user application data or null if an error occurs or no data is found.
|
* @returns The user application data or null if an error occurs or no data is found.
|
||||||
*/
|
*/
|
||||||
export const getUsersAppData = async (): Promise<UserAppData | null> => {
|
export const getUsersAppData = async (): Promise<UserAppData | null> => {
|
||||||
|
// Get an instance of the NostrController
|
||||||
|
const nostrController = NostrController.getInstance()
|
||||||
|
|
||||||
// Initialize an array to hold relay URLs
|
// Initialize an array to hold relay URLs
|
||||||
const relays: string[] = []
|
const relays: string[] = []
|
||||||
|
|
||||||
// Retrieve the user's public key and relay map from the Redux store
|
// Retrieve the user's public key and relay map from the Redux store
|
||||||
const usersPubkey = store.getState().auth.usersPubkey!
|
const usersPubkey = store.getState().auth.usersPubkey!
|
||||||
|
|
||||||
|
// Decryption can fail down in the code if extension options changed
|
||||||
|
// Forcefully log out the user if we detect missmatch between pubkeys
|
||||||
|
if (usersPubkey !== (await nostrController.capturePublicKey())) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
const relayMap = store.getState().relays?.map
|
const relayMap = store.getState().relays?.map
|
||||||
|
|
||||||
// Check if relayMap is undefined in the Redux store
|
// Check if relayMap is undefined in the Redux store
|
||||||
@ -450,9 +460,6 @@ export const getUsersAppData = async (): Promise<UserAppData | null> => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get an instance of the NostrController
|
|
||||||
const nostrController = NostrController.getInstance()
|
|
||||||
|
|
||||||
// Decrypt the encrypted content
|
// Decrypt the encrypted content
|
||||||
const decrypted = await nostrController
|
const decrypted = await nostrController
|
||||||
.nip04Decrypt(usersPubkey, encryptedContent)
|
.nip04Decrypt(usersPubkey, encryptedContent)
|
||||||
|
Loading…
Reference in New Issue
Block a user