Skip to content

Commit

Permalink
Merge pull request #4282 from janhq/fix/message-attachments-preview
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-jan authored Dec 17, 2024
2 parents 14b1e61 + 3a9c999 commit 0cd0ff0
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 95 deletions.
4 changes: 2 additions & 2 deletions core/src/browser/extensions/engines/OAIEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export abstract class OAIEngine extends AIEngine {
role: ChatCompletionRole.Assistant,
content: [],
status: MessageStatus.Pending,
created: timestamp,
updated: timestamp,
created_at: timestamp,
completed_at: timestamp,
object: 'thread.message',
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/node/api/restful/helper/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,8 @@ export const createMessage = async (threadId: string, message: any) => {
id: msgId,
thread_id: threadId,
status: MessageStatus.Ready,
created: createdAt,
updated: createdAt,
created_at: createdAt,
completed_at: createdAt,
object: 'thread.message',
role: message.role,
content: [
Expand Down
4 changes: 2 additions & 2 deletions core/src/types/message/messageEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ export type ThreadMessage = {
/** The status of this message. **/
status: MessageStatus
/** The timestamp indicating when this message was created. Represented in Unix time. **/
created: number
created_at: number
/** The timestamp indicating when this message was updated. Represented in Unix time. **/
updated: number
completed_at: number
/** The additional metadata of this message. **/
metadata?: Record<string, unknown>

Expand Down
26 changes: 19 additions & 7 deletions web/hooks/usePath.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { openFileExplorer, joinPath, baseName } from '@janhq/core'
import { openFileExplorer, joinPath, baseName, fs } from '@janhq/core'

Check warning on line 1 in web/hooks/usePath.ts

View workflow job for this annotation

GitHub Actions / test-on-macos

'fs' is defined but never used

Check warning on line 1 in web/hooks/usePath.ts

View workflow job for this annotation

GitHub Actions / test-on-ubuntu

'fs' is defined but never used

Check warning on line 1 in web/hooks/usePath.ts

View workflow job for this annotation

GitHub Actions / coverage-check

'fs' is defined but never used
import { useAtomValue } from 'jotai'

import { getFileInfo } from '@/utils/file'

import { janDataFolderPathAtom } from '@/helpers/atoms/AppConfig.atom'
import { activeAssistantAtom } from '@/helpers/atoms/Assistant.atom'
import { selectedModelAtom } from '@/helpers/atoms/Model.atom'
Expand Down Expand Up @@ -47,13 +49,23 @@ export const usePath = () => {
const onViewFile = async (id: string) => {
if (!activeThread) return

let filePath = undefined

id = await baseName(id)
filePath = await joinPath(['threads', `${activeThread.id}/files`, `${id}`])
if (!filePath) return
const fullPath = await joinPath([janDataFolderPath, filePath])
openFileExplorer(fullPath)

// New ID System
if (!id.startsWith('file-')) {
const threadFilePath = await joinPath([
janDataFolderPath,
'threads',
`${activeThread.id}/files`,
id,
])
openFileExplorer(threadFilePath)
} else {
id = id.split('.')[0]
const fileName = (await getFileInfo(id)).filename
const filesPath = await joinPath([janDataFolderPath, 'files', fileName])
openFileExplorer(filesPath)
}
}

const onViewFileContainer = async () => {
Expand Down
2 changes: 1 addition & 1 deletion web/hooks/useSendChatMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export default function useSendChatMessage() {
// Update thread state
const updatedThread: Thread = {
...activeThreadRef.current,
updated: newMessage.created,
updated: newMessage.created_at,
metadata: {
...activeThreadRef.current.metadata,
lastMessage: prompt,
Expand Down
2 changes: 1 addition & 1 deletion web/screens/Thread/ThreadCenterPanel/ChatInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ const ChatInput = () => {
const renderPreview = (fileUpload: any) => {
if (fileUpload) {
if (fileUpload.type === 'image') {
return <ImageUploadPreview file={fileUpload[0].file} />
return <ImageUploadPreview file={fileUpload.file} />
} else {
return <FileUploadPreview />
}
Expand Down
47 changes: 21 additions & 26 deletions web/screens/Thread/ThreadCenterPanel/TextMessage/DocMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
import { memo } from 'react'

import { Tooltip } from '@janhq/joi'

import { FolderOpenIcon } from 'lucide-react'
import { memo, useEffect, useState } from 'react'

import { usePath } from '@/hooks/usePath'

import { toGibibytes } from '@/utils/converter'
import { openFileTitle } from '@/utils/titleUtils'
import { getFileInfo } from '@/utils/file'

import Icon from '../FileUploadPreview/Icon'

const DocMessage = ({ id, name }: { id: string; name?: string }) => {
const { onViewFile, onViewFileContainer } = usePath()
const DocMessage = ({ id }: { id: string }) => {
const { onViewFile } = usePath()
const [fileInfo, setFileInfo] = useState<
{ filename: string; id: string } | undefined
>()
useEffect(() => {
if (!fileInfo) {
getFileInfo(id).then((data) => {
setFileInfo(data)
})
}
}, [fileInfo, id])

return (
<div className="group/file bg-secondary relative mb-2 inline-flex w-60 cursor-pointer gap-x-3 overflow-hidden rounded-lg p-4">
<div
className="absolute left-0 top-0 z-20 hidden h-full w-full bg-black/20 backdrop-blur-sm group-hover/file:inline-block"
className="absolute left-0 top-0 z-20 hidden h-full w-full bg-black/20 opacity-50 group-hover/file:inline-block"
onClick={() => onViewFile(`${id}.pdf`)}
/>
<Tooltip
trigger={
<div
className="absolute right-2 top-2 z-20 hidden h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-[hsla(var(--app-bg))] group-hover/file:flex"
onClick={onViewFileContainer}
>
<FolderOpenIcon size={20} />
</div>
}
content={<span>{openFileTitle()}</span>}
/>

<Icon type="pdf" />
<div className="w-full">
<h6 className="line-clamp-1 w-4/5 font-medium">
{name?.replaceAll(/[-._]/g, ' ')}
<h6 className="line-clamp-1 w-4/5 overflow-hidden font-medium">
{fileInfo?.filename}
</h6>
{/* <p className="text-[hsla(var(--text-secondary)]">
{toGibibytes(Number(size))}
</p> */}
<p className="text-[hsla(var(--text-secondary)] line-clamp-1 overflow-hidden truncate">
{fileInfo?.id ?? id}
</p>
</div>
</div>
)
Expand Down
27 changes: 2 additions & 25 deletions web/screens/Thread/ThreadCenterPanel/TextMessage/ImageMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,11 @@
import { memo } from 'react'

import { Tooltip } from '@janhq/joi'

import { FolderOpenIcon } from 'lucide-react'

import { usePath } from '@/hooks/usePath'

import { openFileTitle } from '@/utils/titleUtils'

import { RelativeImage } from '../TextMessage/RelativeImage'

const ImageMessage = ({ image }: { image: string }) => {
const { onViewFile, onViewFileContainer } = usePath()

return (
<div className="group/image relative mb-2 inline-flex cursor-pointer overflow-hidden rounded-xl">
<div className="left-0 top-0 z-20 h-full w-full group-hover/image:inline-block">
<RelativeImage src={image} onClick={() => onViewFile(image)} />
</div>
<Tooltip
trigger={
<div
className="absolute right-2 top-2 z-20 hidden h-8 w-8 cursor-pointer items-center justify-center rounded-md bg-[hsla(var(--app-bg))] group-hover/image:flex"
onClick={onViewFileContainer}
>
<FolderOpenIcon size={20} />
</div>
}
content={<span>{openFileTitle()}</span>}
/>
<div className="group/file relative mb-2 inline-flex overflow-hidden rounded-xl">
<RelativeImage src={image} />
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const RelativeImage = ({
onClick,
}: {
src: string
onClick: () => void
onClick?: () => void
}) => {
const [path, setPath] = useState<string>('')

Expand All @@ -17,9 +17,12 @@ export const RelativeImage = ({
})
}, [])
return (
<button onClick={onClick}>
<button
onClick={onClick}
className={onClick ? 'cursor-pointer' : 'cursor-default'}
>
<img
className="aspect-auto h-[300px] cursor-pointer"
className="aspect-auto h-[300px]"
alt={src}
src={src.includes('files/') ? `file://${path}/${src}` : src}
/>
Expand Down
6 changes: 4 additions & 2 deletions web/screens/Thread/ThreadCenterPanel/TextMessage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const MessageContainer: React.FC<
: (activeAssistant?.assistant_name ?? props.role)}
</div>
<p className="text-xs font-medium text-gray-400">
{props.created && displayDate(props.created ?? new Date())}
{props.created_at && displayDate(props.created_at ?? new Date())}
</p>
</div>

Expand Down Expand Up @@ -125,7 +125,9 @@ const MessageContainer: React.FC<
>
<>
{image && <ImageMessage image={image} />}
{attachedFile && <DocMessage id={props.id} name={props.id} />}
{attachedFile && (
<DocMessage id={props.attachments?.[0]?.file_id ?? props.id} />
)}

{editMessage === props.id ? (
<div>
Expand Down
39 changes: 20 additions & 19 deletions web/utils/datetime.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import { displayDate } from './datetime'
import { isToday } from './datetime'

import { displayDate } from './datetime';
import { isToday } from './datetime';

test('should return only time for today\'s timestamp', () => {
const today = new Date();
const timestamp = today.getTime();
const expectedTime = today.toLocaleTimeString(undefined, {
test("should return only time for today's timestamp", () => {
const today = new Date()
const timestamp = today.getTime()
const expectedTime = `${today.toLocaleDateString(undefined, {
day: '2-digit',
month: 'short',
year: 'numeric',
})}, ${today.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: true,
});
expect(displayDate(timestamp)).toBe(expectedTime);
});

})}`
expect(displayDate(timestamp / 1000)).toBe(expectedTime)
})

test('should return N/A for undefined timestamp', () => {
expect(displayDate()).toBe('N/A');
});

expect(displayDate()).toBe('N/A')
})

test('should return true for today\'s timestamp', () => {
const today = new Date();
const timestamp = today.setHours(0, 0, 0, 0);
expect(isToday(timestamp)).toBe(true);
});
test("should return true for today's timestamp", () => {
const today = new Date()
const timestamp = today.setHours(0, 0, 0, 0)
expect(isToday(timestamp)).toBe(true)
})
5 changes: 4 additions & 1 deletion web/utils/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ export const isToday = (timestamp: number) => {
export const displayDate = (timestamp?: string | number | Date) => {
if (!timestamp) return 'N/A'

const date = new Date(timestamp)
const date =
typeof timestamp === 'number'
? new Date(timestamp * 1000)
: new Date(timestamp)

let displayDate = `${date.toLocaleDateString(undefined, {
day: '2-digit',
Expand Down
9 changes: 9 additions & 0 deletions web/utils/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,12 @@ export const uploader = () => {
})
return uppy
}

/**
* Get the file information from the server.
*/
export const getFileInfo = (id: string) => {
return fetch(`${API_BASE_URL}/v1/files/${id}`)
.then((e) => e.json())
.catch(() => undefined)
}
4 changes: 2 additions & 2 deletions web/utils/threadMessageBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ describe('ThreadMessageBuilder', () => {
expect(result.thread_id).toBe(msgRequest.thread.id)
expect(result.role).toBe(ChatCompletionRole.User)
expect(result.status).toBe(MessageStatus.Ready)
expect(result.created).toBeDefined()
expect(result.updated).toBeDefined()
expect(result.created_at).toBeDefined()
expect(result.completed_at).toBeDefined()
expect(result.object).toBe('thread.message')
expect(result.content).toEqual([])
})
Expand Down
4 changes: 2 additions & 2 deletions web/utils/threadMessageBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export class ThreadMessageBuilder {
attachments: this.attachments,
role: ChatCompletionRole.User,
status: MessageStatus.Ready,
created: timestamp,
updated: timestamp,
created_at: timestamp,
completed_at: timestamp,
object: 'thread.message',
content: this.content,
}
Expand Down

0 comments on commit 0cd0ff0

Please sign in to comment.