chore: use-ndk #283

Merged
s merged 18 commits from use-ndk into staging 2025-01-06 11:10:49 +00:00
24 changed files with 131 additions and 202 deletions
Showing only changes of commit 95f5398736 - Show all commits

View File

@ -37,30 +37,19 @@ export const AppBar = () => {
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null)
const authState = useAppSelector((state) => state.auth)
const metadataState = useAppSelector((state) => state.metadata)
const userRobotImage = useAppSelector((state) => state.userRobotImage)
const userProfile = useAppSelector((state) => state.user.profile)
const userRobotImage = useAppSelector((state) => state.user.robotImage)
useEffect(() => {
if (metadataState) {
if (metadataState.content) {
const profileMetadata = JSON.parse(metadataState.content)
const { picture } = profileMetadata
if (picture || userRobotImage) {
setUserAvatar(picture || userRobotImage)
}
const npub = authState.usersPubkey
? hexToNpub(authState.usersPubkey)
: ''
setUsername(getProfileUsername(npub, profileMetadata))
} else {
setUserAvatar(userRobotImage || '')
setUsername('')
}
const npub = authState.usersPubkey ? hexToNpub(authState.usersPubkey) : ''
if (userProfile) {
setUserAvatar(userProfile.image || userRobotImage || '')
setUsername(getProfileUsername(npub, userProfile))
} else {
setUserAvatar('')
setUsername(getProfileUsername(npub))
}
}, [metadataState, userRobotImage, authState.usersPubkey])
}, [userRobotImage, authState.usersPubkey, userProfile])
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget)

View File

