mirror of
https://github.com/lorsanstand/Aether.git
synced 2026-06-19 12:05:16 +03:00
Add edit message
This commit is contained in:
@@ -1,53 +0,0 @@
|
|||||||
COMPANY_NAME=AETHER
|
|
||||||
|
|
||||||
MODE=DEV
|
|
||||||
LOG_LEVEL=DEBUG
|
|
||||||
|
|
||||||
BACKEND_HOST=localhost
|
|
||||||
BACKEND_PORT=8080
|
|
||||||
WORKERS=4
|
|
||||||
FRONTEND_URL=http://localhost:5173
|
|
||||||
|
|
||||||
VITE_API_URL=/api/v1
|
|
||||||
FRONTEND_PORT=3056
|
|
||||||
|
|
||||||
FIRST_SUPER_USER_EMAIL=admin@example.com
|
|
||||||
FIRST_SUPER_USER_PASS=admin
|
|
||||||
FIRST_SUPER_USER_USERNAME=admin
|
|
||||||
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_PORT=5432
|
|
||||||
DB_PASS=postgres
|
|
||||||
DB_USER=postgres
|
|
||||||
DB_NAME=Aether
|
|
||||||
|
|
||||||
REDIS_HOST=localhost
|
|
||||||
REDIS_PORT=6379
|
|
||||||
# REDIS_PASS=
|
|
||||||
# REDIS_DB=
|
|
||||||
|
|
||||||
#CORS_HEADERS=["Content-Type", "Set-Cookie", "Access-Control-Allow-Headers", "Access-Control-Allow-Origin", "Authorization"]
|
|
||||||
#CORS_ORIGINS=["http://localhost:3000"]
|
|
||||||
#CORS_METHODS=["GET", "POST", "OPTIONS", "DELETE", "PATCH", "PUT"]
|
|
||||||
|
|
||||||
CORS_HEADERS=["Content-Type", "Set-Cookie", "Access-Control-Allow-Headers", "Access-Control-Allow-Origin", "Authorization"]
|
|
||||||
CORS_ORIGINS=["http://localhost:5500", "http://localhost:5173", "http://localhost:8080", "http://127.0.0.1:8080", "null"]
|
|
||||||
CORS_METHODS=["GET", "POST", "OPTIONS", "DELETE", "PATCH", "PUT"]
|
|
||||||
|
|
||||||
SECRET_KEY=sercretKey
|
|
||||||
ALGORITHM=HS256
|
|
||||||
|
|
||||||
SMTP_SERVER=localhost
|
|
||||||
SMTP_PORT=1025
|
|
||||||
SMTP_EMAIL=noreply@cityvibe.ru
|
|
||||||
SMTP_PASS=test
|
|
||||||
|
|
||||||
RMQ_HOST=localhost
|
|
||||||
RMQ_USER=guest
|
|
||||||
RMQ_PASS=guest
|
|
||||||
RMQ_PORT=5672
|
|
||||||
|
|
||||||
S3_URL=http://192.168.31.190:9002
|
|
||||||
S3_ACCESS_KEY_ID=lorsan
|
|
||||||
S3_SECRET_ACCESS_KEY=Lorser2009!
|
|
||||||
S3_BUCKET_NAME=aether
|
|
||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
.env
|
.env.prod
|
||||||
test.py
|
test.py
|
||||||
.env.old
|
.env
|
||||||
Generated
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/backend" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/backend" isTestSource="false" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13 (aetherbackend-6Zf3gKAD-py3.13)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.14 (aetherbackend-6Zf3gKAD-py3.14)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PyDocumentationSettings">
|
<component name="PyDocumentationSettings">
|
||||||
|
|||||||
Generated
+1
-1
@@ -3,5 +3,5 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.13" />
|
<option name="sdkName" value="Python 3.13" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (aetherbackend-6Zf3gKAD-py3.13)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14 (aetherbackend-6Zf3gKAD-py3.14)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
@@ -97,8 +97,8 @@ pip install -e .
|
|||||||
# Или используя poetry
|
# Или используя poetry
|
||||||
poetry install
|
poetry install
|
||||||
|
|
||||||
# Создайте .env файл
|
# Создайте .env.prod файл
|
||||||
cat > .env << EOF
|
cat > .env.prod << EOF
|
||||||
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/aether
|
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/aether
|
||||||
REDIS_URL=redis://localhost:6379
|
REDIS_URL=redis://localhost:6379
|
||||||
SECRET_KEY=your-secret-key-here
|
SECRET_KEY=your-secret-key-here
|
||||||
@@ -130,8 +130,8 @@ cd frontend
|
|||||||
# Установите зависимости
|
# Установите зависимости
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
# Создайте .env файл
|
# Создайте .env.prod файл
|
||||||
echo "VITE_API_URL=http://localhost:8000" > .env
|
echo "VITE_API_URL=http://localhost:8000" > .env.prod
|
||||||
|
|
||||||
# Запустите dev сервер
|
# Запустите dev сервер
|
||||||
npm run dev
|
npm run dev
|
||||||
@@ -279,7 +279,7 @@ pytest tests/ -v
|
|||||||
docker build -t aether-backend .
|
docker build -t aether-backend .
|
||||||
|
|
||||||
# Запуск контейнера
|
# Запуск контейнера
|
||||||
docker run -p 8000:8000 --env-file .env aether-backend
|
docker run -p 8000:8000 --env-file .env.prod aether-backend
|
||||||
```
|
```
|
||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class MessageDAO(BaseDAO[MessageModel, MessageCreateDB, MessageUpdateDB]):
|
|||||||
model = MessageModel
|
model = MessageModel
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def find_all_asc(
|
async def find_all_desc(
|
||||||
cls,
|
cls,
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
offset: Optional[int],
|
offset: Optional[int],
|
||||||
@@ -85,7 +85,7 @@ class MessageDAO(BaseDAO[MessageModel, MessageCreateDB, MessageUpdateDB]):
|
|||||||
*filter,
|
*filter,
|
||||||
**filter_by
|
**filter_by
|
||||||
) -> List[MessageModel]:
|
) -> List[MessageModel]:
|
||||||
stmt = select(MessageModel).filter(*filter).filter_by(**filter_by).order_by(MessageModel.created_at.asc())
|
stmt = select(MessageModel).filter(*filter).filter_by(**filter_by).order_by(MessageModel.created_at.desc())
|
||||||
|
|
||||||
if offset is not None:
|
if offset is not None:
|
||||||
stmt = stmt.offset(offset)
|
stmt = stmt.offset(offset)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
from sqlalchemy import ForeignKey, UUID, UniqueConstraint
|
from sqlalchemy import ForeignKey, UUID, UniqueConstraint, text
|
||||||
|
|
||||||
from app.core.database import Base
|
from app.core.database import Base
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ class MessageModel(Base):
|
|||||||
chat_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey("chat.id", ondelete="CASCADE"), index=True)
|
chat_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey("chat.id", ondelete="CASCADE"), index=True)
|
||||||
content: Mapped[str] = mapped_column()
|
content: Mapped[str] = mapped_column()
|
||||||
is_read: Mapped[bool] = mapped_column(default=False)
|
is_read: Mapped[bool] = mapped_column(default=False)
|
||||||
|
is_edited: Mapped[bool] = mapped_column(default=False, server_default=text("false"))
|
||||||
|
|
||||||
|
|
||||||
class ChatModel(Base):
|
class ChatModel(Base):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
|
|||||||
from app.chats.service import ChatService
|
from app.chats.service import ChatService
|
||||||
from app.auth.dependencies import get_current_verified_user
|
from app.auth.dependencies import get_current_verified_user
|
||||||
from app.users.models import UserModel
|
from app.users.models import UserModel
|
||||||
from app.chats.schemas import Chat, MessageCreate, Message
|
from app.chats.schemas import Chat, MessageCreate, Message, MessageUpdate
|
||||||
|
|
||||||
router = APIRouter(prefix="/chats", tags=["chats"])
|
router = APIRouter(prefix="/chats", tags=["chats"])
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ async def get_chats(
|
|||||||
return await ChatService.get_chats(user, offset, limit)
|
return await ChatService.get_chats(user, offset, limit)
|
||||||
|
|
||||||
@router.get("/{chat_id}")
|
@router.get("/{chat_id}")
|
||||||
async def get_chat(
|
async def get_messages(
|
||||||
chat_id: uuid.UUID,
|
chat_id: uuid.UUID,
|
||||||
offset: int = 0,
|
offset: int = 0,
|
||||||
limit: int = 10,
|
limit: int = 10,
|
||||||
@@ -31,6 +31,10 @@ async def get_chat(
|
|||||||
async def send_message(message: MessageCreate, user: UserModel = Depends(get_current_verified_user)) -> Message:
|
async def send_message(message: MessageCreate, user: UserModel = Depends(get_current_verified_user)) -> Message:
|
||||||
return await ChatService.send_message(user, message)
|
return await ChatService.send_message(user, message)
|
||||||
|
|
||||||
|
@router.put("/message")
|
||||||
|
async def edit_message(message_update: MessageUpdate, user: UserModel = Depends(get_current_verified_user)) -> Message:
|
||||||
|
return await ChatService.update_message(user, message_update)
|
||||||
|
|
||||||
|
|
||||||
@router.websocket("/ws")
|
@router.websocket("/ws")
|
||||||
async def websocket_endpoint(ws: WebSocket, user: UserModel = Depends(get_current_verified_user)):
|
async def websocket_endpoint(ws: WebSocket, user: UserModel = Depends(get_current_verified_user)):
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from datetime import datetime
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, ConfigDict
|
||||||
|
|
||||||
|
|
||||||
class MessageCreate(BaseModel):
|
class MessageCreate(BaseModel):
|
||||||
@@ -21,10 +21,13 @@ class MessageCreateDB(BaseModel):
|
|||||||
chat_id: Optional[uuid.UUID]
|
chat_id: Optional[uuid.UUID]
|
||||||
content: Optional[str]
|
content: Optional[str]
|
||||||
is_read: Optional[bool] = False
|
is_read: Optional[bool] = False
|
||||||
|
is_edited: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
class MessageUpdateDB(BaseModel):
|
class MessageUpdateDB(BaseModel):
|
||||||
content: Optional[str]
|
content: Optional[str]
|
||||||
|
is_edited: Optional[bool] = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Message(BaseModel):
|
class Message(BaseModel):
|
||||||
@@ -32,9 +35,12 @@ class Message(BaseModel):
|
|||||||
sender_id: int
|
sender_id: int
|
||||||
chat_id: uuid.UUID
|
chat_id: uuid.UUID
|
||||||
content: str
|
content: str
|
||||||
|
is_edited: Optional[bool] = False
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
|
|
||||||
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
class ChatBase(BaseModel):
|
class ChatBase(BaseModel):
|
||||||
is_group: Optional[bool] = False
|
is_group: Optional[bool] = False
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from sqlalchemy import and_
|
|||||||
from app.core.database import async_session_maker
|
from app.core.database import async_session_maker
|
||||||
from app.chats.dao import ChatDAO, MessageDAO, ParticipantDAO
|
from app.chats.dao import ChatDAO, MessageDAO, ParticipantDAO
|
||||||
from app.chats.models import ChatModel, MessageModel, ParticipantModel
|
from app.chats.models import ChatModel, MessageModel, ParticipantModel
|
||||||
from app.chats.schemas import Chat, MessageCreate, MessageCreateDB, ChatCreateDB, ParticipantCreateDB, Message
|
from app.chats.schemas import Chat, MessageCreate, MessageCreateDB, ChatCreateDB, ParticipantCreateDB, Message, MessageUpdateDB, MessageUpdate
|
||||||
from app.users.models import UserModel
|
from app.users.models import UserModel
|
||||||
from app.core.redis import get_redis
|
from app.core.redis import get_redis
|
||||||
|
|
||||||
@@ -92,14 +92,7 @@ class ChatService:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
await cls._send_ws_message(members_ids, Message(
|
await cls._send_ws_message(members_ids, Message.model_validate(message_db))
|
||||||
id=message_db.id,
|
|
||||||
sender_id=message_db.sender_id,
|
|
||||||
chat_id=message_db.chat_id,
|
|
||||||
content=message_db.content,
|
|
||||||
created_at=message_db.created_at,
|
|
||||||
updated_at=message_db.updated_at
|
|
||||||
))
|
|
||||||
|
|
||||||
await ChatDAO.update(
|
await ChatDAO.update(
|
||||||
session,
|
session,
|
||||||
@@ -124,7 +117,7 @@ class ChatService:
|
|||||||
log.warning("Access denied to chat", extra={"user_id": user.id, "chat_id": chat_id})
|
log.warning("Access denied to chat", extra={"user_id": user.id, "chat_id": chat_id})
|
||||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Access denied")
|
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Access denied")
|
||||||
|
|
||||||
messages = await MessageDAO.find_all_asc(
|
messages = await MessageDAO.find_all_desc(
|
||||||
session,
|
session,
|
||||||
offset,
|
offset,
|
||||||
limit,
|
limit,
|
||||||
@@ -181,3 +174,44 @@ class ChatService:
|
|||||||
}
|
}
|
||||||
await redis_client.publish("messenger_updates", json.dumps(payload))
|
await redis_client.publish("messenger_updates", json.dumps(payload))
|
||||||
log.debug(f"Published message for user_id: {user_id}")
|
log.debug(f"Published message for user_id: {user_id}")
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def update_message(cls, user: UserModel, message_update: MessageUpdate) -> Message:
|
||||||
|
async with async_session_maker() as session:
|
||||||
|
message_exist = await MessageDAO.find_one_or_none(
|
||||||
|
session,
|
||||||
|
and_(
|
||||||
|
MessageModel.id==message_update.id,
|
||||||
|
MessageModel.sender_id==user.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if message_exist is None:
|
||||||
|
log.warning("Message not found", extra={"user_id": user.id, "message_id": message_update.id})
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Message not found")
|
||||||
|
|
||||||
|
|
||||||
|
message_update_db = await MessageDAO.update(
|
||||||
|
session,
|
||||||
|
MessageModel.id==message_update.id,
|
||||||
|
obj_in=MessageUpdateDB(
|
||||||
|
content=message_update.content,
|
||||||
|
is_edited=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
members = await ParticipantDAO.find_all(
|
||||||
|
session,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
ParticipantModel.chat_id==message_exist.chat_id
|
||||||
|
)
|
||||||
|
|
||||||
|
member_ids = [member.user_id for member in members]
|
||||||
|
|
||||||
|
await cls._send_ws_message(member_ids, Message.model_validate(message_update_db))
|
||||||
|
|
||||||
|
await session.commit()
|
||||||
|
log.info("Message update successfully", extra={"user_id": user.id, "message_id": message_update.id})
|
||||||
|
return message_update_db
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
"""Edit message table: adding is_edited column
|
||||||
|
|
||||||
|
Revision ID: 76159faa56c8
|
||||||
|
Revises: 0d3f7039ba77
|
||||||
|
Create Date: 2026-01-20 17:22:23.965106
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '76159faa56c8'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '0d3f7039ba77'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('message', sa.Column('is_edited', sa.Boolean(), nullable=False, server_default=sa.text("false")))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('message', 'is_edited')
|
||||||
|
# ### end Alembic commands ###
|
||||||
+3
-3
@@ -56,7 +56,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- aether
|
- aether
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env.prod
|
||||||
|
|
||||||
celery:
|
celery:
|
||||||
build:
|
build:
|
||||||
@@ -70,7 +70,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- aether
|
- aether
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env.prod
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
@@ -95,7 +95,7 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- aether
|
- aether
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env.prod
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
|
|||||||
@@ -66,6 +66,14 @@ function App() {
|
|||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/chat/:chatId"
|
||||||
|
element={
|
||||||
|
<PrivateRoute>
|
||||||
|
<ChatPage />
|
||||||
|
</PrivateRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/profile"
|
path="/profile"
|
||||||
element={
|
element={
|
||||||
|
|||||||
+1003
-6
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,58 @@
|
|||||||
|
import apiClient from './api';
|
||||||
|
|
||||||
|
export type Chat = {
|
||||||
|
chat_id: string;
|
||||||
|
user_id: number;
|
||||||
|
last_message: string | null;
|
||||||
|
avatar_url: string | null;
|
||||||
|
display_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Message = {
|
||||||
|
id: string;
|
||||||
|
sender_id: number;
|
||||||
|
chat_id: string;
|
||||||
|
content: string;
|
||||||
|
is_edited?: boolean;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageCreate = {
|
||||||
|
content: string;
|
||||||
|
chat_id?: string;
|
||||||
|
recipient_id?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MessageUpdate = {
|
||||||
|
id: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatService = {
|
||||||
|
async getChats(offset: number = 0, limit: number = 10): Promise<Chat[]> {
|
||||||
|
const response = await apiClient.get('/chats/', {
|
||||||
|
params: { offset, limit }
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
async getChatMessages(chatId: string, offset: number = 0, limit: number = 50): Promise<Message[]> {
|
||||||
|
const response = await apiClient.get(`/chats/${chatId}`, {
|
||||||
|
params: { offset, limit }
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
async sendMessage(data: MessageCreate): Promise<Message> {
|
||||||
|
const response = await apiClient.post('/chats/message', data);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateMessage(data: MessageUpdate): Promise<Message> {
|
||||||
|
const response = await apiClient.put('/chats/message', data);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default chatService;
|
||||||
@@ -26,6 +26,11 @@ export const userService = {
|
|||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getUserById: async (userId: number): Promise<User> => {
|
||||||
|
const response = await apiClient.get(`/users/${userId}`);
|
||||||
|
return response.data;
|
||||||
|
},
|
||||||
|
|
||||||
updateProfile: async (data: UserUpdate): Promise<User> => {
|
updateProfile: async (data: UserUpdate): Promise<User> => {
|
||||||
const response = await apiClient.put('/users/me', data);
|
const response = await apiClient.put('/users/me', data);
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import type { Chat } from '../services/chatService';
|
||||||
|
|
||||||
|
interface ChatStore {
|
||||||
|
chats: Chat[];
|
||||||
|
setChats: (chats: Chat[]) => void;
|
||||||
|
updateChat: (chatId: string, updates: Partial<Chat>) => void;
|
||||||
|
clearChats: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useChatStore = create<ChatStore>()(
|
||||||
|
persist(
|
||||||
|
(set) => ({
|
||||||
|
chats: [],
|
||||||
|
|
||||||
|
setChats: (chats) => set({ chats }),
|
||||||
|
|
||||||
|
updateChat: (chatId, updates) =>
|
||||||
|
set((state) => ({
|
||||||
|
chats: state.chats.map(chat =>
|
||||||
|
chat.chat_id === chatId ? { ...chat, ...updates } : chat
|
||||||
|
)
|
||||||
|
})),
|
||||||
|
|
||||||
|
clearChats: () => set({ chats: [] }),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'aether-chats',
|
||||||
|
partialize: (state) => ({ chats: state.chats }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user