Skip to content
Open
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
61 changes: 53 additions & 8 deletions shared/constants/init/shared.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import type * as UseTeamsStateType from '@/stores/teams'
import type * as UseTracker2StateType from '@/stores/tracker2'
import type * as UseUnlockFoldersStateType from '@/stores/unlock-folders'
import type * as UseUsersStateType from '@/stores/users'
import {createTBStore} from '@/stores/team-building'
import {createTBStore, getTBStore} from '@/stores/team-building'
import {getSelectedConversation} from '@/constants/chat2/common'
import {handleKeybaseLink} from '@/constants/deeplinks'
import {ignorePromise} from '../utils'
Expand All @@ -41,6 +41,8 @@ import {useProvisionState} from '@/stores/provision'
import {usePushState} from '@/stores/push'
import {useSettingsContactsState} from '@/stores/settings-contacts'
import {useSettingsEmailState} from '@/stores/settings-email'
import {useSettingsPhoneState} from '@/stores/settings-phone'
import {useSettingsState} from '@/stores/settings'
import {useSignupState} from '@/stores/signup'
import {useState as useRecoverPasswordState} from '@/stores/recover-password'
import {useTeamsState} from '@/stores/teams'
Expand All @@ -49,7 +51,7 @@ import {useUsersState} from '@/stores/users'
import {useWhatsNewState} from '@/stores/whats-new'
import {useRouterState} from '@/stores/router2'
import * as Util from '@/constants/router2'
import {setOtherStores} from '@/stores/convostate'
import {setConvoDefer} from '@/stores/convostate'

let _emitStartupOnLoadDaemonConnectedOnce = false
let _devicesLoaded = false
Expand Down Expand Up @@ -373,12 +375,54 @@ export const initTracker2Callbacks = () => {
})
}

export const initSettingsCallbacks = () => {
const currentState = useSettingsState.getState()
useSettingsState.setState({
dispatch: {
...currentState.dispatch,
defer: {
...currentState.dispatch.defer,
getSettingsPhonePhones: () => {
return useSettingsPhoneState.getState().phones
},
onSettingsEmailNotifyEmailsChanged: (emails: ReadonlyArray<T.RPCChat.Keybase1.Email>) => {
useSettingsEmailState.getState().dispatch.notifyEmailAddressEmailsChanged(emails)
},
onSettingsPhoneSetNumbers: (phoneNumbers?: ReadonlyArray<T.RPCChat.Keybase1.UserPhoneNumber>) => {
useSettingsPhoneState.getState().dispatch.setNumbers(phoneNumbers)
},
},
},
})
}

