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
77 changes: 64 additions & 13 deletions shared/chat/conversation/bot/install.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as C from '@/constants'
import * as Meta from '@/constants/chat/meta'
import * as Chat from '@/stores/chat'
import * as Kb from '@/common-adapters'
import * as Teams from '@/stores/teams'
Expand All @@ -15,17 +16,41 @@ const RestrictedItem = '---RESTRICTED---'
export const useBotConversationIDKey = (inConvIDKey?: T.Chat.ConversationIDKey, teamID?: T.Teams.TeamID) => {
const cleanInConvIDKey = T.Chat.isValidConversationIDKey(inConvIDKey ?? '') ? inConvIDKey : undefined
const [conversationIDKey, setConversationIDKey] = React.useState(cleanInConvIDKey)
const generalConvID = Chat.useChatState(s => teamID && s.teamIDToGeneralConvID.get(teamID))
const findGeneralConvIDFromTeamID = Chat.useChatState(s => s.dispatch.findGeneralConvIDFromTeamID)
const findGeneralConvIDFromTeamID = C.useRPC(T.RPCChat.localFindGeneralConvFromTeamIDRpcPromise)
const metasReceived = Chat.useChatState(s => s.dispatch.metasReceived)
const requestIDRef = React.useRef(0)

React.useEffect(() => {
if (!cleanInConvIDKey && teamID) {
if (!generalConvID) {
findGeneralConvIDFromTeamID(teamID)
} else {
setConversationIDKey(generalConvID)
setConversationIDKey(cleanInConvIDKey)
}, [cleanInConvIDKey])

React.useEffect(() => {
requestIDRef.current += 1
if (cleanInConvIDKey || !teamID) {
return
}
const requestID = requestIDRef.current
findGeneralConvIDFromTeamID(
[{teamID}],
conv => {
if (requestIDRef.current !== requestID) {
return
}
const meta = Meta.inboxUIItemToConversationMeta(conv)
if (!meta) {
return
}
metasReceived([meta])
setConversationIDKey(meta.conversationIDKey)
},
() => {}
)
Comment thread
chrisnojima marked this conversation as resolved.
return () => {
if (requestIDRef.current === requestID) {
requestIDRef.current += 1
}
}
}, [cleanInConvIDKey, findGeneralConvIDFromTeamID, generalConvID, teamID])
}, [cleanInConvIDKey, findGeneralConvIDFromTeamID, metasReceived, teamID])
return conversationIDKey
}

