-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
app.vue
141 lines (122 loc) · 4.17 KB
/
app.vue
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
<script setup lang="ts">
let ws: WebSocket | undefined;
const message = ref<string>("");
const messages = useState<{ id: number, user: string, message: string, created_at: string }[]>(() => []);
const userId = useCookie<string>("userId")
if (!userId.value) {
userId.value = generateName()
}
if (!messages.value.length) {
const res = await $fetch("/api/messages")
messages.value.push(...res.messages)
}
const log = (user: string, ...args: string[]) => {
console.log("[ws]", user, ...args);
messages.value.push({
id: 0,
message: args.join(" "),
user: user,
created_at: new Date().toLocaleString(),
});
scroll();
};
const connect = async () => {
const isSecure = location.protocol === "https:";
const url = (isSecure ? "wss:https://" : "ws:https://") + location.host + "/api/chat-ws?userId=" + userId.value;
if (ws) {
log("ws", "Closing previous connection before reconnecting...");
ws.close();
clear();
}
log("ws", "Connecting to", url, "...");
ws = new WebSocket(url);
ws.addEventListener("message", (event) => {
const { user = "system", message = "" } = event.data.startsWith("{")
? JSON.parse(event.data)
: { message: event.data };
log(
user,
typeof message === "string" ? message : JSON.stringify(message),
);
});
await new Promise((resolve) => ws!.addEventListener("open", resolve));
log("ws", "Connected!");
};
const clear = () => {
messages.value.splice(0, messages.value.length);
log("system", "previous messages cleared");
};
const scroll = () => {
nextTick(() => {
console.log('scrooling')
window.scrollTo(0, document.body.scrollHeight + 100);
})
}
const send = () => {
console.log("sending message...");
if (message.value) {
ws!.send(message.value);
}
message.value = "";
};
const ping = () => {
log("ws", "Sending ping");
ws!.send("ping");
};
onMounted(async () => {
connect();
scroll();
});
useServerHead({
title: "Nuxt Chat",
})
</script>
<template>
<div class="h-screen flex flex-col justify-between">
<main>
<div class="flex flex-col text-white" style="position: fixed; right: 0;">
<a href="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/pi0/nuxt-websocket">
view source on GitHub
</a>
</div>
<!-- Messages -->
<div id="messages" class="flex-grow flex flex-col justify-end px-4 pt-8 pb-21 sm:pb-12 bg-slate-900 min-h-screen">
<div class="flex items-center mb-4 overflow-x-scroll" v-for="message in messages" :key="message.id">
<div class="flex flex-col">
<p class="text-gray-500 mb-1 text-xs ml-10">{{ message.user }}</p>
<div class="flex items-center">
<img :src="'https://www.gravatar.com/avatar/' + encodeURIComponent(message.user) + '?s=512&d=monsterid'"
alt="Avatar" class="w-8 h-8 rounded-full" />
<div class="ml-2 bg-gray-800 rounded-lg p-2">
<p class="text-white">{{ message.message }}</p>
</div>
</div>
<p class="text-gray-500 mt-1 text-xs ml-10">{{ message.created_at }}</p>
</div>
</div>
</div>
<!-- Chatbox -->
<div class="bg-gray-800 px-4 py-2 flex items-center justify-between fixed bottom-0 w-full flex-col sm:flex-row">
<div class="w-full min-w-6">
<input type="text" placeholder="Type your message..."
class="w-full rounded-none px-4 py-2 bg-gray-700 text-white focus:outline-none focus:ring focus:border-blue-300 sm:rounded-l-lg"
@keydown.enter="send" v-model="message" />
</div>
<div class="flex w-full">
<button class="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 w-1/4" @click="send">
Send
</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 w-1/4" @click="ping">
Ping
</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 w-1/4" @click="connect">
Reconnect
</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 sm:rounded-r-lg w-1/4" @click="clear">
Clear
</button>
</div>
</div>
</main>
</div>
</template>