diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 63ea23a40..b94360604 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -86,6 +86,7 @@ model UserSettings {
disablePingPulse Boolean @default(false)
replacePingWithIcons Boolean @default(false)
useDebugLanguage Boolean @default(false)
+ autoFocusSearch Boolean @default(false)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId])
diff --git a/public/locales/en/user/preferences.json b/public/locales/en/user/preferences.json
index 069cd7065..d1bbf2171 100644
--- a/public/locales/en/user/preferences.json
+++ b/public/locales/en/user/preferences.json
@@ -36,6 +36,10 @@
"newTab": {
"label": "Open search results in a new tab"
},
+ "autoFocus": {
+ "label": "Focus search bar on page load.",
+ "description": "This will automatically focus the search bar, when you navigate to the board pages. It will only work on desktop devices."
+ },
"template": {
"label": "Query URL",
"description": "Use %s as a placeholder for the query"
diff --git a/src/components/User/Preferences/SearchEngineSelector.tsx b/src/components/User/Preferences/SearchEngineSelector.tsx
index 6c34f4fa0..d8e301ca2 100644
--- a/src/components/User/Preferences/SearchEngineSelector.tsx
+++ b/src/components/User/Preferences/SearchEngineSelector.tsx
@@ -31,6 +31,11 @@ export const SearchEngineSettings = () => {
label={t('searchEngine.newTab.label')}
{...form.getInputProps('openSearchInNewTab', { type: 'checkbox' })}
/>
+
{
const { config } = useConfigContext();
+ const { data: session } = useSession();
return (
- }>
+ }
+ >
{children}
diff --git a/src/components/layout/Templates/MainLayout.tsx b/src/components/layout/Templates/MainLayout.tsx
index 4a648650c..af3c8bbad 100644
--- a/src/components/layout/Templates/MainLayout.tsx
+++ b/src/components/layout/Templates/MainLayout.tsx
@@ -6,9 +6,16 @@ type MainLayoutProps = {
headerActions?: React.ReactNode;
contentComponents?: React.ReactNode;
children: React.ReactNode;
+ autoFocusSearch?: boolean;
};
-export const MainLayout = ({ showExperimental, headerActions, contentComponents, children }: MainLayoutProps) => {
+export const MainLayout = ({
+ showExperimental,
+ headerActions,
+ contentComponents,
+ children,
+ autoFocusSearch,
+}: MainLayoutProps) => {
const theme = useMantineTheme();
return (
@@ -18,7 +25,14 @@ export const MainLayout = ({ showExperimental, headerActions, contentComponents,
background: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
},
}}
- header={}
+ header={
+
+ }
className="dashboard-app-shell"
>
{children}
diff --git a/src/components/layout/header/Header.tsx b/src/components/layout/header/Header.tsx
index 5ecd9256f..0bc301db7 100644
--- a/src/components/layout/header/Header.tsx
+++ b/src/components/layout/header/Header.tsx
@@ -23,6 +23,7 @@ type MainHeaderProps = {
headerActions?: React.ReactNode;
contentComponents?: React.ReactNode;
leftIcon?: React.ReactNode;
+ autoFocusSearch?: boolean;
};
export const MainHeader = ({
@@ -31,6 +32,7 @@ export const MainHeader = ({
headerActions,
leftIcon,
contentComponents,
+ autoFocusSearch,
}: MainHeaderProps) => {
const { breakpoints } = useMantineTheme();
const isSmallerThanMd = useMediaQuery(`(max-width: ${breakpoints.sm})`);
@@ -51,7 +53,7 @@ export const MainHeader = ({
- {!isSmallerThanMd && }
+ {!isSmallerThanMd && }
diff --git a/src/components/layout/header/Search.tsx b/src/components/layout/header/Search.tsx
index d4e2d73fd..e0de950b2 100644
--- a/src/components/layout/header/Search.tsx
+++ b/src/components/layout/header/Search.tsx
@@ -19,9 +19,10 @@ import { MovieModal } from './Search/MovieModal';
type SearchProps = {
isMobile?: boolean;
+ autoFocus?: boolean;
};
-export const Search = ({ isMobile }: SearchProps) => {
+export const Search = ({ isMobile, autoFocus }: SearchProps) => {
const { t } = useTranslation('layout/header');
const [search, setSearch] = useState('');
const ref = useRef(null);
@@ -62,6 +63,7 @@ export const Search = ({ isMobile }: SearchProps) => {
variant="filled"
placeholder={`${t('search.label')}...`}
hoverOnSearchChange
+ autoFocus={autoFocus}
rightSection={
ref.current?.focus()}
diff --git a/src/pages/user/preferences.tsx b/src/pages/user/preferences.tsx
index c6a12e897..2dcef24e0 100644
--- a/src/pages/user/preferences.tsx
+++ b/src/pages/user/preferences.tsx
@@ -89,6 +89,7 @@ const SettingsComponent = ({
replaceDotsWithIcons: settings.replacePingWithIcons,
searchTemplate: settings.searchTemplate,
openSearchInNewTab: settings.openSearchInNewTab,
+ autoFocusSearch: settings.autoFocusSearch,
},
validate: i18nZodResolver(updateSettingsValidationSchema),
validateInputOnBlur: true,
diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts
index 6b8db0a3f..1287d1d95 100644
--- a/src/server/api/routers/user.ts
+++ b/src/server/api/routers/user.ts
@@ -221,6 +221,7 @@ export const userRouter = createTRPCRouter({
firstDayOfWeek: input.firstDayOfWeek,
searchTemplate: input.searchTemplate,
openSearchInNewTab: input.openSearchInNewTab,
+ autoFocusSearch: input.autoFocusSearch,
},
},
},
diff --git a/src/server/auth.ts b/src/server/auth.ts
index 70ba1f873..770af0f10 100644
--- a/src/server/auth.ts
+++ b/src/server/auth.ts
@@ -24,6 +24,7 @@ declare module 'next-auth' {
id: string;
isAdmin: boolean;
colorScheme: 'light' | 'dark' | 'environment';
+ autoFocusSearch: boolean;
language: string;
// ...other properties
// role: UserRole;
@@ -33,6 +34,7 @@ declare module 'next-auth' {
interface User {
isAdmin: boolean;
colorScheme: 'light' | 'dark' | 'environment';
+ autoFocusSearch: boolean;
language: string;
// ...other properties
// role: UserRole;
@@ -75,6 +77,7 @@ export const constructAuthOptions = (
select: {
colorScheme: true,
language: true,
+ autoFocusSearch: true,
},
},
},
@@ -83,6 +86,7 @@ export const constructAuthOptions = (
session.user.isAdmin = userFromDatabase.isAdmin;
session.user.colorScheme = colorSchemeParser.parse(userFromDatabase.settings?.colorScheme);
session.user.language = userFromDatabase.settings?.language ?? 'en';
+ session.user.autoFocusSearch = userFromDatabase.settings?.autoFocusSearch ?? false;
}
return session;
@@ -148,6 +152,7 @@ export const constructAuthOptions = (
select: {
colorScheme: true,
language: true,
+ autoFocusSearch: true,
},
},
},
@@ -173,6 +178,7 @@ export const constructAuthOptions = (
isAdmin: false,
colorScheme: colorSchemeParser.parse(user.settings?.colorScheme),
language: user.settings?.language ?? 'en',
+ autoFocusSearch: user.settings?.autoFocusSearch ?? false,
};
},
}),
diff --git a/src/validations/user.ts b/src/validations/user.ts
index c44679a49..c88bcc99a 100644
--- a/src/validations/user.ts
+++ b/src/validations/user.ts
@@ -49,4 +49,5 @@ export const updateSettingsValidationSchema = z.object({
replaceDotsWithIcons: z.boolean(),
searchTemplate: z.string().nonempty().max(256),
openSearchInNewTab: z.boolean(),
+ autoFocusSearch: z.boolean(),
});