Add live contact search to invitee input
The static datalist only carried the first 50 address book contacts, so typed fragments could not match anything beyond that. A new read-only GET /contacts/search endpoint queries the full address book per keystroke, and a small vanilla JS combobox shows the results and replaces only the fragment after the last separator, so comma-separated lists keep working. The server-rendered datalist remains as the no-JS fallback; the JS removes the list attribute at runtime so the native popup does not double the custom one.
This commit is contained in:
1 parent
0e5b190971
commit
cc1fd4e508
7 files changed
+352
-19
No files matched your search
@@ -11,6 +11,7 @@ use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\Calendar\ICreateFromString;
|
||||
use OCP\Calendar\IManager;
|
||||
@@ -292,6 +293,19 @@ class CalendarController extends Controller
|
||||
return new RedirectResponse($calendarUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Live contact search for the invitee input.
|
||||
*
|
||||
* Read-only GET endpoint, needs no CSRF protection.
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<array{email: string, name: string}>>
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
public function searchContacts(string $query = ''): DataResponse
|
||||
{
|
||||
return new DataResponse($this->contactSuggestions($query));
|
||||
}
|
||||
|
||||
/**
|
||||
* All writable calendars of the given principal.
|
||||
*
|
||||
@@ -402,10 +416,11 @@ class CalendarController extends Controller
|
||||
array $contacts = []
|
||||
): TemplateResponse {
|
||||
/*
|
||||
* Stylesheets are not auto-loaded, css/style.css
|
||||
* must be registered for the page layout.
|
||||
* Stylesheets and scripts are not auto-loaded, they must
|
||||
* be registered for the page layout.
|
||||
*/
|
||||
Util::addStyle(Application::APP_ID, 'style');
|
||||
Util::addScript(Application::APP_ID, 'invitees');
|
||||
|
||||
return new TemplateResponse(
|
||||
Application::APP_ID,
|
||||
@@ -432,6 +447,9 @@ class CalendarController extends Controller
|
||||
'calendarId' => $calendarId,
|
||||
'attendees' => $attendees,
|
||||
'contacts' => $contacts,
|
||||
'contactsSearchUrl' => $this->urlGenerator->linkToRoute(
|
||||
Application::APP_ID . '.calendar.searchContacts'
|
||||
),
|
||||
'cancelUrl' => $this->urlGenerator->getAbsoluteURL('/'),
|
||||
'createUrl' => $this->urlGenerator->linkToRoute(
|
||||
Application::APP_ID . '.calendar.create'
|
||||
@@ -485,16 +503,17 @@ class CalendarController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Address book entries with an email address, offered as
|
||||
* autocomplete options by the invite input.
|
||||
* Address book entries with an email address, offered by the
|
||||
* invitee input: as autocomplete options on the initial page
|
||||
* and as live search results while typing.
|
||||
*
|
||||
* @return list<array{email: string, name: string}>
|
||||
*/
|
||||
private function contactSuggestions(): array
|
||||
private function contactSuggestions(string $query = ''): array
|
||||
{
|
||||
$contacts = [];
|
||||
|
||||
$results = $this->contactsManager->search('', ['FN', 'EMAIL']);
|
||||
$results = $this->contactsManager->search(trim($query), ['FN', 'EMAIL']);
|
||||
|
||||
foreach ($results ?? [] as $result) {
|
||||
$name = (string)($result['FN'] ?? '');
|
||||
|
||||
Reference in new issue
Block a user