itube-specs 0.0.899 → 0.0.902
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/clock-filled.svg +1 -0
- package/assets/scss/mixins/_breakpoints.scss +3 -2
- package/assets/scss/themes/dark/_theme.scss +17 -23
- package/assets/scss/themes/light/_theme.scss +17 -23
- package/assets/scss/vars/_palette.scss +1 -0
- package/components/card/category.vue +4 -8
- package/components/card/dropdown/index.vue +47 -9
- package/components/card/dropdown/trigger.vue +1 -0
- package/components/card/model.vue +1 -1
- package/components/card/playlist.vue +15 -6
- package/components/card/round.vue +3 -3
- package/components/card/video/actions.vue +123 -0
- package/components/card/video/index.vue +149 -30
- package/components/filter/selects.vue +1 -1
- package/components/info/grid.vue +1 -1
- package/components/layout/header-user.vue +1 -1
- package/components/library/feed.vue +65 -24
- package/components/library/follows.vue +8 -44
- package/components/library/item.vue +4 -4
- package/components/player/like.vue +1 -1
- package/components/player/playlist.vue +2 -2
- package/components/report/main.vue +20 -5
- package/components/ui/checkbox.vue +1 -1
- package/components/ui/dropdown.vue +1 -1
- package/components/ui/input.vue +2 -2
- package/components/ui/radio.vue +1 -1
- package/components/ui/toggle.vue +1 -1
- package/composables/use-report-popup.ts +26 -3
- package/composables/use-video-collections.ts +30 -0
- package/lib/scheme/profile-navigation-items.ts +1 -1
- package/package.json +1 -1
- package/pages/history.vue +12 -36
- package/pages/library/categories.vue +2 -1
- package/pages/library/channels.vue +1 -1
- package/pages/library/index.vue +3 -3
- package/pages/library/models.vue +4 -4
- package/stubs/library-follows.ts +19 -16
- package/types/library-feed.d.ts +6 -0
- package/types/video-card.d.ts +4 -0
- package/components/card/history.vue +0 -359
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Модалка жалобы на
|
|
1
|
+
<!-- Модалка жалобы на видео или плейлист: 2 шага (причина + анкета) с reCAPTCHA -->
|
|
2
2
|
<template>
|
|
3
3
|
<transition mode="out-in">
|
|
4
4
|
<UiPopup
|
|
@@ -11,7 +11,13 @@
|
|
|
11
11
|
>
|
|
12
12
|
<template #title>{{ activeStep !== 'list' && activeCategory ? t(`report_form.${activeCategory.title}`) : t('report') }}</template>
|
|
13
13
|
<template #fixedContent>
|
|
14
|
+
<CardPlaylistMini
|
|
15
|
+
v-if="reportTargetType === 'playlist'"
|
|
16
|
+
:card="reportedPlaylistCard"
|
|
17
|
+
class="report-main__video-card"
|
|
18
|
+
/>
|
|
14
19
|
<CardVideoMini
|
|
20
|
+
v-else
|
|
15
21
|
:card="reportedVideoCard"
|
|
16
22
|
class="report-main__video-card"
|
|
17
23
|
/>
|
|
@@ -163,7 +169,16 @@ const form = ref<IReportForm>({
|
|
|
163
169
|
data: {},
|
|
164
170
|
});
|
|
165
171
|
|
|
166
|
-
const {
|
|
172
|
+
const {
|
|
173
|
+
isReportPopupOpen,
|
|
174
|
+
closeReportPopup,
|
|
175
|
+
reportedVideoCard,
|
|
176
|
+
reportedPlaylistCard,
|
|
177
|
+
reportTargetType,
|
|
178
|
+
reportedUrl,
|
|
179
|
+
reportedGuid,
|
|
180
|
+
onBack,
|
|
181
|
+
} = useReportPopup();
|
|
167
182
|
|
|
168
183
|
const showBack = computed(() => activeStep.value !== 'list' || !!onBack.value);
|
|
169
184
|
|
|
@@ -196,7 +211,7 @@ watch(reasonValue, (val) => {
|
|
|
196
211
|
if (val) errorRadio.value = false;
|
|
197
212
|
});
|
|
198
213
|
|
|
199
|
-
const
|
|
214
|
+
const reportGuid = computed(() => reportedGuid.value);
|
|
200
215
|
|
|
201
216
|
const loading = ref(false);
|
|
202
217
|
|
|
@@ -294,14 +309,14 @@ async function submit() {
|
|
|
294
309
|
const baseURL = import.meta.client ? window.location.origin : '';
|
|
295
310
|
loading.value = true;
|
|
296
311
|
form.value.token = await getRecaptchaToken('contact_form');
|
|
297
|
-
form.value.url = `${baseURL}
|
|
312
|
+
form.value.url = `${baseURL}${reportedUrl.value}`;
|
|
298
313
|
|
|
299
314
|
if (reasonValue.value) {
|
|
300
315
|
form.value.data[ 'reason' ] = reasonValue.value;
|
|
301
316
|
}
|
|
302
317
|
emit('submit', {
|
|
303
318
|
form: form.value,
|
|
304
|
-
guid:
|
|
319
|
+
guid: reportGuid.value
|
|
305
320
|
});
|
|
306
321
|
closeReportPopup();
|
|
307
322
|
resetForm();
|
package/components/ui/input.vue
CHANGED
|
@@ -302,13 +302,13 @@ const normalizedValue = computed(() => {
|
|
|
302
302
|
height: 20px;
|
|
303
303
|
color: var(--input-icon, #{$input-icon});
|
|
304
304
|
|
|
305
|
-
@
|
|
305
|
+
@media (hover: hover) {
|
|
306
306
|
transition: var(--transition) color;
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
.ui-input__button:is(:hover, :focus-visible) .ui-input__button-icon {
|
|
311
|
-
@
|
|
311
|
+
@media (hover: hover) {
|
|
312
312
|
color: var(--input-button-text--hover, #{$input-button-text--hover});
|
|
313
313
|
}
|
|
314
314
|
}
|
package/components/ui/radio.vue
CHANGED
package/components/ui/toggle.vue
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
-
import type { IVideoCard } from '../types';
|
|
1
|
+
import type { IPlaylistCard, IVideoCard } from '../types';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export type ReportTargetType = 'video' | 'playlist';
|
|
4
|
+
|
|
5
|
+
/** Управляет состоянием попапа жалобы на видео или плейлист. */
|
|
4
6
|
export const useReportPopup = () => {
|
|
5
7
|
const isReportPopupOpen = useState('report-popup-open', () => false);
|
|
6
8
|
const reportedVideoCard = useState<IVideoCard>('report-popup-card', () => ({} as IVideoCard));
|
|
9
|
+
const reportedPlaylistCard = useState<IPlaylistCard>('report-popup-playlist-card', () => ({} as IPlaylistCard));
|
|
10
|
+
const reportTargetType = useState<ReportTargetType>('report-popup-target-type', () => 'video');
|
|
11
|
+
const reportedUrl = useState('report-popup-url', () => '');
|
|
12
|
+
const reportedGuid = useState('report-popup-guid', () => '');
|
|
7
13
|
const onBack = useState<(() => void) | null>('report-popup-on-back', () => null);
|
|
8
14
|
|
|
9
15
|
const openReportPopup = (card: IVideoCard, onBackCallback?: () => void) => {
|
|
10
|
-
|
|
16
|
+
reportTargetType.value = 'video';
|
|
11
17
|
reportedVideoCard.value = card;
|
|
18
|
+
reportedUrl.value = `/videos/${card.id}`;
|
|
19
|
+
reportedGuid.value = card.guid;
|
|
20
|
+
isReportPopupOpen.value = true;
|
|
21
|
+
onBack.value = onBackCallback ?? null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const openPlaylistReportPopup = (card: IPlaylistCard, onBackCallback?: () => void) => {
|
|
25
|
+
reportTargetType.value = 'playlist';
|
|
26
|
+
reportedPlaylistCard.value = card;
|
|
27
|
+
reportedUrl.value = card.firstVideoId ? `/playlists/${card.id}/${card.firstVideoId}` : `/playlists/${card.id}`;
|
|
28
|
+
reportedGuid.value = card.id;
|
|
29
|
+
isReportPopupOpen.value = true;
|
|
12
30
|
onBack.value = onBackCallback ?? null;
|
|
13
31
|
};
|
|
14
32
|
|
|
@@ -20,8 +38,13 @@ export const useReportPopup = () => {
|
|
|
20
38
|
return {
|
|
21
39
|
isReportPopupOpen,
|
|
22
40
|
openReportPopup,
|
|
41
|
+
openPlaylistReportPopup,
|
|
23
42
|
closeReportPopup,
|
|
24
43
|
reportedVideoCard,
|
|
44
|
+
reportedPlaylistCard,
|
|
45
|
+
reportTargetType,
|
|
46
|
+
reportedUrl,
|
|
47
|
+
reportedGuid,
|
|
25
48
|
onBack,
|
|
26
49
|
};
|
|
27
50
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Заглушка коллекций видео — избранное и «смотреть позже».
|
|
3
|
+
* Ручек в API пока нет, поэтому состояние живёт только на клиенте и сбрасывается при перезагрузке.
|
|
4
|
+
* Когда появятся эндпоинты, тут же меняем toggle-и на запросы, интерфейс наружу остаётся тем же.
|
|
5
|
+
*/
|
|
6
|
+
export const useVideoCollections = () => {
|
|
7
|
+
const favorites = useState<string[]>('video-collections-favorites', () => []);
|
|
8
|
+
const watchLater = useState<string[]>('video-collections-watch-later', () => []);
|
|
9
|
+
|
|
10
|
+
function toggle(list: Ref<string[]>, guid: string) {
|
|
11
|
+
list.value = list.value.includes(guid)
|
|
12
|
+
? list.value.filter(item => item !== guid)
|
|
13
|
+
: [ ...list.value, guid ];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const isFavorite = (guid: string) => favorites.value.includes(guid);
|
|
17
|
+
const isWatchLater = (guid: string) => watchLater.value.includes(guid);
|
|
18
|
+
|
|
19
|
+
const toggleFavorite = (guid: string) => toggle(favorites, guid);
|
|
20
|
+
const toggleWatchLater = (guid: string) => toggle(watchLater, guid);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
favorites,
|
|
24
|
+
watchLater,
|
|
25
|
+
isFavorite,
|
|
26
|
+
isWatchLater,
|
|
27
|
+
toggleFavorite,
|
|
28
|
+
toggleWatchLater,
|
|
29
|
+
};
|
|
30
|
+
};
|
package/package.json
CHANGED
package/pages/history.vue
CHANGED
|
@@ -49,23 +49,23 @@
|
|
|
49
49
|
:key="group.key"
|
|
50
50
|
class="history__group"
|
|
51
51
|
>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</header>
|
|
52
|
+
<UiSectionTitle
|
|
53
|
+
:title="getGroupTitle(group)"
|
|
54
|
+
:count="n(group.items.length, 'compact')"
|
|
55
|
+
class="history__group-header"
|
|
56
|
+
icon="video-camera"
|
|
57
|
+
title-tag="h2"
|
|
58
|
+
size="xl"
|
|
59
|
+
/>
|
|
61
60
|
|
|
62
61
|
<div class="history__grid">
|
|
63
|
-
<
|
|
62
|
+
<CardVideo
|
|
64
63
|
v-for="(item, index) in group.items"
|
|
65
64
|
:key="`history-${item.guid}`"
|
|
66
65
|
:card="item"
|
|
67
66
|
:priority="groupIndex === 0 && index < 4"
|
|
68
67
|
:loading="groupIndex === 0 && index < 4 ? 'eager' : 'lazy'"
|
|
68
|
+
history
|
|
69
69
|
@share="onShare"
|
|
70
70
|
@remove="onRemove"
|
|
71
71
|
/>
|
|
@@ -124,14 +124,13 @@
|
|
|
124
124
|
<script setup lang="ts">
|
|
125
125
|
import { historyPeriods } from '../lib';
|
|
126
126
|
import { getHistoryStub } from '../stubs/history';
|
|
127
|
-
import type { HistoryStubCard } from '../stubs/history';
|
|
128
127
|
import { groupByWatchedDay } from '../utils/history/group-by-watched-day';
|
|
129
128
|
import type { HistoryDayGroup } from '../utils/history/group-by-watched-day';
|
|
130
129
|
import type { IBreadcrumbItem, IVideoCard } from '../types';
|
|
131
130
|
|
|
132
131
|
const PER_PAGE = 24;
|
|
133
132
|
|
|
134
|
-
const { t, locale } = useI18n();
|
|
133
|
+
const { t, n, locale } = useI18n();
|
|
135
134
|
const route = useRoute();
|
|
136
135
|
|
|
137
136
|
const isFilterOpen = ref(false);
|
|
@@ -169,7 +168,7 @@ function getGroupTitle(group: HistoryDayGroup) {
|
|
|
169
168
|
});
|
|
170
169
|
}
|
|
171
170
|
|
|
172
|
-
function onRemove(card:
|
|
171
|
+
function onRemove(card: IVideoCard) {
|
|
173
172
|
removedGuids.value = [...removedGuids.value, card.guid];
|
|
174
173
|
}
|
|
175
174
|
|
|
@@ -235,32 +234,9 @@ const breadcrumbs = [
|
|
|
235
234
|
}
|
|
236
235
|
|
|
237
236
|
.history__group-header {
|
|
238
|
-
display: flex;
|
|
239
|
-
align-items: center;
|
|
240
|
-
column-gap: 8px;
|
|
241
237
|
margin-bottom: 16px;
|
|
242
238
|
}
|
|
243
239
|
|
|
244
|
-
.history__group-icon {
|
|
245
|
-
flex-shrink: 0;
|
|
246
|
-
color: $page-history-group-icon;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.history__group-title {
|
|
250
|
-
@include font-lg;
|
|
251
|
-
|
|
252
|
-
margin: 0;
|
|
253
|
-
font-weight: 600;
|
|
254
|
-
color: $page-history-group-title;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
.history__group-count {
|
|
258
|
-
@include font-sm;
|
|
259
|
-
|
|
260
|
-
font-weight: 400;
|
|
261
|
-
color: $page-history-group-count;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
240
|
.history__grid {
|
|
265
241
|
position: relative;
|
|
266
242
|
display: grid;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:items="items"
|
|
11
11
|
:title="t('library.my_categories')"
|
|
12
12
|
icon="grid"
|
|
13
|
+
without-avatar
|
|
13
14
|
/>
|
|
14
15
|
</div>
|
|
15
16
|
</template>
|
|
@@ -29,7 +30,7 @@ const items = computed<ILibraryFeedItem[]>(() => getLibraryCategoriesStub().map(
|
|
|
29
30
|
newCount: category.newCount,
|
|
30
31
|
meta: [
|
|
31
32
|
{ icon: 'video-camera', text: String(category.videosCount) },
|
|
32
|
-
{ icon: 'date', text: t('library.updated_at', { date: formatDate(category.lastVideoAddedAt) }) },
|
|
33
|
+
{ icon: 'date', text: t('library.updated_at', { date: formatDate(category.lastVideoAddedAt) }), desktopOnly: true },
|
|
33
34
|
],
|
|
34
35
|
videosCount: category.videosCount,
|
|
35
36
|
// Нечего показать в новинках — лента откатывается на лучшее за неделю
|
|
@@ -32,7 +32,7 @@ const items = computed<ILibraryFeedItem[]>(() => getLibraryChannelsStub().map(ch
|
|
|
32
32
|
meta: [
|
|
33
33
|
{ icon: 'video-camera', text: String(channel.videosCount) },
|
|
34
34
|
{ icon: 'user-group', text: t('library.followers', { count: n(channel.followersCount, 'compact') }) },
|
|
35
|
-
{ icon: 'date', text: t('library.updated_at', { date: formatDate(channel.lastVideoAddedAt) }) },
|
|
35
|
+
{ icon: 'date', text: t('library.updated_at', { date: formatDate(channel.lastVideoAddedAt) }), desktopOnly: true },
|
|
36
36
|
],
|
|
37
37
|
videosCount: channel.videosCount,
|
|
38
38
|
videos: channel.videos,
|
package/pages/library/index.vue
CHANGED
|
@@ -41,12 +41,13 @@
|
|
|
41
41
|
</header>
|
|
42
42
|
|
|
43
43
|
<div class="library__continue">
|
|
44
|
-
<
|
|
44
|
+
<CardVideo
|
|
45
45
|
v-for="item in continueWatching"
|
|
46
46
|
:key="`continue-${item.guid}`"
|
|
47
47
|
:card="item"
|
|
48
48
|
loading="eager"
|
|
49
49
|
priority
|
|
50
|
+
history
|
|
50
51
|
remaining
|
|
51
52
|
@share="onShare"
|
|
52
53
|
@remove="onRemove"
|
|
@@ -134,7 +135,6 @@
|
|
|
134
135
|
|
|
135
136
|
<script setup lang="ts">
|
|
136
137
|
import { getHistoryStub } from '../../stubs/history';
|
|
137
|
-
import type { HistoryStubCard } from '../../stubs/history';
|
|
138
138
|
import { libraryStub } from '../../stubs/library';
|
|
139
139
|
import type { IBreadcrumbItem, IVideoCard } from '../../types';
|
|
140
140
|
|
|
@@ -230,7 +230,7 @@ const following = computed<LibraryLink[]>(() => [
|
|
|
230
230
|
},
|
|
231
231
|
]);
|
|
232
232
|
|
|
233
|
-
function onRemove(card:
|
|
233
|
+
function onRemove(card: IVideoCard) {
|
|
234
234
|
removedGuids.value = [...removedGuids.value, card.guid];
|
|
235
235
|
}
|
|
236
236
|
|
package/pages/library/models.vue
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- Подписки на моделей: аватар,
|
|
1
|
+
<!-- Подписки на моделей: аватар, параметры (тип/телосложение/этнос), лента последних роликов. Данные пока заглушка -->
|
|
2
2
|
<template>
|
|
3
3
|
<div class="_page _container">
|
|
4
4
|
<UiBreadcrumbs
|
|
@@ -28,12 +28,12 @@ const items = computed<ILibraryFeedItem[]>(() => getLibraryModelsStub().map(mode
|
|
|
28
28
|
to: generateLink(`/models/${convertString().toSlug(model.title)}`),
|
|
29
29
|
avatarUrl: model.avatarUrl,
|
|
30
30
|
country: model.country,
|
|
31
|
-
|
|
31
|
+
parameters: [model.guyType, model.bodyBuild, model.ethnicity],
|
|
32
|
+
age: `${model.age} ${t('yo')}`,
|
|
32
33
|
newCount: model.newCount,
|
|
33
34
|
meta: [
|
|
34
35
|
{ icon: 'video-camera', text: String(model.videosCount) },
|
|
35
|
-
{ text:
|
|
36
|
-
{ icon: 'date', text: t('library.updated_at', { date: formatDate(model.lastVideoAddedAt) }) },
|
|
36
|
+
{ icon: 'date', text: t('library.updated_at', { date: formatDate(model.lastVideoAddedAt) }), desktopOnly: true },
|
|
37
37
|
],
|
|
38
38
|
videosCount: model.videosCount,
|
|
39
39
|
videos: model.videos,
|
package/stubs/library-follows.ts
CHANGED
|
@@ -32,7 +32,10 @@ export type LibraryModelStub = LibraryFollowBase & {
|
|
|
32
32
|
title: string
|
|
33
33
|
};
|
|
34
34
|
age: number;
|
|
35
|
-
|
|
35
|
+
/** Параметры модели из API: guy_type, body_build, ethnicity */
|
|
36
|
+
guyType: string;
|
|
37
|
+
bodyBuild: string;
|
|
38
|
+
ethnicity: string;
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
export type LibraryChannelStub = LibraryFollowBase & {
|
|
@@ -65,21 +68,21 @@ function buildVideos(slug: string, offset: number, newCount: number): LibraryFol
|
|
|
65
68
|
|
|
66
69
|
/** Порядок по числу новинок намеренно не совпадает с алфавитным — иначе обе сортировки дают одно и то же. */
|
|
67
70
|
const MODELS = [
|
|
68
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
69
|
-
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2,
|
|
70
|
-
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1,
|
|
71
|
-
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0,
|
|
72
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
73
|
-
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2,
|
|
74
|
-
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1,
|
|
75
|
-
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0,
|
|
76
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
77
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
78
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
79
|
-
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3,
|
|
80
|
-
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2,
|
|
81
|
-
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1,
|
|
82
|
-
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0,
|
|
71
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
72
|
+
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2, guyType: 'Daddy', bodyBuild: 'Muscular', ethnicity: 'Latino', updatedAgo: 2 * DAY, videoOffset: 9 },
|
|
73
|
+
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1, guyType: 'Twink', bodyBuild: 'Athletic', ethnicity: 'Caucasian', updatedAgo: 9 * DAY, videoOffset: 0 },
|
|
74
|
+
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0, guyType: 'Jock', bodyBuild: 'Muscular', ethnicity: 'Black', updatedAgo: 21 * DAY, videoOffset: 6 },
|
|
75
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
76
|
+
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2, guyType: 'Daddy', bodyBuild: 'Muscular', ethnicity: 'Latino', updatedAgo: 2 * DAY, videoOffset: 9 },
|
|
77
|
+
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1, guyType: 'Twink', bodyBuild: 'Athletic', ethnicity: 'Caucasian', updatedAgo: 9 * DAY, videoOffset: 0 },
|
|
78
|
+
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0, guyType: 'Jock', bodyBuild: 'Muscular', ethnicity: 'Black', updatedAgo: 21 * DAY, videoOffset: 6 },
|
|
79
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
80
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
81
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
82
|
+
{ title: 'Dakota Lovell', image: 'Dakota_Lovell.png', country: { name: 'us', title: 'USA' }, age: 22, videosCount: 41, newCount: 3, guyType: 'Twink', bodyBuild: 'Slim', ethnicity: 'Caucasian', updatedAgo: 6 * HOUR, videoOffset: 3 },
|
|
83
|
+
{ title: 'Jax Thirio', image: 'Jax_Thirio.png', country: { name: 'ca', title: 'Canada' }, age: 38, videosCount: 17, newCount: 2, guyType: 'Daddy', bodyBuild: 'Muscular', ethnicity: 'Latino', updatedAgo: 2 * DAY, videoOffset: 9 },
|
|
84
|
+
{ title: 'Austin Young', image: 'Austin_Young.png', country: { name: 'us', title: 'USA' }, age: 24, videosCount: 75, newCount: 1, guyType: 'Twink', bodyBuild: 'Athletic', ethnicity: 'Caucasian', updatedAgo: 9 * DAY, videoOffset: 0 },
|
|
85
|
+
{ title: 'Devin Franco', image: 'Devin_Franco.png', country: { name: 'us', title: 'USA' }, age: 33, videosCount: 24, newCount: 0, guyType: 'Jock', bodyBuild: 'Muscular', ethnicity: 'Black', updatedAgo: 21 * DAY, videoOffset: 6 },
|
|
83
86
|
];
|
|
84
87
|
|
|
85
88
|
const CHANNELS = [
|
package/types/library-feed.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import type { IVideoCard } from './video-card';
|
|
|
5
5
|
export interface ILibraryFeedMeta {
|
|
6
6
|
icon?: string
|
|
7
7
|
text: string
|
|
8
|
+
/** Второстепенный счётчик: на мобиле в строке не помещается и скрывается */
|
|
9
|
+
desktopOnly?: boolean
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -24,6 +26,10 @@ export interface ILibraryFeedItem {
|
|
|
24
26
|
title: string
|
|
25
27
|
}
|
|
26
28
|
tags?: IChipsItem[]
|
|
29
|
+
/** Строка параметров модели вместо тегов: «Jock · Muscular · Latino» */
|
|
30
|
+
parameters?: string[]
|
|
31
|
+
/** Возраст с суффиксом («34 y.o.») — печатается в хвосте строки параметров приглушённым цветом */
|
|
32
|
+
age?: string
|
|
27
33
|
/** Роликов с прошлого захода: плашка «N new» и порядок в сортировке «сначала новые». 0 — плашки нет */
|
|
28
34
|
newCount: number
|
|
29
35
|
meta: ILibraryFeedMeta[]
|
package/types/video-card.d.ts
CHANGED
|
@@ -18,4 +18,8 @@ export interface IVideoCard {
|
|
|
18
18
|
thumbNum?: number;
|
|
19
19
|
models?: string[];
|
|
20
20
|
thumbUrls?: IThumbUrls;
|
|
21
|
+
/** Доля просмотренного, 0–100 — рисует полосу прогресса на постере */
|
|
22
|
+
progress?: number;
|
|
23
|
+
/** Unix timestamp (сек.) последнего просмотра */
|
|
24
|
+
watchedAt?: number;
|
|
21
25
|
}
|