fix: when decrypting file, have better error messages #17
@ -1,80 +0,0 @@
|
||||
import axios, { AxiosError, AxiosInstance } from "axios"
|
||||
import { PostMediaResponse } from "../types/media"
|
||||
import sha256 from 'crypto-js/sha256'
|
||||
import Hex from 'crypto-js/enc-hex'
|
||||
import { NostrController } from "."
|
||||
|
||||
export class ApiController {
|
||||
private mediaUrl: string = 'https://media.nquiz.io'
|
||||
private api: AxiosInstance
|
||||
private baseUrl: string = ''
|
||||
|
||||
private nostrController = NostrController.getInstance()
|
||||
|
||||
constructor(baseUrl?: string) {
|
||||
this.baseUrl = baseUrl ?? this.baseUrl
|
||||
|
||||
this.api = axios.create({
|
||||
withCredentials: true,
|
||||
baseURL: this.baseUrl,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
uploadMediaImage = async (
|
||||
file: Buffer,
|
||||
): Promise<PostMediaResponse> => {
|
||||
const mediaUrl = this.mediaUrl
|
||||
|
||||
const formData = new FormData()
|
||||
const imageBlob = new Blob([file])
|
||||
formData.append('mediafile', imageBlob)
|
||||
|
||||
const endpointUrl = 'v1/media'
|
||||
const tags = [
|
||||
['u', `${mediaUrl}/${endpointUrl}`],
|
||||
['method', 'POST'],
|
||||
['payload', this.hashString('{}')]
|
||||
]
|
||||
|
||||
const authToken = await this.nostrController.createNostrHttpAuthToken(tags)
|
||||
|
||||
console.info('Auth token created for media server.', authToken)
|
||||
|
||||
return axios
|
||||
.post<PostMediaResponse>(`${mediaUrl}/api/v1/media`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Nostr ${authToken}`
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
console.info(
|
||||
`POST ${mediaUrl}/api/v1/media | res.data:`,
|
||||
res.data
|
||||
)
|
||||
// Create correct api url, because res.url is not including 'api/v1/'
|
||||
if (!res || !res.data) {
|
||||
console.warn('Image upload: no data returned after upload')
|
||||
}
|
||||
|
||||
const data = res.data
|
||||
data.directLink = `${data.url.replace(mediaUrl, `${mediaUrl}/api/v1`)}`
|
||||
|
||||
return data
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
console.info(`POST ${mediaUrl}/api/v1/media | err:`, err)
|
||||
throw {
|
||||
code: 500,
|
||||
message: err.response?.data || err.response || err.message || err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
hashString = (payload: any): string => {
|
||||
return sha256(payload).toString(Hex)
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
||||
import {
|
||||
CircularProgress,
|
||||
IconButton,
|
||||
InputProps,
|
||||
List,
|
||||
ListItem,
|
||||
ListSubheader,
|
||||
TextField,
|
||||
Tooltip,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material'
|
||||
@ -23,8 +26,7 @@ import { Dispatch } from '../../store/store'
|
||||
import { setMetadataEvent } from '../../store/actions'
|
||||
import { LoadingSpinner } from '../../components/LoadingSpinner'
|
||||
import { LoginMethods } from '../../store/auth/types'
|
||||
import { ApiController } from '../../controllers/ApiController'
|
||||
import { PostMediaResponse } from '../../types/media'
|
||||
import { SmartToy } from '@mui/icons-material'
|
||||
|
||||
export const ProfilePage = () => {
|
||||
const theme = useTheme()
|
||||
@ -45,7 +47,6 @@ export const ProfilePage = () => {
|
||||
const metadataState = useSelector((state: State) => state.metadata)
|
||||
const keys = useSelector((state: State) => state.auth?.keyPair)
|
||||
const { usersPubkey, loginMethod } = useSelector((state: State) => state.auth)
|
||||
const [uploadingImage, setUploadingImage] = useState<boolean>()
|
||||
|
||||
const [isUsersOwnProfile, setIsUsersOwnProfile] = useState(false)
|
||||
|
||||
@ -118,7 +119,8 @@ export const ProfilePage = () => {
|
||||
key: keyof ProfileMetadata,
|
||||
label: string,
|
||||
multiline = false,
|
||||
rows = 1
|
||||
rows = 1,
|
||||
inputProps?: InputProps
|
||||
) => (
|
||||
<ListItem sx={{ marginTop: 1 }}>
|
||||
<TextField
|
||||
@ -130,6 +132,7 @@ export const ProfilePage = () => {
|
||||
rows={rows}
|
||||
className={styles.textField}
|
||||
disabled={!isUsersOwnProfile}
|
||||
InputProps={inputProps}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target
|
||||
|
||||
@ -208,41 +211,6 @@ export const ProfilePage = () => {
|
||||
setSavingProfileMetadata(false)
|
||||
}
|
||||
|
||||
const onProfileImageChange = (event: any) => {
|
||||
const file = event?.target?.files[0]
|
||||
|
||||
if (file) {
|
||||
setUploadingImage(true)
|
||||
|
||||
const apiController = new ApiController()
|
||||
|
||||
apiController.uploadMediaImage(file).then((imageResponse: PostMediaResponse) => {
|
||||
if (imageResponse && imageResponse.directLink) {
|
||||
setTimeout(() => {
|
||||
setProfileMetadata((prev) => ({
|
||||
...prev,
|
||||
picture: imageResponse.directLink
|
||||
}))
|
||||
}, 200)
|
||||
} else {
|
||||
toast.error(`Uploading image failed. Please try again.`)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Error uploading image to media server.', err)
|
||||
})
|
||||
.finally(() => {
|
||||
setUploadingImage(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const progressSpinner = (show = false) => {
|
||||
if (!show) return undefined
|
||||
|
||||
return <CircularProgress size={20} />
|
||||
}
|
||||
|
||||
const generateRobotAvatar = () => {
|
||||
setAvatarLoading(true)
|
||||
|
||||
@ -261,6 +229,21 @@ export const ProfilePage = () => {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns robohash generate button, loading spinner or no button
|
||||
*/
|
||||
const robohashButton = () => {
|
||||
if (profileMetadata?.picture?.includes('robohash')) return null
|
||||
|
||||
return <Tooltip title="Generate a robohash avatar">
|
||||
{avatarLoading ? <CircularProgress style={{padding: 8}} size={22}/>
|
||||
: <IconButton onClick={generateRobotAvatar}>
|
||||
<SmartToy/>
|
||||
</IconButton>}
|
||||
</Tooltip>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
|
||||
@ -304,20 +287,6 @@ export const ProfilePage = () => {
|
||||
alt='Profile Image'
|
||||
/>
|
||||
|
||||
<LoadingButton onClick={generateRobotAvatar} loading={avatarLoading}>Generate Robot Avatar</LoadingButton>
|
||||
|
||||
<TextField
|
||||
onChange={onProfileImageChange}
|
||||
type="file"
|
||||
size="small"
|
||||
label="Profile Image"
|
||||
className={styles.textField}
|
||||
InputProps={{
|
||||
endAdornment: progressSpinner(uploadingImage)
|
||||
}}
|
||||
focused
|
||||
/>
|
||||
|
||||
{nostrJoiningBlock && (
|
||||
<Typography
|
||||
sx={{
|
||||
@ -334,6 +303,10 @@ export const ProfilePage = () => {
|
||||
)}
|
||||
</ListItem>
|
||||
|
||||
{editItem('picture', 'Picture URL', undefined, undefined, {
|
||||
endAdornment: robohashButton()
|
||||
})}
|
||||
|
||||
{editItem('name', 'Username')}
|
||||
{editItem('display_name', 'Display Name')}
|
||||
{editItem('nip05', 'Nostr Address (nip05)')}
|
||||
|
@ -1,19 +0,0 @@
|
||||
export interface PostMediaResponse {
|
||||
result: boolean
|
||||
description: string
|
||||
status: string
|
||||
id: number
|
||||
pubkey: string
|
||||
url: string
|
||||
directLink: string
|
||||
hash: string
|
||||
magnet: string
|
||||
tags: any[]
|
||||
}
|
||||
|
||||
export interface GetMediaResponse extends PostMediaResponse {}
|
||||
|
||||
export interface MediaErrorResponse {
|
||||
description: string
|
||||
result: boolean
|
||||
}
|
Loading…
Reference in New Issue
Block a user