diff --git a/angular.json b/angular.json index ee954a0..3e333f4 100644 --- a/angular.json +++ b/angular.json @@ -28,12 +28,12 @@ } ], "styles": [ - "@angular/material/prebuilt-themes/rose-red.css", + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css", - "./node_modules/bootstrap/dist/css/bootstrap.min.css", + "./node_modules/bootstrap/dist/css/bootstrap.css", "./node_modules/bootstrap-icons/font/bootstrap-icons.css" ], - "scripts": ["./node_modules/bootstrap/dist/js/bootstrap.min.js"] + "scripts": ["./node_modules/bootstrap/dist/js/bootstrap.js"] }, "configurations": { "production": { @@ -95,10 +95,12 @@ } ], "styles": [ - "@angular/material/prebuilt-themes/rose-red.css", - "src/styles.css" + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.css", + "./node_modules/bootstrap/dist/css/bootstrap.css", + "./node_modules/bootstrap-icons/font/bootstrap-icons.css" ], - "scripts": [] + "scripts": ["./node_modules/bootstrap/dist/js/bootstrap.js"] } } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index e664c46..e5296d3 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -13,7 +13,7 @@ const routes: Routes = [ ] @NgModule({ - imports: [RouterModule.forRoot(routes)], + imports: [RouterModule.forRoot(routes, { useHash: true })], exports: [RouterModule] }) export class AppRoutingModule { } \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 263b88b..47716c9 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core' +import { NgModule, APP_INITIALIZER } from '@angular/core' import { CommonModule } from '@angular/common' import { BrowserModule } from '@angular/platform-browser' import { FormsModule, ReactiveFormsModule } from '@angular/forms' @@ -14,6 +14,7 @@ import { MatTabsModule } from '@angular/material/tabs' import { MatDialogModule } from "@angular/material/dialog" import { authGuard } from './guards/auth.guard' +import { ConfigService } from './services/config.service' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { LoginComponent } from './components/login/login.component' @@ -22,8 +23,13 @@ import { HomeComponent } from './components/home/home.component' import { NavbarComponent } from './components/navbar/navbar.component' import { FabComponent } from './components/fab/fab.component' import { PublishProyectDialogComponent } from './components/dialogs/publish-proyect-dialog/publish-proyect-dialog.component' +import { MultimediaComponent } from './components/multimedia/multimedia.component' import { provideAnimationsAsync } from '@angular/platform-browser/animations/async' +export function initializeAppFactory(appConfig: ConfigService) { + return () => appConfig.loadEnvironment() +} + @NgModule({ declarations: [ AppComponent, @@ -32,7 +38,8 @@ import { provideAnimationsAsync } from '@angular/platform-browser/animations/asy HomeComponent, NavbarComponent, FabComponent, - PublishProyectDialogComponent + PublishProyectDialogComponent, + MultimediaComponent ], imports: [ CommonModule, @@ -50,7 +57,17 @@ import { provideAnimationsAsync } from '@angular/platform-browser/animations/asy MatTabsModule, MatDialogModule ], - providers: [provideHttpClient(), provideAnimationsAsync()], + providers: [ + ConfigService, + provideHttpClient(), + provideAnimationsAsync(), + { + provide: APP_INITIALIZER, + useFactory: initializeAppFactory, + deps: [ConfigService], + multi: true, + } + ], bootstrap: [AppComponent] }) export class AppModule { } \ No newline at end of file diff --git a/src/app/components/dialogs/publish-proyect-dialog/publish-proyect-dialog.component.html b/src/app/components/dialogs/publish-proyect-dialog/publish-proyect-dialog.component.html index f3acd19..79abd17 100644 --- a/src/app/components/dialogs/publish-proyect-dialog/publish-proyect-dialog.component.html +++ b/src/app/components/dialogs/publish-proyect-dialog/publish-proyect-dialog.component.html @@ -1,6 +1,6 @@

Publicar proyecto

This dialog showcases the title, close, content and actions elements. - + \ No newline at end of file diff --git a/src/app/components/fab/fab.component.css b/src/app/components/fab/fab.component.css index ddbaaa0..131239f 100644 --- a/src/app/components/fab/fab.component.css +++ b/src/app/components/fab/fab.component.css @@ -25,7 +25,7 @@ position: relative; height: 56px; width: 56px; - background-color: #36cf9c; + background-color: #45DFB1; border-radius: 50%; z-index: 2; transition: .4s; @@ -54,7 +54,7 @@ right: 7px; height: 43px; width: 43px; - background-color: rgb(255, 75, 120); + background-color: #F78C6B; border-radius: 50%; transition: all .3s ease; diff --git a/src/app/components/fab/fab.component.ts b/src/app/components/fab/fab.component.ts index 36bec57..8c73827 100644 --- a/src/app/components/fab/fab.component.ts +++ b/src/app/components/fab/fab.component.ts @@ -14,8 +14,10 @@ export class FabComponent implements OnInit { openDialog(): void { const dialogRef = this.dialog.open(PublishProyectDialogComponent, { - height: '70%', - width: '98%' + height: '90%', + width: '60%', + maxWidth: 'none', + disableClose: true }) dialogRef.afterClosed().subscribe(result => { diff --git a/src/app/components/home/home.component.css b/src/app/components/home/home.component.css index c13d135..e69de29 100644 --- a/src/app/components/home/home.component.css +++ b/src/app/components/home/home.component.css @@ -1,37 +0,0 @@ -.mat-tab-group { - margin-bottom: 48px; -} - -.example-header-image { - background-image: url('https://miro.medium.com/v2/resize:fit:1358/0*NwkeBmXVnnEoVcgj'); - background-size: cover; -} - -:host ::ng-deep .mat-mdc-card-avatar { - height: 40px !important; - width: 40px !important; - border-radius: 50% !important; - flex-shrink: 0 !important; - margin-bottom: 0px !important; - object-fit: cover !important; -} - -:host ::ng-deep .mat-mdc-card-title, -.mat-mdc-card-subtitle { - display: block !important; - margin-top: 5px !important; - margin-left: 0 !important; - margin-bottom: 0 !important; - margin-right: 0 !important; -} - -.scale-fit-image { - height: 360px; - overflow: hidden; - resize: both; - object-fit: cover; - position: relative; - display: block; - left: 70px; - right: 70px; -} \ No newline at end of file diff --git a/src/app/components/home/home.component.html b/src/app/components/home/home.component.html index c140651..252f9db 100644 --- a/src/app/components/home/home.component.html +++ b/src/app/components/home/home.component.html @@ -5,123 +5,31 @@ - +
-
-
- - -
- Alyx_450 -
- - - -

- {{longText}} -

-
- - - - - - - - -
-
-
- - -
- Karol_300 -
- ... - -

- {{longText}} -

-
- - - - - - - - -
-
-
+
- + - Content 2 + +
+ Sala de Chat +
- + - Content 3 + +
+ GitHub +
diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index b744da8..6d7a2e3 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -1,12 +1,12 @@ -import { Component } from '@angular/core' +import { Component, OnInit } from '@angular/core' @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrl: './home.component.css' }) -export class HomeComponent { - longText = `The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog - from Japan. A small, agile dog that copes very well with mountainous terrain, the Shiba Inu was - originally bred for hunting.` +export class HomeComponent implements OnInit { + constructor() { } + + ngOnInit(): void { } } diff --git a/src/app/components/login/login.component.html b/src/app/components/login/login.component.html index 46b4f69..f48dd43 100644 --- a/src/app/components/login/login.component.html +++ b/src/app/components/login/login.component.html @@ -14,7 +14,7 @@

Sign In to Brain

-
+
{ - Swal.fire({ - title: "Usuario autenticado", - text: "Welcome " + this.username, - icon: "success" - }); this.router.navigate(['/home']) console.log("Token de acceso : " + this.authService.getToken()) }, @@ -34,7 +29,7 @@ export class LoginComponent implements OnInit { title: "Credenciales inválidas", text: "Verifique sus credenciales", icon: "error" - }); + }) this.error = 'Credenciales inválidas' } }) diff --git a/src/app/components/multimedia/multimedia.component.css b/src/app/components/multimedia/multimedia.component.css new file mode 100644 index 0000000..53dcb8b --- /dev/null +++ b/src/app/components/multimedia/multimedia.component.css @@ -0,0 +1,90 @@ +.mat-tab-group { + margin-bottom: 48px; +} + +.custom-card { + width: 560px; +} + +.avatar-header-image { + background-image: url('https://miro.medium.com/v2/resize:fit:1358/0*NwkeBmXVnnEoVcgj'); + background-size: cover; +} + +:host ::ng-deep .mat-mdc-card-avatar { + height: 40px !important; + width: 40px !important; + border-radius: 50% !important; + flex-shrink: 0 !important; + margin-bottom: 0px !important; + object-fit: cover !important; +} + +:host ::ng-deep .mat-mdc-card-title, +.mat-mdc-card-subtitle { + display: block !important; + margin-top: 5px !important; + margin-left: 0 !important; + margin-bottom: 0 !important; + margin-right: 0 !important; +} + +.scale-fit-image { + height: 360px; + overflow: hidden; + resize: both; + object-fit: cover; + position: relative; + display: block; + left: 70px; + right: 70px; +} + +.carousel-control-next, +.carousel-control-prev { + position: absolute !important; + top: 135px !important; + bottom: 135px !important; + z-index: 1 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + width: 13% !important; + padding: 0 !important; + color: #fff !important; + text-align: center !important; + background: 0 0 !important; + border: 0 !important; + opacity: .5 !important; + transition: opacity .15s ease !important; +} + +.custom-video { + border-radius: 0.2em; + background-color: #080808; + cursor: pointer; +} + +.custom-video__control { + position: absolute; + top: 43%; + left: 46%; + background-color: rgba(0, 0, 0, 0.5); + border-radius: 50%; + padding: 1em; + display: flex; + justify-content: center; + align-items: center; + color: #ffffff; + font-size: 1em; + font-weight: 400; + width: 3em; + height: 3em; + white-space: nowrap; + line-height: 0; +} + +video::-webkit-media-controls { + position: relative; + z-index: 1; +} \ No newline at end of file diff --git a/src/app/components/multimedia/multimedia.component.html b/src/app/components/multimedia/multimedia.component.html new file mode 100644 index 0000000..8f479de --- /dev/null +++ b/src/app/components/multimedia/multimedia.component.html @@ -0,0 +1,138 @@ +
+ @if (loadIsFull) { + @for (item of this.arrMediaDetail; track $index) { + @if (item.array == 1) { + @for (content of item.content; track $index) { +
+ + +
+ {{userNameAvatar}} +
+ + @if (content.contentType == 'video/mp4') { + +
+ } @else if (content.contentType == 'image/jpg' || content.contentType == 'image/jpeg' || content.contentType == 'image/gif' || content.contentType == 'image/png') { + single item + } + +

{{item.overview}}

+
+ + + + + + + + +
+
+ } + } @else if (item.array > 1) { +
+ + +
+ {{userNameAvatar}} +
+ + + + +

{{item.overview}}

+
+ + + + + + + + +
+
+ } + } + } @else { +

Not found Items

+ } +
\ No newline at end of file diff --git a/src/app/components/multimedia/multimedia.component.ts b/src/app/components/multimedia/multimedia.component.ts new file mode 100644 index 0000000..60b1586 --- /dev/null +++ b/src/app/components/multimedia/multimedia.component.ts @@ -0,0 +1,125 @@ +import { AfterViewInit, Component, HostListener, OnInit } from '@angular/core' +import { PublishedMultimediaService } from '../../services/published.multimedia.service' +import { MediaResponse } from '../../models/media-response' +import { MediaDetail } from '../../models/media-detail' +import { MediaContent } from '../../models/media-content' +import { ConfigService } from '../../services/config.service' + +@Component({ + selector: 'app-multimedia', + templateUrl: './multimedia.component.html', + styleUrl: './multimedia.component.css' +}) +export class MultimediaComponent implements OnInit, AfterViewInit { + mediaResponse?: MediaResponse + arrMediaDetail?: Array + mediaDetail?: MediaDetail + arrMediaContent?: Array + + projectOverview?: string + publishDate?: string + userNameAvatar?: string + urlMedia: string = '' + media?: HTMLVideoElement + + // This is what I’ve found to be the best ratio to start & stop videos at from a User Experience perspective. + proportion: Number = 0.65 + + loadIsFull: boolean = false + + constructor(private publishedMultimediaService: PublishedMultimediaService, private configService: ConfigService) { + this.urlMedia = this.configService.properties.getUrlMultimedia + } + + ngOnInit(): void { + this.getAllPost(555111, 0, 20) + } + + ngAfterViewInit(): void { } + + getAllPost(userId: number, page: number, size: number): void { + this.cleaner() + + this.publishedMultimediaService.getAllPost(userId, page, size).subscribe(response => { + this.mediaResponse = response + + if (this.mediaResponse?.result == 'OK') { + this.arrMediaDetail = this.mediaResponse?.data?.post + this.userNameAvatar = this.mediaResponse?.data?.userName + + this.loadIsFull = true + } + }) + } + + onPlay(mediaId: any): void { + this.media = document.getElementById(mediaId) as HTMLVideoElement + const controls: Element | null = this.media.nextElementSibling + + if (this.media.paused) { + controls!.innerHTML = "| |" + this.media.play() + } else { + controls!.innerHTML = "▶" + this.media.pause() + } + } + + onMouseLeave(mediaId: any): void { + this.media = document.getElementById(mediaId) as HTMLVideoElement + + if (!this.media.paused) { + const controls = this.media.nextElementSibling + controls?.setAttribute('style', 'display: none;') + } + } + + onMouseEnter(mediaId: any): void { + this.media = document.getElementById(mediaId) as HTMLVideoElement + + const controls = this.media.nextElementSibling + controls?.removeAttribute('style') + } + + onEnded(mediaId: any): void { + this.media = document.getElementById(mediaId) as HTMLVideoElement + + const controls = this.media.nextElementSibling + controls?.removeAttribute('style') + controls!.innerHTML = "▶" + } + + openMedia(mediaId: any) { + console.log('open modal for media: ' + mediaId) + } + + @HostListener('window:scroll') + onWindowScroll() { + const callback = (entries: any, observer: any) => { + entries.forEach((entry: any) => { + const ratio = entry.intersectionRatio + + if (ratio >= this.proportion) { + entry.target.play() + } else { + entry.target.pause() + } + + observer.unobserve(entry.target) + }) + } + + const observer = new IntersectionObserver(callback) + document.querySelectorAll('video').forEach(mediaItem => { + observer.observe(mediaItem) + }) + } + + private cleaner(): void { + this.mediaResponse = new MediaResponse + this.arrMediaDetail = [] + this.mediaDetail = new MediaDetail + this.arrMediaContent = [] + this.userNameAvatar = '' + } +} \ No newline at end of file diff --git a/src/app/components/navbar/navbar.component.css b/src/app/components/navbar/navbar.component.css index 65ecc8f..4990ae3 100644 --- a/src/app/components/navbar/navbar.component.css +++ b/src/app/components/navbar/navbar.component.css @@ -3,13 +3,14 @@ } .mat-toolbar { - background: #36cf9c; + background: #45DFB1; color: #fff } .profile-header-avatar { background-image: url('https://play-lh.googleusercontent.com/hJGHtbYSQ0nCnoEsK6AGojonjELeAh_Huxg361mVrPmzdwm8Ots-JzEH5488IS2nojI'); background-size: cover; + margin-bottom: 0px !important; } .brand-header-title { diff --git a/src/app/models/backdrop.spec.ts b/src/app/models/backdrop.spec.ts deleted file mode 100644 index 2da35f6..0000000 --- a/src/app/models/backdrop.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Backdrop } from './backdrop'; - -describe('Backdrop', () => { - it('should create an instance', () => { - expect(new Backdrop()).toBeTruthy(); - }); -}); diff --git a/src/app/models/backdrop.ts b/src/app/models/backdrop.ts deleted file mode 100644 index 17f34b9..0000000 --- a/src/app/models/backdrop.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Backdrop { - id?: number -} diff --git a/src/app/models/media-content.ts b/src/app/models/media-content.ts new file mode 100644 index 0000000..c0e7063 --- /dev/null +++ b/src/app/models/media-content.ts @@ -0,0 +1,9 @@ +export class MediaContent { + _id?: string + collectionId?: number + contentType?: string + multimediaName?: string + share?: number + likes?: number + comments?: Array +} \ No newline at end of file diff --git a/src/app/models/media-detail.ts b/src/app/models/media-detail.ts new file mode 100644 index 0000000..190e39c --- /dev/null +++ b/src/app/models/media-detail.ts @@ -0,0 +1,9 @@ +import { MediaContent } from "./media-content" + +export class MediaDetail { + collectionId?: number + postDate?: string + overview?: string + array: number = 0 + content?: Array +} diff --git a/src/app/models/media-response.ts b/src/app/models/media-response.ts new file mode 100644 index 0000000..fba711d --- /dev/null +++ b/src/app/models/media-response.ts @@ -0,0 +1,6 @@ +import { Result } from "./result" + +export class MediaResponse { + result?: string + data?: Result +} \ No newline at end of file diff --git a/src/app/models/media-types.ts b/src/app/models/media-types.ts new file mode 100644 index 0000000..916e24f --- /dev/null +++ b/src/app/models/media-types.ts @@ -0,0 +1,6 @@ +export class MediaTypes { + jpg: string = 'image/jpg' + png: string = 'image/png' + video: string = 'video/mp4' + audio: string = 'audio/mp3' +} \ No newline at end of file diff --git a/src/app/models/result.ts b/src/app/models/result.ts new file mode 100644 index 0000000..b2cf4a5 --- /dev/null +++ b/src/app/models/result.ts @@ -0,0 +1,11 @@ +import { MediaDetail } from "./media-detail" + +export class Result { + userId?: number + userName?: string + fullName?: string + email?: string + backdropName?: string + countContacts?: number + post?: Array +} \ No newline at end of file diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index 8ab12f8..d989f86 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -3,20 +3,20 @@ import { Injectable } from '@angular/core' import { Router } from '@angular/router' import { catchError, map, Observable, tap, throwError } from 'rxjs' +import { ConfigService } from './config.service' + @Injectable({ providedIn: 'root' }) export class AuthService { - private baseUrl = 'http://localhost:8082/api' - - constructor(private http?: HttpClient, private router?: Router) { } + constructor(private http?: HttpClient, private router?: Router, private configService?: ConfigService) { } login(username: string, password: string): Observable { if (!this.http) { throw new Error('HttpClient is not initialized') } - return this.http?.post(`${this.baseUrl}/auth/login`, { username, password }) + return this.http?.post(this.configService?.properties.login, { username, password }) ?.pipe( tap(response => { console.log('response server: ', response) @@ -40,7 +40,7 @@ export class AuthService { if (!this.http) { throw new Error('HttpClient is not initialized') } - return this.http?.post(`${this.baseUrl}/auth/signup`, { username, password }) + return this.http?.post(this.configService?.properties.signup, { username, password }) ?.pipe( catchError(this.handleError) ) diff --git a/src/app/services/backdrop.service.ts b/src/app/services/backdrop.service.ts index 30e7215..7d8e1cb 100644 --- a/src/app/services/backdrop.service.ts +++ b/src/app/services/backdrop.service.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs'; }) export class BackdropService { - private baseUrl: string = 'http://192.168.1.121:8081/api/backProfile' + private baseUrl: string = 'only test' constructor(private http: HttpClient) { } diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts new file mode 100644 index 0000000..1a86066 --- /dev/null +++ b/src/app/services/config.service.ts @@ -0,0 +1,37 @@ +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, catchError, throwError } from 'rxjs'; +import { environment } from '../../environments/environment.development' + +@Injectable({ + providedIn: 'root' +}) +export class ConfigService { + public properties: any + + constructor(private http: HttpClient) { } + + public loadEnvironment() { + return new Promise((resolve, reject) => { + this.http.get(environment.baseUrl) + .pipe( + catchError(this.handleError) + ).subscribe((data) => { + this.properties = data + resolve(true) + }) + }) + } + + private handleError(error: HttpErrorResponse) { + if (error.error instanceof ErrorEvent) { + console.error('An error occurred:', error.error.message) + } else { + console.error( + `Backend returned code ${error.status}, ` + + `body was: ${error.error}` + ); + } + return throwError(() => error) + } +} \ No newline at end of file diff --git a/src/app/services/published.multimedia.service.ts b/src/app/services/published.multimedia.service.ts new file mode 100644 index 0000000..d8b6f36 --- /dev/null +++ b/src/app/services/published.multimedia.service.ts @@ -0,0 +1,38 @@ +import { HttpClient } from '@angular/common/http' +import { Injectable } from '@angular/core' +import { catchError, Observable, of, map, tap } from 'rxjs' + +import { ConfigService } from './config.service' + +@Injectable({ + providedIn: 'root' +}) +export class PublishedMultimediaService { + + constructor(private http: HttpClient, private configService: ConfigService) { } + + getAllPost(userId: number, page: number, size: number): Observable { + const url = `${this.configService.properties.getAllPost}?userId=${userId}&page=${page}&size=${size}` + return this.http.get(url) + .pipe( + tap(_ => this.log(`fetched getAllPost()`)), + catchError(this.handleError(`getAllPost id=${userId}`)) + ) + } + + private handleError(operation = 'operation', result?: T) { + return (error: any): Observable => { + // TODO: send the error to remote logging infrastructure + console.error(error) + // TODO: better job of transforming error for user consumption + this.log(`${operation} failed: ${error.message}`) + + // Let the app keep running by returning an empty result + return of(result as T) + } + } + + private log(message: string) { + console.log('PublishedMultimediaService: ' + message) + } +} \ No newline at end of file diff --git a/src/assets/config.json b/src/assets/config.json new file mode 100644 index 0000000..b648c47 --- /dev/null +++ b/src/assets/config.json @@ -0,0 +1,6 @@ +{ + "login": "http://localhost:8082/api/auth/login", + "signup": "http://localhost:8082/api/auth/signup", + "getAllPost": "http://localhost:8081/api/allPost", + "getUrlMultimedia": "http://localhost:8081/api/multimedia/" +} \ No newline at end of file diff --git a/src/environments/environment.development.ts b/src/environments/environment.development.ts index 1faa93d..3526854 100644 --- a/src/environments/environment.development.ts +++ b/src/environments/environment.development.ts @@ -1,4 +1,4 @@ export const environment = { production: false, - baseUrl: 'http://localhost:8082/api' + baseUrl: '../assets/config.json' } diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 5de0309..c1ed40a 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -1,4 +1,4 @@ export const environment = { production: true, - baseUrl: 'http://www.brain.com/api' + baseUrl: 'brain/assets/config.json' } diff --git a/tsconfig.json b/tsconfig.json index d3dbc4a..cdb56a7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "noFallthroughCasesInSwitch": true, "skipLibCheck": true, "esModuleInterop": true, - "sourceMap": true, + "sourceMap": false, "declaration": false, "experimentalDecorators": true, "moduleResolution": "bundler",