/
githubmirror
/
client
Обзор
Документация
Войти
/
githubmirror
/
client
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
shared/teams/channel/header.tsx
255 строк
8 KB
chrisnojima
refactor(styles): theme via hooks, no android remount (#29515)
08 авг 2026, 02:24
Не верифицирован
08 авг 2026, 02:24
a41d38e
Код
Авторство
О чём код?
import * as T from '@/constants/types' import * as C from '@/constants' import * as React from 'react' import * as Kb from '@/common-adapters' import {pluralize} from '@/util/string' import {useInboxMetadataState} from '@/chat/inbox/metadata' import {Activity, useActivityLevels, useChannelParticipants} from '../common' import {useLoadedTeamChannels} from '../common/use-loaded-team-channels' import {useLoadedTeam} from '../team/use-loaded-team' import {useSafeNavigation} from '@/util/safe-navigation' const useRecentJoins = (conversationIDKey: T.Chat.ConversationIDKey) => { const [loadedRecentJoins, setLoadedRecentJoins] = React.useState< {conversationIDKey: T.Chat.ConversationIDKey; recentJoins: number} | undefined >(undefined) const getRecentJoinsRPC = C.useRPC(T.RPCChat.localGetRecentJoinsLocalRpcPromise) React.useEffect(() => { let canceled = false getRecentJoinsRPC( [{convID: T.Chat.keyToConversationID(conversationIDKey)}], recentJoins => { if (!canceled) { setLoadedRecentJoins({conversationIDKey, recentJoins}) } }, () => {} ) return () => { canceled = true } }, [conversationIDKey, getRecentJoinsRPC]) return loadedRecentJoins?.conversationIDKey === conversationIDKey ? loadedRecentJoins.recentJoins : undefined } type HeaderTitleProps = { teamID: T.Teams.TeamID conversationIDKey: T.Chat.ConversationIDKey } const HeaderTitle = (props: HeaderTitleProps) => { const styles = useStyles() const theme = Kb.Styles.useTheme() const {teamID, conversationIDKey} = props const {teamMeta, yourOperations} = useLoadedTeam(teamID) const {channels} = useLoadedTeamChannels(teamID, teamMeta.teamname) const channelInfo = channels.get(conversationIDKey) const channelname = channelInfo?.channelname ?? '' const description = channelInfo?.description ?? '' const inboxParticipants = useInboxMetadataState(s => s.participants.get(conversationIDKey)) const numParticipants = useChannelParticipants(teamID, conversationIDKey, inboxParticipants).length const canDelete = !!channelInfo && yourOperations.deleteChannel && channelname !== 'general' const canEdit = !!channelInfo && yourOperations.editChannelDescription const {channels: activityByChannel} = useActivityLevels() const editChannelProps = { channelname: channelname, conversationIDKey, description: description, teamID, } const nav = useSafeNavigation() const onEditChannel = () => nav.safeNavigateAppend({name: 'teamEditChannel', params: editChannelProps}) const onAddMembers = () => nav.safeNavigateAppend({name: 'chatAddToChannel', params: {conversationIDKey, teamID}}) const onNavToTeam = () => nav.safeNavigateAppend({name: 'team', params: {teamID}}) const activityLevel = activityByChannel.get(conversationIDKey) || 'none' const newMemberCount = useRecentJoins(conversationIDKey) const previewConversation = C.Router2.previewConversation const onChat = () => previewConversation({conversationIDKey, reason: 'channelHeader'}) const topDescriptors = ( <Kb.Box2 direction="vertical" alignSelf="flex-start" gap="xxtiny" style={styles.flexShrink}> <Kb.Box2 direction="horizontal" gap="xtiny" alignSelf="flex-start" style={styles.flexShrink}> <Kb.Avatar teamname={teamMeta.teamname} size={16} style={styles.alignSelfFlexStart} /> <Kb.Text className="hover-underline" type="BodySmallSemibold" onClick={onNavToTeam}> {teamMeta.teamname} </Kb.Text> </Kb.Box2> <Kb.Text type="Header" lineClamp={1} style={styles.flexShrink}> {'#' + channelname} </Kb.Text> </Kb.Box2> ) const menuItems: Array<Kb.MenuItem> = [ // Not including settings here because there's already a settings tab below and plumbing the tab selection logic to here would be a real pain. // It's included in the other place this menu appears. ...(canDelete ? [ { danger: true, onClick: () => { nav.safeNavigateUp() nav.safeNavigateAppend({name: 'teamDeleteChannel', params: {conversationIDKey, teamID}}) }, title: 'Delete channel', }, ] : []), ] const makePopup = (p: Kb.Popup2Parms) => { const {attachTo, hidePopup} = p return ( <Kb.FloatingMenu attachTo={attachTo} closeOnSelect={true} items={menuItems} onHidden={hidePopup} visible={true} /> ) } const {showPopup, popupAnchor, popup} = Kb.usePopup2(makePopup) const bottomDescriptorsAndButtons = ( <> <Kb.Box2 direction="vertical" alignSelf="flex-start" gap="xxtiny" gapStart={!isMobile}> {!!description && ( <Kb.Text type="Body" lineClamp={3}> {description} </Kb.Text> )} {numParticipants !== -1 && ( <Kb.Text type="BodySmall"> {numParticipants.toLocaleString()} {pluralize('member', numParticipants)} {!!newMemberCount && ' · ' + newMemberCount.toLocaleString() + ' new this week'} </Kb.Text> )} <Kb.Box2 direction="horizontal" alignSelf="flex-start"> <Activity level={activityLevel} /> </Kb.Box2> <Kb.Box2 direction="horizontal" gap="tiny" alignItems="center" style={styles.rightActionsContainer}> {yourOperations.chat && <Kb.Button label="View" onClick={onChat} small={true} />} {canEdit && ( <Kb.Button label="Edit" onClick={onEditChannel} small={true} mode="Secondary" /> )} {!isMobile && ( <Kb.Button label="Add members" onClick={onAddMembers} small={true} mode="Secondary" style={styles.addMembersButton} /> )} {!!menuItems.length && ( <Kb.IconButton mode="Secondary" small={true} icon="iconfont-ellipsis" iconColor={theme.blue} ref={popupAnchor} onClick={showPopup} /> )} </Kb.Box2> </Kb.Box2> {popup} </> ) const tip = ( <Kb.Box2 direction="horizontal" alignSelf="flex-start" gap="tiny" style={styles.tipBox}> <Kb.Icon color={theme.black_20} type="iconfont-info" sizeType="Small" /> <Kb.Text type="BodySmall">Tip: Use @mentions to invite team members to channels from the chat.</Kb.Text> </Kb.Box2> ) if (isMobile) { return ( <Kb.Box2 alignItems="flex-start" direction="vertical" fullWidth={true} style={styles.backButton}> <Kb.Box2 direction="vertical" fullWidth={true} gap="xtiny" style={styles.outerBoxMobile}> {topDescriptors} {bottomDescriptorsAndButtons} {tip} </Kb.Box2> </Kb.Box2> ) } return ( <Kb.Box2 alignItems="center" direction="horizontal" gap="small" gapStart={true} fullWidth={true} className="headerTitle" > <Kb.Box2 direction="vertical" alignItems="flex-start" alignSelf="flex-start" flex={1} style={styles.outerBoxDesktop} > {topDescriptors} <Kb.Box2 direction="horizontal" fullWidth={true}> {bottomDescriptorsAndButtons} {tip} </Kb.Box2> </Kb.Box2> </Kb.Box2> ) } export default HeaderTitle const useStyles = Kb.Styles.createStyleHook( theme => ({ addMembersButton: { flexGrow: 0, }, alignSelfFlexStart: { alignSelf: 'flex-start', }, backButton: { backgroundColor: theme.white, }, flexShrink: { flexShrink: 1, }, outerBoxDesktop: { flexShrink: 1, marginBottom: Kb.Styles.globalMargins.small, }, outerBoxMobile: { ...Kb.Styles.padding(Kb.Styles.globalMargins.small), backgroundColor: theme.white, }, rightActionsContainer: Kb.Styles.platformStyles({ common: { alignSelf: 'flex-start', paddingTop: Kb.Styles.globalMargins.tiny, }, isElectron: Kb.Styles.desktopStyles.windowDraggingClickable, }), tipBox: Kb.Styles.platformStyles({ isElectron: { marginLeft: Kb.Styles.globalMargins.xlarge + Kb.Styles.globalMargins.large, marginRight: Kb.Styles.globalMargins.large, maxWidth: 460, paddingTop: Kb.Styles.globalMargins.xxtiny, }, isMobile: { paddingTop: Kb.Styles.globalMargins.tiny, }, }), }) as const )