export const initSharedSubscriptions = () => {
setOtherStores(
storeRegistry.getStore('chat'),
storeRegistry.getStore('teams'),
storeRegistry.getStore('users')
)
setConvoDefer({
chatBlockButtonsMapHas: teamID =>
storeRegistry.getState('chat').blockButtonsMap.has(teamID),
chatInboxLayoutSmallTeamsFirstConvID: () =>
storeRegistry.getState('chat').inboxLayout?.smallTeams?.[0]?.convID,
chatInboxRefresh: reason =>
storeRegistry.getState('chat').dispatch.inboxRefresh(reason),
chatMetasReceived: metas =>
storeRegistry.getState('chat').dispatch.metasReceived(metas),
chatNavigateToInbox: () =>
storeRegistry.getState('chat').dispatch.navigateToInbox(),
chatPaymentInfoReceived: (_messageID, paymentInfo) =>
storeRegistry.getState('chat').dispatch.paymentInfoReceived(paymentInfo),
chatPreviewConversation: p =>
storeRegistry.getState('chat').dispatch.previewConversation(p),
chatResetConversationErrored: () =>
storeRegistry.getState('chat').dispatch.resetConversationErrored(),
chatUnboxRows: (convIDs, force) =>
storeRegistry.getState('chat').dispatch.unboxRows(convIDs, force),
chatUpdateInfoPanel: (show, tab) =>
storeRegistry.getState('chat').dispatch.updateInfoPanel(show, tab),
teamsGetMembers: teamID =>
storeRegistry.getState('teams').dispatch.getMembers(teamID),
usersGetBio: username =>
storeRegistry.getState('users').dispatch.getBio(username),
})
useConfigState.subscribe((s, old) => {
if (s.loadOnStartPhase !== old.loadOnStartPhase) {
if (s.loadOnStartPhase === 'startupOrReloginButNotInARush') {
Expand Down Expand Up @@ -581,7 +625,7 @@ export const initSharedSubscriptions = () => {
// team building or modal on top of that still
const isTeamBuilding = namespaceToRoute.get(namespace) === Util.getVisibleScreen(next)?.name
if (!isTeamBuilding) {
storeRegistry.getTBStore(namespace).dispatch.cancelTeamBuilding()
getTBStore(namespace).dispatch.cancelTeamBuilding()
}
}
}
Expand Down Expand Up @@ -651,6 +695,7 @@ export const initSharedSubscriptions = () => {
initProfileCallbacks()
initPushCallbacks()
initRecoverPasswordCallbacks()
initSettingsCallbacks()
initSignupCallbacks()
initTracker2Callbacks()
}
Expand Down
152 changes: 104 additions & 48 deletions shared/stores/convostate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ import * as Strings from '@/constants/strings'
import {useConfigState} from '@/stores/config'
import {useCurrentUserState} from '@/stores/current-user'
import type {useChatState} from '@/stores/chat2'
import type {useTeamsState} from '@/stores/teams'
import type {useUsersState} from '@/stores/users'

const {darwinCopyToChatTempUploadFile} = KB2.functions

Expand Down Expand Up @@ -227,6 +225,33 @@ export interface ConvoState extends ConvoStore {
botCommandsUpdateStatus: (b: T.RPCChat.UIBotCommandsUpdateStatus) => void
channelSuggestionsTriggered: () => void
clearAttachmentView: () => void
defer: {
chatBlockButtonsMapHas: (teamID: T.RPCGen.TeamID) => boolean
chatInboxLayoutSmallTeamsFirstConvID: () => T.Chat.ConversationIDKey | undefined
chatInboxRefresh: (reason: string) => void
chatMetasReceived: (metas: ReadonlyArray<T.Chat.ConversationMeta>) => void
chatNavigateToInbox: () => void
chatPaymentInfoReceived: (
messageID: T.Chat.MessageID,
paymentInfo: T.Chat.ChatPaymentInfo
) => void
chatPreviewConversation: (
p: Parameters<
ReturnType<typeof useChatState.getState>['dispatch']['previewConversation']
>[0]
) => void
chatResetConversationErrored: () => void
chatUnboxRows: (
convIDs: ReadonlyArray<T.Chat.ConversationIDKey>,
force: boolean
) => void
chatUpdateInfoPanel: (
show: boolean,
tab: 'settings' | 'members' | 'attachments' | 'bots' | undefined
) => void
teamsGetMembers: (teamID: T.RPCGen.TeamID) => void
usersGetBio: (username: string) => void
}
dismissBottomBanner: () => void
dismissBlockButtons: (teamID: T.RPCGen.TeamID) => void
dismissJourneycard: (cardType: T.RPCChat.JourneycardType, ordinal: T.Chat.Ordinal) => void
Expand Down Expand Up @@ -375,15 +400,68 @@ type ScrollDirection = 'none' | 'back' | 'forward'
export const numMessagesOnInitialLoad = isMobile ? 20 : 100
export const numMessagesOnScrollback = isMobile ? 100 : 100

const createSlice = (
chatStateHook: typeof useChatState,
teamsStateHook: typeof useTeamsState,
usersStateHook: typeof useUsersState
): Z.ImmerStateCreator<ConvoState> => (set, get) => {
const stubDefer: ConvoState['dispatch']['defer'] = {
chatBlockButtonsMapHas: () => {
throw new Error('convostate defer not initialized')
},
chatInboxLayoutSmallTeamsFirstConvID: () => {
throw new Error('convostate defer not initialized')
},
chatInboxRefresh: () => {
throw new Error('convostate defer not initialized')
},
chatMetasReceived: () => {
throw new Error('convostate defer not initialized')
},
chatNavigateToInbox: () => {
throw new Error('convostate defer not initialized')
},
chatPaymentInfoReceived: () => {
throw new Error('convostate defer not initialized')
},
chatPreviewConversation: () => {
throw new Error('convostate defer not initialized')
},
chatResetConversationErrored: () => {
throw new Error('convostate defer not initialized')
},
chatUnboxRows: () => {
throw new Error('convostate defer not initialized')
},
chatUpdateInfoPanel: () => {
throw new Error('convostate defer not initialized')
},
teamsGetMembers: () => {
throw new Error('convostate defer not initialized')
},
usersGetBio: () => {
throw new Error('convostate defer not initialized')
},
}

let convoDeferImpl: ConvoState['dispatch']['defer'] | undefined

export const setConvoDefer = (impl: ConvoState['dispatch']['defer']) => {
convoDeferImpl = impl
for (const store of chatStores.values()) {
const s = store.getState()
store.setState({
...s,
dispatch: {
...s.dispatch,
defer: impl,
},
})
}
}

const createSlice = (): Z.ImmerStateCreator<ConvoState> => (set, get) => {
const defer = convoDeferImpl ?? stubDefer

const closeBotModal = () => {
clearModals()
if (get().meta.teamname) {
teamsStateHook.getState().dispatch.getMembers(get().meta.teamID)
get().dispatch.defer.teamsGetMembers(get().meta.teamID)
}
}

Expand Down Expand Up @@ -501,7 +579,7 @@ const createSlice = (

const onClick = () => {
useConfigState.getState().dispatch.showMain()
chatStateHook.getState().dispatch.navigateToInbox()
get().dispatch.defer.chatNavigateToInbox()
get().dispatch.navigateToThread('desktopNotification')
}
const onClose = () => {}
Expand Down Expand Up @@ -638,7 +716,7 @@ const createSlice = (
logger.error(errMsg)
throw new Error(errMsg)
}
chatStateHook.getState().dispatch.paymentInfoReceived(paymentInfo)
get().dispatch.defer.chatPaymentInfoReceived(msgID, paymentInfo)
getConvoState(conversationIDKey).dispatch.paymentInfoReceived(msgID, paymentInfo)
}

Expand Down Expand Up @@ -1076,7 +1154,7 @@ const createSlice = (
}

// If there are block buttons on this conversation, clear them.
if (chatStateHook.getState().blockButtonsMap.has(meta.teamID)) {
if (get().dispatch.defer.chatBlockButtonsMapHas(meta.teamID)) {
get().dispatch.dismissBlockButtons(meta.teamID)
}

Expand Down Expand Up @@ -1244,7 +1322,7 @@ const createSlice = (
},
blockConversation: reportUser => {
const f = async () => {
chatStateHook.getState().dispatch.navigateToInbox()
get().dispatch.defer.chatNavigateToInbox()
useConfigState.getState().dispatch.defer.persistRoute?.()
await T.RPCChat.localSetConversationStatusLocalRpcPromise({
conversationID: get().getConvID(),
Expand Down Expand Up @@ -1308,6 +1386,7 @@ const createSlice = (
}
ignorePromise(f())
},
defer,
editBotSettings: (username, allowCommands, allowMentions, convs) => {
const f = async () => {
try {
Expand Down Expand Up @@ -1348,7 +1427,7 @@ const createSlice = (
// Nav to inbox but don't use findNewConversation since changeSelectedConversation
// does that with better information. It knows the conversation is hidden even before
// that state bounces back.
chatStateHook.getState().dispatch.navigateToInbox()
get().dispatch.defer.chatNavigateToInbox()
get().dispatch.showInfoPanel(false, undefined)
}

Expand Down Expand Up @@ -1401,7 +1480,7 @@ const createSlice = (
const params = vs?.params as undefined | {conversationIDKey?: T.Chat.ConversationIDKey}
if (params?.conversationIDKey === get().id) {
// select a convo
const next = chatStateHook.getState().inboxLayout?.smallTeams?.[0]?.convID
const next = get().dispatch.defer.chatInboxLayoutSmallTeamsFirstConvID()
if (next) {
getConvoState(next).dispatch.navigateToThread('findNewestConversationFromLayout')
}
Expand Down Expand Up @@ -1666,8 +1745,8 @@ const createSlice = (
logger.warn(`loadMoreMessages: error: ${error.desc}`)
// no longer in team
if (error.code === T.RPCGen.StatusCode.scchatnotinteam) {
chatStateHook.getState().dispatch.inboxRefresh('maybeKickedFromTeam')
chatStateHook.getState().dispatch.navigateToInbox()
get().dispatch.defer.chatInboxRefresh('maybeKickedFromTeam')
get().dispatch.defer.chatNavigateToInbox()
}
if (error.code !== T.RPCGen.StatusCode.scteamreaderror) {
// scteamreaderror = user is not in team. they'll see the rekey screen so don't throw for that
Expand Down Expand Up @@ -1994,7 +2073,7 @@ const createSlice = (

const text = formatTextForQuoting(message.text.stringValue())
getConvoState(newThreadCID).dispatch.injectIntoInput(text)
chatStateHook.getState().dispatch.metasReceived([meta])
get().dispatch.defer.chatMetasReceived([meta])
getConvoState(newThreadCID).dispatch.navigateToThread('createdMessagePrivately')
}
ignorePromise(f())
Expand Down Expand Up @@ -2139,7 +2218,7 @@ const createSlice = (
loadMessages()

// load meta
chatStateHook.getState().dispatch.unboxRows([get().id], true)
get().dispatch.defer.chatUnboxRows([get().id], true)

const updateNav = () => {
const reason = _reason
Expand Down Expand Up @@ -2465,7 +2544,7 @@ const createSlice = (
// remove all bad people
const goodParticipants = new Set(participantInfo.all)
meta.resetParticipants.forEach(r => goodParticipants.delete(r))
chatStateHook.getState().dispatch.previewConversation({
get().dispatch.defer.chatPreviewConversation({
participants: [...goodParticipants],
reason: 'resetChatWithoutThem',
})
Expand Down Expand Up @@ -2508,7 +2587,7 @@ const createSlice = (
return
}

usersStateHook.getState().dispatch.getBio(username)
get().dispatch.defer.usersGetBio(username)
}
}

Expand All @@ -2518,19 +2597,19 @@ const createSlice = (
if (isMetaGood()) {
const {teamID, teamname} = meta
if (teamname) {
teamsStateHook.getState().dispatch.getMembers(teamID)
get().dispatch.defer.teamsGetMembers(teamID)
}
}
}
ensureSelectedTeamLoaded()
const participantInfo = get().participants
const force = !get().isMetaGood() || participantInfo.all.length === 0
chatStateHook.getState().dispatch.unboxRows([conversationIDKey], force)
get().dispatch.defer.chatUnboxRows([conversationIDKey], force)
set(s => {
s.threadLoadStatus = T.RPCChat.UIChatThreadStatusTyp.none
})
fetchConversationBio()
chatStateHook.getState().dispatch.resetConversationErrored()
get().dispatch.defer.chatResetConversationErrored()
},
sendAudioRecording: async (path, duration, amps) => {
const outboxID = Common.generateOutboxID()
Expand Down Expand Up @@ -2851,7 +2930,7 @@ const createSlice = (
})
}, 1000),
showInfoPanel: (show, tab) => {
chatStateHook.getState().dispatch.updateInfoPanel(show, tab)
get().dispatch.defer.chatUpdateInfoPanel(show, tab)
const conversationIDKey = get().id
if (Platform.isPhone) {
const visibleScreen = getVisibleScreen()
Expand Down Expand Up @@ -3252,33 +3331,10 @@ registerDebugClear(() => {
clearChatStores()
})

let chatStore: typeof useChatState | undefined
let teamsStore: typeof useTeamsState | undefined
let usersStore: typeof useUsersState | undefined

export const setOtherStores = (
chat: typeof useChatState,
teams: typeof useTeamsState,
users: typeof useUsersState
) => {
chatStore = chat
teamsStore = teams
usersStore = users
}

const createConvoStore = (id: T.Chat.ConversationIDKey) => {
const existing = chatStores.get(id)
if (existing) return existing
if (!chatStore || !teamsStore || !usersStore) {
throw new Error('Stores not initialized. Call setOtherStores before creating conversation stores.')
}
const next = Z.createZustand<ConvoState>(
createSlice(
chatStore,
teamsStore,
usersStore
)
)
const next = Z.createZustand<ConvoState>(createSlice())
next.setState({id})
chatStores.set(id, next)
return next
Expand Down
Loading