Skip to content

Commit

Permalink
feat: high-contrast theme (#29805)
Browse files Browse the repository at this point in the history
  • Loading branch information
juliajforesti committed Aug 1, 2023
1 parent 6d453f7 commit 357a3a5
Show file tree
Hide file tree
Showing 21 changed files with 513 additions and 102 deletions.
7 changes: 7 additions & 0 deletions .changeset/smooth-planes-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@rocket.chat/ui-theming': minor
'@rocket.chat/rest-typings': minor
'@rocket.chat/meteor': minor
---

feat: high-contrast theme
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type GenericUpsellModalProps = {
icon?: IconName;
img: ComponentProps<typeof Modal.HeroImage>['src'];
onCancel?: () => void;
onClose?: () => void;
onClose: () => void;
onConfirm?: () => void;
annotation?: ReactNode;
} & ComponentProps<typeof Modal>;
Expand All @@ -30,8 +30,8 @@ const GenericUpsellModal = ({
icon,
description,
onCancel,
onClose,
onConfirm,
onClose = onCancel,
annotation,
...props
}: GenericUpsellModalProps) => {
Expand Down
12 changes: 11 additions & 1 deletion apps/meteor/client/providers/UserProvider/UserProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { IRoom, ISubscription, IUser } from '@rocket.chat/core-typings';
import { useLocalStorage } from '@rocket.chat/fuselage-hooks';
import { UserContext, useSetting } from '@rocket.chat/ui-contexts';
import { UserContext, useEndpoint, useSetting } from '@rocket.chat/ui-contexts';
import type { LoginService, SubscriptionWithRoom } from '@rocket.chat/ui-contexts';
import { Meteor } from 'meteor/meteor';
import type { ContextType, ReactElement, ReactNode } from 'react';
Expand All @@ -10,6 +10,7 @@ import { Subscriptions, ChatRoom } from '../../../app/models/client';
import { getUserPreference } from '../../../app/utils/client';
import { sdk } from '../../../app/utils/client/lib/SDKClient';
import { afterLogoutCleanUpCallback } from '../../../lib/callbacks/afterLogoutCleanUpCallback';
import { useIsEnterprise } from '../../hooks/useIsEnterprise';
import { useReactiveValue } from '../../hooks/useReactiveValue';
import { createReactiveSubscriptionFactory } from '../../lib/createReactiveSubscriptionFactory';
import { useEmailVerificationWarning } from './hooks/useEmailVerificationWarning';
Expand Down Expand Up @@ -66,6 +67,9 @@ const UserProvider = ({ children }: UserProviderProps): ReactElement => {
const user = useReactiveValue(getUser);
const [language, setLanguage] = useLocalStorage('userLanguage', user?.language ?? 'en');

const { data: license } = useIsEnterprise();
const setUserPreferences = useEndpoint('POST', '/v1/users.setPreferences');

const loginMethod: LoginMethods = (isLdapEnabled && 'loginWithLDAP') || (isCrowdEnabled && 'loginWithCrowd') || 'loginWithPassword';

useLDAPAndCrowdCollisionWarning();
Expand Down Expand Up @@ -166,6 +170,12 @@ const UserProvider = ({ children }: UserProviderProps): ReactElement => {
}
}, [user?.language, language, setLanguage]);

useEffect(() => {
if (!license?.isEnterprise && user?.settings?.preferences?.themeAppearence === 'high-contrast') {
setUserPreferences({ data: { themeAppearence: 'light' } });
}
}, [license?.isEnterprise, setUserPreferences, user?.settings?.preferences?.themeAppearence]);

return <UserContext.Provider children={children} value={contextValue} />;
};

