Skip to content

Commit

Permalink
feat(apps/mobile): track when an image is being uploaded
Browse files Browse the repository at this point in the history
  • Loading branch information
hassankhan committed Oct 14, 2024
1 parent dae7cff commit 1c1b5de
Show file tree
Hide file tree
Showing 16 changed files with 375 additions and 94 deletions.
2 changes: 1 addition & 1 deletion apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@
"ios": "expo run:ios"
},
"devDependencies": {}
}
}
61 changes: 51 additions & 10 deletions apps/mobile/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { useCallback, useState } from 'react';
import { ActivityIndicator, View } from 'react-native';
import { View } from 'react-native';
import { createStyleSheet, useStyles } from 'react-native-unistyles';
import { match, P } from 'ts-pattern';
import { ActivityIndicator } from '../components/ActivityIndicator';

import { UploadButton } from '../components/UploadButton';
import { CatCardSkeleton } from '../features/CatCard/CatCardSkeleton';
import { ImageList } from '../features/HomePage/ImageList';
import { ImageListWrapper } from '../features/HomePage/ImageListWrapper';
import { NoImagesFound } from '../features/HomePage/NoImagesFound';
import { UploadImageSheet } from '../features/UploadImageModal/UploadImageSheet';
import { useAppSelector } from '../store/overrides';
import { getIsImageUploading } from '../store/selectors/getIsImageUploading';
import {
useGetMyFavouritesQuery,
useGetMyImagesQuery,
Expand All @@ -20,29 +25,65 @@ const Home = () => {
);
const { isLoading: isFavouritesLoading } = useGetMyFavouritesQuery();
const { isLoading: isVotesLoading } = useGetMyVotesQuery();

const isLoading = isImagesLoading || isFavouritesLoading || isVotesLoading;

const isImageUploading = useAppSelector(getIsImageUploading);
const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false);

const handleUploadButtonPress = useCallback(() => {
setIsBottomSheetOpen(true);
}, []);

const handleBottomSheetClose = useCallback(() => {
setIsBottomSheetOpen(false);
}, []);

const isLoading = isImagesLoading || isFavouritesLoading || isVotesLoading;

