Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 16 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
[
'babel-preset-expo',
{
// src/lib/install-message-event-polyfill.ts deep-requires a private
// react-native module on purpose (RN doesn't expose MessageEvent as
// a global) — silence the resulting dev-only deprecation warning.
disableDeepImportWarnings: true,
},
],
],
};
};
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ module.exports = {
setupFiles: ['./jest.setup.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
// Under Jest's RN environment, lucide-react-native's package.json `exports` picks its ESM
// `.mjs` build (customExportConditions includes `react-native`), but Jest has no transform
// registered for `.mjs` (only `.[jt]sx?`) — `require()`-ing it raw hits `export` syntax and
// throws. Its CJS build is real CommonJS and needs no transform, so map straight to it.
'^lucide-react-native$':
'<rootDir>/node_modules/lucide-react-native/dist/cjs/lucide-react-native.js',
},
// react-native-worklets (reanimated 4's underlying worklets runtime) ships a real native
// module with no Jest mock; its own package provides this resolver, which steers Jest away
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"packageManager": "pnpm@11.15.1",
"dependencies": {
"@expo-google-fonts/plus-jakarta-sans": "^0.4.2",
"@expo/vector-icons": "^15.1.1",
"@gorhom/bottom-sheet": "^5.2.14",
"@react-native-async-storage/async-storage": "2.2.0",
"@shopify/flash-list": "2.0.2",
Expand Down Expand Up @@ -35,6 +34,7 @@
"expo-updates": "~57.0.19",
"expo-web-browser": "~57.0.2",
"i18next": "^26.3.6",
"lucide-react-native": "^1.35.0",
"openapi-fetch": "^0.17.0",
"partysocket": "^1.3.0",
"react": "19.2.3",
Expand All @@ -44,6 +44,7 @@
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0",
"react-native-svg": "15.15.4",
"react-native-worklets": "0.10.1",
"zustand": "^5.0.15"
},
Expand Down
129 changes: 113 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 15 additions & 5 deletions src/app/(app)/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,20 @@ import { useWorkspaceStore } from '@/stores/use-workspace-store';
import { useTheme } from '@/theme/use-theme';

/** react-navigation types tabBarIcon's `color` as `ColorValue` (includes opaque platform colors);
* our theme tokens are always plain hex strings, so this casts back for the Icon primitive. */
* our theme tokens are always plain hex strings, so this casts back for the Icon primitive.
* `strokeWidth` bumps to 2.5 when focused — lucide icons are stroke-only, so this is how the
* active tab recovers the visual weight Ionicons' filled/outline pair used to convey. */
function tabIcon(name: IconName) {
function TabIcon({ color, size }: { color: ColorValue; size: number }) {
return <Icon name={name} size={size} color={color as string} />;
function TabIcon({
color,
size,
focused,
}: {
color: ColorValue;
size: number;
focused: boolean;
}) {
return <Icon name={name} size={size} color={color as string} strokeWidth={focused ? 2.5 : 2} />;
}
return TabIcon;
}
Expand Down Expand Up @@ -52,7 +62,7 @@ export default function TabsLayout() {
name="conversations/index"
options={{
title: t('inbox.title'),
tabBarIcon: tabIcon('chatbubbles'),
tabBarIcon: tabIcon('messages-square'),
// `useInboxUnreadCount` only counts CACHED list pages, not a true server-side unread
// total (no such endpoint exists yet — see the hook's own doc comment). `undefined`
// (not 0) hides the badge entirely when there's nothing unread.
Expand All @@ -61,7 +71,7 @@ export default function TabsLayout() {
/>
<Tabs.Screen
name="contacts/index"
options={{ title: t('contacts.title'), tabBarIcon: tabIcon('people') }}
options={{ title: t('contacts.title'), tabBarIcon: tabIcon('users') }}
/>
<Tabs.Screen
name="settings/index"
Expand Down
Loading