Compare commits

...

15 Commits

Author SHA1 Message Date
adb
5ca45742e4 chatInfo styling 2020-11-06 16:20:42 +01:00
adb
c31a08e5ea Merge remote-tracking branch 'origin/master' into chatInformations
# Conflicts:
#	src/App.vue
2020-11-06 16:00:47 +01:00
adb
a2b82ab615 update min-width 2020-11-06 15:46:48 +01:00
adb
40cf3e9cca Merge branch 'topBanner' into master 2020-11-06 15:43:40 +01:00
adb
2465487417 topBanner styling and implementation 2020-11-06 15:42:56 +01:00
adb
6ad113327d login styling 2020-11-06 15:07:47 +01:00
adb
dafcb5da40 Merge remote-tracking branch 'origin/master' into topBanner
# Conflicts:
#	src/App.vue
2020-11-06 11:08:25 +01:00
adb
d18ceaa7a7 vue-router and login page 2020-11-06 01:06:26 +01:00
adb
d87ff0e725 resize update 2020-11-05 23:08:20 +01:00
adb
54d712555b message align left on oversize 2020-11-04 17:31:21 +01:00
adb
62ad4f40e8 message align left on oversize 2020-11-03 21:38:42 +01:00
adb
cbc7e7eb3a message screen resize 2020-11-03 20:47:23 +01:00
adb
b5513e4eae WebSocket JSON 2020-11-03 19:26:01 +01:00
adb
237e7e7a66 websocket connection and error messages 2020-11-02 23:08:19 +01:00
adb
16afd7e156 api server 2020-11-02 20:38:27 +01:00
23 changed files with 649 additions and 253 deletions

37
api.js Normal file
View File

