From cc3e1a6a9cc1793dff1027aa4f3567fb17c0ea04 Mon Sep 17 00:00:00 2001 From: topchetoeu <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 30 Jun 2023 23:52:08 +0300 Subject: [PATCH] feat: add image page --- backend/routers/ImageRouter.ts | 17 +++++- frontend/src/app/app-routing.module.ts | 2 + frontend/src/app/app.component.scss | 21 ++++++++ frontend/src/app/app.module.ts | 4 +- frontend/src/app/image/image.component.html | 21 ++++---- frontend/src/app/image/image.component.scss | 21 ++++---- frontend/src/app/image/image.component.ts | 28 ++++++++-- .../src/app/message/message.component.html | 2 +- .../src/app/message/message.component.scss | 8 +++ .../app/page-image/page-image.component.html | 16 ++++++ .../app/page-image/page-image.component.scss | 40 ++++++++++++++ .../app/page-image/page-image.component.ts | 53 +++++++++++++++++++ frontend/src/app/services/images.service.ts | 3 ++ frontend/src/assets/trash.svg | 1 + 14 files changed, 210 insertions(+), 27 deletions(-) create mode 100644 frontend/src/app/page-image/page-image.component.html create mode 100644 frontend/src/app/page-image/page-image.component.scss create mode 100644 frontend/src/app/page-image/page-image.component.ts create mode 100644 frontend/src/assets/trash.svg diff --git a/backend/routers/ImageRouter.ts b/backend/routers/ImageRouter.ts index 93e94b4..4a55a30 100644 --- a/backend/routers/ImageRouter.ts +++ b/backend/routers/ImageRouter.ts @@ -106,7 +106,7 @@ export default class ImageRouter extends AppRouter { // Clean up request const req = await convert(rawReq, { name: 'string?', visibility: 'number?' }); - req.name ??= new UUID().toString(); + req.name ??= rawFile.name; req.visibility ??= 0; if (req.visibility < 0 || req.visibility > 2) throw new HttpError("body.visibility: Must be 0, 1, or 2"); const id = new UUID(); @@ -150,6 +150,21 @@ export default class ImageRouter extends AppRouter { await this.db.images.updateOne({ _id: id }, { $set: { name: body.name, visibility: body.visibility } }); return ImageRouter.deserialize(img); } + @rest('POST', '/delete') + async delete(@schema('uuid') id: UUID, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) { + const user = await this.db.users.findOne({ username: jwt.name }); + if (!user) throw new HttpError("You don't exist."); + + const img = await this.db.images.findOne({ _id: id }); + if (!img) throw new HttpError("Image doesn't exist."); + if (user.username !== img.author) throw new HttpError("You don't own the image."); + + await this.db.images.deleteOne({ _id: id }); + await this.db.users.updateMany({}, { + $pull: { images: id, likes: id } + }); + return ImageRouter.deserialize(img); + } @rest('POST', '/like') async like(@schema('uuid') id: UUID, @jwt(v => v.salt, true) @auth() jwt: JWTPayload) { diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index b32fdaf..84860c8 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -5,6 +5,7 @@ import { PageLoginComponent } from './page-login/page-login.component'; import { PageSignupComponent } from './page-signup/page-signup.component'; import { PageUploadComponent } from './page-upload/page-upload.component'; import { PageUserComponent } from './page-user/page-user.component'; +import { PageImageComponent } from './page-image/page-image.component'; const routes: Routes = [ { path: '', component: PageHomeComponent }, @@ -12,6 +13,7 @@ const routes: Routes = [ { path: 'signup', component: PageSignupComponent }, { path: 'upload', component: PageUploadComponent }, { path: 'user/:name', component: PageUserComponent }, + { path: 'image/:id', component: PageImageComponent }, ]; @NgModule({ diff --git a/frontend/src/app/app.component.scss b/frontend/src/app/app.component.scss index 9a33dd8..113b9d2 100644 --- a/frontend/src/app/app.component.scss +++ b/frontend/src/app/app.component.scss @@ -39,3 +39,24 @@ overflow: hidden; overflow-y: auto; } + +.overlay { + pointer-events: none; + position: absolute; + top: 0; + right: 0; + z-index: 10000000; + width: 100vw; + + .messages-container { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + align-items: flex-end; + display: inline-block; + overflow: hidden; + max-height: 100vh; + position: absolute; + right: 0; + } +} diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index a38f4d7..f0f3070 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -13,6 +13,7 @@ import { PageUserComponent } from './page-user/page-user.component'; import { ImagesComponent } from './images/images.component'; import { ImageComponent } from './image/image.component'; import { MessageComponent } from './message/message.component'; +import { PageImageComponent } from './page-image/page-image.component'; @NgModule({ declarations: [ @@ -24,7 +25,8 @@ import { MessageComponent } from './message/message.component'; PageUserComponent, ImagesComponent, ImageComponent, - MessageComponent + MessageComponent, + PageImageComponent ], imports: [ BrowserModule, diff --git a/frontend/src/app/image/image.component.html b/frontend/src/app/image/image.component.html index b58676a..1053136 100644 --- a/frontend/src/app/image/image.component.html +++ b/frontend/src/app/image/image.component.html @@ -1,10 +1,13 @@ - -

{{image.name}}

-
- - - - {{image.likes}} - - By {{image.author}} +
+ +

{{image.name}}

+
+ + By {{image.author}} + +
\ No newline at end of file diff --git a/frontend/src/app/image/image.component.scss b/frontend/src/app/image/image.component.scss index 9d8dada..9278663 100644 --- a/frontend/src/app/image/image.component.scss +++ b/frontend/src/app/image/image.component.scss @@ -1,12 +1,5 @@ -:host { +.card { user-select: none; - - .image { - pointer-events: none; - height: 15rem; - display: block; - } - width: min-content; border-radius: 1rem; box-shadow: .25rem .25rem 1rem -.5rem #000; @@ -17,6 +10,12 @@ gap: 1rem; margin: 1rem; + .image { + cursor: pointer; + height: 15rem; + display: block; + } + .name, .author { text-align: left; padding: 0; @@ -27,11 +26,11 @@ display: flex; justify-content: space-between; + .button { + height: 1.5rem; + } .likes { // flex: 1 0 auto; - .like { - height: 1.5rem; - } display: inline-flex; align-items: center; gap: .5rem; diff --git a/frontend/src/app/image/image.component.ts b/frontend/src/app/image/image.component.ts index c5de98c..ccbc804 100644 --- a/frontend/src/app/image/image.component.ts +++ b/frontend/src/app/image/image.component.ts @@ -1,6 +1,8 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, Output } from '@angular/core'; import { environment } from 'src/environments/environment'; import { Image, ImagesService } from '../services/images.service'; +import { MessagesService } from '../services/messages.service'; +import { UsersService } from '../services/users.service'; @Component({ selector: 'app-image', @@ -11,13 +13,31 @@ export class ImageComponent { @Input() public image?: Image | null; public environment = environment; + public deleted = false; public async like() { - this.image = await this.images.like(this.image!.id); + try { + this.image = await this.images.like(this.image!.id); + } + catch (e: any) { this.msgs.error(e); } } public async unlike() { - this.image = await this.images.unlike(this.image!.id); + try { + this.image = await this.images.unlike(this.image!.id); + } + catch (e: any) { this.msgs.error(e); } + } + public async delete() { + try { + await this.images.delete(this.image!.id); + this.deleted = true; + } + catch (e: any) { this.msgs.error(e); } } - public constructor(public images: ImagesService) {} + public constructor( + public images: ImagesService, + public users: UsersService, + private msgs: MessagesService, + ) {} } diff --git a/frontend/src/app/message/message.component.html b/frontend/src/app/message/message.component.html index c8c62a6..f223ed2 100644 --- a/frontend/src/app/message/message.component.html +++ b/frontend/src/app/message/message.component.html @@ -1,4 +1,4 @@ -
+
{{message.content}}
diff --git a/frontend/src/app/message/message.component.scss b/frontend/src/app/message/message.component.scss index 9cb0e32..d7c3749 100644 --- a/frontend/src/app/message/message.component.scss +++ b/frontend/src/app/message/message.component.scss @@ -1,5 +1,13 @@ .container { + width: min-content; + background-color: white; + box-shadow: .1rem .1rem .5rem -.1rem #000; + box-sizing: border-box; + display: inline-flex; + flex-direction: column; + gap: 1rem; + padding: .5rem 1rem; margin: .5rem; border-bottom-right-radius: 0; diff --git a/frontend/src/app/page-image/page-image.component.html b/frontend/src/app/page-image/page-image.component.html new file mode 100644 index 0000000..0592004 --- /dev/null +++ b/frontend/src/app/page-image/page-image.component.html @@ -0,0 +1,16 @@ +
+

{{image.name}}

+ + +
+ + + + + By {{image.author}} +
+
\ No newline at end of file diff --git a/frontend/src/app/page-image/page-image.component.scss b/frontend/src/app/page-image/page-image.component.scss new file mode 100644 index 0000000..aefdcef --- /dev/null +++ b/frontend/src/app/page-image/page-image.component.scss @@ -0,0 +1,40 @@ +.container { + user-select: none; + width: min(100%, 50rem); + padding: 1rem; + box-sizing: border-box; + flex-direction: column; + margin: auto; + + .image { + margin: 1rem auto; + max-height: 30rem; + max-width: 100%; + display: block; + } + + .name, .author { + text-align: left; + padding: 0; + margin: 0; + } + + .stats { + .actions { + display: flex; + gap: 1rem; + .likes { + // flex: 1 0 auto; + display: inline-flex; + align-items: center; + gap: .5rem; + } + .button { + height: 1.5rem; + } + } + + display: flex; + justify-content: space-between; + } +} \ No newline at end of file diff --git a/frontend/src/app/page-image/page-image.component.ts b/frontend/src/app/page-image/page-image.component.ts new file mode 100644 index 0000000..ea80ff5 --- /dev/null +++ b/frontend/src/app/page-image/page-image.component.ts @@ -0,0 +1,53 @@ +import { Component } from '@angular/core'; +import { Image, ImagesService } from '../services/images.service'; +import { ActivatedRoute, Router } from '@angular/router'; +import { UsersService } from '../services/users.service'; +import { ScrollService } from '../services/scroll.service'; +import { MessagesService } from '../services/messages.service'; +import { environment } from 'src/environments/environment'; + +@Component({ + selector: 'app-page-image', + templateUrl: './page-image.component.html', + styleUrls: ['./page-image.component.scss'] +}) +export class PageImageComponent { + public image?: Image | null; + public environment = environment; + + public async like() { + try { + this.image = await this.images.like(this.image!.id); + } + catch (e: any) { this.msgs.error(e); } + } + public async unlike() { + try { + this.image = await this.images.unlike(this.image!.id); + } + catch (e: any) { this.msgs.error(e); } + } + public async delete() { + try { + await this.images.delete(this.image!.id); + this.router.navigateByUrl('/'); + } + catch (e: any) { this.msgs.error(e); } + } + + public constructor( + route: ActivatedRoute, + public users: UsersService, + private images: ImagesService, + private msgs: MessagesService, + private router: Router, + ) { + route.paramMap.subscribe(async v => { + try { + this.image = await images.get(v.get('id')!); + } + catch (e: any) { msgs.error(e); } + // this.imagesEl.next(); + }); + } +} diff --git a/frontend/src/app/services/images.service.ts b/frontend/src/app/services/images.service.ts index 82b4576..1016a80 100644 --- a/frontend/src/app/services/images.service.ts +++ b/frontend/src/app/services/images.service.ts @@ -57,6 +57,9 @@ export class ImagesService { public async change(id: string, proto: CreateImageBody) { return await firstValueFrom(this.http.post(`${this.url}/change?id=${id}`, proto, this.users.httpOptions({}))); } + public async delete(id: string) { + return await firstValueFrom(this.http.post(`${this.url}/delete?id=${id}`, {}, this.users.httpOptions({}))); + } public async like(id: string) { return await firstValueFrom(this.http.post(`${this.url}/like?id=${id}`, {}, this.users.httpOptions({}))); diff --git a/frontend/src/assets/trash.svg b/frontend/src/assets/trash.svg new file mode 100644 index 0000000..4e25d5f --- /dev/null +++ b/frontend/src/assets/trash.svg @@ -0,0 +1 @@ + \ No newline at end of file