Skip to content

Commit

Permalink
add working basic avatar upload, add tabs
Browse files Browse the repository at this point in the history
  • Loading branch information
natew committed Dec 11, 2024
1 parent e27a314 commit 3209db2
Show file tree
Hide file tree
Showing 8 changed files with 1,778 additions and 57 deletions.
57 changes: 57 additions & 0 deletions apps/onechat/app/api/image/upload+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { S3Client } from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'

const endpoint = 'https://aa20b480cc813f2131bc005e2b7fd140.r2.cloudflarestorage.com/onechatimages'
const bucket = 'onechatimages'
const folder = 'uploads'

const s3Client = new S3Client({
credentials: {
accessKeyId: process.env.CLOUDFLARE_R2_ACCESS_KEY!,
secretAccessKey: process.env.CLOUDFLARE_R2_SECRET_KEY!,
},
endpoint,
region: 'auto',
forcePathStyle: true, // Important for bucket compatibility with R2
})

export default async function handler(req: Request) {
if (req.method !== 'POST') {
return Response.json({ error: 'Method not allowed' }, { status: 405 })
}

const formData = await req.formData()
const file = formData.get('file')
if (!file || !(file instanceof File)) {
return Response.json({ error: 'Failed to parse the form' }, { status: 500 })
}

const fileStream = file.stream()

const uploadParams = {
Bucket: bucket,
Key: `${folder}/${file.name}`,
Body: fileStream,
ContentType: file.type,
}

try {
const uploader = new Upload({
client: s3Client,
params: uploadParams,
})

await uploader.done()

const key = uploadParams.Key
const url = `https://one1.dev/${bucket}/${key}`

return new Response(JSON.stringify({ message: 'File uploaded successfully', key, url }), {
status: 200,
})
} catch (error) {
return new Response(JSON.stringify({ error: 'Failed to upload file', details: `${error}` }), {
status: 500,
})
}
}
43 changes: 34 additions & 9 deletions apps/onechat/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { MessageCircle, Reply, Smile, UserCircle } from '@tamagui/lucide-icons'
import {
IndentIncrease,
MessageCircle,
MessageCircleCode,
MoreVertical,
Reply,
ReplyAll,
Smile,
UserCircle,
} from '@tamagui/lucide-icons'
import { memo, useEffect, useRef, useState } from 'react'
import {
Button,
Expand All @@ -12,6 +21,7 @@ import {
SizableText,
styled,
TamaguiElement,
TooltipSimple,
XGroup,
XStack,
YStack,
Expand Down Expand Up @@ -575,17 +585,32 @@ const MessageItem = ({
$group-message-hover={{ o: 1 }}
>
<XGroup bg="$color2">
<Button chromeless size="$3">
<Smile size={16} />
<Button chromeless size="$2.5">
🙏
</Button>
<Button chromeless size="$3">
<Smile size={16} />
<Button chromeless size="$2.5">
🥺
</Button>
<Button chromeless size="$3">
<Smile size={16} />
<Button chromeless size="$2.5">
😊
</Button>
<Button chromeless size="$3">
<Reply size={16} />

<Separator my="$2" vertical />

<TooltipSimple label="Create Thread">
<Button chromeless size="$2.5" br={0}>
<IndentIncrease size={16} />
</Button>
</TooltipSimple>

<TooltipSimple label="Quote Reply">
<Button chromeless size="$2.5" br={0}>
<Reply size={16} />
</Button>
</TooltipSimple>

<Button chromeless size="$2.5">
<MoreVertical size={16} />
</Button>
</XGroup>
</XStack>
Expand Down
121 changes: 80 additions & 41 deletions apps/onechat/interface/dialogs/DialogCreateServer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useState } from 'react'
import { Button, Circle, Dialog, H3, Input, ScrollView, Separator, XStack, YStack } from 'tamagui'
import { createEmitter } from '~/helpers/emitter'
// import { Button } from '../Button'
import { DialogContent, dialogEmitter, DialogOverlay, useDialogEmitter } from './shared'
import { LabeledRow } from '../forms/LabeledRow'
import { DialogContent, dialogEmitter, DialogOverlay, useDialogEmitter } from './shared'
import { Tabs } from '../tabs/Tabs'

const [dialogCreateServerEmitter] = createEmitter<boolean>()

Expand All @@ -26,60 +26,99 @@ export const DialogCreateServer = () => {
useDialogEmitter((next) => {
if (next.type === 'create-server') {
setShow(true)
} else {
setShow(false)
}
})

return (
<Dialog modal open={!!show}>
<Dialog modal open={show}>
<Dialog.Portal>
<DialogOverlay
key="overlay"
onPress={() => {
setShow(false)
}}
/>

<DialogContent>
<YStack f={1}>
<YStack gap="$2">
<H3>Create Server</H3>
<Separator />
</YStack>
<DialogContent key="content">
<Tabs
initialTab="create"
tabs={[
{ label: 'Create', value: 'create' },
{ label: 'Join', value: 'join' },
]}
>
<Tabs.Content value="create">
<DialogCreateServerContent setShow={setShow} />
</Tabs.Content>

<Tabs.Content value="join">
<DialogJoinServerContent setShow={setShow} />
</Tabs.Content>
</Tabs>
</DialogContent>
</Dialog.Portal>
</Dialog>
)
}

type ContentProps = {
setShow: React.Dispatch<React.SetStateAction<boolean>>
}

const DialogCreateServerContent = (props: ContentProps) => {
return (
<>
<YStack f={1}>
<ScrollView>
<YStack py="$4" gap="$2">
<LabeledRow label="Name" htmlFor="name">
<Input f={1} id="name" />
</LabeledRow>

<ScrollView>
<YStack py="$4" gap="$2">
<LabeledRow label="Name" htmlFor="name">
<Input f={1} id="name" />
</LabeledRow>
<LabeledRow label="Avatar" htmlFor="avatar">
<Circle size={100} bg="$color5" />
</LabeledRow>

<LabeledRow label="Avatar" htmlFor="avatar">
<Circle size={100} bg="$color5" />
</LabeledRow>
</YStack>
</ScrollView>
<form action="/api/image/upload" method="post" enctype="multipart/form-data">
<label for="file">Choose file to upload:</label>
<input type="file" id="file" name="file" required />
<button type="submit">Upload File</button>
</form>
</YStack>
</ScrollView>
</YStack>

<XStack jc="flex-end" gap="$2">
<Dialog.Close asChild>
<Button
onPress={() => {
setShow(false)
}}
>
Cancel
</Button>
</Dialog.Close>
<XStack jc="flex-end" gap="$2">
<Dialog.Close asChild>
<Button
onPress={() => {
props.setShow(false)
}}
>
Cancel
</Button>
</Dialog.Close>

<Button
theme="active"
onPress={() => {
setShow(false)
}}
>
Accept
</Button>
</XStack>
</DialogContent>
</Dialog.Portal>
</Dialog>
<Button
theme="active"
onPress={() => {
props.setShow(false)
}}
>
Accept
</Button>
</XStack>
</>
)
}
const DialogJoinServerContent = (props: ContentProps) => {
return (
<>
<YStack gap="$2">
<Input size="$5" autoFocus />
</YStack>
</>
)
}
3 changes: 1 addition & 2 deletions apps/onechat/interface/dialogs/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ export const DialogContent = styled(Dialog.Content, {
bordered: true,
elevate: true,
bg: '$color2',
key: 'content',
w: '60%',
h: '50%',
miw: 200,
maw: 500,
mih: 300,
mih: 400,
mah: 'max-content',
enterStyle: { x: 0, y: -10, opacity: 0 },
exitStyle: { x: 0, y: 10, opacity: 0 },
Expand Down
Loading

0 comments on commit 3209db2

Please sign in to comment.