Skip to content

Commit ede96a6

Browse files
committed
fix upload-file component & cahched video reload
1 parent a9e9283 commit ede96a6

13 files changed

Lines changed: 269 additions & 92 deletions

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.html

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,41 @@
11
<!-- @format -->
22

3-
<div class="exclude" [class.exclude--hasContent]="hasVideoUrl() || !!data.imageUrl">
4-
<div class="exclude__body" [class.exclude__body--hasContent]="hasVideoUrl() || !!data.imageUrl">
3+
<div class="exclude" [class.exclude--hasContent]="cachedVideoUrl || !!data.imageUrl">
4+
<div class="exclude__body" [class.exclude__body--hasContent]="cachedVideoUrl || !!data.imageUrl">
55
<p class="text-body-12 exclude__answer">задание {{ data.order }}</p>
66
<div
77
class="exclude__text text-body-12"
8-
[class.exclude__text--hasContent]="hasVideoUrl() || !!data.imageUrl"
8+
[class.exclude__text--hasContent]="cachedVideoUrl || !!data.imageUrl"
99
>
10-
@if (getSafeVideoUrl(); as videoUrl) {
11-
<iframe [src]="videoUrl" frameborder="0" allowfullscreen allowtransparency="true"></iframe>
10+
@if (cachedVideoUrl) {
11+
<iframe
12+
[src]="cachedVideoUrl"
13+
frameborder="0"
14+
allowfullscreen
15+
allowtransparency="true"
16+
></iframe>
1217
} @else if (data.imageUrl) {
1318
<img appImagePreview alt="exclude-image" [src]="data.imageUrl" class="exclude__image" />
1419
<p class="text-body-12-bold">{{ data.title | truncate: 50 }}</p>
1520
} @if (!data.imageUrl) {
1621
<p class="exclude__text text-body-12-bold">{{ data.answerTitle | truncate: 110 }}</p>
1722
}
1823

19-
<p class="text-body-12 exclude__description">{{ data.bodyText | truncate: 700 }}</p>
24+
<p #descEl class="text-body-12 exclude__description">{{ data.bodyText | truncate: 700 }}</p>
25+
@if (descriptionExpandable) {
26+
<div
27+
class="read-more text-body-10"
28+
(click)="onExpandDescription(descEl, 'expanded', readFullDescription)"
29+
>
30+
{{ readFullDescription ? "скрыть" : "подробнее" }}
31+
</div>
32+
}
2033
</div>
2134
</div>
2235

2336
<div
2437
class="exclude__content"
25-
[class.exclude__content--hasContent]="hasVideoUrl() || !!data.imageUrl"
38+
[class.exclude__content--hasContent]="cachedVideoUrl || !!data.imageUrl"
2639
>
2740
<p class="exclude__answer text-body-12">ответ</p>
2841
<ul class="exclude__list">

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,30 @@
7676
}
7777

7878
&__description {
79+
display: -webkit-box;
80+
overflow: hidden;
7981
text-align: justify;
82+
-webkit-box-orient: vertical;
83+
-webkit-line-clamp: 6;
84+
transition: all 0.7s ease-in-out;
85+
86+
&.expanded {
87+
display: block;
88+
max-height: 200px;
89+
overflow-y: auto;
90+
-webkit-line-clamp: unset;
91+
}
92+
}
93+
94+
.read-more {
95+
margin-top: 12px;
96+
color: var(--accent);
97+
cursor: pointer;
98+
transition: color 0.2s;
99+
100+
&:hover {
101+
color: var(--accent-dark);
102+
}
80103
}
81104

