Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: active users indicator reliability and removed context #904

Merged
merged 1 commit into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { ViewChannelDetailsModalContent } from "../channels/ViewChannelDetailsModal"
import { useContext, useState } from "react"
import { ActiveUsersContext } from "@/utils/users/ActiveUsersProvider"
import { useState } from "react"
import { ChannelListItem } from "@/utils/channel/ChannelListProvider"
import { ChannelMembers } from "@/utils/channel/ChannelMembersProvider"
import { Button, Dialog, Tooltip } from "@radix-ui/themes"
import { UserAvatar } from "@/components/common/UserAvatar"
import { BiSolidUser } from "react-icons/bi"
import { clsx } from "clsx"
import useFetchActiveUsers from "@/hooks/fetchers/useFetchActiveUsers"

interface ViewChannelDetailsButtonProps {
channelData: ChannelListItem,
Expand All @@ -23,7 +23,9 @@ export const ViewChannelDetailsButton = ({ channelData, allowAddMembers, channel
setOpen(false)
}

const activeUsers = useContext(ActiveUsersContext)
const { data } = useFetchActiveUsers()
const activeUsers = data?.message ?? []

const totalMembers = Object.keys(channelMembers).length

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { useNavigate } from 'react-router-dom'
import { UserContext } from '../../../utils/auth/UserProvider'
import { BiSearch, BiX } from 'react-icons/bi'
import { UserListContext } from '@/utils/users/UserListProvider'
import { ActiveUsersContext } from '@/utils/users/ActiveUsersProvider'
import { ModalTypes, useModalManager } from '@/hooks/useModalManager'
import { Flex, IconButton, Box, Text, Link } from '@radix-ui/themes'
import useFetchActiveUsers from '@/hooks/fetchers/useFetchActiveUsers'

interface CommandPaletteProps {
isOpen: boolean,
Expand All @@ -37,7 +37,11 @@ export const CommandPalette = ({ isOpen, onClose }: CommandPaletteProps) => {
const isHome = activePage === ''
const debouncedText = useDebounce(inputValue, 200)
const { currentUser } = useContext(UserContext)
const activeUsers = useContext(ActiveUsersContext)

const { data } = useFetchActiveUsers()

const activeUsers = data?.message ?? []

const { call, reset } = useFrappePostCall<{ message: string }>("raven.api.raven_channel.create_direct_message_channel")
let navigate = useNavigate()

Expand Down
55 changes: 55 additions & 0 deletions raven-app/src/hooks/fetchers/useFetchActiveUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { UserContext } from '@/utils/auth/UserProvider'
import { useFrappeEventListener, useFrappeGetCall, useSWRConfig } from 'frappe-react-sdk'
import { useContext } from 'react'
import { useActiveState } from '../useActiveState'

/**
* Hook to fetch active users from the server.
* SWRKey: active_users
*/
const useFetchActiveUsers = () => {
const res = useFrappeGetCall<{ message: string[] }>('raven.api.user_availability.get_active_users',
undefined,
'active_users',
{
dedupingInterval: 1000 * 60 * 5, // 5 minutes - do not refetch if the data is fresh
}
)

return res
}

/**
* Hook to listen to user_active_state_updated event and update the active_users list in realtime
* Also handles the user's active state via visibilty change and idle timer
*/
export const useFetchActiveUsersRealtime = () => {
const { currentUser } = useContext(UserContext)

const { mutate } = useSWRConfig()

useActiveState()

/** Hook to listen to user_active_state */
useFrappeEventListener('raven:user_active_state_updated', (data) => {
if (data.user !== currentUser) {
// If the user is not the current user, update the active_users list
// No need to revalidate the data as the websocket event has emitted the new data for that user
mutate('active_users', (res?: { message: string[] }) => {
if (res) {
if (data.active) {
return { message: [...res.message, data.user] }
} else {
return { message: res.message.filter(user => user !== data.user) }
}
} else {
return undefined
}
}, {
revalidate: false
})
}
})
}

export default useFetchActiveUsers
18 changes: 17 additions & 1 deletion raven-app/src/hooks/useActiveState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,24 @@ export const useActiveState = () => {
useEffect(() => {
// Update user availability when the app is opened
call.get('raven.api.user_availability.refresh_user_active_state', {
deactivate
deactivate: false
})

document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
updateUserActiveState().then(activate)
}
else {
updateUserActiveState(true).then(deactivate)
}
})

return () => {
// Update user availability when the app is closed
call.get('raven.api.user_availability.refresh_user_active_state', {
deactivate: true
})
}
}, [])

return isActive
Expand Down
9 changes: 5 additions & 4 deletions raven-app/src/hooks/useIsUserActive.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { useContext, useMemo } from 'react';
import { ActiveUsersContext } from '../utils/users/ActiveUsersProvider';
import { UserContext } from '@/utils/auth/UserProvider';
import useFetchActiveUsers from './fetchers/useFetchActiveUsers';

export const useIsUserActive = (userID?: string): boolean => {

const { currentUser } = useContext(UserContext)
const activeUsers = useContext(ActiveUsersContext)

const { data } = useFetchActiveUsers()

const isActive = useMemo(() => {
if (userID === currentUser) {
return true
} else if (userID) {
return activeUsers.includes(userID)
return data?.message.includes(userID) ?? false
} else {
return false
}
}, [userID, activeUsers])
}, [userID, data])

return isActive
}
47 changes: 26 additions & 21 deletions raven-app/src/pages/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { lazy, Suspense } from 'react'
import { Sidebar } from '../components/layout/Sidebar/Sidebar'
import { ChannelListProvider } from '../utils/channel/ChannelListProvider'
import { UserListProvider } from '@/utils/users/UserListProvider'
import { ActiveUsersProvider } from '@/utils/users/ActiveUsersProvider'
import { hasRavenUserRole } from '@/utils/roles'
import { FullPageLoader } from '@/components/layout/Loaders'
import { MobileAppRedirectBanner } from '@/components/layout/AlertBanner'
import '../components/layout/AlertBanner/styles.css'
import CommandMenu from '@/components/feature/CommandMenu/CommandMenu'
import { useFetchActiveUsersRealtime } from '@/hooks/fetchers/useFetchActiveUsers'