return (
<View style={styles.container}>
{match({ isLoading, images })
{match({ isLoading, isImageUploading, images })
.with({ isLoading: true, images: P.any }, () => (
<ActivityIndicator size="large" color="blue" />
<ActivityIndicator expand size="large" />
))
.with({ isLoading: false, images: [] }, () => <NoImagesFound />)
.with({ isLoading: false, images: [P.any, ...P.array()] }, () => (
<ImageList />
.with({ isLoading: false, isImageUploading: false, images: [] }, () => (
<NoImagesFound />
))
.with(
{
isLoading: false,
isImageUploading: true,
images: [],
},
() => (
<ImageListWrapper>
<CatCardSkeleton />
<ImageList />
</ImageListWrapper>
)
)
.with(
{
isLoading: false,
isImageUploading: false,
images: [P.any, ...P.array()],
},
() => (
<ImageListWrapper>
<ImageList />
</ImageListWrapper>
)
)
.with(
{
isLoading: false,
isImageUploading: true,
images: [P.any, ...P.array()],
},
() => (
<ImageListWrapper>
<CatCardSkeleton />
<ImageList />
</ImageListWrapper>
)
)
.exhaustive()}
<View style={styles.overlay}>
<UploadButton onPress={handleUploadButtonPress} />
Expand Down
39 changes: 39 additions & 0 deletions apps/mobile/src/components/ActivityIndicator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ComponentProps } from 'react';
import { ActivityIndicator as RNActivityIndicator, View } from 'react-native';
import {
createStyleSheet,
UnistylesVariants,
useStyles,
} from 'react-native-unistyles';

export type ActivityIndicatorProps = ComponentProps<
typeof RNActivityIndicator
> &
UnistylesVariants<typeof stylesheet>;

export const ActivityIndicator = ({
expand = false,
size = 'large',
}: ActivityIndicatorProps) => {
const { styles, theme } = useStyles(stylesheet, { expand });
return (
<View style={styles.root}>
<RNActivityIndicator size={size} color={theme.colors.typography.$5} />
</View>
);
};

const stylesheet = createStyleSheet((theme) => ({
root: {
variants: {
expand: {
false: {},
true: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
},
},
},
}));
138 changes: 95 additions & 43 deletions apps/mobile/src/components/CatButton/CatButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import { GestureResponderEvent, Pressable, View } from 'react-native';
import Animated, {
interpolate,
useAnimatedStyle,
useSharedValue, withDelay,
useSharedValue,
withDelay,
withRepeat,
withSequence, withSpring, withTiming
withSequence,
withSpring,
withTiming,
} from 'react-native-reanimated';
import { useCallback, useEffect } from 'react';

type CatButtonProps = {
onPress: ((event: GestureResponderEvent) => void) | null | undefined;
}

const CatButton = ({ onPress }: CatButtonProps) => {
};

export const CatButton = ({ onPress }: CatButtonProps) => {
const xVal = useSharedValue(-1);
const yVal = useSharedValue(0);
const blinkVal = useSharedValue(1);
Expand All @@ -28,19 +30,31 @@ const CatButton = ({ onPress }: CatButtonProps) => {
'worklet';
xVal.value = withRepeat(
withSequence(
withDelay(2000 + Math.random() * 2000, withSpring(-1 + Math.random() * 2)),
withDelay(2000 + Math.random() * 3000, withSpring(0)),
withDelay(2000 + Math.random() * 2000, withSpring(-1 + Math.random() * 2)),
withDelay(
2000 + Math.random() * 2000,
withSpring(-1 + Math.random() * 2)
),
withDelay(2000 + Math.random() * 3000, withSpring(0)),
withDelay(
2000 + Math.random() * 2000,
withSpring(-1 + Math.random() * 2)
),
withDelay(2000 + Math.random() * 3000, withSpring(0))
),
0
);
yVal.value = withRepeat(
withSequence(
withDelay(2000 + Math.random() * 3000, withSpring(-1 + Math.random() * 2)),
withDelay(2000 + Math.random() * 3000, withSpring(0)),
withDelay(2000 + Math.random() * 3000, withSpring(-1 + Math.random() * 2)),
withDelay(
2000 + Math.random() * 3000,
withSpring(-1 + Math.random() * 2)
),
withDelay(2000 + Math.random() * 3000, withSpring(0)),
withDelay(
2000 + Math.random() * 3000,
withSpring(-1 + Math.random() * 2)
),
withDelay(2000 + Math.random() * 3000, withSpring(0))
),
0
);
Expand All @@ -53,10 +67,10 @@ const CatButton = ({ onPress }: CatButtonProps) => {
withDelay(1000, withSpring(0.9)),
withTiming(0, { duration: 50 }),
withTiming(1, { duration: 100 }),
withDelay(2000, withSpring(0.8)),
withDelay(2000, withSpring(0.8))
),
0
)
);
}, [blinkVal, xVal, yVal]);

const boop = useCallback(() => {
Expand All @@ -67,17 +81,16 @@ const CatButton = ({ onPress }: CatButtonProps) => {
blinkVal.value = withTiming(0.2);
boopVal.value = withSequence(
withTiming(1, { duration: 50 }),
withTiming(0, { duration: 50 }),
withTiming(0, { duration: 50 })
);

setTimeout(() => {
idle();
if (onPress) {
// @ts-ignore
onPress();
onPress({} as GestureResponderEvent);
}
}, 400);
}, [blinkVal, idle, xVal, yVal])
}, [blinkVal, boopVal, idle, onPress, xVal, yVal]);

useEffect(() => {
idle();
Expand All @@ -87,79 +100,118 @@ const CatButton = ({ onPress }: CatButtonProps) => {
return {
transform: [
{ translateY: interpolate(yVal.value, [-1, 1], [20, -20]) },
{ translateX: interpolate(xVal.value, [-1, 1], [-50, 30]) }
]
}
{ translateX: interpolate(xVal.value, [-1, 1], [-50, 30]) },
],
};
});

const leftEyeStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateY: interpolate(yVal.value, [-1, 1], [-30, -50]) },
{ translateX: interpolate(xVal.value, [-1, 1], [-100, -60]) },
{ scaleY: interpolate(blinkVal.value, [0, 1], [0, 1]) }
]
}
{ scaleY: interpolate(blinkVal.value, [0, 1], [0, 1]) },
],
};
});

const rightEyeStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateY: interpolate(yVal.value, [-1, 1], [-30, -50]) },
{ translateX: interpolate(xVal.value, [-1, 1], [60, 100]) },
{ scaleY: interpolate(blinkVal.value, [0, 1], [0, 1]) }
]
}
{ scaleY: interpolate(blinkVal.value, [0, 1], [0, 1]) },
],
};
});

const leftEarStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateX: -160 },
{ translateY: interpolate(yVal.value, [-1, 1], [-160, -140]) },
{ scale: interpolate(xVal.value, [-1, 1], [0.95, 1.05]) }
]
}
{ scale: interpolate(xVal.value, [-1, 1], [0.95, 1.05]) },
],
};
});

const rightEarStyle = useAnimatedStyle(() => {
return {
transform: [
{ translateX: 60 },
{ translateY: interpolate(yVal.value, [-1, 1], [-160, -140]) },
{ scale: interpolate(xVal.value, [-1, 1], [1.05, 0.95]) }
]
}
{ scale: interpolate(xVal.value, [-1, 1], [1.05, 0.95]) },
],
};
});

return (
<Pressable onPress={boop}>
<View style={{ flex: 1, transform: [ { scale: 0.5 }], shadowOpacity: 1, shadowRadius: 10, shadowColor: '#000' }}>

<View
style={{
flex: 1,
transform: [{ scale: 0.5 }],
shadowOpacity: 1,
shadowRadius: 10,
shadowColor: '#000',
}}
>
<Animated.View style={[leftEarStyle, { position: 'absolute' }]}>
<CatEarL stroke={'#444'} strokeWidth={5} color={'#000'} secondaryColor={'#fff'} />
<CatEarL
stroke={'#444'}
strokeWidth={5}
color={'#000'}
secondaryColor={'#fff'}
/>
</Animated.View>

<Animated.View style={[rightEarStyle, { position: 'absolute' }]}>
<CatEarR stroke={'#444'} strokeWidth={5} color={'#000'} secondaryColor={'#fff'} />
<CatEarR
stroke={'#444'}
strokeWidth={5}
color={'#000'}
secondaryColor={'#fff'}
/>
</Animated.View>

<CatHead stroke={'#777'} strokeWidth={5} color={'#000'} style={{ position: 'absolute', transform: [ { translateX: -140 }, { translateY: -100 }]}}/>
<CatHead
stroke={'#777'}
strokeWidth={5}
color={'#000'}
style={{
position: 'absolute',
transform: [{ translateX: -140 }, { translateY: -100 }],
}}
/>

<Animated.View style={[noseStyle, { position: 'absolute' }]}>
<CatNose color={'#fff'} />
</Animated.View>

<Animated.View style={[leftEyeStyle, { position: 'absolute', transform: [ { translateX: -80 }, { translateY: -50 }]}]}>
<Animated.View
style={[
leftEyeStyle,
{
position: 'absolute',
transform: [{ translateX: -80 }, { translateY: -50 }],
},
]}
>
<CatEye color={'#fff'} />
</Animated.View>

<Animated.View style={[rightEyeStyle, { position: 'absolute', transform: [ { translateX: 80 }, { translateY: -40 }]}]}>
<Animated.View
style={[
rightEyeStyle,
{
position: 'absolute',
transform: [{ translateX: 80 }, { translateY: -40 }],
},
]}
>
<CatEye color={'#fff'} />
</Animated.View>
</View>
</Pressable>
)
}

export default CatButton;
);
};
Loading

0 comments on commit 1c1b5de

Please sign in to comment.