Repair: copy legacy joinDate into new user's created_at when creating users from legacy wallz

This commit is contained in:
2026-03-22 09:13:39 +01:00
parent e8b5edf5d2
commit 2608be7420
80 changed files with 3991 additions and 723 deletions

View File

@@ -61,10 +61,12 @@ function buildSearchPreview(item) {
function MessagesPage({ userId, username, activeConversationId: initialId }) {
const [conversations, setConversations] = useState([])
const [unreadTotal, setUnreadTotal] = useState(null)
const [loadingConvs, setLoadingConvs] = useState(true)
const [activeId, setActiveId] = useState(initialId ?? null)
const [realtimeEnabled, setRealtimeEnabled] = useState(false)
const [realtimeStatus, setRealtimeStatus] = useState('offline')
const [onlineUserIds, setOnlineUserIds] = useState([])
const [typingByConversation, setTypingByConversation] = useState({})
const [showNewModal, setShowNewModal] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
@@ -75,6 +77,7 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
try {
const data = await apiFetch('/api/messages/conversations')
setConversations(data.data ?? [])
setUnreadTotal(Number.isFinite(Number(data?.summary?.unread_total)) ? Number(data.summary.unread_total) : null)
} catch (e) {
console.error('Failed to load conversations', e)
} finally {
@@ -173,6 +176,11 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
}
setConversations((prev) => mergeConversationSummary(prev, nextConversation))
const nextUnreadTotal = Number(payload?.summary?.unread_total)
if (Number.isFinite(nextUnreadTotal)) {
setUnreadTotal(nextUnreadTotal)
}
}
channel.listen('.conversation.updated', handleConversationUpdated)
@@ -192,6 +200,79 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
}
}, [realtimeEnabled, userId])
useEffect(() => {
if (!realtimeEnabled || !userId) {
setOnlineUserIds([])
return undefined
}
const echo = getEcho()
if (!echo) {
setOnlineUserIds([])
return undefined
}
const setMembers = (users) => {
const nextIds = (users ?? [])
.map((user) => Number(user?.id))
.filter((id) => Number.isFinite(id) && id !== Number(userId))
setOnlineUserIds(Array.from(new Set(nextIds)))
}
const channel = echo.join('messaging')
channel
.here(setMembers)
.joining((user) => setOnlineUserIds((prev) => (
prev.includes(Number(user?.id)) || Number(user?.id) === Number(userId)
? prev
: [...prev, Number(user.id)]
)))
.leaving((user) => setOnlineUserIds((prev) => prev.filter((id) => id !== Number(user?.id))))
return () => {
echo.leave('messaging')
}
}, [realtimeEnabled, userId])
useEffect(() => {
if (!userId) {
return undefined
}
let intervalId = null
const sendHeartbeat = () => {
if (document.visibilityState === 'hidden') {
return
}
apiFetch('/api/messages/presence/heartbeat', {
method: 'POST',
body: JSON.stringify(activeId ? { conversation_id: activeId } : {}),
}).catch(() => {})
}
const handleVisibilitySync = () => {
if (document.visibilityState === 'visible') {
sendHeartbeat()
}
}
sendHeartbeat()
intervalId = window.setInterval(sendHeartbeat, 25000)
window.addEventListener('focus', sendHeartbeat)
document.addEventListener('visibilitychange', handleVisibilitySync)
return () => {
if (intervalId) {
window.clearInterval(intervalId)
}
window.removeEventListener('focus', sendHeartbeat)
document.removeEventListener('visibilitychange', handleVisibilitySync)
}
}, [activeId, userId])
useEffect(() => {
if (!realtimeEnabled) {
setTypingByConversation({})
@@ -310,12 +391,16 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
history.replaceState(null, '', `/messages/${conv.id}`)
}, [loadConversations])
const handleMarkRead = useCallback((conversationId) => {
const handleMarkRead = useCallback((conversationId, nextUnreadTotal = null) => {
setConversations((prev) => prev.map((conversation) => (
conversation.id === conversationId
? { ...conversation, unread_count: 0 }
: conversation
)))
if (Number.isFinite(Number(nextUnreadTotal))) {
setUnreadTotal(Number(nextUnreadTotal))
}
}, [])
const handleConversationPatched = useCallback((patch) => {
@@ -369,7 +454,9 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
}, [])
const activeConversation = conversations.find((conversation) => conversation.id === activeId) ?? null
const unreadCount = conversations.reduce((sum, conversation) => sum + Number(conversation.unread_count || 0), 0)
const unreadCount = Number.isFinite(Number(unreadTotal))
? Number(unreadTotal)
: conversations.reduce((sum, conversation) => sum + Number(conversation.unread_count || 0), 0)
const pinnedCount = conversations.reduce((sum, conversation) => {
const me = conversation.my_participant ?? conversation.all_participants?.find((participant) => participant.user_id === userId)
return sum + (me?.is_pinned ? 1 : 0)
@@ -475,6 +562,7 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
loading={loadingConvs}
activeId={activeId}
currentUserId={userId}
onlineUserIds={onlineUserIds}
typingByConversation={typingByConversation}
onSelect={handleSelectConversation}
/>
@@ -490,6 +578,7 @@ function MessagesPage({ userId, username, activeConversationId: initialId }) {
realtimeStatus={realtimeStatus}
currentUserId={userId}
currentUsername={username}
onlineUserIds={onlineUserIds}
apiFetch={apiFetch}
onBack={() => {
setActiveId(null)