Expand Down Expand Up @@ -66,8 +91,8 @@ const InstallBotPopup = (props: Props) => {
const [installWithRestrict, setInstallWithRestrict] = React.useState(true)
const [installInConvs, setInstallInConvs] = React.useState<ReadonlyArray<string>>([])
const [disableDone, setDisableDone] = React.useState(false)
const [botPublicCommands, setBotPublicCommands] = React.useState<T.Chat.BotPublicCommands | undefined>()

const botPublicCommands = Chat.useChatState(s => s.botPublicCommands.get(botUsername))
const meta = Chat.useChatContext(s => s.meta)
const commands = (() => {
const {botCommands} = meta
Expand Down Expand Up @@ -150,13 +175,39 @@ const InstallBotPopup = (props: Props) => {
const noCommands = !commands?.commands

const dispatchClearWaiting = C.Waiting.useDispatchClearWaiting()
const refreshBotPublicCommands = Chat.useChatState(s => s.dispatch.refreshBotPublicCommands)
const loadBotPublicCommands = C.useRPC(T.RPCChat.localListPublicBotCommandsLocalRpcPromise)
const botPublicCommandsRequestIDRef = React.useRef(0)
React.useEffect(() => {
dispatchClearWaiting([C.waitingKeyChatBotAdd, C.waitingKeyChatBotRemove])
if (noCommands) {
refreshBotPublicCommands(botUsername)
botPublicCommandsRequestIDRef.current += 1
if (!noCommands) {
setBotPublicCommands(undefined)
return
}
setBotPublicCommands(undefined)
const requestID = botPublicCommandsRequestIDRef.current
loadBotPublicCommands(
[{username: botUsername}],
res => {
if (botPublicCommandsRequestIDRef.current !== requestID) {
return
}
const commands = (res.commands ?? []).map(command => command.name)
setBotPublicCommands({commands, loadError: false})
},
() => {
if (botPublicCommandsRequestIDRef.current !== requestID) {
return
}
setBotPublicCommands({commands: [], loadError: true})
}
)
return () => {
if (botPublicCommandsRequestIDRef.current === requestID) {
botPublicCommandsRequestIDRef.current += 1
}
}
}, [dispatchClearWaiting, refreshBotPublicCommands, noCommands, botUsername])
}, [botUsername, dispatchClearWaiting, loadBotPublicCommands, noCommands])

const restrictedButton = (
<Kb.Box2 key={RestrictedItem} direction="vertical" fullWidth={true} style={styles.dropdownButton}>
Expand Down
10 changes: 4 additions & 6 deletions shared/chat/conversation/info-panel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ type Props = {
}

const InfoPanelConnector = (ownProps: Props) => {
const storeSelectedTab = Chat.useChatState(s => s.infoPanelSelectedTab)
const setInfoPanelTab = Chat.useChatState(s => s.dispatch.setInfoPanelTab)
const initialTab = ownProps.tab ?? storeSelectedTab
const initialTab = ownProps.tab
const conversationIDKey = Chat.useChatContext(s => s.id)
const meta = Chat.useConvoState(conversationIDKey, s => s.meta)
const shouldNavigateOut = meta.conversationIDKey === Chat.noConversationIDKey
Expand Down Expand Up @@ -52,9 +50,9 @@ const InfoPanelConnector = (ownProps: Props) => {
}

React.useEffect(() => {
if (selectedTab === storeSelectedTab) return
setInfoPanelTab(selectedTab)
}, [selectedTab, storeSelectedTab, setInfoPanelTab])
if (ownProps.tab === undefined || ownProps.tab === selectedTab) return
onSelectTab(ownProps.tab)
}, [ownProps.tab, selectedTab])

const getTabs = (): Array<TabType<Panel>> => {
const showSettings = !isPreview || Teams.isAdmin(yourRole) || Teams.isOwner(yourRole)
Expand Down
11 changes: 7 additions & 4 deletions shared/chat/conversation/input-area/normal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {assertionToDisplay} from '@/common-adapters/usernames'
import {FocusContext, ScrollContext} from '@/chat/conversation/normal/context'
import type {RefType as InputRef} from './input'
import {useCurrentUserState} from '@/stores/current-user'
import {useRoute} from '@react-navigation/native'
import type {RootRouteProps} from '@/router-v2/route-params'

const useHintText = (p: {
isExploding: boolean
Expand Down Expand Up @@ -107,7 +109,8 @@ const doInjectText = (inputRef: React.RefObject<InputRef | null>, text: string,
}

const ConnectedPlatformInput = function ConnectedPlatformInput() {
const infoPanelShowing = Chat.useChatState(s => s.infoPanelShowing)
const route = useRoute<RootRouteProps<'chatConversation'> | RootRouteProps<'chatRoot'>>()
const infoPanelShowing = route.name === 'chatRoot' ? !!route.params?.infoPanel : false
const data = Chat.useChatContext(
C.useShallow(s => {
const {meta, id: conversationIDKey, editing: editOrdinal, messageMap, unsentText} = s
Expand All @@ -128,9 +131,9 @@ const ConnectedPlatformInput = function ConnectedPlatformInput() {
: explodingMode
// prettier-ignore
return {cannotWrite, conversationIDKey, convoID, explodingMode, explodingModeSeconds,
infoPanelShowing, isEditing, jumpToRecent, minWriterRole, sendMessage, setEditing,
setExplodingMode, showReplyPreview, storeDraft, suggestBotCommandsUpdateStatus,
tlfname, unsentText, updateUnsentText}
isEditing, jumpToRecent, minWriterRole, sendMessage, setEditing, setExplodingMode,
showReplyPreview, storeDraft, suggestBotCommandsUpdateStatus, tlfname, unsentText,
updateUnsentText}
})
)

Expand Down
21 changes: 6 additions & 15 deletions shared/chat/conversation/input-area/suggestors/emoji.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as C from '@/constants'
import * as Chat from '@/stores/chat'
import * as Common from './common'
import * as Kb from '@/common-adapters'
import * as React from 'react'
import {type EmojiData, RPCToEmojiData, emojiData} from '@/common-adapters/emoji'
import {useUserEmoji} from '@/chat/user-emoji'

export const transformer = (
emoji: EmojiData,
Expand Down Expand Up @@ -40,13 +39,7 @@ const empty = new Array<EmojiData>()

const useDataSource = (filter: string) => {
const conversationIDKey = Chat.useChatContext(s => s.id)
const fetchUserEmoji = Chat.useChatState(s => s.dispatch.fetchUserEmoji)
React.useEffect(() => {
fetchUserEmoji(conversationIDKey)
}, [conversationIDKey, fetchUserEmoji])

const userEmojisLoading = C.Waiting.useAnyWaiting(C.waitingKeyChatLoadingEmoji)
const userEmojis = Chat.useChatState(s => s.userEmojisForAutocomplete)
const {emojis: userEmojis, loading: userEmojisLoading} = useUserEmoji({conversationIDKey})

if (!emojiPrepass.test(filter)) {
return {
Expand All @@ -58,12 +51,10 @@ const useDataSource = (filter: string) => {
// prefill data with stock emoji
let results: Array<EmojiData> = emojiData.emojiSearch(filter, 50)

if (userEmojis) {
const userEmoji = userEmojis
.filter(emoji => emoji.alias.toLowerCase().includes(filter))
.map(emoji => RPCToEmojiData(emoji, false))
results = userEmoji.sort((a, b) => a.short_name.localeCompare(b.short_name)).concat(results)
}
const userEmoji = userEmojis
.filter(emoji => emoji.alias.toLowerCase().includes(filter))
.map(emoji => RPCToEmojiData(emoji, false))
results = userEmoji.sort((a, b) => a.short_name.localeCompare(b.short_name)).concat(results)

return {
items: results,
Expand Down
24 changes: 7 additions & 17 deletions shared/chat/emoji-picker/container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import EmojiPicker, {getSkinToneModifierStrIfAvailable} from '.'
import {type RenderableEmoji, emojiData} from '@/common-adapters/emoji'
import {usePickerState, type PickKey} from './use-picker'
import {Keyboard} from 'react-native'
import {useUserEmoji} from '@/chat/user-emoji'

type Props = {
disableCustomEmoji?: boolean
Expand Down Expand Up @@ -68,23 +69,12 @@ const useSkinTone = () => {

const useCustomReacji = (onlyInTeam: boolean | undefined, disabled?: boolean) => {
const conversationIDKey = Chat.useChatContext(s => s.id)
const customEmojiGroups = Chat.useChatState(s => s.userEmojis)
const waiting = C.Waiting.useAnyWaiting(C.waitingKeyChatLoadingEmoji)
const [lastOnlyInTeam, setLastOnlyInTeam] = React.useState(onlyInTeam)
const [lastDisabled, setLastDisabled] = React.useState(disabled)
const fetchUserEmoji = Chat.useChatState(s => s.dispatch.fetchUserEmoji)

React.useEffect(() => {
if (lastOnlyInTeam !== onlyInTeam || lastDisabled !== disabled) {
setLastOnlyInTeam(onlyInTeam)
setLastDisabled(disabled)
}
if (!disabled) {
fetchUserEmoji(conversationIDKey, onlyInTeam)
}
}, [conversationIDKey, fetchUserEmoji, lastDisabled, lastOnlyInTeam, onlyInTeam, disabled])

return disabled ? {customEmojiGroups: undefined, waiting: false} : {customEmojiGroups, waiting}
const {emojiGroups: customEmojiGroups, loading: waiting} = useUserEmoji({
conversationIDKey,
disabled,
onlyInTeam,
})
return {customEmojiGroups, waiting}
}

const useCanManageEmoji = () => {
Expand Down
3 changes: 2 additions & 1 deletion shared/chat/inbox-and-conversation-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ const Header = () => {
}

const Header2 = () => {
const {params} = useRoute<RootRouteProps<'chatRoot'>>()
const username = useCurrentUserState(s => s.username)
const infoPanelShowing = Chat.useChatState(s => s.infoPanelShowing)
const infoPanelShowing = !!params?.infoPanel
const data = Chat.useChatContext(
C.useShallow(s => {
const {meta, id, dispatch} = s
Expand Down
13 changes: 8 additions & 5 deletions shared/chat/inbox-and-conversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import type * as T from '@/constants/types'
import Conversation from './conversation/container'
import Inbox from './inbox'
import InboxSearch from './inbox-search'
import InfoPanel from './conversation/info-panel'
import InfoPanel, {type Panel} from './conversation/info-panel'

type Props = {conversationIDKey?: T.Chat.ConversationIDKey}
type Props = {
conversationIDKey?: T.Chat.ConversationIDKey
infoPanel?: {tab?: Panel}
}

function InboxAndConversation(props: Props) {
const conversationIDKey = props.conversationIDKey ?? Chat.noConversationIDKey
const inboxSearch = Chat.useChatState(s => s.inboxSearch)
const infoPanelShowing = Chat.useChatState(s => s.infoPanelShowing)
const infoPanel = props.infoPanel
const validConvoID = conversationIDKey && conversationIDKey !== Chat.noConversationIDKey
const seenValidCIDRef = React.useRef(validConvoID ? conversationIDKey : '')
const selectNextConvo = Chat.useChatState(s => {
Expand Down Expand Up @@ -47,9 +50,9 @@ function InboxAndConversation(props: Props) {
<Kb.Box2 direction="vertical" fullHeight={true} flex={1}>
<Conversation />
</Kb.Box2>
{infoPanelShowing ? (
{infoPanel ? (
<Kb.Box2 direction="vertical" fullHeight={true} style={styles.infoPanel}>
<InfoPanel key={conversationIDKey} />
<InfoPanel key={conversationIDKey} tab={infoPanel.tab} />
</Kb.Box2>
) : null}
</Kb.Box2>
Expand Down
85 changes: 85 additions & 0 deletions shared/chat/user-emoji.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import * as C from '@/constants'
import * as T from '@/constants/types'
import * as React from 'react'

const emptyEmojiGroups: ReadonlyArray<T.RPCChat.EmojiGroup> = []
const emptyEmojis: ReadonlyArray<T.RPCChat.Emoji> = []

const flattenUserEmojis = (groups: ReadonlyArray<T.RPCChat.EmojiGroup>) => {
const emojis = new Array<T.RPCChat.Emoji>()
groups.forEach(group => {
group.emojis?.forEach(emoji => emojis.push(emoji))
})
return emojis
}

export const useUserEmoji = ({
conversationIDKey,
disabled,
onlyInTeam,
}: {
conversationIDKey?: T.Chat.ConversationIDKey
disabled?: boolean
onlyInTeam?: boolean
}) => {
const loadUserEmoji = C.useRPC(T.RPCChat.localUserEmojisRpcPromise)
const [emojiGroups, setEmojiGroups] = React.useState(emptyEmojiGroups)
const [emojis, setEmojis] = React.useState(emptyEmojis)
const [loading, setLoading] = React.useState(false)
const requestIDRef = React.useRef(0)

React.useEffect(() => {
if (disabled) {
requestIDRef.current += 1
setLoading(false)
return
}

const requestID = requestIDRef.current + 1
requestIDRef.current = requestID
setLoading(true)

loadUserEmoji(
[
{
convID:
conversationIDKey && conversationIDKey !== T.Chat.noConversationIDKey
? T.Chat.keyToConversationID(conversationIDKey)
: null,
opts: {
getAliases: true,
getCreationInfo: false,
onlyInTeam: onlyInTeam ?? false,
},
},
],
results => {
if (requestIDRef.current !== requestID) {
return
}
const nextGroups = results.emojis.emojis ?? emptyEmojiGroups
setEmojiGroups(nextGroups)
setEmojis(flattenUserEmojis(nextGroups))
setLoading(false)
},
() => {
if (requestIDRef.current !== requestID) {
return
}
setLoading(false)
}
)

return () => {
if (requestIDRef.current === requestID) {
requestIDRef.current += 1
}
}
}, [conversationIDKey, disabled, loadUserEmoji, onlyInTeam])

return {
emojiGroups: disabled ? undefined : emojiGroups,
emojis,
loading: disabled ? false : loading,
}
}
Loading