mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-05-15 22:26:58 +00:00
added autocomplete for user lookup. scrollbar theme
This commit is contained in:
parent
4d888e832f
commit
6b2d92d73d
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "qortal-hub",
|
"name": "qortal-hub",
|
||||||
"version": "0.5.3",
|
"version": "0.5.4-pre",
|
||||||
"description": "A desktop app that gives you access to the Qortal network",
|
"description": "A desktop app that gives you access to the Qortal network",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "",
|
"name": "",
|
||||||
@ -57,4 +57,4 @@
|
|||||||
"capacitor",
|
"capacitor",
|
||||||
"electron"
|
"electron"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import LanguageSelector from '../components/Language/LanguageSelector';
|
|||||||
import { MyContext } from '../App';
|
import { MyContext } from '../App';
|
||||||
|
|
||||||
const manifestData = {
|
const manifestData = {
|
||||||
version: '0.5.3',
|
version: '0.5.4',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
|
export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||||
|
@ -21,7 +21,7 @@ const defaultTheme = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ThemeContext = createContext({
|
const ThemeContext = createContext({
|
||||||
themeMode: 'light',
|
themeMode: 'dark',
|
||||||
toggleTheme: () => {},
|
toggleTheme: () => {},
|
||||||
userThemes: [defaultTheme],
|
userThemes: [defaultTheme],
|
||||||
addUserTheme: (themes) => {},
|
addUserTheme: (themes) => {},
|
||||||
@ -30,7 +30,7 @@ const ThemeContext = createContext({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ThemeProvider = ({ children }) => {
|
export const ThemeProvider = ({ children }) => {
|
||||||
const [themeMode, setThemeMode] = useState('light');
|
const [themeMode, setThemeMode] = useState('dark');
|
||||||
const [userThemes, setUserThemes] = useState([defaultTheme]);
|
const [userThemes, setUserThemes] = useState([defaultTheme]);
|
||||||
const [currentThemeId, setCurrentThemeId] = useState('default');
|
const [currentThemeId, setCurrentThemeId] = useState('default');
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { DrawerUserLookup } from '../Drawer/DrawerUserLookup';
|
import { DrawerUserLookup } from '../Drawer/DrawerUserLookup';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -17,6 +17,7 @@ import {
|
|||||||
Table,
|
Table,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
useTheme,
|
useTheme,
|
||||||
|
Autocomplete,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { getAddressInfo, getNameOrAddress } from '../../background';
|
import { getAddressInfo, getNameOrAddress } from '../../background';
|
||||||
import { getBaseApiReact } from '../../App';
|
import { getBaseApiReact } from '../../App';
|
||||||
@ -31,6 +32,7 @@ import {
|
|||||||
subscribeToEvent,
|
subscribeToEvent,
|
||||||
unsubscribeFromEvent,
|
unsubscribeFromEvent,
|
||||||
} from '../../utils/events';
|
} from '../../utils/events';
|
||||||
|
import { useNameSearch } from '../../hooks/useNameSearch';
|
||||||
|
|
||||||
function formatAddress(str) {
|
function formatAddress(str) {
|
||||||
if (str.length <= 12) return str;
|
if (str.length <= 12) return str;
|
||||||
@ -44,6 +46,9 @@ function formatAddress(str) {
|
|||||||
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [nameOrAddress, setNameOrAddress] = useState('');
|
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 [errorMessage, setErrorMessage] = useState('');
|
||||||
const [addressInfo, setAddressInfo] = useState(null);
|
const [addressInfo, setAddressInfo] = useState(null);
|
||||||
const [isLoadingUser, setIsLoadingUser] = useState(false);
|
const [isLoadingUser, setIsLoadingUser] = useState(false);
|
||||||
@ -120,6 +125,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
|||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
setIsOpenDrawerLookup(false);
|
setIsOpenDrawerLookup(false);
|
||||||
setNameOrAddress('');
|
setNameOrAddress('');
|
||||||
|
setInputValue('');
|
||||||
setErrorMessage('');
|
setErrorMessage('');
|
||||||
setPayments([]);
|
setPayments([]);
|
||||||
setIsLoadingUser(false);
|
setIsLoadingUser(false);
|
||||||
@ -146,31 +152,39 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
|||||||
gap: '5px',
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextField
|
<Autocomplete
|
||||||
autoFocus
|
|
||||||
value={nameOrAddress}
|
value={nameOrAddress}
|
||||||
onChange={(e) => setNameOrAddress(e.target.value)}
|
onChange={(event: any, newValue: string | null) => {
|
||||||
size="small"
|
if (!newValue) {
|
||||||
placeholder="Address or Name"
|
setNameOrAddress('');
|
||||||
autoComplete="off"
|
return;
|
||||||
onKeyDown={(e) => {
|
|
||||||
if (e.key === 'Enter' && nameOrAddress) {
|
|
||||||
lookupFunc();
|
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
autoComplete="off"
|
||||||
|
{...params}
|
||||||
|
label="Address or Name"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' && nameOrAddress) {
|
||||||
|
lookupFunc(inputValue);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<ButtonBase
|
|
||||||
onClick={() => {
|
|
||||||
lookupFunc();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SearchIcon
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
marginRight: '20px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ButtonBase>
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
|
55
src/hooks/useNameSearch.tsx
Normal file
55
src/hooks/useNameSearch.tsx
Normal 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,
|
||||||
|
};
|
||||||
|
};
|
@ -42,31 +42,6 @@
|
|||||||
opacity: 0.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 {
|
@property --var1 {
|
||||||
syntax: '<color>';
|
syntax: '<color>';
|
||||||
inherits: true;
|
inherits: true;
|
||||||
@ -78,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scrollable-container:hover {
|
.scrollable-container:hover {
|
||||||
--var1: #444444;
|
--var1: var(--primary-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scrollable-container::-webkit-scrollbar-thumb {
|
.scrollable-container::-webkit-scrollbar-thumb {
|
||||||
|
@ -32,6 +32,7 @@ export const darkThemeOptions: ThemeOptions = {
|
|||||||
unread: 'rgb(66, 151, 226)',
|
unread: 'rgb(66, 151, 226)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
MuiCard: {
|
MuiCard: {
|
||||||
styleOverrides: {
|
styleOverrides: {
|
||||||
@ -48,19 +49,12 @@ export const darkThemeOptions: ThemeOptions = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiCssBaseline: {
|
MuiCssBaseline: {
|
||||||
styleOverrides: {
|
styleOverrides: (theme) => ({
|
||||||
':root': {
|
':root': {
|
||||||
'--color-instance': 'rgb(30, 30, 32)',
|
|
||||||
'--color-instance-popover-bg': 'rgb(34, 34, 34)',
|
|
||||||
'--Mail-Background': 'rgb(43, 43, 43)',
|
'--Mail-Background': 'rgb(43, 43, 43)',
|
||||||
'--new-message-text': 'rgb(0, 0, 0)',
|
|
||||||
'--bg-primary': 'rgba(31, 32, 35, 1)',
|
'--bg-primary': 'rgba(31, 32, 35, 1)',
|
||||||
'--bg-2': 'rgb(39, 40, 44)',
|
'--bg-2': 'rgb(39, 40, 44)',
|
||||||
'--bg-3': 'rgba(0, 0, 0, 0.1)',
|
'--primary-main': theme.palette.primary.main,
|
||||||
'--unread': 'rgb(66, 151, 226)',
|
|
||||||
'--danger': 'rgb(177, 70, 70)',
|
|
||||||
'--apps-circle': 'rgb(31, 32, 35)',
|
|
||||||
'--green': 'rgb(94, 176, 73)',
|
|
||||||
},
|
},
|
||||||
'*, *::before, *::after': {
|
'*, *::before, *::after': {
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
@ -74,9 +68,32 @@ export const darkThemeOptions: ThemeOptions = {
|
|||||||
margin: 0,
|
margin: 0,
|
||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
backgroundColor: 'var(--bg-primary)',
|
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: {
|
MuiIcon: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
|
@ -6,9 +6,9 @@ export const lightThemeOptions: ThemeOptions = {
|
|||||||
palette: {
|
palette: {
|
||||||
mode: 'light',
|
mode: 'light',
|
||||||
primary: {
|
primary: {
|
||||||
main: 'rgb(162, 162, 221)', // old light becomes main
|
main: 'rgb(162, 162, 221)',
|
||||||
dark: 'rgb(113, 198, 212)',
|
dark: 'rgb(113, 198, 212)',
|
||||||
light: 'rgba(244, 244, 251, 1)', // former main becomes light
|
light: 'rgb(180, 200, 235)',
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: 'rgba(194, 222, 236, 1)',
|
main: 'rgba(194, 222, 236, 1)',
|
||||||
@ -49,36 +49,57 @@ export const lightThemeOptions: ThemeOptions = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
MuiCssBaseline: {
|
MuiCssBaseline: {
|
||||||
styleOverrides: {
|
styleOverrides: (theme) => ({
|
||||||
':root': {
|
':root': {
|
||||||
'--color-instance': 'rgba(30, 30, 32, 1)',
|
|
||||||
'--color-instance-popover-bg': 'rgba(34, 34, 34, 1)',
|
|
||||||
'--Mail-Background': 'rgba(49, 51, 56, 1)',
|
'--Mail-Background': 'rgba(49, 51, 56, 1)',
|
||||||
'--new-message-text': 'rgba(0, 0, 0, 1)',
|
|
||||||
'--bg-primary': 'rgba(31, 32, 35, 1)',
|
'--bg-primary': 'rgba(31, 32, 35, 1)',
|
||||||
'--bg-2': 'rgba(39, 40, 44, 1)',
|
'--bg-2': 'rgba(39, 40, 44, 1)',
|
||||||
'--bg-3': 'rgba(0, 0, 0, 0.1)',
|
'--primary-main': theme.palette.primary.main,
|
||||||
'--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)',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
'*, *::before, *::after': {
|
'*, *::before, *::after': {
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
},
|
},
|
||||||
|
|
||||||
html: {
|
html: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: 0,
|
margin: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
body: {
|
body: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: 0,
|
margin: 0,
|
||||||
wordBreak: 'break-word',
|
wordBreak: 'break-word',
|
||||||
backgroundColor: 'var(--bg-primary)',
|
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: {
|
MuiIcon: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
style: {
|
style: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user