Skip to content

Simple Chat

Video Lecture

Simple Chat Simple Chat

Description

Creating a simple chat client and server.

<>

./src/server/randomScreenNameGenerator.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export default class RandomScreenNameGenerator {
  private animals = [
    'Cat',
    'Dog',
    'Bird',
    'Tiger',
    'Giraffe',
    'Elephant',
    'Koala',
    'Bee',
    'Fly',
    'Fish',
    'Frog'
  ]
  private colours = ['Red', 'Green', 'Blue', 'Yellow', 'Orange', 'Purple']

  public generateRandomScreenName() {
    let colour = this.colours[Math.floor(Math.random() * this.colours.length)]
    let animal = this.animals[Math.floor(Math.random() * this.animals.length)]

    let screenName = {
      name: colour + ' ' + animal,
      abbreviation: colour[0] + animal[0]
    }

    return screenName
  }
}

./src/server/server.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import { createServer } from 'http'
import { Server } from 'socket.io'
import * as express from 'express'
import * as path from 'path'
import RandomScreenNameGenerator from './randomScreenNameGenerator'

const port = 3000

const app = express()
app.use(express.static(path.join(__dirname, '../client')))

const server = createServer(app)

const io = new Server(server)

const randomScreenNameGenerator = new RandomScreenNameGenerator()

io.on('connection', (socket) => {
  console.log('a user connected : ' + socket.id)

  let screenName = randomScreenNameGenerator.generateRandomScreenName()

  socket.emit('screenName', screenName)

  socket.broadcast.emit('systemMessage', screenName.name + ' has joined the chat')

  socket.on('disconnect', () => {
    console.log('socket disconnected : ' + socket.id)

    socket.broadcast.emit('systemMessage', screenName.name + ' has left the chat')
  })

  socket.on('chatMessage', (message: ChatMessage) => {
    socket.broadcast.emit('chatMessage', message)
  })
})

server.listen(port, () => {
  console.log('Server listening on port ' + port)
})

./src/typings/index.d.ts

1
2
3
4
5
6
7
8
9
type ScreenName = {
  name: string
  abbreviation: string
}

type ChatMessage = {
  message: string
  from: string
}

./src/server/tsconfig.json

1
2
3
4
5
6
7
8
9
{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "outDir": "../../dist/server",
    "strict": true
  },
  "include": ["**/*.ts", "../typings/*"]
}

./src/client/client.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { io } from 'socket.io-client'

let screenName: ScreenName

const messageList = document.getElementById('messageList') as HTMLOListElement
const messageText = document.getElementById('messageText') as HTMLInputElement
const sendButton = document.getElementById('sendButton') as HTMLButtonElement

const socket = io()

socket.on('connect', () => {
  console.log('connect')
})

socket.on('disconnect', (message) => {
  console.log('disconnect ' + message)
})

socket.on('screenName', (message: ScreenName) => {
  screenName = message
  ;(document.getElementsByClassName('screenName')[0] as HTMLSpanElement).innerText =
    screenName.name
})

socket.on('chatMessage', (message: ChatMessage) => {
  const li = document.createElement('li')
  li.innerHTML =
    "<span class='circle' style='float: right;'>" +
    message.from +
    "</span><div class='otherMessage'>" +
    message.message +
    '</div>'
  messageList.appendChild(li)

  scrollChatWindow()
})

socket.on('systemMessage', (message) => {
  const li = document.createElement('li')
  li.innerHTML = "<div class='systemMessage'>" + message + '</div>'
  messageList.appendChild(li)

  scrollChatWindow()
})

messageText.addEventListener('keypress', (e) => {
  if (e.code === 'Enter') {
    sendMessage()
    return false
  }
})

function sendMessage() {
  if (messageText.value.length > 0) {
    socket.emit('chatMessage', <ChatMessage>{
      message: messageText.value,
      from: screenName.abbreviation
    })

    const li = document.createElement('li')
    li.innerHTML =
      "<span class='circle' style='float: left;'>" +
      screenName.abbreviation +
      "</span><div class='myMessage'>" +
      messageText.value +
      '</div>'
    messageList.appendChild(li)

    messageText.value = ''

    scrollChatWindow()
  }
}

sendButton.addEventListener('click', () => {
  sendMessage()
})

function scrollChatWindow() {
  const count = document.querySelectorAll('#messageList li').length
  if (count > 10) {
    messageList.removeChild(messageList.firstChild as HTMLLIElement)
  }

  messageList.scrollTo({ top: messageList.scrollHeight, behavior: 'smooth' })
}

./src/client/tsconfig.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "NodeNext",
    "outDir": "../../dist/client/",
    "moduleResolution": "NodeNext",
    "strict": true
  },
  "include": ["**/*.ts", "../typings/*"]
}

./dist/client/index.html

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>
      Simple Chat : Socket.IO TypeScript Tutorials by Sean Bradley :
      https://sbcode.net/tssock/
    </title>
    <style>
      body {
        font-family: Arial;
      }

      .chatPanel {
        border: solid 1px gray;
        border-radius: 20px 0px 4px 4px;
        box-shadow: 15px 10px #dddddd;
        background: rgb(34, 193, 195);
        background: linear-gradient(
          56deg,
          rgba(34, 193, 195, 0.49193275943189773) 0%,
          rgba(253, 187, 45, 0.494733879880077) 100%
        );
        position: relative;
        width: 500px;
      }

      .chatMessageInputDiv {
        position: absolute;
        bottom: 0px;
        width: 100%;
      }

      #messageText {
        width: 400px;
        font-size: 21px;
      }

      #sendButton {
        position: absolute;
        right: 0px;
        width: 93px;
        height: 31px;
        border-radius: 0;
        border: 0px;
        color: #fff;
        background-color: #17a2b8;
        border-color: #17a2b8;
        font-size: 21px;
      }

      #messageList {
        padding-left: 0;
        overflow: hidden;
        height: 338px;
        margin-bottom: 40px;
      }

      #messageList li {
        list-style-type: none;
        margin-bottom: 30px;
        padding: 6px 6px 6px 6px;
        display: block;
      }

      .otherMessage {
        background: #ffcda3;
        border: solid 1px gray;
        margin: 0px 5px 5px 0px;
        float: right;
        padding: 0px 6px 0px 6px;
        border-radius: 10px 10px 0 10px;
        font-size: 21px;
      }

      .myMessage {
        background: #1bffbb;
        border: solid 1px gray;
        margin: 0px 5px 5px 5px;
        float: left;
        padding: 0px 6px 0px 6px;
        border-radius: 10px 10px 10px 0;
        font-size: 21px;
      }

      .systemMessage {
        background: #dddddd;
        border: solid 1px gray;
        margin: 0px 5px 5px 0px;
        float: right;
        padding: 0px 6px 0px 6px;
        border-radius: 10px 10px 0 10px;
        font-size: 21px;
      }

      .screenName {
        padding: 6px 6px 6px 6px;
        border-radius: 10px 10px 10px 10px;
        font-weight: bold;
        background: #1bffbb;
      }

      .circle {
        padding: 6px;
        background: gray;
        border-radius: 50px;
      }
    </style>
  </head>

  <body>
    <h1>Your Screen Name is <span class="screenName"></span></h1>
    <div class="chatPanel">
      <ol id="messageList"></ol>
      <div class="chatMessageInputDiv">
        <div>
          <input id="messageText" placeholder="Enter Chat Message" />
          <span>
            <button id="sendButton">Send</button>
          </span>
        </div>
      </div>
    </div>
    <script type="module" src="bundle.js"></script>
  </body>
</html>