82105
&__image {

projects/social_platform/src/app/office/courses/lesson/shared/exclude-task/exclude-task.component.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
/** @format */
22

3-
import { Component, EventEmitter, inject, Input, type OnInit, Output, signal } from "@angular/core";
3+
import {
4+
AfterViewInit,
5+
ChangeDetectorRef,
6+
Component,
7+
ElementRef,
8+
EventEmitter,
9+
inject,
10+
Input,
11+
type OnInit,
12+
Output,
13+
signal,
14+
ViewChild,
15+
} from "@angular/core";
416
import { CommonModule } from "@angular/common";
517
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
618
import { CheckboxComponent } from "@ui/components";
719
import { TruncatePipe } from "projects/core/src/lib/pipes/truncate.pipe";
820
import { Task } from "@office/models/courses.model";
921
import { resolveVideoUrlForIframe } from "@utils/video-url-embed";
22+
import { expandElement } from "@utils/expand-element";
1023
import { ImagePreviewDirective } from "../image-preview/image-preview.directive";
1124

1225
/**
@@ -36,8 +49,11 @@ import { ImagePreviewDirective } from "../image-preview/image-preview.directive"
3649
templateUrl: "./exclude-task.component.html",
3750
styleUrl: "./exclude-task.component.scss",
3851
})
39-
export class ExcludeTaskComponent implements OnInit {
52+
export class ExcludeTaskComponent implements OnInit, AfterViewInit {
4053
private readonly sanitizer = inject(DomSanitizer);
54+
private readonly cdRef = inject(ChangeDetectorRef);
55+
56+
@ViewChild("descEl") descEl?: ElementRef<HTMLElement>;
4157

4258
@Input({ required: true }) data!: Task; // Данные вопроса
4359
@Input() hint!: string; // Текст подсказки
@@ -63,18 +79,28 @@ export class ExcludeTaskComponent implements OnInit {
6379

6480
result = signal<number[]>([]);
6581
_error = signal<boolean>(false);
82+
descriptionExpandable = false;
83+
readFullDescription = false;
84+
cachedVideoUrl: SafeResourceUrl | null = null;
6685

67-
getSafeVideoUrl(): SafeResourceUrl | null {
86+
ngOnInit(): void {
6887
const iframeUrl = resolveVideoUrlForIframe(this.data?.videoUrl);
69-
if (!iframeUrl) {
70-
return null;
71-
}
88+
this.cachedVideoUrl = iframeUrl
89+
? this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl)
90+
: null;
91+
}
7292

73-
return this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl);
93+
ngAfterViewInit(): void {
94+
const el = this.descEl?.nativeElement;
95+
if (el) {
96+
this.descriptionExpandable = el.scrollHeight > el.clientHeight;
97+
this.cdRef.detectChanges();
98+
}
7499
}
75100

76-
hasVideoUrl(): boolean {
77-
return !!resolveVideoUrlForIframe(this.data?.videoUrl);
101+
onExpandDescription(elem: HTMLElement, expandedClass: string, isExpanded: boolean): void {
102+
expandElement(elem, expandedClass, isExpanded);
103+
this.readFullDescription = !isExpanded;
78104
}
79105

80106
/**
@@ -93,6 +119,4 @@ export class ExcludeTaskComponent implements OnInit {
93119

94120
this.update.emit(this.result());
95121
}
96-
97-
ngOnInit(): void {}
98122
}

projects/social_platform/src/app/office/courses/lesson/shared/file-task/file-task.component.html

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<!-- @format -->
22

3-
<div class="file-task" [class.file-task--hasVideo]="hasVideoUrl()">
3+
<div class="file-task" [class.file-task--hasVideo]="cachedVideoUrl">
44
<div class="file-task__header">
55
<p class="text-body-12 file-task__task">задание {{ data.order }}</p>
66
@if ((!data.videoUrl && !data.imageUrl) && data.attachmentUrl) {
77
<p class="file-task__text text-body-12-bold">{{ data.title | truncate: 80 }}</p>
88
}
99
<div
1010
class="file-task__description text-body-12"
11-
[class.file-task__description--hasVideo]="hasVideoUrl()"
11+
[class.file-task__description--hasVideo]="cachedVideoUrl"
1212
>
13-
@if (getSafeVideoUrl(); as videoUrl) {
13+
@if (cachedVideoUrl) {
1414
<iframe
15-
[src]="videoUrl"
15+
[src]="cachedVideoUrl"
1616
width="397px"
1717
height="223px"
1818
frameborder="0"
@@ -29,12 +29,14 @@
2929
<div
3030
#descEl
3131
class="file-task__desc text-body-12"
32-
[class.file-task__desc--expanded]="readFullDescription"
3332
[innerHTML]="data.bodyText | truncate: 700"
3433
></div>
3534

3635
@if (descriptionExpandable) {
37-
<div class="read-more text-body-10" (click)="onToggleDescription()">
36+
<div
37+
class="read-more text-body-10"
38+
(click)="onExpandDescription(descEl, 'expanded', readFullDescription)"
39+
>
3840
{{ readFullDescription ? "скрыть" : "подробнее" }}
3941
</div>
4042
}

projects/social_platform/src/app/office/courses/lesson/shared/file-task/file-task.component.scss

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
&__content {
1717
flex-grow: 1;
1818
min-height: 358px;
19-
max-height: 393px;
2019
padding: 24px;
2120
background: var(--light-white);
2221
border: 0.5px solid var(--medium-grey-for-outline);
@@ -123,6 +122,7 @@
123122
max-width: 300px;
124123
height: 100%;
125124
max-height: 150px;
125+
object-fit: cover;
126126
border-radius: var(--rounded-lg);
127127

128128
&-text {
@@ -137,20 +137,18 @@
137137
}
138138

139139
&__desc {
140-
max-height: 12em;
140+
display: -webkit-box;
141141
overflow: hidden;
142142
text-align: justify;
143-
transition: max-height 0.5s ease-in-out;
143+
-webkit-box-orient: vertical;
144+
-webkit-line-clamp: 6;
145+
transition: all 0.7s ease-in-out;
144146

145-
&:not(&--expanded) {
146-
display: box;
147-
-webkit-box-orient: vertical;
148-
-webkit-line-clamp: 8;
149-
}
150-
151-
&--expanded {
152-
max-height: 210px;
147+
&.expanded {
148+
display: block;
149+
max-height: 200px;
153150
overflow-y: auto;
151+
-webkit-line-clamp: unset;
154152
}
155153
}
156154

projects/social_platform/src/app/office/courses/lesson/shared/file-task/file-task.component.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
EventEmitter,
99
inject,
1010
Input,
11+
OnInit,
1112
Output,
1213
signal,
1314
ViewChild,
@@ -23,6 +24,7 @@ import { FileService } from "@core/services/file.service";
2324
import { Task } from "@office/models/courses.model";
2425
import { FileModel } from "@office/models/file.model";
2526
import { resolveVideoUrlForIframe } from "@utils/video-url-embed";
27+
import { expandElement } from "@utils/expand-element";
2628
import { ImagePreviewDirective } from "../image-preview/image-preview.directive";
2729

2830
@Component({
@@ -40,7 +42,7 @@ import { ImagePreviewDirective } from "../image-preview/image-preview.directive"
4042
templateUrl: "./file-task.component.html",
4143
styleUrl: "./file-task.component.scss",
4244
})
43-
export class FileTaskComponent implements AfterViewInit {
45+
export class FileTaskComponent implements OnInit, AfterViewInit {
4446
private readonly fileService = inject(FileService);
4547
private readonly sanitizer = inject(DomSanitizer);
4648
private readonly cdRef = inject(ChangeDetectorRef);
@@ -74,6 +76,14 @@ export class FileTaskComponent implements AfterViewInit {
7476
uploadedFiles = signal<FileModel[]>([]);
7577
descriptionExpandable = false;
7678
readFullDescription = false;
79+
cachedVideoUrl: SafeResourceUrl | null = null;
80+
81+
ngOnInit(): void {
82+
const iframeUrl = resolveVideoUrlForIframe(this.data?.videoUrl);
83+
this.cachedVideoUrl = iframeUrl
84+
? this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl)
85+
: null;
86+
}
7787

7888
ngAfterViewInit(): void {
7989
const el = this.descEl?.nativeElement;
@@ -83,21 +93,9 @@ export class FileTaskComponent implements AfterViewInit {
8393
}
8494
}
8595

86-
onToggleDescription(): void {
87-
this.readFullDescription = !this.readFullDescription;
88-
}
89-
90-
getSafeVideoUrl(): SafeResourceUrl | null {
91-
const iframeUrl = resolveVideoUrlForIframe(this.data?.videoUrl);
92-
if (!iframeUrl) {
93-
return null;
94-
}
95-
96-
return this.sanitizer.bypassSecurityTrustResourceUrl(iframeUrl);
97-
}
98-
99-
hasVideoUrl(): boolean {
100-
return !!resolveVideoUrlForIframe(this.data?.videoUrl);
96+
onExpandDescription(elem: HTMLElement, expandedClass: string, isExpanded: boolean): void {
97+
expandElement(elem, expandedClass, isExpanded);
98+
this.readFullDescription = !isExpanded;
10199
}
102100

103101
onFileUploaded(event: { url: string; name: string; size: number; mimeType: string }) {

projects/social_platform/src/app/office/courses/lesson/shared/video-task/info-task.component.html

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@
1515
[class.video__wrapper--hasVideo]="data.videoUrl"
1616
[class.video__wrapper--hasImage]="data.imageUrl"
1717
>
18-
@if (data.videoUrl) { @if (getSafeVideoUrl(); as videoUrl) {
18+
@if (cachedVideoUrl) {
1919
<iframe
20-
[src]="videoUrl"
20+
[src]="cachedVideoUrl"
2121
width="395px"
2222
height="222px"
2323
frameborder="0"
2424
allowfullscreen
2525
allowtransparency="true"
2626
>
2727
</iframe>
28-
} } @if (data.imageUrl) {
28+
} @if (data.imageUrl) {
2929
<img appImagePreview class="video__img" [src]="data.imageUrl" />
3030
}
3131

@@ -35,16 +35,23 @@
3535
}
3636

3737
<p class="video__title text-body-12-bold">
38-
{{ data.title | truncate: (hasVideoUrl() ? 80 : data.imageUrl ? 100 : 200) }}
38+
{{ data.title | truncate: (cachedVideoUrl ? 80 : data.imageUrl ? 100 : 200) }}
3939
</p>
4040
<div
41+
#descEl
4142
class="video__text text-body-12"
4243
[innerHTML]="
43-
data.bodyText | truncateHtml: (hasVideoUrl() ? 627 : data.imageUrl ? 500 : 1000)
44+
data.bodyText | truncateHtml: (cachedVideoUrl ? 627 : data.imageUrl ? 500 : 1000)
4445
"
4546
></div>
46-
47-
@if (data.attachmentUrl) {
47+
@if (descriptionExpandable) {
48+
<div
49+
class="read-more text-body-10"
50+
(click)="onExpandDescription(descEl, 'expanded', readFullDescription)"
51+
>
52+
{{ readFullDescription ? "скрыть" : "подробнее" }}
53+
</div>
54+
} @if (data.attachmentUrl) {
4855
<app-file-item
4956
mode="preview"
5057
[type]="'file'"

projects/social_platform/src/app/office/courses/lesson/shared/video-task/info-task.component.scss

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@
7070
}
7171

7272
&__text {
73+
display: -webkit-box;
74+
overflow: hidden;
7375
white-space: pre-line;
76+
-webkit-box-orient: vertical;
77+
-webkit-line-clamp: 6;
78+
transition: all 0.7s ease-in-out;
79+
80+
&.expanded {
81+
display: block;
82+
max-height: 200px;
83+
overflow-y: auto;
84+
-webkit-line-clamp: unset;
85+
}
86+
}
87+
88+
.read-more {
89+
margin-top: 12px;
90+
color: var(--accent);
91+
cursor: pointer;
92+
transition: color 0.2s;
93+
94+
&:hover {
95+
color: var(--accent-dark);
96+
}
7497
}
7598
}

0 commit comments

Comments
 (0)