(
+ path: K,
+ value: StateType[K]
+ ) {
set(this.state, path, value)
this.emit(StoreEvents.UPDATED)
}
diff --git a/src/mockData.ts b/src/mockData.ts
deleted file mode 100644
index 8e30f8406..000000000
--- a/src/mockData.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { MessageProps } from './components/message/message'
-
-export const mockMessages: MessageProps[] = [
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '0',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
- {
- id: '1',
- body: 'Lorem Ipsum - это текст-"рыба", часто используемый в печати и вэб-дизайне. Lorem Ipsum является стандартной "рыбой" для текстов на латинице с начала XVI века.',
- },
-]
diff --git a/src/pages/loginPage/loginPage.ts b/src/pages/loginPage/loginPage.ts
index b19407107..b7d524692 100644
--- a/src/pages/loginPage/loginPage.ts
+++ b/src/pages/loginPage/loginPage.ts
@@ -7,8 +7,8 @@ import { AuthController } from '@/controllers/AuthController.ts'
import Block, { Props } from '@/core/Block'
import router from '@/router.ts'
import { RegisterData } from '@/services/AuthService.ts'
-import { withUserdata } from '@/utils/connect.ts'
import './loginPage.css'
+import connect from '@/utils/connect.ts'
// language=hbs
const loginPageTemplate = `
@@ -55,7 +55,8 @@ const submitHandler = (e: Event) => {
if (resp instanceof XMLHttpRequest && resp.status === 200) {
router.go(routes.messenger)
} else {
- alert('kek')
+ loginForm.showInputError('login', 'Неверные данные')
+ loginForm.showInputError('password', 'Неверные данные')
}
})
}
@@ -99,9 +100,9 @@ const loginForm = new Form({
}),
})
-const connectedLoginPage = withUserdata(LoginPage)
+const withUserdata = connect((state) => ({ userdata: state.userdata }))(LoginPage)
-export const loginPage = new connectedLoginPage({
+export const loginPage = new withUserdata({
loginForm: loginForm,
registerLink: new Link({
to: routes.register,
diff --git a/src/pages/messengerPage/messengerPage.css b/src/pages/messengerPage/messengerPage.css
index 8f7bd7daa..f1fb37661 100644
--- a/src/pages/messengerPage/messengerPage.css
+++ b/src/pages/messengerPage/messengerPage.css
@@ -4,6 +4,7 @@
height: 100%;
.sidebar {
+ position: relative;
height: 100%;
overflow-y: scroll;
background: var(--sidebar-bg);
@@ -22,5 +23,20 @@
color: var(--color-gray);
}
}
+
+ .create-chat-btn {
+ position: absolute;
+ bottom: 8px;
+ right: 8px;
+ width: 40px;
+ height: 40px;
+ padding: 12px;
+ border-radius: 50%;
+ border: none;
+ background: var(--color-blue);
+ color: var(--color-white);
+ font-weight: 600;
+ cursor: pointer;
+ }
}
}
diff --git a/src/pages/messengerPage/messengerPage.ts b/src/pages/messengerPage/messengerPage.ts
index 0029ca63c..8f0c4545d 100644
--- a/src/pages/messengerPage/messengerPage.ts
+++ b/src/pages/messengerPage/messengerPage.ts
@@ -1,15 +1,16 @@
import Button from '@/components/button/button.ts'
-import { ChatWindow } from '@/components/chat/chat'
import ChatItem from '@/components/chatItem/chatItem.ts'
+import { ChatWindow, chatWindow } from '@/components/chatWindow/chatWindow.ts'
+import Input from '@/components/input/input.ts'
+import { Modal } from '@/components/modal/modal.ts'
import { routes } from '@/constants/routes.ts'
import { Chat } from '@/constants/types.ts'
import { AuthController } from '@/controllers/AuthController.ts'
import { ChatController } from '@/controllers/ChatController.ts'
import Block, { Props } from '@/core/Block'
import store from '@/core/Store.ts'
-import { mockMessages } from '@/mockData'
import router from '@/router.ts'
-import { withChats } from '@/utils/connect.ts'
+import connect from '@/utils/connect.ts'
import './messengerPage.css'
// language=hbs
@@ -22,6 +23,7 @@ const MessengerPageTemplate = `
{{{ chats }}}
+ {{{ createChatBtn }}}
{{{ chat }}}
@@ -30,7 +32,6 @@ const MessengerPageTemplate = `
type MessengerPageProps = {
profileBtn: Button
- chats: Chat[]
chat: ChatWindow
} & Props
@@ -38,8 +39,47 @@ const authController = new AuthController()
const chatController = new ChatController()
export class MessengerPage extends Block {
+ private modal: Modal
+
constructor(props: MessengerPageProps) {
super(props)
+ this.modal = new Modal()
+ this.children.createChatBtn = new Button({
+ label: '',
+ className: 'create-chat-btn',
+ withId: true,
+ events: {
+ click: () => {
+ this.showCreateChatModal()
+ },
+ },
+ })
+ }
+
+ showCreateChatModal() {
+ const content = document.createElement('div')
+ const input = new Input({
+ type: 'text',
+ label: 'Название чата',
+ placeholder: 'Название...',
+ name: 'create-chat'
+ })
+ const btn = new Button({
+ label: 'Создать чат',
+ className: 'button input-submit',
+ events: {
+ click: () => {
+ chatController.createChat(input.getValue())
+ this.modal.close()
+ }
+ }
+ })
+
+ content.appendChild(input.element)
+ content.appendChild(btn.element)
+
+ this.modal.setContent('Создать чат', content);
+ this.modal.open();
}
createChatItems(chats: Chat[]) {
@@ -66,9 +106,11 @@ export class MessengerPage extends Block {
}
}
-const connectedMessengerPage = withChats(MessengerPage)
+export const withChats = connect((state) => ({
+ chats: state.chats,
+}))(MessengerPage)
-export const messengerPage = new connectedMessengerPage({
+export const messengerPage = new withChats({
profileBtn: new Button({
label: 'Профиль>',
className: 'button-icon back-btn',
@@ -78,9 +120,7 @@ export const messengerPage = new connectedMessengerPage({
},
},
}),
- chats: store.getState().chats,
- chat: new ChatWindow({
- user: store.getState().userdata,
- messages: mockMessages,
+ chat: new chatWindow({
+ selectedChat: store.getState().selectedChat,
}),
})
diff --git a/src/pages/profilePage/profilePage.ts b/src/pages/profilePage/profilePage.ts
index 2e59d1f0b..5aab58e8f 100644
--- a/src/pages/profilePage/profilePage.ts
+++ b/src/pages/profilePage/profilePage.ts
@@ -8,7 +8,7 @@ import { UserController } from '@/controllers/UserController.ts'
import Block from '@/core/Block'
import store from '@/core/Store.ts'
import router from '@/router.ts'
-import { withUserAvatar, withUserdata } from '@/utils/connect.ts'
+import connect from '@/utils/connect.ts'
import './profilePage.css'
// language=hbs
@@ -116,10 +116,12 @@ const logoutBtnHandler = () => {
})
}
-const connectedProfilePage = withUserdata(ProfilePage)
-const connectedAvatar = withUserAvatar(Avatar)
+const withUserdata = connect((state) => ({ userdata: state.userdata }))(ProfilePage)
+export const withUserAvatar = connect((state) => ({
+ src: state.userdata.avatar,
+}))(Avatar)
-export const profilePage = new connectedProfilePage({
+export const profilePage = new withUserdata({
userdata: store.getState().userdata,
backBtn: new Button({
label: '',
@@ -130,9 +132,10 @@ export const profilePage = new connectedProfilePage({
},
},
}),
- avatar: new connectedAvatar({
+ avatar: new withUserAvatar({
src: store.getState().userdata.avatar,
alt: 'avatar',
+ canChange: true,
events: {
click: avatarUploadHandler,
},
diff --git a/src/pages/registerPage/registerPage.ts b/src/pages/registerPage/registerPage.ts
index 8c4897975..f5b0361a0 100644
--- a/src/pages/registerPage/registerPage.ts
+++ b/src/pages/registerPage/registerPage.ts
@@ -7,8 +7,8 @@ import { AuthController } from '@/controllers/AuthController.ts'
import Block from '@/core/Block'
import router from '@/router.ts'
import { RegisterData } from '@/services/AuthService.ts'
-import { withUserdata } from '@/utils/connect.ts'
import './registerPage.css'
+import connect from '@/utils/connect.ts'
// language=hbs
const registerPageTemplate = `
@@ -146,9 +146,9 @@ const registerForm = new Form({
}),
})
-const connectedRegisterPage = withUserdata(RegisterPage)
+const withUserdata = connect((state) => ({ userdata: state.userdata }))(RegisterPage)
-export const registerPage = new connectedRegisterPage({
+export const registerPage = new withUserdata({
registerForm: registerForm,
loginLink: new Link({
to: routes.login,
diff --git a/src/services/ChatService.ts b/src/services/ChatService.ts
index 524fc0677..0d00889a9 100644
--- a/src/services/ChatService.ts
+++ b/src/services/ChatService.ts
@@ -12,6 +12,14 @@ export class ChatService {
return HTTPTransport.get(`${this.baseURL}`, {})
}
+ getChatUsers(chatId: number) {
+ return HTTPTransport.get(`${this.baseURL}/${chatId}/users`, {})
+ }
+
+ getToken(chatId: number) {
+ return HTTPTransport.post(`${this.baseURL}/token/${chatId}`, {})
+ }
+
createChat(title: string) {
return HTTPTransport.post(`${this.baseURL}`, {
body: { title },
diff --git a/src/styles/base.css b/src/styles/base.css
index 9902de775..cc36b9e85 100644
--- a/src/styles/base.css
+++ b/src/styles/base.css
@@ -57,3 +57,29 @@ h4 {
width: 100%;
padding-bottom: 16px;
}
+
+.modal-content {
+ .users {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ .user {
+ display: flex;
+ align-items: center;
+
+ &__login {
+ flex-grow: 1;
+ }
+
+ &__delete-btn {
+ margin: 0;
+ border: none;
+ background: none;
+ cursor: pointer;
+ font-size: 20px;
+ line-height: 1;
+ }
+ }
+ }
+}
diff --git a/src/styles/variables.css b/src/styles/variables.css
index 50b93df77..13c952476 100644
--- a/src/styles/variables.css
+++ b/src/styles/variables.css
@@ -13,7 +13,7 @@
--blue-bg: #e4edfd;
--hover-bg: #ccc8;
--border: #eaeaea;
- --box-shadow: 0 0 3px 3px var(--color1);
+ --box-shadow: 0 0 3px 3px var(--hover-bg);
--border-radius: 8px;
--font-family: 'Inter', sans-serif;
}
diff --git a/src/utils/connect.ts b/src/utils/connect.ts
index f25135fbb..4207478c7 100644
--- a/src/utils/connect.ts
+++ b/src/utils/connect.ts
@@ -26,10 +26,3 @@ export default function connect(
}
}
-export const withUserdata = connect((state) => ({ userdata: state.userdata }))
-export const withUserAvatar = connect((state) => ({
- src: state.userdata.avatar,
-}))
-export const withChats = connect((state) => ({
- chats: state.chats,
-}))
diff --git a/src/utils/connectToMessageSocket.ts b/src/utils/connectToMessageSocket.ts
new file mode 100644
index 000000000..550fdade6
--- /dev/null
+++ b/src/utils/connectToMessageSocket.ts
@@ -0,0 +1,65 @@
+import { MessageItemProps } from '@/components/message/message.ts'
+import { ChatController } from '@/controllers/ChatController.ts'
+import store from '@/core/Store.ts'
+
+const chatController = new ChatController()
+
+export default async (userId: number, chatId: number) => {
+ let token = ''
+
+ await chatController.getToken(chatId).then((resp) => {
+ token = resp.token
+ })
+
+ const socket = new WebSocket(
+ `wss://ya-praktikum.tech/ws/chats/${userId}/${chatId}/${token}`
+ )
+
+ socket.addEventListener('open', () => {
+ console.log(`Соединение установлено c чатом ${chatId}`)
+
+ if (socket) {
+ socket.send(
+ JSON.stringify({
+ content: '0',
+ type: 'get old',
+ })
+ )
+ }
+ })
+
+ const ping = setInterval(() => {
+ socket?.send(JSON.stringify({ type: 'ping' }))
+ }, 10000)
+
+ socket.addEventListener('message', (event) => {
+ const data = JSON.parse(event.data)
+
+ if (Array.isArray(data)) {
+ store.set('messages', JSON.parse(event.data))
+ }
+ if (data.type === 'message') {
+ const messages = store.getState().messages
+ const message: MessageItemProps = JSON.parse(event.data)
+
+ message.time = new Date().toISOString()
+
+ store.set('messages', [message, ...messages])
+ }
+ if (data.type === 'user connected') {
+ const messages = store.getState().messages
+ const message: MessageItemProps = JSON.parse(event.data)
+
+ message.time = new Date().toISOString()
+ message.content = `Юзер с id: ${message.content} присоединился к чату`
+
+ store.set('messages', [message, ...messages])
+ }
+ })
+
+ socket.addEventListener('close', () => {
+ clearInterval(ping)
+ })
+
+ return socket
+}
diff --git a/src/utils/formatMessageDate.ts b/src/utils/formatMessageDate.ts
index 427aea1dd..2e69576d1 100644
--- a/src/utils/formatMessageDate.ts
+++ b/src/utils/formatMessageDate.ts
@@ -5,7 +5,6 @@ export default (date: string) => {
const offset = now - messageDate
let options: Intl.DateTimeFormatOptions
- console.log(offset)
if (offset < 24) {
options = {
diff --git a/vite.config.js b/vite.config.js
index 57676653d..6de0d954f 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -14,6 +14,7 @@ export default defineConfig({
},
},
server: {
+ host: true,
port: 3000,
open: true,
},