Skip to content

Commit

Permalink
cleanup: remove extra screen from events
Browse files Browse the repository at this point in the history
  • Loading branch information
billyjacoby committed Oct 13, 2023
1 parent f1d2adc commit 7ce1330
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Though this is built in React Native, the immediate focus is to build an iOS nat
Though this project is still in the very early stages, it is maturing quickly. The items below are what is next on the roadmap for feature implementation:

- [x] Event viewing
- [ ] Recording viewing
- [x] Recording viewing
- [ ] View & update config
- [ ] Local user settings
- [ ] User onboarding
Expand Down
5 changes: 4 additions & 1 deletion src/components/VideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ export const VideoPlayer = ({
isPaused = true,
snapshotURL,
isForcedFullscreen,
onError,
}: {
videoURI: string;
isPaused?: boolean;
snapshotURL?: string;
isForcedFullscreen?: boolean;
onError?: Video['props']['onError'];
}) => {
React.useEffect(() => {}, []);
return (
Expand All @@ -24,8 +26,9 @@ export const VideoPlayer = ({
paused={isPaused}
pictureInPicture={true}
controls
source={{uri: videoURI}}
source={{uri: videoURI, type: 'm3u8'}}
fullscreen={isForcedFullscreen}
onError={onError || console.error}
/>
);
};
56 changes: 44 additions & 12 deletions src/components/snapshotCard.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import {Image, ImageURISource, useWindowDimensions, View} from 'react-native';
import {
Image,
ImageURISource,
TouchableOpacity,
useColorScheme,
useWindowDimensions,
View,
} from 'react-native';

import clsx from 'clsx';

import {BaseText} from './baseText';
import {Label} from './label';
import {FrigateEvent} from '@api';
import {PlayRect} from '@icons';

import {colors, hslFunction} from '../../themeColors';

export const SnapshotCard = ({
imageSource,
camEvent,
imageOverlay,
addtlClasses,
onPlayPress,
}: {
imageSource: ImageURISource['uri'];
camEvent?: FrigateEvent & {lastEventEnded: string};
imageOverlay?: React.ReactNode;
addtlClasses?: string;
onPlayPress?: () => void;
}) => {
const {width} = useWindowDimensions();
const imageWidth = width;
const imageHeight = imageWidth * 0.75;

const isDarkMode = useColorScheme() === 'dark';

return (
<View className={clsx('rounded-xl', addtlClasses)}>
<Image
Expand All @@ -31,20 +45,38 @@ export const SnapshotCard = ({
/>
{!!imageOverlay && imageOverlay}
{!!camEvent && (
<View
className="justify-between flex-row absolute p-1"
style={{width: imageWidth}}>
<Label>
<BaseText className="text-md font-semibold">
{camEvent.label.replaceAll('_', ' ').toLocaleUpperCase()}
</BaseText>
</Label>
{!!camEvent.lastEventEnded && (
<View className="absolute w-full h-full">
<View
className="flex-row justify-between absolute p-1"
style={{width: imageWidth}}>
<Label>
<BaseText className="text-xs text-mutedForeground dark:text-mutedForeground-dark">
{camEvent.lastEventEnded}
<BaseText className="text-md font-semibold">
{camEvent.label.replaceAll('_', ' ').toLocaleUpperCase()}
</BaseText>
</Label>
{!!camEvent.lastEventEnded && (
<Label>
<BaseText className="text-xs text-mutedForeground dark:text-mutedForeground-dark">
{camEvent.lastEventEnded}
</BaseText>
</Label>
)}
</View>
{!!onPlayPress && (
<TouchableOpacity
className="mt-auto ml-auto mr-2 mb-2 rounded-lg p-2 bg-background dark:bg-background-dark opacity-60 items-center justify-center flex"
onPress={onPlayPress}>
<PlayRect
height={64}
width={64}
className="-mb-3.5"
fill={
isDarkMode
? hslFunction(colors.dark.foreground)
: hslFunction(colors.light.foreground)
}
/>
</TouchableOpacity>
)}
</View>
)}
Expand Down
58 changes: 16 additions & 42 deletions src/screens/CameraScreens/EventsScreen/components/CameraEvent.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import React from 'react';
import {
NativeScrollEvent,
NativeSyntheticEvent,
Pressable,
ScrollView,
useWindowDimensions,
} from 'react-native';
import {ScrollView, useWindowDimensions} from 'react-native';

import {useNavigation} from '@react-navigation/native';
import clsx from 'clsx';

import {EventDetails} from './EventDetails';
import {FrigateEvent} from '@api';
import {BaseView, SnapshotCard, VideoPlayer} from '@components';
import {BaseView, SnapshotCard} from '@components';

export const CameraEvent = ({
camEvent,
Expand All @@ -24,9 +19,9 @@ export const CameraEvent = ({
const imageWidth = width * 0.97;
const imageHeight = imageWidth * 0.75;

const scrollviewRef = React.useRef<ScrollView>(null);
const navigation = useNavigation();

const [videoIsPaused, setVideoIsPaused] = React.useState(true);
const scrollviewRef = React.useRef<ScrollView>(null);

const getDateString = (date: Date) => {
return (
Expand All @@ -40,21 +35,12 @@ export const CameraEvent = ({
);
};

const onEventPress = () => {
scrollviewRef?.current?.scrollToEnd();
setVideoIsPaused(false);
};

const onScrollEnd = (event: NativeSyntheticEvent<NativeScrollEvent>) => {
if (scrollviewRef.current) {
const xOffset = event.nativeEvent.contentOffset.x;
const scrollIndex = Math.round(xOffset / width);
if (scrollIndex === 2) {
setVideoIsPaused(false);
} else {
setVideoIsPaused(true);
}
}
const onPlayPress = () => {
// Open fullscreen player
navigation.navigate('Fullscreen Video', {
videoURI: camEvent.vodURL,
title: 'Event',
});
};

const lastEventEnded = getDateString(new Date(camEvent?.end_time * 1000));
Expand All @@ -65,7 +51,6 @@ export const CameraEvent = ({
return (
<ScrollView
ref={scrollviewRef}
onMomentumScrollEnd={onScrollEnd}
horizontal
contentOffset={{x: width, y: 0}}
showsHorizontalScrollIndicator={false}
Expand All @@ -81,24 +66,13 @@ export const CameraEvent = ({
{/* //? snapshot in middle, default view */}
<BaseView style={{width, height: imageHeight}}>
{(lastEventImage || lastThumbnail) && (
<Pressable onPress={onEventPress}>
<SnapshotCard
camEvent={{...camEvent, lastEventEnded}}
imageSource={lastEventImage || lastThumbnail}
/>
</Pressable>
<SnapshotCard
camEvent={{...camEvent, lastEventEnded}}
imageSource={lastEventImage || lastThumbnail}
onPlayPress={onPlayPress}
/>
)}
</BaseView>

{/* //? if there's a video, show that to the right */}
<BaseView style={{width, height: imageHeight}}>
<VideoPlayer
key={camEvent.id}
videoURI={camEvent.vodURL}
isPaused={videoIsPaused}
snapshotURL={lastEventImage || lastThumbnail}
/>
</BaseView>
</ScrollView>
);
};
Expand Down
23 changes: 17 additions & 6 deletions src/screens/FullscreenVideo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import {NativeStackScreenProps} from '@react-navigation/native-stack';

import {BaseView, VideoPlayer} from '@components';
import {BaseText, BaseView, VideoPlayer} from '@components';
import {MainStackParamList} from '@navigation';

type FullscreenVideoProps = NativeStackScreenProps<
Expand All @@ -15,18 +15,29 @@ export const FullscreenVideoPlayer = ({
navigation,
}: FullscreenVideoProps) => {
const title = route.params.title || '';
const [hasError, setHasError] = React.useState<boolean>(false);

React.useEffect(() => {
navigation.setOptions({title});
}, [navigation, title]);

return (
<BaseView className="flex-1">
<VideoPlayer
videoURI={route.params.videoURI}
isForcedFullscreen
isPaused={false}
/>
{hasError ? (
<BaseText className="text-destructive dark:text-destructive-dark">
Error loading video.
</BaseText>
) : (
<VideoPlayer
videoURI={route.params.videoURI}
isForcedFullscreen
isPaused={false}
onError={data => {
setHasError(true);
console.error(data, route.params.videoURI);
}}
/>
)}
</BaseView>
);
};

0 comments on commit 7ce1330

Please sign in to comment.