debounce search OK
All checks were successful
repod / nextcloud (push) Successful in 59s
repod / nodejs (push) Successful in 1m13s

This commit is contained in:
Michel Roux 2023-07-26 01:26:46 +02:00
parent 0f1ea1f5d0
commit 431cbf0c42
7 changed files with 147 additions and 8 deletions

View File

@ -13,6 +13,7 @@ declare(strict_types=1);
return [ return [
'routes' => [ 'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'top#index', 'url' => '/top/{count}', 'verb' => 'GET'] ['name' => 'top#index', 'url' => '/top/{count}', 'verb' => 'GET'],
['name' => 'search#index', 'url' => '/search/{value}', 'verb' => 'GET']
] ]
]; ];

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace OCA\RePod\Controller;
use Exception;
use OCA\RePod\AppInfo\Application;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Http\Client\IClientService;
use OCP\IRequest;
class SearchController extends Controller
{
public function __construct(
IRequest $request,
private IClientService $clientService
) {
parent::__construct(Application::APP_ID, $request);
}
public function index(string $value): JSONResponse {
$podcasts = [];
try {
$fyydClient = $this->clientService->newClient();
$fyydReponse = $fyydClient->get("https://api.fyyd.de/0.2/search/podcast", [
'query' => [
'title' => $value,
'term' => $value
]
]);
$fyydJson = (array) json_decode((string) $fyydReponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $fyydJson) && is_array($fyydJson['data'])) {
/** @var string[] $fyydFeed */
foreach ($fyydJson['data'] as $fyydFeed) {
$podcasts[] = [
'id' => $fyydFeed['id'],
'title' => $fyydFeed['title'],
'description' => $fyydFeed['description'],
'image' => $fyydFeed['imgURL'],
'url' => $fyydFeed['xmlURL'],
'lang' => $fyydFeed['language'],
'lastpub' => $fyydFeed['lastpub']
];
}
}
} catch (Exception $e) {
}
return new JSONResponse($podcasts);
}
}

View File

