Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -69,7 +69,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand All @@ -91,7 +91,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x]
node-version: [20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
9 changes: 5 additions & 4 deletions docs-validation/1_introduction/NativeModules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ import * as ExpoFS from 'expo-file-system';
import * as ExpoImagePicker from 'expo-image-picker';
import * as ExpoMediaLibrary from 'expo-media-library';
import * as ExpoNotifications from 'expo-notifications';
import * as ExpoAV from 'expo-av';
import * as ExpoAudio from 'expo-audio';
import * as ExpoVideo from 'expo-video';
import * as ExpoVideoThumbnail from 'expo-video-thumbnails';
import * as ExpoImageManipulator from 'expo-image-manipulator';

Expand All @@ -82,16 +83,16 @@ const expoPlatformServices = {
documentPickerModule: ExpoDocumentPicker,
}),
media: createExpoMediaService({
avModule: ExpoAV,
avModule: ExpoVideo,
thumbnailModule: ExpoVideoThumbnail,
imageManipulator: ExpoImageManipulator,
fsModule: ExpoFS,
}),
player: createExpoPlayerService({
avModule: ExpoAV,
avModule: ExpoAudio,
}),
recorder: createExpoRecorderService({
avModule: ExpoAV,
avModule: ExpoAudio,
}),
};
/** ------------------ **/
11 changes: 11 additions & 0 deletions packages/uikit-react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@
"@types/react": "*",
"@types/react-native": "*",
"date-fns": ">=2.28.0",
"expo": "^54.0.12",
"expo-av": "^13.2.1",
"expo-audio": "^1.0.13",
"expo-video": "^3.0.11",
"expo-clipboard": "^4.1.2",
"expo-document-picker": "^11.5.3",
"expo-file-system": "^15.2.2",
Expand Down Expand Up @@ -118,6 +121,8 @@
"@sendbird/uikit-tools": ">=0.0.10",
"date-fns": ">=2.28.0",
"expo-av": ">=12.0.4",
"expo-audio": ">=1.0.0",
"expo-video": ">=3.0.0",
"expo-clipboard": ">=2.1.1",
"expo-document-picker": ">=10.1.3",
"expo-file-system": ">=13.1.4",
Expand Down Expand Up @@ -165,6 +170,12 @@
"expo-av": {
"optional": true
},
"expo-audio": {
"optional": true
},
"expo-video": {
"optional": true
},
"expo-clipboard": {
"optional": true
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,112 @@
import type * as ExpoAV from 'expo-av';
import type * as ExpoFS from 'expo-file-system';
import type * as ExpoImageManipulator from 'expo-image-manipulator';
import type { EventSubscription } from 'expo-modules-core';
import type * as ExpoVideo from 'expo-video';
import type { StatusChangeEventPayload } from 'expo-video';
import type * as ExpoVideoThumbnail from 'expo-video-thumbnails';
import React from 'react';
import React, { useEffect } from 'react';

import { getDownscaleSize } from '@sendbird/uikit-utils';
import { Logger, getDownscaleSize } from '@sendbird/uikit-utils';

import SBUUtils from '../libs/SBUUtils';
import expoBackwardUtils from '../utils/expoBackwardUtils';
import type { MediaServiceInterface } from './types';
import type { ExpoVideoModule } from '../utils/expoBackwardUtils';
import type { MediaServiceInterface, VideoProps } from './types';

type Modules = {
avModule: typeof ExpoAV;
avModule: ExpoVideoModule;
thumbnailModule: typeof ExpoVideoThumbnail;
imageManipulator: typeof ExpoImageManipulator;
fsModule: typeof ExpoFS;
};

interface VideoModuleAdapter {
VideoComponent: React.ComponentType<VideoProps>;
}

class LegacyExpoAVVideoAdapter implements VideoModuleAdapter {
private readonly avModule: typeof ExpoAV;
constructor(avModule: typeof ExpoAV) {
this.avModule = avModule;
}

VideoComponent = ({ source, resizeMode, onLoad, ...props }: VideoProps) => {
// FIXME: type error https://github.com/expo/expo/issues/17101
// @ts-ignore
return <this.avModule.Video {...props} source={source} resizeMode={resizeMode} onLoad={onLoad} useNativeControls />;
};
}

class ExpoVideoAdapter implements VideoModuleAdapter {
constructor(private readonly _videoModule: typeof ExpoVideo) {}

VideoComponent = ({ source, resizeMode, onLoad, ...props }: VideoProps) => {
const player = this._videoModule.useVideoPlayer(source);

useEffect(() => {
if (onLoad && player) {
let subscription: EventSubscription | null = null;
try {
subscription = player.addListener('statusChange', (eventData: StatusChangeEventPayload) => {
const { status, error } = eventData;
if (status === 'readyToPlay' && !error) {
onLoad();
}
});
} catch (error) {
const timeout = setTimeout(() => onLoad(), 300);
return () => clearTimeout(timeout);
}

return () => {
if (subscription) {
subscription.remove();
}
};
}
return undefined;
}, [onLoad, player]);

const getContentFit = (mode: typeof resizeMode): 'cover' | 'contain' | 'fill' => {
switch (mode) {
case 'cover':
return 'cover';
case 'contain':
return 'contain';
case 'stretch':
return 'fill';
default:
return 'contain';
}
};

return React.createElement(this._videoModule.VideoView, {
...props,
player,
contentFit: getContentFit(resizeMode),
});
};
}

const createExpoMediaService = ({
avModule,
thumbnailModule,
imageManipulator,
fsModule,
}: Modules): MediaServiceInterface => {
if (expoBackwardUtils.expoAV.isLegacyAVModule(avModule)) {
Logger.warn(
'[MediaService.Expo] expo-av is deprecated and will be removed in Expo 54. Please migrate to expo-video.',
);
}

const videoAdapter = expoBackwardUtils.expoAV.isVideoModule(avModule)
? new ExpoVideoAdapter(avModule)
: new LegacyExpoAVVideoAdapter(avModule);

return {
VideoComponent({ source, resizeMode, onLoad, ...props }) {
// FIXME: type error https://github.com/expo/expo/issues/17101
// @ts-ignore
return <avModule.Video {...props} source={source} resizeMode={resizeMode} onLoad={onLoad} useNativeControls />;
},
VideoComponent: videoAdapter.VideoComponent,
async getVideoThumbnail({ url, quality, timeMills }) {
try {
const { uri } = await thumbnailModule.getThumbnailAsync(url, { quality, time: timeMills });
Expand Down
Loading