itube-specs 0.0.876 → 0.0.877
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.
|
@@ -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
|
+
}
|