@ -30,24 +30,29 @@ class TopController extends Controller
* @NoCSRFRequired * @NoCSRFRequired
*/ */
public function index(int $count = 10): JSONResponse { public function index(int $count = 10): JSONResponse {
$userLang = 'en'; $language = 'en';
try { try {
$langClient = $this->clientService->newClient(); $langClient = $this->clientService->newClient();
$langResponse = $langClient->get("https://api.fyyd.de/0.2/feature/podcast/hot/languages"); $langResponse = $langClient->get("https://api.fyyd.de/0.2/feature/podcast/hot/languages");
$langJson = (array) json_decode((string) $langResponse->getBody(), true, flags: JSON_THROW_ON_ERROR); $langJson = (array) json_decode((string) $langResponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
if (array_key_exists('data', $langJson) && is_array($langJson['data'])) { if (array_key_exists('data', $langJson) && is_array($langJson['data'])) {
$userLang = $this->l10n->getUserLanguage($this->userSession->getUser()); $language = $this->l10n->getUserLanguage($this->userSession->getUser());
$userLang = explode('_', $userLang); $language = explode('_', $language);
$userLang = count($userLang) > 1 ? $userLang[1] : $userLang[0]; $language = count($language) > 1 ? $language[1] : $language[0];
$userLang = in_array($userLang, $langJson['data']) ? $userLang : 'en'; $language = in_array($language, $langJson['data']) ? $language : 'en';
} }
} catch (Exception $e) { } catch (Exception $e) {
} }
try { try {
$podcastClient = $this->clientService->newClient(); $podcastClient = $this->clientService->newClient();
$podcastReponse = $podcastClient->get("https://api.fyyd.de/0.2/feature/podcast/hot?count={$count}&language={$userLang}"); $podcastReponse = $podcastClient->get("https://api.fyyd.de/0.2/feature/podcast/hot", [
'query' => [
'count' => $count,
'language' => $language
]
]);
$podcastJson = (array) json_decode((string) $podcastReponse->getBody(), true, flags: JSON_THROW_ON_ERROR); $podcastJson = (array) json_decode((string) $podcastReponse->getBody(), true, flags: JSON_THROW_ON_ERROR);
return new JSONResponse($podcastJson, $podcastReponse->getStatusCode()); return new JSONResponse($podcastJson, $podcastReponse->getStatusCode());
} catch (Exception $e) { } catch (Exception $e) {

52
src/components/Search.vue Normal file
View File

@ -0,0 +1,52 @@
<template>
<ul>
<NcListItem v-for="feed in feeds" :key="feed.id" />
</ul>
</template>
<script>
import { NcListItem } from '@nextcloud/vue'
import axios from '@nextcloud/axios'
import { debounce } from '../utils/debounce.js'
import { generateUrl } from '@nextcloud/router'
import { showError } from '@nextcloud/dialogs'
export default {
name: 'Search',
components: {
NcListItem,
},
props: {
value: {
type: String,
required: true,
},
},
data() {
return {
loading: true,
feeds: [],
}
},
watch: {
value() {
this.search()
},
},
methods: {
search: debounce(async function value() {
this.loading = true
try {
const feeds = await axios.get(generateUrl('/apps/repod/search/{value}', { value: this.value }))
this.feeds = feeds.data
} catch (e) {
console.error(e)
showError(t('Could not fetch search results'))
}
this.loading = false
}, 200),
},
}
</script>

View File

@ -4,7 +4,7 @@
<span>{{ t('Discover') }}</span> <span>{{ t('Discover') }}</span>
<NcSelect v-model="count" <NcSelect v-model="count"
class="select" class="select"
:components="{Deselect: {}}" :components="Deselect"
:options="[...Array(10).keys()].map(x=>(x+1)*10)" :options="[...Array(10).keys()].map(x=>(x+1)*10)"
@input="fetch" /> @input="fetch" />
</p> </p>
@ -39,6 +39,9 @@ export default {
tops: [], tops: [],
loading: true, loading: true,
count: 10, count: 10,
Deselect: {
render: null,
},
} }
}, },
async mounted() { async mounted() {

20
src/utils/debounce.js Normal file
View File

@ -0,0 +1,20 @@
/**
* Returns a function, that, as long as it continues to be invoked, will not
* be triggered. The function will be called after it stops being called for
* N milliseconds.
* https://stackoverflow.com/a/53486112
*
* @param {Function} fn function to wrap
* @param {number} delay timeout in ms
*/
export function debounce(fn, delay) {
let timeoutID = null
return function() {
clearTimeout(timeoutID)
const args = arguments
const that = this
timeoutID = setTimeout(function() {
fn.apply(that, args)
}, delay)
}
}

View File

@ -5,6 +5,7 @@
<Magnify :size="20" /> <Magnify :size="20" />
</NcTextField> </NcTextField>
</p> </p>
<Search v-if="search" :value="search" />
<TopList v-if="!search" /> <TopList v-if="!search" />
<AddRss v-if="!search" /> <AddRss v-if="!search" />
</div> </div>
@ -14,6 +15,7 @@
import AddRss from '../components/AddRss.vue' import AddRss from '../components/AddRss.vue'
import Magnify from 'vue-material-design-icons/Magnify.vue' import Magnify from 'vue-material-design-icons/Magnify.vue'
import { NcTextField } from '@nextcloud/vue' import { NcTextField } from '@nextcloud/vue'
import Search from '../components/Search.vue'
import TopList from '../components/TopList.vue' import TopList from '../components/TopList.vue'
export default { export default {
@ -22,6 +24,7 @@ export default {
AddRss, AddRss,
Magnify, Magnify,
NcTextField, NcTextField,
Search,
TopList, TopList,
}, },
data() { data() {