Подсказки в поиске (#901)

This commit is contained in:
lalka2018 2023-06-22 12:39:25 +03:00 committed by GitHub
parent 53fa14d89e
commit 2fed1d9fef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 178 additions and 4 deletions

76
ServiceAPI/Search.php Normal file
View file

@ -0,0 +1,76 @@
<?php declare(strict_types=1);
namespace openvk\ServiceAPI;
use openvk\Web\Models\Entities\{User, Club};
use openvk\Web\Models\Repositories\{Users, Clubs, Videos};
use Chandler\Database\DatabaseConnection;
class Search implements Handler
{
protected $user;
private $users;
private $clubs;
private $videos;
function __construct(?User $user)
{
$this->user = $user;
$this->users = new Users;
$this->clubs = new Clubs;
$this->videos = new Videos;
}
function fastSearch(string $query, string $type = "users", callable $resolve, callable $reject)
{
if($query == "" || strlen($query) < 3)
$reject(12, "No input or input < 3");
$repo;
$sort;
switch($type) {
default:
case "users":
$repo = (new Users);
$sort = "rating DESC";
break;
case "groups":
$repo = (new Clubs);
$sort = "id ASC";
break;
case "videos":
$repo = (new Videos);
$sort = "created ASC";
break;
}
$res = $repo->find($query, ["doNotSearchMe" => $this->user->getId()], $sort);
$results = array_slice(iterator_to_array($res), 0, 5);
$count = sizeof($results);
$arr = [
"count" => $count,
"items" => []
];
if(sizeof($results) < 1) {
$reject(2, "No results");
}
foreach($results as $res) {
$arr["items"][] = [
"id" => $res->getId(),
"name" => $type == "users" ? $res->getCanonicalName() : $res->getName(),
"avatar" => $type != "videos" ? $res->getAvatarUrl() : $res->getThumbnailURL(),
"url" => $type != "videos" ? $res->getUrl() : "/video".$res->getPrettyId(),
"description" => ovk_proc_strtr($res->getDescription() ?? "...", 40)
];
}
$resolve($arr);
}
}

View file

@ -58,7 +58,7 @@ class Users
$nnparamsCount = 0; $nnparamsCount = 0;
foreach($pars as $paramName => $paramValue) foreach($pars as $paramName => $paramValue)
if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews") if($paramName != "before" && $paramName != "after" && $paramName != "gender" && $paramName != "maritalstatus" && $paramName != "politViews" && $paramName != "doNotSearchMe")
$paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL; $paramValue != NULL ? $notNullParams += ["$paramName" => "%$paramValue%"] : NULL;
else else
$paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL; $paramValue != NULL ? $notNullParams += ["$paramName" => "$paramValue"] : NULL;
@ -125,6 +125,9 @@ class Users
case "gender": case "gender":
$result->where("sex ?", $paramValue); $result->where("sex ?", $paramValue);
break; break;
case "doNotSearchMe":
$result->where("id !=", $paramValue);
break;
} }
} }
} }

View file

@ -93,8 +93,8 @@
{if !$atSearch} {if !$atSearch}
<form action="/search" method="get" id="searcher" style="position:relative;"> <form action="/search" method="get" id="searcher" style="position:relative;">
<input id="searchInput" onfocus="expandSearch()" onblur="decreaseSearch()" class="sr" type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" /> <input autocomplete="off" id="searchInput" oninput="checkSearchTips()" onfocus="expandSearch()" onblur="decreaseSearch()" class="sr" type="search" name="query" placeholder="{_header_search}" style="height: 20px;background: url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px; background-color: #fff; padding-left: 18px;width: 120px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />
<select name="type" class="whatFind" style="display:none;top: 0px;"> <select onchange="checkSearchTips()" id="typer" name="type" class="whatFind" style="display:none;top: 0px;">
<option value="users">{_s_by_people}</option> <option value="users">{_s_by_people}</option>
<option value="groups">{_s_by_groups}</option> <option value="groups">{_s_by_groups}</option>
<option value="posts">{_s_by_posts}</option> <option value="posts">{_s_by_posts}</option>
@ -103,6 +103,12 @@
<option value="apps">{_s_by_apps}</option> <option value="apps">{_s_by_apps}</option>
</select> </select>
</form> </form>
<div class="searchTips" id="srcht" hidden>
<table style="border:none;border-spacing: 0;">
<tbody id="srchrr">
</tbody>
</table>
</div>
{else} {else}
<form action="/search" method="get" id="searcher" style="margin-top: -1px;position:relative;"> <form action="/search" method="get" id="searcher" style="margin-top: -1px;position:relative;">
<input id="searchInput" value="{$_GET['query'] ?? ''}" type="search" class="sr" name="query" placeholder="{_header_search}" style="height: 20px; background-color: #fff; padding-left: 6px;width: 555px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" /> <input id="searchInput" value="{$_GET['query'] ?? ''}" type="search" class="sr" name="query" placeholder="{_header_search}" style="height: 20px; background-color: #fff; padding-left: 6px;width: 555px;" title="{_header_search} [Alt+Shift+F]" accesskey="f" />

