Skip to content

Commit 2317d1e

Browse files
authored
feat: add location support (#84)
🎫 Ticket: https://linear.app/stream/issue/XYZ-123 πŸ“‘ Docs: https://github.com/GetStream/docs-content/pull/<id> ### πŸ’‘ Overview This is a draft PR which adds support for adding locations to `activities`. Better description will follow. ### πŸ“ Implementation notes
1 parent 9f296b8 commit 2317d1e

25 files changed

+996
-151
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
EXPO_PUBLIC_TOKEN_CREATION_URL=
22
EXPO_PUBLIC_BASE_URL=
3+
EXPO_PUBLIC_PLACES_API_KEY=
4+
EXPO_PUBLIC_MAPS_API_KEY=

β€Žsample-apps/react-native/ExpoTikTokApp/app.jsonβ€Ž

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,22 @@
1616
"ios": {
1717
"supportsTablet": true,
1818
"bundleIdentifier": "io.getstream.expotiktokapp",
19-
"appleTeamId": "EHV7XZLAHA"
19+
"appleTeamId": "EHV7XZLAHA",
20+
"config": {
21+
"googleMapsApiKey": "process.env.EXPO_PUBLIC_MAPS_API_KEY"
22+
}
2023
},
2124
"android": {
2225
"adaptiveIcon": {
2326
"foregroundImage": "./assets/images/adaptive-icon.png",
2427
"backgroundColor": "#ffffff"
2528
},
26-
"package": "io.getstream.expotiktokapp"
29+
"package": "io.getstream.expotiktokapp",
30+
"config": {
31+
"googleMaps": {
32+
"apiKey": "process.env.EXPO_PUBLIC_MAPS_API_KEY"
33+
}
34+
}
2735
},
2836
"web": {
2937
"bundler": "metro",
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { PostCreationContextProvider } from '@/contexts/PostCreationContext';
2+
import { Slot, Stack, useRouter } from 'expo-router';
3+
import React from 'react';
4+
import { useColorScheme } from '@/components/useColorScheme';
5+
import { Pressable } from 'react-native';
6+
import Ionicons from '@expo/vector-icons/Ionicons';
7+
import Colors from '@/constants/Colors';
8+
9+
const HeaderLeft = () => {
10+
const colorScheme = useColorScheme();
11+
const router = useRouter();
12+
return (
13+
<Pressable
14+
onPress={() =>
15+
router.back()
16+
}
17+
>
18+
{({ pressed }) => (
19+
<Ionicons
20+
name="arrow-back"
21+
size={25}
22+
color={Colors[colorScheme ?? 'light'].text}
23+
style={{ marginRight: 15, opacity: pressed ? 0.5 : 1 }}
24+
/>
25+
)}
26+
</Pressable>
27+
);
28+
};
29+
30+
const PostCreationLayout = () => {
31+
return (
32+
<PostCreationContextProvider>
33+
<Stack>
34+
<Stack.Screen
35+
name="create-post-modal"
36+
options={{
37+
title: 'New Post',
38+
headerLeft: HeaderLeft,
39+
}}
40+
/>
41+
<Stack.Screen
42+
name="pick-location-modal"
43+
options={{
44+
title: 'Select Location',
45+
presentation: 'modal',
46+
animation: 'slide_from_bottom',
47+
}}
48+
/>
49+
</Stack>
50+
</PostCreationContextProvider>
51+
);
52+
}
53+
54+
export default PostCreationLayout;

sample-apps/react-native/ExpoTikTokApp/app/create-post-modal.tsx renamed to sample-apps/react-native/ExpoTikTokApp/app/(post-creation)/create-post-modal.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from '@stream-io/feeds-react-native-sdk';
88
import { useMemo } from 'react';
99

10-
const ModalScreen = () => {
10+
const CreatePostScreen = () => {
1111
const client = useFeedsClient();
1212
const connectedUser = useClientConnectedUser();
1313

@@ -22,12 +22,10 @@ const ModalScreen = () => {
2222
}
2323

2424
return (
25-
<SafeAreaView style={{ flex: 1 }}>
2625
<StreamFeed feed={feed}>
2726
<ActivityComposer />
2827
</StreamFeed>
29-
</SafeAreaView>
3028
);
3129
}
3230

33-
export default ModalScreen;
31+
export default CreatePostScreen;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { PlaceSearchDropdown } from '@/components/PlaceSearchDropdown';
2+
import { StyleSheet, View } from 'react-native';
3+
4+
const CreatePostScreen = () => {
5+
return (
6+
<View style={styles.container}>
7+
<PlaceSearchDropdown />
8+
</View>
9+
);
10+
};
11+
12+
export default CreatePostScreen;
13+
14+
const styles = StyleSheet.create({
15+
container: { flex: 1, backgroundColor: 'fff' },
16+
});

β€Žsample-apps/react-native/ExpoTikTokApp/app/(tabs)/_layout.tsxβ€Ž

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,19 @@ import { Pressable } from 'react-native';
55

66
import Colors from '@/constants/Colors';
77
import { useColorScheme } from '@/components/useColorScheme';
8-
import {
9-
useClientConnectedUser,
10-
} from '@stream-io/feeds-react-native-sdk';
118

12-
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
13-
function TabBarIcon(props: {
9+
const TabBarIcon = (props: {
1410
name: React.ComponentProps<typeof FontAwesome>['name'];
1511
color: string;
16-
}) {
12+
}) => {
1713
return <FontAwesome size={28} style={{ marginBottom: -3 }} {...props} />;
18-
}
14+
};
1915

2016
const HeaderRight = () => {
2117
const colorScheme = useColorScheme();
2218
const router = useRouter();
2319
return (
24-
<Pressable
25-
onPress={() =>
26-
router.push('/create-post-modal')
27-
}
28-
>
20+
<Pressable onPress={() => router.push('/create-post-modal')}>
2921
{({ pressed }) => (
3022
<FontAwesome
3123
name="plus-square"
@@ -38,7 +30,7 @@ const HeaderRight = () => {
3830
);
3931
};
4032

41-
export default function TabLayout() {
33+
const TabLayout = () => {
4234
const colorScheme = useColorScheme();
4335

4436
return (
@@ -71,4 +63,6 @@ export default function TabLayout() {
7163
/>
7264
</Tabs>
7365
);
74-
}
66+
};
67+
68+
export default TabLayout;

β€Žsample-apps/react-native/ExpoTikTokApp/app/(tabs)/profile.tsxβ€Ž

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
1-
import React, { useMemo } from 'react';
1+
import React from 'react';
22
import { View, Text } from '@/components/Themed';
33
import { ConnectionLostHeader } from '@/components/ConnectionLostHeader';
4-
import { Pressable, StyleSheet, Platform } from 'react-native';
4+
import { Pressable, StyleSheet } from 'react-native';
55
import { useOwnFeedsContext } from '@/contexts/OwnFeedsContext';
66
import { useUserContext } from '@/contexts/UserContext';
77
import { Profile } from '@/components/Profile';
8-
// eslint-disable-next-line import/no-extraneous-dependencies
9-
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
10-
import { useSafeAreaInsets } from 'react-native-safe-area-context';
118

129
const ProfileScreen = () => {
1310
const { ownUserFeed, ownTimelineFeed } = useOwnFeedsContext();
1411
const { logOut } = useUserContext();
15-
const tabBarHeight = useBottomTabBarHeight();
16-
const insets = useSafeAreaInsets();
17-
18-
const extraContainerStyles = useMemo(
19-
() => ({
20-
paddingBottom:
21-
insets.bottom + tabBarHeight + (Platform.OS === 'ios' ? 0 : 80),
22-
}),
23-
[insets.bottom, tabBarHeight],
24-
);
2512

2613
if (!ownUserFeed || !ownTimelineFeed) {
2714
return null;
2815
}
2916

3017
return (
31-
<View style={[styles.container, extraContainerStyles]}>
18+
<View style={styles.container}>
3219
<ConnectionLostHeader />
3320
<Pressable
3421
onPress={logOut}
@@ -50,8 +37,8 @@ const ProfileScreen = () => {
5037
export default ProfileScreen;
5138

5239
const styles = StyleSheet.create({
53-
container: { flex: 1, paddingTop: 16, backgroundColor: '#fff' },
54-
profileContainer: { flex: 1, paddingBottom: 16 },
40+
container: { flex: 1, paddingTop: 16, paddingBottom: 20, backgroundColor: '#fff' },
41+
profileContainer: { height: '100%', paddingBottom: 16 },
5542
button: {
5643
flexDirection: 'row',
5744
alignItems: 'center',

β€Žsample-apps/react-native/ExpoTikTokApp/app/_layout.tsxβ€Ž

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,25 @@ const RootLayoutNav = ({ user }: { user: UserRequest }) => {
9393
options={{ title: 'Profile' }}
9494
/>
9595
<Stack.Screen
96-
name="create-post-modal"
96+
name="location-map-screen"
97+
options={{ title: 'Location' }}
98+
/>
99+
<Stack.Screen
100+
name="(post-creation)"
97101
options={{
98-
title: 'New Post',
99-
presentation: 'modal',
100-
animation: 'slide_from_bottom',
102+
headerShown: false,
103+
// presentation: 'modal',
104+
// animation: 'slide_from_bottom',
101105
}}
102106
/>
107+
{/* <Stack.Screen */}
108+
{/* name="(post-creation)/pick-location-modal" */}
109+
{/* options={{ */}
110+
{/* title: 'New Post', */}
111+
{/* // presentation: 'modal', */}
112+
{/* // animation: 'slide_from_bottom', */}
113+
{/* }} */}
114+
{/* /> */}
103115
<Stack.Screen
104116
name="followers-modal"
105117
options={{
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { useMemo } from 'react';
2+
import { StyleSheet } from 'react-native';
3+
import MapView, { Marker } from 'react-native-maps';
4+
import { useLocalSearchParams } from 'expo-router';
5+
import { View } from '@/components/Themed';
6+
7+
const LocationMapScreen = () => {
8+
const { latitude: latitudeParam, longitude: longitudeParam } =
9+
useLocalSearchParams();
10+
const latitude = Number(latitudeParam);
11+
const longitude = Number(longitudeParam);
12+
13+
const initialRegion = useMemo(
14+
() => ({
15+
latitude,
16+
longitude,
17+
latitudeDelta: 0.01,
18+
longitudeDelta: 0.01,
19+
}),
20+
[latitude, longitude],
21+
);
22+
23+
return (
24+
<View style={styles.container}>
25+
<MapView style={StyleSheet.absoluteFill} initialRegion={initialRegion}>
26+
<Marker coordinate={{ latitude, longitude }} />
27+
</MapView>
28+
</View>
29+
);
30+
};
31+
32+
const styles = StyleSheet.create({
33+
container: {
34+
height: '100%',
35+
width: '100%',
36+
borderRadius: 10,
37+
overflow: 'hidden',
38+
},
39+
});
40+
41+
export default LocationMapScreen;

0 commit comments

Comments
Β (0)