Expand Down
43 changes: 24 additions & 19 deletions apps/meteor/client/sidebar/header/hooks/useAccountItems.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,28 @@
import { Badge } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { defaultFeaturesPreview, useFeaturePreviewList } from '@rocket.chat/ui-client';
import { useLogout, useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import { useRouter, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem';

export const useAccountItems = (): GenericMenuItemProps[] => {
const t = useTranslation();
const accountRoute = useRoute('account-index');
const featurePreviewRoute = useRoute('feature-preview');
const { unseenFeatures, featurePreviewEnabled } = useFeaturePreviewList();
const router = useRouter();

const logout = useLogout();
const { unseenFeatures, featurePreviewEnabled } = useFeaturePreviewList();

const handleMyAccount = useMutableCallback(() => {
accountRoute.push({});
router.navigate('/account');
});

const handleFeaturePreview = useMutableCallback(() => {
featurePreviewRoute.push();
const handleThemes = useMutableCallback(() => {
router.navigate('/account/theme');
});

const handleLogout = useMutableCallback(() => {
logout();
const handlePreferences = useMutableCallback(() => {
router.navigate('/account/preferences');
});
const handleFeaturePreview = useMutableCallback(() => {
router.navigate('/account/feature-preview');
});

const featurePreviewItem = {
Expand All @@ -42,17 +41,23 @@ export const useAccountItems = (): GenericMenuItemProps[] => {

return [
{
id: 'my-account',
id: 'profile',
icon: 'user',
content: t('My_Account'),
content: t('Profile'),
onClick: handleMyAccount,
},
...(featurePreviewEnabled && defaultFeaturesPreview.length > 0 ? [featurePreviewItem] : []),
{
id: 'logout',
icon: 'sign-out',
content: t('Logout'),
onClick: handleLogout,
id: 'theme',
icon: 'palette',
content: t('Theme'),
onClick: handleThemes,
},
{
id: 'preferences',
icon: 'customize',
content: t('Preferences'),
onClick: handlePreferences,
},
...(featurePreviewEnabled && defaultFeaturesPreview.length > 0 ? [featurePreviewItem] : []),
];
};
33 changes: 0 additions & 33 deletions apps/meteor/client/sidebar/header/hooks/useThemeItems.tsx

This file was deleted.

24 changes: 18 additions & 6 deletions apps/meteor/client/sidebar/header/hooks/useUserMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import type { IUser } from '@rocket.chat/core-typings';
import { useTranslation } from '@rocket.chat/ui-contexts';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useLogout, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import type { GenericMenuItemProps } from '../../../components/GenericMenu/GenericMenuItem';
import UserMenuHeader from '../UserMenuHeader';
import { useAccountItems } from './useAccountItems';
import { useStatusItems } from './useStatusItems';
import { useThemeItems } from './useThemeItems';

export const useUserMenu = (user: IUser) => {
const t = useTranslation();

const statusItems = useStatusItems(user);
const themeItems = useThemeItems();
const accountItems = useAccountItems();

const logout = useLogout();
const handleLogout = useMutableCallback(() => {
logout();
});

const logoutItem: GenericMenuItemProps = {
id: 'logout',
icon: 'sign-out',
content: t('Logout'),
onClick: handleLogout,
};

return [
{
title: <UserMenuHeader user={user} />,
Expand All @@ -24,11 +36,11 @@ export const useUserMenu = (user: IUser) => {
items: statusItems,
},
{
title: t('Theme'),
items: themeItems,
title: t('Account'),
items: accountItems,
},
{
items: accountItems,
items: [logoutItem],
},
];
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { SelectOption } from '@rocket.chat/fuselage';
import { Select, Accordion, Field, FieldGroup, MultiSelect } from '@rocket.chat/fuselage';
import { Accordion, Field, FieldGroup, MultiSelect } from '@rocket.chat/fuselage';
import { useUserPreference, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo } from 'react';
Expand All @@ -11,7 +11,6 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
const t = useTranslation();

const userDontAskAgainList = useUserPreference<{ action: string; label: string }[]>('dontAskAgainList');
const themePreference = useUserPreference<'light' | 'dark' | 'auto'>('themeAppearence');

const options = useMemo(
() => (userDontAskAgainList || []).map(({ action, label }) => [action, label]) as SelectOption[],
Expand All @@ -23,26 +22,18 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
const { values, handlers, commit } = useForm(
{
dontAskAgainList: selectedOptions,
themeAppearence: themePreference,
},
onChange,
);

const { dontAskAgainList, themeAppearence } = values as {
const { dontAskAgainList } = values as {
dontAskAgainList: string[];
themeAppearence: string;
};

const { handleDontAskAgainList, handleThemeAppearence } = handlers;
const { handleDontAskAgainList } = handlers;

commitRef.current.global = commit;

const themeOptions: SelectOption[] = [
['auto', t('Theme_match_system')],
['light', t('Theme_light')],
['dark', t('Theme_dark')],
];

return (
<Accordion.Item title={t('Global')} {...props}>
<FieldGroup>
Expand All @@ -57,12 +48,6 @@ const PreferencesGlobalSection = ({ onChange, commitRef, ...props }: FormSection
/>
</Field.Row>
</Field>
<Field>
<Field.Label>{t('Theme_Appearence')}</Field.Label>
<Field.Row>
<Select value={themeAppearence} onChange={handleThemeAppearence} options={themeOptions} />
</Field.Row>
</Field>
</FieldGroup>
</Accordion.Item>
);
Expand Down
8 changes: 8 additions & 0 deletions apps/meteor/client/views/account/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ declare module '@rocket.chat/ui-contexts' {
pathname: '/account/feature-preview';
pattern: '/account/feature-preview';
};
'theme': {
pathname: '/account/theme';
pattern: '/account/theme';
};
}
}

Expand Down Expand Up @@ -79,3 +83,7 @@ registerAccountRoute('/feature-preview', {
name: 'feature-preview',
component: lazy(() => import('./featurePreview/AccountFeaturePreviewPage')),
});
registerAccountRoute('/theme', {
name: 'theme',
component: lazy(() => import('./themes/ThemePage')),
});
15 changes: 10 additions & 5 deletions apps/meteor/client/views/account/sidebarItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,22 @@ export const {
getSidebarItems: getAccountSidebarItems,
subscribeToSidebarItems: subscribeToAccountSidebarItems,
} = createSidebarItems([
{
href: '/account/preferences',
i18nLabel: 'Preferences',
icon: 'customize',
},
{
href: '/account/profile',
i18nLabel: 'Profile',
icon: 'user',
permissionGranted: (): boolean => settings.get('Accounts_AllowUserProfileChange'),
},
{
href: '/account/theme',
i18nLabel: 'Theme',
icon: 'palette',
},
{
href: '/account/preferences',
i18nLabel: 'Preferences',
icon: 'customize',
},
{
href: '/account/security',
i18nLabel: 'Security',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useRole, useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';

import GenericUpsellModal from '../../../components/GenericUpsellModal';
import { useUpsellActions } from '../../../components/GenericUpsellModal/hooks';

const HighContrastUpsellModal = ({ onClose }: { onClose: () => void }) => {
const t = useTranslation();

const isAdmin = useRole('admin');
const { handleGoFullyFeatured, handleTalkToSales } = useUpsellActions();

if (!isAdmin) {
return (
<GenericUpsellModal
title={t('High_contrast_upsell_title')}
img='images/high-contrast-upsell-modal.png'
subtitle={t('High_contrast_upsell_subtitle')}
description={t('High_contrast_upsell_description')}
onClose={onClose}
onCancel={onClose}
cancelText={t('Close')}
annotation={t('High_contrast_upsell_annotation')}
/>
);
}
return (
<GenericUpsellModal
title={t('High_contrast_upsell_title')}
img='images/high-contrast-upsell-modal.png'
subtitle={t('High_contrast_upsell_subtitle')}
description={t('High_contrast_upsell_description')}
onClose={onClose}
onCancel={handleTalkToSales}
onConfirm={handleGoFullyFeatured}
cancelText={t('Talk_to_sales')}
confirmText={t('Start_free_trial')}
/>
);
};
export default HighContrastUpsellModal;

0 comments on commit 357a3a5

Please sign in to comment.