hidden shit, and online status bars, search settings
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Avatar, Box, Button, config, IconButton, MenuItem, Overlay, OverlayBackdrop, OverlayCenter, Text } from 'folds';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
config,
|
||||
IconButton,
|
||||
Input,
|
||||
MenuItem,
|
||||
Overlay,
|
||||
OverlayBackdrop,
|
||||
OverlayCenter,
|
||||
Text,
|
||||
} from 'folds';
|
||||
import { Icon, Icons, IconSrc } from '../../components/icons';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { General } from './general';
|
||||
@@ -22,18 +34,17 @@ import { Plugins } from './plugins';
|
||||
import { UseStateProvider } from '../../components/UseStateProvider';
|
||||
import { stopPropagation } from '../../utils/keyboard';
|
||||
import { LogoutDialog } from '../../components/LogoutDialog';
|
||||
import { fuzzyFilter } from '../../utils/fuzzy';
|
||||
import { SETTINGS_SEARCH_CATALOG } from './settingsSearchCatalog';
|
||||
import { SettingsPages } from './SettingsPages';
|
||||
import { SettingsSearchResult } from './styles.css';
|
||||
import { BreakWord } from '../../styles/Text.css';
|
||||
import {
|
||||
SettingsFocusProvider,
|
||||
settingAnchorId,
|
||||
} from '../../components/setting-tile';
|
||||
|
||||
export enum SettingsPages {
|
||||
GeneralPage,
|
||||
AccountPage,
|
||||
NotificationPage,
|
||||
AudioPage,
|
||||
DevicesPage,
|
||||
EmojisStickersPage,
|
||||
PluginsPage,
|
||||
DeveloperToolsPage,
|
||||
AboutPage,
|
||||
}
|
||||
export { SettingsPages };
|
||||
|
||||
type SettingsMenuItem = {
|
||||
page: SettingsPages;
|
||||
@@ -112,7 +123,37 @@ export function Settings({ initialPage, requestClose }: SettingsProps) {
|
||||
if (initialPage) return initialPage;
|
||||
return screenSize === ScreenSize.Mobile ? undefined : SettingsPages.GeneralPage;
|
||||
});
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [focusAnchorId, setFocusAnchorId] = useState<string | undefined>();
|
||||
const menuItems = useSettingsMenuItems();
|
||||
const pageNameByPage = useMemo(() => {
|
||||
const map = new Map<SettingsPages, string>();
|
||||
menuItems.forEach((item) => map.set(item.page, item.name));
|
||||
return map;
|
||||
}, [menuItems]);
|
||||
const iconByPage = useMemo(() => {
|
||||
const map = new Map<SettingsPages, IconSrc>();
|
||||
menuItems.forEach((item) => map.set(item.page, item.icon));
|
||||
return map;
|
||||
}, [menuItems]);
|
||||
|
||||
const searchResults = useMemo(() => {
|
||||
const q = searchQuery.trim();
|
||||
if (!q) return [];
|
||||
return fuzzyFilter(SETTINGS_SEARCH_CATALOG, q, (entry) => [
|
||||
entry.title,
|
||||
pageNameByPage.get(entry.page),
|
||||
...(entry.keywords ?? []),
|
||||
]).slice(0, 40);
|
||||
}, [pageNameByPage, searchQuery]);
|
||||
|
||||
const settingsFocus = useMemo(
|
||||
() => ({
|
||||
anchorId: focusAnchorId,
|
||||
setAnchorId: setFocusAnchorId,
|
||||
}),
|
||||
[focusAnchorId]
|
||||
);
|
||||
|
||||
const handlePageRequestClose = () => {
|
||||
if (screenSize === ScreenSize.Mobile) {
|
||||
@@ -122,120 +163,244 @@ export function Settings({ initialPage, requestClose }: SettingsProps) {
|
||||
requestClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<PageRoot
|
||||
nav={
|
||||
screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
|
||||
<PageNav size="300">
|
||||
<PageNavHeader outlined={false}>
|
||||
<Box grow="Yes" gap="200">
|
||||
<Avatar size="200" radii="300">
|
||||
<UserAvatar
|
||||
userId={userId}
|
||||
src={avatarUrl}
|
||||
renderFallback={() => <Text size="H6">{nameInitials(displayName)}</Text>}
|
||||
/>
|
||||
</Avatar>
|
||||
<Text size="H4" truncate>
|
||||
Settings
|
||||
</Text>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<IconButton onClick={requestClose} variant="Background">
|
||||
<Icon src={Icons.Cross} />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</PageNavHeader>
|
||||
<Box grow="Yes" direction="Column">
|
||||
<PageNavContent>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
{menuItems.map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
variant="Background"
|
||||
radii="400"
|
||||
aria-pressed={activePage === item.page}
|
||||
before={<Icon src={item.icon} size="100" filled={activePage === item.page} />}
|
||||
onClick={() => setActivePage(item.page)}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontWeight: activePage === item.page ? config.fontWeight.W600 : undefined,
|
||||
}}
|
||||
size="T300"
|
||||
truncate
|
||||
>
|
||||
{item.name}
|
||||
</Text>
|
||||
</MenuItem>
|
||||
))}
|
||||
</div>
|
||||
</PageNavContent>
|
||||
<Box style={{ padding: config.space.S200 }} shrink="No" direction="Column">
|
||||
<UseStateProvider initial={false}>
|
||||
{(logout, setLogout) => (
|
||||
<>
|
||||
<Button
|
||||
size="300"
|
||||
variant="Critical"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
before={<Icon src={Icons.Power} size="100" />}
|
||||
onClick={() => setLogout(true)}
|
||||
>
|
||||
<Text size="B400">Logout</Text>
|
||||
</Button>
|
||||
{logout && (
|
||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||
<OverlayCenter>
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
onDeactivate: () => setLogout(false),
|
||||
clickOutsideDeactivates: true,
|
||||
escapeDeactivates: stopPropagation,
|
||||
}}
|
||||
>
|
||||
<LogoutDialog handleClose={() => setLogout(false)} />
|
||||
</FocusTrap>
|
||||
</OverlayCenter>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</UseStateProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
</PageNav>
|
||||
)
|
||||
const openSearchResult = useCallback(
|
||||
(page: SettingsPages, title: string) => {
|
||||
const pageName = pageNameByPage.get(page);
|
||||
// Page-level entries just open the page; tile entries scroll into view.
|
||||
if (title !== pageName) {
|
||||
setFocusAnchorId(settingAnchorId(title));
|
||||
} else {
|
||||
setFocusAnchorId(undefined);
|
||||
}
|
||||
>
|
||||
{activePage === SettingsPages.GeneralPage && (
|
||||
<General requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AccountPage && (
|
||||
<Account requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.NotificationPage && (
|
||||
<Notifications requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AudioPage && (
|
||||
<Audio requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.DevicesPage && (
|
||||
<Devices requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.EmojisStickersPage && (
|
||||
<EmojisStickers requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.PluginsPage && (
|
||||
<Plugins requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.DeveloperToolsPage && (
|
||||
<DeveloperTools requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AboutPage && <About requestClose={handlePageRequestClose} />}
|
||||
</PageRoot>
|
||||
setActivePage(page);
|
||||
setSearchQuery('');
|
||||
},
|
||||
[pageNameByPage]
|
||||
);
|
||||
|
||||
// Fallback scroll if the page just mounted and tiles need a moment to render.
|
||||
useEffect(() => {
|
||||
if (!focusAnchorId) return;
|
||||
let cancelled = false;
|
||||
let attempts = 0;
|
||||
|
||||
const tryScroll = () => {
|
||||
if (cancelled) return;
|
||||
const el = document.getElementById(focusAnchorId);
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
return;
|
||||
}
|
||||
if (attempts < 40) {
|
||||
attempts += 1;
|
||||
window.requestAnimationFrame(tryScroll);
|
||||
} else {
|
||||
setFocusAnchorId(undefined);
|
||||
}
|
||||
};
|
||||
|
||||
const timer = window.setTimeout(tryScroll, 50);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
window.clearTimeout(timer);
|
||||
};
|
||||
}, [focusAnchorId, activePage]);
|
||||
|
||||
const isSearching = searchQuery.trim().length > 0;
|
||||
|
||||
return (
|
||||
<SettingsFocusProvider value={settingsFocus}>
|
||||
<PageRoot
|
||||
nav={
|
||||
screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
|
||||
<PageNav size="300">
|
||||
<PageNavHeader outlined={false}>
|
||||
<Box grow="Yes" gap="200">
|
||||
<Avatar size="200" radii="300">
|
||||
<UserAvatar
|
||||
userId={userId}
|
||||
src={avatarUrl}
|
||||
renderFallback={() => <Text size="H6">{nameInitials(displayName)}</Text>}
|
||||
/>
|
||||
</Avatar>
|
||||
<Text size="H4" truncate>
|
||||
Settings
|
||||
</Text>
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<IconButton onClick={requestClose} variant="Background">
|
||||
<Icon src={Icons.Cross} />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
</PageNavHeader>
|
||||
<Box grow="Yes" direction="Column">
|
||||
<Box
|
||||
style={{ padding: `${config.space.S100} ${config.space.S200}`, width: '100%' }}
|
||||
shrink="No"
|
||||
>
|
||||
<Input
|
||||
style={{ width: '100%' }}
|
||||
variant="Background"
|
||||
size="400"
|
||||
placeholder="Search"
|
||||
value={searchQuery}
|
||||
onChange={(evt) => setSearchQuery(evt.target.value)}
|
||||
before={<Icon size="100" src={Icons.Search} />}
|
||||
after={
|
||||
searchQuery ? (
|
||||
<IconButton
|
||||
size="300"
|
||||
variant="Background"
|
||||
radii="300"
|
||||
onClick={() => setSearchQuery('')}
|
||||
aria-label="Clear search"
|
||||
>
|
||||
<Icon size="50" src={Icons.Cross} />
|
||||
</IconButton>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<PageNavContent>
|
||||
<div style={{ flexGrow: 1 }}>
|
||||
{isSearching ? (
|
||||
searchResults.length > 0 ? (
|
||||
searchResults.map(({ item }) => (
|
||||
<button
|
||||
key={`${item.page}-${item.title}`}
|
||||
type="button"
|
||||
className={SettingsSearchResult}
|
||||
aria-pressed={activePage === item.page}
|
||||
onClick={() => openSearchResult(item.page, item.title)}
|
||||
>
|
||||
<Box shrink="No" style={{ paddingTop: config.space.S100 }}>
|
||||
<Icon
|
||||
src={iconByPage.get(item.page) ?? Icons.Setting}
|
||||
size="100"
|
||||
filled={activePage === item.page}
|
||||
/>
|
||||
</Box>
|
||||
<Box grow="Yes" direction="Column" gap="100" style={{ minWidth: 0 }}>
|
||||
<Text
|
||||
className={BreakWord}
|
||||
size="T300"
|
||||
style={{
|
||||
fontWeight:
|
||||
activePage === item.page ? config.fontWeight.W600 : undefined,
|
||||
}}
|
||||
>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text size="T200" priority="300">
|
||||
{pageNameByPage.get(item.page)}
|
||||
</Text>
|
||||
</Box>
|
||||
</button>
|
||||
))
|
||||
) : (
|
||||
<Box
|
||||
direction="Column"
|
||||
alignItems="Center"
|
||||
style={{ padding: config.space.S400 }}
|
||||
>
|
||||
<Text size="T300" priority="300" className={BreakWord}>
|
||||
No settings match “{searchQuery.trim()}”
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
) : (
|
||||
menuItems.map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
variant="Background"
|
||||
radii="400"
|
||||
aria-pressed={activePage === item.page}
|
||||
before={
|
||||
<Icon src={item.icon} size="100" filled={activePage === item.page} />
|
||||
}
|
||||
onClick={() => setActivePage(item.page)}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontWeight:
|
||||
activePage === item.page ? config.fontWeight.W600 : undefined,
|
||||
}}
|
||||
size="T300"
|
||||
truncate
|
||||
>
|
||||
{item.name}
|
||||
</Text>
|
||||
</MenuItem>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</PageNavContent>
|
||||
<Box style={{ padding: config.space.S200 }} shrink="No" direction="Column">
|
||||
<UseStateProvider initial={false}>
|
||||
{(logout, setLogout) => (
|
||||
<>
|
||||
<Button
|
||||
size="300"
|
||||
variant="Critical"
|
||||
fill="None"
|
||||
radii="Pill"
|
||||
before={<Icon src={Icons.Power} size="100" />}
|
||||
onClick={() => setLogout(true)}
|
||||
>
|
||||
<Text size="B400">Logout</Text>
|
||||
</Button>
|
||||
{logout && (
|
||||
<Overlay open backdrop={<OverlayBackdrop />}>
|
||||
<OverlayCenter>
|
||||
<FocusTrap
|
||||
focusTrapOptions={{
|
||||
onDeactivate: () => setLogout(false),
|
||||
clickOutsideDeactivates: true,
|
||||
escapeDeactivates: stopPropagation,
|
||||
}}
|
||||
>
|
||||
<LogoutDialog handleClose={() => setLogout(false)} />
|
||||
</FocusTrap>
|
||||
</OverlayCenter>
|
||||
</Overlay>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</UseStateProvider>
|
||||
</Box>
|
||||
</Box>
|
||||
</PageNav>
|
||||
)
|
||||
}
|
||||
>
|
||||
{activePage === SettingsPages.GeneralPage && (
|
||||
<General requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AccountPage && (
|
||||
<Account requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.NotificationPage && (
|
||||
<Notifications requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AudioPage && (
|
||||
<Audio requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.DevicesPage && (
|
||||
<Devices requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.EmojisStickersPage && (
|
||||
<EmojisStickers requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.PluginsPage && (
|
||||
<Plugins requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.DeveloperToolsPage && (
|
||||
<DeveloperTools requestClose={handlePageRequestClose} />
|
||||
)}
|
||||
{activePage === SettingsPages.AboutPage && <About requestClose={handlePageRequestClose} />}
|
||||
</PageRoot>
|
||||
</SettingsFocusProvider>
|
||||
);
|
||||
}
|
||||
|
||||
11
src/app/features/settings/SettingsPages.ts
Normal file
11
src/app/features/settings/SettingsPages.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export enum SettingsPages {
|
||||
GeneralPage,
|
||||
AccountPage,
|
||||
NotificationPage,
|
||||
AudioPage,
|
||||
DevicesPage,
|
||||
EmojisStickersPage,
|
||||
PluginsPage,
|
||||
DeveloperToolsPage,
|
||||
AboutPage,
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import { SettingTile } from '../../../components/setting-tile';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { pluginMarketplaceManager, pluginRegistry, SettingDefinition } from './PluginAPI';
|
||||
import { stopPropagation } from '../../../utils/keyboard';
|
||||
import { fuzzyFilter } from '../../../utils/fuzzy';
|
||||
|
||||
/**
|
||||
* Renders settings UI for a plugin based on its settings schema
|
||||
@@ -320,13 +321,6 @@ export function Plugins({ requestClose }: PluginsProps) {
|
||||
[installedPlugins]
|
||||
);
|
||||
|
||||
const fuzzySearch = (query: string, text: string): boolean => {
|
||||
if (!query) return true;
|
||||
const searchLower = query.toLowerCase();
|
||||
const textLower = text.toLowerCase();
|
||||
return textLower.includes(searchLower);
|
||||
};
|
||||
|
||||
const PluginAvatar = ({ thumbnail, name }: { thumbnail: string; name: string }) => {
|
||||
const [imageError, setImageError] = useState(false);
|
||||
|
||||
@@ -365,22 +359,22 @@ export function Plugins({ requestClose }: PluginsProps) {
|
||||
};
|
||||
|
||||
const filteredMarketplacePlugins = useMemo(() => {
|
||||
if (!searchQuery) return marketplacePlugins;
|
||||
return marketplacePlugins.filter((plugin) =>
|
||||
fuzzySearch(searchQuery, plugin.name) ||
|
||||
fuzzySearch(searchQuery, plugin.description) ||
|
||||
fuzzySearch(searchQuery, plugin.author) ||
|
||||
plugin.tags.some((tag) => fuzzySearch(searchQuery, tag))
|
||||
);
|
||||
if (!searchQuery.trim()) return marketplacePlugins;
|
||||
return fuzzyFilter(marketplacePlugins, searchQuery, (plugin) => [
|
||||
plugin.name,
|
||||
plugin.description,
|
||||
plugin.author,
|
||||
...plugin.tags,
|
||||
]).map(({ item }) => item);
|
||||
}, [marketplacePlugins, searchQuery]);
|
||||
|
||||
const filteredInstalledPlugins = useMemo(() => {
|
||||
if (!searchQuery) return installedPlugins;
|
||||
return installedPlugins.filter((plugin) =>
|
||||
fuzzySearch(searchQuery, plugin.name) ||
|
||||
fuzzySearch(searchQuery, plugin.description) ||
|
||||
fuzzySearch(searchQuery, plugin.author)
|
||||
);
|
||||
if (!searchQuery.trim()) return installedPlugins;
|
||||
return fuzzyFilter(installedPlugins, searchQuery, (plugin) => [
|
||||
plugin.name,
|
||||
plugin.description,
|
||||
plugin.author,
|
||||
]).map(({ item }) => item);
|
||||
}, [installedPlugins, searchQuery]);
|
||||
|
||||
return (
|
||||
|
||||
137
src/app/features/settings/settingsSearchCatalog.ts
Normal file
137
src/app/features/settings/settingsSearchCatalog.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { SettingsPages } from './SettingsPages';
|
||||
|
||||
export type SettingsSearchEntry = {
|
||||
page: SettingsPages;
|
||||
/** Primary label shown in results. */
|
||||
title: string;
|
||||
/** Extra terms for fuzzy matching (aliases, related words). */
|
||||
keywords?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Catalog of settings pages and individual tiles for fuzzy search.
|
||||
* Keep titles aligned with SettingTile labels in each page.
|
||||
*/
|
||||
export const SETTINGS_SEARCH_CATALOG: SettingsSearchEntry[] = [
|
||||
// Pages
|
||||
{ page: SettingsPages.GeneralPage, title: 'General', keywords: ['appearance', 'theme', 'messages', 'editor'] },
|
||||
{ page: SettingsPages.AccountPage, title: 'Account', keywords: ['profile', 'email', 'mxid', 'ignore'] },
|
||||
{
|
||||
page: SettingsPages.NotificationPage,
|
||||
title: 'Notifications',
|
||||
keywords: ['alerts', 'push', 'desktop', 'sound', 'mentions'],
|
||||
},
|
||||
{ page: SettingsPages.AudioPage, title: 'Audio & Video', keywords: ['microphone', 'speaker', 'call', 'camera'] },
|
||||
{ page: SettingsPages.DevicesPage, title: 'Devices', keywords: ['sessions', 'verification', 'backup', 'export'] },
|
||||
{
|
||||
page: SettingsPages.EmojisStickersPage,
|
||||
title: 'Emojis & Stickers',
|
||||
keywords: ['emoticons', 'stickers', 'telegram', 'custom emoji'],
|
||||
},
|
||||
{ page: SettingsPages.PluginsPage, title: 'Plugins', keywords: ['extensions', 'addons', 'marketplace'] },
|
||||
{
|
||||
page: SettingsPages.DeveloperToolsPage,
|
||||
title: 'Developer Tools',
|
||||
keywords: ['debug', 'token', 'account data'],
|
||||
},
|
||||
{ page: SettingsPages.AboutPage, title: 'About', keywords: ['version', 'cache', 'protocol', 'paarrot'] },
|
||||
|
||||
// General
|
||||
{
|
||||
page: SettingsPages.GeneralPage,
|
||||
title: 'System Theme',
|
||||
keywords: ['appearance', 'dark', 'light', 'auto'],
|
||||
},
|
||||
{ page: SettingsPages.GeneralPage, title: 'Theme', keywords: ['appearance', 'color scheme'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Monochrome Mode', keywords: ['grayscale', 'appearance'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Emoji Style', keywords: ['appearance', 'twemoji', 'native'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Page Zoom', keywords: ['scale', 'accessibility', 'size'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Date Format', keywords: ['time', 'calendar'] },
|
||||
{ page: SettingsPages.GeneralPage, title: '24-Hour Time Format', keywords: ['clock', 'time'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'ENTER for Newline', keywords: ['editor', 'keyboard', 'shift enter'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Markdown Formatting', keywords: ['editor', 'formatting'] },
|
||||
{
|
||||
page: SettingsPages.GeneralPage,
|
||||
title: 'Hide Typing & Read Receipts',
|
||||
keywords: ['privacy', 'activity', 'typing'],
|
||||
},
|
||||
{ page: SettingsPages.GeneralPage, title: 'Auto-Join Space Rooms', keywords: ['spaces', 'join'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Message Layout', keywords: ['bubbles', 'compact', 'modern'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Message Spacing', keywords: ['density', 'gap'] },
|
||||
{
|
||||
page: SettingsPages.GeneralPage,
|
||||
title: 'Scroll to Latest on Room Reselect',
|
||||
keywords: ['timeline', 'jump'],
|
||||
},
|
||||
{ page: SettingsPages.GeneralPage, title: 'Legacy Username Color', keywords: ['appearance', 'mxid color'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Hide Membership Change', keywords: ['joins', 'leaves', 'timeline'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Hide Profile Change', keywords: ['avatar', 'displayname', 'timeline'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Disable Media Auto Load', keywords: ['images', 'bandwidth', 'lazy'] },
|
||||
{ page: SettingsPages.GeneralPage, title: 'Url Preview', keywords: ['link', 'embed', 'unfurl'] },
|
||||
{
|
||||
page: SettingsPages.GeneralPage,
|
||||
title: 'Url Preview in Encrypted Room',
|
||||
keywords: ['e2ee', 'link', 'embed'],
|
||||
},
|
||||
{ page: SettingsPages.GeneralPage, title: 'Show Hidden Events', keywords: ['state', 'debug', 'timeline'] },
|
||||
|
||||
// Account
|
||||
{ page: SettingsPages.AccountPage, title: 'Profile', keywords: ['displayname', 'avatar', 'name'] },
|
||||
{ page: SettingsPages.AccountPage, title: 'Email Address', keywords: ['contact', 'mail'] },
|
||||
{ page: SettingsPages.AccountPage, title: 'Ignored Users', keywords: ['block', 'mute', 'ignore'] },
|
||||
|
||||
// Notifications
|
||||
{ page: SettingsPages.NotificationPage, title: 'Desktop Notifications', keywords: ['system', 'os'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Notification Sound', keywords: ['audio', 'alert'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Email Notification', keywords: ['mail'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Background Notifications', keywords: ['tray', 'electron'] },
|
||||
{ page: SettingsPages.NotificationPage, title: '1-to-1 Chats', keywords: ['dm', 'direct'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Rooms', keywords: ['messages', 'push'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Mention @room', keywords: ['highlight', 'ping'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Keyword Messages', keywords: ['highlight', 'filter'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Special Messages', keywords: ['mentions', 'displayname'] },
|
||||
{ page: SettingsPages.NotificationPage, title: 'Test Notification', keywords: ['preview', 'sound'] },
|
||||
|
||||
// Audio & Video
|
||||
{ page: SettingsPages.AudioPage, title: 'Input Device', keywords: ['microphone', 'mic'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Output Device', keywords: ['speaker', 'headphones'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Noise Suppression', keywords: ['microphone', 'processing'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Echo Cancellation', keywords: ['microphone', 'aec'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Auto Gain Control', keywords: ['microphone', 'agc'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Resolution', keywords: ['screen share', 'video'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Bitrate', keywords: ['screen share', 'quality'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Frame Rate', keywords: ['screen share', 'fps'] },
|
||||
{ page: SettingsPages.AudioPage, title: 'Show Remote Cursor', keywords: ['screen share', 'pointer'] },
|
||||
|
||||
// Devices
|
||||
{ page: SettingsPages.DevicesPage, title: 'Device Verification', keywords: ['crypto', 'cross signing'] },
|
||||
{ page: SettingsPages.DevicesPage, title: 'Device Dashboard', keywords: ['sessions', 'logout'] },
|
||||
{ page: SettingsPages.DevicesPage, title: 'Export Messages Data', keywords: ['backup', 'download'] },
|
||||
{ page: SettingsPages.DevicesPage, title: 'Import Messages Data', keywords: ['backup', 'restore'] },
|
||||
|
||||
// Emojis & Stickers
|
||||
{
|
||||
page: SettingsPages.EmojisStickersPage,
|
||||
title: 'Auto-convert Text Emoticons',
|
||||
keywords: ['smileys', 'colon'],
|
||||
},
|
||||
{ page: SettingsPages.EmojisStickersPage, title: 'Telegram Bot Token', keywords: ['stickers', 'import'] },
|
||||
{ page: SettingsPages.EmojisStickersPage, title: 'Sticker Pack URL', keywords: ['telegram', 'import'] },
|
||||
|
||||
// Developer Tools
|
||||
{
|
||||
page: SettingsPages.DeveloperToolsPage,
|
||||
title: 'Enable Developer Tools',
|
||||
keywords: ['debug'],
|
||||
},
|
||||
{ page: SettingsPages.DeveloperToolsPage, title: 'Access Token', keywords: ['auth', 'secret'] },
|
||||
{ page: SettingsPages.DeveloperToolsPage, title: 'Account Data', keywords: ['global', 'events'] },
|
||||
|
||||
// About
|
||||
{ page: SettingsPages.AboutPage, title: 'Clear Cache & Reload', keywords: ['reset', 'storage'] },
|
||||
{
|
||||
page: SettingsPages.AboutPage,
|
||||
title: 'Protocol Handler (paarrot://)',
|
||||
keywords: ['deeplink', 'url scheme'],
|
||||
},
|
||||
];
|
||||
@@ -1,6 +1,27 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
import { config } from 'folds';
|
||||
import { color, config } from 'folds';
|
||||
|
||||
export const SequenceCardStyle = style({
|
||||
padding: config.space.S300,
|
||||
});
|
||||
|
||||
export const SettingsSearchResult = style({
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
cursor: 'pointer',
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
padding: `${config.space.S200} ${config.space.S300}`,
|
||||
borderRadius: config.radii.R400,
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: config.space.S200,
|
||||
selectors: {
|
||||
'&:hover': {
|
||||
backgroundColor: color.Background.ContainerHover,
|
||||
},
|
||||
'&[aria-pressed="true"]': {
|
||||
backgroundColor: color.Background.ContainerActive,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user