added autocomplete for user lookup. scrollbar theme

This commit is contained in:
PhilReact 2025-04-30 15:13:06 +03:00
parent 4d888e832f
commit 6b2d92d73d
8 changed files with 159 additions and 77 deletions

View File

@ -1,6 +1,6 @@
{
"name": "qortal-hub",
"version": "0.5.3",
"version": "0.5.4-pre",
"description": "A desktop app that gives you access to the Qortal network",
"author": {
"name": "",

View File

@ -34,7 +34,7 @@ import LanguageSelector from '../components/Language/LanguageSelector';
import { MyContext } from '../App';
const manifestData = {
version: '0.5.3',
version: '0.5.4',
};
export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (

View File

@ -21,7 +21,7 @@ const defaultTheme = {
};
const ThemeContext = createContext({
themeMode: 'light',
themeMode: 'dark',
toggleTheme: () => {},
userThemes: [defaultTheme],
addUserTheme: (themes) => {},
@ -30,7 +30,7 @@ const ThemeContext = createContext({
});
export const ThemeProvider = ({ children }) => {
const [themeMode, setThemeMode] = useState('light');
const [themeMode, setThemeMode] = useState('dark');
const [userThemes, setUserThemes] = useState([defaultTheme]);
const [currentThemeId, setCurrentThemeId] = useState('default');

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { DrawerUserLookup } from '../Drawer/DrawerUserLookup';
import {
Avatar,
@ -17,6 +17,7 @@ import {
Table,
CircularProgress,
useTheme,
Autocomplete,
} from '@mui/material';
import { getAddressInfo, getNameOrAddress } from '../../background';
import { getBaseApiReact } from '../../App';
@ -31,6 +32,7 @@ import {
subscribeToEvent,
unsubscribeFromEvent,
} from '../../utils/events';
import { useNameSearch } from '../../hooks/useNameSearch';
function formatAddress(str) {
if (str.length <= 12) return str;
@ -44,6 +46,9 @@ function formatAddress(str) {
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
const theme = useTheme();
const [nameOrAddress, setNameOrAddress] = useState('');
const [inputValue, setInputValue] = useState('');
const { results, isLoading } = useNameSearch(inputValue);
const options = useMemo(() => results?.map((item) => item.name), [results]);
const [errorMessage, setErrorMessage] = useState('');
const [addressInfo, setAddressInfo] = useState(null);
const [isLoadingUser, setIsLoadingUser] = useState(false);
@ -120,6 +125,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
const onClose = () => {
setIsOpenDrawerLookup(false);
setNameOrAddress('');
setInputValue('');
setErrorMessage('');
setPayments([]);
setIsLoadingUser(false);
@ -146,31 +152,39 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
gap: '5px',
}}
>
<Autocomplete
value={nameOrAddress}
onChange={(event: any, newValue: string | null) => {
if (!newValue) {
setNameOrAddress('');
return;
}
setNameOrAddress(newValue);
lookupFunc(newValue);
}}
inputValue={inputValue}
onInputChange={(event, newInputValue) => {
setInputValue(newInputValue);
}}
id="controllable-states-demo"
loading={isLoading}
options={options}
sx={{ width: 300 }}
renderInput={(params) => (
<TextField
autoFocus
value={nameOrAddress}
onChange={(e) => setNameOrAddress(e.target.value)}
size="small"
placeholder="Address or Name"
autoComplete="off"
{...params}
label="Address or Name"
onKeyDown={(e) => {
if (e.key === 'Enter' && nameOrAddress) {
lookupFunc();
lookupFunc(inputValue);
}
}}
/>
<ButtonBase
onClick={() => {
lookupFunc();
}}
>
<SearchIcon
sx={{
color: theme.palette.text.primary,
marginRight: '20px',
}}
)}
/>
</ButtonBase>
<ButtonBase
sx={{
marginLeft: 'auto',

View File

@ -0,0 +1,55 @@
import { useCallback, useEffect, useState } from 'react';
import { getBaseApiReact } from '../App';
interface NameListItem {
name: string;
address: string;
}
export const useNameSearch = (value: string, limit = 20) => {
const [nameList, setNameList] = useState<NameListItem[]>([]);
const [isLoading, setIsLoading] = useState(false);
const checkIfNameExisits = useCallback(
async (name: string, listLimit: number) => {
try {
if (!name) {
setNameList([]);
return;
}
const res = await fetch(
`${getBaseApiReact()}/names/search?query=${name}&prefix=true&limit=${listLimit}`
);
const data = await res.json();
setNameList(
data?.map((item: any) => {
return {
name: item.name,
address: item.owner,
};
})
);
} catch (error) {
console.error(error);
} finally {
setIsLoading(false);
}
},
[]
);
// Debounce logic
useEffect(() => {
setIsLoading(true);
const handler = setTimeout(() => {
checkIfNameExisits(value, limit);
}, 500);
// Cleanup timeout if searchValue changes before the timeout completes
return () => {
clearTimeout(handler);
};
}, [value, limit, checkIfNameExisits]);
return {
isLoading,
results: nameList,
};
};

View File

@ -42,31 +42,6 @@
opacity: 0.6;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-track:hover {
background-color: transparent;
}
::-webkit-scrollbar {
width: 16px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #444444;
border-radius: 8px;
background-clip: content-box;
border: 4px solid transparent;
transition: 0.3s background-color;
}
::-webkit-scrollbar-thumb:hover {
background-color: #363636;
}
@property --var1 {
syntax: '<color>';
inherits: true;
@ -78,7 +53,7 @@
}
.scrollable-container:hover {
--var1: #444444;
--var1: var(--primary-main);
}
.scrollable-container::-webkit-scrollbar-thumb {

View File

@ -32,6 +32,7 @@ export const darkThemeOptions: ThemeOptions = {
unread: 'rgb(66, 151, 226)',
},
},
components: {
MuiCard: {
styleOverrides: {
@ -48,19 +49,12 @@ export const darkThemeOptions: ThemeOptions = {
},
},
MuiCssBaseline: {
styleOverrides: {
styleOverrides: (theme) => ({
':root': {
'--color-instance': 'rgb(30, 30, 32)',
'--color-instance-popover-bg': 'rgb(34, 34, 34)',
'--Mail-Background': 'rgb(43, 43, 43)',
'--new-message-text': 'rgb(0, 0, 0)',
'--bg-primary': 'rgba(31, 32, 35, 1)',
'--bg-2': 'rgb(39, 40, 44)',
'--bg-3': 'rgba(0, 0, 0, 0.1)',
'--unread': 'rgb(66, 151, 226)',
'--danger': 'rgb(177, 70, 70)',
'--apps-circle': 'rgb(31, 32, 35)',
'--green': 'rgb(94, 176, 73)',
'--primary-main': theme.palette.primary.main,
},
'*, *::before, *::after': {
boxSizing: 'border-box',
@ -74,9 +68,32 @@ export const darkThemeOptions: ThemeOptions = {
margin: 0,
wordBreak: 'break-word',
backgroundColor: 'var(--bg-primary)',
color: 'var(--new-message-text)',
},
'::-webkit-scrollbar-track': {
backgroundColor: 'transparent',
},
'::-webkit-scrollbar-track:hover': {
backgroundColor: 'transparent',
},
'::-webkit-scrollbar': {
width: '16px',
height: '10px',
},
'::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main,
borderRadius: '8px',
backgroundClip: 'content-box',
border: '4px solid transparent',
transition: '0.3s background-color',
},
'::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.light,
},
}),
},
MuiIcon: {
defaultProps: {

View File

@ -6,9 +6,9 @@ export const lightThemeOptions: ThemeOptions = {
palette: {
mode: 'light',
primary: {
main: 'rgb(162, 162, 221)', // old light becomes main
main: 'rgb(162, 162, 221)',
dark: 'rgb(113, 198, 212)',
light: 'rgba(244, 244, 251, 1)', // former main becomes light
light: 'rgb(180, 200, 235)',
},
secondary: {
main: 'rgba(194, 222, 236, 1)',
@ -49,36 +49,57 @@ export const lightThemeOptions: ThemeOptions = {
},
},
MuiCssBaseline: {
styleOverrides: {
styleOverrides: (theme) => ({
':root': {
'--color-instance': 'rgba(30, 30, 32, 1)',
'--color-instance-popover-bg': 'rgba(34, 34, 34, 1)',
'--Mail-Background': 'rgba(49, 51, 56, 1)',
'--new-message-text': 'rgba(0, 0, 0, 1)',
'--bg-primary': 'rgba(31, 32, 35, 1)',
'--bg-2': 'rgba(39, 40, 44, 1)',
'--bg-3': 'rgba(0, 0, 0, 0.1)',
'--unread': 'rgba(66, 151, 226, 1)',
'--danger': 'rgba(177, 70, 70, 1)',
'--apps-circle': 'rgba(31, 32, 35, 1)',
'--green': 'rgba(94, 176, 73, 1)',
'--primary-main': theme.palette.primary.main,
},
'*, *::before, *::after': {
boxSizing: 'border-box',
},
html: {
padding: 0,
margin: 0,
},
body: {
padding: 0,
margin: 0,
wordBreak: 'break-word',
backgroundColor: 'var(--bg-primary)',
color: 'var(--new-message-text)',
},
'::-webkit-scrollbar-track': {
backgroundColor: 'transparent',
},
'::-webkit-scrollbar-track:hover': {
backgroundColor: 'transparent',
},
'::-webkit-scrollbar': {
width: '16px',
height: '10px',
},
'::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main,
borderRadius: '8px',
backgroundClip: 'content-box',
border: '4px solid transparent',
transition: '0.3s background-color',
},
'::-webkit-scrollbar-thumb:hover': {
backgroundColor: theme.palette.primary.light,
},
}),
},
MuiIcon: {
defaultProps: {
style: {