View file

@ -2487,3 +2487,49 @@ a.poll-retract-vote {
.page_content.overscrolled div[class$="_big_block"] { .page_content.overscrolled div[class$="_big_block"] {
width: 100%; width: 100%;
} }
.searchTips
{
position: absolute;
background: white;
padding-left: 6px;
padding-right: 6px;
padding-bottom: 6px;
border: 1px solid #C0CAD5;
border-top: 0px;
width: fit-content;
z-index: 666666;
margin-top: -2px;
}
.searchTips td
{
color: black;
padding: 3px;
font-weight: lighter;
position:relative;
}
.searchTips td .desq
{
margin-top: -9px;
}
.restip td a:hover
{
color: black;
font-weight: lighter;
text-decoration: none;
}
.searchTips .restip
{
padding-right: 10px;
cursor: pointer;
user-select: none;
}
.searchTips .restip:hover
{
background: rgb(236, 235, 235);
}

View file

@ -590,9 +590,11 @@ async function decreaseSearch()
{ {
// чтобы люди успели выбрать что искать и поиск не скрывался сразу // чтобы люди успели выбрать что искать и поиск не скрывался сразу
await new Promise(r => setTimeout(r, 4000)); await new Promise(r => setTimeout(r, 4000));
// console.log("search decreased") // console.log("search decreased")
if(document.activeElement !== searchInput) if(document.activeElement !== searchInput && document.activeElement !== typer)
{ {
srcht.setAttribute("hidden", "hidden")
document.getElementById("searchInput").style.background = "url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px"; document.getElementById("searchInput").style.background = "url('/assets/packages/static/openvk/img/search_icon.png') no-repeat 3px 4px";
document.getElementById("searchInput").style.backgroundColor = "#fff"; document.getElementById("searchInput").style.backgroundColor = "#fff";
document.getElementById("searchInput").style.paddingLeft = "18px"; document.getElementById("searchInput").style.paddingLeft = "18px";
@ -642,6 +644,43 @@ function resetSearch()
} }
} }
async function checkSearchTips()
{
let query = searchInput.value;
await new Promise(r => setTimeout(r, 1000));
let type = typer.value;
let smt = type == "users" || type == "groups" || type == "videos";
if(query.length > 3 && query == searchInput.value && smt) {
srcht.removeAttribute("hidden")
let etype = type
try {
let results = await API.Search.fastSearch(escapeHtml(query), etype)
srchrr.innerHTML = ""
for(const el of results["items"]) {
srchrr.insertAdjacentHTML("beforeend", `
<tr class="restip" onmouseup="if (event.which === 2) { window.open('${el.url}', '_blank'); } else {location.href='${el.url}'}">
<td>
<img src="${el.avatar}" width="30">
</td>
<td valign="top">
<p class="nameq" style="margin-top: -2px;text-transform:none;">${escapeHtml(el.name)}</p>
<p class="desq" style="text-transform:none;">${escapeHtml(el.description)}</p>
</td>
</tr>
`)
}
} catch(rejection) {
srchrr.innerHTML = tr("no_results")
}
}
}
$(document).on("scroll", () => { $(document).on("scroll", () => {
if($(document).scrollTop() > $(".sidebar").height() + 50) { if($(document).scrollTop() > $(".sidebar").height() + 50) {
$(".floating_sidebar")[0].classList.add("show"); $(".floating_sidebar")[0].classList.add("show");

View file

@ -1507,6 +1507,8 @@
"closed_group_post" = "This is a post from private group"; "closed_group_post" = "This is a post from private group";
"deleted_target_comment" = "This comment belongs to deleted post"; "deleted_target_comment" = "This comment belongs to deleted post";
"no_results" = "No results";
/* Mobile */ /* Mobile */
"mobile_friends" = "Friends"; "mobile_friends" = "Friends";
"mobile_photos" = "Photos"; "mobile_photos" = "Photos";

View file

@ -1400,6 +1400,8 @@
"closed_group_post" = "Эта запись из закрытой группы"; "closed_group_post" = "Эта запись из закрытой группы";
"deleted_target_comment" = "Этот комментарий принадлежит к удалённой записи"; "deleted_target_comment" = "Этот комментарий принадлежит к удалённой записи";
"no_results" = "Результатов нет";
/* Mobile */ /* Mobile */
"mobile_friends" = "Друзья"; "mobile_friends" = "Друзья";
"mobile_photos" = "Фотографии"; "mobile_photos" = "Фотографии";