Fix time display, fix file attachments not working properly after sending without refresh

This commit is contained in:
2025-08-21 14:06:37 +02:00
parent f2ac7d7209
commit 4dcacd0d73
7 changed files with 142 additions and 28 deletions

View File

@@ -18,7 +18,16 @@ export const uploadFile = async (req: Request, res: Response) => {
const result = await FileService.uploadFile(channelId, messageId, filePath, fileType!, fileSize!, originalName!);
logger.info(`File ${originalName} uploaded to message ${messageId} as ${filePath}`);
res.json({ id: result.lastInsertRowid, channelId, messageId, filePath, fileType });
res.json({
id: result.lastInsertRowid,
channel_id: parseInt(channelId),
message_id: parseInt(messageId),
file_path: filePath,
file_type: fileType,
file_size: fileSize,
original_name: originalName,
created_at: new Date().toISOString()
});
}

View File

@@ -11,11 +11,16 @@ export const uploadFile = async (channelId: string, messageId: string, filePath:
const result2 = updateQuery.run({ fileId: fileId, messageId: messageId });
events.emit('file-uploaded', result.lastInsertRowid, channelId, messageId, filePath, fileType, fileSize, originalName);
return result2; ''
return result;
}
export const getFiles = async (messageId: string) => {
const query = db.prepare(`SELECT * FROM files WHERE messageId = $messageId`);
// Get the file linked to this message via the fileId in the messages table
const query = db.prepare(`
SELECT files.* FROM files
JOIN messages ON messages.fileId = files.id
WHERE messages.id = $messageId
`);
const rows = query.all({ messageId: messageId });
return rows;
}

View File

