generated from yandex-praktikum/middle.messenger.praktikum.yandex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add socket, messagner, chat & chat api
- Loading branch information
Showing
39 changed files
with
1,513 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import HTTPTransport from '../core/HTTPTransport'; | ||
import { Chat, User } from '../utils/types'; | ||
|
||
const chats = new HTTPTransport('/chats'); | ||
|
||
class ChatAPI { | ||
public async getChats(): Promise<Chat[]> { | ||
return chats.get('/'); | ||
} | ||
|
||
public async createChat(title: string): Promise<Chat> { | ||
return chats.post('/', { | ||
data: { title }, | ||
headers: { | ||
'Content-type': 'application/json; charset=UTF-8', | ||
}, | ||
}); | ||
} | ||
|
||
public async getUserToken(chatId: number): Promise<{ token: string }> { | ||
const response = await chats.post(`/token/${chatId}`); | ||
if (response instanceof XMLHttpRequest) { | ||
return response.response; | ||
} | ||
|
||
return response; | ||
} | ||
|
||
public async getChatUsers(chatId: number): Promise<User[]> { | ||
return chats.get(`/${chatId}/users`); | ||
} | ||
|
||
public async addUsers(chatId: number, users: any): Promise<Chat> { | ||
return chats.put('/users', { | ||
data: { chatId, users }, | ||
headers: { | ||
'Content-type': 'application/json; charset=UTF-8', | ||
}, | ||
}); | ||
} | ||
|
||
public async removeUsers(chatId: number, users: any): Promise<void> { | ||
return chats.delete('/users', { | ||
data: { chatId, users }, | ||
headers: { | ||
'Content-type': 'application/json; charset=UTF-8', | ||
} | ||
}); | ||
} | ||
|
||
public async deleteChat(chatId: any): Promise<void> { | ||
return chats.delete('/', { | ||
data: chatId, | ||
headers: { | ||
'Content-type': 'application/json; charset=UTF-8', | ||
} | ||
}); | ||
} | ||
} | ||
|
||
export default new ChatAPI(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{{#if isAddUserOpen }} | ||
<form class="user-modal" method="dialog"> | ||
<h3 class="title">Добавить пользователя в чат</h3> | ||
{{#if isUserSearchEnabled}} | ||
{{{ Search users=users class="search" placeholder="Search users" }}} | ||
{{/if}} | ||
{{#if usersList }} | ||
{{#each currentChatUsers }} | ||
<ul> | ||
<li class="user-item" data-user-id="{{id}}">{{login}}</li> | ||
</ul> | ||
{{/each}} | ||
{{/if}} | ||
{{{ Button class="cancel" type="button" label="Cancel" onClick=onClose }}} | ||
</form> | ||
{{/if}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
@import '../../utils.less'; | ||
|
||
.user-modal { | ||
position: absolute; | ||
left: 50%; | ||
top: 50%; | ||
transform: translate(-50%, -50%); | ||
border-radius: 14px; | ||
z-index: 1000; | ||
background: @main__background-color; | ||
padding: 3rem 2rem 1rem 2rem; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
|
||
.title { | ||
margin: 0 0 1rem 0; | ||
} | ||
|
||
.search { | ||
.search-results { | ||
display: block; | ||
background: @secondary__background-color; | ||
position: relative; | ||
list-style: none; | ||
width: auto; | ||
max-height: 15rem; | ||
overflow-y: scroll; | ||
border-radius: 5px; | ||
margin-top: 0; | ||
padding-inline-start: 15px; | ||
padding-inline-end: 15px; | ||
|
||
.user-item { | ||
display: flex; | ||
flex-direction: column; | ||
border-bottom: 1px solid @main__background-color; | ||
padding: 5px; | ||
} | ||
} | ||
} | ||
|
||
.cancel { | ||
.button( | ||
@margin: 3rem 0 0 0, | ||
@width: auto, | ||
@background: none, | ||
@border: none, | ||
@color: @secondary__button-background-color | ||
); | ||
|
||
&:hover { | ||
.custom-button-hover(@color: @main__button-background-hover); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import './user-modal.less'; | ||
import UserModalTmpl from './user-modal.hbs?raw'; | ||
import Block, {Props} from '../../core/Block'; | ||
import { Search } from '../search/search'; | ||
import ChatController from "../../controllers/ChatController"; | ||
import Store from '../../core/Store'; | ||
import { connect } from '../../utils/connect'; | ||
import {User} from "../../utils/types"; | ||
|
||
export class UserModalBase extends Block { | ||
constructor(props: Props) { | ||
super({ | ||
...props, | ||
onClose: () => { | ||
Store.set('isAddUserOpen', false); | ||
Store.set('selectedUser', null); | ||
}, | ||
events: { | ||
click: (event: Event) => { | ||
this.handleUserClick(event); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
init() { | ||
this.children.Search = new Search({} as any); | ||
} | ||
|
||
protected componentDidUpdate(oldProps: any, newProps: any) { | ||
if (oldProps.selectedUser !== newProps.selectedUser && newProps.selectedUser) { | ||
if (this.props.isUserSearchEnabled) { | ||
this.addUserToChat(newProps.selectedUser); | ||
} else { | ||
this.removeUserFromChat(newProps.selectedUser); | ||
} | ||
Store.set('selectedUser', null); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private async addUserToChat(user: any) { | ||
const chatId = Store.getState().selectedChat?.id; | ||
if (chatId) { | ||
try { | ||
await ChatController.addUsers({ id: chatId, user: user.id }); | ||
await ChatController.getChatUsers(chatId); | ||
Store.set('isAddUserOpen', false); | ||
} catch (error) { | ||
console.error('Error adding user to chat:', error); | ||
} | ||
} else { | ||
console.error('No selected chat found'); | ||
} | ||
} | ||
|
||
private async handleUserClick(e: Event) { | ||
const target = e.target as HTMLElement; | ||
const userItem = target.closest('.user-item'); | ||
if (userItem) { | ||
const userId = userItem.getAttribute('data-user-id') || userItem.id; | ||
if (userId) { | ||
let user; | ||
if (this.props.isUserSearchEnabled) { | ||
user = this.props.users.find((u: User) => u.id.toString() === userId); | ||
} else { | ||
user = this.props.currentChatUsers.find((u: User) => u.id.toString() === userId); | ||
} | ||
if (user) { | ||
Store.setSelectedUser(user); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private async removeUserFromChat(user: any) { | ||
const chatId = Store.getState().selectedChat?.id; | ||
if (chatId) { | ||
try { | ||
await ChatController.removeUsers({ id: chatId, users: [user.id] }); | ||
await ChatController.getChatUsers(chatId); | ||
Store.set('isAddUserOpen', false); | ||
} catch (error) { | ||
console.error('Error removing user from chat:', error); | ||
} | ||
} else { | ||
console.error('No selected chat found'); | ||
} | ||
} | ||
|
||
render(): string { | ||
return UserModalTmpl; | ||
} | ||
} | ||
|
||
export const UserModal = connect((state) => { | ||
return { | ||
isAddUserOpen: state?.isAddUserOpen || false, | ||
isUserSearchEnabled: state?.isUserSearchEnabled || false, | ||
usersList: state?.usersList || false, | ||
selectedUser: state.selectedUser, | ||
selectedChatId: state.selectedChat?.id, | ||
currentChatUsers: state?.currentChatUsers || [] | ||
} | ||
})(UserModalBase); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
{{#if selectedChat}} | ||
<div class="chat-body"> | ||
<div class="chat-header"> | ||
<h2>{{selectedChat.title}}</h2> | ||
{{#if websocketError}} | ||
<div class="error-message">{{websocketError}}</div> | ||
{{/if}} | ||
<div class="chat-menu"> | ||
{{{ MenuButton isOpenChatMenu=isOpenChatMenu class="chat-menu-button" onClick=toggleMenu }}} | ||
{{#if isOpenChatMenu}} | ||
{{{ ChatMenu }}} | ||
{{/if}} | ||
{{#if isAddUserOpen }} | ||
{{{ UserModal }}} | ||
{{/if}} | ||
</div> | ||
</div> | ||
<div class="chat-messages"> | ||
{{#if messages.length}} | ||
{{#each messages}} | ||
<div class="message {{#if (eq user_id ../currentUserId)}}out{{else}}in{{/if}}"> | ||
<p class="content">{{content}}</p> | ||
<span class="time">{{formatDate time}}</span> | ||
</div> | ||
{{/each}} | ||
{{else}} | ||
<p>No messages yet</p> | ||
{{/if}} | ||
</div> | ||
<form class="chat-input"> | ||
<input class="sender" type="text" placeholder="Message..." /> | ||
<button type="submit" class="send-message"></button> | ||
</form> | ||
</div> | ||
{{else}} | ||
<div class="no-chat-selected"> | ||
<p>Select a chat to start messaging</p> | ||
</div> | ||
{{/if}} |
Oops, something went wrong.