mirror of
https://github.com/fromchat-messenger/web.git
synced 2026-09-23 03:25:07 +03:00
Implement logic
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
import { useDialog } from "../../contexts/DialogContext";
|
||||
|
||||
export function BottomAppBar() {
|
||||
const { openSettings } = useDialog();
|
||||
|
||||
const handleSettingsClick = () => {
|
||||
openSettings();
|
||||
};
|
||||
|
||||
return (
|
||||
<mdui-bottom-app-bar>
|
||||
<mdui-button-icon icon="settings--filled" id="settings-open"></mdui-button-icon>
|
||||
<mdui-button-icon icon="settings--filled" id="settings-open" onClick={handleSettingsClick}></mdui-button-icon>
|
||||
<mdui-button-icon icon="group_add--filled"></mdui-button-icon>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
<mdui-fab icon="edit--filled"></mdui-fab>
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { PRODUCT_NAME } from "../../../core/config";
|
||||
import { useDialog } from "../../contexts/DialogContext";
|
||||
|
||||
export function ChatHeader() {
|
||||
const { openProfile } = useDialog();
|
||||
|
||||
const handleProfileClick = () => {
|
||||
openProfile();
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="chat-header-left">
|
||||
<div className="product-name">{PRODUCT_NAME}</div>
|
||||
<div className="profile">
|
||||
<a href="#" id="profile-open">
|
||||
<a href="#" id="profile-open" onClick={handleProfileClick}>
|
||||
<img src="./src/resources/images/default-avatar.png" alt="" id="preview1" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
import { useState } from "react";
|
||||
import { useChat } from "../../hooks/useChat";
|
||||
|
||||
export function ChatInputWrapper() {
|
||||
const [message, setMessage] = useState("");
|
||||
const { sendMessage } = useChat();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (message.trim()) {
|
||||
await sendMessage(message);
|
||||
setMessage("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-input-wrapper">
|
||||
<div className="chat-input">
|
||||
<form className="input-group" id="message-form">
|
||||
<input type="text" className="message-input" id="message-input" placeholder="Напишите сообщение..." autoComplete="off" />
|
||||
<form className="input-group" id="message-form" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
className="message-input"
|
||||
id="message-input"
|
||||
placeholder="Напишите сообщение..."
|
||||
autoComplete="off"
|
||||
value={message}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
/>
|
||||
<button type="submit" className="send-btn">
|
||||
<span className="material-symbols filled">send</span>
|
||||
</button>
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
import { useChat } from "../../hooks/useChat";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ChatMainHeader() {
|
||||
const { currentChat } = useChat();
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
const handleCollapse = () => {
|
||||
setIsCollapsed(!isCollapsed);
|
||||
// TODO: Implement chat collapse animation
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-header">
|
||||
<img src="./src/resources/images/default-avatar.png" alt="Avatar" className="chat-header-avatar" />
|
||||
<div className="chat-header-info">
|
||||
<div className="info-chat">
|
||||
<h4 id="chat-name">Общий чат</h4>
|
||||
<h4 id="chat-name">{currentChat}</h4>
|
||||
<p>
|
||||
<span className="online-status"></span>
|
||||
Онлайн
|
||||
</p>
|
||||
</div>
|
||||
<a href="#" id="hide-chat">Свернуть чат</a>
|
||||
<a href="#" id="hide-chat" onClick={handleCollapse}>Свернуть чат</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
import { useChat } from "../../hooks/useChat";
|
||||
import { Message } from "./Message";
|
||||
import { useAppState } from "../../state";
|
||||
import type { Message as MessageType } from "../../../core/types";
|
||||
|
||||
export function ChatMessages() {
|
||||
const { messages } = useChat();
|
||||
const { user } = useAppState();
|
||||
|
||||
const handleProfileClick = (username: string) => {
|
||||
// TODO: Show user profile dialog
|
||||
console.log("Show profile for:", username);
|
||||
};
|
||||
|
||||
const handleContextMenu = (e: React.MouseEvent, message: MessageType) => {
|
||||
e.preventDefault();
|
||||
// TODO: Show context menu
|
||||
console.log("Show context menu for message:", message.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-messages" id="chat-messages">
|
||||
{messages.map((message) => (
|
||||
<Message
|
||||
key={message.id}
|
||||
message={message}
|
||||
isAuthor={message.username === user.currentUser?.username}
|
||||
onProfileClick={handleProfileClick}
|
||||
onContextMenu={handleContextMenu}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
import { useChat } from "../../hooks/useChat";
|
||||
|
||||
export function ChatTabs() {
|
||||
const { activeTab, setActiveTab, setCurrentChat } = useChat();
|
||||
|
||||
const handleChatClick = (chatName: string) => {
|
||||
setCurrentChat(chatName);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="chat-tabs">
|
||||
<mdui-tabs value="chats" full-width>
|
||||
<mdui-tab value="chats">Чаты</mdui-tab>
|
||||
<mdui-tab value="channels">Каналы</mdui-tab>
|
||||
<mdui-tab value="contacts">Контакты</mdui-tab>
|
||||
<mdui-tab value="dms">ЛС</mdui-tab>
|
||||
<mdui-tabs value={activeTab} full-width onChange={(e: any) => setActiveTab(e.detail.value)}>
|
||||
<mdui-tab value="chats">
|
||||
Чаты
|
||||
</mdui-tab>
|
||||
<mdui-tab value="channels">
|
||||
Каналы
|
||||
</mdui-tab>
|
||||
<mdui-tab value="contacts">
|
||||
Контакты
|
||||
</mdui-tab>
|
||||
<mdui-tab value="dms">
|
||||
ЛС
|
||||
</mdui-tab>
|
||||
|
||||
<mdui-tab-panel slot="panel" value="chats">
|
||||
<mdui-list>
|
||||
<mdui-list-item headline="Общий чат" description="Вы: Последнее сообщение" id="chat-list-chat-1">
|
||||
<mdui-list-item
|
||||
headline="Общий чат"
|
||||
description="Вы: Последнее сообщение"
|
||||
id="chat-list-chat-1"
|
||||
onClick={() => handleChatClick("Общий чат")}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<img src="./src/resources/images/default-avatar.png" alt="" slot="icon" />
|
||||
</mdui-list-item>
|
||||
<mdui-list-item headline="Общий чат 2" description="Вы: Последнее сообщение" id="chat-list-chat-2">
|
||||
<mdui-list-item
|
||||
headline="Общий чат 2"
|
||||
description="Вы: Последнее сообщение"
|
||||
id="chat-list-chat-2"
|
||||
onClick={() => handleChatClick("Общий чат 2")}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<img src="./src/resources/images/default-avatar.png" alt="" slot="icon" />
|
||||
</mdui-list-item>
|
||||
</mdui-list>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { formatTime } from "../../../utils/utils";
|
||||
import type { Message as MessageType } from "../../../core/types";
|
||||
import defaultAvatar from "../../../resources/images/default-avatar.png";
|
||||
|
||||
interface MessageProps {
|
||||
message: MessageType;
|
||||
isAuthor: boolean;
|
||||
onProfileClick: (username: string) => void;
|
||||
onContextMenu: (e: React.MouseEvent, message: MessageType) => void;
|
||||
}
|
||||
|
||||
export function Message({ message, isAuthor, onProfileClick, onContextMenu }: MessageProps) {
|
||||
return (
|
||||
<div
|
||||
className={`message ${isAuthor ? "sent" : "received"}`}
|
||||
data-id={message.id}
|
||||
onContextMenu={(e) => onContextMenu(e, message)}
|
||||
>
|
||||
<div className="message-inner">
|
||||
{/* Add profile picture for received messages */}
|
||||
{!isAuthor && (
|
||||
<div className="message-profile-pic">
|
||||
<img
|
||||
src={message.profile_picture || defaultAvatar}
|
||||
alt={message.username}
|
||||
onClick={() => onProfileClick(message.username)}
|
||||
style={{ cursor: "pointer" }}
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = defaultAvatar;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isAuthor && (
|
||||
<div
|
||||
className="message-username"
|
||||
onClick={() => onProfileClick(message.username)}
|
||||
style={{ cursor: "pointer" }}> {/* TODO extract to SCSS */}
|
||||
{message.username}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Add reply preview if this is a reply */}
|
||||
{message.reply_to && (
|
||||
<div className="message-reply">
|
||||
<div className="reply-content">
|
||||
<span className="reply-username">{message.reply_to.username}</span>
|
||||
<span className="reply-text">{message.reply_to.content}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="message-content">
|
||||
{message.content}
|
||||
</div>
|
||||
|
||||
<div className="message-time">
|
||||
{formatTime(message.timestamp)}
|
||||
{message.is_edited ? " (edited)" : undefined}
|
||||
|
||||
{isAuthor && message.is_read ? (
|
||||
<span className="material-symbols outlined"></span>
|
||||
) : undefined}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user