itube-specs 0.0.876 → 0.0.878
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/history.svg +1 -0
- package/assets/icons/common/library.svg +1 -0
- package/assets/icons/common/playlist.svg +1 -0
- package/assets/scss/base/_hacks.scss +0 -2
- package/components/player/playlist.vue +3 -3
- package/composables/use-next-video.ts +21 -0
- package/composables/use-playlist-mode.ts +31 -0
- package/layouts/default.vue +1 -1
- package/lib/scheme/navigation-items.ts +6 -32
- package/lib/scheme/profile-navigation-items.ts +3 -3
- package/package.json +1 -1
- package/pages/history.vue +6 -0
- package/pages/library.vue +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path><path d="M3 3v5h5"></path><path d="M12 7v5l4 2"></path></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 4m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M9 4m0 1a1 1 0 0 1 1 -1h2a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1z"></path><path d="M5 8h4"></path><path d="M9 16h4"></path><path d="M13.803 4.56l2.184 -.53c.562 -.135 1.133 .19 1.282 .732l3.695 13.418a1.02 1.02 0 0 1 -.634 1.219l-.133 .041l-2.184 .53c-.562 .135 -1.133 -.19 -1.282 -.732l-3.695 -13.418a1.02 1.02 0 0 1 .634 -1.219l.133 -.041z"></path><path d="M14 9l4 -1"></path><path d="M16 16l3.923 -.98"></path></svg>
|
|
@@ -0,0 +1 @@
|
|
|
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>
|
|
@@ -15,9 +15,7 @@ select:-webkit-autofill:focus {
|
|
|
15
15
|
box-shadow: 0 0 0 1000px transparent inset !important;
|
|
16
16
|
-webkit-text-fill-color: var(--input-autofill-text, #{$input-autofill-text}) !important;
|
|
17
17
|
color: var(--input-autofill-text, #{$input-autofill-text}) !important;
|
|
18
|
-
border: none !important;
|
|
19
18
|
transition: background-color 5000s ease-in-out 0s !important;
|
|
20
|
-
font-size: inherit !important;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
/**
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
:class="{'--active': playMode === 'cycle'}"
|
|
59
59
|
class="player-playlist__play-button"
|
|
60
60
|
type="button"
|
|
61
|
-
@click="
|
|
61
|
+
@click="setPlayMode('cycle')"
|
|
62
62
|
>
|
|
63
63
|
<UiIcon
|
|
64
64
|
name="cycle"
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
:class="{'--active': playMode === 'random'}"
|
|
70
70
|
class="player-playlist__play-button"
|
|
71
71
|
type="button"
|
|
72
|
-
@click="
|
|
72
|
+
@click="setPlayMode('random')"
|
|
73
73
|
>
|
|
74
74
|
<UiIcon
|
|
75
75
|
name="random"
|
|
@@ -188,7 +188,7 @@ const userProfileData = computed(() => useState<IProfileData>('profileData').val
|
|
|
188
188
|
const isOwner = computed(() => isAuthorized.value && !!userProfileData.value?.username && userProfileData.value.username === props.user);
|
|
189
189
|
|
|
190
190
|
const slug = useSlug();
|
|
191
|
-
const playMode
|
|
191
|
+
const { mode: playMode, setMode: setPlayMode } = usePlaylistMode();
|
|
192
192
|
|
|
193
193
|
const currentIndex = computed(() => {
|
|
194
194
|
const idx = visibleItems.value.findIndex(item => String(item.id) === String(slug.value));
|
|
@@ -34,6 +34,12 @@ export function useNextVideo({ data, suggestedVideos, basePath }: UseNextVideoPa
|
|
|
34
34
|
const cardKey = computed<'url' | 'id'>(() => featureFlags.FetchByUrlEnabled ? 'url' : 'id');
|
|
35
35
|
const currentCardId = computed(() => String(route.query.view || data()?.[cardKey.value] || ''));
|
|
36
36
|
|
|
37
|
+
// Режим воспроизведения плейлиста (тумблеры cycle/random в PlayerPlaylist).
|
|
38
|
+
// Random работает только на странице плейлиста — на обычной странице видео related
|
|
39
|
+
// всегда идёт по порядку.
|
|
40
|
+
const { mode: playMode } = usePlaylistMode();
|
|
41
|
+
const isRandom = computed(() => !!basePath() && playMode.value === 'random');
|
|
42
|
+
|
|
37
43
|
const currentItems = computed<IVideoCard[]>(() => suggestedVideos()?.items ?? []);
|
|
38
44
|
const currentPage = computed(() => suggestedVideos()?.currentPage ?? 1);
|
|
39
45
|
const allPages = computed(() => suggestedVideos()?.allPages ?? 1);
|
|
@@ -51,12 +57,27 @@ export function useNextVideo({ data, suggestedVideos, basePath }: UseNextVideoPa
|
|
|
51
57
|
nextPageItems.value = [];
|
|
52
58
|
}, { immediate: true });
|
|
53
59
|
|
|
60
|
+
// Случайный ролик в режиме random. Индекс фиксируем в ref и перекатываем при смене
|
|
61
|
+
// текущего ролика/списка/режима — чтобы ховер-превью и реальный переход вели на один
|
|
62
|
+
// и тот же ролик. Только на клиенте: Math.random в SSR даст рассинхрон гидрации.
|
|
63
|
+
// Текущий ролик в playlistUpcoming уже исключён, так что попасть в себя нельзя.
|
|
64
|
+
const randomIndex = ref(0);
|
|
65
|
+
|
|
66
|
+
if (import.meta.client) {
|
|
67
|
+
watch([currentCardId, currentItems, isRandom], () => {
|
|
68
|
+
const length = currentItems.value.length;
|
|
69
|
+
randomIndex.value = length > 1 ? Math.floor(Math.random() * length) : 0;
|
|
70
|
+
}, { immediate: true });
|
|
71
|
+
}
|
|
72
|
+
|
|
54
73
|
// Следующий ролик: за текущим в текущем листе; на конце листа — первый следующей
|
|
55
74
|
// страницы (если догружена); на самом конце всех страниц — первый ролик вообще.
|
|
56
75
|
const nextVideo = computed<IVideoCard | undefined>(() => {
|
|
57
76
|
const items = currentItems.value;
|
|
58
77
|
if (!items.length) return undefined;
|
|
59
78
|
|
|
79
|
+
if (isRandom.value) return items[randomIndex.value] ?? items[0];
|
|
80
|
+
|
|
60
81
|
const idx = currentIndex.value;
|
|
61
82
|
if (idx === -1) return items[0];
|
|
62
83
|
if (idx + 1 < items.length) return items[idx + 1];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type PlaylistMode = 'cycle' | 'random';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = 'playlist-mode';
|
|
4
|
+
|
|
5
|
+
function isPlaylistMode(value: unknown): value is PlaylistMode {
|
|
6
|
+
return value === 'cycle' || value === 'random';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Режим воспроизведения плейлиста (cycle/random). Состояние общее для тумблеров в
|
|
11
|
+
* PlayerPlaylist и логики next в useNextVideo, переживает перезагрузку — хранится в
|
|
12
|
+
* localStorage.
|
|
13
|
+
*
|
|
14
|
+
* Восстанавливаем в onMounted, а не в setup: на сервере localStorage нет, а правка
|
|
15
|
+
* значения до гидрации разошлась бы с отрендеренной разметкой (--active на тумблере).
|
|
16
|
+
*/
|
|
17
|
+
export function usePlaylistMode() {
|
|
18
|
+
const mode = useState<PlaylistMode>('playlistMode', () => 'cycle');
|
|
19
|
+
|
|
20
|
+
onMounted(() => {
|
|
21
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
22
|
+
if (isPlaylistMode(saved)) mode.value = saved;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function setMode(value: PlaylistMode): void {
|
|
26
|
+
mode.value = value;
|
|
27
|
+
if (import.meta.client) localStorage.setItem(STORAGE_KEY, value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { mode, setMode };
|
|
31
|
+
}
|
package/layouts/default.vue
CHANGED
|
@@ -28,7 +28,7 @@ export function getNavigationItems(): INavigationItems[] {
|
|
|
28
28
|
{
|
|
29
29
|
title: 'playlists',
|
|
30
30
|
link: '/playlists',
|
|
31
|
-
icon: '
|
|
31
|
+
icon: 'playlist',
|
|
32
32
|
},
|
|
33
33
|
...(featureFlags.FeatureLiveEnabled ? [{
|
|
34
34
|
title: 'live_sex',
|
|
@@ -38,41 +38,15 @@ export function getNavigationItems(): INavigationItems[] {
|
|
|
38
38
|
}] : []),
|
|
39
39
|
...(featureFlags.FeatureHistoryEnabled ? [{
|
|
40
40
|
title: 'history',
|
|
41
|
-
link: '/
|
|
42
|
-
icon: '
|
|
41
|
+
link: '/history',
|
|
42
|
+
icon: 'history',
|
|
43
43
|
top: true,
|
|
44
44
|
}] : []),
|
|
45
45
|
...(featureFlags.FeatureFavoritesEnabled ? [{
|
|
46
|
-
title: '
|
|
47
|
-
|
|
46
|
+
title: 'library',
|
|
47
|
+
link: '/library',
|
|
48
|
+
icon: 'library',
|
|
48
49
|
top: true,
|
|
49
|
-
childs: [
|
|
50
|
-
{
|
|
51
|
-
title: 'videos',
|
|
52
|
-
icon: 'video-camera',
|
|
53
|
-
link: '/favorites',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
title: 'watch_later',
|
|
57
|
-
icon: 'time',
|
|
58
|
-
link: '/2257',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
title: 'playlists',
|
|
62
|
-
icon: 'list',
|
|
63
|
-
link: '/2257',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
title: 'models',
|
|
67
|
-
icon: 'user',
|
|
68
|
-
link: '/2257',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
title: 'channels',
|
|
72
|
-
icon: 'play-circle',
|
|
73
|
-
link: '/2257',
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
50
|
}] : []),
|
|
77
51
|
];
|
|
78
52
|
}
|
|
@@ -7,8 +7,8 @@ export const profileNavigationItems = [
|
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
value: '/user/profile?edit=true',
|
|
10
|
-
title: '
|
|
11
|
-
icon: '
|
|
10
|
+
title: 'library',
|
|
11
|
+
icon: 'library',
|
|
12
12
|
group: 1,
|
|
13
13
|
childs: [
|
|
14
14
|
{
|
|
@@ -41,7 +41,7 @@ export const profileNavigationItems = [
|
|
|
41
41
|
{
|
|
42
42
|
value: '/user/settings',
|
|
43
43
|
title: 'edit_profile',
|
|
44
|
-
icon: '
|
|
44
|
+
icon: 'edit',
|
|
45
45
|
group: 2,
|
|
46
46
|
},
|
|
47
47
|
];
|
package/package.json
CHANGED