@ -679,7 +679,7 @@ export const DrawPDFFields = ({
renderValue={(value) => (
<Counterpart
npub={value}
metadata={userProfiles}
userProfiles={userProfiles}
signers={signers}
/>
)}

View File

@ -1,31 +1,32 @@
import React from 'react'
import { ProfileMetadata, User } from '../../../types'
import { User } from '../../../types'
import _ from 'lodash'
import { npubToHex, getProfileUsername } from '../../../utils'
import { AvatarIconButton } from '../../UserAvatarIconButton'
import styles from './Counterpart.module.scss'
import { NDKUserProfile } from '@nostr-dev-kit/ndk'
interface CounterpartProps {
npub: string
metadata: {
[key: string]: ProfileMetadata
userProfiles: {
[key: string]: NDKUserProfile
}
signers: User[]
}
export const Counterpart = React.memo(
({ npub, metadata, signers }: CounterpartProps) => {
({ npub, userProfiles, signers }: CounterpartProps) => {
let displayValue = _.truncate(npub, { length: 16 })
const signer = signers.find((u) => u.pubkey === npubToHex(npub))
if (signer) {
const signerMetadata = metadata[signer.pubkey]
displayValue = getProfileUsername(npub, signerMetadata)
const profile = userProfiles[signer.pubkey]
displayValue = getProfileUsername(npub, profile)
return (
<div className={styles.counterpartSelectValue}>
<AvatarIconButton
src={signerMetadata.picture}
src={profile.image}
Outdated
Review

Had this error during test on Create page: Uncaught TypeError: Cannot read properties of undefined (reading 'image') after selecting DrawnField rect.

Steps:

  1. Create a new sigit, add files, and after navigating to Create Page
  2. Add field with any tool
  3. Click the box - error
Had this error during test on Create page: `Uncaught TypeError: Cannot read properties of undefined (reading 'image')` after selecting `DrawnField` rect. Steps: 1. Create a new sigit, add files, and after navigating to `Create Page` 2. Add field with any tool 3. Click the box - error
Outdated
Review

I tested multiple times and it worked every time

I tested multiple times and it worked every time
hexKey={signer.pubkey || undefined}
sx={{
padding: 0,

View File

@ -1,18 +1,17 @@
import { Event, EventTemplate } from 'nostr-tools'
import { EventTemplate } from 'nostr-tools'
import { useCallback } from 'react'
import { NostrController } from '../controllers'
import { appPrivateRoutes } from '../routes'
import {
setAuthState,
setMetadataEvent,
setRelayMapAction
setRelayMapAction,
setUserProfile
} from '../store/actions'
import {
base64DecodeAuthToken,
compareObjects,
createAndSaveAuthToken,
getAuthToken,
getEmptyMetadataEvent,
getRelayMapFromNDKRelayList,
unixNow
} from '../utils'
@ -62,20 +61,11 @@ export const useAuth = () => {
*/
const authAndGetMetadataAndRelaysMap = useCallback(
async (pubkey: string) => {
const emptyMetadata = getEmptyMetadataEvent()
try {
const profile = await findMetadata(pubkey)
if (profile && profile.profileEvent) {
const event: Event = JSON.parse(profile.profileEvent)
dispatch(setMetadataEvent(event))
} else {
dispatch(setMetadataEvent(emptyMetadata))
}
dispatch(setUserProfile(profile))
} catch (err) {
console.warn('Error occurred while finding metadata', err)
dispatch(setMetadataEvent(emptyMetadata))
}
const timestamp = unixNow()

View File

@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import { Outlet, useNavigate, useSearchParams } from 'react-router-dom'
import { Event, getPublicKey, nip19 } from 'nostr-tools'
import { getPublicKey, nip19 } from 'nostr-tools'
import { init as initNostrLogin } from 'nostr-login'
import { NostrLoginAuthOptions } from 'nostr-login/dist/types'
@ -22,14 +22,14 @@ import {
import {
restoreState,
setMetadataEvent,
setUserProfile,
updateKeyPair,
updateLoginMethod,
updateNostrLoginAuthMethod,
updateUserAppData
updateUserAppData,
setUserRobotImage
} from '../store/actions'
import { LoginMethod } from '../store/auth/types'
import { setUserRobotImage } from '../store/userRobotImage/action'
import { getRoboHashPicture, loadState } from '../utils'
@ -165,17 +165,7 @@ export const MainLayout = () => {
if (!loginMethod || !usersPubkey) return logout()
findMetadata(usersPubkey).then((profile) => {
if (profile && profile.profileEvent) {
try {
const event: Event = JSON.parse(profile.profileEvent)
dispatch(setMetadataEvent(event))
} catch (error) {
console.error(
'An error occurred in parsing profile event from profile obj',
error
)
}
}
dispatch(setUserProfile(profile))
})
} else {
setIsLoading(false)

View File

@ -17,8 +17,7 @@ store.subscribe(
_.throttle(() => {
saveState({
auth: store.getState().auth,
metadata: store.getState().metadata,
userRobotImage: store.getState().userRobotImage,
user: store.getState().user,
relays: store.getState().relays
})
}, 1000)

View File

@ -1127,7 +1127,7 @@ export const CreatePage = () => {
key={option.pubkey}
>
<AvatarIconButton
src={contentJson.picture}
src={contentJson.picture || contentJson.image}
hexKey={option.pubkey}
color="inherit"
sx={{

View File

@ -22,7 +22,7 @@ import {
shorten
} from '../../utils'
import { NDKEvent, NDKUserProfile, profileFromEvent } from '@nostr-dev-kit/ndk'
import { NDKUserProfile } from '@nostr-dev-kit/ndk'
import { useNDKContext } from '../../hooks'
import styles from './style.module.scss'
@ -35,8 +35,8 @@ export const ProfilePage = () => {
const [pubkey, setPubkey] = useState<string>()
const [userProfile, setUserProfile] = useState<NDKUserProfile | null>(null)
const userRobotImage = useAppSelector((state) => state.userRobotImage)
const metadataState = useAppSelector((state) => state.metadata)
const userRobotImage = useAppSelector((state) => state.user.robotImage)
const currentUserProfile = useAppSelector((state) => state.user.profile)
const { usersPubkey } = useAppSelector((state) => state.auth)
const [isUsersOwnProfile, setIsUsersOwnProfile] = useState(false)
@ -58,12 +58,8 @@ export const ProfilePage = () => {
}, [npub, usersPubkey])
useEffect(() => {
if (isUsersOwnProfile && metadataState) {
const ndkEvent = new NDKEvent(ndk, metadataState)
const profile = profileFromEvent(ndkEvent)
setUserProfile(profile)
if (isUsersOwnProfile && currentUserProfile) {
setUserProfile(currentUserProfile)
setIsLoading(false)
return
@ -81,7 +77,7 @@ export const ProfilePage = () => {
setIsLoading(false)
})
}
}, [ndk, isUsersOwnProfile, metadataState, pubkey, findMetadata])
}, [ndk, isUsersOwnProfile, currentUserProfile, pubkey, findMetadata])
/**
* Rendering text with button which copies the provided text

View File

@ -17,12 +17,7 @@ import {
Tooltip
} from '@mui/material'
import {
NDKEvent,
NDKUserProfile,
profileFromEvent,
serializeProfile
} from '@nostr-dev-kit/ndk'
import { NDKEvent, NDKUserProfile, serializeProfile } from '@nostr-dev-kit/ndk'
import { launch as launchNostrLoginDialog } from 'nostr-login'
import { kinds, nip19, UnsignedEvent } from 'nostr-tools'
@ -37,7 +32,7 @@ import { Container } from '../../../components/Container'
import { Footer } from '../../../components/Footer/Footer'
import { LoadingSpinner } from '../../../components/LoadingSpinner'
import { setMetadataEvent } from '../../../store/actions'
import { setUserProfile as updateUserProfile } from '../../../store/actions'
import { LoginMethod, NostrLoginAuthMethod } from '../../../store/auth/types'
import { Dispatch } from '../../../store/store'
@ -52,8 +47,8 @@ export const ProfileSettingsPage = () => {
const [pubkey, setPubkey] = useState<string>()
const [userProfile, setUserProfile] = useState<NDKUserProfile | null>(null)
const userRobotImage = useAppSelector((state) => state.userRobotImage)
const metadataState = useAppSelector((state) => state.metadata)
const userRobotImage = useAppSelector((state) => state.user.robotImage)
const currentUserProfile = useAppSelector((state) => state.user.profile)
const keys = useAppSelector((state) => state.auth?.keyPair)
const { usersPubkey, loginMethod, nostrLoginAuthMethod } = useAppSelector(
(state) => state.auth
@ -80,11 +75,8 @@ export const ProfileSettingsPage = () => {
}, [npub, usersPubkey])
useEffect(() => {
if (isUsersOwnProfile && metadataState) {
const ndkEvent = new NDKEvent(ndk, metadataState)
const profile = profileFromEvent(ndkEvent)
setUserProfile(profile)
if (isUsersOwnProfile && currentUserProfile) {
setUserProfile(currentUserProfile)
setIsLoading(false)
@ -103,7 +95,7 @@ export const ProfileSettingsPage = () => {
setIsLoading(false)
})
}
}, [ndk, isUsersOwnProfile, metadataState, pubkey, findMetadata])
}, [ndk, isUsersOwnProfile, currentUserProfile, pubkey, findMetadata])
const editItem = (
key: keyof NDKUserProfile,
@ -208,7 +200,7 @@ export const ProfileSettingsPage = () => {
)}`
)
dispatch(setMetadataEvent(signedEvent))
dispatch(updateUserProfile(userProfile))
}
setSavingProfileMetadata(false)
@ -323,12 +315,12 @@ export const ProfileSettingsPage = () => {
/>
</ListItem>
{editItem('picture', 'Picture URL', undefined, undefined, {
{editItem('image', 'Picture URL', undefined, undefined, {
endAdornment: isUsersOwnProfile ? robohashButton() : undefined
})}
{editItem('name', 'Username')}
{editItem('display_name', 'Display Name')}
{editItem('displayName', 'Display Name')}
{editItem('nip05', 'Nostr Address (nip05)')}
{editItem('lud16', 'Lightning Address (lud16)')}
{editItem('about', 'About', true, 4)}

View File

@ -7,7 +7,7 @@ export const UPDATE_LOGIN_METHOD = 'UPDATE_LOGIN_METHOD'
export const UPDATE_NOSTR_LOGIN_AUTH_METHOD = 'UPDATE_NOSTR_LOGIN_AUTH_METHOD'
export const UPDATE_KEYPAIR = 'UPDATE_KEYPAIR'
export const SET_METADATA_EVENT = 'SET_METADATA_EVENT'
export const SET_USER_PROFILE = 'SET_USER_PROFILE'
export const SET_USER_ROBOT_IMAGE = 'SET_USER_ROBOT_IMAGE'

View File

@ -2,7 +2,7 @@ import * as ActionTypes from './actionTypes'
import { State } from './rootReducer'
export * from './auth/action'
export * from './metadata/action'
export * from './user/action'
export * from './relays/action'
export * from './userAppData/action'

View File

@ -1,8 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { SetMetadataEvent } from './types'
import { Event } from 'nostr-tools'
export const setMetadataEvent = (payload: Event): SetMetadataEvent => ({
type: ActionTypes.SET_METADATA_EVENT,
payload
})

View File

@ -1,25 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { MetadataDispatchTypes } from './types'
import { Event } from 'nostr-tools'
const initialState: Event | null = null
const reducer = (
state = initialState,
action: MetadataDispatchTypes
): Event | null => {
switch (action.type) {
case ActionTypes.SET_METADATA_EVENT:
return {
...action.payload
}
case ActionTypes.RESTORE_STATE:
return action.payload.metadata || initialState
default:
return state
}
}
export default reducer

View File

@ -1,10 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { Event } from 'nostr-tools'
import { RestoreState } from '../actions'
export interface SetMetadataEvent {
type: typeof ActionTypes.SET_METADATA_EVENT
payload: Event
}
export type MetadataDispatchTypes = SetMetadataEvent | RestoreState

View File

@ -1,37 +1,31 @@
import { Event } from 'nostr-tools'
import { combineReducers } from 'redux'
import { UserAppData } from '../types'
import * as ActionTypes from './actionTypes'
import authReducer from './auth/reducer'
import { AuthDispatchTypes, AuthState } from './auth/types'
import metadataReducer from './metadata/reducer'
import userReducer from './user/reducer'
import relaysReducer from './relays/reducer'
import { RelaysDispatchTypes, RelaysState } from './relays/types'
import UserAppDataReducer from './userAppData/reducer'
import userRobotImageReducer from './userRobotImage/reducer'
import { MetadataDispatchTypes } from './metadata/types'
import { UserAppDataDispatchTypes } from './userAppData/types'
import { UserRobotImageDispatchTypes } from './userRobotImage/types'
import { UserDispatchTypes, UserState } from './user/types'
export interface State {
auth: AuthState
metadata?: Event
userRobotImage?: string
user: UserState
relays: RelaysState
userAppData?: UserAppData
}
type AppActions =
| AuthDispatchTypes
| MetadataDispatchTypes
| UserRobotImageDispatchTypes
| UserDispatchTypes
| RelaysDispatchTypes
| UserAppDataDispatchTypes
export const appReducer = combineReducers({
auth: authReducer,
metadata: metadataReducer,
userRobotImage: userRobotImageReducer,
user: userReducer,
relays: relaysReducer,
userAppData: UserAppDataReducer
})

17
src/store/user/action.ts Normal file
View File

@ -0,0 +1,17 @@
import { NDKUserProfile } from '@nostr-dev-kit/ndk'
import * as ActionTypes from '../actionTypes'
import { SetUserProfile, SetUserRobotImage } from './types'
export const setUserRobotImage = (
payload: string | null
): SetUserRobotImage => ({
type: ActionTypes.SET_USER_ROBOT_IMAGE,
payload
})
export const setUserProfile = (
payload: NDKUserProfile | null
): SetUserProfile => ({
type: ActionTypes.SET_USER_PROFILE,
payload
})

34
src/store/user/reducer.ts Normal file
View File

@ -0,0 +1,34 @@
import * as ActionTypes from '../actionTypes'
import { UserDispatchTypes, UserState } from './types'
const initialState: UserState = {
robotImage: null,
profile: null
}
const reducer = (
state = initialState,
action: UserDispatchTypes
): UserState => {
switch (action.type) {
case ActionTypes.SET_USER_ROBOT_IMAGE:
return {
...state,
robotImage: action.payload
}
case ActionTypes.SET_USER_PROFILE:
return {
...state,
profile: action.payload
}
// case ActionTypes.RESTORE_STATE:
s marked this conversation as resolved Outdated
Outdated
Review

Are we intentionally skipping restoration of the user state?

Are we intentionally skipping restoration of the user state?
Outdated
Review

It wasn't intentional, fixed

It wasn't intentional, fixed
// return action.payload.
default:
return state
}
}
export default reducer

23
src/store/user/types.ts Normal file
View File

@ -0,0 +1,23 @@
import { NDKUserProfile } from '@nostr-dev-kit/ndk'
import * as ActionTypes from '../actionTypes'
import { RestoreState } from '../actions'
export interface UserState {
robotImage: string | null
profile: NDKUserProfile | null
}
export interface SetUserRobotImage {
type: typeof ActionTypes.SET_USER_ROBOT_IMAGE
payload: string | null
}
export interface SetUserProfile {
type: typeof ActionTypes.SET_USER_PROFILE
payload: NDKUserProfile | null
}
export type UserDispatchTypes =
| SetUserRobotImage
| SetUserProfile
| RestoreState

View File

@ -1,9 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { SetUserRobotImage } from './types'
export const setUserRobotImage = (
payload: string | null
): SetUserRobotImage => ({
type: ActionTypes.SET_USER_ROBOT_IMAGE,
payload
})

View File

@ -1,22 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { UserRobotImageDispatchTypes } from './types'
const initialState: string | null = null
const reducer = (
state = initialState,
action: UserRobotImageDispatchTypes
): string | null | undefined => {
switch (action.type) {
case ActionTypes.SET_USER_ROBOT_IMAGE:
return action.payload
case ActionTypes.RESTORE_STATE:
return action.payload.userRobotImage || initialState
default:
return state
}
}
export default reducer

View File

@ -1,9 +0,0 @@
import * as ActionTypes from '../actionTypes'
import { RestoreState } from '../actions'
export interface SetUserRobotImage {
type: typeof ActionTypes.SET_USER_ROBOT_IMAGE
payload: string | null
}
export type UserRobotImageDispatchTypes = SetUserRobotImage | RestoreState

View File

@ -1,7 +1,6 @@
export * from './cache'
export * from './core'
export * from './nostr'
export * from './profile'
export * from './relay'
export * from './zip'
export * from './event'

View File

@ -1,12 +0,0 @@
export interface ProfileMetadata {
name?: string
display_name?: string
/** @deprecated use name instead */
username?: string
picture?: string
banner?: string
about?: string
website?: string
nip05?: string
lud16?: string
}

View File

@ -1,5 +1,5 @@
import { hexToBytes } from '@noble/hashes/utils'
import { NDKEvent } from '@nostr-dev-kit/ndk'
import { NDKEvent, NDKUserProfile } from '@nostr-dev-kit/ndk'
import axios from 'axios'
import { truncate } from 'lodash'
import {
@ -18,7 +18,7 @@ import {
import { toast } from 'react-toastify'
import { NIP05_REGEX } from '../constants'
import store from '../store/store'
import { Meta, ProfileMetadata, SignedEvent } from '../types'
import { Meta, SignedEvent } from '../types'
import { SIGIT_BLOSSOM } from './const.ts'
import { getHash } from './hash'
import { parseJson, removeLeadingSlash } from './string'
@ -497,9 +497,9 @@ export const getUserAppDataFromBlossom = async (
*/
export const getProfileUsername = (
npub: `npub1${string}` | string,
profile?: ProfileMetadata // todo: use NDKUserProfile
profile?: NDKUserProfile
) =>
truncate(profile?.display_name || profile?.name || hexToNpub(npub), {
truncate(profile?.displayName || profile?.name || hexToNpub(npub), {
length: 16
})