const AddRavenUsersPage = lazy(() => import('@/pages/AddRavenUsersPage'))

Expand All @@ -19,26 +19,7 @@ export const MainPage = () => {

if (isRavenUser) {
return (
<UserListProvider>
<ChannelListProvider>
<ActiveUsersProvider>
<div className='web-app'>
<Flex>
<Box className={`w-64 bg-gray-2 border-r-gray-3 border-r dark:bg-gray-1`} left="0" top='0' position="fixed">
<Sidebar />
</Box>
<Box className='ml-[var(--sidebar-width)] w-[calc(100vw-var(--sidebar-width))] dark:bg-gray-2'>
<Outlet />
</Box>
</Flex>
</div>
<div className='mobile-app-message'>
<MobileAppRedirectBanner />
</div>
<CommandMenu />
</ActiveUsersProvider>
</ChannelListProvider>
</UserListProvider>
<MainPageContent />
)
} else {
// If the user does not have the Raven User role, then show an error message if the user cannot add more people.
Expand All @@ -48,4 +29,28 @@ export const MainPage = () => {
</Suspense>
}

}

const MainPageContent = () => {

useFetchActiveUsersRealtime()

return <UserListProvider>
<ChannelListProvider>
<div className='web-app'>
<Flex>
<Box className={`w-64 bg-gray-2 border-r-gray-3 border-r dark:bg-gray-1`} left="0" top='0' position="fixed">
<Sidebar />
</Box>
<Box className='ml-[var(--sidebar-width)] w-[calc(100vw-var(--sidebar-width))] dark:bg-gray-2'>
<Outlet />
</Box>
</Flex>
</div>
<div className='mobile-app-message'>
<MobileAppRedirectBanner />
</div>
<CommandMenu />
</ChannelListProvider>
</UserListProvider>
}
5 changes: 1 addition & 4 deletions raven-app/src/utils/channel/ChannelRedirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom'
import { hasRavenUserRole } from '../roles'
import { FullPageLoader } from '@/components/layout/Loaders'
import AddRavenUsersPage from '@/pages/AddRavenUsersPage'
import { ActiveUsersProvider } from '../users/ActiveUsersProvider'
import { UserListProvider } from '../users/UserListProvider'
import { ChannelListProvider } from './ChannelListProvider'

Expand All @@ -30,9 +29,7 @@ export const ChannelRedirect = () => {
return (
<UserListProvider>
<ChannelListProvider>
<ActiveUsersProvider>
<Outlet />
</ActiveUsersProvider>
<Outlet />
</ChannelListProvider>
</UserListProvider>
)
Expand Down
17 changes: 0 additions & 17 deletions raven-app/src/utils/users/ActiveUsersProvider.tsx

This file was deleted.

Loading