import './tab-bar.css'
import * as ConfigGen from '../actions/config-gen'
import * as LoginGen from '../actions/login-gen'
import * as Container from '../util/container'
import * as FsConstants from '../constants/fs'
import * as Kb from '../common-adapters'
import * as Kbfs from '../fs/common'
import * as Platforms from '../constants/platform'
import * as ProfileGen from '../actions/profile-gen'
import * as ProvisionGen from '../actions/provision-gen'
import * as RPCTypes from '../constants/types/rpc-gen'
import * as React from 'react'
import * as RouteTreeGen from '../actions/route-tree-gen'
import * as SettingsConstants from '../constants/settings'
import * as SettingsGen from '../actions/settings-gen'
import * as Styles from '../styles'
import * as Tabs from '../constants/tabs'
import * as TrackerConstants from '../constants/tracker2'
import flags from '../util/feature-flags'
import AccountSwitcher from './account-switcher/container'
import RuntimeStats from '../app/runtime-stats'
import InviteFriends from '../people/invite-friends/tab-bar-button'
import HiddenString from '../util/hidden-string'
import openURL from '../util/open-url'
import {isLinux} from '../constants/platform'
import {quit} from '../desktop/app/ctl.desktop'
import {tabRoots} from './routes'
export type Props = {
navigation: any
selectedTab: Tabs.AppTab
}
const data = {
[Tabs.chatTab]: {icon: 'iconfont-nav-2-chat', label: 'Chat'},
[Tabs.cryptoTab]: {icon: 'iconfont-nav-2-crypto', label: 'Crypto'},
[Tabs.devicesTab]: {icon: 'iconfont-nav-2-devices', label: 'Devices'},
[Tabs.fsTab]: {icon: 'iconfont-nav-2-files', label: 'Files'},
[Tabs.gitTab]: {icon: 'iconfont-nav-2-git', label: 'Git'},
[Tabs.peopleTab]: {icon: 'iconfont-nav-2-people', label: 'People'},
[Tabs.settingsTab]: {icon: 'iconfont-nav-2-settings', label: 'Settings'},
[Tabs.teamsTab]: {icon: 'iconfont-nav-2-teams', label: 'Teams'},
[Tabs.walletsTab]: {icon: 'iconfont-nav-2-wallets', label: 'Wallet'},
} as const
const tabs = Tabs.desktopTabOrder
const FilesTabBadge = () => {
const uploadIcon = FsConstants.getUploadIconForFilesTab(Container.useSelector(state => state.fs.badge))
return uploadIcon ? : null
}
const Header = () => {
const dispatch = Container.useDispatch()
const [showingMenu, setShowingMenu] = React.useState(false)
const attachmentRef = React.useRef(null)
const getAttachmentRef = () => attachmentRef.current
const fullname = Container.useSelector(
state => TrackerConstants.getDetails(state, state.config.username).fullname || ''
)
const username = Container.useSelector(state => state.config.username)
const onProfileClick = () => dispatch(ProfileGen.createShowUserProfile({username}))
const onClickWrapper = () => {
setShowingMenu(false)
onProfileClick()
}
const onAddAccount = () => dispatch(ProvisionGen.createStartProvision())
const onHelp = () => openURL('https://book.keybase.io')
const onQuit = () => {
if (!__DEV__) {
if (isLinux) {
dispatch(SettingsGen.createStop({exitCode: RPCTypes.ExitCode.ok}))
} else {
dispatch(ConfigGen.createDumpLogs({reason: 'quitting through menu'}))
}
}
// In case dump log doesn't exit for us
Electron.remote.getCurrentWindow().hide()
setTimeout(() => {
quit()
}, 2000)
}
const onSettings = () => dispatch(RouteTreeGen.createSwitchTab({tab: Tabs.settingsTab}))
const onSignOut = () => dispatch(RouteTreeGen.createNavigateAppend({path: [SettingsConstants.logOutTab]}))
const menuHeader = () => (
{fullname}
}
/>
)
const menuItems = (): Kb.MenuItems => [
{onClick: onAddAccount, title: 'Log in as another user'},
{onClick: onSettings, title: 'Settings'},
{onClick: onHelp, title: 'Help'},
{danger: true, onClick: onSignOut, title: 'Sign out'},
{danger: true, onClick: onQuit, title: 'Quit Keybase'},
]
return (
<>
setShowingMenu(true)}>
<>
Hi {username}!
>
setShowingMenu(false)}
/>
>
)
}
const keysMap = Tabs.desktopTabOrder.reduce((map, tab, index) => {
map[`mod+${index + 1}`] = tab
return map
}, {})
const hotKeys = Object.keys(keysMap)
const TabBar = (props: Props) => {
const {selectedTab, navigation} = props
const username = Container.useSelector(state => state.config.username)
const badgeNumbers = Container.useSelector(state => state.notifications.navBadges)
const fsCriticalUpdate = Container.useSelector(state => state.fs.criticalUpdate)
const navRef = React.useRef(navigation.navigate)
const onChangeTab = React.useCallback((tab: Tabs.AppTab) => {
navRef.current(tab)
}, [])
const onNavUp = React.useCallback((tab: Tabs.AppTab) => {
navRef.current(tabRoots[tab])
}, [])
const onHotKey = React.useCallback((cmd: string) => {
navRef.current(keysMap[cmd])
}, [])
return username ? (
{tabs.map((t, i) => (
))}
{flags.inviteFriends && }
) : null
}
type TabProps = {
tab: Tabs.AppTab
index: number
isSelected: boolean
onTabClick: (t: Tabs.AppTab) => void
badge?: number
}
const Tab = React.memo((props: TabProps) => {
const {tab, index, isSelected, onTabClick, badge} = props
const {label} = data[tab]
const dispatch = Container.useDispatch()
const accountRows = Container.useSelector(state => state.config.configuredAccounts)
const current = Container.useSelector(state => state.config.username)
const onQuickSwitch = React.useMemo(
() =>
index === 0
? () => {
const row = accountRows.find(a => a.username !== current && a.hasStoredSecret)
if (row) {
dispatch(ConfigGen.createSetUserSwitching({userSwitching: true}))
dispatch(LoginGen.createLogin({password: new HiddenString(''), username: row.username}))
} else {
onTabClick(tab)
}
}
: undefined,
[accountRows, dispatch, index, current, onTabClick, tab]
)
// no long press on desktop so a quick version
const [mouseTime, setMouseTime] = React.useState(0)
const onMouseUp = React.useMemo(
() =>
index === 0
? () => {
if (mouseTime && Date.now() - mouseTime > 1000) {
onQuickSwitch?.()
}
setMouseTime(0)
}
: undefined,
[index, onQuickSwitch, mouseTime]
)
const onMouseDown = React.useMemo(
() =>
index === 0
? () => {
setMouseTime(Date.now())
}
: undefined,
[index]
)
const onMouseLeave = React.useMemo(
() =>
index === 0
? () => {
setMouseTime(0)
}
: undefined,
[index]
)
return (
onTabClick(tab)}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
>
{tab === Tabs.fsTab && }
{label}
{!!badge && }
)
})
const styles = Styles.styleSheetCreate(
() =>
({
avatar: {marginLeft: 14},
badgeIcon: {
bottom: -4,
position: 'absolute',
right: 8,
},
badgeIconUpload: {
bottom: -Styles.globalMargins.xxtiny,
height: Styles.globalMargins.xsmall,
position: 'absolute',
right: Styles.globalMargins.xsmall,
width: Styles.globalMargins.xsmall,
},
button: {
margin: Styles.globalMargins.xsmall,
},
caret: {marginRight: 12},
divider: {marginTop: Styles.globalMargins.tiny},
fullname: {maxWidth: 180},
header: {flexShrink: 0, height: 80, marginBottom: 20},
headerBox: {
paddingTop: Styles.globalMargins.small,
},
iconBox: {
justifyContent: 'flex-end',
position: 'relative',
},
menu: {marginLeft: Styles.globalMargins.tiny},
nameContainer: {height: 24},
osButtons: Styles.platformStyles({
isElectron: {
...Styles.desktopStyles.windowDragging,
flexGrow: 1,
},
}),
tab: {
alignItems: 'center',
paddingRight: 12,
position: 'relative',
},
username: Styles.platformStyles({
isElectron: {color: Styles.globalColors.blueLighter, flexGrow: 1, wordBreak: 'break-all'},
}),
} as const)
)
export default TabBar