From 6240ec62badb4cd6691b7c6784287b1134589a97 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 4 Dec 2024 00:34:04 -0600 Subject: [PATCH 1/6] initial try --- .../src/components/AuthenticatedApp.tsx | 2 + .../src/hooks/useFindContactSuggestions.ts | 10 ++ desk/app/groups-ui.hoon | 28 +++- desk/mar/ships.hoon | 1 + desk/mar/ui/add-contact-suggestions.hoon | 2 + packages/app/features/top/ContactsScreen.tsx | 48 +++--- packages/shared/src/api/contactsApi.ts | 15 +- packages/shared/src/db/keyValue.ts | 5 + packages/shared/src/db/queries.ts | 26 ++++ packages/shared/src/store/contactActions.ts | 142 +++++++++++++++++- packages/shared/src/store/dbHooks.ts | 8 + .../ui/src/components/ContactsScreenView.tsx | 12 +- 12 files changed, 259 insertions(+), 40 deletions(-) create mode 100644 apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts create mode 100644 desk/mar/ui/add-contact-suggestions.hoon diff --git a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx index c60cab2bd4..add787441a 100644 --- a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx +++ b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx @@ -15,6 +15,7 @@ import { AppStateStatus } from 'react-native'; import { useCheckAppUpdated } from '../hooks/analytics'; import { useDeepLinkListener } from '../hooks/useDeepLinkListener'; +import useFindContactSuggestions from '../hooks/useFindContactSuggestions'; import useNotificationListener from '../hooks/useNotificationListener'; function AuthenticatedApp() { @@ -29,6 +30,7 @@ function AuthenticatedApp() { useNavigationLogging(); useNetworkLogger(); useCheckAppUpdated(); + useFindContactSuggestions(); useEffect(() => { configureClient(); diff --git a/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts b/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts new file mode 100644 index 0000000000..f335958915 --- /dev/null +++ b/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts @@ -0,0 +1,10 @@ +import * as store from '@tloncorp/shared/store'; +import { useEffect } from 'react'; + +export default function useFindContactSuggestions() { + const { data: numJoinedGroups } = store.useJoinedGroupsCount(); + + useEffect(() => { + store.findContactSuggestions(); + }, [numJoinedGroups]); +} diff --git a/desk/app/groups-ui.hoon b/desk/app/groups-ui.hoon index 193615b82f..050911842e 100644 --- a/desk/app/groups-ui.hoon +++ b/desk/app/groups-ui.hoon @@ -7,8 +7,9 @@ |% +$ card card:agent:gall +$ current-state - $: %2 + $: %3 hidden-contact-suggestions=(set ship) + manual-contact-suggestions=(set ship) pins=(list whom:u) first-load=? == @@ -90,18 +91,27 @@ =? old ?=(~ old) *current-state =? old ?=(%0 -.old) (state-0-to-1 old) =? old ?=(%1 -.old) (state-1-to-2 old) - ?> ?=(%2 -.old) + =? old ?=(%2 -.old) (state-2-to-3 old) + ?> ?=(%3 -.old) =. state old init :: - +$ versioned-state $@(~ $%(state-2 state-1 state-0)) - +$ state-2 current-state + +$ versioned-state $@(~ $%(state-3 state-2 state-1 state-0)) + +$ state-3 current-state + +$ state-2 + $: %2 + hidden-contact-suggestions=(set ship) + pins=(list whom:u) + first-load=? + == +$ state-1 $: %1 pins=(list whom:u) first-load=? == - :: + :: + ++ state-2-to-3 + |=(state-2 [%3 hidden-contact-suggestions ~ pins first-load]) ++ state-1-to-2 |=(state-1 [%2 ~ pins first-load]) +$ state-0 [%0 first-load=?] @@ -227,6 +237,12 @@ =. hidden-contact-suggestions (~(put in hidden-contact-suggestions) ship) cor + :: + %ui-add-contact-suggestions + =+ ship-list=!<((list @p) vase) + =. manual-contact-suggestions + (~(uni in manual-contact-suggestions) (sy ship-list)) + cor :: %ui-vita-toggle =+ !<(=vita-enabled:u vase) @@ -288,6 +304,8 @@ =? suggestions pals-running =+ .^(targets=(set ship) (scry %gx %pals /targets/noun)) (~(uni in suggestions) targets) + =. suggestions + (~(uni in suggestions) manual-contact-suggestions) (~(dif in suggestions) hidden-contact-suggestions) ++ import-pals =+ .^(pals-running=? (scry %gu %pals /$)) diff --git a/desk/mar/ships.hoon b/desk/mar/ships.hoon index e8a882d113..c04d494559 100644 --- a/desk/mar/ships.hoon +++ b/desk/mar/ships.hoon @@ -10,5 +10,6 @@ ++ grab |% ++ noun ,(set ship) + ++ json (ar:dejs:format ship:dejs:j) -- -- diff --git a/desk/mar/ui/add-contact-suggestions.hoon b/desk/mar/ui/add-contact-suggestions.hoon new file mode 100644 index 0000000000..5e6a1552f7 --- /dev/null +++ b/desk/mar/ui/add-contact-suggestions.hoon @@ -0,0 +1,2 @@ +/= mark /mar/ships +mark diff --git a/packages/app/features/top/ContactsScreen.tsx b/packages/app/features/top/ContactsScreen.tsx index 67a8ebd6a4..29ad9215b8 100644 --- a/packages/app/features/top/ContactsScreen.tsx +++ b/packages/app/features/top/ContactsScreen.tsx @@ -37,30 +37,34 @@ export default function ContactsScreen(props: Props) { ); const onContactLongPress = useCallback((contact: db.Contact) => { - if (!isWeb && contact.isContactSuggestion) { - Alert.alert(`Add ${getDisplayName(contact)}?`, '', [ - { - text: 'Cancel', - style: 'cancel', - }, - { - text: 'Add Contact', - style: 'default', - onPress: () => { - store.addContact(contact.id); - }, - }, - { - text: 'Decline Suggestion', - style: 'destructive', - onPress: () => { - store.removeContactSuggestion(contact.id); - }, - }, - ]); - } + store.addContactSuggestions([contact.id]); }, []); + // const onContactLongPress = useCallback((contact: db.Contact) => { + // if (!isWeb && contact.isContactSuggestion) { + // Alert.alert(`Add ${getDisplayName(contact)}?`, '', [ + // { + // text: 'Cancel', + // style: 'cancel', + // }, + // { + // text: 'Add Contact', + // style: 'default', + // onPress: () => { + // store.addContact(contact.id); + // }, + // }, + // { + // text: 'Decline Suggestion', + // style: 'destructive', + // onPress: () => { + // store.removeContactSuggestion(contact.id); + // }, + // }, + // ]); + // } + // }, []); + return ( diff --git a/packages/shared/src/api/contactsApi.ts b/packages/shared/src/api/contactsApi.ts index 9f720645e0..887c01b0c7 100644 --- a/packages/shared/src/api/contactsApi.ts +++ b/packages/shared/src/api/contactsApi.ts @@ -47,11 +47,20 @@ export const removeContactSuggestion = async (contactId: string) => { }); }; -export const addContacts = async (contactIds: string[]) => { +export const addContactSuggestions = async (contactIds: string[]) => { + console.log(`firing poke with`, contactIds); + return poke({ + app: 'groups-ui', + mark: 'ui-add-contact-suggestions', + json: contactIds, + }); +}; + +export const syncUserProfiles = async (userIds: string[]) => { return poke({ app: 'contacts', - mark: 'contact-action', - json: { heed: contactIds }, + mark: 'contact-action-1', + json: { meet: userIds }, }); }; diff --git a/packages/shared/src/db/keyValue.ts b/packages/shared/src/db/keyValue.ts index 2ca38c6472..c18055201b 100644 --- a/packages/shared/src/db/keyValue.ts +++ b/packages/shared/src/db/keyValue.ts @@ -298,6 +298,11 @@ export const finishingSelfHostedLogin = createStorageItem({ defaultValue: false, }); +export const groupsUsedForSuggestions = createStorageItem({ + key: 'groupsUsedForSuggestions', + defaultValue: [], +}); + export const postDraft = (opts: { key: string; type: 'caption' | 'text' | undefined; // matches GalleryDraftType diff --git a/packages/shared/src/db/queries.ts b/packages/shared/src/db/queries.ts index cce64ed748..e341ff5742 100644 --- a/packages/shared/src/db/queries.ts +++ b/packages/shared/src/db/queries.ts @@ -153,6 +153,32 @@ export const getGroupPreviews = createReadQuery( ['groups'] ); +export const getJoinedGroupsCount = createReadQuery( + 'getJoinedGroupCount', + async (ctx: QueryCtx) => { + const joinedGroups = await ctx.db.query.groups.findMany({ + where: eq($groups.currentUserIsMember, true), + }); + return joinedGroups.length; + }, + ['groups'] +); + +export const getGroupsWithMemberThreshold = createReadQuery( + 'getGroupsWithMemberThreshold', + async (threshold: number, ctx: QueryCtx) => { + const allJoinedWithMembers = await ctx.db.query.groups.findMany({ + where: eq($groups.currentUserIsMember, true), + with: { + members: true, + }, + }); + + return allJoinedWithMembers.filter((g) => g.members.length <= threshold); + }, + ['groups'] +); + export const getGroups = createReadQuery( 'getGroups', async ( diff --git a/packages/shared/src/store/contactActions.ts b/packages/shared/src/store/contactActions.ts index edbd8658eb..0ef53e4a37 100644 --- a/packages/shared/src/store/contactActions.ts +++ b/packages/shared/src/store/contactActions.ts @@ -1,8 +1,9 @@ import * as api from '../api'; import * as db from '../db'; import { createDevLogger } from '../debug'; +import { syncContacts, syncGroup } from './sync'; -const logger = createDevLogger('ContactActions', false); +const logger = createDevLogger('ContactActions', true); export async function addContact(contactId: string) { // Optimistic update @@ -71,6 +72,145 @@ export async function removeContactSuggestion(contactId: string) { } } +export async function addContactSuggestions(contactIds: string[]) { + // optimistic update + const contacts = await db.getContacts(); + const toUpdate = contacts.filter( + (c) => contactIds.includes(c.id) && !c.isContact + ); + const optimisticUpdates = toUpdate.map((contact) => + db.updateContact({ id: contact.id, isContactSuggestion: true }) + ); + await Promise.all(optimisticUpdates); + + try { + await api.addContactSuggestions(contactIds); + } catch (e) { + // Rollback the update + const rolbacks = toUpdate.map((contact) => + db.updateContact({ id: contact.id, isContactSuggestion: false }) + ); + await Promise.all(rolbacks); + } +} + +export async function findContactSuggestions() { + const runContext: Record = {}; + const currentUserId = api.getCurrentUserId(); + const GROUP_SIZE_LIMIT = 32; // arbitrary + const MAX_SUGGESTIONS = 6; // arbitrary + + try { + // first see if we have any joined groups and seem to be a somewhat + // new user + const groups = await db.getGroups({ includeUnjoined: false }); + runContext.joinedGroups = groups.length; + const hasFewGroups = groups.length < 4; + runContext.hasFewGroups = hasFewGroups; + + if (groups.length > 0 && hasFewGroups) { + logger.crumb('Found joined groups'); + // if yes, see if we have new groups and if some are small enough that + // grabbing suggestions at random might be worthwhile + const groupSyncs = groups.map((group) => syncGroup(group.id)); // sync member lists + await Promise.all(groupSyncs); + + const groupchats = + await db.getGroupsWithMemberThreshold(GROUP_SIZE_LIMIT); + runContext.groupsWithinSizeLimit = groupchats.length; + const groupsFromLastRun = await db.groupsUsedForSuggestions.getValue(); + const haveSomeNewGroups = groupchats.some( + (gc) => !groupsFromLastRun.includes(gc.id) + ); + runContext.haveSomeNewGroups = haveSomeNewGroups; + if (groupchats.length > 0 && haveSomeNewGroups) { + logger.crumb('Found groups under size limit'); + // if some are, load the profiles of all(?) members + const allRelevantMembers = groupchats + .reduce((acc, group) => { + return acc.concat(group.members.map((mem) => mem.contactId)); + }, [] as string[]) + .filter((mem) => mem !== currentUserId); + + logger.crumb(`Found ${allRelevantMembers.length} relevant members`); + + await api.syncUserProfiles(allRelevantMembers); + // hack: we don't track when the profiles actually populate, so wait a bit then resync + await new Promise((resolve) => setTimeout(resolve, 5000)); + await syncContacts(); + + logger.crumb('Synced profiles and contacts'); + + const contacts = await db.getContacts(); + const memberSet = new Set(allRelevantMembers); + const memberContacts = contacts.filter( + (c) => memberSet.has(c.id) && !c.isContact && !c.isContactSuggestion + ); + runContext.relevantMembers = memberContacts.length; + + // welcome to my suggestion ranking algorithm + const contactScores = memberContacts.map((contact) => { + let score = 0; + if (contact.nickname) { + score += 10; + } + + if (contact.pinnedGroups.length > 0) { + score += 5; + } + + if (contact.avatarImage) { + score += 3; + } + + if (contact.bio) { + score += 2; + } + + if (contact.status) { + score += 1; + } + + return { userId: contact.id, score }; + }); + + contactScores + .filter((item) => item.score > 0) + .sort((a, b) => b.score - a.score); + logger.crumb('Scored relevant members'); + + const suggestions = contactScores + .slice(0, MAX_SUGGESTIONS) + .map((s) => s.userId); + runContext.suggestions = suggestions.length; + + logger.crumb(`Found ${suggestions.length} suggestions`); + db.groupsUsedForSuggestions.setValue(groupchats.map((g) => g.id)); + + if (suggestions.length > 0) { + await addContactSuggestions(suggestions); + logger.trackEvent('Client Contact Suggestions', { + ...runContext, + suggestionsFound: true, + }); + return true; + } + } + } + logger.trackEvent('Client Contact Suggestions', { + ...runContext, + suggestionsFound: false, + }); + } catch (e) { + logger.trackError('Client Contact Suggestions Failure', { + errorMessage: e.message, + errorStack: e.stack, + }); + } + logger.log('No suggestions added'); + return false; +} + export async function updateContactMetadata( contactId: string, metadata: { diff --git a/packages/shared/src/store/dbHooks.ts b/packages/shared/src/store/dbHooks.ts index e357e5f33b..b1f1768554 100644 --- a/packages/shared/src/store/dbHooks.ts +++ b/packages/shared/src/store/dbHooks.ts @@ -328,6 +328,14 @@ export const useGroup = (options: { id?: string }) => { }); }; +export const useJoinedGroupsCount = () => { + const deps = useKeyFromQueryDeps(db.getJoinedGroupsCount); + return useQuery({ + queryKey: ['joinedGroupsCount', deps], + queryFn: () => db.getJoinedGroupsCount(), + }); +}; + export const useGroupByChannel = (channelId: string) => { return useQuery({ queryKey: [['group', channelId]], diff --git a/packages/ui/src/components/ContactsScreenView.tsx b/packages/ui/src/components/ContactsScreenView.tsx index a93ff9aa30..f4a5b4b424 100644 --- a/packages/ui/src/components/ContactsScreenView.tsx +++ b/packages/ui/src/components/ContactsScreenView.tsx @@ -25,12 +25,6 @@ interface Section { export function ContactsScreenView(props: Props) { const currentUserId = useCurrentUserId(); const userContact = useContact(currentUserId); - const trimmedSuggested = useMemo(() => { - if (props.suggestions.length < 4 || props.contacts.length === 0) { - return props.suggestions; - } - return props.suggestions.slice(0, 4); - }, [props.contacts, props.suggestions]); const sortedContacts = useSortedContacts({ contacts: props.contacts, @@ -52,15 +46,15 @@ export function ContactsScreenView(props: Props) { }); } - if (trimmedSuggested.length > 0) { + if (props.suggestions.length > 0) { result.push({ title: 'Suggested from %pals and DMs', - data: trimmedSuggested, + data: props.suggestions, }); } return result; - }, [userContact, sortedContacts, trimmedSuggested]); + }, [userContact, sortedContacts, props.suggestions]); const renderItem = useCallback( ({ item }: { item: db.Contact }) => { From 8a33678dd137b8bbe3be65ed87db5123da637a57 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 4 Dec 2024 00:36:56 -0600 Subject: [PATCH 2/6] cleanup --- packages/app/features/top/ContactsScreen.tsx | 48 +++++++++----------- packages/shared/src/store/contactActions.ts | 2 +- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/packages/app/features/top/ContactsScreen.tsx b/packages/app/features/top/ContactsScreen.tsx index 29ad9215b8..67a8ebd6a4 100644 --- a/packages/app/features/top/ContactsScreen.tsx +++ b/packages/app/features/top/ContactsScreen.tsx @@ -37,34 +37,30 @@ export default function ContactsScreen(props: Props) { ); const onContactLongPress = useCallback((contact: db.Contact) => { - store.addContactSuggestions([contact.id]); + if (!isWeb && contact.isContactSuggestion) { + Alert.alert(`Add ${getDisplayName(contact)}?`, '', [ + { + text: 'Cancel', + style: 'cancel', + }, + { + text: 'Add Contact', + style: 'default', + onPress: () => { + store.addContact(contact.id); + }, + }, + { + text: 'Decline Suggestion', + style: 'destructive', + onPress: () => { + store.removeContactSuggestion(contact.id); + }, + }, + ]); + } }, []); - // const onContactLongPress = useCallback((contact: db.Contact) => { - // if (!isWeb && contact.isContactSuggestion) { - // Alert.alert(`Add ${getDisplayName(contact)}?`, '', [ - // { - // text: 'Cancel', - // style: 'cancel', - // }, - // { - // text: 'Add Contact', - // style: 'default', - // onPress: () => { - // store.addContact(contact.id); - // }, - // }, - // { - // text: 'Decline Suggestion', - // style: 'destructive', - // onPress: () => { - // store.removeContactSuggestion(contact.id); - // }, - // }, - // ]); - // } - // }, []); - return ( diff --git a/packages/shared/src/store/contactActions.ts b/packages/shared/src/store/contactActions.ts index 0ef53e4a37..b780321fc6 100644 --- a/packages/shared/src/store/contactActions.ts +++ b/packages/shared/src/store/contactActions.ts @@ -3,7 +3,7 @@ import * as db from '../db'; import { createDevLogger } from '../debug'; import { syncContacts, syncGroup } from './sync'; -const logger = createDevLogger('ContactActions', true); +const logger = createDevLogger('ContactActions', false); export async function addContact(contactId: string) { // Optimistic update From 83ff238eab4141b5a2e0a4cd571600c029d21c73 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 4 Dec 2024 00:54:47 -0600 Subject: [PATCH 3/6] stray log --- packages/shared/src/api/contactsApi.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared/src/api/contactsApi.ts b/packages/shared/src/api/contactsApi.ts index 887c01b0c7..84eabbdb05 100644 --- a/packages/shared/src/api/contactsApi.ts +++ b/packages/shared/src/api/contactsApi.ts @@ -48,7 +48,6 @@ export const removeContactSuggestion = async (contactId: string) => { }; export const addContactSuggestions = async (contactIds: string[]) => { - console.log(`firing poke with`, contactIds); return poke({ app: 'groups-ui', mark: 'ui-add-contact-suggestions', From 5a32dbd6d8b5f121b6883c0db626652b5853acc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?b=E1=B5=A3=E1=B5=A2=E2=82=90=E2=82=99?= <90741358+latter-bolden@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:02:58 -0600 Subject: [PATCH 4/6] Update desk/app/groups-ui.hoon Co-authored-by: fang --- desk/app/groups-ui.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/app/groups-ui.hoon b/desk/app/groups-ui.hoon index 050911842e..5b397d27af 100644 --- a/desk/app/groups-ui.hoon +++ b/desk/app/groups-ui.hoon @@ -241,7 +241,7 @@ %ui-add-contact-suggestions =+ ship-list=!<((list @p) vase) =. manual-contact-suggestions - (~(uni in manual-contact-suggestions) (sy ship-list)) + (~(gas in manual-contact-suggestions) ship-list) cor :: %ui-vita-toggle From 752e0a3106a54fbecf9cdbc3de508a8f15875546 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 4 Dec 2024 12:10:07 -0600 Subject: [PATCH 5/6] initialize variable to manual suggestions rather than bunt + add them in later --- desk/app/groups-ui.hoon | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/desk/app/groups-ui.hoon b/desk/app/groups-ui.hoon index 5b397d27af..4646bef2b3 100644 --- a/desk/app/groups-ui.hoon +++ b/desk/app/groups-ui.hoon @@ -285,7 +285,7 @@ == ++ get-suggested-contacts =+ .^(chat-running=? (scry %gu %chat /$)) - =| suggestions=(set ship) + =/ suggestions=(set ship) manual-contact-suggestions =? suggestions chat-running =+ .^ [dms=(map ship dm:c) *] (scry %gx %chat /full/noun) @@ -304,8 +304,6 @@ =? suggestions pals-running =+ .^(targets=(set ship) (scry %gx %pals /targets/noun)) (~(uni in suggestions) targets) - =. suggestions - (~(uni in suggestions) manual-contact-suggestions) (~(dif in suggestions) hidden-contact-suggestions) ++ import-pals =+ .^(pals-running=? (scry %gu %pals /$)) From afa2b0e78ac2bc41430b732792b7a381abe21fd4 Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Wed, 4 Dec 2024 14:22:17 -0600 Subject: [PATCH 6/6] move findContacts to sync start, remove unneeded hooks, make note of member query perf for future optimization --- apps/tlon-mobile/src/components/AuthenticatedApp.tsx | 2 -- .../src/hooks/useFindContactSuggestions.ts | 10 ---------- packages/shared/src/db/queries.ts | 12 +----------- packages/shared/src/store/contactActions.ts | 7 ++----- packages/shared/src/store/dbHooks.ts | 8 -------- packages/shared/src/store/sync.ts | 5 +++++ 6 files changed, 8 insertions(+), 36 deletions(-) delete mode 100644 apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts diff --git a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx index add787441a..c60cab2bd4 100644 --- a/apps/tlon-mobile/src/components/AuthenticatedApp.tsx +++ b/apps/tlon-mobile/src/components/AuthenticatedApp.tsx @@ -15,7 +15,6 @@ import { AppStateStatus } from 'react-native'; import { useCheckAppUpdated } from '../hooks/analytics'; import { useDeepLinkListener } from '../hooks/useDeepLinkListener'; -import useFindContactSuggestions from '../hooks/useFindContactSuggestions'; import useNotificationListener from '../hooks/useNotificationListener'; function AuthenticatedApp() { @@ -30,7 +29,6 @@ function AuthenticatedApp() { useNavigationLogging(); useNetworkLogger(); useCheckAppUpdated(); - useFindContactSuggestions(); useEffect(() => { configureClient(); diff --git a/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts b/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts deleted file mode 100644 index f335958915..0000000000 --- a/apps/tlon-mobile/src/hooks/useFindContactSuggestions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as store from '@tloncorp/shared/store'; -import { useEffect } from 'react'; - -export default function useFindContactSuggestions() { - const { data: numJoinedGroups } = store.useJoinedGroupsCount(); - - useEffect(() => { - store.findContactSuggestions(); - }, [numJoinedGroups]); -} diff --git a/packages/shared/src/db/queries.ts b/packages/shared/src/db/queries.ts index e341ff5742..373aa48ff9 100644 --- a/packages/shared/src/db/queries.ts +++ b/packages/shared/src/db/queries.ts @@ -153,17 +153,7 @@ export const getGroupPreviews = createReadQuery( ['groups'] ); -export const getJoinedGroupsCount = createReadQuery( - 'getJoinedGroupCount', - async (ctx: QueryCtx) => { - const joinedGroups = await ctx.db.query.groups.findMany({ - where: eq($groups.currentUserIsMember, true), - }); - return joinedGroups.length; - }, - ['groups'] -); - +// TODO: inefficient, should optimize export const getGroupsWithMemberThreshold = createReadQuery( 'getGroupsWithMemberThreshold', async (threshold: number, ctx: QueryCtx) => { diff --git a/packages/shared/src/store/contactActions.ts b/packages/shared/src/store/contactActions.ts index b780321fc6..93a5bf333c 100644 --- a/packages/shared/src/store/contactActions.ts +++ b/packages/shared/src/store/contactActions.ts @@ -86,11 +86,8 @@ export async function addContactSuggestions(contactIds: string[]) { try { await api.addContactSuggestions(contactIds); } catch (e) { - // Rollback the update - const rolbacks = toUpdate.map((contact) => - db.updateContact({ id: contact.id, isContactSuggestion: false }) - ); - await Promise.all(rolbacks); + // Intentionally unhandled, make a best effort to persist the suggestions + // failure is acceptable } } diff --git a/packages/shared/src/store/dbHooks.ts b/packages/shared/src/store/dbHooks.ts index b1f1768554..e357e5f33b 100644 --- a/packages/shared/src/store/dbHooks.ts +++ b/packages/shared/src/store/dbHooks.ts @@ -328,14 +328,6 @@ export const useGroup = (options: { id?: string }) => { }); }; -export const useJoinedGroupsCount = () => { - const deps = useKeyFromQueryDeps(db.getJoinedGroupsCount); - return useQuery({ - queryKey: ['joinedGroupsCount', deps], - queryFn: () => db.getJoinedGroupsCount(), - }); -}; - export const useGroupByChannel = (channelId: string) => { return useQuery({ queryKey: [['group', channelId]], diff --git a/packages/shared/src/store/sync.ts b/packages/shared/src/store/sync.ts index bed4de2db6..034e4ace09 100644 --- a/packages/shared/src/store/sync.ts +++ b/packages/shared/src/store/sync.ts @@ -12,6 +12,7 @@ import { INFINITE_ACTIVITY_QUERY_KEY, resetActivityFetchers, } from '../store/useActivityFetchers'; +import { findContactSuggestions } from './contactActions'; import { useLureState } from './lure'; import { getSyncing, updateIsSyncing, updateSession } from './session'; import { SyncCtx, SyncPriority, syncQueue } from './syncQueue'; @@ -1174,6 +1175,10 @@ export const syncStart = async (alreadySubscribed?: boolean) => { }); updateIsSyncing(false); + + // finding contacts is a bit of an outlier here, but it's work we need to do + // that can roughly be batched whenever we sync + findContactSuggestions(); }; export const setupHighPrioritySubscriptions = async (ctx?: SyncCtx) => {