20 changed files with 952 additions and 805 deletions
File diff suppressed because it is too large
@ -0,0 +1,111 @@ |
|||
import React from 'react'; |
|||
|
|||
import { m } from 'framer-motion'; |
|||
import { FiSettings } from 'react-icons/fi'; |
|||
import { MdPublic } from 'react-icons/md'; |
|||
import TimeAgo from 'timeago-react'; |
|||
|
|||
import { SidebarItem } from '@components/layout/Sidebar/SidebarItem'; |
|||
import { Hashicon } from '@emeraldpay/hashicon-react'; |
|||
import { useAppSelector } from '@hooks/useAppSelector'; |
|||
import { IconButton, Tooltip } from '@meshtastic/components'; |
|||
import { Protobuf } from '@meshtastic/meshtasticjs'; |
|||
|
|||
export interface ChannelChatProps { |
|||
channel: Protobuf.Channel; |
|||
selectedIndex: number; |
|||
setSelectedIndex: (index: number) => void; |
|||
} |
|||
|
|||
export const ChannelChat = ({ |
|||
channel, |
|||
selectedIndex, |
|||
setSelectedIndex, |
|||
}: ChannelChatProps): JSX.Element => { |
|||
const myNodeNum = useAppSelector( |
|||
(state) => state.meshtastic.radio.hardware, |
|||
).myNodeNum; |
|||
const nodes = useAppSelector((state) => state.meshtastic.nodes).filter( |
|||
(node) => node.number !== myNodeNum, |
|||
); |
|||
const chats = useAppSelector((state) => state.meshtastic.chats); |
|||
const channels = useAppSelector( |
|||
(state) => state.meshtastic.radio.channels, |
|||
).filter((ch) => ch.role !== Protobuf.Channel_Role.DISABLED); |
|||
|
|||
return ( |
|||
<SidebarItem |
|||
key={channel.index} |
|||
selected={channel.index === selectedIndex} |
|||
setSelected={(): void => { |
|||
setSelectedIndex(channel.index); |
|||
}} |
|||
actions={<IconButton icon={<FiSettings />} />} |
|||
> |
|||
<Tooltip |
|||
content={ |
|||
channel.settings?.name.length |
|||
? channel.settings.name |
|||
: `CH: ${channel.index}` |
|||
} |
|||
> |
|||
<div className="flex h-8 w-8 rounded-full bg-gray-200 dark:bg-primaryDark dark:text-white"> |
|||
<div className="m-auto"> |
|||
{channel.role === Protobuf.Channel_Role.PRIMARY ? ( |
|||
<MdPublic /> |
|||
) : ( |
|||
<p> |
|||
{channel.settings?.name.length |
|||
? channel.settings.name.substring(0, 3).toUpperCase() |
|||
: `CH: ${channel.index}`} |
|||
</p> |
|||
)} |
|||
</div> |
|||
</div> |
|||
</Tooltip> |
|||
{chats[channel.index]?.messages.length ? ( |
|||
<> |
|||
<div className="mx-2 flex h-8"> |
|||
{[ |
|||
...new Set( |
|||
chats[channel.index]?.messages.flatMap(({ message }) => [ |
|||
message.packet.from, |
|||
]), |
|||
), |
|||
] |
|||
.sort() |
|||
.map((nodeId) => { |
|||
return ( |
|||
<Tooltip |
|||
key={nodeId} |
|||
content={ |
|||
nodes.find((node) => node.number === nodeId)?.user |
|||
?.longName ?? 'UNK' |
|||
} |
|||
> |
|||
<div className="flex h-full"> |
|||
<m.div |
|||
whileHover={{ scale: 1.1 }} |
|||
className="my-auto -ml-2" |
|||
> |
|||
<Hashicon value={nodeId.toString()} size={20} /> |
|||
</m.div> |
|||
</div> |
|||
</Tooltip> |
|||
); |
|||
})} |
|||
</div> |
|||
<div className="my-auto ml-auto text-xs font-semibold dark:text-gray-400"> |
|||
{chats[channel.index].messages.length ? ( |
|||
<TimeAgo datetime={chats[channel.index].lastInterraction} /> |
|||
) : ( |
|||
<div>No messages</div> |
|||
)} |
|||
</div> |
|||
</> |
|||
) : ( |
|||
<div className="my-auto dark:text-white">No messages</div> |
|||
)} |
|||
</SidebarItem> |
|||
); |
|||
}; |
|||
@ -0,0 +1,40 @@ |
|||
import React from 'react'; |
|||
|
|||
import { FiSettings } from 'react-icons/fi'; |
|||
|
|||
import { SidebarItem } from '@components/layout/Sidebar/SidebarItem'; |
|||
import type { Node } from '@core/slices/meshtasticSlice'; |
|||
import { Hashicon } from '@emeraldpay/hashicon-react'; |
|||
import { IconButton } from '@meshtastic/components'; |
|||
|
|||
export interface DmChatProps { |
|||
node: Node; |
|||
selectedIndex: number; |
|||
setSelectedIndex: (index: number) => void; |
|||
} |
|||
|
|||
export const DmChat = ({ |
|||
node, |
|||
selectedIndex, |
|||
setSelectedIndex, |
|||
}: DmChatProps): JSX.Element => { |
|||
return ( |
|||
<SidebarItem |
|||
key={node.number} |
|||
selected={node.number === selectedIndex} |
|||
setSelected={(): void => { |
|||
setSelectedIndex(node.number); |
|||
}} |
|||
actions={<IconButton icon={<FiSettings />} />} |
|||
> |
|||
<div className="flex dark:text-white"> |
|||
<div className="m-auto"> |
|||
<Hashicon value={node.number.toString()} size={32} /> |
|||
</div> |
|||
</div> |
|||
<div className="my-auto mr-auto font-semibold dark:text-white"> |
|||
{node.user?.longName ?? 'Unknown'} |
|||
</div> |
|||
</SidebarItem> |
|||
); |
|||
}; |
|||
Loading…
Reference in new issue