🚧 Add search engine to user preferences

This commit is contained in:
Meier Lukas
2023-08-05 09:57:48 +02:00
parent c0b836f2a4
commit e9904ababf
10 changed files with 135 additions and 208 deletions

View File

@@ -1,11 +1,11 @@
import { Stack, Switch } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import { useFormContext } from '~/pages/user/preferences';
import { useUserPreferencesFormContext } from '~/pages/user/preferences';
export const AccessibilitySettings = () => {
const { t } = useTranslation('user/preferences');
const form = useFormContext();
const form = useUserPreferencesFormContext();
return (
<Stack>

View File

@@ -1,136 +0,0 @@
import { Alert, Paper, SegmentedControl, Space, Stack, TextInput, Title } from '@mantine/core';
import { IconInfoCircle } from '@tabler/icons-react';
import { useTranslation } from 'next-i18next';
import { ChangeEventHandler, useState } from 'react';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import {
CommonSearchEngineCommonSettingsType,
SearchEngineCommonSettingsType,
} from '../../../../types/settings';
import { SearchNewTabSwitch } from './SearchNewTabSwitch';
interface Props {
searchEngine: SearchEngineCommonSettingsType;
}
export const SearchEngineSelector = ({ searchEngine }: Props) => {
const { t } = useTranslation(['settings/general/search-engine']);
const { updateSearchEngineConfig } = useUpdateSearchEngineConfig();
const [engine, setEngine] = useState(searchEngine.type);
const [searchUrl, setSearchUrl] = useState(
searchEngine.type === 'custom' ? searchEngine.properties.template : searchUrls.google
);
const onEngineChange = (value: EngineType) => {
setEngine(value);
updateSearchEngineConfig(value, searchUrl);
};
const onSearchUrlChange: ChangeEventHandler<HTMLInputElement> = (ev) => {
const url = ev.currentTarget.value;
setSearchUrl(url);
updateSearchEngineConfig(engine, url);
};
return (
<Stack spacing={0} mt="xs">
<Title order={5} mb="xs">
{t('title')}
</Title>
<SegmentedControl
fullWidth
mb="sm"
title={t('title') ?? undefined}
value={engine}
onChange={onEngineChange}
data={searchEngineOptions}
/>
<Paper p="md" py="sm" mb="md" withBorder>
<Title order={6} mb={0}>
{t('configurationName')}
</Title>
<SearchNewTabSwitch defaultValue={searchEngine.properties.openInNewTab} />
{engine === 'custom' && (
<>
<Space mb="md" />
<TextInput
label={t('customEngine.label')}
name={t('configurationName') ?? undefined}
description={t('tips.placeholderTip')}
placeholder={t('customEngine.placeholder') ?? undefined}
value={searchUrl}
onChange={onSearchUrlChange}
/>
</>
)}
</Paper>
<Alert icon={<IconInfoCircle />} color="blue">
{t('tips.generalTip')}
</Alert>
</Stack>
);
};
const searchEngineOptions: { label: string; value: EngineType }[] = [
{ label: 'Google', value: 'google' },
{ label: 'DuckDuckGo', value: 'duckDuckGo' },
{ label: 'Bing', value: 'bing' },
{ label: 'Custom', value: 'custom' },
];
export const searchUrls: { [key in CommonSearchEngineCommonSettingsType['type']]: string } = {
google: 'https://google.com/search?q=',
duckDuckGo: 'https://duckduckgo.com/?q=',
bing: 'https://bing.com/search?q=',
};
type EngineType = SearchEngineCommonSettingsType['type'];
const useUpdateSearchEngineConfig = () => {
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
if (!configName) {
return {
updateSearchEngineConfig: () => {},
};
}
const updateSearchEngineConfig = (engine: EngineType, searchUrl: string) => {
updateConfig(configName, (prev) => ({
...prev,
settings: {
...prev.settings,
common: {
...prev.settings.common,
searchEngine:
engine === 'custom'
? {
type: engine,
properties: {
...prev.settings.common.searchEngine.properties,
template: searchUrl,
},
}
: {
type: engine,
properties: {
openInNewTab: prev.settings.common.searchEngine.properties.openInNewTab,
enabled: prev.settings.common.searchEngine.properties.enabled,
},
},
},
},
}));
};
return {
updateSearchEngineConfig,
};
};

View File

@@ -1,45 +0,0 @@
import { Switch } from '@mantine/core';
import { useTranslation } from 'next-i18next';
import { useState } from 'react';
import { useConfigContext } from '../../../../config/provider';
import { useConfigStore } from '../../../../config/store';
import { SearchEngineCommonSettingsType } from '../../../../types/settings';
interface SearchNewTabSwitchProps {
defaultValue: boolean | undefined;
}
export function SearchNewTabSwitch({ defaultValue }: SearchNewTabSwitchProps) {
const { t } = useTranslation('settings/general/search-engine');
const { name: configName } = useConfigContext();
const updateConfig = useConfigStore((x) => x.updateConfig);
const [openInNewTab, setOpenInNewTab] = useState<boolean>(defaultValue ?? true);
if (!configName) return null;
const toggleOpenInNewTab = () => {
setOpenInNewTab(!openInNewTab);
updateConfig(configName, (prev) => ({
...prev,
settings: {
...prev.settings,
common: {
...prev.settings.common,
searchEngine: {
...prev.settings.common.searchEngine,
properties: {
...prev.settings.common.searchEngine.properties,
openInNewTab: !openInNewTab,
},
} as SearchEngineCommonSettingsType,
},
},
}));
};
return (
<Switch checked={openInNewTab} onChange={toggleOpenInNewTab} label={t('searchNewTab.label')} />
);
}

View File

@@ -0,0 +1,60 @@
import { Paper, SegmentedControl, Stack, Switch, TextInput } from '@mantine/core';
import { useTranslation } from 'next-i18next';
import { useMemo } from 'react';
import { useUserPreferencesFormContext } from '~/pages/user/preferences';
const searchEngineOptions = [
{ label: 'Google', value: 'https://google.com/search?q=%s' },
{ label: 'DuckDuckGo', value: 'https://duckduckgo.com/?q=%s' },
{ label: 'Bing', value: 'https://bing.com/search?q=%s' },
{ value: 'custom' },
] as const;
const useSegmentData = () => {
const { t } = useTranslation('user/preferences');
return searchEngineOptions.map((option) => ({
label: option.value === 'custom' ? t('searchEngine.custom') : option.label,
value: option.value,
}));
};
export const SearchEngineSelector = () => {
const { t } = useTranslation('user/preferences');
const form = useUserPreferencesFormContext();
const segmentData = useSegmentData();
const segmentValue = useMemo(
() =>
searchEngineOptions.find((x) => x.value === form.values.searchTemplate)?.value ?? 'custom',
[form.values.searchTemplate]
);
return (
<Stack>
<SegmentedControl
fullWidth
data={segmentData}
value={segmentValue}
onChange={(v: typeof segmentValue) => {
v === 'custom'
? form.setFieldValue('searchTemplate', '')
: form.setFieldValue('searchTemplate', v);
}}
/>
<Paper p="md" py="sm" mb="md" withBorder>
<Stack spacing="sm">
<Switch
label={t('searchEngine.newTab.label')}
{...form.getInputProps('openSearchInNewTab', { type: 'checkbox' })}
/>
<TextInput
label={t('searchEngine.template.label')}
description={t('searchEngine.template.description')}
inputWrapperOrder={['label', 'input', 'description', 'error']}
{...form.getInputProps('searchTemplate')}
/>
</Stack>
</Paper>
</Stack>
);
};

View File

@@ -1,5 +1,5 @@
import { Autocomplete, Group, Kbd, Modal, Text, Tooltip, useMantineTheme } from '@mantine/core';
import { useDisclosure, useHotkeys, useMediaQuery } from '@mantine/hooks';
import { Autocomplete, Group, Text, useMantineTheme } from '@mantine/core';
import { useDisclosure, useHotkeys } from '@mantine/hooks';
import {
IconBrandYoutube,
IconDownload,
@@ -10,7 +10,7 @@ import {
} from '@tabler/icons-react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { ReactNode, forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import { ReactNode, forwardRef, useMemo, useRef, useState } from 'react';
import { useConfigContext } from '~/config/provider';
import { api } from '~/utils/api';