-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: static & live location sharing (draft) #6694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 17 commits
5098f26
bd531f2
c91c399
7c01380
06de63b
f985d4d
5057700
1b35e25
72dc234
5450781
6f44e91
a991844
db4f0f3
3ef9f97
f59c26d
fdd3834
2152119
c4e2514
f4b2aec
bd47d05
366cd18
23ae666
7cc84af
43c0adc
8625c64
8712efc
90e3452
1311bd5
320705f
ad3c8dc
03af849
f17aa5b
3d83f5e
3eff38a
95ec9cd
ba6ca43
d695613
289bcfe
8bc29d2
6ea1090
1de7572
c37b1fa
a10295e
4e7a86f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,10 @@ | ||
| { | ||
| "name": "RocketChatRN" | ||
| "name": "RocketChatRN", | ||
| "plugins": ["expo-web-browser"], | ||
| "expo": { | ||
| "extra": { | ||
| "GOOGLE_MAPS_API_KEY": "", | ||
| "OSM_API_KEY": "" | ||
| } | ||
| } | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,19 @@ | |
| import { useAppSelector, usePermissions } from '../../../../lib/hooks'; | ||
| import { useCanUploadFile, useChooseMedia } from '../../hooks'; | ||
| import { useRoomContext } from '../../../../views/RoomView/context'; | ||
| import { Platform, PermissionsAndroid, InteractionManager } from 'react-native'; | ||
|
Check failure on line 12 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
|
||
| import * as Location from 'expo-location'; | ||
|
Check failure on line 13 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
|
||
| import { showErrorAlert } from '../../../../lib/methods/helpers'; | ||
|
Check failure on line 14 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
|
||
| import { getCurrentPositionOnce } from '../../../../views/LocationShare/services/staticLocation'; | ||
| import { MapProviderName } from '../../../../views/LocationShare/services/mapProviders'; | ||
| import { useUserPreferences } from '../../../../lib/methods'; | ||
| import { | ||
|
Check failure on line 18 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
|
||
| MAP_PROVIDER_PREFERENCE_KEY, | ||
| GOOGLE_MAPS_API_KEY_PREFERENCE_KEY, | ||
| OSM_API_KEY_PREFERENCE_KEY, | ||
| MAP_PROVIDER_DEFAULT | ||
| } from '../../../../lib/constants'; | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
Check failure on line 24 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
|
||
| export const ActionsButton = () => { | ||
| const { rid, tmid, t } = useRoomContext(); | ||
| const { closeEmojiKeyboardAndAction } = useContext(MessageInnerContext); | ||
|
|
@@ -22,6 +34,30 @@ | |
| }); | ||
| const { showActionSheet, hideActionSheet } = useActionSheet(); | ||
| const isMasterDetail = useAppSelector(state => state.app.isMasterDetail); | ||
| const userId = useAppSelector(state => state.login.user.id); | ||
|
|
||
| const [mapProvider] = useUserPreferences<MapProviderName>(`${MAP_PROVIDER_PREFERENCE_KEY}_${userId}`, MAP_PROVIDER_DEFAULT); | ||
| const [googleApiKey] = useUserPreferences<string>(`${GOOGLE_MAPS_API_KEY_PREFERENCE_KEY}_${userId}`, ''); | ||
| const [osmApiKey] = useUserPreferences<string>(`${OSM_API_KEY_PREFERENCE_KEY}_${userId}`, ''); | ||
|
|
||
| // --- Sheet transition helpers --- | ||
| const sheetBusyRef = React.useRef(false); | ||
| /** Safely close the current ActionSheet and then run `fn` (open next sheet) */ | ||
| const openSheetSafely = (fn: () => void, delayMs = 350) => { | ||
| if (sheetBusyRef.current) return; | ||
| sheetBusyRef.current = true; | ||
|
|
||
| hideActionSheet(); | ||
| InteractionManager.runAfterInteractions(() => { | ||
| setTimeout(() => { | ||
| try { | ||
| fn(); | ||
| } finally { | ||
| sheetBusyRef.current = false; | ||
| } | ||
| }, delayMs); | ||
| }); | ||
| }; | ||
|
|
||
| const createDiscussion = async () => { | ||
| if (!rid) return; | ||
|
|
@@ -34,62 +70,207 @@ | |
| } | ||
| }; | ||
|
|
||
| const openCurrentPreview = async (provider: MapProviderName) => { | ||
| try { | ||
| if (!rid) { | ||
| showErrorAlert(I18n.t('Room_not_available'), I18n.t('Oops')); | ||
| return; | ||
| } | ||
|
|
||
| if (Platform.OS === 'android') { | ||
| const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION); | ||
| if (granted !== PermissionsAndroid.RESULTS.GRANTED) { | ||
| showErrorAlert(I18n.t('Location_permission_required'), I18n.t('Oops')); | ||
| return; | ||
| } | ||
| } else { | ||
| const { status } = await Location.requestForegroundPermissionsAsync(); | ||
| if (status !== 'granted') { | ||
| showErrorAlert(I18n.t('Location_permission_required'), I18n.t('Oops')); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| const coords = await getCurrentPositionOnce(); | ||
|
|
||
| const params = { | ||
| rid, | ||
| tmid, | ||
| provider, | ||
| coords, | ||
| googleKey: provider === 'google' ? googleApiKey : undefined, | ||
| osmKey: provider === 'osm' ? osmApiKey : undefined | ||
| }; | ||
|
|
||
| InteractionManager.runAfterInteractions(() => { | ||
| if (isMasterDetail) { | ||
| Navigation.navigate('ModalStackNavigator', { screen: 'LocationPreviewModal', params }); | ||
| } else { | ||
| Navigation.navigate('LocationPreviewModal', params); | ||
| } | ||
| }); | ||
| } catch (e: any) { | ||
|
||
| showErrorAlert(e?.message || I18n.t('Could_not_get_location'), I18n.t('Oops')); | ||
|
||
| } | ||
| }; | ||
|
|
||
| const openLivePreview = async (provider: MapProviderName) => { | ||
| try { | ||
| if (!rid) { | ||
| showErrorAlert(I18n.t('Room_not_available'), I18n.t('Oops')); | ||
| return; | ||
| } | ||
|
|
||
| if (Platform.OS === 'android') { | ||
| const res = await PermissionsAndroid.requestMultiple([ | ||
| PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, | ||
| PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION | ||
| ]); | ||
| const fine = res[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION]; | ||
| const coarse = res[PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION]; | ||
| if (fine !== PermissionsAndroid.RESULTS.GRANTED && coarse !== PermissionsAndroid.RESULTS.GRANTED) { | ||
| throw new Error(I18n.t('Permission_denied')); | ||
| } | ||
| } else { | ||
| const { status } = await Location.requestForegroundPermissionsAsync(); | ||
| if (status !== 'granted') { | ||
| throw new Error(I18n.t('Location_permission_required')); | ||
| } | ||
| } | ||
|
|
||
| const params = { | ||
| rid, | ||
| tmid, | ||
| provider, | ||
| googleKey: provider === 'google' ? googleApiKey : undefined, | ||
| osmKey: provider === 'osm' ? osmApiKey : undefined | ||
| }; | ||
|
|
||
| // Defer navigation until after sheets/animations are done | ||
| InteractionManager.runAfterInteractions(() => { | ||
| // @ts-ignore | ||
| if (isMasterDetail) { | ||
| Navigation.navigate('ModalStackNavigator', { screen: 'LiveLocationPreviewModal', params }); | ||
| } else { | ||
| Navigation.navigate('LiveLocationPreviewModal', params); | ||
| } | ||
| }); | ||
| } catch (e: any) { | ||
|
||
| showErrorAlert(e?.message || I18n.t('Could_not_get_location'), I18n.t('Oops')); | ||
|
||
| } | ||
| }; | ||
|
Comment on lines
111
to
160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove @ts-ignore by updating navigation types. The live location preview flow is well-structured, but the Ensure that the navigation types (e.g., in LiveLocationPreviewModal: {
rid: string;
tmid?: string;
provider: MapProviderName;
googleKey?: string;
osmKey?: string;
liveLocationId?: string;
ownerName?: string;
isTracking?: boolean;
};This will eliminate the need for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolve this too There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| const openModeSheetForProvider = (provider: MapProviderName) => { | ||
| const modeOptions: TActionSheetOptionsItem[] = [ | ||
| { | ||
| title: I18n.t('Share_current_location'), | ||
| icon: 'pin-map', | ||
| onPress: () => { | ||
| // sheet -> navigation: close current sheet safely, then start flow | ||
|
||
| openSheetSafely(() => openCurrentPreview(provider)); | ||
| } | ||
| }, | ||
| { | ||
| title: I18n.t('Start_live_location'), | ||
| icon: 'live', | ||
| onPress: () => { | ||
| // sheet -> navigation: close current sheet safely, then start flow | ||
|
||
| openSheetSafely(() => openLivePreview(provider)); | ||
| } | ||
| } | ||
| ]; | ||
| showActionSheet({ options: modeOptions }); | ||
| }; | ||
|
|
||
| const onPress = () => { | ||
| const options: TActionSheetOptionsItem[] = []; | ||
|
|
||
| if (t === 'l' && permissionToViewCannedResponses) { | ||
| options.push({ | ||
| title: I18n.t('Canned_Responses'), | ||
| icon: 'canned-response', | ||
| onPress: () => Navigation.navigate('CannedResponsesListView', { rid }) | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| InteractionManager.runAfterInteractions(() => { | ||
| Navigation.navigate('CannedResponsesListView', { rid }); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| if (permissionToUpload) { | ||
| options.push( | ||
| { | ||
| title: I18n.t('Take_a_photo'), | ||
| icon: 'camera-photo', | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| // This is necessary because the action sheet does not close properly on Android | ||
| setTimeout(() => { | ||
| InteractionManager.runAfterInteractions(() => { | ||
| takePhoto(); | ||
| }, 250); | ||
| }); | ||
| } | ||
| }, | ||
| { | ||
| title: I18n.t('Take_a_video'), | ||
| icon: 'camera', | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| // This is necessary because the action sheet does not close properly on Android | ||
| setTimeout(() => { | ||
| InteractionManager.runAfterInteractions(() => { | ||
| takeVideo(); | ||
| }, 250); | ||
| }); | ||
| } | ||
| }, | ||
| { | ||
| title: I18n.t('Choose_from_library'), | ||
| icon: 'image', | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| // This is necessary because the action sheet does not close properly on Android | ||
| setTimeout(() => { | ||
| InteractionManager.runAfterInteractions(() => { | ||
| chooseFromLibrary(); | ||
| }, 250); | ||
| }); | ||
| } | ||
| }, | ||
| { | ||
| title: I18n.t('Choose_file'), | ||
| icon: 'attach', | ||
| onPress: () => chooseFile() | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| InteractionManager.runAfterInteractions(() => { | ||
| chooseFile(); | ||
| }); | ||
| } | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| options.push({ | ||
| title: I18n.t('Create_Discussion'), | ||
| icon: 'discussions', | ||
| onPress: () => createDiscussion() | ||
| onPress: () => { | ||
| hideActionSheet(); | ||
| InteractionManager.runAfterInteractions(() => { | ||
| createDiscussion(); | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| options.push({ | ||
| title: I18n.t('Share_Location'), | ||
| icon: 'pin-map', | ||
| onPress: () => { | ||
| // Check if the user has configured API keys for their preferred provider | ||
| const needsApiKey = (mapProvider === 'google' && !googleApiKey) || (mapProvider === 'osm' && !osmApiKey); | ||
|
|
||
| if (needsApiKey) { | ||
| showErrorAlert( | ||
| I18n.t('API_key_required', { provider: mapProvider === 'google' ? 'Google Maps' : 'OpenStreetMap' }), | ||
| I18n.t('Please_configure_API_key_in_settings') | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| openSheetSafely(() => openModeSheetForProvider(mapProvider)); | ||
| } | ||
| }); | ||
|
|
||
| closeEmojiKeyboardAndAction(showActionSheet, { options }); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.