@ -0,0 +1,37 @@
const ws = require('ws')
//WS server
const wss = new ws.Server({
port: 8090,
perMessageDeflate: {
zlibDeflateOptions: {
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
clientNoContextTakeover: true,
serverNoContextTakeover: true,
serverMaxWindowBits: 10,
concurrencyLimit: 10,
threshold: 1024
}
});
//WS handler
wss.on('connection', (ws, req) => {
console.log(`${req.socket.remoteAddress} connected`)
ws.on('message', msgJSON => {
let msg = JSON.parse(msgJSON)
console.log(`${req.socket.remoteAddress} => ${msgJSON}`)
if (msg.type === 'message') wss.clients.forEach(client => client.send(msgJSON))
})
let msg = {
type: "info",
time: Date.now(),
content: "connected"
}
ws.send(JSON.stringify(msg))
})

194
package-lock.json generated
View File

@ -1744,16 +1744,6 @@
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"cacache": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
@ -1780,34 +1770,6 @@
"unique-filename": "^1.1.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"find-cache-dir": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
@ -1829,25 +1791,6 @@
"path-exists": "^4.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -1912,16 +1855,6 @@
"minipass": "^3.1.1"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"terser-webpack-plugin": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
@ -1938,18 +1871,6 @@
"terser": "^4.6.12",
"webpack-sources": "^1.4.3"
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.9",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.9.tgz",
"integrity": "sha512-mu9pg6554GbXDSO8LlxkQM6qUJzUkb/A0FJc9LgRqnU9MCnhzEXwCt1Zx5NObvFpzs2mH2dH/uUCDwL8Qaz9sA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
}
}
},
@ -11019,6 +10940,93 @@
}
}
},
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.9",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.9.tgz",
"integrity": "sha512-mu9pg6554GbXDSO8LlxkQM6qUJzUkb/A0FJc9LgRqnU9MCnhzEXwCt1Zx5NObvFpzs2mH2dH/uUCDwL8Qaz9sA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-router": {
"version": "3.4.9",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz",
"integrity": "sha512-CGAKWN44RqXW06oC+u4mPgHLQQi2t6vLD/JbGRDAXm0YpMv0bgpKuU5bBd7AvMgfTz9kXVRIWKHqRwGEb8xFkA==",
"dev": true
},
"vue-style-loader": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.2.tgz",
@ -11268,6 +11276,15 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
"dev": true
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
}
}
},
@ -11579,6 +11596,15 @@
}
}
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
},
"yargs": {
"version": "13.3.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
@ -11744,13 +11770,9 @@
}
},
"ws": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
"dev": true,
"requires": {
"async-limiter": "~1.0.0"
}
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
},
"xtend": {
"version": "4.0.2",

View File

@ -9,7 +9,8 @@
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11"
"vue": "^2.6.11",
"ws": "^7.3.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
@ -18,6 +19,7 @@
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-router": "^3.4.9",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {

View File

Before

Width:  |  Height:  |  Size: 210 B

After

Width:  |  Height:  |  Size: 210 B

View File

@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 265 B

View File

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

View File

Before

Width:  |  Height:  |  Size: 350 B

After

Width:  |  Height:  |  Size: 350 B

View File

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 465 B

View File

@ -1,68 +1,19 @@
<template>
<div id="app">
<div class="content">
<div class="messages">
<div class="spacer" style="height: 4rem;"></div>
<message msg="Hey :D" />
<message msg="Du bist blööööd xD" />
<messageReceive msg="Du auch" />
<message msg="lol" />
<messageReceive msg="Du bist voll blöd, ich hasse dich, warum tust du das?!" />
<message msg="Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Ut imperdiet vel risus tristique mollis. Proin aliquam felis non vehicula ornare.
Fusce scelerisque pellentesque erat quis sollicitudin.
Quisque aliquet, ligula ut volutpat vulputate, ligula lorem dictum velit, et aliquam sapien orci sed magna.
Nam suscipit ex eget urna accumsan pulvinar. Pellentesque fringilla placerat feugiat.
Aenean aliquam vestibulum metus. Nulla augue turpis, consectetur vitae quam ac, porttitor rhoncus nunc.
Nullam non turpis consequat, placerat lectus nec, ornare orci.
Fusce lorem tortor, viverra ac suscipit sit amet, scelerisque id eros.
Suspendisse et ultricies elit, vitae pretium ipsum. Suspendisse vel ex in turpis pulvinar feugiat. "
/>
<messageReceive msg="Du hast Pizza!" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<div class="spacer" style="height: 1rem;"></div>
</div>
<newMessage />
<topBanner />
<chatInformations />
<router-view />
<error />
</div>
</div>
</template>
<script>
import message from './components/message.vue';
import messageReceive from './components/messageReceive.vue';
import newMessage from './components/newMessage.vue';
import topBanner from './components/topBanner.vue';
import chatInformations from './components/chatInformations.vue';
import error from "@/components/error.vue";
export default {
name: 'App',
components: {
message,
messageReceive,
newMessage,
topBanner,
chatInformations
components:{
error
}
}
</script>
@ -86,20 +37,10 @@ body{
padding: 0;
min-height: calc(100%);
width: 50rem;
min-width: 15rem;
min-width: 18rem;
background-color: #313131;
box-shadow: 3px 3px 10px #111;
}
.messages{
position: absolute;
margin: 0;
left: 0;
top: 0;
height: calc(100% - 4rem);
width: 100%;
overflow-y: auto;
}
@media (max-width: 55rem){
.content{
width: calc(100%);

View File

@ -0,0 +1,48 @@
<template>
<div id="chatInformation" class="chatInformation">
<h1>open chat</h1>
<icon class="closeBtn" onclick="this.parentNode.style.display = 'none'" ic="./sym/ic_close_white_24px.svg" />
</div>
</template>
<script>
import icon from './icon.vue';
export default {
name: "chatInformation",
components:{
icon
}
}
</script>
<style scoped>
.chatInformation{
position: absolute;
left: 50%;
transform: translate(-50%, 0);
top: 5rem;
width: calc(100% - 8rem);
max-width: 30rem;
height: calc(100% - 10rem);
background-color: #1d1d1d;
box-shadow: 6px 6px 20px #111;
border-radius: 1rem;
text-align: center;
}
.closeBtn{
position: absolute;
top: 0;
right: 0;
background-color: #0000;
box-shadow: none;
}
@media (max-width: 30rem) {
.chatInformation{
transform: unset;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
}
</style>

View File

@ -1,18 +0,0 @@
<template>
<div class="chatInformations">
</div>
</template>
<script >
export default {
name: "chatInformations"
}
</script>
<style scoped>
.chatInformations{
position: absolute;
width: 100%;
height: 100%;
background-color: #f00;
}
</style>

68
src/components/error.vue Normal file
View File

@ -0,0 +1,68 @@
<template>
<div id="errorBox" class="errorBox">
<icon class="errorBtn" onclick="this.parentNode.style.display = 'none'" ic="./sym/ic_close_white_24px.svg" />
<div id="errorMessage" class="btnText">
{{msg}}
</div>
</div>
</template>
<script>
import icon from './icon.vue';
export default {
name: "error",
components: {
icon
},
props:{
msg: String
}
}
</script>
<style scoped>
.errorBox{
position: absolute;
bottom: 1rem;
left: 1rem;
height: 10rem;
width: 16rem;
background-color: #E53935;
border-radius: 15px;
box-shadow: 3px 3px 10px #111;
}
#errorBox{
display: none;
}
.btnText {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.2rem;
color: #fff;
font-family:Arial, "lucida console", sans-serif;
}
.errorBtn{
position: absolute;
top: 0;
right: 0;
background-color: #0000;
box-shadow: none;
}
@keyframes slide-from-left{
from{left:-30rem;opacity: 0}
to{left:1rem;opacity: 1}
}
@media (max-width: 35rem) {
.errorBox {
bottom: 1rem;
left: 1rem;
height: 8rem;
width: calc(100% - 2rem);
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="iconContainer" >
<img class="icon" :src="ic" alt="" />
</div>
<button class="iconContainer" >
<img class="icon" v-bind:src=ic alt="" />
</button>
</template>
<script>
@ -22,12 +22,14 @@ name: "icon",
box-shadow: 3px 3px 10px #111;
cursor: pointer;
user-select: none;
border: none;
}
.icon{
position: absolute;
height: 1.5rem;
width: auto;
top: 0.75rem;
left: 0.75rem;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
</style>

View File

@ -18,7 +18,7 @@ export default {
<style scoped>
.messageContainer{
position: relative;
margin-top: 0.5rem;
margin-top: 0.25rem;
width: calc(100% - 2rem);
left: 1rem;
}
@ -28,10 +28,18 @@ export default {
width: max-content;
min-width: 5rem;
max-width: calc(100% - 3rem);
padding: 1rem;
padding: 0.7rem 1rem 0.45rem 1rem;
right: 0;
background-color: #42b983;
background-color: #42a7b9;
border-radius: 1rem 1rem 0 1rem;
text-align: left;
word-break: break-word;
}
@media (min-width: 45rem){
.message{
margin-left: 0; margin-right: auto;
border-radius: 1rem 1rem 1rem 0;
}
}
</style>

View File

@ -18,7 +18,7 @@ export default {
<style scoped>
.messageContainer{
position: relative;
margin-top: 0.5rem;
margin-top: 0.25rem;
width: calc(100% - 2rem);
left: 1rem;
}
@ -27,9 +27,9 @@ export default {
width: max-content;
min-width: 5rem;
max-width: calc(100% - 3rem);
padding: 1rem;
padding: 0.7rem 1rem 0.45rem 1rem;
left: 0;
background-color: #42a7b9;
background-color: #42b983;
border-radius: 1rem 1rem 1rem 0;
text-align: left;
}

View File

@ -1,33 +1,53 @@
<template>
<div class="newMessageBanner">
<textarea name="input" id="newMessageInput" class="newMessageInput" placeholder="type a message ..." type="text" v-model="content" />
<icon style="position: absolute; right: 1rem; bottom: 0.5rem;" ic="./sym/ic_send_white_24px.svg" />
<label for="newMessageInput"></label>
<textarea @input="resizeMessageBanner()" ref="newMessageInput" id="newMessageInput" class="newMessageInput"
autocomplete="off" placeholder="type a message ..." v-model="msg.content.text" />
<icon @click.native="sendMessage()" id="sendMessageBtn" style="position: absolute; right: 1rem; bottom: 0.5rem;"
ic="./sym/ic_send_white_24px.svg" />
</div>
</template>
<script>
import icon from './icon.vue';
import main from '../main.js';
export default {
name: "newMessage",
components: {
icon
},
props: {
content: String,
methods: {
sendMessage(){
if (this.msg.content.text !== "") {
this.msg.time = Date.now()
main.methods.sendWebSocket(this.msg)
this.msg.content.text = ""
this.resizeMessageBanner()
}
},
resizeMessageBanner(){
let id = this.$refs.newMessageInput
id.style.height = '1.25rem'
id.style.height = `${id.scrollHeight}px`
let msgContainer = document.getElementById("messagesContainer")
msgContainer.style.height
= `calc(100% - ${id.parentElement.clientHeight}px - 3rem)`
//msgContainer.scrollTo(0, msgContainer.scrollHeight)
}
},
mounted() {
ResizeListener(document.getElementById("newMessageInput"));
data(){
return {
msg: {
type: "message",
time: Date.now(),
content: {
text: ""
}
}
}
}
}
export const ResizeListener = (id) => {
id.addEventListener("input", resize);
}
function resize() {
this.style.height = "auto";
this.style.height = `${this.scrollHeight}px`;
}
</script>
<style scoped>
@ -43,12 +63,13 @@ function resize() {
}
.newMessageInput{
position: relative;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
margin-top: 1.5rem;
margin-bottom: 1rem;
left: 2rem;
min-height: 3rem;
min-height: 1.25rem;
max-height: 14rem;
width: calc(100% - 7rem);
height: 1.25rem;
background-color: #fff0;
border: 0 solid #fff0;
color: #fff;

View File

@ -0,0 +1,34 @@
<template>
<button class="btn">
<div class="btnText">{{text}}</div>
</button>
</template>
<script>
export default {
name: "textbtn",
props:{
text: String
}
}
</script>
<style scoped>
.btn{
cursor: pointer;
border: none;
height: 2.5rem;
padding-left: 2rem;
padding-right: 2rem;
background-color: #00BCD4;
box-shadow: 3px 3px 10px #222;
border-radius: 1.25rem;
margin: 1rem;
}
.btnText {
position: relative;
font-size: 1.4rem;
color:#fff;
font-family:Arial, "lucida console", sans-serif;
}
</style>

View File

@ -1,50 +1,65 @@
<template>
<div class="topBanner">
<div>
<div id = "icon-arrow" ><img alt="arrow" class="arrow-Top-Left" src="../sym/arrow_back-24px.svg"></div>
<div id = "picTop"><img alt="Bild" id="picTopPic" src="../sym/supervisor_account-24px.svg"></div>
<icon class="smallIcon" id="icon-arrow" ic="./sym/arrow_back-24px.svg" />
<icon class="smallIcon" id="picTop" ic="./sym/supervisor_account-24px.svg" />
<div id="container">
<div id = "chatName"> OpenChat</div>
<div id = "users">9 Mitglieder</div>
<div id="chatName">{{roomInfo.name}}</div>
<div id="users">{{roomInfo.user.length}} members</div>
</div>
<div id = "icon-menu"><img alt="menu" class="menu-Top-Right" src="../sym/menu-24px.svg"></div>
<icon v-on:click.native="showChatInfo()" class="smallIcon" id="icon-menu" ic="./sym/menu-24px.svg" />
</div>
</div>
</template>
<script >
export default {
name: "topBanner"
<script>
import icon from '@/components/icon.vue';
export default {
name: "topBanner",
components:{
icon
},
methods:{
showChatInfo(){
document.getElementById("chatInformation").style.display = 'block';
}
},
data(){
return {
roomInfo: {
name: "open chat",
user: []
}
}
}
}
</script>
<style scoped>
.topBanner{
position: absolute;
width: 100%;
height: 3rem;
background-color: #1d1d1d;
box-shadow: 0 3px 10px #111;
}
.smallIcon{
top: 0.25rem;
background-color: #2d2d2d;
height: 2.5rem;
width: 2.5rem;
}
#icon-arrow{
position: absolute;
top: 0.24rem;
left: 1rem;
width: 100%;
height: 100%;
max-height: 2.3rem;
max-width: 2.3rem;
border-radius: 1.5rem;
text-align: center;
background-color: #2d2d2d;
}
.arrow-Top-Left{
margin-top: 0.2rem;
height: 2rem;
width: 2rem;
}
#picTop{
top: 0.24rem;
position: absolute;
fill: #42a7b9;
left: 4rem;
background-color: #42a7b9;
}
#picTopPic{
width: 2.3rem;
height: 2.3rem;
border-radius: 1.5rem;
#icon-menu{
position: absolute;
right: 1rem;
background-color: #2d2d2d;
}
#container{
@ -53,35 +68,11 @@
left: 7.5rem;
}
#chatName{
font-size: medium;
font-size: 1rem;
color: #ededed;
}
#users{
font-size: x-small;
font-size: 0.75rem;
color: #9c9c9c;
}
#icon-menu{
position: absolute;
top: 0.24rem;
right: 1rem;
width: 100%;
height: 100%;
max-height: 2.3rem;
max-width: 2.3rem;
border-radius: 1.5rem;
text-align: center;
background-color: #2d2d2d;
}
.menu-Top-Right {
margin-top: 0.2rem;
height: 2rem;
width: 2rem;
}
.topBanner{
position: absolute;
width: 100%;
height: 3rem;
background-color: #1d1d1d;
}
</style>

View File

@ -1,8 +1,107 @@
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import login from './views/login.vue'
import chat from './views/chat.vue'
Vue.config.productionTip = false
Vue.use(VueRouter)
const router = new VueRouter({
routes: [
{
path: '/',
name: 'home',
component: login
},
{
path: '/login',
name: 'login',
component: login
},
{
path: '/chat',
name: 'chat',
component: chat
}
]
})
new Vue({
render: h => h(App),
el: '#app',
router,
template: '<App/>',
components: {App}
}).$mount('#app')
export default {
mounted() {
sendMessage()
},
methods: {
sendMessage(message){
let msg = {
type: "message",
time: Date.now(),
content: {
message: message
}
}
socket.send(JSON.stringify(msg))
},
sendWebSocket(msg){
socket.send(JSON.stringify(msg))
}
}
}
const wsurl = 'ws://127.0.0.1:8090'
const socket = new WebSocket(wsurl)
function element(id){ return document.getElementById(id)}
socket.onopen = () => {
let msg = {
type: "info",
time: Date.now(),
content: "new session"
}
socket.send(JSON.stringify(msg))
}
socket.onerror = (error) => {
console.log(`WebSocket error: ${error}`)
}
socket.onclose = () => show_error('session ended (refresh)')
socket.onmessage = (e) => {
console.log(`data received => ${e.data}`)
let msg = JSON.parse(e.data)
if (msg.type === 'error') show_error(msg.content)
else if (msg.type === 'message'){
//just for now, ik it's dirty
element('messages').innerHTML +=
`<div class="messageContainer" data-v-032da2b2="">
<div class="message" data-v-032da2b2="">
${msg.content.text}
</div>
</div>`;
}
}
function show_error(msg) {
let error_style = element('errorBox').style
element('errorMessage').innerText = msg
error_style.display = "block"
error_style.animation = "slide-from-left alternate 0.2s"
setTimeout(() => {error_style.animation = ""}, 200)
}
function sendMessage(message){
let msg = {
type: "message",
time: Date.now(),
content: {
message: message
}
}
socket.send(JSON.stringify(msg))
}

0
src/router/index.js Normal file
View File

68
src/views/chat.vue Normal file
View File

@ -0,0 +1,68 @@
<template>
<div>
<div ref="msgContainer" id="messagesContainer" class="messagesContainer">
<div id="messages" class="messages">
<message msg="Hey :D" />
<message msg="Du bist blööööd xD" />
<messageReceive msg="Du auch" />
<message msg="lol" />
<messageReceive msg="Du bist voll blöd, ich hasse dich, warum tust du das?!" />
<message msg="Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Ut imperdiet vel risus tristique mollis. Proin aliquam felis non vehicula ornare.
Fusce scelerisque pellentesque erat quis sollicitudin.
Quisque aliquet, ligula ut volutpat vulputate, ligula lorem dictum velit, et aliquam sapien orci sed magna.
Nam suscipit ex eget urna accumsan pulvinar. Pellentesque fringilla placerat feugiat.
Aenean aliquam vestibulum metus. Nulla augue turpis, consectetur vitae quam ac, porttitor rhoncus nunc.
Nullam non turpis consequat, placerat lectus nec, ornare orci.
Fusce lorem tortor, viverra ac suscipit sit amet, scelerisque id eros.
Suspendisse et ultricies elit, vitae pretium ipsum. Suspendisse vel ex in turpis pulvinar feugiat. "
/>
<messageReceive msg="Du hast Pizza!" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
<message msg="und Kuchen :P" />
</div>
</div>
<newMessage />
<topBanner />
<chatInformation />
</div>
</template>
<script>
import message from '@/components/message.vue';
import messageReceive from '@/components/messageReceive.vue';
import newMessage from '@/components/newMessage.vue';
import topBanner from "@/components/topBanner";
import chatInformation from "@/components/chatInformation";
export default {
name: 'chat',
components: {
message,
messageReceive,
newMessage,
topBanner,
chatInformation
}
}
</script>
<style scoped>
.messagesContainer{
position: absolute;
margin: 0;
left: 0;
top: 3rem;
height: calc(100% - 7rem);
width: 100%;
overflow-y: auto;
}
.messages{
position: relative;
margin-top: 1rem;
margin-bottom: 1rem;
}
</style>

66
src/views/login.vue Normal file
View File

@ -0,0 +1,66 @@
<template>
<div id="login">
<h1 class="title">open chat</h1>
<div class="input-field" id="longurl">
<label for="longurl-input"></label>
<input v-model="session.content.user" class="input" id="longurl-input" type="text" autocomplete="off" maxlength="20" placeholder="chose nickname">
</div>
<input type="hidden" value="search" name="login">
<textbtn text="login" />
</div>
</template>
<script>
import textbtn from '@/components/textbtn';
export default {
name: "login.vue",
components: {
textbtn
},
data(){
return {
session: {
type: "session",
time: Date.now(),
content: {
user: ""
}
}
}
}
}
</script>
<style scoped>
#login{
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
height: min-content;
width: 100%;
}
input{
padding: 0 2rem 0 2rem;
height: 2.5rem;
color: #fff;
background-color: #1d1d1d;
border-radius: 1.25rem;
border: 1px solid #fff;
text-align: center;
font-size: 1.1rem;
}
input:focus{
color: #000;
background-color: #fff;
}
@media (max-width: 35rem) {
input {
width: calc(100% - 8rem);
}
}
</style>

3
vue.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
runtimeCompiler: true
}