Skip to content
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

native: on-network and lure invites #3867

Merged
merged 11 commits into from
Aug 30, 2024
45 changes: 42 additions & 3 deletions apps/tlon-mobile/src/components/AddGroupSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
NativeStackScreenProps,
createNativeStackNavigator,
} from '@react-navigation/native-stack';
import { BRANCH_DOMAIN, BRANCH_KEY } from '@tloncorp/app/constants';
import { useCurrentUserId } from '@tloncorp/app/hooks/useCurrentUser';
import { QueryClientProvider, queryClient } from '@tloncorp/shared/dist/api';
import * as db from '@tloncorp/shared/dist/db';
import {
Expand All @@ -16,6 +18,7 @@ import {
CreateGroupWidget,
GroupPreviewPane,
Icon,
InviteUsersWidget,
Sheet,
View,
ViewUserGroupsWidget,
Expand Down Expand Up @@ -55,6 +58,10 @@ type StackParamList = {
Home: undefined;
Root: undefined;
CreateGroup: undefined;
InviteUsers: {
group: db.Group;
onInviteComplete: () => void;
};
ViewContactGroups: {
contactId: string;
};
Expand Down Expand Up @@ -138,6 +145,10 @@ export default function AddGroupSheet({
name="CreateGroup"
component={CreateGroupScreen}
/>
<Stack.Screen
name="InviteUsers"
component={InviteUsersScreen}
/>
<Stack.Screen
name="ViewContactGroups"
component={ViewContactGroupsScreen}
Expand Down Expand Up @@ -263,10 +274,15 @@ function CreateGroupScreen(
const { onCreatedGroup, dismiss } = useContext(ActionContext);
const handleCreate = useCallback(
(args: { group: db.Group; channel: db.Channel }) => {
dismiss();
onCreatedGroup(args);
props.navigation.push('InviteUsers', {
group: args.group,
onInviteComplete: () => {
dismiss();
onCreatedGroup(args);
},
});
},
[dismiss, onCreatedGroup]
[dismiss, onCreatedGroup, props.navigation]
);
return (
<ScreenWrapper>
Expand All @@ -277,3 +293,26 @@ function CreateGroupScreen(
</ScreenWrapper>
);
}

function InviteUsersScreen(
props: NativeStackScreenProps<StackParamList, 'InviteUsers'>
) {
const { contacts } = useContext(ActionContext);
const currentUserId = useCurrentUserId();

return (
<ScreenWrapper>
<AppDataContextProvider
branchKey={BRANCH_KEY}
branchDomain={BRANCH_DOMAIN}
contacts={contacts ?? null}
currentUserId={currentUserId}
>
<InviteUsersWidget
group={props.route.params.group}
onInviteComplete={props.route.params.onInviteComplete}
/>
</AppDataContextProvider>
</ScreenWrapper>
);
}
1 change: 1 addition & 0 deletions apps/tlon-mobile/src/components/AuthenticatedApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function AuthenticatedApp({
shipName: ship ?? '',
shipUrl: shipUrl ?? '',
onReset: () => sync.syncStart(),
verbose: __DEV__,
onChannelReset: () => sync.handleDiscontinuity(),
});

Expand Down
23 changes: 18 additions & 5 deletions apps/tlon-mobile/src/controllers/ChatListScreenController.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
import { BRANCH_DOMAIN, BRANCH_KEY } from '@tloncorp/app/constants';
import ChatListScreen from '@tloncorp/app/features/top/ChatListScreen';
import * as db from '@tloncorp/shared/dist/db';
import * as store from '@tloncorp/shared/dist/store';
import { AppDataContextProvider } from '@tloncorp/ui';
import { useCallback, useState } from 'react';

import AddGroupSheet from '../components/AddGroupSheet';
Expand Down Expand Up @@ -37,6 +40,8 @@ export function ChatListScreenController({
[goToChannel]
);

const { data: contacts } = store.useContacts();

return (
<>
<ChatListScreen
Expand All @@ -61,12 +66,20 @@ export function ChatListScreenController({
navigateToProfile={() => {
navigation.navigate('Profile');
}}
branchDomain={BRANCH_DOMAIN}
branchKey={BRANCH_KEY}
/>
<AddGroupSheet
open={addGroupOpen}
onOpenChange={handleAddGroupOpenChange}
onCreatedGroup={handleGroupCreated}
/>
<AppDataContextProvider
contacts={contacts ?? []}
branchDomain={BRANCH_DOMAIN}
branchKey={BRANCH_KEY}
>
<AddGroupSheet
open={addGroupOpen}
onOpenChange={handleAddGroupOpenChange}
onCreatedGroup={handleGroupCreated}
/>
</AppDataContextProvider>
</>
);
}
17 changes: 16 additions & 1 deletion apps/tlon-mobile/src/screens/GroupSettings/GroupMetaScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { BRANCH_DOMAIN, BRANCH_KEY } from '@tloncorp/app/constants';
import { useGroupContext } from '@tloncorp/app/hooks/useGroupContext';
import * as db from '@tloncorp/shared/dist/db';
import * as store from '@tloncorp/shared/dist/store';
import { uploadAsset, useCanUpload } from '@tloncorp/shared/dist/store';
import {
AttachmentProvider,
Expand All @@ -24,13 +26,26 @@ export function GroupMetaScreen(props: GroupMetaScreenProps) {
});
const canUpload = useCanUpload();
const [showDeleteSheet, setShowDeleteSheet] = useState(false);
const { enabled, describe } = store.useLure({
flag: groupId,
branchDomain: BRANCH_DOMAIN,
branchKey: BRANCH_KEY,
});

const handleSubmit = useCallback(
(data: db.ClientMeta) => {
setGroupMetadata(data);
props.navigation.goBack();
if (enabled) {
describe({
title: data.title ?? '',
description: data.description ?? '',
image: data.iconImage ?? '',
cover: data.coverImage ?? '',
});
}
},
[setGroupMetadata, props.navigation]
[setGroupMetadata, props.navigation, enabled, describe]
);

const handlePressDelete = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BRANCH_DOMAIN, BRANCH_KEY } from '@tloncorp/app/constants';
import ChatListScreen from '@tloncorp/app/features/top/ChatListScreen';
import { isDmChannelId } from '@tloncorp/shared/dist';
import * as db from '@tloncorp/shared/dist/db';
Expand Down Expand Up @@ -75,6 +76,8 @@ export function ChatListScreenController() {
navigateToProfile={() => {
navigate('/profile');
}}
branchKey={BRANCH_KEY}
branchDomain={BRANCH_DOMAIN}
/>
{/*
<AddGroupSheet
Expand Down
23 changes: 22 additions & 1 deletion packages/app/features/top/ChatListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
FloatingAddButton,
GroupPreviewSheet,
Icon,
InviteUsersSheet,
NavBarView,
RequestsProvider,
ScreenHeader,
Expand Down Expand Up @@ -48,6 +49,8 @@ export default function ChatListScreen({
navigateToHome,
navigateToNotifications,
navigateToProfile,
branchDomain,
branchKey,
}: {
startDmOpen: boolean;
setStartDmOpen: (open: boolean) => void;
Expand All @@ -58,8 +61,11 @@ export default function ChatListScreen({
navigateToHome: () => void;
navigateToNotifications: () => void;
navigateToProfile: () => void;
branchDomain: string;
branchKey: string;
}) {
const [screenTitle, setScreenTitle] = useState('Home');
const [inviteSheetGroup, setInviteSheetGroup] = useState<db.Group | null>();
const chatOptionsSheetRef = useRef<ChatOptionsSheetMethods>(null);
const [longPressedChat, setLongPressedChat] = useState<
db.Channel | db.Group | null
Expand Down Expand Up @@ -119,7 +125,6 @@ export default function ChatListScreen({
[navigateToDm, setStartDmOpen]
);


const [isChannelSwitcherEnabled] = useFeatureFlag('channelSwitcher');

const onPressChat = useCallback(
Expand Down Expand Up @@ -166,6 +171,11 @@ export default function ChatListScreen({
}
}, []);

const handleInviteSheetOpenChange = useCallback((open: boolean) => {
if (!open) {
setInviteSheetGroup(null);
}
}, []);

const { pinned: pinnedChats, unpinned } = resolvedChats;
const allChats = [...pinnedChats, ...unpinned];
Expand Down Expand Up @@ -226,6 +236,8 @@ export default function ChatListScreen({
<AppDataContextProvider
currentUserId={currentUser}
contacts={contacts ?? []}
branchKey={branchKey}
branchDomain={branchDomain}
>
<RequestsProvider
usePostReference={store.usePostReference}
Expand All @@ -239,6 +251,9 @@ export default function ChatListScreen({
groupId={chatOptionsGroupId}
pinned={pinned}
{...useChatSettingsNavigation()}
onPressInvite={(group) => {
setInviteSheetGroup(group);
}}
>
<View flex={1}>
<ScreenHeader
Expand Down Expand Up @@ -290,6 +305,12 @@ export default function ChatListScreen({
onOpenChange={handleGroupPreviewSheetOpenChange}
group={selectedGroup ?? undefined}
/>
<InviteUsersSheet
open={inviteSheetGroup !== null}
onOpenChange={handleInviteSheetOpenChange}
onInviteComplete={() => setInviteSheetGroup(null)}
group={inviteSheetGroup ?? undefined}
/>
</View>
<NavBarView
navigateToHome={() => {
Expand Down
3 changes: 3 additions & 0 deletions packages/app/lib/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,20 @@ export function configureClient({
shipUrl,
onReset,
onChannelReset,
verbose,
}: {
shipName: string;
shipUrl: string;
onReset?: () => void;
onChannelReset?: () => void;
verbose?: boolean;
}) {
api.configureClient({
shipName,
shipUrl,
fetchFn: apiFetch,
onReset,
onChannelReset,
verbose,
});
}
40 changes: 40 additions & 0 deletions packages/shared/src/api/groupsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,46 @@ export function cancelGroupJoin(groupId: string) {
});
}

export function inviteGroupMembers({
groupId,
contactIds,
}: {
groupId: string;
contactIds: string[];
}) {
return poke(
groupAction(groupId, {
cordon: {
shut: {
'add-ships': {
ships: contactIds,
kind: 'pending',
},
},
},
})
);
}

export function addGroupMembers({
groupId,
contactIds,
}: {
groupId: string;
contactIds: string[];
}) {
return poke(
groupAction(groupId, {
fleet: {
ships: contactIds,
diff: {
add: null,
},
},
})
);
}

export function rescindGroupInvitationRequest(groupId: string) {
logger.log('api rescinding', groupId);
return poke({
Expand Down
7 changes: 6 additions & 1 deletion packages/shared/src/api/urbit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,13 @@ export const subscribeOnce = async <T>(
endpoint: UrbitEndpoint,
timeout?: number
) => {
if (!clientInstance) {
throw new Error(
'Tried to subscribe once, but Urbit client is not initialized'
);
}
logger.log('subscribing once to', printEndpoint(endpoint));
return client.subscribeOnce<T>(endpoint.app, endpoint.path, timeout);
return clientInstance.subscribeOnce<T>(endpoint.app, endpoint.path, timeout);
};

export const configureApi = (shipName: string, shipUrl: string) => {
Expand Down
35 changes: 35 additions & 0 deletions packages/shared/src/logic/subscriptionTracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export interface PreviewCheck {
inProgress: boolean;
attempted: number;
}

const DEFAULT_WAIT = 10 * 60 * 1000;

export function getPreviewTracker(wait = DEFAULT_WAIT) {
const tracked: Record<string, PreviewCheck> = {};

const getPreviewTracking = (k: string) =>
tracked[k] || {
inProgress: false,
attempted: 0,
};

const isPastWaiting = (attempted: number) => Date.now() - attempted >= wait;

return {
tracked,
shouldLoad: (k: string) => {
const { attempted, inProgress } = getPreviewTracking(k);
return isPastWaiting(attempted) && !inProgress;
},
newAttempt: (k: string) => {
tracked[k] = {
inProgress: true,
attempted: Date.now(),
};
},
finished: (k: string) => {
tracked[k].inProgress = false;
},
};
}
Loading