diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 7a145b2140..93d2c4a3c1 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -71,6 +71,11 @@ module.exports = {
message:
'Please use the useTypedReset() hook instead of importing reset from @react-navigation/native for type safety.',
},
+ {
+ selector: 'ImportDeclaration > Literal[value=/^packages/]',
+ message:
+ 'Do not import directly from the "packages" directory. Use the package name (or relative path, if within the same package) instead.',
+ },
],
},
};
diff --git a/packages/app/features/top/ChatListScreen.tsx b/packages/app/features/top/ChatListScreen.tsx
index 38819cac2a..1b3c2d214d 100644
--- a/packages/app/features/top/ChatListScreen.tsx
+++ b/packages/app/features/top/ChatListScreen.tsx
@@ -14,6 +14,7 @@ import {
ChatOptionsProvider,
ChatOptionsSheet,
ChatOptionsSheetMethods,
+ GroupPreviewAction,
GroupPreviewSheet,
InviteUsersSheet,
NavBarView,
@@ -27,6 +28,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { TLON_EMPLOYEE_GROUP } from '../../constants';
import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation';
import { useCurrentUserId } from '../../hooks/useCurrentUser';
+import { useGroupActions } from '../../hooks/useGroupActions';
import { useFeatureFlag } from '../../lib/featureFlags';
import type { RootStackParamList } from '../../navigation/types';
import { screenNameFromChannelId } from '../../navigation/utils';
@@ -91,6 +93,7 @@ export function ChatListScreenView({
const { data: chats } = store.useCurrentChats({
enabled: isFocused,
});
+ const { performGroupAction } = useGroupActions();
const currentUser = useCurrentUserId();
@@ -286,6 +289,14 @@ export function ChatListScreenView({
setShowSearchInput(!showSearchInput);
}, [showSearchInput]);
+ const handleGroupAction = useCallback(
+ (action: GroupPreviewAction, group: db.Group) => {
+ performGroupAction(action, group);
+ setSelectedGroupId(null);
+ },
+ [performGroupAction]
+ );
+
return (
(null);
const resetToDm = useResetToDm();
+ const { performGroupAction } = useGroupActions();
const handleGoToDm = useCallback(
async (participants: string[]) => {
@@ -43,6 +46,14 @@ export function UserProfileScreen({ route: { params }, navigation }: Props) {
navigation.push('EditProfile');
}, [navigation]);
+ const handleGroupAction = useCallback(
+ (action: GroupPreviewAction, group: db.Group) => {
+ setSelectedGroup(null);
+ performGroupAction(action, group);
+ },
+ [performGroupAction]
+ );
+
return (
diff --git a/packages/app/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx b/packages/app/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx
index 82d8f2b55d..e4ba7753a8 100644
--- a/packages/app/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx
+++ b/packages/app/fixtures/ActionSheet/GroupPreviewSheet.fixture.tsx
@@ -45,6 +45,7 @@ export default Object.fromEntries(
open={true}
onOpenChange={() => {}}
group={group}
+ onActionComplete={() => {}}
/>,
];
})
diff --git a/packages/app/hooks/useGroupActions.tsx b/packages/app/hooks/useGroupActions.tsx
index fa97703023..f00ae8945a 100644
--- a/packages/app/hooks/useGroupActions.tsx
+++ b/packages/app/hooks/useGroupActions.tsx
@@ -5,24 +5,19 @@ import { useCallback } from 'react';
import { useGroupNavigation } from './useGroupNavigation';
export const useGroupActions = () => {
- const { goToChannel, goToHome } = useGroupNavigation();
+ const { goToHome, goToGroupChannels } = useGroupNavigation();
const performGroupAction = useCallback(
async (action: GroupPreviewAction, updatedGroup: db.Group) => {
- if (action === 'goTo' && updatedGroup.lastPost?.channelId) {
- const channel = await db.getChannel({
- id: updatedGroup.lastPost.channelId,
- });
- if (channel) {
- goToChannel(channel);
- }
+ if (action === 'goTo') {
+ goToGroupChannels(updatedGroup.id);
}
if (action === 'joined') {
goToHome();
}
},
- [goToChannel, goToHome]
+ [goToGroupChannels, goToHome]
);
return {
diff --git a/packages/app/hooks/useGroupNavigation.ts b/packages/app/hooks/useGroupNavigation.ts
index ac7a5e7a6e..e62d7998c4 100644
--- a/packages/app/hooks/useGroupNavigation.ts
+++ b/packages/app/hooks/useGroupNavigation.ts
@@ -3,12 +3,14 @@ import * as db from '@tloncorp/shared/db';
import { useCallback } from 'react';
import { RootStackParamList } from '../navigation/types';
+import { useResetToGroup } from '../navigation/utils';
export const useGroupNavigation = () => {
const navigation = useNavigation<
// @ts-expect-error - TODO: pass navigation handlers into context
NativeStackNavigationProp
>();
+ const resetToGroup = useResetToGroup();
const goToChannel = useCallback(
async (
@@ -24,6 +26,13 @@ export const useGroupNavigation = () => {
[navigation]
);
+ const goToGroupChannels = useCallback(
+ async (groupId: string) => {
+ resetToGroup(groupId);
+ },
+ [resetToGroup]
+ );
+
const goToHome = useCallback(() => {
navigation.navigate('ChatList');
}, [navigation]);
@@ -39,5 +48,6 @@ export const useGroupNavigation = () => {
goToChannel,
goToHome,
goToContactHostedGroups,
+ goToGroupChannels,
};
};
diff --git a/packages/app/navigation/utils.ts b/packages/app/navigation/utils.ts
index aa2b6c68a1..13348316aa 100644
--- a/packages/app/navigation/utils.ts
+++ b/packages/app/navigation/utils.ts
@@ -80,6 +80,17 @@ export function useResetToDm() {
};
}
+export function useResetToGroup() {
+ const reset = useTypedReset();
+
+ return function resetToGroup(groupId: string) {
+ reset([
+ { name: 'ChatList' },
+ { name: 'GroupChannels', params: { groupId } },
+ ]);
+ };
+}
+
export function screenNameFromChannelId(channelId: string) {
return logic.isDmChannelId(channelId)
? 'DM'
diff --git a/packages/app/package.json b/packages/app/package.json
index 1fd3d95f92..90e38ca9b7 100644
--- a/packages/app/package.json
+++ b/packages/app/package.json
@@ -4,6 +4,9 @@
"main": "index.ts",
"private": true,
"scripts": {
+ "lint": "eslint ./",
+ "lint:fix": "eslint ./ --fix --quiet",
+ "lint:format": "prettier ./ --write",
"test": "vitest --watch false"
},
"dependencies": {
diff --git a/packages/ui/src/components/ContentReference/ContentReference.tsx b/packages/ui/src/components/ContentReference/ContentReference.tsx
index 7ea9599d7d..f0d9b777ca 100644
--- a/packages/ui/src/components/ContentReference/ContentReference.tsx
+++ b/packages/ui/src/components/ContentReference/ContentReference.tsx
@@ -2,7 +2,7 @@
import { ContentReference } from '@tloncorp/shared/api';
import * as db from '@tloncorp/shared/db';
import { getChannelType } from '@tloncorp/shared/urbit';
-import React, { useState } from 'react';
+import React from 'react';
import { ComponentProps, useCallback } from 'react';
import { View, XStack, styled } from 'tamagui';
@@ -11,7 +11,6 @@ import { useRequests } from '../../contexts/requests';
import { ContactAvatar, GroupAvatar } from '../Avatar';
import { useContactName } from '../ContactNameV2';
import { GalleryContentRenderer } from '../GalleryPost';
-import { GroupPreviewSheet } from '../GroupPreviewSheet';
import { IconType } from '../Icon';
import { ListItem } from '../ListItem';
import { useBoundHandler } from '../ListItem/listItemUtils';
@@ -76,7 +75,6 @@ export function PostReferenceLoader({
postId: string;
replyId?: string;
}) {
- const [selectedGroup, setSelectedGroup] = useState(null);
const { usePostReference, useChannel, useGroup } = useRequests();
const postQuery = usePostReference({
postId: replyId ? replyId : postId,
@@ -84,20 +82,14 @@ export function PostReferenceLoader({
});
const { data: channel } = useChannel({ id: channelId });
const { data: group } = useGroup(channel?.groupId ?? '');
- const { onPressRef } = useNavigation();
+ const { onPressRef, onPressGroupRef } = useNavigation();
const handlePress = useCallback(async () => {
if (channel && postQuery.data && group && group.currentUserIsMember) {
onPressRef?.(channel, postQuery.data);
} else if (group) {
- setSelectedGroup(group ?? null);
+ onPressGroupRef?.(group);
}
- }, [channel, onPressRef, postQuery.data, group]);
-
- const handleGroupPreviewSheetOpenChange = useCallback((open: boolean) => {
- if (!open) {
- setSelectedGroup(null);
- }
- }, []);
+ }, [channel, onPressRef, postQuery.data, group, onPressGroupRef]);
return (
<>
@@ -111,12 +103,6 @@ export function PostReferenceLoader({
onPress={openOnPress ? handlePress : undefined}
{...props}
/>
-
-
>
);
}
diff --git a/packages/ui/src/components/GroupPreviewSheet.tsx b/packages/ui/src/components/GroupPreviewSheet.tsx
index b457370a20..e4c16b48e4 100644
--- a/packages/ui/src/components/GroupPreviewSheet.tsx
+++ b/packages/ui/src/components/GroupPreviewSheet.tsx
@@ -16,7 +16,7 @@ import { LoadingSpinner } from './LoadingSpinner';
interface Props {
open: boolean;
onOpenChange: (open: boolean) => void;
- onActionComplete?: (action: GroupPreviewAction, group: db.Group) => void;
+ onActionComplete: (action: GroupPreviewAction, group: db.Group) => void;
group?: db.Group;
}
@@ -44,7 +44,11 @@ function GroupPreviewSheetComponent({
const actionHandler = useCallback(
(action: GroupPreviewAction, updatedGroup: db.Group) => {
- onActionComplete?.(action, updatedGroup);
+ // Delay the action complete callback to allow the sheet to close.
+ // If we don't do this the app will crash.
+ setTimeout(() => {
+ onActionComplete?.(action, updatedGroup);
+ }, 100);
onOpenChange(false);
},
[onActionComplete, onOpenChange]