Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5098f26
chore: remove obsolete webview patch
Aug 25, 2025
bd531f2
feat(mobile): implement static location sharing
Sep 1, 2025
c91c399
feat(mobile): implement static location sharing
Sep 8, 2025
7c01380
feat(mobile): implement live location sharing
Sep 22, 2025
06de63b
feat(mobile): implement live location sharing
Sep 22, 2025
f985d4d
feat(mobile): implement live location sharing
Sep 23, 2025
5057700
Merge remote-tracking branch 'origin/develop' into new/location-sharing
Sep 24, 2025
1b35e25
feat: static & live location sharing (draft)
Sep 25, 2025
72dc234
action: organized translations
yiweigao0226 Sep 25, 2025
5450781
feat: static & live location sharing (draft)
Sep 29, 2025
6f44e91
action: organized translations
yiweigao0226 Sep 29, 2025
a991844
feat: static & live location sharing (draft)
Sep 29, 2025
db4f0f3
feat: static & live location sharing (draft)
Sep 29, 2025
3ef9f97
action: organized translations
yiweigao0226 Sep 29, 2025
f59c26d
feat: static & live location sharing (draft)
Sep 30, 2025
fdd3834
feat: static & live location sharing (draft)
Sep 30, 2025
2152119
feat: static & live location sharing (draft)
yiweigao0226 Sep 30, 2025
c4e2514
password show/hide
yiweigao0226 Apr 1, 2018
f4b2aec
[FIX] Bottom border style on DirectoryView (#1963)
yiweigao0226 Apr 1, 2020
bd47d05
feat: static & live location sharing (draft)
yiweigao0226 Oct 3, 2025
366cd18
feat: static & live location sharing (draft)
yiweigao0226 Oct 13, 2025
23ae666
Merge branch 'develop' into new/location-sharing
yiweigao0226 Oct 13, 2025
7cc84af
Merge branch 'RocketChat:develop' into new/location-sharing
yiweigao0226 Oct 14, 2025
43c0adc
feat: static & live location sharing (draft)
yiweigao0226 Oct 14, 2025
8625c64
Merge branch 'develop' into new/location-sharing
yiweigao0226 Oct 20, 2025
8712efc
Merge branch 'develop' into new/location-sharing
yiweigao0226 Oct 22, 2025
90e3452
Merge branch 'RocketChat:develop' into new/location-sharing
yiweigao0226 Oct 28, 2025
1311bd5
feat: static & live location sharing (draft)
yiweigao0226 Oct 28, 2025
320705f
action: organized translations
yiweigao0226 Oct 28, 2025
ad3c8dc
feat: static & live location sharing (draft)
yiweigao0226 Oct 28, 2025
03af849
feat: static & live location sharing (draft)
yiweigao0226 Oct 28, 2025
f17aa5b
action: organized translations
yiweigao0226 Oct 28, 2025
3d83f5e
feat: static & live location sharing (draft)
yiweigao0226 Oct 28, 2025
3eff38a
feat: static & live location sharing (draft)
yiweigao0226 Oct 30, 2025
95ec9cd
action: organized translations
yiweigao0226 Oct 30, 2025
ba6ca43
feat: static & live location sharing (draft)
yiweigao0226 Oct 30, 2025
d695613
feat: static & live location sharing (draft)
yiweigao0226 Oct 31, 2025
289bcfe
feat: static & live location sharing (draft)
yiweigao0226 Oct 31, 2025
8bc29d2
feat: static & live location sharing (draft)
yiweigao0226 Oct 31, 2025
6ea1090
feat: static & live location sharing (draft)
yiweigao0226 Oct 31, 2025
1de7572
feat: static & live location sharing (draft)
yiweigao0226 Oct 31, 2025
c37b1fa
Fix typo in coords property from 'lng' to 'log'
yiweigao0226 Oct 31, 2025
a10295e
Fix typo in coords property from 'log' to 'lon'
yiweigao0226 Oct 31, 2025
4e7a86f
Add tests for CurrentLocationCard and LiveLocationCard
yiweigao0226 Nov 1, 2025
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: 10 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@
<!-- android 13 media permission -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

<!-- Location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- If you stream/live share or track in background -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- Optional: only if you truly need background updates on Android 10+ -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<application
android:name="chat.rocket.reactnative.MainApplication"
android:allowBackup="false"
Expand Down
9 changes: 8 additions & 1 deletion app.json
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": ""
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Ensure runtime handling of empty API keys.

The GOOGLE_MAPS_API_KEY and OSM_API_KEY are initialized as empty strings, which is appropriate for an open-source repository. However, verify that the application gracefully handles missing API keys at runtime (e.g., shows a configuration prompt, disables features, or provides clear error messages).


🏁 Script executed:

#!/bin/bash
# Description: Check how the app handles empty API keys in map provider logic

rg -nP --type=ts --type=tsx -C3 'GOOGLE_MAPS_API_KEY|OSM_API_KEY' -g '!node_modules' -g '!*.json'

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for API key references across code
rg -n 'GOOGLE_MAPS_API_KEY|OSM_API_KEY' --glob '**/*.{ts,tsx,js,jsx}' --glob '!node_modules/**'

Length of output: 1264


🏁 Script executed:

#!/bin/bash
rg -n "import .* from 'expo-constants'" -C3 --glob "**/*.{ts,tsx,js,jsx}" --glob "!node_modules/**"

Length of output: 0


Implement runtime loading and graceful fallback for map API keys
I didn’t find any code consuming the GOOGLE_MAPS_API_KEY/OSM_API_KEY values from your app.json extras. Import Constants from expo-constants, read Constants.expoConfig.extra?.GOOGLE_MAPS_API_KEY (or Constants.manifest.extra for SDK < 48), merge with your useUserPreferences values, and disable map features or prompt for key entry when both are empty.

}
205 changes: 193 additions & 12 deletions app/containers/MessageComposer/components/Buttons/ActionsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

