mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Apps and tags are suggested in global search.
This commit is contained in:
parent
d042b06e8b
commit
fdfb8797fe
7 changed files with 68 additions and 15 deletions
|
|
@ -158,7 +158,7 @@ export function DashboardPage() {
|
|||
const navigate = useNavigate();
|
||||
|
||||
const {searchStr, setSearchStr, activeTags} = useSearch();
|
||||
const {suggestions} = useSearchSuggestions();
|
||||
const {suggestions, handleResultSelect} = useSearchSuggestions();
|
||||
const {status} = useContext(StatusContext);
|
||||
|
||||
let title = 'Dashboard';
|
||||
|
|
@ -198,6 +198,7 @@ export function DashboardPage() {
|
|||
onClear={() => navigate('/')}
|
||||
style={{marginBottom: '2em'}}
|
||||
results={suggestions}
|
||||
handleResultSelect={handleResultSelect}
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column width={3} textAlign='right'>
|
||||
|
|
@ -220,6 +221,7 @@ export function DashboardPage() {
|
|||
onClear={() => navigate('/')}
|
||||
style={{marginBottom: '2em'}}
|
||||
results={suggestions}
|
||||
handleResultSelect={handleResultSelect}
|
||||
/>
|
||||
</Grid.Column>
|
||||
<Grid.Column textAlign='right' width={2}>
|
||||
|
|
|
|||
|
|
@ -1026,9 +1026,11 @@ export async function searchSuggestions(search_str) {
|
|||
const content = await response.json();
|
||||
const channels = content['channels'];
|
||||
const domains = content['domains'];
|
||||
const tags = content['tags'];
|
||||
return {
|
||||
channels,
|
||||
domains,
|
||||
tags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,10 +413,10 @@ export function SearchInput({
|
|||
onClear = null,
|
||||
onChange = null,
|
||||
results = null,
|
||||
handleResultSelect = null,
|
||||
...props
|
||||
}) {
|
||||
const [value, setValue] = useState(searchStr || '');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (e) => {
|
||||
if (e) {
|
||||
|
|
@ -455,19 +455,13 @@ export function SearchInput({
|
|||
}
|
||||
}
|
||||
|
||||
const localHandleResultSelect = (e, {result}) => {
|
||||
const localHandleResultSelect = (e, data) => {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result['type'] === 'channel') {
|
||||
navigate(`/videos/channel/${result['id']}/video`);
|
||||
} else if (result['type'] === 'domain') {
|
||||
navigate(`/archive?domain=${result['domain']}`);
|
||||
console.debug(`Selected result`, data);
|
||||
if (handleResultSelect) {
|
||||
handleResultSelect(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,19 @@ import {searchEstimate, searchSuggestions} from "../api";
|
|||
import {normalizeEstimate, TabLinks} from "./Common";
|
||||
import _ from "lodash";
|
||||
|
||||
const suggestedApps = [
|
||||
{location: '/more/otp', title: 'One Time Pad', description: 'Encrypt and Decrypt messages'},
|
||||
{location: '/inventory', title: 'Inventory', description: 'Track and organize your food storage'},
|
||||
{location: '/more/vin', title: 'Vin Decoder', description: 'Decode and analyze vehicle VIN numbers'},
|
||||
{location: '/admin', title: 'Downloads', description: 'View your downloads'},
|
||||
{location: '/admin/settings', title: 'Settings', description: 'View and modify your settings'},
|
||||
{location: '/admin/wrol', title: 'WROL Mode', description: 'Enable or disable WROL Mode'},
|
||||
];
|
||||
|
||||
export function useSearchSuggestions() {
|
||||
const navigate = useNavigate();
|
||||
const {searchParams} = useQuery();
|
||||
|
||||
const searchStr = searchParams.get('q');
|
||||
|
||||
const [suggestions, setSuggestions] = useState(null);
|
||||
|
|
@ -17,6 +28,7 @@ export function useSearchSuggestions() {
|
|||
if (searchStr && searchStr.length > 0) {
|
||||
try {
|
||||
const i = await searchSuggestions(searchStr);
|
||||
const matchingApps = suggestedApps.filter(i => i.title.toLowerCase().includes(searchStr));
|
||||
const newSuggestions = {
|
||||
channels: {
|
||||
name: 'Channels', results: i['channels'].map(i => {
|
||||
|
|
@ -28,6 +40,12 @@ export function useSearchSuggestions() {
|
|||
return {title: i['domain'], id: i['id'], type: 'domain'}
|
||||
})
|
||||
},
|
||||
tags: {
|
||||
name: 'Tags', results: i['tags'].map(i => {
|
||||
return {title: i['name'], type: 'tag'}
|
||||
})
|
||||
},
|
||||
apps: {name: 'Apps', results: matchingApps},
|
||||
};
|
||||
console.debug('newSuggestions', newSuggestions);
|
||||
setSuggestions(newSuggestions);
|
||||
|
|
@ -44,7 +62,24 @@ export function useSearchSuggestions() {
|
|||
fetchSuggestions();
|
||||
}, [searchStr]);
|
||||
|
||||
return {suggestions, searchStr}
|
||||
|
||||
const handleResultSelect = ({result}) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.location) {
|
||||
navigate(result.location);
|
||||
} else if (result['type'] === 'channel') {
|
||||
navigate(`/videos/channel/${result['id']}/video`);
|
||||
} else if (result['type'] === 'domain') {
|
||||
navigate(`/archive?domain=${result['domain']}`);
|
||||
} else if (result['type'] === 'tag') {
|
||||
navigate(`/search?tag=${result['title']}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {suggestions, searchStr, handleResultSelect}
|
||||
}
|
||||
|
||||
export const useSearch = (defaultLimit = 48, totalPages = 0, emptySearch = false, model) => {
|
||||
|
|
|
|||
|
|
@ -553,9 +553,11 @@ async def post_search_suggestions(_: Request, body: schema.SearchSuggestionsRequ
|
|||
|
||||
channels = sorted(search_channels_by_name(body.search_str), key=lambda i: i.name)
|
||||
domains = sorted(search_domains_by_name(body.search_str), key=lambda i: i.domain)
|
||||
tags_ = tags.search_tags_by_name(body.search_str)
|
||||
ret = dict(
|
||||
channels=channels,
|
||||
domains=domains,
|
||||
tags=tags_,
|
||||
)
|
||||
return json_response(ret)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import pathlib
|
|||
from datetime import datetime
|
||||
from typing import List, Dict, Tuple
|
||||
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, BigInteger
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, BigInteger, asc
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import relationship, Session
|
||||
|
||||
|
|
@ -461,3 +461,13 @@ def tag_names_to_zim_sub_select(tag_names: List[str], zim_id: int = None) -> Tup
|
|||
HAVING array_agg(t.name)::TEXT[] @> %(tag_names)s::TEXT[]
|
||||
'''
|
||||
return stmt, params
|
||||
|
||||
|
||||
@optional_session
|
||||
def search_tags_by_name(name: str, limit: int = 5, session: Session = None) -> List[Tag]:
|
||||
tags = session.query(Tag) \
|
||||
.filter(Tag.name.ilike(f'%{name}%')) \
|
||||
.order_by(asc(Tag.name)) \
|
||||
.limit(limit) \
|
||||
.all()
|
||||
return tags
|
||||
|
|
|
|||
|
|
@ -488,12 +488,13 @@ def test_search_str_estimate(test_session, test_client, test_directory, test_zim
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_suggestions(test_session, test_async_client, channel_factory, archive_factory):
|
||||
async def test_search_suggestions(test_session, test_async_client, channel_factory, archive_factory, tag_factory):
|
||||
channel_factory(name='Foo')
|
||||
channel_factory(name='Fool')
|
||||
channel_factory(name='Bar')
|
||||
archive_factory(domain='foo.com')
|
||||
archive_factory(domain='bar.com')
|
||||
tag_factory()
|
||||
test_session.commit()
|
||||
|
||||
body = dict(search_str='foo')
|
||||
|
|
@ -512,6 +513,13 @@ async def test_search_suggestions(test_session, test_async_client, channel_facto
|
|||
assert response.json['channels'] == [{'directory': 'Bar', 'id': 3, 'name': 'Bar', 'url': 'https://example.com/Bar'}]
|
||||
assert response.json['domains'] == [{'directory': 'archive/bar.com', 'domain': 'bar.com', 'id': 2}]
|
||||
|
||||
body = dict(search_str='one')
|
||||
request, response = await test_async_client.post('/api/search_suggestions', json=body)
|
||||
assert response.status_code == HTTPStatus.OK
|
||||
assert response.json['channels'] == []
|
||||
assert response.json['domains'] == []
|
||||
assert response.json['tags'] == [{'color': '#111111', 'id': 1, 'name': 'one'}]
|
||||
|
||||
|
||||
def test_recursive_errors():
|
||||
"""Errors are reported recursively."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue