From 5a081466feec26bfd48c3fdc46de3fb27fafb038 Mon Sep 17 00:00:00 2001
From: Pavel <14926950+prichodko@users.noreply.github.com>
Date: Tue, 23 Aug 2022 15:48:10 +0200
Subject: [PATCH] Add sorting of chats (#302)
* add sorting of chats
* simplify sidebar structure
* move categories at the bottom
* fix duplicate fields
* disable context menu for chat group
* rename chat group to chat category
* fix category font color
* show active chat even when category closed
---
.../components/channels/channel-group.tsx | 64 ---------------
.../components/channels/channel-item.tsx | 22 ------
.../components/channels/index.tsx | 27 -------
.../components/chats/chat-category.tsx | 78 +++++++++++++++++++
.../index.tsx => chats/chat-item.tsx} | 26 +++----
.../main-sidebar/components/chats/index.tsx | 26 +++++++
.../src/components/main-sidebar/index.tsx | 4 +-
packages/status-react/src/protocol/index.tsx | 2 +-
.../src/protocol/use-active-chat.tsx | 12 +++
.../status-react/src/protocol/use-chats.tsx | 27 -------
.../src/protocol/use-sorted-chats.tsx | 77 ++++++++++++++++++
11 files changed, 209 insertions(+), 156 deletions(-)
delete mode 100644 packages/status-react/src/components/main-sidebar/components/channels/channel-group.tsx
delete mode 100644 packages/status-react/src/components/main-sidebar/components/channels/channel-item.tsx
delete mode 100644 packages/status-react/src/components/main-sidebar/components/channels/index.tsx
create mode 100644 packages/status-react/src/components/main-sidebar/components/chats/chat-category.tsx
rename packages/status-react/src/components/main-sidebar/components/{sidebar-item/index.tsx => chats/chat-item.tsx} (74%)
create mode 100644 packages/status-react/src/components/main-sidebar/components/chats/index.tsx
create mode 100644 packages/status-react/src/protocol/use-active-chat.tsx
delete mode 100644 packages/status-react/src/protocol/use-chats.tsx
create mode 100644 packages/status-react/src/protocol/use-sorted-chats.tsx
diff --git a/packages/status-react/src/components/main-sidebar/components/channels/channel-group.tsx b/packages/status-react/src/components/main-sidebar/components/channels/channel-group.tsx
deleted file mode 100644
index fbe8986..0000000
--- a/packages/status-react/src/components/main-sidebar/components/channels/channel-group.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import React from 'react'
-
-import * as Collapsible from '@radix-ui/react-collapsible'
-
-import { BellIcon } from '../../../../icons/bell-icon'
-import { ChevronDownIcon } from '../../../../icons/chevron-down-icon'
-import { styled } from '../../../../styles/config'
-import { ContextMenu, ContextMenuTrigger, Text } from '../../../../system'
-
-interface Props {
- name: string
- children: React.ReactNode
-}
-
-export const ChannelGroup = (props: Props) => {
- const { name, children } = props
-
- return (
-
-
-
-
- {name}
-
-
-
- {children}
-
-
- }>
- For 15 min
- For 1 hour
- For 8 hours
- For 24 hours
- Until I turn it back on
-
- }>Mark as Read
-
-
- )
-}
-
-const CollapsibleTrigger = styled(Collapsible.Trigger, {
- width: '100%',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'space-between',
- padding: 8,
- borderRadius: 8,
- height: 34,
- color: '$accent-1',
-
- '&:hover': {
- background: '$gray-3',
- },
-
- '&[aria-expanded="true"] svg': {
- transform: 'rotate(180deg)',
- },
-})
-
-const CollapsibleContent = styled(Collapsible.Content, {
- overflow: 'hidden',
-})
diff --git a/packages/status-react/src/components/main-sidebar/components/channels/channel-item.tsx b/packages/status-react/src/components/main-sidebar/components/channels/channel-item.tsx
deleted file mode 100644
index f34e544..0000000
--- a/packages/status-react/src/components/main-sidebar/components/channels/channel-item.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react'
-
-// import { ChatMenu } from '../../../../components/chat-menu'
-// import { ContextMenuTrigger } from '../../../../system'
-import { SidebarItem } from '../sidebar-item'
-
-import type { SidebarItemProps } from '../sidebar-item'
-
-interface Props extends SidebarItemProps {
- children: string
-}
-
-export const ChannelItem = (props: Props) => {
- const { children, ...sidebarItemProps } = props
-
- return (
- //
- #{children}
- //
- //
- )
-}
diff --git a/packages/status-react/src/components/main-sidebar/components/channels/index.tsx b/packages/status-react/src/components/main-sidebar/components/channels/index.tsx
deleted file mode 100644
index 7e28d9a..0000000
--- a/packages/status-react/src/components/main-sidebar/components/channels/index.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-
-import { useChats } from '../../../../protocol'
-import { Box } from '../../../../system'
-// import { ChannelGroup } from './channel-group'
-import { ChannelItem } from './channel-item'
-
-export const Channels = () => {
- const chats = useChats()
-
- return (
-
- {chats.map(chat => (
-
- {chat.identity!.displayName}
-
- ))}
-
- )
-}
diff --git a/packages/status-react/src/components/main-sidebar/components/chats/chat-category.tsx b/packages/status-react/src/components/main-sidebar/components/chats/chat-category.tsx
new file mode 100644
index 0000000..784a30f
--- /dev/null
+++ b/packages/status-react/src/components/main-sidebar/components/chats/chat-category.tsx
@@ -0,0 +1,78 @@
+import React, { useState } from 'react'
+
+import * as Collapsible from '@radix-ui/react-collapsible'
+
+import { ChevronDownIcon } from '../../../../icons/chevron-down-icon'
+import { useActiveChat } from '../../../../protocol/use-active-chat'
+import { styled } from '../../../../styles/config'
+
+import type { ChatItem } from './chat-item'
+
+interface Props {
+ name: string
+ children: React.ReactNode
+}
+
+export const ChatCategory = (props: Props) => {
+ const { name, children } = props
+ const chat = useActiveChat()
+
+ const [open, setOpen] = useState(true)
+
+ // show active chat even though the category is closed
+ const activeChild = React.Children.toArray(children).find(child => {
+ if (React.isValidElement>(child)) {
+ return child.props.chat.id === chat?.uuid
+ }
+ })
+
+ return (
+ <>
+
+
+ {name}
+
+
+ {children}
+
+ {open === false && activeChild}
+ >
+ )
+}
+
+/*
+
+ }>
+ For 15 min
+ For 1 hour
+ For 8 hours
+ For 24 hours
+ Until I turn it back on
+
+ }>Mark as Read
+
+ */
+
+const CollapsibleTrigger = styled(Collapsible.Trigger, {
+ width: '100%',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: 8,
+ borderRadius: 8,
+ height: 34,
+ fontWeight: '$500',
+ color: '$accent-4',
+
+ '&:hover': {
+ background: '$gray-3',
+ },
+
+ '&[aria-expanded="true"] svg': {
+ transform: 'rotate(180deg)',
+ },
+})
+
+const CollapsibleContent = styled(Collapsible.Content, {
+ overflow: 'hidden',
+})
diff --git a/packages/status-react/src/components/main-sidebar/components/sidebar-item/index.tsx b/packages/status-react/src/components/main-sidebar/components/chats/chat-item.tsx
similarity index 74%
rename from packages/status-react/src/components/main-sidebar/components/sidebar-item/index.tsx
rename to packages/status-react/src/components/main-sidebar/components/chats/chat-item.tsx
index ce0822c..fa71d6a 100644
--- a/packages/status-react/src/components/main-sidebar/components/sidebar-item/index.tsx
+++ b/packages/status-react/src/components/main-sidebar/components/chats/chat-item.tsx
@@ -5,35 +5,35 @@ import { NavLink } from 'react-router-dom'
import { styled } from '../../../../styles/config'
import { Avatar } from '../../../../system'
+import type { Chat } from '../../../../protocol/use-sorted-chats'
import type { Ref } from 'react'
interface Props {
- to: string
- muted: boolean
- unread: boolean
- children: React.ReactNode
- name?: string
- color?: string
+ chat: Chat
}
-const SidebarItem = (props: Props, ref: Ref) => {
- const { muted, unread, children, name, color, ...buttonProps } = props
+const ChatItem = (props: Props, ref: Ref) => {
+ const { chat } = props
+
+ const muted = false
+ const unread = false
+
+ const { color, displayName } = chat.identity!
return (
-
- {children}
+ #{displayName}
)
}
-const _SidebarItem = forwardRef(SidebarItem)
+const _ChatItem = forwardRef(ChatItem)
-export { _SidebarItem as SidebarItem }
+export { _ChatItem as ChatItem }
export type SidebarItemProps = Omit
const Link = styled(NavLink, {
diff --git a/packages/status-react/src/components/main-sidebar/components/chats/index.tsx b/packages/status-react/src/components/main-sidebar/components/chats/index.tsx
new file mode 100644
index 0000000..598bf95
--- /dev/null
+++ b/packages/status-react/src/components/main-sidebar/components/chats/index.tsx
@@ -0,0 +1,26 @@
+import React from 'react'
+
+import { useSortedChats } from '../../../../protocol'
+import { Box } from '../../../../system'
+import { ChatCategory } from './chat-category'
+import { ChatItem } from './chat-item'
+
+export const Chats = () => {
+ const { categories, chats } = useSortedChats()
+
+ return (
+
+ {chats.map(chat => (
+
+ ))}
+
+ {categories.map(category => (
+
+ {category.chats.map(chat => (
+
+ ))}
+
+ ))}
+
+ )
+}
diff --git a/packages/status-react/src/components/main-sidebar/index.tsx b/packages/status-react/src/components/main-sidebar/index.tsx
index 4f8ecd6..d14ce63 100644
--- a/packages/status-react/src/components/main-sidebar/index.tsx
+++ b/packages/status-react/src/components/main-sidebar/index.tsx
@@ -4,7 +4,7 @@ import { useAppState } from '../../contexts/app-context'
import { useAccount } from '../../protocol'
import { styled } from '../../styles/config'
import { Separator } from '../../system'
-import { Channels } from './components/channels'
+import { Chats } from './components/chats'
import { CommunityInfo } from './components/community-info'
import { GetStarted } from './components/get-started'
@@ -19,7 +19,7 @@ export const MainSidebar = () => {
return (
-
+
{!account && (
<>
diff --git a/packages/status-react/src/protocol/index.tsx b/packages/status-react/src/protocol/index.tsx
index 3bc9445..54f7646 100644
--- a/packages/status-react/src/protocol/index.tsx
+++ b/packages/status-react/src/protocol/index.tsx
@@ -3,8 +3,8 @@ export type { Account } from './use-account'
export { useAccount } from './use-account'
export type { Chat } from './use-chat'
export { useChat } from './use-chat'
-export { useChats } from './use-chats'
export type { Member } from './use-members'
export { useMembers } from './use-members'
export type { Message, Reaction, Reactions } from './use-messages'
export { useMessages } from './use-messages'
+export { useSortedChats } from './use-sorted-chats'
diff --git a/packages/status-react/src/protocol/use-active-chat.tsx b/packages/status-react/src/protocol/use-active-chat.tsx
new file mode 100644
index 0000000..3107874
--- /dev/null
+++ b/packages/status-react/src/protocol/use-active-chat.tsx
@@ -0,0 +1,12 @@
+import { useMatch } from 'react-router-dom'
+
+import { useProtocol } from './provider'
+
+export const useActiveChat = () => {
+ const { client } = useProtocol()
+
+ const { params } = useMatch(':id')!
+ const chatId = params.id!
+
+ return client.community.getChat(chatId)
+}
diff --git a/packages/status-react/src/protocol/use-chats.tsx b/packages/status-react/src/protocol/use-chats.tsx
deleted file mode 100644
index 9c4dd1d..0000000
--- a/packages/status-react/src/protocol/use-chats.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import { useMemo } from 'react'
-
-import { useProtocol } from './provider'
-
-import type { Community } from '@status-im/js'
-
-export type Chat = Community['description']['chats'][0] & {
- id: string
-}
-
-export const useChats = (): Chat[] => {
- const { community } = useProtocol()
-
- return useMemo(() => {
- return Object.entries(community.chats)
- .map(([chatId, chat]) => ({ id: chatId, ...chat }))
- .sort((a, b) => {
- if (a.position < b.position) {
- return -1
- }
- if (a.position > b.position) {
- return 1
- }
- return 0
- })
- }, [community])
-}
diff --git a/packages/status-react/src/protocol/use-sorted-chats.tsx b/packages/status-react/src/protocol/use-sorted-chats.tsx
new file mode 100644
index 0000000..093656b
--- /dev/null
+++ b/packages/status-react/src/protocol/use-sorted-chats.tsx
@@ -0,0 +1,77 @@
+import { useMemo } from 'react'
+
+import { useProtocol } from './provider'
+
+import type { Community } from '@status-im/js'
+
+export type Chat = Community['description']['chats'][0] & {
+ id: string
+}
+
+export type Category = {
+ id: string
+ name: string
+ position: number
+ chats: Chat[]
+}
+
+type Result = {
+ categories: Category[]
+ chats: Chat[]
+}
+
+function sortByPosition(items: T[]): T[] {
+ items.sort((a, b) => {
+ if (a.position < b.position) {
+ return -1
+ }
+ if (a.position > b.position) {
+ return 1
+ }
+ return 0
+ })
+
+ return items
+}
+
+export const useSortedChats = (): Result => {
+ const { community } = useProtocol()
+
+ return useMemo(() => {
+ const categoryChats: Record = {}
+
+ const chats = Object.entries(community.chats).reduce(
+ (acc, [chatId, chat]) => {
+ const parsedChat: Chat = {
+ id: chatId,
+ ...chat,
+ }
+
+ if (chat.categoryId && community.categories[chat.categoryId]) {
+ categoryChats[chat.categoryId] ??= []
+ categoryChats[chat.categoryId].push(parsedChat)
+ } else {
+ acc.push(parsedChat)
+ }
+
+ return acc
+ },
+ []
+ )
+
+ const categories = Object.entries(categoryChats).map(([id, chats]) => {
+ const { name, position } = community.categories[id]
+ return {
+ id,
+ name,
+ position,
+ chats: sortByPosition(chats),
+ }
+ })
+
+ return {
+ categories: sortByPosition(categories),
+ chats: sortByPosition(chats),
+ }
+ }, [community])
+}