Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Commit

Permalink
frontend: add avatar to drafts
Browse files Browse the repository at this point in the history
  • Loading branch information
horacioh committed Jul 11, 2024
1 parent 2c818c1 commit 1ea452f
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 179 deletions.
37 changes: 17 additions & 20 deletions frontend/apps/desktop/src/components/avatar-form.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import { Avatar } from '@/components/avatar'
import { useMyAccount_deprecated } from '@/models/accounts'
import { API_FILE_UPLOAD_URL } from '@shm/shared'
import { Stack, Tooltip } from '@shm/ui'
import { ChangeEvent } from 'react'
import {Avatar} from '@/components/avatar'
import {API_FILE_UPLOAD_URL} from '@shm/shared'
import {Stack, Tooltip} from '@shm/ui'
import {ChangeEvent} from 'react'
import appError from '../errors'

export function AvatarForm({
url,
disabled,
label,
id,
size = 140,
onAvatarUpload,
}: {
disabled?: boolean
label?: string
id?: string
url?: string
onAvatarUpload: (avatar: string) => Awaited<void>
size?: number
onAvatarUpload?: (avatar: string) => Awaited<void>
}) {
const account = useMyAccount_deprecated()
const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
const fileList = event.target.files
const file = fileList?.[0]
if (!file) return
handleUpload(file)
.then(() => { })
.then(() => {})
.catch((error) => {
appError(`Failed to upload avatar: ${e.message}`, { error })
appError(`Failed to upload avatar: ${e.message}`, {error})
})
.finally(() => {
event.target.value = ''
})
}

