diff --git a/mobile/src/components/features/channels/channel-members/AddChannelMembers.tsx b/mobile/src/components/features/channels/channel-members/AddChannelMembers.tsx index 9c62ffb9a..9532e8fcb 100644 --- a/mobile/src/components/features/channels/channel-members/AddChannelMembers.tsx +++ b/mobile/src/components/features/channels/channel-members/AddChannelMembers.tsx @@ -62,7 +62,7 @@ export const AddChannelMembers = ({ presentingElement, isOpen, onDismiss, channe const [searchText, setSearchText] = useState('') const filteredUsers = useMemo(() => { - return users.users.filter(member => { + return users.enabledUsers.filter(member => { return member.name.toLowerCase().includes(searchText.toLowerCase()) }) }, [users, searchText]) diff --git a/mobile/src/components/features/chat-input/Tiptap.tsx b/mobile/src/components/features/chat-input/Tiptap.tsx index 2b57dd3df..a972c4f8f 100644 --- a/mobile/src/components/features/chat-input/Tiptap.tsx +++ b/mobile/src/components/features/chat-input/Tiptap.tsx @@ -60,7 +60,7 @@ const ChannelMention = Mention.extend({ }) export const Tiptap = ({ onMessageSend, messageSending, defaultText = '' }: TiptapEditorProps) => { - const { users } = useContext(UserListContext) + const { enabledUsers } = useContext(UserListContext) const { channels } = useContext(ChannelListContext) as ChannelListContextType @@ -114,7 +114,7 @@ export const Tiptap = ({ onMessageSend, messageSending, defaultText = '' }: Tipt }, suggestion: { items: (query) => { - return users.filter((user) => user.full_name.toLowerCase().startsWith(query.query.toLowerCase())) + return enabledUsers.filter((user) => user.full_name.toLowerCase().startsWith(query.query.toLowerCase())) .slice(0, 10); }, // char: '@', diff --git a/mobile/src/hooks/useGetUserRecords.ts b/mobile/src/hooks/useGetUserRecords.ts index 2817b816c..f2a991fd4 100644 --- a/mobile/src/hooks/useGetUserRecords.ts +++ b/mobile/src/hooks/useGetUserRecords.ts @@ -13,6 +13,7 @@ export const useGetUserRecords = () => { full_name: user.full_name, user_image: user.user_image ?? '', first_name: user.first_name, + enabled: user.enabled } }) return usersMap diff --git a/mobile/src/types/Raven/RavenSettings.ts b/mobile/src/types/Raven/RavenSettings.ts index edde68c2e..2c6a19091 100644 --- a/mobile/src/types/Raven/RavenSettings.ts +++ b/mobile/src/types/Raven/RavenSettings.ts @@ -12,4 +12,6 @@ export interface RavenSettings{ idx?: number /** Automatically add system users to Raven : Check */ auto_add_system_users?: 0 | 1 + /** Show Raven on Desk : Check */ + show_raven_on_desk?: 0 | 1 } \ No newline at end of file diff --git a/mobile/src/types/Raven/RavenUser.ts b/mobile/src/types/Raven/RavenUser.ts index e3dbd8ec8..22441972d 100644 --- a/mobile/src/types/Raven/RavenUser.ts +++ b/mobile/src/types/Raven/RavenUser.ts @@ -1,5 +1,5 @@ -export interface RavenUser{ +export interface RavenUser { creation: string name: string modified: string @@ -17,5 +17,6 @@ export interface RavenUser{ /** First Name : Data */ first_name?: string /** User Image : Attach Image */ - user_image?: string + user_image?: string, + enabled: 0 | 1 } \ No newline at end of file diff --git a/mobile/src/types/RavenMessaging/RavenMessage.ts b/mobile/src/types/RavenMessaging/RavenMessage.ts index 54d96bf54..a1d716c68 100644 --- a/mobile/src/types/RavenMessaging/RavenMessage.ts +++ b/mobile/src/types/RavenMessaging/RavenMessage.ts @@ -1,5 +1,5 @@ -export interface RavenMessage{ +export interface RavenMessage { creation: string name: string modified: string @@ -16,8 +16,18 @@ export interface RavenMessage{ text?: string /** JSON : JSON */ json?: any + /** Message Reactions : JSON */ + message_reactions?: any + /** Is Reply : Check */ + is_reply?: 0 | 1 + /** Replied Message ID : Link - Raven Message */ + linked_message?: string + /** Replied Message Details : JSON */ + replied_message_details?: any /** Message Type : Select */ message_type?: "Text" | "Image" | "File" + /** Content : Long Text */ + content?: string /** File : Attach */ file?: string /** Image Width : Data */ @@ -30,16 +40,10 @@ export interface RavenMessage{ thumbnail_width?: string /** Thumbnail Height : Data */ thumbnail_height?: string - /** Message Reactions : JSON */ - message_reactions?: any - /** Is Reply : Check */ - is_reply?: 0 | 1 - /** Linked Message : Link - Raven Message */ - linked_message?: string /** Link Doctype : Link - DocType */ link_doctype?: string /** Link Document : Dynamic Link */ link_document?: string - /** Content : Long Text */ - content?: string + /** Is Edited : Check */ + is_edited?: 0 | 1 } \ No newline at end of file diff --git a/mobile/src/utils/users/UserListProvider.tsx b/mobile/src/utils/users/UserListProvider.tsx index c4b895c4d..99ae25919 100644 --- a/mobile/src/utils/users/UserListProvider.tsx +++ b/mobile/src/utils/users/UserListProvider.tsx @@ -1,17 +1,18 @@ import { FrappeError, useFrappeDocTypeEventListener, useFrappeGetCall } from "frappe-react-sdk"; -import { PropsWithChildren, createContext, useContext } from "react"; -import { User } from "../../../../types/Core/User"; +import { PropsWithChildren, createContext, useContext, useMemo } from "react"; import { UserContext } from "../auth/UserProvider"; import { ErrorBanner } from "@/components/layout"; import { IonButton, IonButtons, IonContent, IonFooter, IonHeader, IonPage, IonTitle, IonToolbar } from "@ionic/react"; import { FullPageLoader } from "@/components/layout/loaders"; +import { RavenUser } from "@/types/Raven/RavenUser"; -export const UserListContext = createContext<{ users: UserFields[] }>({ - users: [] +export const UserListContext = createContext<{ users: UserFields[], enabledUsers: UserFields[] }>({ + users: [], + enabledUsers: [] }) -export type UserFields = Pick +export type UserFields = Pick /** Hook to fetch a list of users */ export const useUserList = () => { @@ -22,7 +23,7 @@ export const useUserList = () => { export const UserListProvider = ({ children }: PropsWithChildren) => { const { isLoggedIn } = useContext(UserContext) - const { data, error: usersError, isLoading, mutate } = useFrappeGetCall<{ message: UserFields[] }>('raven.api.raven_users.get_list', undefined, isLoggedIn ? undefined : null, { + const { data, error: usersError, isLoading, mutate } = useFrappeGetCall<{ message: UserFields[] }>('raven.api.raven_users.get_list', undefined, isLoggedIn ? 'raven.api.raven_users.get_list' : null, { revalidateOnFocus: false, revalidateOnReconnect: false, errorRetryCount: 10 @@ -30,6 +31,13 @@ export const UserListProvider = ({ children }: PropsWithChildren) => { useFrappeDocTypeEventListener('User', () => mutate()) + const { users, enabledUsers } = useMemo(() => { + return { + users: data?.message ?? [], + enabledUsers: data?.message?.filter(user => user.enabled === 1) ?? [] + } + }, [data]) + if (isLoading) { return } @@ -37,7 +45,7 @@ export const UserListProvider = ({ children }: PropsWithChildren) => { return } - return + return {children} diff --git a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx index c83d5429d..bce415780 100644 --- a/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx +++ b/raven-app/src/components/feature/chat/ChatInput/Tiptap.tsx @@ -73,7 +73,7 @@ export const ChannelMention = Mention.extend({ }) const Tiptap = ({ slotBefore, fileProps, onMessageSend, clearReplyMessage, placeholder = 'Type a message...', messageSending, sessionStorageKey = 'tiptap-editor', disableSessionStorage = false, defaultText = '' }: TiptapEditorProps) => { - const { users } = useContext(UserListContext) + const { enabledUsers } = useContext(UserListContext) const { channels } = useContext(ChannelListContext) as ChannelListContextType @@ -264,7 +264,7 @@ const Tiptap = ({ slotBefore, fileProps, onMessageSend, clearReplyMessage, place }, suggestion: { items: (query) => { - return users.filter((user) => user.full_name.toLowerCase().startsWith(query.query.toLowerCase())) + return enabledUsers.filter((user) => user.full_name.toLowerCase().startsWith(query.query.toLowerCase())) .slice(0, 10); }, // char: '@', diff --git a/raven-app/src/components/feature/command-palette/CommandPalette.tsx b/raven-app/src/components/feature/command-palette/CommandPalette.tsx index b0085b5ff..f16725715 100644 --- a/raven-app/src/components/feature/command-palette/CommandPalette.tsx +++ b/raven-app/src/components/feature/command-palette/CommandPalette.tsx @@ -38,7 +38,7 @@ export const CommandPalette = ({ isOpen, onClose }: CommandPaletteProps) => { const debouncedText = useDebounce(inputValue, 200) const { currentUser } = useContext(UserContext) const activeUsers = useContext(ActiveUsersContext) - const { call, reset } = useFrappePostCall<{ message: string }>("raven.raven_channel_management.doctype.raven_channel.raven_channel.create_direct_message_channel") + const { call, reset } = useFrappePostCall<{ message: string }>("raven.api.raven_channel.create_direct_message_channel") let navigate = useNavigate() const gotoDMChannel = async (user: string) => { diff --git a/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx b/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx index b8253e94c..21dbe2104 100644 --- a/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx +++ b/raven-app/src/components/feature/raven-users/AddRavenUsersContent.tsx @@ -10,11 +10,16 @@ import { ErrorBanner } from "@/components/layout/AlertBanner" import { TableLoader } from "@/components/layout/Loaders/TableLoader" import { UsersTable } from "./UsersTable" import { UserListContext } from "@/utils/users/UserListProvider" -import { Button, Dialog, Flex, Text, TextField } from "@radix-ui/themes" +import { Button, Dialog, Em, Flex, Strong, Text, TextField } from "@radix-ui/themes" import { Loader } from "@/components/common/Loader" import { BiSearch } from "react-icons/bi" import { useToast } from "@/hooks/useToast" +import { ErrorCallout } from "@/components/layout/AlertBanner/ErrorBanner" +interface AddUsersResponse { + failed_users: User[], + success_users: User[] +} const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { const { mutate } = useSWRConfig() @@ -25,7 +30,7 @@ const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { setSearchText(event.target.value) } - const filters: Filter[] = [['enabled', '=', 1], ['name', 'not in', ['Guest', 'Administrator']], ['user_type', '!=', 'Website User'], ['full_name', 'like', `%${debouncedText}%`]] + const filters: Filter[] = [['enabled', '=', 1], ['name', 'not in', ['Guest', 'Administrator']], ['full_name', 'like', `%${debouncedText}%`]] const { start, count, selectedPageLength, setPageLength, nextPage, previousPage } = usePaginationWithDoctype("User", 10, filters) const [sortOrder, setSortOder] = useState<"asc" | "desc">("desc") @@ -42,25 +47,38 @@ const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { }) const users = useContext(UserListContext) - const ravenUsersArray = users.users.map(user => user.name) + const ravenUsersArray = users.enabledUsers.map(user => user.name) const [selected, setSelected] = useState([]) - const { loading, call, error: postError } = useFrappePostCall('raven.api.raven_users.add_users_to_raven') + const { loading, call, error: postError } = useFrappePostCall<{ message: AddUsersResponse }>('raven.api.raven_users.add_users_to_raven') const { toast } = useToast() + const [failedUsers, setFailedUsers] = useState([]) + const handleAddUsers = async () => { + setFailedUsers([]) if (selected.length > 0) { call({ users: JSON.stringify(selected) - }).then(() => { - toast({ - title: `You have added ${selected.length} users to Raven`, - variant: 'success', - duration: 1000 - }) - onClose() + }).then((res) => { + if (res.message.success_users.length !== 0) { + toast({ + title: `You have added ${res.message.success_users.length} users to Raven`, + variant: 'success', + duration: 1000 + }) + } + mutate('raven.api.raven_users.get_list') + + if (res.message.failed_users.length === 0) { + onClose() + setSelected([]) + } else { + setFailedUsers(res.message.failed_users) + setSelected(s => s.filter(user => res.message.failed_users.map(u => u.name).includes(user))) + } }) } } @@ -102,6 +120,15 @@ const AddRavenUsersContent = ({ onClose }: { onClose: VoidFunction }) => { + {failedUsers.length > 0 && + Could not add the following users to Raven since they have a Role Profile attached.
+ Please remove the role profile and try again.

+ +
    + {failedUsers.map((user, i) =>
  1. {user.full_name} - {user.email}
  2. )} +
+ +
} {!data && !error && } {data && data.length === 0 && debouncedText.length >= 2 && diff --git a/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx b/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx index 4df41697e..1c965ccba 100644 --- a/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx +++ b/raven-app/src/components/feature/select-member/AddMembersDropdown.tsx @@ -21,7 +21,7 @@ const AddMembersDropdown = ({ channelMembers, label = 'Select users', selectedUs const users = useContext(UserListContext) //Options for dropdown - const nonChannelMembers = users.users?.filter((m: UserFields) => !channelMembers?.[m.name]) ?? [] + const nonChannelMembers = users.enabledUsers?.filter((m: UserFields) => !channelMembers?.[m.name]) ?? [] /** Function to filter users */ function getFilteredUsers(selectedUsers: UserFields[], inputValue: string) { @@ -170,7 +170,7 @@ const AddMembersDropdown = ({ channelMembers, label = 'Select users', selectedUs
    diff --git a/raven-app/src/hooks/useGetUserRecords.ts b/raven-app/src/hooks/useGetUserRecords.ts index 2817b816c..f2a991fd4 100644 --- a/raven-app/src/hooks/useGetUserRecords.ts +++ b/raven-app/src/hooks/useGetUserRecords.ts @@ -13,6 +13,7 @@ export const useGetUserRecords = () => { full_name: user.full_name, user_image: user.user_image ?? '', first_name: user.first_name, + enabled: user.enabled } }) return usersMap diff --git a/raven-app/src/pages/AddRavenUsersPage.tsx b/raven-app/src/pages/AddRavenUsersPage.tsx index df358eb01..82ce0a921 100644 --- a/raven-app/src/pages/AddRavenUsersPage.tsx +++ b/raven-app/src/pages/AddRavenUsersPage.tsx @@ -21,7 +21,7 @@ import { Loader } from '@/components/common/Loader' const AddRavenUsersPage = () => { const canAddRavenUsers = isSystemManager() - console.log('canAddRavenUsers', canAddRavenUsers) + return ( diff --git a/raven-app/src/types/Raven/RavenSettings.ts b/raven-app/src/types/Raven/RavenSettings.ts index edde68c2e..2c6a19091 100644 --- a/raven-app/src/types/Raven/RavenSettings.ts +++ b/raven-app/src/types/Raven/RavenSettings.ts @@ -12,4 +12,6 @@ export interface RavenSettings{ idx?: number /** Automatically add system users to Raven : Check */ auto_add_system_users?: 0 | 1 + /** Show Raven on Desk : Check */ + show_raven_on_desk?: 0 | 1 } \ No newline at end of file diff --git a/raven-app/src/types/Raven/RavenUser.ts b/raven-app/src/types/Raven/RavenUser.ts index e3dbd8ec8..22441972d 100644 --- a/raven-app/src/types/Raven/RavenUser.ts +++ b/raven-app/src/types/Raven/RavenUser.ts @@ -1,5 +1,5 @@ -export interface RavenUser{ +export interface RavenUser { creation: string name: string modified: string @@ -17,5 +17,6 @@ export interface RavenUser{ /** First Name : Data */ first_name?: string /** User Image : Attach Image */ - user_image?: string + user_image?: string, + enabled: 0 | 1 } \ No newline at end of file diff --git a/raven-app/src/types/RavenMessaging/RavenMessage.ts b/raven-app/src/types/RavenMessaging/RavenMessage.ts index 54d96bf54..a1d716c68 100644 --- a/raven-app/src/types/RavenMessaging/RavenMessage.ts +++ b/raven-app/src/types/RavenMessaging/RavenMessage.ts @@ -1,5 +1,5 @@ -export interface RavenMessage{ +export interface RavenMessage { creation: string name: string modified: string @@ -16,8 +16,18 @@ export interface RavenMessage{ text?: string /** JSON : JSON */ json?: any + /** Message Reactions : JSON */ + message_reactions?: any + /** Is Reply : Check */ + is_reply?: 0 | 1 + /** Replied Message ID : Link - Raven Message */ + linked_message?: string + /** Replied Message Details : JSON */ + replied_message_details?: any /** Message Type : Select */ message_type?: "Text" | "Image" | "File" + /** Content : Long Text */ + content?: string /** File : Attach */ file?: string /** Image Width : Data */ @@ -30,16 +40,10 @@ export interface RavenMessage{ thumbnail_width?: string /** Thumbnail Height : Data */ thumbnail_height?: string - /** Message Reactions : JSON */ - message_reactions?: any - /** Is Reply : Check */ - is_reply?: 0 | 1 - /** Linked Message : Link - Raven Message */ - linked_message?: string /** Link Doctype : Link - DocType */ link_doctype?: string /** Link Document : Dynamic Link */ link_document?: string - /** Content : Long Text */ - content?: string + /** Is Edited : Check */ + is_edited?: 0 | 1 } \ No newline at end of file diff --git a/raven-app/src/utils/channel/ChannelListProvider.tsx b/raven-app/src/utils/channel/ChannelListProvider.tsx index c43c748dc..271883011 100644 --- a/raven-app/src/utils/channel/ChannelListProvider.tsx +++ b/raven-app/src/utils/channel/ChannelListProvider.tsx @@ -60,7 +60,7 @@ export const useFetchChannelList = (): ChannelListContextType => { const { toast } = useToast() const { data, mutate, ...rest } = useFrappeGetCall<{ message: ChannelList }>("raven.api.raven_channel.get_all_channels", { hide_archived: false - }, undefined, { + }, `channel_list`, { revalidateOnFocus: false, revalidateIfStale: false, onError: (error) => { diff --git a/raven-app/src/utils/users/UserListProvider.tsx b/raven-app/src/utils/users/UserListProvider.tsx index 1e3e7df9e..8e7f6fe36 100644 --- a/raven-app/src/utils/users/UserListProvider.tsx +++ b/raven-app/src/utils/users/UserListProvider.tsx @@ -1,26 +1,40 @@ -import { useFrappeDocTypeEventListener, useFrappeGetCall } from "frappe-react-sdk"; -import { PropsWithChildren, createContext } from "react"; -import { User } from "../../../../types/Core/User"; +import { useFrappeDocTypeEventListener, useFrappeGetCall, useSWRConfig } from "frappe-react-sdk"; +import { PropsWithChildren, createContext, useMemo } from "react"; import { ErrorBanner } from "@/components/layout/AlertBanner"; import { FullPageLoader } from "@/components/layout/Loaders"; import { Box, Flex, Link } from "@radix-ui/themes"; +import { RavenUser } from "@/types/Raven/RavenUser"; -export const UserListContext = createContext<{ users: UserFields[] }>({ - users: [] +export const UserListContext = createContext<{ users: UserFields[], enabledUsers: UserFields[] }>({ + users: [], + enabledUsers: [] }) -export type UserFields = Pick +export type UserFields = Pick export const UserListProvider = ({ children }: PropsWithChildren) => { + const { mutate: globalMutate } = useSWRConfig() const { data, error: usersError, mutate, isLoading } = useFrappeGetCall<{ message: UserFields[] }>('raven.api.raven_users.get_list', undefined, 'raven.api.raven_users.get_list', { revalidateOnFocus: false, revalidateOnReconnect: false, }) - useFrappeDocTypeEventListener('User', () => mutate()) + useFrappeDocTypeEventListener('Raven User', () => { + mutate() + + // Mutate the channel list as well + globalMutate(`channel_list`) + }) + + const { users, enabledUsers } = useMemo(() => { + return { + users: data?.message ?? [], + enabledUsers: data?.message?.filter(user => user.enabled === 1) ?? [] + } + }, [data]) if (isLoading) { return @@ -35,7 +49,7 @@ export const UserListProvider = ({ children }: PropsWithChildren) => { } - return + return {children} } \ No newline at end of file diff --git a/raven/api/raven_channel.py b/raven/api/raven_channel.py index c622dd977..9ee65a10a 100644 --- a/raven/api/raven_channel.py +++ b/raven/api/raven_channel.py @@ -97,10 +97,11 @@ def get_extra_users(dm_channels): for dm_channel in dm_channels] existing_users.append('Administrator') existing_users.append('Guest') - return frappe.db.get_list('User', filters=[ + + # Skip permissions since we are only fetching user_id, full_name, and user_image and have applied filters + return frappe.db.get_all('User', filters=[ ['name', 'not in', existing_users], ['enabled', '=', 1], - ['user_type', '=', 'System User'], ["Has Role", "role", "=", 'Raven User']], fields=['name', 'full_name', 'user_image']) diff --git a/raven/api/raven_users.py b/raven/api/raven_users.py index 8214e4f9c..775f04ad0 100644 --- a/raven/api/raven_users.py +++ b/raven/api/raven_users.py @@ -16,8 +16,9 @@ def get_list(): if not frappe.db.exists("Raven User", { "user": frappe.session.user }): frappe.throw(_("You do not have a Raven User profile. Please contact your administrator to add your user profile as a Raven User."), title="Insufficient permissions. Please contact your administrator.") - users = frappe.db.get_all("Raven User", fields=["full_name", "user_image", - "name", "first_name"], + users = frappe.db.get_all("Raven User", + fields=["full_name", "user_image", + "name", "first_name", "enabled"], order_by="full_name") return users @@ -28,12 +29,26 @@ def add_users_to_raven(users): if isinstance(users, str): users = json.loads(users) + failed_users = [] + success_users = [] + for user in users: user_doc = frappe.get_doc("User", user) - user_doc.append("roles", { - "role": "Raven User" - }) - user_doc.save() + + if user_doc.role_profile_name: + failed_users.append(user_doc) + + elif hasattr(user_doc, 'role_profiles') and len(user_doc.role_profiles) > 0: + failed_users.append(user_doc) + else: + user_doc.append("roles", { + "role": "Raven User" + }) + user_doc.save() + success_users.append(user_doc) - return "success" + return { + "success_users": success_users, + "failed_users": failed_users + } \ No newline at end of file diff --git a/raven/raven/doctype/raven_user/raven_user.json b/raven/raven/doctype/raven_user/raven_user.json index d15dde972..3e6900c63 100644 --- a/raven/raven/doctype/raven_user/raven_user.json +++ b/raven/raven/doctype/raven_user/raven_user.json @@ -61,7 +61,7 @@ ], "image_field": "user_image", "links": [], - "modified": "2023-12-08 04:14:02.588812", + "modified": "2024-02-23 00:05:54.819092", "modified_by": "Administrator", "module": "Raven", "name": "Raven User", diff --git a/raven/raven_messaging/doctype/raven_message/raven_message.json b/raven/raven_messaging/doctype/raven_message/raven_message.json index 5137edc9e..b81b345a2 100644 --- a/raven/raven_messaging/doctype/raven_message/raven_message.json +++ b/raven/raven_messaging/doctype/raven_message/raven_message.json @@ -26,7 +26,6 @@ "thumbnail_height", "link_doctype", "link_document", - "content", "is_edited" ], "fields": [