Skip to content
Closed
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
51 changes: 49 additions & 2 deletions src/ui/components/Shell/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,12 @@ describe('Shell', () => {
).toBeInTheDocument();
});

it('forces the dark theme regardless of the machine theme', () => {
it('forces the dark theme when transparency is disabled, regardless of the machine theme', () => {
renderWithStore(<Shell />, {
preloadedState: buildState({
machineTheme: 'light',
userThemePreference: 'auto',
isTransparentWindowEnabled: false,
}),
});

Expand All @@ -222,11 +223,57 @@ describe('Shell', () => {
);
});

it('forces the dark theme regardless of the user theme preference', () => {
it('forces the dark theme when transparency is disabled, regardless of the user theme preference', () => {
renderWithStore(<Shell />, {
preloadedState: buildState({
machineTheme: 'light',
userThemePreference: 'light',
isTransparentWindowEnabled: false,
}),
});

expect(screen.getByTestId('palette-style-tag')).toHaveAttribute(
'data-theme',
'dark'
);
});

it('follows the machine theme when transparency is enabled and preference is auto', () => {
renderWithStore(<Shell />, {
preloadedState: buildState({
machineTheme: 'light',
userThemePreference: 'auto',
isTransparentWindowEnabled: true,
}),
});

expect(screen.getByTestId('palette-style-tag')).toHaveAttribute(
'data-theme',
'light'
);
});

it('follows the explicit user theme preference when transparency is enabled', () => {
renderWithStore(<Shell />, {
preloadedState: buildState({
machineTheme: 'dark',
userThemePreference: 'light',
isTransparentWindowEnabled: true,
}),
});

expect(screen.getByTestId('palette-style-tag')).toHaveAttribute(
'data-theme',
'light'
);
});

it('keeps the dark theme with transparency enabled when the resolved theme is dark', () => {
renderWithStore(<Shell />, {
preloadedState: buildState({
machineTheme: 'dark',
userThemePreference: 'auto',
isTransparentWindowEnabled: true,
}),
});

Expand Down
7 changes: 5 additions & 2 deletions src/ui/components/Shell/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { TelephonyDefaultHandlerPromptModal } from '../TelephonyDefaultHandlerPr
import { TelephonyServerSelectModal } from '../TelephonyServerSelectModal';
import { TopBar } from '../TopBar';
import { UpdateDialog } from '../UpdateDialog';
import { useShellTheme } from '../hooks/useShellTheme';
import TooltipProvider from '../utils/TooltipProvider';
import { GlobalStyles, WindowDragBar } from './styles';

Expand All @@ -36,6 +37,8 @@ export const Shell = () => {
({ navigationLayout }: RootState) => navigationLayout
);

const shellTheme = useShellTheme();

useLayoutEffect(() => {
if (!appPath) {
return undefined;
Expand All @@ -54,7 +57,7 @@ export const Shell = () => {
return (
<TooltipProvider>
<PaletteStyleTag
theme='dark'
theme={shellTheme}
selector=':root'
// tagId='sidebar-palette'
/>
Expand All @@ -63,7 +66,7 @@ export const Shell = () => {
<WindowDragBar />
)}
<Box
bg='sidebar'
bg={isTransparentWindowEnabled ? 'transparent' : 'sidebar'}
display='flex'
flexWrap='wrap'
height='100vh'
Expand Down
47 changes: 44 additions & 3 deletions src/ui/components/TabBar/WorkspaceTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { TooltipContext } from '../utils/TooltipContext';
import { getServerPanelId, getServerTabId } from '../utils/getServerDomId';
import { getServerInitials } from '../utils/getServerInitials';
import {
Divider,
Favicon,
Initials,
Label,
Expand All @@ -28,6 +29,22 @@ const formatMentionCount = (count: number | undefined): string | undefined => {
return count > 99 ? '99+' : String(count);
};

// Some servers embed their address in the title (e.g.
// "Rocket.Chat - https://stable.rocket.chat/"). Strip it out so the tooltip can
// show the name on its own line. Returns '' when the title is only the address.
const removeServerAddress = (title: string, serverAddress: string): string => {
const index = title.toLowerCase().indexOf(serverAddress.toLowerCase());

if (index === -1) {
return title.trim();
}

const before = title.slice(0, index);
const after = title.slice(index + serverAddress.length).replace(/^\/+/, '');

return `${before}${after}`.replace(/^[\s\-–—|·:]+|[\s\-–—|·:]+$/g, '').trim();
};

type WorkspaceTabProps = {
url: string;
title: string;
Expand Down Expand Up @@ -110,7 +127,26 @@ const WorkspaceTab = ({

const unreadSuffix = getUnreadSuffix();

const tooltipText = `${title}${unreadSuffix}${shortcutSuffix}`;
const serverAddress = url.replace(/\/+$/, '');
const tooltipName = removeServerAddress(title, serverAddress);
const tooltipPrimaryLine = `${
tooltipName || serverAddress
}${unreadSuffix}${shortcutSuffix}`;
// Show the name on the first line and the address on a second line. When the
// title is only the address, the primary line already is it, so skip line two.
const tooltipLines = tooltipName
? [tooltipPrimaryLine, serverAddress]
: [tooltipPrimaryLine];
// The TooltipProvider renders each '\n'-separated line on its own row, so the
// native title, the custom hover tooltip and the aria-label all stay in sync.
const tooltipText = tooltipLines.join('\n');
const tooltipNode = (
<>
{tooltipLines.map((line, index) => (
<div key={index}>{line}</div>
))}
</>
);

const handleClick = (): void => {
dispatch({ type: SIDE_BAR_SERVER_SELECTED, payload: url });
Expand All @@ -129,7 +165,7 @@ const WorkspaceTab = ({
};

const handleFocus = (event: FocusEvent<HTMLButtonElement>): void => {
tooltip.open(<>{tooltipText}</>, event.currentTarget);
tooltip.open(tooltipNode, event.currentTarget);
};

const handleBlur = (): void => {
Expand Down Expand Up @@ -163,7 +199,11 @@ const WorkspaceTab = ({
>
<Initials visible={!favicon}>{initials}</Initials>
<Favicon visible={!!favicon} src={favicon ?? ''} draggable='false' />
{!compact && <Label>{title}</Label>}
{!compact && (
<Label>
{title.replace(/(^|\s)(https?:\/\/)/, '$1').replace(/\/+$/, '')}
</Label>
)}
{!compact && isShortcutVisible && shortcutNumber && (
<ShortcutChip>{shortcutNumber}</ShortcutChip>
)}
Expand All @@ -172,6 +212,7 @@ const WorkspaceTab = ({
)}
{!userLoggedIn && <TabBadge variant='warning'>!</TabBadge>}
</Tab>
<Divider></Divider>
{isVisible && (
<WorkspaceContextMenu
reference={ref}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/TabBar/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ describe('TabBar', () => {
expect(tab).toHaveStyle({
flex: '0 1 auto',
minWidth: '52px',
maxWidth: '180px',
maxWidth: '230px',
});
});

Expand Down
12 changes: 10 additions & 2 deletions src/ui/components/TabBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SIDE_BAR_ADD_NEW_SERVER_CLICKED } from '../../actions';
import { isDarwin } from '../../utils/platform';
import { useKeyboardShortcuts } from '../hooks/useKeyboardShortcuts';
import { useServers } from '../hooks/useServers';
import { useShellTheme } from '../hooks/useShellTheme';
import { useSorting } from '../hooks/useSorting';
import WorkspaceTab from './WorkspaceTab';
import {
Expand Down Expand Up @@ -38,6 +39,8 @@ export const TabBar = ({ leadingSlot, trailingSlot }: TabBarProps) => {
({ isTransparentWindowEnabled }: RootState) => isTransparentWindowEnabled
);

const paletteTheme = useShellTheme();

const isFullscreen = useSelector(
({ rootWindowState }: RootState) => rootWindowState.fullscreen
);
Expand Down Expand Up @@ -103,7 +106,10 @@ export const TabBar = ({ leadingSlot, trailingSlot }: TabBarProps) => {
};

return (
<Strip isTransparentWindowEnabled={isTransparentWindowEnabled}>
<Strip
isTransparentWindowEnabled={isTransparentWindowEnabled}
paletteTheme={paletteTheme}
>
{leadingSlot}
{isDarwin && <TrafficLightSpacer collapsed={isFullscreen} />}
<TabList
Expand Down Expand Up @@ -147,7 +153,9 @@ export const TabBar = ({ leadingSlot, trailingSlot }: TabBarProps) => {
);
})}
{isAddNewServersEnabled && (
<AddButtonWrapper>
<AddButtonWrapper
isTransparentWindowEnabled={isTransparentWindowEnabled}
>
<IconButton
small
icon='plus-small'
Expand Down
Loading
Loading