const handleUpload = async (file: File) => {
if (!onAvatarUpload) return null
const formData = new FormData()
formData.append('file', file)
const response = await fetch(API_FILE_UPLOAD_URL, {
Expand All @@ -43,18 +46,12 @@ export function AvatarForm({
await onAvatarUpload(data)
}
const avatarImage = (
<Avatar
label={account.data?.profile?.alias}
id={account.data?.id}
size={140}
url={url}
color="$blue12"
/>
<Avatar label={label} id={id} size={size} url={url} color="$blue12" />
)
if (disabled) return avatarImage
if (!onAvatarUpload) return avatarImage
return (
<Tooltip content="Click or Drag to Set Avatar Image">
<Stack hoverStyle={{ opacity: 0.7 }}>
<Stack hoverStyle={{opacity: 0.7}}>
<input
type="file"
onChange={handleFileChange}
Expand Down
2 changes: 2 additions & 0 deletions frontend/apps/desktop/src/models/documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ export function useDraftEditor({id}: {id: string | undefined}) {
deps: [],
metadata: {
name: input.name,
avatar: input.avatar,
},
members: {},
index: {},
Expand All @@ -608,6 +609,7 @@ export function useDraftEditor({id}: {id: string | undefined}) {
metadata: {
...input.draft.metadata,
name: input.name,
avatar: input.avatar,
},
signingAccount,
}
Expand Down
53 changes: 37 additions & 16 deletions frontend/apps/desktop/src/models/draft-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const draftMachine = setup({
types: {
context: {} as {
name: string
avatar: string
draft: null | HMDraft
document: null | HMDocument
errorMessage: string
Expand All @@ -16,7 +17,7 @@ export const draftMachine = setup({
hasChangedWhileSaving: boolean
},
events: {} as
| {type: 'CHANGE'; name?: string}
| {type: 'CHANGE'; name?: string; avatar?: string}
| {type: 'RESET.DRAFT'}
| {type: 'RESTORE.DRAFT'}
| {type: 'RESET.CORRUPT.DRAFT'}
Expand Down Expand Up @@ -59,6 +60,21 @@ export const draftMachine = setup({
return context.name
},
}),
setAvatar: assign({
avatar: ({context, event}) => {
if (event.type == 'GET.DRAFT.SUCCESS') {
if (event.draft) {
return event.draft.metadata.avatar
} else if (event.document) {
return event.document.metadata.avatar
}
}
if (event.type == 'CHANGE' && event.avatar) {
return event.avatar
}
return context.avatar
},
}),
setErrorMessage: assign({
errorMessage: ({context, event}) => {
if (event.type == 'GET.DRAFT.ERROR') {
Expand Down Expand Up @@ -100,6 +116,7 @@ export const draftMachine = setup({
id: 'Draft',
context: {
name: '',
avatar: '',
draft: null,
document: null,
errorMessage: '',
Expand All @@ -121,6 +138,7 @@ export const draftMachine = setup({
{type: 'setDraft'},
{type: 'setDocument'},
{type: 'setName'},
{type: 'setAvatar'},
],
},
],
Expand Down Expand Up @@ -153,9 +171,12 @@ export const draftMachine = setup({
on: {
CHANGE: {
target: 'changed',
actions: {
type: 'setName',
},
actions: [
{
type: 'setName',
},
{type: 'setAvatar'},
],
},
},
},
Expand All @@ -169,9 +190,12 @@ export const draftMachine = setup({
on: {
CHANGE: {
target: 'changed',
actions: {
type: 'setName',
},
actions: [
{
type: 'setName',
},
{type: 'setAvatar'},
],
reenter: true,
},
},
Expand Down Expand Up @@ -201,13 +225,15 @@ export const draftMachine = setup({
{
type: 'setName',
},
{type: 'setAvatar'},
],
reenter: false,
},
},
invoke: {
input: ({context}) => ({
name: context.name,
avatar: context.avatar,
currentDraft: context.draft,
}),
id: 'createOrUpdateDraft',
Expand All @@ -231,15 +257,10 @@ export const draftMachine = setup({
{
type: 'onSaveSuccess',
},
{
type: 'setDraft',
},
{
type: 'setName',
},
{
type: 'replaceRouteifNeeded',
},
{type: 'setDraft'},
{type: 'setName'},
{type: 'setAvatar'},
{type: 'replaceRouteifNeeded'},
{
type: 'setDraftStatus',
params: {status: 'saved'},
Expand Down
6 changes: 5 additions & 1 deletion frontend/apps/desktop/src/pages/account-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,11 @@ function AccountPageHeader() {
id={accountId}
size={60}
label={accountName}
url={getAvatarUrl(profile)}
url={
profile?.metadata.avatar
? getAvatarUrl(profile?.metadata.avatar)
: ''
}
/>
<SizableText
whiteSpace="nowrap"
Expand Down
20 changes: 18 additions & 2 deletions frontend/apps/desktop/src/pages/draft.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {AvatarForm} from '@/components/avatar-form'
import {HMEditorContainer, HyperMediaEditorView} from '@/components/editor'
import {MainWrapper} from '@/components/main-wrapper'
import {BlockNoteEditor, getBlockInfoFromPos} from '@/editor'
import {useDraftEditor} from '@/models/documents'
import {draftMachine} from '@/models/draft-machine'
import {trpc} from '@/trpc'
import {getAvatarUrl} from '@/utils/account-url'
import {
chromiumSupportedImageMimeTypes,
chromiumSupportedVideoMimeTypes,
Expand Down Expand Up @@ -32,8 +34,6 @@ import {AppDocContentProvider} from './document-content-provider'
export default function DraftPage() {
const route = useNavRoute()

console.log(`== ~ DraftPage ~ route:`, route)

const importWebFile = trpc.webImporting.importWebFile.useMutation()
const [isDragging, setIsDragging] = useState(false)
if (route.key != 'draft') throw new Error('DraftPage must have draft route')
Expand Down Expand Up @@ -238,11 +238,15 @@ export function DraftNameInput({
draftActor: ActorRefFrom<typeof draftMachine>
disabled?: boolean
}) {
const route = useNavRoute()
const {textUnit, layoutUnit} = useDocContentContext()
let headingTextStyles = useHeadingTextStyles(1, textUnit)
const name = useSelector(draftActor, (s) => {
return s.context.name
})
const avatar = useSelector(draftActor, (s) => {
return s.context.avatar
})
const input = useRef<HTMLTextAreaElement | null>(null)
const headingMarginStyles = useHeadingMarginStyles(2, layoutUnit)

Expand Down Expand Up @@ -289,7 +293,19 @@ export function DraftNameInput({
borderBottomWidth={1}
paddingHorizontal={54}
{...headingMarginStyles}
gap="$4"
>
<AvatarForm
size={80}
id={route.key == 'draft' ? route.id : 'document-avatar'}
label={name}
url={avatar ? getAvatarUrl(avatar) : ''}
onAvatarUpload={(avatar) => {
if (avatar) {
draftActor.send({type: 'CHANGE', avatar: `ipfs://${avatar}`})
}
}}
/>
<Input
disabled={disabled}
// we use multiline so that we can avoid horizontal scrolling for long titles
Expand Down
10 changes: 10 additions & 0 deletions frontend/apps/desktop/src/pages/home.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {useGRPCClient, useQueryInvalidator} from '@/app-context'
import {Avatar} from '@/components/avatar'
import {MainWrapper} from '@/components/main-wrapper'
import {useProfileWithDraft} from '@/models/accounts'
import {queryKeys} from '@/models/query-keys'
import {trpc} from '@/trpc'
import {getAvatarUrl} from '@/utils/account-url'
import {useOpenDraft} from '@/utils/open-draft'
import {useNavigate} from '@/utils/useNavigate'
import {Add, Button, Form, Input, toast} from '@shm/ui'
Expand Down Expand Up @@ -70,6 +72,14 @@ function AccountKeyItem({accountKey}: {accountKey: NamedKey}) {
return (
<XStack key={accountKey.accountId}>
<XStack f={1} ai="center" gap="$2">
<Avatar
size={40}
url={
profile?.metadata.avatar
? getAvatarUrl(profile.metadata.avatar)
: ''
}
/>
<YStack f={1}>
<p
style={{
Expand Down
7 changes: 4 additions & 3 deletions frontend/apps/desktop/src/utils/account-url.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {API_FILE_URL, HMDocument} from '@shm/shared'
import {API_FILE_URL} from '@shm/shared'

export function getAvatarUrl(profile: HMDocument | null | undefined) {
const image = profile?.metadata?.image
export function getAvatarUrl(image?: string) {
if (image) {
return `${API_FILE_URL}/${extractIpfsUrlCid(image)}`
}

return ''
}

function extractIpfsUrlCid(url: string): null | string {
Expand Down
Loading

0 comments on commit 1ea452f

Please sign in to comment.