chore: update Angular cache and TypeScript build info
This commit is contained in:
parent
f7b941fcaa
commit
7cb75e2d3b
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
2
.angular/cache/20.2.2/app/.tsbuildinfo
vendored
File diff suppressed because one or more lines are too long
@ -100,8 +100,8 @@ import {
|
|||||||
DOCUMENT,
|
DOCUMENT,
|
||||||
IMAGE_CONFIG
|
IMAGE_CONFIG
|
||||||
} from "./chunk-FVA7C6JK.js";
|
} from "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import "./chunk-HWYXSU2G.js";
|
import "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import "./chunk-MARUHEWW.js";
|
import "./chunk-MARUHEWW.js";
|
||||||
import "./chunk-GOMI4DH3.js";
|
import "./chunk-GOMI4DH3.js";
|
||||||
export {
|
export {
|
||||||
|
@ -41,8 +41,8 @@ import {
|
|||||||
} from "./chunk-5DRVFSXL.js";
|
} from "./chunk-5DRVFSXL.js";
|
||||||
import "./chunk-OUSM42MY.js";
|
import "./chunk-OUSM42MY.js";
|
||||||
import "./chunk-FVA7C6JK.js";
|
import "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import "./chunk-HWYXSU2G.js";
|
import "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import "./chunk-MARUHEWW.js";
|
import "./chunk-MARUHEWW.js";
|
||||||
import "./chunk-GOMI4DH3.js";
|
import "./chunk-GOMI4DH3.js";
|
||||||
export {
|
export {
|
||||||
|
@ -498,8 +498,8 @@ import {
|
|||||||
ɵɵviewQuery,
|
ɵɵviewQuery,
|
||||||
ɵɵviewQuerySignal
|
ɵɵviewQuerySignal
|
||||||
} from "./chunk-FVA7C6JK.js";
|
} from "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import "./chunk-HWYXSU2G.js";
|
import "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import "./chunk-MARUHEWW.js";
|
import "./chunk-MARUHEWW.js";
|
||||||
import "./chunk-GOMI4DH3.js";
|
import "./chunk-GOMI4DH3.js";
|
||||||
export {
|
export {
|
||||||
|
@ -46,10 +46,10 @@ import {
|
|||||||
ɵɵgetInheritedFactory,
|
ɵɵgetInheritedFactory,
|
||||||
ɵɵlistener
|
ɵɵlistener
|
||||||
} from "./chunk-FVA7C6JK.js";
|
} from "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import {
|
import {
|
||||||
forkJoin
|
forkJoin
|
||||||
} from "./chunk-HWYXSU2G.js";
|
} from "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import {
|
import {
|
||||||
Subject,
|
Subject,
|
||||||
from,
|
from,
|
||||||
|
@ -41,8 +41,8 @@ import {
|
|||||||
} from "./chunk-H4LQPAO2.js";
|
} from "./chunk-H4LQPAO2.js";
|
||||||
import "./chunk-OUSM42MY.js";
|
import "./chunk-OUSM42MY.js";
|
||||||
import "./chunk-FVA7C6JK.js";
|
import "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import "./chunk-HWYXSU2G.js";
|
import "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import "./chunk-MARUHEWW.js";
|
import "./chunk-MARUHEWW.js";
|
||||||
import "./chunk-GOMI4DH3.js";
|
import "./chunk-GOMI4DH3.js";
|
||||||
export {
|
export {
|
||||||
|
@ -83,11 +83,11 @@ import {
|
|||||||
ɵɵqueryRefresh,
|
ɵɵqueryRefresh,
|
||||||
ɵɵsanitizeUrlOrResourceUrl
|
ɵɵsanitizeUrlOrResourceUrl
|
||||||
} from "./chunk-FVA7C6JK.js";
|
} from "./chunk-FVA7C6JK.js";
|
||||||
import "./chunk-JRFR6BLO.js";
|
|
||||||
import {
|
import {
|
||||||
defer,
|
defer,
|
||||||
isObservable
|
isObservable
|
||||||
} from "./chunk-HWYXSU2G.js";
|
} from "./chunk-HWYXSU2G.js";
|
||||||
|
import "./chunk-JRFR6BLO.js";
|
||||||
import {
|
import {
|
||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
ConnectableObservable,
|
ConnectableObservable,
|
||||||
|
@ -1,61 +1,61 @@
|
|||||||
{
|
{
|
||||||
"hash": "4a15fadf",
|
"hash": "3cecccc1",
|
||||||
"configHash": "d859ec53",
|
"configHash": "d859ec53",
|
||||||
"lockfileHash": "c86d7ad1",
|
"lockfileHash": "80653b50",
|
||||||
"browserHash": "3b79ea76",
|
"browserHash": "cc6248f8",
|
||||||
"optimized": {
|
"optimized": {
|
||||||
"@angular/common": {
|
"@angular/common": {
|
||||||
"src": "../../../../../../node_modules/@angular/common/fesm2022/common.mjs",
|
"src": "../../../../../../node_modules/@angular/common/fesm2022/common.mjs",
|
||||||
"file": "@angular_common.js",
|
"file": "@angular_common.js",
|
||||||
"fileHash": "3cdf8c58",
|
"fileHash": "c6e29582",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@angular/common/http": {
|
"@angular/common/http": {
|
||||||
"src": "../../../../../../node_modules/@angular/common/fesm2022/http.mjs",
|
"src": "../../../../../../node_modules/@angular/common/fesm2022/http.mjs",
|
||||||
"file": "@angular_common_http.js",
|
"file": "@angular_common_http.js",
|
||||||
"fileHash": "c729a905",
|
"fileHash": "3a2fb905",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@angular/core": {
|
"@angular/core": {
|
||||||
"src": "../../../../../../node_modules/@angular/core/fesm2022/core.mjs",
|
"src": "../../../../../../node_modules/@angular/core/fesm2022/core.mjs",
|
||||||
"file": "@angular_core.js",
|
"file": "@angular_core.js",
|
||||||
"fileHash": "262b482a",
|
"fileHash": "236bf4f0",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@angular/forms": {
|
"@angular/forms": {
|
||||||
"src": "../../../../../../node_modules/@angular/forms/fesm2022/forms.mjs",
|
"src": "../../../../../../node_modules/@angular/forms/fesm2022/forms.mjs",
|
||||||
"file": "@angular_forms.js",
|
"file": "@angular_forms.js",
|
||||||
"fileHash": "08308e50",
|
"fileHash": "910649fe",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@angular/platform-browser": {
|
"@angular/platform-browser": {
|
||||||
"src": "../../../../../../node_modules/@angular/platform-browser/fesm2022/platform-browser.mjs",
|
"src": "../../../../../../node_modules/@angular/platform-browser/fesm2022/platform-browser.mjs",
|
||||||
"file": "@angular_platform-browser.js",
|
"file": "@angular_platform-browser.js",
|
||||||
"fileHash": "f61025b6",
|
"fileHash": "669d4d15",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@angular/router": {
|
"@angular/router": {
|
||||||
"src": "../../../../../../node_modules/@angular/router/fesm2022/router.mjs",
|
"src": "../../../../../../node_modules/@angular/router/fesm2022/router.mjs",
|
||||||
"file": "@angular_router.js",
|
"file": "@angular_router.js",
|
||||||
"fileHash": "f2b90ce6",
|
"fileHash": "697a6c94",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"@google/genai": {
|
"@google/genai": {
|
||||||
"src": "../../../../../../node_modules/@google/genai/dist/web/index.mjs",
|
"src": "../../../../../../node_modules/@google/genai/dist/web/index.mjs",
|
||||||
"file": "@google_genai.js",
|
"file": "@google_genai.js",
|
||||||
"fileHash": "3476d74f",
|
"fileHash": "3ef22c22",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"rxjs": {
|
"rxjs": {
|
||||||
"src": "../../../../../../node_modules/rxjs/dist/esm5/index.js",
|
"src": "../../../../../../node_modules/rxjs/dist/esm5/index.js",
|
||||||
"file": "rxjs.js",
|
"file": "rxjs.js",
|
||||||
"fileHash": "df88f612",
|
"fileHash": "9c55814f",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
},
|
},
|
||||||
"rxjs/operators": {
|
"rxjs/operators": {
|
||||||
"src": "../../../../../../node_modules/rxjs/dist/esm5/operators/index.js",
|
"src": "../../../../../../node_modules/rxjs/dist/esm5/operators/index.js",
|
||||||
"file": "rxjs_operators.js",
|
"file": "rxjs_operators.js",
|
||||||
"fileHash": "8d5942d8",
|
"fileHash": "4e738648",
|
||||||
"needsInterop": false
|
"needsInterop": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -75,12 +75,12 @@
|
|||||||
"chunk-FVA7C6JK": {
|
"chunk-FVA7C6JK": {
|
||||||
"file": "chunk-FVA7C6JK.js"
|
"file": "chunk-FVA7C6JK.js"
|
||||||
},
|
},
|
||||||
"chunk-JRFR6BLO": {
|
|
||||||
"file": "chunk-JRFR6BLO.js"
|
|
||||||
},
|
|
||||||
"chunk-HWYXSU2G": {
|
"chunk-HWYXSU2G": {
|
||||||
"file": "chunk-HWYXSU2G.js"
|
"file": "chunk-HWYXSU2G.js"
|
||||||
},
|
},
|
||||||
|
"chunk-JRFR6BLO": {
|
||||||
|
"file": "chunk-JRFR6BLO.js"
|
||||||
|
},
|
||||||
"chunk-MARUHEWW": {
|
"chunk-MARUHEWW": {
|
||||||
"file": "chunk-MARUHEWW.js"
|
"file": "chunk-MARUHEWW.js"
|
||||||
},
|
},
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"hash": "65ebd7bc",
|
"hash": "75f2f65a",
|
||||||
"configHash": "3d00a7fd",
|
"configHash": "3d00a7fd",
|
||||||
"lockfileHash": "c86d7ad1",
|
"lockfileHash": "80653b50",
|
||||||
"browserHash": "acdb5c3c",
|
"browserHash": "f89b65f9",
|
||||||
"optimized": {},
|
"optimized": {},
|
||||||
"chunks": {}
|
"chunks": {}
|
||||||
}
|
}
|
0
app/shared/components/like-button/like-button.css
Normal file
0
app/shared/components/like-button/like-button.css
Normal file
16
app/shared/components/like-button/like-button.ts
Normal file
16
app/shared/components/like-button/like-button.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-like-button',
|
||||||
|
imports: [],
|
||||||
|
template: `
|
||||||
|
<p>
|
||||||
|
like-button works!
|
||||||
|
</p>
|
||||||
|
`,
|
||||||
|
styleUrl: './like-button.css',
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class LikeButton {
|
||||||
|
|
||||||
|
}
|
BIN
db/newtube.db
BIN
db/newtube.db
Binary file not shown.
@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>NewTube</title>
|
<title>NewTube</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/png" href="src/assets/images/NewTube.png">
|
||||||
<script>
|
<script>
|
||||||
try {
|
try {
|
||||||
var t = localStorage.getItem('newtube.theme') || 'system';
|
var t = localStorage.getItem('newtube.theme') || 'system';
|
||||||
|
BIN
src/assets/images/NewTube.png
Normal file
BIN
src/assets/images/NewTube.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
BIN
src/assets/images/NewTube_1024.png
Normal file
BIN
src/assets/images/NewTube_1024.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 MiB |
BIN
src/assets/images/NewTube_192.png
Normal file
BIN
src/assets/images/NewTube_192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
src/assets/images/NewTube_500.png
Normal file
BIN
src/assets/images/NewTube_500.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 109 KiB |
@ -9,9 +9,7 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<a routerLink="/" class="flex items-center space-x-2 shrink-0">
|
<a routerLink="/" class="flex items-center space-x-2 shrink-0">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-red-500" viewBox="0 0 24 24" fill="currentColor">
|
<img src="assets/images/NewTube.png" alt="" class="h-8 w-auto" />
|
||||||
<path d="M10,15.6l5.3-3.3L10,9V15.6z M21.8,8.1c-0.2-0.8-0.9-1.4-1.7-1.7C18.2,6,12,6,12,6s-6.2,0-8.1,0.5 c-0.8,0.2-1.4,0.9-1.7,1.7C2,9.9,2,12,2,12s0,2.1,0.5,3.9c0.2,0.8,0.9,1.4,1.7,1.7C6.2,18,12,18,12,18s6.2,0,8.1-0.5 c0.8-0.2,1.4-0.9,1.7-1.7c0.5-1.8,0.5-3.9,0.5-3.9S22.2,9.9,21.8,8.1z"/>
|
|
||||||
</svg>
|
|
||||||
<h1 class="text-2xl font-bold tracking-tight text-white">NewTube</h1>
|
<h1 class="text-2xl font-bold tracking-tight text-white">NewTube</h1>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,9 +27,14 @@
|
|||||||
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
||||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="video.videoId" [provider]="'youtube'"></app-like-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 flex-grow flex flex-col">
|
<div class="p-4 flex-grow flex flex-col">
|
||||||
<h3 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h3>
|
<div class="flex justify-between items-start">
|
||||||
|
<h3 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2 pr-2">{{ video.title }}</h3>
|
||||||
|
</div>
|
||||||
<div class="mt-2 flex items-center space-x-3 text-sm text-slate-400">
|
<div class="mt-2 flex items-center space-x-3 text-sm text-slate-400">
|
||||||
<img [src]="video.uploaderAvatar" [alt]="video.uploaderName" class="w-8 h-8 rounded-full">
|
<img [src]="video.uploaderAvatar" [alt]="video.uploaderName" class="w-8 h-8 rounded-full">
|
||||||
<span>{{ video.uploaderName }}</span>
|
<span>{{ video.uploaderName }}</span>
|
||||||
|
@ -8,13 +8,20 @@ import { InstanceService } from '../../services/instance.service';
|
|||||||
import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component';
|
import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component';
|
||||||
import { formatRelativeFr } from '../../utils/date.util';
|
import { formatRelativeFr } from '../../utils/date.util';
|
||||||
import { TranslatePipe } from '../../pipes/translate.pipe';
|
import { TranslatePipe } from '../../pipes/translate.pipe';
|
||||||
|
import { LikeButtonComponent } from '../shared/components/like-button/like-button.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [CommonModule, RouterLink, InfiniteAnchorComponent, TranslatePipe]
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterLink,
|
||||||
|
InfiniteAnchorComponent,
|
||||||
|
TranslatePipe,
|
||||||
|
LikeButtonComponent
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent {
|
||||||
private apiService = inject(YoutubeApiService);
|
private apiService = inject(YoutubeApiService);
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
<div class="absolute top-2 left-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
<div class="absolute top-2 left-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
||||||
{{ formatViews(video.views) }} en direct
|
{{ formatViews(video.views) }} en direct
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="video.videoId" [provider]="'twitch'"></app-like-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 flex-grow flex flex-col">
|
<div class="p-4 flex-grow flex flex-col">
|
||||||
<h4 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h4>
|
<h4 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h4>
|
||||||
@ -87,6 +90,9 @@
|
|||||||
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
||||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="video.videoId" [provider]="'twitch'"></app-like-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 flex-grow flex flex-col">
|
<div class="p-4 flex-grow flex flex-col">
|
||||||
<h4 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h4>
|
<h4 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h4>
|
||||||
@ -120,6 +126,9 @@
|
|||||||
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
<div class="absolute bottom-2 right-2 bg-black/75 text-white text-xs px-2 py-1 rounded">
|
||||||
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
{{ video.duration / 60 | number:'1.0-0' }}:{{ (video.duration % 60) | number:'2.0-0' }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="video.videoId" [provider]="selectedProviderForView()"></app-like-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 flex-grow flex flex-col">
|
<div class="p-4 flex-grow flex flex-col">
|
||||||
<h3 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h3>
|
<h3 class="font-semibold text-slate-100 group-hover:text-red-400 transition-colors duration-200 line-clamp-2">{{ video.title }}</h3>
|
||||||
|
@ -9,13 +9,14 @@ import { InstanceService, Provider } from '../../services/instance.service';
|
|||||||
import { HistoryService } from '../../services/history.service';
|
import { HistoryService } from '../../services/history.service';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { TranslatePipe } from '../../pipes/translate.pipe';
|
import { TranslatePipe } from '../../pipes/translate.pipe';
|
||||||
|
import { LikeButtonComponent } from '../shared/components/like-button/like-button.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-search',
|
selector: 'app-search',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl: './search.component.html',
|
templateUrl: './search.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [CommonModule, RouterLink, InfiniteAnchorComponent, TranslatePipe]
|
imports: [CommonModule, RouterLink, InfiniteAnchorComponent, TranslatePipe, LikeButtonComponent]
|
||||||
})
|
})
|
||||||
export class SearchComponent {
|
export class SearchComponent {
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
|
@ -0,0 +1,225 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, inject, signal } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { LikesService } from '../../../../services/likes.service';
|
||||||
|
import { finalize } from 'rxjs';
|
||||||
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
import { AuthService } from '../../../../services/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-like-button',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule, HttpClientModule],
|
||||||
|
// Le service est déjà fourni via providedIn: 'root' dans le service
|
||||||
|
template: `
|
||||||
|
<button
|
||||||
|
(click)="onClick($event)"
|
||||||
|
[class.text-red-500]="isLiked()"
|
||||||
|
[class.text-slate-400]="!isLiked()"
|
||||||
|
[class.hover\:text-red-500]="!isLiked()"
|
||||||
|
[disabled]="isLoading() || !isAuthenticated()"
|
||||||
|
class="transition-colors duration-200 p-1 rounded-full hover:bg-slate-700/50 flex items-center justify-center"
|
||||||
|
[attr.aria-label]="!isAuthenticated() ? 'Connectez-vous pour aimer' : (isLiked() ? 'Retirer des vidéos aimées' : 'Ajouter aux vidéos aimées')"
|
||||||
|
[attr.aria-pressed]="isLiked()"
|
||||||
|
>
|
||||||
|
@if (isLoading()) {
|
||||||
|
<svg class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Chargement...</span>
|
||||||
|
} @else {
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="currentColor"
|
||||||
|
[attr.aria-hidden]="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
[attr.d]="isLiked() ? 'M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z' : 'M16.5 3c-1.74 0-3.41.81-4.5 2.09C10.91 3.81 9.24 3 7.5 3 4.42 3 2 5.42 2 8.5c0 3.78 3.4 6.86 8.55 11.54L12 21.35l1.45-1.32C18.6 15.36 22 12.28 22 8.5 22 5.42 19.58 3 16.5 3zm-4.4 15.55l-.1.1-.1-.1C7.14 14.24 4 11.39 4 8.5 4 6.5 5.5 5 7.5 5c1.54 0 3.04.99 3.57 2.36h1.87C13.46 5.99 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5 0 2.89-3.14 5.74-7.9 10.05z'"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">{{ isLiked() ? 'Retirer des vidéos aimées' : 'Ajouter aux vidéos aimées' }}</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
`,
|
||||||
|
styles: [
|
||||||
|
`
|
||||||
|
:host {
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:not(:disabled):hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class LikeButtonComponent implements OnInit, OnDestroy, OnChanges {
|
||||||
|
private likesService = inject(LikesService);
|
||||||
|
private auth = inject(AuthService);
|
||||||
|
|
||||||
|
@Input() provider = 'youtube';
|
||||||
|
|
||||||
|
private _videoId = '';
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set videoId(value: string) {
|
||||||
|
if (this._videoId !== value) {
|
||||||
|
this._videoId = value;
|
||||||
|
this.checkLikeStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get videoId(): string {
|
||||||
|
return this._videoId;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLiked = signal<boolean>(false);
|
||||||
|
isLoading = signal<boolean>(true);
|
||||||
|
isAuthenticated = signal<boolean>(false);
|
||||||
|
private destroyRef = false;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
// Vérifie le statut du like à l'initialisation
|
||||||
|
this.isAuthenticated.set(!!this.auth.currentUser());
|
||||||
|
this.checkLikeStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
// Vérifie le statut du like lorsque les propriétés d'entrée changent
|
||||||
|
if (changes['videoId'] && !changes['videoId'].firstChange) {
|
||||||
|
this.checkLikeStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.destroyRef = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(event: MouseEvent) {
|
||||||
|
try { event.preventDefault(); event.stopPropagation(); } catch {}
|
||||||
|
// Re-evaluate auth state on interaction (in case it changed)
|
||||||
|
this.isAuthenticated.set(!!this.auth.currentUser());
|
||||||
|
if (!this.isAuthenticated()) return;
|
||||||
|
this.toggleLike();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkLikeStatus() {
|
||||||
|
if (!this.videoId) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoading.set(true);
|
||||||
|
try {
|
||||||
|
this.likesService.isLiked(this.provider, this.videoId)
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: (response: { liked: boolean }) => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLiked.set(response.liked);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err: any) => {
|
||||||
|
console.error('Erreur lors de la vérification du like:', err);
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de la vérification du like:', error);
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleLike() {
|
||||||
|
if (this.isLoading() || !this.videoId) return;
|
||||||
|
|
||||||
|
this.isLoading.set(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.isLiked()) {
|
||||||
|
this.likesService.unlike(this.provider, this.videoId)
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: (response: { removed: boolean }) => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLiked.set(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err: any) => {
|
||||||
|
console.error('Erreur lors du retrait du like:', err);
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.likesService.like(this.provider, this.videoId)
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLiked.set(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err: any) => {
|
||||||
|
console.error('Erreur lors de l\'ajout du like:', err);
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de la gestion du like:', error);
|
||||||
|
if (!this.destroyRef) {
|
||||||
|
this.isLoading.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -41,18 +41,18 @@
|
|||||||
<span [class.hidden]="collapsed">{{ 'nav.history' | t }}</span>
|
<span [class.hidden]="collapsed">{{ 'nav.history' | t }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a routerLink="/library/playlists" routerLinkActive="bg-slate-800" class="flex items-center px-3 py-2 rounded hover:bg-slate-800 transition" [ngClass]="{ 'gap-0 justify-center': collapsed, 'gap-3': !collapsed }" [attr.title]="collapsed ? ('nav.playlists' | t) : null">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h12v2H4V6zm0 4h12v2H4v-2zm0 4h8v2H4v-2zm14-6h2v8h-2v-8z"/></svg>
|
|
||||||
<span [class.hidden]="collapsed">{{ 'nav.playlists' | t }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
<a routerLink="/library/liked" routerLinkActive="bg-slate-800" class="flex items-center px-3 py-2 rounded hover:bg-slate-800 transition" [ngClass]="{ 'gap-0 justify-center': collapsed, 'gap-3': !collapsed }" [attr.title]="collapsed ? ('nav.liked' | t) : null">
|
<a routerLink="/library/liked" routerLinkActive="bg-slate-800" class="flex items-center px-3 py-2 rounded hover:bg-slate-800 transition" [ngClass]="{ 'gap-0 justify-center': collapsed, 'gap-3': !collapsed }" [attr.title]="collapsed ? ('nav.liked' | t) : null">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-6.716-4.248-9.193-6.725A6 6 0 0 1 12 5.414 6 6 0 0 1 21.193 14.275C18.716 16.752 12 21 12 21z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-6.716-4.248-9.193-6.725A6 6 0 0 1 12 5.414 6 6 0 0 1 21.193 14.275C18.716 16.752 12 21 12 21z"/></svg>
|
||||||
<span [class.hidden]="collapsed">{{ 'nav.liked' | t }}</span>
|
<span [class.hidden]="collapsed">{{ 'nav.liked' | t }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a routerLink="/library/playlists" routerLinkActive="bg-slate-800" class="flex items-center px-3 py-2 rounded hover:bg-slate-800 transition" [ngClass]="{ 'gap-0 justify-center': collapsed, 'gap-3': !collapsed }" [attr.title]="collapsed ? ('nav.playlists' | t) : null">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h12v2H4V6zm0 4h12v2H4v-2zm0 4h8v2H4v-2zm14-6h2v8h-2v-8z"/></svg>
|
||||||
|
<span [class.hidden]="collapsed">{{ 'nav.playlists' | t }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,12 @@
|
|||||||
<a *ngFor="let v of (recorded.items | slice:0:recorded.visibleCount); trackBy: trackByVideo"
|
<a *ngFor="let v of (recorded.items | slice:0:recorded.visibleCount); trackBy: trackByVideo"
|
||||||
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
|
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
|
||||||
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
|
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
|
||||||
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
<div class="relative">
|
||||||
|
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -123,7 +128,12 @@
|
|||||||
<a *ngFor="let v of (videos.items | slice:0:videos.visibleCount); trackBy: trackByVideo"
|
<a *ngFor="let v of (videos.items | slice:0:videos.visibleCount); trackBy: trackByVideo"
|
||||||
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
|
[routerLink]="['/watch', v.videoId]" [queryParams]="watchQueryParams(v)" [state]="{ video: v }"
|
||||||
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
|
class="group bg-slate-800/50 rounded-lg overflow-hidden hover:bg-slate-700/50">
|
||||||
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
<div class="relative">
|
||||||
|
<img [src]="v.thumbnail" [alt]="v.title" loading="lazy" decoding="async" class="w-full h-32 object-cover">
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="v.videoId" [provider]="provider()"></app-like-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
<div class="p-2 text-sm line-clamp-2">{{ v.title }}</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,6 +8,7 @@ import { YoutubeApiService } from '../../services/youtube-api.service';
|
|||||||
import { Video } from '../../models/video.model';
|
import { Video } from '../../models/video.model';
|
||||||
import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component';
|
import { InfiniteAnchorComponent } from '../shared/infinite-anchor/infinite-anchor.component';
|
||||||
import { TranslatePipe } from '../../pipes/translate.pipe';
|
import { TranslatePipe } from '../../pipes/translate.pipe';
|
||||||
|
import { LikeButtonComponent } from '../shared/components/like-button/like-button.component';
|
||||||
|
|
||||||
interface SectionState {
|
interface SectionState {
|
||||||
key: 'recorded' | 'videos' | 'categories';
|
key: 'recorded' | 'videos' | 'categories';
|
||||||
@ -24,7 +25,7 @@ interface SectionState {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl: './provider-theme-page.component.html',
|
templateUrl: './provider-theme-page.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [CommonModule, FormsModule, RouterLink, InfiniteAnchorComponent, TranslatePipe]
|
imports: [CommonModule, FormsModule, RouterLink, InfiniteAnchorComponent, TranslatePipe, LikeButtonComponent]
|
||||||
})
|
})
|
||||||
export class ProviderThemePageComponent implements OnDestroy {
|
export class ProviderThemePageComponent implements OnDestroy {
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
|
@ -32,6 +32,9 @@
|
|||||||
<div class="absolute bottom-2 right-2 bg-black/70 text-white text-xs px-2 py-0.5 rounded" *ngIf="v.duration">
|
<div class="absolute bottom-2 right-2 bg-black/70 text-white text-xs px-2 py-0.5 rounded" *ngIf="v.duration">
|
||||||
{{ v.duration / 60 | number:'1.0-0' }}:{{ (v.duration % 60) | number:'2.0-0' }}
|
{{ v.duration / 60 | number:'1.0-0' }}:{{ (v.duration % 60) | number:'2.0-0' }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="absolute bottom-2 left-2">
|
||||||
|
<app-like-button [videoId]="v.videoId" [provider]="b.provider"></app-like-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-3">
|
<div class="p-3">
|
||||||
<h4 class="font-semibold line-clamp-2">{{ v.title }}</h4>
|
<h4 class="font-semibold line-clamp-2">{{ v.title }}</h4>
|
||||||
|
@ -6,6 +6,7 @@ import { YoutubeApiService } from '../../services/youtube-api.service';
|
|||||||
import { Provider, InstanceService } from '../../services/instance.service';
|
import { Provider, InstanceService } from '../../services/instance.service';
|
||||||
import { Video } from '../../models/video.model';
|
import { Video } from '../../models/video.model';
|
||||||
import { TranslatePipe } from '../../pipes/translate.pipe';
|
import { TranslatePipe } from '../../pipes/translate.pipe';
|
||||||
|
import { LikeButtonComponent } from '../shared/components/like-button/like-button.component';
|
||||||
|
|
||||||
interface ProviderBlock {
|
interface ProviderBlock {
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
@ -20,7 +21,7 @@ interface ProviderBlock {
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
templateUrl: './theme-page.component.html',
|
templateUrl: './theme-page.component.html',
|
||||||
imports: [CommonModule, RouterLink, TranslatePipe]
|
imports: [CommonModule, RouterLink, TranslatePipe, LikeButtonComponent]
|
||||||
})
|
})
|
||||||
export class ThemePageComponent {
|
export class ThemePageComponent {
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user