There should be at least one empty line between import groups

Check failure on line 12 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

There should be at least one empty line between import groups
import * as Location from 'expo-location';

Check failure on line 13 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

`react-native` import should occur before import of `../../../../lib/database/services/Subscription`

Check failure on line 13 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

`react-native` import should occur before import of `../../../../lib/database/services/Subscription`
import { showErrorAlert } from '../../../../lib/methods/helpers';

Check failure on line 14 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

`expo-location` import should occur before import of `../../../../lib/database/services/Subscription`

Check failure on line 14 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

There should be at least one empty line between import groups

Check failure on line 14 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

`expo-location` import should occur before import of `../../../../lib/database/services/Subscription`

Check failure on line 14 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

There should be at least one empty line between import groups
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

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Missing file extension for "../../../../lib/methods"

Check failure on line 18 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Unable to resolve path to module '../../../../lib/methods'

Check failure on line 18 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Missing file extension for "../../../../lib/methods"

Check failure on line 18 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Unable to resolve path to module '../../../../lib/methods'
MAP_PROVIDER_PREFERENCE_KEY,
GOOGLE_MAPS_API_KEY_PREFERENCE_KEY,
OSM_API_KEY_PREFERENCE_KEY,
MAP_PROVIDER_DEFAULT
} from '../../../../lib/constants';

Check failure on line 24 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Missing file extension for "../../../../lib/constants"

Check failure on line 24 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Unable to resolve path to module '../../../../lib/constants'

Check failure on line 24 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Missing file extension for "../../../../lib/constants"

Check failure on line 24 in app/containers/MessageComposer/components/Buttons/ActionsButton.tsx

View workflow job for this annotation

GitHub Actions / ESLint and Test / run-eslint-and-test

Unable to resolve path to module '../../../../lib/constants'
export const ActionsButton = () => {
const { rid, tmid, t } = useRoomContext();
const { closeEmojiKeyboardAndAction } = useContext(MessageInnerContext);
Expand All @@ -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;
Expand All @@ -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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use any

showErrorAlert(e?.message || I18n.t('Could_not_get_location'), I18n.t('Oops'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the custom error messages? let me know in reply.

}
};

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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't use any type

showErrorAlert(e?.message || I18n.t('Could_not_get_location'), I18n.t('Oops'));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are the expected error message here? if they are not related to user, just show Could_not_get_location

}
};
Comment on lines 111 to 160
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 30, 2025

Choose a reason for hiding this comment

The 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 @ts-ignore on line 151 indicates missing type definitions for LiveLocationPreviewModal in the navigation param list.

Ensure that the navigation types (e.g., in app/stacks/types.ts or app/stacks/MasterDetailStack/types.ts) include:

LiveLocationPreviewModal: {
  rid: string;
  tmid?: string;
  provider: MapProviderName;
  googleKey?: string;
  osmKey?: string;
  liveLocationId?: string;
  ownerName?: string;
  isTracking?: boolean;
};

This will eliminate the need for @ts-ignore and provide type safety.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolve this too

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment

openSheetSafely(() => openCurrentPreview(provider));
}
},
{
title: I18n.t('Start_live_location'),
icon: 'live',
onPress: () => {
// sheet -> navigation: close current sheet safely, then start flow
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this comment

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 });
Expand Down
Loading
Loading