You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

505 lines
14 KiB

<script lang="ts" context="module">
export interface chatMessagesType {
id: number;
author: User;
text: string;
}
import { createEventDispatcher, onDestroy, onMount } from "svelte";
import { store, API_URL } from "../Auth";
import { socket } from "../socket";
import type { ChannelsType } from "./Channels.svelte";
import type User from "./Profile.svelte";
</script>
<script lang="ts">
let usersInterval: ReturnType<typeof setInterval>;
let blockedUsers: Array<User> = [];
let chatMembers: Array<User> = [];
let chatMessages: Array<chatMessagesType> = [];
export let channel: ChannelsType;
let newText = "";
onMount(async () => {
socket.connect();
getMembers();
usersInterval = setInterval(async () => {
getMembers();
}, 1000);
});
socket.on("messages", (msgs: Array<chatMessagesType>) => {
chatMessages = msgs;
});
async function getMembers() {
if (!channel) return;
let res = await fetch(API_URL + "/users/blocked/", {
credentials: "include",
mode: "cors",
});
if (res.ok) blockedUsers = await res.json();
res = await fetch(`${API_URL}/channels/${channel.id}/users`, {
credentials: "include",
mode: "cors"
})
if (res.ok) chatMembers = await res.json();
}
socket.on("newMessage", (msg: chatMessagesType) => {
console.log(msg)
chatMessages = [...chatMessages, msg];
});
onDestroy(() => {
socket.emit("LeaveChanel", async (response) => {
console.log(response.status);
});
clearInterval(usersInterval)
socket.disconnect();
});
//--------------------------------------------------------------------------------/
const sendMessage = () => {
if (newText !== "") {
chatMessages = [...chatMessages.slice(-5 + 1)];
socket.emit("addMessage", {
text: newText,
UserId: $store.ftId,
ChannelId: channel.id,
});
newText = "";
const messagesDiv = document.querySelector(".messages");
if (messagesDiv) {
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
}
};
//--------------------------------------------------------------------------------/
const dispatch = createEventDispatcher();
let showProfileMenu = false;
let selectedUser: string | null = null;
function openProfile(username: string) {
showProfileMenu = true;
selectedUser = username;
showChatMembers = false;
}
function closeProfileMenu() {
showProfileMenu = false;
selectedUser = "";
}
onMount(closeProfileMenu);
//--------------------------------------------------------------------------------/
let showChatMembers = false;
function toggleChatMembers() {
showChatMembers = !showChatMembers;
}
//--------------------------------------------------------------------------------/
const blockUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
response = await fetch(API_URL + "/users/" + target.ftId + "/block", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: target.ftId }),
});
}
if (response.ok) alert("User blocked");
else alert("Failed to block user");
};
//--------------------------------------------------------------------------------/
const unblockUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
response = await fetch(API_URL + "/users/" + target.ftId + "/block", {
credentials: "include",
method: "DELETE",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: target.ftId }),
});
}
if (response.ok) alert("User blocked");
else alert("Failed to block user");
};
//--------------------------------------------------------------------------------/
const banUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
const duration = prompt("Enter a time for which the user will be banned from this channel")
response = await fetch(API_URL + "/channels/" + channel.id + "/ban", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ data: [target.ftId, duration]}),
});
socket.emit("kickUser", channel.id, $store.ftId, target.ftId);
}
if (response.ok) {
alert("User banned");
} else alert("Failed to ban user");
};
//--------------------------------------------------------------------------------/
const unbanUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
response = await fetch(API_URL + "/channels/" + channel.id + "/ban", {
credentials: "include",
method: "DELETE",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: target.ftId }),
});
}
if (response.ok) alert("User unbanned");
else alert("Failed to unban user");
};
//--------------------------------------------------------------------------------/
const kickUser = async (username: string) => {
const response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
socket.emit("kickUser", {chan : channel.id, from : $store.ftId, to: target.ftId});
} else {alert("merde")}
};
//--------------------------------------------------------------------------------/
const muteUser = async (username: string) => {
const prompt = window.prompt("Enter mute duration in seconds");
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
const target = await response.json();
if (response.ok) {
response = await fetch(API_URL + "/channels/" + channel.id + "/mute", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ data: [target.ftId, +prompt] }),
});
}
if (response.ok) alert("User muted");
else alert("Failed to mute user");
};
//--------------------------------------------------------------------------------/
const adminUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
response = await fetch(API_URL + "/channels/" + channel.id + "/admin", {
credentials: "include",
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: target.ftId }),
});
}
if (response.ok) {
alert("User admined");
} else {
alert("Failed to admin user");
}
};
//--------------------------------------------------------------------------------/
const removeAdminUser = async (username: string) => {
let response = await fetch(API_URL + "/users/" + username + "/byname", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
const target = await response.json();
response = await fetch(API_URL + "/channels/" + channel.id + "/admin", {
credentials: "include",
method: "DELETE",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ id: target.ftId }),
});
}
if (response.ok) {
alert("User admined");
} else {
alert("Failed to admin user");
}
};
//--------------------------------------------------------------------------------/
const leaveChannel = async () => {
const prompt = window.prompt("Are you sure you want to leave this channel? (y/n)");
if (prompt == "y") {
const response = await fetch(API_URL + "/channels/" + channel.id + "/leave", {
credentials: "include",
mode: "cors",
});
if (response.ok) {
window.location.href = "/channels";
} else {
alert("Failed to leave channel");
}
}
}
function onSendMessage() {
dispatch("send-message", selectedUser);
showProfileMenu = false;
}
</script>
<div class="overlay">
<div class="chat" on:click|stopPropagation on:keydown|stopPropagation>
<div class="messages">
{#each chatMessages as message}
<p class="message">
{#if !blockedUsers.filter((user) => user.username == message.author).length}
<span
class="message-name"
on:click={() => openProfile(message.author.username)}
on:keydown={() => openProfile(message.author.username)}
style="cursor: pointer;"
>
{message.author.username}
</span>: {message.text}
{/if}
</p>
{/each}
</div>
{#if showProfileMenu}
<div
class="profile-menu"
on:click|stopPropagation
on:keydown|stopPropagation
>
<ul>
<li>
<button on:click={onSendMessage}>
Send Message
</button>
</li>
<li>
<button on:click={() => dispatch("view-profile", selectedUser)}>
View Profile
</button>
</li>
<li>
<button on:click={() => dispatch("add-friend", selectedUser)}>
Add Friend
</button>
</li>
<li>
<button on:click={() => dispatch("invite-to-game", selectedUser)}>
Invite to Game
</button>
</li>
<li>
{#if !blockedUsers.filter((user) => (user.username = selectedUser)).length}
<button on:click={() => blockUser(selectedUser)}>
Block User
</button>
{:else}
<button on:click={() => unblockUser(selectedUser)}>
Unblock User
</button>
{/if}
</li>
<li><button on:click={closeProfileMenu}> Close </button></li>
</ul>
</div>
{/if}
<form on:submit|preventDefault={sendMessage}>
<input type="text" placeholder="Type a message..." bind:value={newText} />
<button>
<img src="img/send.png" alt="send" />
</button>
</form>
<button on:click={leaveChannel}>Leave</button>
<button
on:click|stopPropagation={toggleChatMembers}
on:keydown|stopPropagation
>
Chat Members
</button>
{#if showChatMembers}
<div
class="chatMembers"
on:click|stopPropagation
on:keydown|stopPropagation
/>
<ul>
{#each chatMembers as member}
<li>
<p>
{member.username}
<button on:click={() => banUser(member.username)}> ban </button>
<button on:click={() => kickUser(member.username)}> kick </button>
<button on:click={() => muteUser(member.username)}> mute </button>
<button on:click={() => adminUser(member.username)}> promote </button>
<button on:click={() => removeAdminUser(member.username)}> demote </button>
</p>
</li>
{/each}
</ul>
{/if}
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.chat {
background-color: #343a40;
border: 1px solid #dedede;
border-radius: 5px;
padding: 1rem;
width: 300px;
display: flex;
flex-direction: column;
}
.messages {
height: 200px;
overflow-y: scroll;
border-bottom: 1px solid #dedede;
padding-bottom: 1rem;
margin-bottom: 1rem;
}
.message {
font-size: 14px;
line-height: 1.4;
margin-bottom: 0.5rem;
}
.message-name {
font-weight: 600;
color: #4c4c4c;
}
input[type="text"] {
width: 100%;
padding: 0.5rem;
border: 1px solid #dedede;
border-radius: 5px;
font-size: 14px;
outline: none;
margin-bottom: 1rem;
}
button {
background-color: #6b8e23;
color: #ffffff;
border: none;
border-radius: 5px;
padding: 0.5rem 1rem;
font-size: 14px;
cursor: pointer;
outline: none;
margin-bottom: 1rem;
}
button:last-child {
margin-bottom: 0;
}
img {
width: 16px;
height: 16px;
}
.profile-menu,
.chatMembers {
position: absolute;
background-color: #ffffff;
border: 1px solid #dedede;
border-radius: 5px;
padding: 1rem;
max-height: 100px;
overflow-y: scroll;
z-index: 1;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
margin-bottom: 0.5rem;
}
li:last-child {
margin-bottom: 0;
}
</style>