@@ -34,6 +34,7 @@ interface Props {
const props = defineProps<Props>()
const fileExtension = computed(() => {
if (!props.file.original_name) return ''
return props.file.original_name.split('.').pop()?.toLowerCase() || ''
})

View File

@@ -20,8 +20,12 @@
</div>
<div class="message__meta">
<time v-if="!isUnsent && 'created_at' in message" class="message__time">
{{ formatTime(message.created_at) }}
<time
v-if="!isUnsent && 'created_at' in message"
class="message__time"
:datetime="message.created_at"
>
{{ formatSmartTimestamp(message.created_at) }}
</time>
<span v-else class="message__status">Sending...</span>
</div>
@@ -33,6 +37,7 @@ import { computed } from 'vue'
import { useAudio } from '@/composables/useAudio'
import { useToastStore } from '@/stores/toast'
import { useAppStore } from '@/stores/app'
import { formatSmartTimestamp, formatTimestampForScreenReader } from '@/utils/time'
import FileAttachment from './FileAttachment.vue'
import type { ExtendedMessage, UnsentMessage, FileAttachment as FileAttachmentType } from '@/types'
@@ -61,21 +66,30 @@ const hasFileAttachment = computed(() => {
const fileAttachment = computed((): FileAttachmentType | null => {
if (!hasFileAttachment.value || !('fileId' in props.message)) return null
// Check if we have the minimum required file metadata
if (!props.message.filePath || !props.message.originalName) {
console.warn('File attachment missing metadata:', {
fileId: props.message.fileId,
filePath: props.message.filePath,
originalName: props.message.originalName,
fileType: props.message.fileType
})
return null
}
return {
id: props.message.fileId!,
channel_id: props.message.channel_id,
message_id: props.message.id,
file_path: props.message.filePath!,
file_type: props.message.fileType!,
file_size: props.message.fileSize!,
file_type: props.message.fileType || 'application/octet-stream',
file_size: props.message.fileSize || 0,
original_name: props.message.originalName!,
created_at: props.message.fileCreatedAt || props.message.created_at
}
})
const formatTime = (timestamp: string): string => {
return new Date(timestamp).toLocaleTimeString()
}
// formatTime function removed - now using formatSmartTimestamp from utils
// Create comprehensive aria-label for screen readers
const messageAriaLabel = computed(() => {
@@ -95,8 +109,8 @@ const messageAriaLabel = computed(() => {
// Add timestamp
if ('created_at' in props.message && props.message.created_at) {
const time = formatTime(props.message.created_at)
label += `. Sent at ${time}`
const time = formatTimestampForScreenReader(props.message.created_at)
label += `. Sent ${time}`
}
// Add status for unsent messages

View File

@@ -58,7 +58,7 @@
{{ result.content }}
</div>
<div class="result-time">
{{ formatTime(result.created_at) }}
{{ formatSmartTimestamp(result.created_at) }}
</div>
</div>
</div>
@@ -79,6 +79,7 @@ import { ref, onMounted } from 'vue'
import { useAppStore } from '@/stores/app'
import { useToastStore } from '@/stores/toast'
import { apiService } from '@/services/api'
import { formatSmartTimestamp } from '@/utils/time'
import BaseInput from '@/components/base/BaseInput.vue'
import BaseButton from '@/components/base/BaseButton.vue'
import type { Message, ExtendedMessage } from '@/types'
@@ -140,16 +141,7 @@ const getChannelName = (channelId: number): string => {
return channel?.name || `Channel ${channelId}`
}
const formatTime = (timestamp: string): string => {
if (!timestamp) return 'Unknown time'
const date = new Date(timestamp)
if (isNaN(date.getTime())) {
return 'Invalid date'
}
return date.toLocaleString()
}
// formatTime function removed - now using formatSmartTimestamp from utils
onMounted(() => {
searchInput.value?.focus()

View File

@@ -0,0 +1,94 @@
/**
* Smart timestamp formatting that shows appropriate level of detail based on message age
*/
export function formatSmartTimestamp(timestamp: string): string {
const now = new Date()
const date = new Date(timestamp)
// Handle invalid dates
if (isNaN(date.getTime())) {
return 'Invalid date'
}
const diffMs = now.getTime() - date.getTime()
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
// Same day (today)
if (diffDays === 0) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}
// Yesterday
if (diffDays === 1) {
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
return `Yesterday ${timeStr}`
}
// This week (2-6 days ago)
if (diffDays <= 6) {
const dayStr = date.toLocaleDateString([], { weekday: 'short' })
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
return `${dayStr} ${timeStr}`
}
// This year (more than a week ago)
if (now.getFullYear() === date.getFullYear()) {
return date.toLocaleDateString([], {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
// Different year
return date.toLocaleDateString([], {
month: 'short',
day: 'numeric',
year: 'numeric'
})
}
/**
* Format timestamp for accessibility/screen readers with full context
*/
export function formatTimestampForScreenReader(timestamp: string): string {
const date = new Date(timestamp)
if (isNaN(date.getTime())) {
return 'Invalid date'
}
const now = new Date()
const diffMs = now.getTime() - date.getTime()
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24))
// Same day
if (diffDays === 0) {
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
return `today at ${timeStr}`
}
// Yesterday
if (diffDays === 1) {
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
return `yesterday at ${timeStr}`
}
// This week
if (diffDays <= 6) {
const dayStr = date.toLocaleDateString([], { weekday: 'long' })
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
return `${dayStr} at ${timeStr}`
}
// Older messages - use full date and time
return date.toLocaleDateString([], {
weekday: 'long',
month: 'long',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}

View File

@@ -130,6 +130,7 @@ import { useOfflineSync } from '@/composables/useOfflineSync'
import { useWebSocket } from '@/composables/useWebSocket'
import { useKeyboardShortcuts } from '@/composables/useKeyboardShortcuts'
import { useAudio } from '@/composables/useAudio'
import { formatTimestampForScreenReader } from '@/utils/time'
import { apiService } from '@/services/api'
import { syncService } from '@/services/sync'
@@ -364,9 +365,7 @@ const handleSelectMessage = async (message: ExtendedMessage) => {
}
}
const formatTime = (timestamp: string): string => {
return new Date(timestamp).toLocaleTimeString()
}
// formatTime function removed - now using formatTimestampForScreenReader from utils
const handleVoiceSent = () => {
// Voice message was sent successfully
@@ -396,8 +395,8 @@ const announceLastMessage = (position: number) => {
}
const message = messages[messageIndex]
const timeStr = formatTime(message.created_at)
const announcement = `${message.content}; ${timeStr}`
const timeStr = formatTimestampForScreenReader(message.created_at)
const announcement = `${message.content}; sent ${timeStr}`
toastStore.info(announcement)