itube-specs 0.0.904 → 0.0.910
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/icons/common/playlist.svg +1 -1
- package/assets/scss/themes/dark/_theme.scss +37 -15
- package/assets/scss/themes/light/_theme.scss +34 -12
- package/assets/scss/vars/_palette.scss +5 -0
- package/assets/scss/vars/_vars.scss +1 -0
- package/components/auth/login.vue +2 -2
- package/components/auth/recovery.vue +2 -2
- package/components/auth/register.vue +2 -2
- package/components/card/collections-more.vue +133 -0
- package/components/card/playlist.vue +6 -1
- package/components/grid/playlists.vue +3 -0
- package/components/info/main.vue +18 -14
- package/components/library/feed.vue +20 -39
- package/components/library/follows.vue +19 -4
- package/components/playlist/add.vue +2 -2
- package/components/playlist/delete-video.vue +2 -2
- package/components/playlist/edit.vue +3 -3
- package/components/profile/delete.vue +2 -2
- package/components/profile/setup.vue +2 -2
- package/components/report/main.vue +9 -4
- package/components/search/input.vue +9 -9
- package/components/section/collections.vue +110 -0
- package/components/ui/follow-btn.vue +162 -0
- package/components/ui/section-title.vue +2 -0
- package/components/ui/snackbar.vue +93 -44
- package/composables/__tests__/use-snackbar.test.ts +126 -20
- package/composables/use-snackbar.ts +75 -20
- package/composables/use-user.ts +4 -4
- package/package.json +1 -1
- package/pages/contacts.vue +2 -2
- package/pages/library/index.vue +1 -1
- package/pages/playlists/index.vue +55 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="
|
|
1
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12H3"></path><path d="M16 6H3"></path><path d="M12 18H3"></path><path d="m16 12 5 3-5 3v-6Z"></path></svg>
|
|
@@ -43,7 +43,7 @@ $nav-border--hover: transparent;
|
|
|
43
43
|
$nav-border: $color-zinc-700-50;
|
|
44
44
|
$nav-icon: $color-zinc-400;
|
|
45
45
|
$nav-icon-accent: $color-red-400;
|
|
46
|
-
$nav-icon--active: $
|
|
46
|
+
$nav-icon--active: $color-zinc-400;
|
|
47
47
|
$nav-container-bg: $color-black;
|
|
48
48
|
$nav-down-icon: $color-zinc-500;
|
|
49
49
|
$nav-menu-bg: $color-zinc-900;
|
|
@@ -105,7 +105,7 @@ $count-bg: $color-zinc-800;
|
|
|
105
105
|
$count-text: $color-zinc-400;
|
|
106
106
|
|
|
107
107
|
// ─── Search ───
|
|
108
|
-
$search-bg: $color-zinc-800;
|
|
108
|
+
$search-bg: $color-zinc-800-50;
|
|
109
109
|
$search-text: $color-white;
|
|
110
110
|
$search-bg--mobile: $color-zinc-800;
|
|
111
111
|
$search-border--active--mobile: $color-indigo-500;
|
|
@@ -119,7 +119,7 @@ $search-button-bg: $color-indigo-600;
|
|
|
119
119
|
$search-button-bg--hover: $color-indigo-500;
|
|
120
120
|
$search-max-width: 500px;
|
|
121
121
|
$search-max-width-md: 672px;
|
|
122
|
-
$search-mobile-bg: $
|
|
122
|
+
$search-mobile-bg: $color-neutral-940-95;
|
|
123
123
|
$search-mobile-border: $color-zinc-800;
|
|
124
124
|
$search-mobile-decorate: $mono-black-50;
|
|
125
125
|
$search-mobile-button: $color-zinc-200;
|
|
@@ -468,18 +468,29 @@ $dropdown-crest: $color-zinc-500;
|
|
|
468
468
|
$dropdown-separator: $color-zinc-800-60;
|
|
469
469
|
|
|
470
470
|
// ─── Snackbar ───
|
|
471
|
+
$snackbar-bg: $color-zinc-800;
|
|
472
|
+
$snackbar-border: $color-zinc-700;
|
|
471
473
|
$snackbar-text: $color-zinc-200;
|
|
472
|
-
$snackbar-
|
|
473
|
-
$snackbar-
|
|
474
|
-
$snackbar-
|
|
475
|
-
$snackbar-
|
|
476
|
-
$snackbar-
|
|
477
|
-
$snackbar-
|
|
478
|
-
$snackbar-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
$
|
|
482
|
-
$
|
|
474
|
+
$snackbar-highlight: $color-white;
|
|
475
|
+
$snackbar-icon: $color-zinc-300;
|
|
476
|
+
$snackbar-icon-bg: $color-zinc-700-60;
|
|
477
|
+
$snackbar-shadow: 0 10px 15px -3px #{$mono-black-25}, 0 4px 6px -4px #{$mono-black-25};
|
|
478
|
+
$snackbar-action-bg: $color-indigo-600;
|
|
479
|
+
$snackbar-action-bg--hover: $color-indigo-500;
|
|
480
|
+
$snackbar-action-text: $color-white;
|
|
481
|
+
|
|
482
|
+
// ─── Follow button ───
|
|
483
|
+
$follow-btn-bg: $color-indigo-600;
|
|
484
|
+
$follow-btn-bg--hover: $color-indigo-500;
|
|
485
|
+
$follow-btn-border: $color-indigo-500;
|
|
486
|
+
$follow-btn-border--hover: $color-indigo-500;
|
|
487
|
+
$follow-btn-text: $color-white;
|
|
488
|
+
$follow-btn-following-bg: $color-zinc-800;
|
|
489
|
+
$follow-btn-following-bg--hover: $color-red-600;
|
|
490
|
+
$follow-btn-following-border: $color-zinc-800;
|
|
491
|
+
$follow-btn-following-border--hover: $color-red-500;
|
|
492
|
+
$follow-btn-following-text: $color-white;
|
|
493
|
+
$follow-btn-following-text--hover: $color-white;
|
|
483
494
|
|
|
484
495
|
// ─── Button ───
|
|
485
496
|
$button-primary-bg: $color-indigo-600;
|
|
@@ -854,6 +865,18 @@ $playlist-card-placeholder-bg: $color-zinc-700;
|
|
|
854
865
|
$playlist-card-dropdown-icon: $color-zinc-500;
|
|
855
866
|
$playlist-card-dropdown-icon--hover: $color-white;
|
|
856
867
|
|
|
868
|
+
// ─── Collections section ───
|
|
869
|
+
$collections-badge-text: $color-indigo-300;
|
|
870
|
+
$collections-badge-bg: $color-indigo-500-20;
|
|
871
|
+
$collections-badge-border: $color-indigo-500-40;
|
|
872
|
+
$collections-more-bg: $color-zinc-800;
|
|
873
|
+
$collections-more-border: $color-zinc-800-50;
|
|
874
|
+
$collections-more-border--hover: $color-zinc-700;
|
|
875
|
+
$collections-more-scrim: $color-black-50;
|
|
876
|
+
$collections-more-scrim--hover: $color-black-42;
|
|
877
|
+
$collections-more-count: $color-white;
|
|
878
|
+
$collections-more-text: $color-zinc-200;
|
|
879
|
+
|
|
857
880
|
// ─── Channel card ───
|
|
858
881
|
$channel-card-border: $color-zinc-800-50;
|
|
859
882
|
$channel-card-bg: $color-zinc-800-50;
|
|
@@ -986,7 +1009,6 @@ $library-item-badge-bordered-text: $color-zinc-400;
|
|
|
986
1009
|
// ─── Library feed (блок подписки: модель / канал / категория) ───
|
|
987
1010
|
$library-feed-bg: $color-zinc-900-40;
|
|
988
1011
|
$library-feed-border: $color-zinc-800;
|
|
989
|
-
$library-feed-fade: $color-zinc-950;
|
|
990
1012
|
$library-feed-avatar-border: $color-zinc-700;
|
|
991
1013
|
$library-feed-avatar-border--hover: $color-zinc-500;
|
|
992
1014
|
$library-feed-initials: $color-zinc-400;
|
|
@@ -523,18 +523,29 @@ $dropdown-crest: $color-gray-400;
|
|
|
523
523
|
$dropdown-separator: $color-slate-750;
|
|
524
524
|
|
|
525
525
|
// ─── Snackbar ───
|
|
526
|
+
$snackbar-bg: $color-slate-800;
|
|
527
|
+
$snackbar-border: $color-slate-750;
|
|
526
528
|
$snackbar-text: $color-gray-200;
|
|
527
|
-
$snackbar-
|
|
528
|
-
$snackbar-
|
|
529
|
-
$snackbar-
|
|
530
|
-
$snackbar-
|
|
531
|
-
$snackbar-
|
|
532
|
-
$snackbar-
|
|
533
|
-
$snackbar-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
$
|
|
537
|
-
$
|
|
529
|
+
$snackbar-highlight: $color-white;
|
|
530
|
+
$snackbar-icon: $color-gray-300;
|
|
531
|
+
$snackbar-icon-bg: $color-slate-700;
|
|
532
|
+
$snackbar-shadow: 0 10px 15px -3px #{$mono-black-25}, 0 4px 6px -4px #{$mono-black-25};
|
|
533
|
+
$snackbar-action-bg: $color-indigo-600;
|
|
534
|
+
$snackbar-action-bg--hover: $color-indigo-500;
|
|
535
|
+
$snackbar-action-text: $color-white;
|
|
536
|
+
|
|
537
|
+
// ─── Follow button ───
|
|
538
|
+
$follow-btn-bg: $color-indigo-600;
|
|
539
|
+
$follow-btn-bg--hover: $color-indigo-500;
|
|
540
|
+
$follow-btn-border: $color-indigo-500;
|
|
541
|
+
$follow-btn-border--hover: $color-indigo-500;
|
|
542
|
+
$follow-btn-text: $color-white;
|
|
543
|
+
$follow-btn-following-bg: $color-gray-300;
|
|
544
|
+
$follow-btn-following-bg--hover: $color-red-500;
|
|
545
|
+
$follow-btn-following-border: $color-gray-300;
|
|
546
|
+
$follow-btn-following-border--hover: $color-red-500;
|
|
547
|
+
$follow-btn-following-text: $color-gray-700;
|
|
548
|
+
$follow-btn-following-text--hover: $color-white;
|
|
538
549
|
|
|
539
550
|
// ─── Button ───
|
|
540
551
|
$button-primary-bg: $color-indigo-600;
|
|
@@ -882,6 +893,18 @@ $playlist-card-placeholder-bg: $color-gray-300;
|
|
|
882
893
|
$playlist-card-dropdown-icon: $color-gray-600;
|
|
883
894
|
$playlist-card-dropdown-icon--hover: $color-gray-900;
|
|
884
895
|
|
|
896
|
+
// ─── Collections section ───
|
|
897
|
+
$collections-badge-text: $color-indigo-600;
|
|
898
|
+
$collections-badge-bg: $color-indigo-500-10;
|
|
899
|
+
$collections-badge-border: $color-indigo-500-20;
|
|
900
|
+
$collections-more-bg: $color-gray-300;
|
|
901
|
+
$collections-more-border: $color-gray-300;
|
|
902
|
+
$collections-more-border--hover: $color-gray-400;
|
|
903
|
+
$collections-more-scrim: $color-black-45;
|
|
904
|
+
$collections-more-scrim--hover: $color-black-32;
|
|
905
|
+
$collections-more-count: $color-white;
|
|
906
|
+
$collections-more-text: $color-white;
|
|
907
|
+
|
|
885
908
|
// ─── Channel card ───
|
|
886
909
|
$channel-card-border: transparent;
|
|
887
910
|
$channel-card-bg: $color-gray-100;
|
|
@@ -1014,7 +1037,6 @@ $library-item-badge-bordered-text: $color-gray-600;
|
|
|
1014
1037
|
// ─── Library feed (блок подписки: модель / канал / категория) ───
|
|
1015
1038
|
$library-feed-bg: $color-gray-100;
|
|
1016
1039
|
$library-feed-border: transparent;
|
|
1017
|
-
$library-feed-fade: $color-gray-100;
|
|
1018
1040
|
$library-feed-avatar-border: $color-gray-300;
|
|
1019
1041
|
$library-feed-avatar-border--hover: $color-indigo-500-50;
|
|
1020
1042
|
$library-feed-initials: $color-gray-600;
|
|
@@ -22,6 +22,7 @@ $color-zinc-750: lab(21.3843% .9572 -2.5207); // #333337
|
|
|
22
22
|
$color-zinc-700: lab(26.8019% 1.35387 -4.68303); // #3f3f46
|
|
23
23
|
$color-zinc-700-80: lab(26.8019% 1.35387 -4.68303 / 80%); // zinc-700 / 70%
|
|
24
24
|
$color-zinc-700-70: lab(26.8019% 1.35387 -4.68303 / 70%); // zinc-700 / 70%
|
|
25
|
+
$color-zinc-700-60: lab(26.8019% 1.35387 -4.68303 / 60%); // zinc-700 / 60%
|
|
25
26
|
$color-zinc-700-50: lab(26.8019% 1.35387 -4.68303 / 50%); // zinc-700 / 50%
|
|
26
27
|
$color-zinc-700-30: lab(26.8019% 1.35387 -4.68303 / 30%); // zinc-700 / 50%
|
|
27
28
|
$color-zinc-650: lab(31.3256% 2.0703 -5.3354); // #494952
|
|
@@ -65,6 +66,10 @@ $color-indigo-800: lab(26.6645% 37.9804 -68.6402); // #372aac
|
|
|
65
66
|
$color-indigo-900: lab(23.3911% 24.6978 -50.4718); // #312c85
|
|
66
67
|
$color-indigo-950: lab(12.9255% 18.7583 -31.0075); // #1e1a4d
|
|
67
68
|
|
|
69
|
+
// Neutral gray (untinted — no zinc blue tint)
|
|
70
|
+
$color-neutral-940: lab(3.6355% 0 0); // #0d0d0d
|
|
71
|
+
$color-neutral-940-95: lab(3.6355% 0 0 / 95%); // #0d0d0d / 95%
|
|
72
|
+
|
|
68
73
|
// Monochrome black (opacity variants)
|
|
69
74
|
$mono-black-100: lab(0% 0 0);
|
|
70
75
|
$mono-black-95: lab(0% 0 0 / 95%);
|
|
@@ -94,7 +94,7 @@ function onCreateClick() {
|
|
|
94
94
|
|
|
95
95
|
const loading = ref(false);
|
|
96
96
|
|
|
97
|
-
const { setErrorState, showError,
|
|
97
|
+
const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
|
|
98
98
|
|
|
99
99
|
async function login() {
|
|
100
100
|
resetSnackbar();
|
|
@@ -115,7 +115,7 @@ async function login() {
|
|
|
115
115
|
loading.value = true;
|
|
116
116
|
await useUser(AuthorizationApiService).login(form.value);
|
|
117
117
|
useAuthPopup().closeAuthPopup();
|
|
118
|
-
|
|
118
|
+
show('Welcome back!');
|
|
119
119
|
} catch (error) {
|
|
120
120
|
setErrorState(error);
|
|
121
121
|
} finally {
|
|
@@ -70,7 +70,7 @@ function onLoginClick() {
|
|
|
70
70
|
|
|
71
71
|
const loading = ref(false);
|
|
72
72
|
|
|
73
|
-
const { setErrorState, showError,
|
|
73
|
+
const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
|
|
74
74
|
|
|
75
75
|
// Загружаем reCAPTCHA при открытии формы (onMounted) — даёт Google время собрать данные о поведении
|
|
76
76
|
onMounted(async () => {
|
|
@@ -97,7 +97,7 @@ async function submit() {
|
|
|
97
97
|
form.value.token = await getRecaptchaToken('password_reset');
|
|
98
98
|
await useUser(AuthorizationApiService).password(form.value);
|
|
99
99
|
useAuthPopup().closeAuthPopup();
|
|
100
|
-
|
|
100
|
+
show('Check your email');
|
|
101
101
|
} catch (error) {
|
|
102
102
|
setErrorState(error);
|
|
103
103
|
} finally {
|
|
@@ -129,7 +129,7 @@ function onLoginClick() {
|
|
|
129
129
|
|
|
130
130
|
const loading = ref(false);
|
|
131
131
|
|
|
132
|
-
const { setErrorState, showError,
|
|
132
|
+
const { setErrorState, showError, show, resetSnackbar } = useSnackbar();
|
|
133
133
|
// Загружаем reCAPTCHA при открытии формы (onMounted) — даёт Google время собрать данные о поведении
|
|
134
134
|
onMounted(async () => {
|
|
135
135
|
try {
|
|
@@ -176,7 +176,7 @@ async function submit() {
|
|
|
176
176
|
// await PlaylistsApiService.postPlaylist(FAVORITES_KEY, PlaylistType.Private);
|
|
177
177
|
|
|
178
178
|
useAuthPopup().closeAuthPopup();
|
|
179
|
-
|
|
179
|
+
show('Registration success');
|
|
180
180
|
} catch (err) {
|
|
181
181
|
setErrorState(err);
|
|
182
182
|
console.error('Ошибка reCAPTCHA или регистрации:', err);
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!-- Плитка «показать ещё» в сетке коллекций: 16 размытых обложек сеткой 4×4, поверх счётчик и подпись -->
|
|
2
|
+
<template>
|
|
3
|
+
<NuxtLink
|
|
4
|
+
:to="generateLink(to)"
|
|
5
|
+
class="card-collections-more"
|
|
6
|
+
>
|
|
7
|
+
<span
|
|
8
|
+
v-if="images.length > 0"
|
|
9
|
+
class="card-collections-more__thumbs"
|
|
10
|
+
aria-hidden="true"
|
|
11
|
+
>
|
|
12
|
+
<UiImg
|
|
13
|
+
v-for="(src, index) in images"
|
|
14
|
+
:key="`collections-more-thumb-${index}`"
|
|
15
|
+
:src="src"
|
|
16
|
+
:width="ThumbSize.Small.split('x')[0]"
|
|
17
|
+
:height="ThumbSize.Small.split('x')[1]"
|
|
18
|
+
alt=""
|
|
19
|
+
class="card-collections-more__thumb"
|
|
20
|
+
/>
|
|
21
|
+
</span>
|
|
22
|
+
|
|
23
|
+
<span class="card-collections-more__content">
|
|
24
|
+
<span
|
|
25
|
+
v-if="count"
|
|
26
|
+
class="card-collections-more__count"
|
|
27
|
+
>+{{ count }}</span>
|
|
28
|
+
<span class="card-collections-more__text">{{ text }}</span>
|
|
29
|
+
</span>
|
|
30
|
+
</NuxtLink>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { ThumbSize } from '../../runtime';
|
|
35
|
+
import type { IThumbUrls } from '../../types';
|
|
36
|
+
|
|
37
|
+
const props = defineProps<{
|
|
38
|
+
to: string
|
|
39
|
+
text: string
|
|
40
|
+
/** Обложки для мозаики: лишние отбрасываем, если меньше 16 — повторяем по кругу */
|
|
41
|
+
thumbs?: IThumbUrls[]
|
|
42
|
+
/** Сколько коллекций осталось за кадром — «+18» */
|
|
43
|
+
count?: number
|
|
44
|
+
}>();
|
|
45
|
+
|
|
46
|
+
const { generateLink } = useGenerateLink();
|
|
47
|
+
|
|
48
|
+
/** Ячеек в мозаике — сетка 4×4 */
|
|
49
|
+
const CELLS = 16;
|
|
50
|
+
|
|
51
|
+
const images = computed(() => {
|
|
52
|
+
const sources = (props.thumbs || [])
|
|
53
|
+
.map(thumb => thumb?.webp?.[ThumbSize.Small])
|
|
54
|
+
.filter((src): src is string => Boolean(src));
|
|
55
|
+
|
|
56
|
+
if (sources.length === 0) return [];
|
|
57
|
+
|
|
58
|
+
/* добиваем по кругу, чтобы мозаика всегда была полной, а не с дырами */
|
|
59
|
+
return Array.from({ length: CELLS }, (_, index) => sources[index % sources.length]);
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<style lang="scss">
|
|
64
|
+
.card-collections-more {
|
|
65
|
+
position: relative;
|
|
66
|
+
display: grid;
|
|
67
|
+
place-content: center;
|
|
68
|
+
min-height: 240px;
|
|
69
|
+
padding: 12px;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
text-align: center;
|
|
72
|
+
border-radius: $radius-xl;
|
|
73
|
+
border: 1px solid $collections-more-border;
|
|
74
|
+
background-color: $collections-more-bg;
|
|
75
|
+
|
|
76
|
+
@include hover(border-color) {
|
|
77
|
+
border-color: $collections-more-border--hover;
|
|
78
|
+
|
|
79
|
+
&::after {
|
|
80
|
+
background-color: $collections-more-scrim--hover;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* затемнение поверх обложек — псевдоэлементом, без отдельной ноды-оверлея */
|
|
85
|
+
&::after {
|
|
86
|
+
content: '';
|
|
87
|
+
position: absolute;
|
|
88
|
+
inset: 0;
|
|
89
|
+
background-color: $collections-more-scrim;
|
|
90
|
+
transition: var(--transition) background-color;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.card-collections-more__thumbs {
|
|
95
|
+
position: absolute;
|
|
96
|
+
inset: 0;
|
|
97
|
+
display: grid;
|
|
98
|
+
|
|
99
|
+
/* minmax(0, 1fr), иначе auto-минимум трека = min-content картинки и сетка распирает блок */
|
|
100
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
101
|
+
grid-template-rows: repeat(4, minmax(0, 1fr));
|
|
102
|
+
gap: 2px;
|
|
103
|
+
filter: blur(12px);
|
|
104
|
+
transform: scale(1.1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.card-collections-more__thumb {
|
|
108
|
+
width: 100%;
|
|
109
|
+
height: 100%;
|
|
110
|
+
object-fit: cover;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.card-collections-more__content {
|
|
114
|
+
position: relative;
|
|
115
|
+
z-index: $z-index-plus;
|
|
116
|
+
display: grid;
|
|
117
|
+
row-gap: 2px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.card-collections-more__count {
|
|
121
|
+
@include font-2xl;
|
|
122
|
+
|
|
123
|
+
font-weight: 700;
|
|
124
|
+
color: $collections-more-count;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.card-collections-more__text {
|
|
128
|
+
@include font-sm;
|
|
129
|
+
|
|
130
|
+
font-weight: 500;
|
|
131
|
+
color: $collections-more-text;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
@@ -107,7 +107,10 @@
|
|
|
107
107
|
<p class="card-playlist__user">
|
|
108
108
|
{{ card.username }}
|
|
109
109
|
</p>
|
|
110
|
-
<div
|
|
110
|
+
<div
|
|
111
|
+
v-if="!noActions"
|
|
112
|
+
class="card-playlist__content-wrapper"
|
|
113
|
+
>
|
|
111
114
|
<UiDropdown
|
|
112
115
|
ref="dropdownRef"
|
|
113
116
|
right
|
|
@@ -177,6 +180,8 @@ import { CSS_BREAKPOINTS } from '../../config/css-breakpoints';
|
|
|
177
180
|
const props = defineProps<{
|
|
178
181
|
card: IPlaylistCard
|
|
179
182
|
topPlaylists?: boolean
|
|
183
|
+
/** Без меню «три точки» — вместе с ним уходят share и report */
|
|
184
|
+
noActions?: boolean
|
|
180
185
|
priority?: boolean
|
|
181
186
|
}>();
|
|
182
187
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
:key="`user-playlist-${index}`"
|
|
7
7
|
:card="item"
|
|
8
8
|
:top-playlists="topPlaylists"
|
|
9
|
+
:no-actions="noActions"
|
|
9
10
|
:priority="priority && index === 0"
|
|
10
11
|
@share="onShare"
|
|
11
12
|
/>
|
|
@@ -32,6 +33,8 @@ import type { IPlaylistCard } from '../../types';
|
|
|
32
33
|
defineProps<{
|
|
33
34
|
items: IPlaylistCard[]
|
|
34
35
|
topPlaylists?: boolean
|
|
36
|
+
/** Скрыть меню карточки (три точки: share/report) — например у редакционных подборок */
|
|
37
|
+
noActions?: boolean
|
|
35
38
|
priority?: boolean
|
|
36
39
|
}>();
|
|
37
40
|
|
package/components/info/main.vue
CHANGED
|
@@ -30,19 +30,6 @@
|
|
|
30
30
|
class="info-main__count"
|
|
31
31
|
icon="video-camera"
|
|
32
32
|
>{{ videosCount || card.videosCount }}</UiCount>
|
|
33
|
-
<UiBtn
|
|
34
|
-
v-if="featureFlags.FeatureFollowEnabled"
|
|
35
|
-
theme="muted"
|
|
36
|
-
class="info-main__header-button"
|
|
37
|
-
size="s"
|
|
38
|
-
@click="onFollowClick"
|
|
39
|
-
>
|
|
40
|
-
<UiIcon
|
|
41
|
-
name="plus"
|
|
42
|
-
size="16"
|
|
43
|
-
/>
|
|
44
|
-
{{ t('follow') }}
|
|
45
|
-
</UiBtn>
|
|
46
33
|
<UiBtn
|
|
47
34
|
theme="muted"
|
|
48
35
|
size="s"
|
|
@@ -55,6 +42,14 @@
|
|
|
55
42
|
/>
|
|
56
43
|
{{ t('share') }}
|
|
57
44
|
</UiBtn>
|
|
45
|
+
<UiFollowBtn
|
|
46
|
+
v-if="featureFlags.FeatureFollowEnabled"
|
|
47
|
+
:following="isFollowing"
|
|
48
|
+
:name="title"
|
|
49
|
+
class="info-main__header-follow"
|
|
50
|
+
size="s"
|
|
51
|
+
@toggle="onFollowClick"
|
|
52
|
+
/>
|
|
58
53
|
</div>
|
|
59
54
|
</div>
|
|
60
55
|
|
|
@@ -190,9 +185,12 @@ function showParameters() {
|
|
|
190
185
|
|
|
191
186
|
const authPopupText = 'register_first';
|
|
192
187
|
|
|
188
|
+
/** Заглушка вместо ручки подписки: состояние живёт только в компоненте, на бэк ничего не уходит */
|
|
189
|
+
const isFollowing = ref(false);
|
|
190
|
+
|
|
193
191
|
function onFollowClick() {
|
|
194
192
|
if (isAuthorized.value) {
|
|
195
|
-
|
|
193
|
+
isFollowing.value = !isFollowing.value;
|
|
196
194
|
} else {
|
|
197
195
|
openAuthPopup(AuthSteps.Login, authPopupText);
|
|
198
196
|
}
|
|
@@ -464,4 +462,10 @@ const socialItems = computed<ILinkItem[]>(() => {
|
|
|
464
462
|
.info-main__header-button {
|
|
465
463
|
@include button-variant(info-button);
|
|
466
464
|
}
|
|
465
|
+
|
|
466
|
+
.info-main__header-follow {
|
|
467
|
+
@include to-br(sm) {
|
|
468
|
+
margin-inline-start: auto;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
467
471
|
</style>
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
class="library-feed__tags"
|
|
60
60
|
hydrate-on-visible
|
|
61
61
|
micro
|
|
62
|
+
hide-more
|
|
62
63
|
/>
|
|
63
64
|
|
|
64
65
|
<p
|
|
@@ -86,22 +87,17 @@
|
|
|
86
87
|
</p>
|
|
87
88
|
</div>
|
|
88
89
|
|
|
89
|
-
<div class="library-feed__actions
|
|
90
|
-
<
|
|
91
|
-
:
|
|
92
|
-
|
|
93
|
-
@
|
|
94
|
-
|
|
95
|
-
<UiIcon
|
|
96
|
-
:name="following ? 'check' : 'plus'"
|
|
97
|
-
size="16"
|
|
98
|
-
/>
|
|
99
|
-
{{ following ? t('library.following_state') : t('follow') }}
|
|
100
|
-
</UiBtn>
|
|
90
|
+
<div class="library-feed__actions">
|
|
91
|
+
<UiFollowBtn
|
|
92
|
+
:following="following"
|
|
93
|
+
:name="item.title"
|
|
94
|
+
@toggle="emit('toggle-follow', item)"
|
|
95
|
+
/>
|
|
101
96
|
|
|
102
97
|
<UiLink
|
|
103
98
|
:to="item.to"
|
|
104
99
|
theme="secondary"
|
|
100
|
+
class="_from-sm"
|
|
105
101
|
>
|
|
106
102
|
{{ videosText }}
|
|
107
103
|
<UiIcon
|
|
@@ -112,10 +108,10 @@
|
|
|
112
108
|
</div>
|
|
113
109
|
</header>
|
|
114
110
|
|
|
115
|
-
<h3
|
|
111
|
+
<!-- <h3
|
|
116
112
|
v-if="item.feedTitle"
|
|
117
113
|
class="library-feed__feed-title"
|
|
118
|
-
>{{ item.feedTitle }}</h3>
|
|
114
|
+
>{{ item.feedTitle }}</h3> -->
|
|
119
115
|
|
|
120
116
|
<div class="library-feed__grid">
|
|
121
117
|
<CardVideo
|
|
@@ -128,7 +124,7 @@
|
|
|
128
124
|
/>
|
|
129
125
|
</div>
|
|
130
126
|
|
|
131
|
-
<UiLink
|
|
127
|
+
<!-- <UiLink
|
|
132
128
|
:to="item.to"
|
|
133
129
|
theme="secondary"
|
|
134
130
|
class="library-feed__all _to-sm"
|
|
@@ -139,7 +135,7 @@
|
|
|
139
135
|
name="arrow-right"
|
|
140
136
|
size="16"
|
|
141
137
|
/>
|
|
142
|
-
</UiLink>
|
|
138
|
+
</UiLink> -->
|
|
143
139
|
</article>
|
|
144
140
|
</template>
|
|
145
141
|
|
|
@@ -185,9 +181,6 @@ const ageText = computed(() => {
|
|
|
185
181
|
|
|
186
182
|
<style lang="scss">
|
|
187
183
|
.library-feed {
|
|
188
|
-
/* Растушёвка «ещё» у тегов уходит в фон карточки, а не страницы */
|
|
189
|
-
--expand-row-fade-end: #{$library-feed-fade};
|
|
190
|
-
|
|
191
184
|
@include from-br(sm) {
|
|
192
185
|
padding: 20px;
|
|
193
186
|
border: 1px solid $library-feed-border;
|
|
@@ -198,23 +191,15 @@ const ageText = computed(() => {
|
|
|
198
191
|
|
|
199
192
|
.library-feed__header {
|
|
200
193
|
display: grid;
|
|
201
|
-
grid-template-columns: auto minmax(0, 1fr);
|
|
194
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
202
195
|
column-gap: 16px;
|
|
203
196
|
row-gap: 12px;
|
|
204
197
|
align-items: center;
|
|
205
198
|
margin-bottom: 20px;
|
|
206
|
-
|
|
207
|
-
@include from-br(sm) {
|
|
208
|
-
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
209
|
-
}
|
|
210
199
|
}
|
|
211
200
|
|
|
212
201
|
.library-feed__header.--without-avatar {
|
|
213
|
-
grid-template-columns: minmax(0, 1fr);
|
|
214
|
-
|
|
215
|
-
@include from-br(sm) {
|
|
216
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
217
|
-
}
|
|
202
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
218
203
|
}
|
|
219
204
|
|
|
220
205
|
.library-feed__avatar {
|
|
@@ -344,17 +329,13 @@ const ageText = computed(() => {
|
|
|
344
329
|
column-gap: 8px;
|
|
345
330
|
}
|
|
346
331
|
|
|
347
|
-
.library-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.library-feed__feed-title {
|
|
352
|
-
@include font-sm;
|
|
332
|
+
// .library-feed__feed-title {
|
|
333
|
+
// @include font-sm;
|
|
353
334
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
335
|
+
// margin: 0 0 12px;
|
|
336
|
+
// font-weight: 400;
|
|
337
|
+
// color: $library-feed-title;
|
|
338
|
+
// }
|
|
358
339
|
|
|
359
340
|
.library-feed__grid {
|
|
360
341
|
display: grid;
|
|
@@ -84,10 +84,21 @@ function isFollowing(item: ILibraryFeedItem) {
|
|
|
84
84
|
return !unfollowed.value.includes(item.guid);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function follow(guid: string) {
|
|
88
|
+
unfollowed.value = unfollowed.value.filter(item => item !== guid);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function unfollow(guid: string) {
|
|
92
|
+
unfollowed.value = [...unfollowed.value, guid];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Заглушка: подписка живёт в локальном стейте, API ещё нет. Снекбар с undo рисует сама кнопка. */
|
|
87
96
|
function onToggleFollow(item: ILibraryFeedItem) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
if (isFollowing(item)) {
|
|
98
|
+
unfollow(item.guid);
|
|
99
|
+
} else {
|
|
100
|
+
follow(item.guid);
|
|
101
|
+
}
|
|
91
102
|
}
|
|
92
103
|
</script>
|
|
93
104
|
|
|
@@ -106,6 +117,10 @@ function onToggleFollow(item: ILibraryFeedItem) {
|
|
|
106
117
|
|
|
107
118
|
.library-follows__list {
|
|
108
119
|
display: grid;
|
|
109
|
-
row-gap:
|
|
120
|
+
row-gap: 40px;
|
|
121
|
+
|
|
122
|
+
@include from-br(sm) {
|
|
123
|
+
row-gap: 24px;
|
|
124
|
+
}
|
|
110
125
|
}
|
|
111
126
|
</style>
|
|
@@ -261,7 +261,7 @@ const postVideoForm = computed(() => ({
|
|
|
261
261
|
duration: videoCard.value?.duration ?? 0,
|
|
262
262
|
}));
|
|
263
263
|
|
|
264
|
-
const { setErrorState,
|
|
264
|
+
const { setErrorState, show, showError } = useSnackbar();
|
|
265
265
|
|
|
266
266
|
async function onSaveClick() {
|
|
267
267
|
let newPlaylist: IPlaylistShort | undefined;
|
|
@@ -293,7 +293,7 @@ async function onSaveClick() {
|
|
|
293
293
|
newPlaylist,
|
|
294
294
|
});
|
|
295
295
|
closePopup();
|
|
296
|
-
|
|
296
|
+
show('video_added');
|
|
297
297
|
} catch (error) {
|
|
298
298
|
setErrorState(error);
|
|
299
299
|
console.log(error, 'error add video to playlist');
|