Browse Source

connected friends part to back

master
nicolas-arnaud 2 years ago
parent
commit
7c4bd5e879
  1. 2
      back/volume/src/auth/42.strategy.ts
  2. 11
      back/volume/src/chat/chat.gateway.ts
  3. 2
      back/volume/src/main.ts
  4. 30
      back/volume/src/users/users.controller.ts
  5. 22
      back/volume/src/users/users.service.ts
  6. 152
      front/volume/package-lock.json
  7. 1
      front/volume/package.json
  8. 163
      front/volume/src/App.svelte
  9. 29
      front/volume/src/Auth.ts
  10. 158
      front/volume/src/components/Channels.svelte
  11. 10
      front/volume/src/components/Chat2.svelte
  12. 44
      front/volume/src/components/Friends.svelte
  13. 24
      front/volume/src/components/NavBar.svelte
  14. 51
      front/volume/src/components/Profile.svelte

2
back/volume/src/auth/42.strategy.ts

@ -36,7 +36,7 @@ export class FtStrategy extends PassportStrategy(Strategy, '42') {
if ((await this.usersService.findUser(ftId)) === null) {
const newUser = new User()
newUser.ftId = profile.id as number
newUser.username = profile.displayName as string
newUser.username = profile.username as string
newUser.avatar = ftId + '.jpg'
this.usersService.create(newUser)
const file = createWriteStream('avatars/' + ftId + '.jpg')

11
back/volume/src/chat/chat.gateway.ts

@ -37,8 +37,10 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
async handleConnection (socket: Socket) {
try {
const user: User | null = await this.userService.findUser(socket.data.user.ftId)
if (!user) {
const user: User | null = await this.userService.findUser(
socket.data.user.ftId
)
if (user == null) {
socket.emit('Error', new UnauthorizedException())
// socket.disconnect();
return
@ -66,8 +68,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
const channel = new Channel()
channel.name = channeldto.name
const owner = await this.userService.findUser(channeldto.owner)
if (!owner)
return null;
if (owner == null) return null
channel.owners.push(owner)
channel.password = channeldto.password
/// ...///
@ -98,7 +99,7 @@ export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
const channel = await this.chatService.getChannel(
createdMessage.channel.id
)
if (channel) {
if (channel != null) {
const users = await this.userService.findOnlineInChannel(channel)
}
/// TODO: Send message to users

2
back/volume/src/main.ts

@ -7,7 +7,7 @@ import * as session from 'express-session'
import * as passport from 'passport'
import { type NestExpressApplication } from '@nestjs/platform-express'
import * as cookieParser from 'cookie-parser'
import { Response } from 'express';
import { Response } from 'express'
async function bootstrap () {
const logger = new Logger()

30
back/volume/src/users/users.controller.ts

@ -54,7 +54,7 @@ export class UsersController {
async getInvits (@FtUser() profile: Profile) {
return await this.usersService.getInvits(profile.id)
}
@Post('avatar')
@UseGuards(AuthenticatedGuard)
@UseInterceptors(
@ -88,18 +88,16 @@ export class UsersController {
async getAvatar (
@FtUser() profile: Profile,
@Res({ passthrough: true }) response: Response
){
return await this.getAvatarById(profile.id, response);
}
) {
return await this.getAvatarById(profile.id, response)
}
@Get('user/:name')
async getUserByName(
@Param('name') username: string
): Promise<User | null> {
return await this.usersService.findUserByName(username);
async getUserByName (@Param('name') username: string): Promise<User | null> {
return await this.usersService.findUserByName(username)
}
@Post('invit/:id')
@Get('invit/:id')
@UseGuards(AuthenticatedGuard)
async invitUser (
@FtUser() profile: Profile,
@ -110,11 +108,11 @@ export class UsersController {
@Get('avatar/:id')
async getAvatarById (
@Param('id', ParseIntPipe) ftId: number,
@Param('id', ParseIntPipe) ftId: number,
@Res({ passthrough: true }) response: Response
) {
const user = await this.usersService.findUser(ftId)
if (!user) return
if (user == null) return
const filename = user.avatar
const stream = createReadStream(join(process.cwd(), 'avatars/' + filename))
response.set({
@ -125,7 +123,9 @@ export class UsersController {
}
@Get(':id')
async getUserById (@Param('id', ParseIntPipe) ftId: number): Promise<User | null> {
async getUserById (
@Param('id', ParseIntPipe) ftId: number
): Promise<User | null> {
return await this.usersService.findUser(ftId)
}
@ -139,7 +139,7 @@ export class UsersController {
@UseGuards(AuthenticatedGuard)
async create (@Body() payload: UserDto, @FtUser() profile: Profile) {
const user = await this.usersService.findUser(profile.id)
if (user) {
if (user != null) {
return await this.usersService.update(user, payload)
} else {
return await this.usersService.create(payload)

22
back/volume/src/users/users.service.ts

@ -44,25 +44,28 @@ export class UsersService {
.getMany()
}
async update (user: User, changes: UserDto):Promise < User | null> {
async update (user: User, changes: UserDto): Promise<User | null> {
this.usersRepository.merge(user, changes)
return await this.usersRepository.save(user)
}
async addAvatar (ftId: number, filename: string) {
return await this.usersRepository.update({ftId}, {
avatar: filename
})
return await this.usersRepository.update(
{ ftId },
{
avatar: filename
}
)
}
async getFriends (ftId: number): Promise< User[] >{
async getFriends (ftId: number): Promise<User[]> {
const user = await this.usersRepository.findOne({
where: { ftId },
relations: {
friends: true
}
})
if (!user) return []
if (user == null) return []
return user.friends
}
@ -73,19 +76,20 @@ export class UsersService {
followers: true
}
})
if (!user) return null
if (user == null) return null
return user.followers
}
async invit (ftId: number, targetFtId: number) {
const user = await this.findUser(ftId)
if (!user) return null
if (user == null) return null
const target = await this.findUser(targetFtId)
if (target == null) {
return new NotFoundException(
`Error: user id ${targetFtId} isn't in our db.`
)
} const id = user.followers.findIndex(
}
const id = user.followers.findIndex(
(follower) => follower.ftId === targetFtId
)
if (id != -1) {

152
front/volume/package-lock.json

@ -10,7 +10,6 @@
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0",
"axios": "^1.3.4",
"svelte": "^3.55.1",
"vite": "^4.1.0"
},
@ -470,21 +469,6 @@
"node": ">= 8"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -567,17 +551,6 @@
"fsevents": "~2.3.2"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -608,14 +581,6 @@
"node": ">=0.10.0"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -704,38 +669,6 @@
"node": ">=8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -935,25 +868,6 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@ -1130,11 +1044,6 @@
"svelte": "^3.2.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@ -1773,21 +1682,6 @@
"picomatch": "^2.0.4"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -1847,14 +1741,6 @@
"readdirp": "~3.6.0"
}
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -1874,11 +1760,6 @@
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.0.tgz",
"integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"detect-indent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
@ -1951,21 +1832,6 @@
"to-regex-range": "^5.0.1"
}
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -2113,19 +1979,6 @@
"picomatch": "^2.3.1"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.52.0"
}
},
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@ -2241,11 +2094,6 @@
"dev": true,
"requires": {}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",

1
front/volume/package.json

@ -20,7 +20,6 @@
"dependencies": {
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@tsconfig/svelte": "^3.0.0",
"axios": "^1.3.4",
"svelte": "^3.55.1",
"vite": "^4.1.0"
}

163
front/volume/src/App.svelte

@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte'
import { onMount } from "svelte";
import Navbar from "./components/NavBar.svelte";
import Profile from "./components/Profile.svelte";
import MatchHistory from "./components/MatchHistory.svelte";
@ -15,14 +15,13 @@
import Channels from "./components/Channels.svelte";
import type { ChannelsType } from "./components/Channels.svelte";
import { store, getUser, login, logout } from "./Auth";
import { store, getUser, login, logout, API_URL } from "./Auth";
// PROFILE
onMount(() => {
getUser()
})
getUser();
});
let isProfileOpen = false;
function clickProfile() {
@ -46,18 +45,26 @@
// FRIENDS
let friends: Friend[] = [];
let invits: Friend[] = [];
export async function getFriends(): Promise<Friend[]> {
let response = await fetch(API_URL + "/friends", {
credentials: "include",
});
return await response.json();
}
export async function getInvits(): Promise<Friend[]> {
let response = await fetch(API_URL + "/invits", { credentials: "include" });
return await response.json();
}
let isFriendOpen = false;
function clickFriends() {
async function clickFriends() {
isFriendOpen = true;
friends = await getFriends();
invits = await getInvits();
}
let friends: Array<Friend> = [
{ username: "Alice", status: "online" },
{ username: "Bob", status: "online" },
{ username: "Charlie", status: "offline" },
{ username: "Dave", status: "offline" },
{ username: "Eve", status: "in a game" },
{ username: "Frank", status: "online" },
];
// SPECTATE
let isSpectateOpen = false;
@ -79,9 +86,15 @@
isChannelsOpen = true;
}
let channels: Array<ChannelsType> = [
{ id: "1", name: "General", messages: [] },
{ id: "2", name: "Lobby", messages: [] },
{ id: "3", name: "Game", messages: [] },
{ id: "1", name: "General", messages: [], privacy: "public", password: "" },
{
id: "2",
name: "Lobby",
messages: [],
privacy: "private",
password: "test",
},
{ id: "3", name: "Game", messages: [], privacy: "private", password: "" },
];
let selectedChannel: ChannelsType;
const handleSelectChannel = (channel: ChannelsType) => {
@ -95,72 +108,72 @@
<h1><button type="button" on:click={login}>Log In</button></h1>
{:else}
<h1><button type="button" on:click={logout}>Log Out</button></h1>
<Navbar
{clickProfile}
{clickHistory}
{clickFriends}
{clickSpectate}
{clickChannels}
/>
{#if isChannelsOpen}
{#if selectedChannel}
<div
on:click={() => (selectedChannel = undefined)}
on:keydown={() => (selectedChannel = undefined)}
>
<Chat2 chatMessages={selectedChannel.messages} />
</div>
{/if}
{#if !selectedChannel}
<div
on:click={() => (isChannelsOpen = false)}
on:keydown={() => (isChannelsOpen = false)}
>
<Channels {channels} onSelectChannel={handleSelectChannel} />
</div>
{/if}
{/if}
{#if isSpectateOpen}
<div
on:click={() => (isSpectateOpen = false)}
on:keydown={() => (isSpectateOpen = false)}
>
<Spectate {spectate} />
</div>
{/if}
{#if isFriendOpen}
<div
on:click={() => (isFriendOpen = false)}
on:keydown={() => (isFriendOpen = false)}
>
<Friends {friends} />
</div>
{/if}
{#if isHistoryOpen}
<Navbar
{clickProfile}
{clickHistory}
{clickFriends}
{clickSpectate}
{clickChannels}
/>
{#if isChannelsOpen}
{#if selectedChannel}
<div
on:click={() => (isHistoryOpen = false)}
on:keydown={() => (isHistoryOpen = false)}
on:click={() => (selectedChannel = undefined)}
on:keydown={() => (selectedChannel = undefined)}
>
<MatchHistory {matches} />
<Chat2 chatMessages={selectedChannel.messages} />
</div>
{/if}
{#if isProfileOpen}
{#if !selectedChannel}
<div
on:click={() => (isProfileOpen = false)}
on:keydown={() => (isProfileOpen = false)}
on:click={() => (isChannelsOpen = false)}
on:keydown={() => (isChannelsOpen = false)}
>
<Profile
username="Alice"
wins={10}
losses={5}
elo={256}
rank={23}
is2faEnabled={false}
/>
<Channels {channels} onSelectChannel={handleSelectChannel} />
</div>
{/if}
<Play />
<Pong />
{/if}
{#if isSpectateOpen}
<div
on:click={() => (isSpectateOpen = false)}
on:keydown={() => (isSpectateOpen = false)}
>
<Spectate {spectate} />
</div>
{/if}
{#if isFriendOpen}
<div
on:click={() => (isFriendOpen = false)}
on:keydown={() => (isFriendOpen = false)}
>
<Friends {friends} {invits} />
</div>
{/if}
{#if isHistoryOpen}
<div
on:click={() => (isHistoryOpen = false)}
on:keydown={() => (isHistoryOpen = false)}
>
<MatchHistory {matches} />
</div>
{/if}
{#if isProfileOpen}
<div
on:click={() => (isProfileOpen = false)}
on:keydown={() => (isProfileOpen = false)}
>
<Profile
username={$store.username}
wins={10}
losses={5}
elo={256}
rank={23}
is2faEnabled={false}
/>
</div>
{/if}
<Play />
<Pong />
{/if}
</div>
</main>

29
front/volume/src/Auth.ts

@ -1,37 +1,36 @@
import { writable } from 'svelte/store';
import { writable } from "svelte/store";
let _user = localStorage.getItem('user');
let _user = localStorage.getItem("user");
export const store = writable(_user ? JSON.parse(_user) : null);
store.subscribe((value) => {
if (value) localStorage.setItem('user', JSON.stringify(value));
else localStorage.removeItem('user');
if (value) localStorage.setItem("user", JSON.stringify(value));
else localStorage.removeItem("user");
});
export const API_URL =
"http://" + import.meta.env.VITE_HOST +
":" + import.meta.env.VITE_BACK_PORT;
"http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT;
export async function getUser() {
const res = await fetch(API_URL, {
method: "get",
mode: 'cors',
mode: "cors",
cache: "no-cache",
credentials: 'include',
credentials: "include",
redirect: "follow",
referrerPolicy: "no-referrer",
})
let user = await res.json()
});
let user = await res.json();
if (user.username) {
console.log(user)
store.set(user)
console.log(user);
store.set(user);
}
}
export function login() {
window.location.replace(API_URL + "/log/in")
window.location.replace(API_URL + "/log/in");
}
export function logout() {
window.location.replace(API_URL + "/log/out")
store.set(null)
window.location.replace(API_URL + "/log/out");
store.set(null);
}

158
front/volume/src/components/Channels.svelte

@ -1,90 +1,92 @@
<script lang="ts" context="module">
import type { chatMessagesType } from "./Chat2.svelte";
export interface ChannelsType {
id: string;
name: string;
privacy: string;
password: string;
messages: Array<chatMessagesType>;
}
import type { chatMessagesType } from "./Chat2.svelte";
export interface ChannelsType {
id: string;
name: string;
privacy: string;
password: string;
messages: Array<chatMessagesType>;
}
</script>
<script lang="ts">
export let channels: Array<ChannelsType> = [];
export let onSelectChannel: (channel: ChannelsType) => void;
const selectChat = (id: string) => {
const channel = channels.find(c => c.id === id);
if (channel) {
onSelectChannel(channel);
}
}
const createChannel = () => {
const name = prompt("Enter a name for the new channel:");
if (name) {
const privacy = prompt("Enter a privacy setting for the new channel (public/private):");
if (privacy !== "public" && privacy !== "private") {
alert("Invalid privacy setting");
}
let password = "";
if (privacy === "private") {
password = prompt("Enter a password for the new channel:");
if (!password) {
alert("Invalid password");
}
}
if (privacy === "public" || password) {
const newChannel: ChannelsType = {
id: Math.random().toString(),
name,
privacy,
password,
messages: []
};
channels = [newChannel, ...channels];
}
}
// TODO: save to database
};
export let channels: Array<ChannelsType> = [];
export let onSelectChannel: (channel: ChannelsType) => void;
const selectChat = (id: string) => {
const channel = channels.find((c) => c.id === id);
if (channel) {
onSelectChannel(channel);
}
};
const createChannel = () => {
const name = prompt("Enter a name for the new channel:");
if (name) {
const privacy = prompt(
"Enter a privacy setting for the new channel (public/private):"
);
if (privacy !== "public" && privacy !== "private") {
alert("Invalid privacy setting");
}
let password = "";
if (privacy === "private") {
password = prompt("Enter a password for the new channel:");
if (!password) {
alert("Invalid password");
}
}
if (privacy === "public" || password) {
const newChannel: ChannelsType = {
id: Math.random().toString(),
name,
privacy,
password,
messages: [],
};
channels = [newChannel, ...channels];
}
}
// TODO: save to database
};
</script>
<div class="overlay">
<div class="channels" on:click|stopPropagation on:keydown|stopPropagation>
<div>
{#if channels.length > 0}
<h2>Channels</h2>
{#each channels.slice(0, 10) as _channels}
<li>
<span>{_channels.name}</span>
<button on:click={() => selectChat(_channels.id)}>Enter</button>
</li>
{/each}
{:else}
<p>No channels available</p>
{/if}
<button on:click={createChannel}>Create Channel</button>
</div>
</div>
<div class="channels" on:click|stopPropagation on:keydown|stopPropagation>
<div>
{#if channels.length > 0}
<h2>Channels</h2>
{#each channels.slice(0, 10) as _channels}
<li>
<span>{_channels.name}</span>
<button on:click={() => selectChat(_channels.id)}>Enter</button>
</li>
{/each}
{:else}
<p>No channels available</p>
{/if}
<button on:click={createChannel}>Create Channel</button>
</div>
</div>
</div>
<style>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 9998;
display: flex;
justify-content: center;
align-items: center;
}
.channels {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
.channels {
background-color: #fff;
border: 1px solid #ccc;
border-radius: 5px;
padding: 1rem;
width: 300px;
}
</style>

10
front/volume/src/components/Chat2.svelte

@ -6,7 +6,6 @@
}
</script>
<script lang="ts">
const sendMessage = () => {
if (newText !== "") {
@ -41,7 +40,7 @@
<li>View comments</li>
</ul>
`;
document.querySelector('.overlay')?.appendChild(optionsModal);
document.querySelector(".overlay")?.appendChild(optionsModal);
optionsModal.addEventListener("click", () => {
document.body.removeChild(optionsModal);
});
@ -54,7 +53,12 @@
<div class="messages">
{#each chatMessages as message}
<p class="message">
<span class="message-name" on:click={openProfile(message.id)} on:keydown={openProfile(message.id)} style="cursor: pointer;">
<span
class="message-name"
on:click={openProfile(message.id)}
on:keydown={openProfile(message.id)}
style="cursor: pointer;"
>
{message.name}
</span>: {message.text}
</p>

44
front/volume/src/components/Friends.svelte

@ -2,13 +2,16 @@
export interface Friend {
username: string;
status: "online" | "offline" | "in a game";
ftId: number;
}
</script>
<script lang="ts">
let api = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT
export let friends: Array<Friend>
fetch(api + "/friends").then((response) => response.json()).then((ret) => {friends = ret});
import { API_URL } from "../Auth";
export let friends: Friend[];
export let invits: Friend[];
async function addFriend(event: any) {
console.log(typeof event);
@ -17,24 +20,23 @@
console.log(usernameInput);
const username = usernameInput.value;
let ftId: number
fetch(api + "/user/" + username).then((response) => response.json()).then((ret) => {ftId = ret.ftID});
fetch(api + "/friends").then((response) => response.json()).then((ret) => {friends = JSON.parse(ret)});
const response = await fetch(api + "/invit/" + ftId, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username })
let response = await fetch(API_URL + "/user/" + username, {
credentials: "include",
mode: "cors",
});
let target = await response.json();
response = await fetch(API_URL + "/invit/" + target.ftId, {
credentials: "include",
});
if (response.ok) {
console.log('Invitation send.');
console.log("Invitation send.");
} else {
console.log('Unknown user.');
console.log("Unknown user.");
}
usernameInput.value = '';
alert("Trying to add friend" + username);
usernameInput.value = "";
alert("Trying to add friend: " + username);
}
</script>
@ -51,6 +53,16 @@
{:else}
<p>No friends to display</p>
{/if}
{#if invits.length > 0}
<h2>Monkey invits</h2>
{#each invits.slice(0, 10) as invit}
<li>
<span>{invit.username} invited you to be friend.</span>
</li>
{/each}
{:else}
<p>No invitations to display</p>
{/if}
<div>
<h3>Add a friend</h3>
<form on:submit={addFriend}>

24
front/volume/src/components/NavBar.svelte

@ -1,9 +1,13 @@
<script lang="ts">
let api = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT;
let api =
"http://" +
import.meta.env.VITE_HOST +
":" +
import.meta.env.VITE_BACK_PORT;
export let links = [
{ text: "Home", url: "img/pong.png" },
{ text: "Spectate" },
{ text: "Channels" },
{ text: "Channels" },
{ text: "History" },
{ text: "Friends" },
{ text: "Profile" },
@ -25,13 +29,13 @@
</button>
</li>
{/if}
{#if link.text === "Channels"}
<li>
<button on:click={clickChannels}>
<p>Channels</p>
</button>
</li>
{/if}
{#if link.text === "Channels"}
<li>
<button on:click={clickChannels}>
<p>Channels</p>
</button>
</li>
{/if}
{#if link.text === "Friends"}
<li>
<button on:click={clickFriends}>
@ -42,7 +46,7 @@
{#if link.text === "Profile"}
<li>
<button on:click={clickProfile}>
<img src={api + '/avatar'} alt="avatar" />
<img src={api + "/avatar"} alt="avatar" />
</button>
</li>
{/if}

51
front/volume/src/components/Profile.svelte

@ -1,38 +1,27 @@
<script lang="ts" context="module">
export interface User {
username: string;
}
</script>
<script lang="ts">
import { API_URL, store } from "../Auth";
export const API_URL = "http://" + import.meta.env.VITE_HOST + ":" + import.meta.env.VITE_BACK_PORT
export const AUTH_SERVER_URL = API_URL + "/log/in"
export let avatar = API_URL + "/avatar"
export let username = "";
export let username = $store.userame;
export let realname = "";
export let wins = 0;
export let losses = 0;
export let elo = 0;
export let rank = -1;
export let is2faEnabled = false;
const handleSubmit = () => {
const user: User = { username : username};
fetch("http://localhost:3001/", {
headers: {"content-type": "application/json"},
async function handleSubmit() {
let response = await fetch(API_URL, {
headers: { "content-type": "application/json" },
method: "POST",
body: JSON.stringify(user),
credentials: 'include'
})
.then((res) => res.json())
.then((data) => {
console.log(data);
})
.catch((err) => {
console.error(err);
});
body: JSON.stringify({ username: username }),
credentials: "include",
});
if (response.ok) {
alert("Succefully changed username.");
$store.username = username;
}
}
async function handle2fa(event: Event) {
event.preventDefault();
alert("Trying to " + (is2faEnabled ? "disable" : "enable") + " 2FA");
@ -42,14 +31,14 @@
<div class="overlay">
<div class="profile" on:click|stopPropagation on:keydown|stopPropagation>
<div class="profile-header">
<img class="profile-img" src={avatar} alt="avatar" />
<img class="profile-img" src={API_URL + "/avatar"} alt="avatar" />
<h3>{realname}</h3>
<form action={avatar}
<form
action={API_URL + "/avatar"}
method="post"
enctype="multipart/form-data">
<label for="mavatar-input">Select a file:</label>
enctype="multipart/form-data"
>
<input type="file" id="avatar-input" name="avatar" />
<br /><br />
<input type="submit" />
</form>
</div>
@ -58,8 +47,8 @@
<div class="username">
<label for="username">Username</label>
<input type="text" id="username" bind:value={username} />
<button type="submit">Submit</button>
</div>
<button type="submit">Submit</button>
</form>
<p>Wins: {wins}</p>
<p>Losses: {losses}</p>

Loading…
Cancel
Save