connect UI to protocol
This commit is contained in:
parent
65d9c33bb8
commit
d0f4104b53
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import React, { useMemo, useRef, useState } from 'react'
|
import React, { useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
|
@ -2,42 +2,42 @@ import React from 'react'
|
||||||
|
|
||||||
import { BellIcon } from '~/src/icons/bell-icon'
|
import { BellIcon } from '~/src/icons/bell-icon'
|
||||||
import { ContextMenu, DropdownMenu } from '~/src/system'
|
import { ContextMenu, DropdownMenu } from '~/src/system'
|
||||||
import { useAlertDialog } from '~/src/system/dialog/alert-dialog'
|
// import { useAlertDialog } from '~/src/system/dialog/alert-dialog'
|
||||||
import { useDialog } from '~/src/system/dialog/dialog'
|
// import { useDialog } from '~/src/system/dialog/dialog'
|
||||||
|
|
||||||
import { UserProfileDialog } from '../user-profile-dialog'
|
// import { UserProfileDialog } from '../user-profile-dialog'
|
||||||
import { EditGroupChatDialog } from './edit-group-chat-dialog'
|
// import { EditGroupChatDialog } from './edit-group-chat-dialog'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: 'dropdown' | 'context'
|
type: 'dropdown' | 'context'
|
||||||
chatType: 'channel' | 'chat' | 'group-chat'
|
// chatType: 'channel' | 'chat' | 'group-chat'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatMenu = (props: Props) => {
|
export const ChatMenu = (props: Props) => {
|
||||||
const { type, chatType } = props
|
const { type } = props
|
||||||
|
|
||||||
const Menu = type === 'dropdown' ? DropdownMenu : ContextMenu
|
const Menu = type === 'dropdown' ? DropdownMenu : ContextMenu
|
||||||
|
|
||||||
const userProfileDialog = useDialog(UserProfileDialog)
|
// const userProfileDialog = useDialog(UserProfileDialog)
|
||||||
const editGroupChatDialog = useDialog(EditGroupChatDialog)
|
// const editGroupChatDialog = useDialog(EditGroupChatDialog)
|
||||||
|
|
||||||
const deleteChatDialog = useAlertDialog({
|
// const deleteChatDialog = useAlertDialog({
|
||||||
title: 'Delete Chat',
|
// title: 'Delete Chat',
|
||||||
description: 'Are you sure you want to delete this chat?',
|
// description: 'Are you sure you want to delete this chat?',
|
||||||
actionLabel: 'Delete',
|
// actionLabel: 'Delete',
|
||||||
actionVariant: 'danger',
|
// actionVariant: 'danger',
|
||||||
cancelLabel: 'Keep',
|
// cancelLabel: 'Keep',
|
||||||
})
|
// })
|
||||||
const leaveGroupDialog = useAlertDialog({
|
// const leaveGroupDialog = useAlertDialog({
|
||||||
title: 'Leave Group',
|
// title: 'Leave Group',
|
||||||
description: 'Are you sure you want to leave this group chat?',
|
// description: 'Are you sure you want to leave this group chat?',
|
||||||
actionLabel: 'Leave',
|
// actionLabel: 'Leave',
|
||||||
actionVariant: 'danger',
|
// actionVariant: 'danger',
|
||||||
cancelLabel: 'Stay',
|
// cancelLabel: 'Stay',
|
||||||
})
|
// })
|
||||||
|
|
||||||
const commonMenuItems = (
|
return (
|
||||||
<>
|
<Menu>
|
||||||
<Menu.TriggerItem label="Mute Chat" icon={<BellIcon />}>
|
<Menu.TriggerItem label="Mute Chat" icon={<BellIcon />}>
|
||||||
<Menu.Item>For 15 min</Menu.Item>
|
<Menu.Item>For 15 min</Menu.Item>
|
||||||
<Menu.Item>For 1 hour</Menu.Item>
|
<Menu.Item>For 1 hour</Menu.Item>
|
||||||
|
@ -52,55 +52,55 @@ export const ChatMenu = (props: Props) => {
|
||||||
<Menu.Item>Last 3 days</Menu.Item>
|
<Menu.Item>Last 3 days</Menu.Item>
|
||||||
<Menu.Item>Last 7 days</Menu.Item>
|
<Menu.Item>Last 7 days</Menu.Item>
|
||||||
</Menu.TriggerItem>
|
</Menu.TriggerItem>
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (chatType === 'channel') {
|
|
||||||
return <Menu>{commonMenuItems}</Menu>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chatType === 'group-chat') {
|
|
||||||
return (
|
|
||||||
<Menu>
|
|
||||||
<Menu.Item icon={<BellIcon />}>Add / remove from group</Menu.Item>
|
|
||||||
<Menu.Item
|
|
||||||
icon={<BellIcon />}
|
|
||||||
onSelect={() => editGroupChatDialog.open({})}
|
|
||||||
>
|
|
||||||
Edit name and image
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Separator />
|
|
||||||
{commonMenuItems}
|
|
||||||
<Menu.Separator />
|
|
||||||
<Menu.Item
|
|
||||||
icon={<BellIcon />}
|
|
||||||
danger
|
|
||||||
onSelect={() => leaveGroupDialog.open()}
|
|
||||||
>
|
|
||||||
Leave Chat
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
// if (chatType === 'channel') {
|
||||||
<Menu>
|
// return <Menu>{commonMenuItems}</Menu>
|
||||||
<Menu.Item
|
// }
|
||||||
icon={<BellIcon />}
|
|
||||||
onSelect={() => userProfileDialog.open({ name: 'Satoshi' })}
|
// if (chatType === 'group-chat') {
|
||||||
>
|
// return (
|
||||||
View Profile
|
// <Menu>
|
||||||
</Menu.Item>
|
// <Menu.Item icon={<BellIcon />}>Add / remove from group</Menu.Item>
|
||||||
<Menu.Separator />
|
// <Menu.Item
|
||||||
{commonMenuItems}
|
// icon={<BellIcon />}
|
||||||
<Menu.Separator />
|
// onSelect={() => editGroupChatDialog.open({})}
|
||||||
<Menu.Item
|
// >
|
||||||
icon={<BellIcon />}
|
// Edit name and image
|
||||||
danger
|
// </Menu.Item>
|
||||||
onSelect={() => deleteChatDialog.open()}
|
// <Menu.Separator />
|
||||||
>
|
// {commonMenuItems}
|
||||||
Delete Chat
|
// <Menu.Separator />
|
||||||
</Menu.Item>
|
// <Menu.Item
|
||||||
</Menu>
|
// icon={<BellIcon />}
|
||||||
)
|
// danger
|
||||||
|
// onSelect={() => leaveGroupDialog.open()}
|
||||||
|
// >
|
||||||
|
// Leave Chat
|
||||||
|
// </Menu.Item>
|
||||||
|
// </Menu>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return (
|
||||||
|
// <Menu>
|
||||||
|
// <Menu.Item
|
||||||
|
// icon={<BellIcon />}
|
||||||
|
// onSelect={() => userProfileDialog.open({ name: 'Satoshi' })}
|
||||||
|
// >
|
||||||
|
// View Profile
|
||||||
|
// </Menu.Item>
|
||||||
|
// <Menu.Separator />
|
||||||
|
// {commonMenuItems}
|
||||||
|
// <Menu.Separator />
|
||||||
|
// <Menu.Item
|
||||||
|
// icon={<BellIcon />}
|
||||||
|
// danger
|
||||||
|
// onSelect={() => deleteChatDialog.open()}
|
||||||
|
// >
|
||||||
|
// Delete Chat
|
||||||
|
// </Menu.Item>
|
||||||
|
// </Menu>
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ export const ChannelItem = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<ContextMenuTrigger>
|
<ContextMenuTrigger>
|
||||||
<SidebarItem {...sidebarItemProps}>#{children}</SidebarItem>
|
<SidebarItem {...sidebarItemProps}>#{children}</SidebarItem>
|
||||||
<ChatMenu type="context" chatType="channel" />
|
<ChatMenu type="context" />
|
||||||
</ContextMenuTrigger>
|
</ContextMenuTrigger>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,28 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { Box } from '~/src/system'
|
|
||||||
import { useChats } from '~/src/protocol'
|
import { useChats } from '~/src/protocol'
|
||||||
|
import { Box } from '~/src/system'
|
||||||
|
|
||||||
import { ChannelGroup } from './channel-group'
|
// import { ChannelGroup } from './channel-group'
|
||||||
import { ChannelItem } from './channel-item'
|
import { ChannelItem } from './channel-item'
|
||||||
|
|
||||||
|
|
||||||
export const Channels = () => {
|
export const Channels = () => {
|
||||||
|
|
||||||
const chats = useChats()
|
const chats = useChats()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box css={{padding:'8px 0'}}>
|
<Box css={{ padding: '24px 0', overflow: 'auto' }}>
|
||||||
{chats.map((chat) => (
|
{chats.map(chat => (
|
||||||
<ChannelItem
|
<ChannelItem
|
||||||
key={chat.id}
|
key={chat.id}
|
||||||
to={`/${chat.id}`}
|
to={`/${chat.id}`}
|
||||||
unread={false}
|
unread={false}
|
||||||
muted={false}
|
muted={false}
|
||||||
|
name={chat.identity?.displayName}
|
||||||
|
color={chat.identity?.color}
|
||||||
>
|
>
|
||||||
{chat.identity!.displayName}
|
{chat.identity!.displayName}
|
||||||
</ChannelItem>
|
</ChannelItem>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* {Object.entries(community.chats).map(([group, channels]) => (
|
|
||||||
<ChannelGroup key={group} name={group}>
|
|
||||||
{channels.map(channel => (
|
|
||||||
<ChannelItem
|
|
||||||
key={group + channel}
|
|
||||||
to={`/${channel}`}
|
|
||||||
unread={channel === 'general'}
|
|
||||||
muted={channel === 'random'}
|
|
||||||
>
|
|
||||||
{channel}
|
|
||||||
</ChannelItem>
|
|
||||||
))}
|
|
||||||
</ChannelGroup>
|
|
||||||
))} */}
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useCommunity } from '~/src/protocol'
|
import { useProtocol } from '~/src/protocol'
|
||||||
import { Button, CopyInput, Dialog, Flex, Grid, Text } from '~/src/system'
|
import { Button, CopyInput, Dialog, Flex, Grid, Text } from '~/src/system'
|
||||||
|
|
||||||
export const CommunityDialog = () => {
|
export const CommunityDialog = () => {
|
||||||
const { identity, publicKey='0xTODO' } = useCommunity()
|
const { community } = useProtocol()
|
||||||
const { displayName, description} = identity
|
const { displayName, description } = community.identity!
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title={displayName}>
|
<Dialog title={displayName}>
|
||||||
|
@ -16,7 +15,7 @@ export const CommunityDialog = () => {
|
||||||
<Dialog.Separator />
|
<Dialog.Separator />
|
||||||
<Dialog.Body>
|
<Dialog.Body>
|
||||||
<Grid gap={3}>
|
<Grid gap={3}>
|
||||||
<CopyInput label="Community Public Key" value={publicKey} />
|
<CopyInput label="Community Public Key" value="0xTODO" />
|
||||||
<Text size="13" color="gray">
|
<Text size="13" color="gray">
|
||||||
To access this community, paste community public key in Status
|
To access this community, paste community public key in Status
|
||||||
desktop or mobile app.
|
desktop or mobile app.
|
||||||
|
@ -54,7 +53,7 @@ export const CommunityDialog = () => {
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<Button href="https://status.im/get">Download Status for Mac</Button>
|
<Button href="https://status.im/get">Download Status</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useCommunity, useMembers } from '~/src/protocol'
|
import { useMembers, useProtocol } from '~/src/protocol'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { Avatar, DialogTrigger, Text } from '~/src/system'
|
import { Avatar, DialogTrigger, Text } from '~/src/system'
|
||||||
|
|
||||||
import { CommunityDialog } from './community-dialog'
|
import { CommunityDialog } from './community-dialog'
|
||||||
|
|
||||||
export const CommunityInfo = () => {
|
export const CommunityInfo = () => {
|
||||||
const community = useCommunity()
|
const { community } = useProtocol()
|
||||||
const members = useMembers()
|
const members = useMembers()
|
||||||
console.log("file: index.tsx > line 11 > CommunityInfo > community", community)
|
|
||||||
|
const { displayName, color } = community.identity!
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<Button>
|
<Button>
|
||||||
<Avatar size={36} />
|
<Avatar size={36} name={displayName} color={color} />
|
||||||
<div>
|
<div>
|
||||||
<Text>{community.identity?.displayName}</Text>
|
<Text>{displayName}</Text>
|
||||||
<Text color="gray" size={12}>
|
<Text color="gray" size={12}>
|
||||||
{members.length} members
|
{members.length} members
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -19,10 +19,10 @@ export const GetStarted = () => {
|
||||||
// TODO: Add skip logic
|
// TODO: Add skip logic
|
||||||
}
|
}
|
||||||
|
|
||||||
const [account, { createAccount }] = useAccount()
|
const { account, createAccount } = useAccount()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" align="center" gap={5}>
|
<Flex direction="column" align="center" gap={5} css={{ padding: '30px 0' }}>
|
||||||
<svg
|
<svg
|
||||||
width={65}
|
width={65}
|
||||||
height={64}
|
height={64}
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface Props {
|
||||||
export const ThrowawayProfileFoundDialog = (props: Props) => {
|
export const ThrowawayProfileFoundDialog = (props: Props) => {
|
||||||
const { onSkip } = props
|
const { onSkip } = props
|
||||||
|
|
||||||
const [account] = useAccount()
|
const { account } = useAccount()
|
||||||
|
|
||||||
const handleLoadThrowawayProfile = () => {
|
const handleLoadThrowawayProfile = () => {
|
||||||
// TODO: load throwaway profile
|
// TODO: load throwaway profile
|
||||||
|
@ -24,8 +24,8 @@ if (!account) {
|
||||||
<Dialog title="Throwaway Profile Found">
|
<Dialog title="Throwaway Profile Found">
|
||||||
<Dialog.Body gap="5">
|
<Dialog.Body gap="5">
|
||||||
<Flex direction="column" align="center" gap="2">
|
<Flex direction="column" align="center" gap="2">
|
||||||
<Avatar size={64} src={account.imageUrl} />
|
<Avatar size={64} />
|
||||||
<Heading weight="600">{account.name}</Heading>
|
<Heading weight="600">{account.username}</Heading>
|
||||||
<Text color="gray">
|
<Text color="gray">
|
||||||
Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
Chatkey: 0x63FaC9201494f0bd17B9892B9fae4d52fe3BD377
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { ChatMenu } from '~/src/components/chat-menu'
|
import { ChatMenu } from '~/src/components/chat-menu'
|
||||||
import { useChannel } from '~/src/protocol'
|
|
||||||
import { ContextMenuTrigger } from '~/src/system'
|
import { ContextMenuTrigger } from '~/src/system'
|
||||||
|
|
||||||
import { SidebarItem } from '../sidebar-item'
|
import { SidebarItem } from '../sidebar-item'
|
||||||
|
@ -15,12 +14,10 @@ interface Props extends SidebarItemProps {
|
||||||
export const ChatItem = (props: Props) => {
|
export const ChatItem = (props: Props) => {
|
||||||
const { children, ...sidebarItemProps } = props
|
const { children, ...sidebarItemProps } = props
|
||||||
|
|
||||||
const chat = useChannel(children)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenuTrigger>
|
<ContextMenuTrigger>
|
||||||
<SidebarItem {...sidebarItemProps}>{children}</SidebarItem>
|
<SidebarItem {...sidebarItemProps}>{children}</SidebarItem>
|
||||||
<ChatMenu type="context" chatType={chat.type} />
|
<ChatMenu type="context" />
|
||||||
</ContextMenuTrigger>
|
</ContextMenuTrigger>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// @ts-nocheck
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { EditIcon } from '~/src/icons/edit-icon'
|
import { EditIcon } from '~/src/icons/edit-icon'
|
||||||
|
|
|
@ -12,10 +12,12 @@ interface Props {
|
||||||
muted: boolean
|
muted: boolean
|
||||||
unread: boolean
|
unread: boolean
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
|
name?: string
|
||||||
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SidebarItem = (props: Props, ref: Ref<HTMLAnchorElement>) => {
|
const SidebarItem = (props: Props, ref: Ref<HTMLAnchorElement>) => {
|
||||||
const { muted, unread, children, ...buttonProps } = props
|
const { muted, unread, children, name, color, ...buttonProps } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
@ -23,7 +25,7 @@ const SidebarItem = (props: Props, ref: Ref<HTMLAnchorElement>) => {
|
||||||
state={muted ? 'muted' : unread ? 'unread' : undefined}
|
state={muted ? 'muted' : unread ? 'unread' : undefined}
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
>
|
>
|
||||||
<Avatar size={24} />
|
<Avatar size={24} name={name} color={color} />
|
||||||
{children}
|
{children}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,25 +1,23 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useAppState } from '~/src/contexts/app-context'
|
import { useAppState } from '~/src/contexts/app-context'
|
||||||
|
import { useAccount } from '~/src/protocol'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { Separator } from '~/src/system'
|
import { Separator } from '~/src/system'
|
||||||
|
|
||||||
import { Channels } from './components/channels'
|
import { Channels } from './components/channels'
|
||||||
import { CommunityInfo } from './components/community-info'
|
import { CommunityInfo } from './components/community-info'
|
||||||
import { GetStarted } from './components/get-started'
|
import { GetStarted } from './components/get-started'
|
||||||
import { useAccount } from '~/src/protocol'
|
|
||||||
// import { Messages } from './components/messages'
|
// import { Messages } from './components/messages'
|
||||||
|
|
||||||
|
|
||||||
export const MainSidebar = () => {
|
export const MainSidebar = () => {
|
||||||
const { options } = useAppState()
|
const { options } = useAppState()
|
||||||
|
const { account } = useAccount()
|
||||||
|
|
||||||
if (options.enableSidebar === false) {
|
if (options.enableSidebar === false) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const [account] = useAccount()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<CommunityInfo />
|
<CommunityInfo />
|
||||||
|
@ -28,7 +26,7 @@ export const MainSidebar = () => {
|
||||||
<Messages /> */}
|
<Messages /> */}
|
||||||
{!account && (
|
{!account && (
|
||||||
<>
|
<>
|
||||||
<Separator css={{ margin: '16px 0' }} />
|
<Separator />
|
||||||
<GetStarted />
|
<GetStarted />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -3,25 +3,32 @@ import React from 'react'
|
||||||
import { useAccount } from '~/src/protocol'
|
import { useAccount } from '~/src/protocol'
|
||||||
import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system'
|
import { Avatar, Dialog, EmojiHash, Flex, Heading, Text } from '~/src/system'
|
||||||
|
|
||||||
export const DisconnectDialog = () => {
|
import type { Account } from '~/src/protocol'
|
||||||
const [account] = useAccount()
|
|
||||||
|
interface Props {
|
||||||
|
account: Account
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DisconnectDialog = (props: Props) => {
|
||||||
|
const { deleteAccount } = useAccount()
|
||||||
|
const { account } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title="Disconnect">
|
<Dialog title="Disconnect">
|
||||||
<Dialog.Body gap="5">
|
<Dialog.Body gap="5">
|
||||||
<Text>Do you want to disconnect your profile from this browser?</Text>
|
<Text>Do you want to disconnect your profile from this browser?</Text>
|
||||||
<Flex direction="column" align="center" gap="2">
|
<Flex direction="column" align="center" gap="2">
|
||||||
<Avatar size={64} src={account.imageUrl} />
|
<Avatar size={64} />
|
||||||
<Heading weight="600">{account.name}</Heading>
|
<Heading weight="600">{account.username}</Heading>
|
||||||
<Text color="gray">
|
<Text color="gray">Chatkey: {account.chatKey}</Text>
|
||||||
Chatkey: {account.chatKey}
|
|
||||||
</Text>
|
|
||||||
<EmojiHash />
|
<EmojiHash />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
<Dialog.Actions>
|
<Dialog.Actions>
|
||||||
<Dialog.Cancel>Stay Connected</Dialog.Cancel>
|
<Dialog.Cancel>Stay Connected</Dialog.Cancel>
|
||||||
<Dialog.Action variant="danger">Disconnect</Dialog.Action>
|
<Dialog.Action variant="danger" onClick={deleteAccount}>
|
||||||
|
Disconnect
|
||||||
|
</Dialog.Action>
|
||||||
</Dialog.Actions>
|
</Dialog.Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { useAccount, useMembers } from '~/src/protocol'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { Grid, Heading } from '~/src/system'
|
import { Grid, Heading } from '~/src/system'
|
||||||
|
|
||||||
import { MemberGroup } from './member-group'
|
import { MemberGroup } from './member-group'
|
||||||
import { MemberItem } from './member-item'
|
import { MemberItem } from './member-item'
|
||||||
import { UserItem } from './user-item'
|
import { UserItem } from './user-item'
|
||||||
import { useMembers } from '~/src/protocol'
|
|
||||||
|
|
||||||
export function MemberSidebar() {
|
export function MemberSidebar() {
|
||||||
const members = useMembers()
|
const members = useMembers()
|
||||||
|
const { account } = useAccount()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
@ -17,9 +18,11 @@ export function MemberSidebar() {
|
||||||
Members
|
Members
|
||||||
</Heading>
|
</Heading>
|
||||||
<Grid gap="2">
|
<Grid gap="2">
|
||||||
|
{account && (
|
||||||
<MemberGroup label="You">
|
<MemberGroup label="You">
|
||||||
<UserItem />
|
<UserItem account={account} />
|
||||||
</MemberGroup>
|
</MemberGroup>
|
||||||
|
)}
|
||||||
<MemberGroup label="Online">
|
<MemberGroup label="Online">
|
||||||
{members.map(member => (
|
{members.map(member => (
|
||||||
<MemberItem
|
<MemberItem
|
||||||
|
|
|
@ -17,10 +17,7 @@ export const MemberItem = (props: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex gap="2" align="center" css={{ height: 56 }}>
|
<Flex gap="2" align="center" css={{ height: 56 }}>
|
||||||
<Avatar
|
<Avatar size={32} indicator={indicator} />
|
||||||
size={32}
|
|
||||||
indicator={indicator}
|
|
||||||
/>
|
|
||||||
<div>
|
<div>
|
||||||
<Flex align="center" gap={1}>
|
<Flex align="center" gap={1}>
|
||||||
<Text size="15" color="accent" truncate>
|
<Text size="15" color="accent" truncate>
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useAccount } from '~/src/protocol'
|
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { Avatar, DialogTrigger, EthAddress, Flex, Text } from '~/src/system'
|
import { Avatar, DialogTrigger, EthAddress, Flex, Text } from '~/src/system'
|
||||||
|
|
||||||
import { DisconnectDialog } from './disconnect-dialog'
|
import { DisconnectDialog } from './disconnect-dialog'
|
||||||
|
|
||||||
export const UserItem = () => {
|
import type { Account } from '~/src/protocol'
|
||||||
const [account] = useAccount()
|
|
||||||
console.log("file: user-item.tsx > line 11 > UserItem > account", account)
|
|
||||||
|
|
||||||
if (!account) {
|
interface Props {
|
||||||
return null
|
account: Account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const UserItem = (props: Props) => {
|
||||||
|
const { account } = props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex align="center" justify="between">
|
<Flex align="center" justify="between">
|
||||||
<Flex gap="2" align="center" css={{ height: 56 }}>
|
<Flex gap="2" align="center" css={{ height: 56 }}>
|
||||||
<Avatar size={32} src={account.imageUrl} />
|
<Avatar size={32} />
|
||||||
<div>
|
<div>
|
||||||
<Flex align="center" gap={1}>
|
<Flex align="center" gap={1}>
|
||||||
<Text size="15" color="accent">
|
<Text size="15" color="accent">
|
||||||
{account.name}
|
{account.username}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
<EthAddress size={10} color="gray">
|
<EthAddress size={10} color="gray">
|
||||||
|
@ -30,7 +30,6 @@ export const UserItem = () => {
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
|
||||||
<DialogTrigger>
|
<DialogTrigger>
|
||||||
<DisconnectButton>
|
<DisconnectButton>
|
||||||
<svg
|
<svg
|
||||||
|
@ -50,10 +49,7 @@ export const UserItem = () => {
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</DisconnectButton>
|
</DisconnectButton>
|
||||||
{account && (
|
<DisconnectDialog account={account} />
|
||||||
|
|
||||||
<DisconnectDialog />
|
|
||||||
)}
|
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,27 +14,27 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emojis: Record<Reaction, { url: string; symbol: string }> = {
|
export const emojis: Record<Reaction, { url: string; symbol: string }> = {
|
||||||
heart: {
|
LOVE: {
|
||||||
symbol: '❤️',
|
symbol: '❤️',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/2764.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/2764.svg',
|
||||||
},
|
},
|
||||||
'thumbs-up': {
|
THUMBS_UP: {
|
||||||
symbol: '👍️',
|
symbol: '👍️',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44d.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44d.svg',
|
||||||
},
|
},
|
||||||
'thumbs-down': {
|
THUMBS_DOWN: {
|
||||||
symbol: '👎️',
|
symbol: '👎️',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44e.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f44e.svg',
|
||||||
},
|
},
|
||||||
smile: {
|
LAUGH: {
|
||||||
symbol: '😆',
|
symbol: '😆',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f606.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f606.svg',
|
||||||
},
|
},
|
||||||
sad: {
|
SAD: {
|
||||||
symbol: '😭',
|
symbol: '😭',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f62d.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f62d.svg',
|
||||||
},
|
},
|
||||||
angry: {
|
ANGRY: {
|
||||||
symbol: '😡',
|
symbol: '😡',
|
||||||
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f621.svg',
|
url: 'https://twemoji.maxcdn.com/v/latest/svg/1f621.svg',
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,35 +1,25 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
import { useCommunity } from '~/src/protocol/use-community'
|
import { useProtocol } from '~/src/protocol'
|
||||||
import { Avatar, Checkbox, Dialog, Flex, Text } from '~/src/system'
|
import { Avatar, Checkbox, Dialog, Flex, Text } from '~/src/system'
|
||||||
|
|
||||||
export const WelcomeDialog = () => {
|
export const WelcomeDialog = () => {
|
||||||
const { name, imageUrl, requestNeeded } = useCommunity()
|
const { community } = useProtocol()
|
||||||
|
const { identity } = community
|
||||||
|
|
||||||
const [agreed, setAgreed] = useState(false)
|
const [agreed, setAgreed] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog title={`Welcome to ${name}`} size={640}>
|
<Dialog title={`Welcome to ${identity?.displayName}`} size={640}>
|
||||||
<Dialog.Body gap="4">
|
<Dialog.Body gap="4">
|
||||||
<Flex justify="center">
|
<Flex justify="center">
|
||||||
<Avatar size="64" src={imageUrl} />
|
<Avatar
|
||||||
|
size="64"
|
||||||
|
src={identity?.displayName}
|
||||||
|
color={identity?.color}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Text>
|
<Text>{identity?.description}</Text>
|
||||||
CryptoKitties sed ut perspiciatis unde omnis iste natus error sit
|
|
||||||
voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque
|
|
||||||
ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
|
|
||||||
dicta sunt explicabo.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Ut enim ad minim veniam Excepteur sint occaecat cupidatat non proident
|
|
||||||
Duis aute irure Dolore eu fugiat nulla pariatur 🚗 consectetur
|
|
||||||
adipiscing elit.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit
|
|
||||||
aut fugit, sed quia consequuntur magni dolores eos qui ratione
|
|
||||||
voluptatem sequi nesciunt.
|
|
||||||
</Text>
|
|
||||||
<Flex>
|
<Flex>
|
||||||
<Checkbox checked={agreed} onChange={setAgreed}>
|
<Checkbox checked={agreed} onChange={setAgreed}>
|
||||||
I agree with the above
|
I agree with the above
|
||||||
|
@ -38,7 +28,8 @@ export const WelcomeDialog = () => {
|
||||||
</Dialog.Body>
|
</Dialog.Body>
|
||||||
<Dialog.Actions>
|
<Dialog.Actions>
|
||||||
<Dialog.Action disabled={agreed === false}>
|
<Dialog.Action disabled={agreed === false}>
|
||||||
{requestNeeded ? 'Request to Join' : `Join ${name}`}
|
Request to Join
|
||||||
|
{/* {requestNeeded ? 'Request to Join' : `Join ${name}`} */}
|
||||||
</Dialog.Action>
|
</Dialog.Action>
|
||||||
</Dialog.Actions>
|
</Dialog.Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
|
@ -1,27 +1,26 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useMatch } from 'react-router-dom'
|
// import { PinIcon } from '~/src/icons/pin-icon'
|
||||||
|
import { Avatar, Flex, Text } from '~/src/system'
|
||||||
|
|
||||||
import { PinIcon } from '~/src/icons/pin-icon'
|
// import { PinnedMessagesDialog } from './pinned-messages-dialog'
|
||||||
import { Avatar, DialogTrigger, Flex, Text } from '~/src/system'
|
import type { Chat } from '~/src/protocol'
|
||||||
|
|
||||||
import { PinnedMessagesDialog } from './pinned-messages-dialog'
|
|
||||||
|
|
||||||
import type { Channel } from '~/src/protocol'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chat: Channel
|
chat: Chat
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatInfo = (props: Props) => {
|
export const ChatInfo = (props: Props) => {
|
||||||
const { chat } = props
|
const { chat } = props
|
||||||
console.log("file: index.tsx > line 18 > ChatInfo > chat", chat)
|
|
||||||
|
|
||||||
|
|
||||||
// if (chat.type == 'channel') {
|
// if (chat.type == 'channel') {
|
||||||
return (
|
return (
|
||||||
<Flex align="center" gap="2">
|
<Flex align="center" gap="2">
|
||||||
<Avatar size={36} />
|
<Avatar
|
||||||
|
size={36}
|
||||||
|
name={chat.identity?.displayName}
|
||||||
|
color={chat.identity?.color}
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Text>#{chat.identity?.displayName}</Text>
|
<Text>#{chat.identity?.displayName}</Text>
|
||||||
<Flex align="center">
|
<Flex align="center">
|
||||||
|
|
|
@ -14,10 +14,11 @@ interface Props {
|
||||||
mode?: 'normal' | 'editing'
|
mode?: 'normal' | 'editing'
|
||||||
value?: string
|
value?: string
|
||||||
editing?: boolean
|
editing?: boolean
|
||||||
|
onSubmit: (value: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChatInput = (props: Props) => {
|
export const ChatInput = (props: Props) => {
|
||||||
const { value, editing } = props
|
const { value, editing, onSubmit } = props
|
||||||
|
|
||||||
const [inputValue, setInputValue] = useState(value ?? '')
|
const [inputValue, setInputValue] = useState(value ?? '')
|
||||||
const { state } = useChatContext()
|
const { state } = useChatContext()
|
||||||
|
@ -32,6 +33,13 @@ export const ChatInput = (props: Props) => {
|
||||||
setInputValue(event.target.value)
|
setInputValue(event.target.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (event.key === 'Enter' && event.shiftKey === false) {
|
||||||
|
onSubmit(inputValue)
|
||||||
|
setInputValue('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Box css={{ paddingBottom: 6 }}>
|
<Box css={{ paddingBottom: 6 }}>
|
||||||
|
@ -47,6 +55,7 @@ export const ChatInput = (props: Props) => {
|
||||||
placeholder="Message"
|
placeholder="Message"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
<Flex>
|
<Flex>
|
||||||
<IconButton label="Pick emoji">
|
<IconButton label="Pick emoji">
|
||||||
|
|
|
@ -25,7 +25,7 @@ export const InputReply = (props: Props) => {
|
||||||
<ReplyIcon />
|
<ReplyIcon />
|
||||||
</Icon>
|
</Icon>
|
||||||
<Text size="13" weight="500" truncate={false}>
|
<Text size="13" weight="500" truncate={false}>
|
||||||
{message.contact.name}
|
TODO: Add name
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
@ -36,14 +36,14 @@ export const InputReply = (props: Props) => {
|
||||||
<CrossIcon />
|
<CrossIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Flex>
|
</Flex>
|
||||||
{message.type === 'text' && (
|
{message.contentType === 'TEXT_PLAIN' && (
|
||||||
<Flex>
|
<Flex>
|
||||||
<Text size="13" truncate>
|
<Text size="13" truncate>
|
||||||
{message.text}
|
{message.text}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
{message.type === 'image' && (
|
{message.contentType === 'IMAGE' && (
|
||||||
<Image
|
<Image
|
||||||
src={message.imageUrl}
|
src={message.imageUrl}
|
||||||
width={56}
|
width={56}
|
||||||
|
@ -53,23 +53,6 @@ export const InputReply = (props: Props) => {
|
||||||
alt="message"
|
alt="message"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{message.type === 'image-text' && (
|
|
||||||
<Box>
|
|
||||||
<Flex>
|
|
||||||
<Text size="13" truncate>
|
|
||||||
{message.text}
|
|
||||||
</Text>
|
|
||||||
</Flex>
|
|
||||||
<Image
|
|
||||||
src={message.imageUrl}
|
|
||||||
width={56}
|
|
||||||
height={56}
|
|
||||||
fit="cover"
|
|
||||||
radius="bubble"
|
|
||||||
alt="message"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,11 @@ import React from 'react'
|
||||||
|
|
||||||
import { ReactionPopover } from '~/src/components/reaction-popover'
|
import { ReactionPopover } from '~/src/components/reaction-popover'
|
||||||
import { PencilIcon } from '~/src/icons/pencil-icon'
|
import { PencilIcon } from '~/src/icons/pencil-icon'
|
||||||
import { PinIcon } from '~/src/icons/pin-icon'
|
// import { PinIcon } from '~/src/icons/pin-icon'
|
||||||
import { ReactionIcon } from '~/src/icons/reaction-icon'
|
import { ReactionIcon } from '~/src/icons/reaction-icon'
|
||||||
import { ReplyIcon } from '~/src/icons/reply-icon'
|
import { ReplyIcon } from '~/src/icons/reply-icon'
|
||||||
import { TrashIcon } from '~/src/icons/trash-icon'
|
import { TrashIcon } from '~/src/icons/trash-icon'
|
||||||
import { UnpinIcon } from '~/src/icons/unpin-icon'
|
// import { UnpinIcon } from '~/src/icons/unpin-icon'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
|
@ -15,7 +15,7 @@ import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '~/src/system'
|
} from '~/src/system'
|
||||||
|
|
||||||
import type { Reactions } from '~/src/protocol/use-messages'
|
import type { Reaction, Reactions } from '~/src/protocol/use-messages'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
owner: boolean
|
owner: boolean
|
||||||
|
@ -23,6 +23,7 @@ interface Props {
|
||||||
onReplyClick: () => void
|
onReplyClick: () => void
|
||||||
onEditClick: () => void
|
onEditClick: () => void
|
||||||
onPinClick: () => void
|
onPinClick: () => void
|
||||||
|
onReactionClick: (reaction: Reaction) => void
|
||||||
reacting: boolean
|
reacting: boolean
|
||||||
onReactingChange: (reacting: boolean) => void
|
onReactingChange: (reacting: boolean) => void
|
||||||
reactions: Reactions
|
reactions: Reactions
|
||||||
|
@ -31,10 +32,11 @@ interface Props {
|
||||||
export const Actions = (props: Props) => {
|
export const Actions = (props: Props) => {
|
||||||
const {
|
const {
|
||||||
owner,
|
owner,
|
||||||
pinned,
|
// pinned,
|
||||||
onReplyClick,
|
onReplyClick,
|
||||||
onEditClick,
|
onEditClick,
|
||||||
onPinClick,
|
// onPinClick,
|
||||||
|
onReactionClick,
|
||||||
reacting,
|
reacting,
|
||||||
onReactingChange,
|
onReactingChange,
|
||||||
reactions,
|
reactions,
|
||||||
|
@ -47,7 +49,7 @@ export const Actions = (props: Props) => {
|
||||||
open={reacting}
|
open={reacting}
|
||||||
onOpenChange={onReactingChange}
|
onOpenChange={onReactingChange}
|
||||||
onClick={emoji => {
|
onClick={emoji => {
|
||||||
console.log(emoji)
|
onReactionClick(emoji)
|
||||||
onReactingChange(false)
|
onReactingChange(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -79,7 +81,7 @@ export const Actions = (props: Props) => {
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Tooltip label={pinned ? 'Unpin' : 'Pin'}>
|
{/* <Tooltip label={pinned ? 'Unpin' : 'Pin'}>
|
||||||
<IconButton
|
<IconButton
|
||||||
label={pinned ? 'Unpin message' : 'Pin message'}
|
label={pinned ? 'Unpin message' : 'Pin message'}
|
||||||
intent="info"
|
intent="info"
|
||||||
|
@ -88,7 +90,7 @@ export const Actions = (props: Props) => {
|
||||||
>
|
>
|
||||||
{pinned ? <UnpinIcon /> : <PinIcon />}
|
{pinned ? <UnpinIcon /> : <PinIcon />}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip> */}
|
||||||
{owner && (
|
{owner && (
|
||||||
<AlertDialogTrigger>
|
<AlertDialogTrigger>
|
||||||
<Tooltip label="Delete">
|
<Tooltip label="Delete">
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
|
||||||
|
import snarkdown from 'snarkdown'
|
||||||
|
|
||||||
import { UserProfileDialog } from '~/src/components/user-profile-dialog'
|
import { UserProfileDialog } from '~/src/components/user-profile-dialog'
|
||||||
import { useChatContext } from '~/src/contexts/chat-context'
|
import { useChatContext } from '~/src/contexts/chat-context'
|
||||||
import { BellIcon } from '~/src/icons/bell-icon'
|
import { BellIcon } from '~/src/icons/bell-icon'
|
||||||
import { PinIcon } from '~/src/icons/pin-icon'
|
import { PinIcon } from '~/src/icons/pin-icon'
|
||||||
|
import { useProtocol } from '~/src/protocol/provider'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
|
@ -25,10 +28,11 @@ import { Actions } from './actions'
|
||||||
import { MessageReply } from './message-reply'
|
import { MessageReply } from './message-reply'
|
||||||
import { MessageReactions } from './reactions'
|
import { MessageReactions } from './reactions'
|
||||||
|
|
||||||
import type { MessageType } from '~/src/protocol/use-messages'
|
import type { Message, Reaction } from '~/src/protocol/use-messages'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
message: MessageType
|
message: Message
|
||||||
|
previousMessage?: Message
|
||||||
}
|
}
|
||||||
|
|
||||||
// const MessageLink = forwardRef(function MessageLink(
|
// const MessageLink = forwardRef(function MessageLink(
|
||||||
|
@ -53,15 +57,17 @@ interface Props {
|
||||||
// })
|
// })
|
||||||
|
|
||||||
export const ChatMessage = (props: Props) => {
|
export const ChatMessage = (props: Props) => {
|
||||||
const { message } = props
|
const { client } = useProtocol()
|
||||||
console.log("🚀 > message", message)
|
|
||||||
|
const { message } = props
|
||||||
|
|
||||||
// const { type, contact, owner, mention, pinned, reply, reactions } = message
|
|
||||||
const owner = false
|
const owner = false
|
||||||
const mention = false
|
const mention = false
|
||||||
const pinned = false
|
const pinned = false
|
||||||
const reply = false
|
const reply = false
|
||||||
const { contentType, text, displayName, reactions } = message
|
|
||||||
|
const { messageId, chatId, contentType, clock, displayName, reactions } =
|
||||||
|
message
|
||||||
|
|
||||||
const [editing, setEditing] = useState(false)
|
const [editing, setEditing] = useState(false)
|
||||||
const [reacting, setReacting] = useState(false)
|
const [reacting, setReacting] = useState(false)
|
||||||
|
@ -70,26 +76,34 @@ export const ChatMessage = (props: Props) => {
|
||||||
|
|
||||||
const userProfileDialog = useDialog(UserProfileDialog)
|
const userProfileDialog = useDialog(UserProfileDialog)
|
||||||
|
|
||||||
const handleReplyClick = () => {
|
const handleMessageSubmit = (message: string) => {
|
||||||
dispatch({
|
client.community.sendTextMessage(
|
||||||
type: 'SET_REPLY',
|
chatId,
|
||||||
message,
|
message,
|
||||||
})
|
'0x0fa999097568d1fdcc39108a08d75340bd2cee5ec59c36799007150d0a9fc896'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReaction = (reaction: Reaction) => {
|
||||||
|
client.community.sendReaction(chatId, messageId, reaction)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReplyClick = () => {
|
||||||
|
dispatch({ type: 'SET_REPLY', message })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePinClick = () => {
|
const handlePinClick = () => {
|
||||||
// console.log(pinned)
|
// TODO: pin message
|
||||||
}
|
|
||||||
|
|
||||||
const handleReaction = (reaction: string) => {
|
|
||||||
console.log(reaction)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderMessage = () => {
|
const renderMessage = () => {
|
||||||
if (editing) {
|
if (editing) {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<ChatInput value={message?.text ?? ''} />
|
<ChatInput
|
||||||
|
value={message?.text ?? ''}
|
||||||
|
onSubmit={handleMessageSubmit}
|
||||||
|
/>
|
||||||
<Flex gap={2}>
|
<Flex gap={2}>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
@ -98,7 +112,9 @@ export const ChatMessage = (props: Props) => {
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small">Save</Button>
|
<Button size="small" onClick={handleMessageSubmit}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
@ -118,37 +134,30 @@ export const ChatMessage = (props: Props) => {
|
||||||
// </AlertDialogTrigger>{' '}
|
// </AlertDialogTrigger>{' '}
|
||||||
return <Text>{message.text}</Text>
|
return <Text>{message.text}</Text>
|
||||||
}
|
}
|
||||||
case 'image': {
|
case 'EMOJI': {
|
||||||
|
return (
|
||||||
|
<Text css={{ fontSize: '3rem', lineHeight: 1.1, letterSpacing: -2 }}>
|
||||||
|
{message.text}
|
||||||
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
case 'IMAGE': {
|
||||||
|
const blob = new Blob([message.image.payload], { type: 'image/jpeg' })
|
||||||
|
|
||||||
|
// TODO?: call URL.revokeObjectURL()
|
||||||
return (
|
return (
|
||||||
<Flex gap={1} css={{ paddingTop: '$2' }}>
|
<Flex gap={1} css={{ paddingTop: '$2' }}>
|
||||||
<Image
|
<Image
|
||||||
width={147}
|
width={150}
|
||||||
alt="message"
|
alt="message"
|
||||||
height={196}
|
height={150}
|
||||||
src={message.imageUrl}
|
src={URL.createObjectURL(blob)}
|
||||||
radius="bubble"
|
radius="bubble"
|
||||||
|
fit="cover"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case 'image-text': {
|
|
||||||
const { text, imageUrl } = message
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Text>{text}</Text>
|
|
||||||
<Flex gap={1} css={{ paddingTop: '$1' }}>
|
|
||||||
<Image
|
|
||||||
width={147}
|
|
||||||
alt="message"
|
|
||||||
height={196}
|
|
||||||
src={imageUrl}
|
|
||||||
radius="bubble"
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,12 +170,11 @@ export const ChatMessage = (props: Props) => {
|
||||||
<Box>
|
<Box>
|
||||||
<DropdownMenuTrigger>
|
<DropdownMenuTrigger>
|
||||||
<button type="button">
|
<button type="button">
|
||||||
{/* <Avatar size={44} src={contact.imageUrl} /> */}
|
<Avatar size={44} />
|
||||||
</button>
|
</button>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<Flex direction="column" align="center" gap="1">
|
<Flex direction="column" align="center" gap="1">
|
||||||
{/* <Avatar size="36" src={contact.imageUrl} /> */}
|
<Avatar size="36" />
|
||||||
{/* <Text>{contact.name}</Text> */}
|
|
||||||
<Text>{displayName}</Text>
|
<Text>{displayName}</Text>
|
||||||
<EmojiHash />
|
<EmojiHash />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -197,20 +205,22 @@ export const ChatMessage = (props: Props) => {
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box css={{ flex: 1 }}>
|
<Box css={{ flex: 1 }}>
|
||||||
{pinned && (
|
{/* {pinned && (
|
||||||
<Flex gap={1}>
|
<Flex gap={1}>
|
||||||
<PinIcon width={8} />
|
<PinIcon width={8} />
|
||||||
<Text size="13">Pinned by carmen.eth</Text>
|
<Text size="13">Pinned by {contact.name}</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
<Flex gap="1" align="center">
|
<Flex gap="1" align="center">
|
||||||
<Text color="primary" weight="500" size="15">
|
<Text color="primary" weight="500" size="15">
|
||||||
{displayName}
|
{displayName}
|
||||||
{/* {contact.name} */}
|
|
||||||
</Text>
|
</Text>
|
||||||
<Text size="10" color="gray">
|
<Text size="10" color="gray">
|
||||||
10:00 AM
|
{new Date(Number(clock)).toLocaleTimeString([], {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
|
@ -229,6 +239,7 @@ export const ChatMessage = (props: Props) => {
|
||||||
onEditClick={() => setEditing(true)}
|
onEditClick={() => setEditing(true)}
|
||||||
onReplyClick={handleReplyClick}
|
onReplyClick={handleReplyClick}
|
||||||
onPinClick={handlePinClick}
|
onPinClick={handlePinClick}
|
||||||
|
onReactionClick={handleReaction}
|
||||||
reacting={reacting}
|
reacting={reacting}
|
||||||
onReactingChange={setReacting}
|
onReactingChange={setReacting}
|
||||||
reactions={reactions}
|
reactions={reactions}
|
||||||
|
@ -236,7 +247,7 @@ export const ChatMessage = (props: Props) => {
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<ContextMenu.Item onSelect={handleReplyClick}>Reply</ContextMenu.Item>
|
<ContextMenu.Item onSelect={handleReplyClick}>Reply</ContextMenu.Item>
|
||||||
<ContextMenu.Item onSelect={handlePinClick}>Pin</ContextMenu.Item>
|
{/* <ContextMenu.Item onSelect={handlePinClick}>Pin</ContextMenu.Item> */}
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</ContextMenuTrigger>
|
</ContextMenuTrigger>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { useAppState } from '~/src/contexts/app-context'
|
||||||
import { BellIcon } from '~/src/icons/bell-icon'
|
import { BellIcon } from '~/src/icons/bell-icon'
|
||||||
import { DotsIcon } from '~/src/icons/dots-icon'
|
import { DotsIcon } from '~/src/icons/dots-icon'
|
||||||
import { GroupIcon } from '~/src/icons/group-icon'
|
import { GroupIcon } from '~/src/icons/group-icon'
|
||||||
import { useChannel } from '~/src/protocol'
|
import { useChat } from '~/src/protocol'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { DropdownMenuTrigger, Flex, IconButton, Separator } from '~/src/system'
|
import { DropdownMenuTrigger, Flex, IconButton, Separator } from '~/src/system'
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export const Navbar = (props: Props) => {
|
||||||
const { state, dispatch } = useAppState()
|
const { state, dispatch } = useAppState()
|
||||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||||
|
|
||||||
const chat = useChannel(params.id!)
|
const chat = useChat(params.id!)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavbarWrapper>
|
<NavbarWrapper>
|
||||||
|
@ -44,7 +44,7 @@ export const Navbar = (props: Props) => {
|
||||||
<IconButton label="Options">
|
<IconButton label="Options">
|
||||||
<DotsIcon />
|
<DotsIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<ChatMenu type="dropdown" chatType="channel" />
|
<ChatMenu type="dropdown" />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
|
|
||||||
<Separator orientation="vertical" css={{ height: 24 }} />
|
<Separator orientation="vertical" css={{ height: 24 }} />
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// TODO: handle non-existing chat ID
|
||||||
|
|
||||||
import React, { useEffect, useRef } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
|
|
||||||
import { useMatch } from 'react-router-dom'
|
import { useMatch } from 'react-router-dom'
|
||||||
|
@ -5,7 +7,8 @@ import { useMatch } from 'react-router-dom'
|
||||||
import { MemberSidebar } from '~/src/components/member-sidebar'
|
import { MemberSidebar } from '~/src/components/member-sidebar'
|
||||||
import { useAppState } from '~/src/contexts/app-context'
|
import { useAppState } from '~/src/contexts/app-context'
|
||||||
import { ChatProvider } from '~/src/contexts/chat-context'
|
import { ChatProvider } from '~/src/contexts/chat-context'
|
||||||
import { useChannel } from '~/src/protocol'
|
import { useChat } from '~/src/protocol'
|
||||||
|
import { useProtocol } from '~/src/protocol/provider'
|
||||||
import { useMessages } from '~/src/protocol/use-messages'
|
import { useMessages } from '~/src/protocol/use-messages'
|
||||||
import { styled } from '~/src/styles/config'
|
import { styled } from '~/src/styles/config'
|
||||||
import { Avatar, Flex, Heading, Text } from '~/src/system'
|
import { Avatar, Flex, Heading, Text } from '~/src/system'
|
||||||
|
@ -14,59 +17,85 @@ import { ChatInput } from './components/chat-input'
|
||||||
import { ChatMessage } from './components/chat-message'
|
import { ChatMessage } from './components/chat-message'
|
||||||
import { Navbar } from './components/navbar'
|
import { Navbar } from './components/navbar'
|
||||||
|
|
||||||
const ChatStart = () => {
|
interface ChatStartProps {
|
||||||
// TODO: unify this with the useChat hook
|
chatId: string
|
||||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
}
|
||||||
|
|
||||||
const chat = useChannel(params.id!)
|
const ChatStart = (props: ChatStartProps) => {
|
||||||
|
const { chatId } = props
|
||||||
|
|
||||||
|
const { identity } = useChat(chatId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" gap="3" align="center" css={{ marginBottom: 50 }}>
|
<Flex direction="column" gap="3" align="center" css={{ marginBottom: 50 }}>
|
||||||
{/* <Avatar size={120} src={chat.imageUrl} /> */}
|
<Avatar size={120} name={identity?.displayName} color={identity?.color} />
|
||||||
<Heading>{chat.identity?.displayName}</Heading>
|
<Heading>{identity?.displayName}</Heading>
|
||||||
<Text>
|
<Text>
|
||||||
Welcome to the beginning of the #{chat.identity?.displayName} channel!
|
Welcome to the beginning of the #{identity?.displayName} channel!
|
||||||
</Text>
|
</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Content = () => {
|
interface ContentProps {
|
||||||
const contentRef = useRef<HTMLDivElement>(null)
|
chatId: string
|
||||||
|
}
|
||||||
|
|
||||||
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
const Content = (props: ContentProps) => {
|
||||||
|
const { chatId } = props
|
||||||
|
|
||||||
|
const contentRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
contentRef.current!.scrollTop = contentRef.current!.scrollHeight ?? 0
|
contentRef.current!.scrollTop = contentRef.current!.scrollHeight ?? 0
|
||||||
}, [])
|
}, [chatId])
|
||||||
|
|
||||||
const messages = useMessages(params.id!)
|
const messages = useMessages(chatId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentWrapper ref={contentRef}>
|
<ContentWrapper ref={contentRef}>
|
||||||
<ChatStart />
|
{/* <Button onClick={messages.fetchMore}>Fetch more</Button> */}
|
||||||
{messages.data.map(message => (
|
<ChatStart chatId={chatId} />
|
||||||
<ChatMessage key={message.messageId} message={message} />
|
{messages.data.map((message, index) => (
|
||||||
|
<ChatMessage
|
||||||
|
key={message.messageId}
|
||||||
|
message={message}
|
||||||
|
previousMessage={messages.data[index - 1]}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ContentWrapper>
|
</ContentWrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Chat = () => {
|
export const Chat = () => {
|
||||||
|
const { client } = useProtocol()
|
||||||
const { state, options } = useAppState()
|
const { state, options } = useAppState()
|
||||||
|
|
||||||
|
const { params } = useMatch(':id')! // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||||
|
const chatId = params.id!
|
||||||
|
|
||||||
|
const chat = useChat(chatId)
|
||||||
|
|
||||||
// TODO: Update condition based on a chat type
|
// TODO: Update condition based on a chat type
|
||||||
const enableMembers = options.enableMembers // && (chat.type === 'group' || chat.type === 'channel')
|
const enableMembers = options.enableMembers ?? false // && (chat.type === 'group' || chat.type === 'channel')
|
||||||
const showMembers = enableMembers && state.showMembers
|
const showMembers = enableMembers && state.showMembers
|
||||||
|
|
||||||
|
const handleMessageSubmit = (message: string) => {
|
||||||
|
client.community.sendTextMessage(
|
||||||
|
chatId,
|
||||||
|
message
|
||||||
|
// '0x0fa999097568d1fdcc39108a08d75340bd2cee5ec59c36799007150d0a9fc896'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatProvider>
|
<ChatProvider>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Main>
|
<Main>
|
||||||
<Navbar enableMembers={enableMembers} />
|
<Navbar enableMembers={enableMembers} />
|
||||||
<Content />
|
<Content chatId={chatId} />
|
||||||
<ChatInput />
|
<ChatInput onSubmit={handleMessageSubmit} />
|
||||||
</Main>
|
</Main>
|
||||||
{showMembers && <MemberSidebar />}
|
{showMembers && <MemberSidebar />}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
Loading…
Reference in New Issue