first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
import {StateCreator} from 'zustand';
import {
PlayerState,
ProviderListeners,
} from '@common/player/state/player-state';
import {ScreenOrientation} from '@common/player/state/fullscreen/screen-orientation';
import {IS_IPHONE} from '@common/utils/platform';
import {FullscreenAdapter} from '@common/player/state/fullscreen/fullscreen-adapter';
import {createNativeFullscreenAdapter} from '@common/player/state/fullscreen/create-native-fullscreen-adapter';
import {createIphoneFullscreenAdapter} from '@common/player/state/fullscreen/create-iphone-fullscreen-adapter';
import {PipSlice} from '@common/player/state/pip/pip-slice';
export interface FullscreenSlice {
isFullscreen: boolean;
canFullscreen: boolean;
enterFullscreen: () => void;
exitFullscreen: () => void;
toggleFullscreen: () => void;
initFullscreen: () => void;
destroyFullscreen: () => void;
}
type BaseSliceCreator = StateCreator<
FullscreenSlice & PlayerState & PipSlice,
[['zustand/immer', unknown]],
[],
FullscreenSlice
>;
type StoreLice = BaseSliceCreator extends (...a: infer U) => infer R
? (...a: [...U, Set<Partial<ProviderListeners>>]) => R
: never;
const iPhoneProviderBlacklist = ['youtube'];
export const createFullscreenSlice: StoreLice = (set, get) => {
let subscription: () => void | undefined;
const orientation = new ScreenOrientation();
let adapter: FullscreenAdapter | undefined;
const onFullscreenChange = async () => {
const isFullscreen = adapter?.isFullscreen();
if (isFullscreen) {
// lock orientation to landscape
orientation.lock();
} else {
orientation.unlock();
}
set({isFullscreen});
};
const isSupported = (): boolean => {
// iPhone only allows putting video element in fullscreen, and
// there's no way to get access to it with YouTube iframe api
if (IS_IPHONE && iPhoneProviderBlacklist.includes(get().providerName!)) {
return false;
}
return adapter?.canFullScreen() ?? false;
};
return {
isFullscreen: false,
canFullscreen: false,
enterFullscreen: () => {
if (!isSupported() || adapter?.isFullscreen()) return;
// exit pip if it's active
if (get().isPip) {
get().exitPip();
}
return adapter?.enter();
},
exitFullscreen: () => {
if (!adapter?.isFullscreen()) return;
return adapter.exit();
},
toggleFullscreen: () => {
if (get().isFullscreen) {
get().exitFullscreen();
} else {
get().enterFullscreen();
}
},
initFullscreen: () => {
subscription = get().subscribe({
providerReady: ({el}) => {
// when changing adapters, remove previous adapter events and exit fullscreen
adapter?.unbindEvents();
if (get().isFullscreen) {
adapter?.exit();
}
// create new adapter, and if fullscreen is supported, bind events
adapter = IS_IPHONE
? createIphoneFullscreenAdapter(
el as HTMLVideoElement,
onFullscreenChange
)
: createNativeFullscreenAdapter(el, onFullscreenChange);
const canFullscreen = isSupported();
set({canFullscreen});
if (canFullscreen) {
adapter.bindEvents();
}
},
});
},
destroyFullscreen: () => {
get().exitFullscreen();
subscription?.();
},
};
};