Skip to content

Commit c0db573

Browse files
committed
Implement a channel language filter.
1 parent 7a44415 commit c0db573

File tree

2 files changed

+103
-9
lines changed
  • kolibri/plugins/learn/assets/src/views/LibraryPage
  • packages/kolibri/utils

2 files changed

+103
-9
lines changed

kolibri/plugins/learn/assets/src/views/LibraryPage/index.vue

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,22 @@
4949
v-else-if="!displayingSearchResults && !rootNodesLoading"
5050
data-test="channels"
5151
>
52-
<h1 class="channels-label">
53-
{{ channelsLabel }}
54-
</h1>
52+
<KFixedGrid numCols="4">
53+
<KFixedGridItem span="3">
54+
<h1 class="channels-label">
55+
{{ channelsLabel }}
56+
</h1>
57+
</KFixedGridItem>
58+
<KFixedGridItem span="1">
59+
<KSelect
60+
v-model="currentChannelLanguage"
61+
data-test="channel-language-select"
62+
:label="'Show channels for language:'"
63+
:options="languageFilterOptions"
64+
clearable
65+
/>
66+
</KFixedGridItem>
67+
</KFixedGrid>
5568
<p
5669
v-if="isLocalLibraryEmpty"
5770
data-test="nothing-in-lib-label"
@@ -175,12 +188,16 @@
175188

176189
<script>
177190
191+
import isEmpty from 'lodash/isEmpty';
192+
import sortBy from 'lodash/sortBy';
193+
import uniqBy from 'lodash/uniqBy';
178194
import { get, set } from '@vueuse/core';
179195
180-
import { onMounted, getCurrentInstance, ref, watch } from '@vue/composition-api';
196+
import { computed, onMounted, getCurrentInstance, ref, watch } from '@vue/composition-api';
181197
import commonCoreStrings from 'kolibri/uiText/commonCoreStrings';
182198
import useKResponsiveWindow from 'kolibri-design-system/lib/composables/useKResponsiveWindow';
183199
import useUser from 'kolibri/composables/useUser';
200+
import { getContentLangActive } from 'kolibri/utils/i18n';
184201
import samePageCheckGenerator from 'kolibri-common/utils/samePageCheckGenerator';
185202
import ContentNodeResource from 'kolibri-common/apiResources/ContentNodeResource';
186203
import { mapState } from 'vuex';
@@ -286,9 +303,11 @@
286303
}
287304
});
288305
289-
const rootNodes = ref([]);
306+
const _rootNodes = ref([]);
290307
const rootNodesLoading = ref(false);
291308
309+
const routeChannelLanguage = computed(() => currentRoute().query.channelLanguage);
310+
292311
function _showChannels(channels, baseurl) {
293312
if (get(isUserLoggedIn) && !baseurl) {
294313
fetchResumableContentNodes();
@@ -305,7 +324,7 @@
305324
if (shouldResolve()) {
306325
// we want them to be in the same order as the channels list
307326
set(
308-
rootNodes,
327+
_rootNodes,
309328
channels
310329
.map(channel => {
311330
const node = channelCollection.find(n => n.channel_id === channel.id);
@@ -316,6 +335,7 @@
316335
node.title = channel.name || node.title;
317336
node.thumbnail = channel.thumbnail;
318337
node.description = channel.tagline || channel.description;
338+
node.included_languages = channel.included_languages;
319339
return node;
320340
}
321341
})
@@ -326,6 +346,22 @@
326346
store.commit('CORE_SET_ERROR', null);
327347
store.commit('SET_PAGE_NAME', PageNames.LIBRARY);
328348
set(rootNodesLoading, false);
349+
if (!get(routeChannelLanguage)) {
350+
// If we have no currently selected channel language, we should try to select one
351+
// based on the user's preferred language.
352+
const closestMatchChannelLanguage = get(channelLanguages)[0];
353+
const channelLanguage =
354+
closestMatchChannelLanguage &&
355+
getContentLangActive(closestMatchChannelLanguage) > 1
356+
? closestMatchChannelLanguage.id
357+
: _allChannelsFlag;
358+
router.replace({
359+
query: {
360+
...currentRoute().query,
361+
channelLanguage,
362+
},
363+
});
364+
}
329365
}
330366
},
331367
error => {
@@ -387,11 +423,59 @@
387423
watch(() => props.deviceId, showLibrary);
388424
389425
watch(displayingSearchResults, () => {
390-
if (!displayingSearchResults.value && !rootNodes.value.length) {
426+
if (!displayingSearchResults.value && !_rootNodes.value.length) {
391427
showLibrary();
392428
}
393429
});
394430
431+
const channelLanguages = computed(() => {
432+
return sortBy(
433+
uniqBy(
434+
get(_rootNodes).map(node => node.lang),
435+
'id',
436+
),
437+
l => -getContentLangActive(l),
438+
);
439+
});
440+
441+
const languageFilterOptions = computed(() => {
442+
return get(channelLanguages).map(lang => ({
443+
label: lang.lang_name,
444+
value: lang.id,
445+
}));
446+
});
447+
448+
const _allChannelsFlag = 'all';
449+
450+
const currentChannelLanguage = computed({
451+
get() {
452+
if (!get(routeChannelLanguage) || get(routeChannelLanguage) === _allChannelsFlag) {
453+
return {};
454+
}
455+
return get(languageFilterOptions).find(o => o.value === get(routeChannelLanguage)) || {};
456+
},
457+
set(newValue) {
458+
const channelLanguage = newValue?.value || _allChannelsFlag;
459+
router.push({
460+
query: {
461+
...currentRoute().query,
462+
channelLanguage,
463+
},
464+
});
465+
},
466+
});
467+
468+
const rootNodes = computed(() => {
469+
if (isEmpty(get(currentChannelLanguage))) {
470+
return get(_rootNodes);
471+
}
472+
return get(_rootNodes).filter(
473+
node =>
474+
node.lang.id === get(currentChannelLanguage).value ||
475+
node.included_languages.includes(get(currentChannelLanguage).value),
476+
);
477+
});
478+
395479
showLibrary();
396480
397481
return {
@@ -426,6 +510,9 @@
426510
isUserLoggedIn,
427511
canManageContent,
428512
isLearnerOnlyImport,
513+
channelLanguages,
514+
languageFilterOptions,
515+
currentChannelLanguage,
429516
};
430517
},
431518
props: {

packages/kolibri/utils/i18n.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,18 @@ export const getContentLangActive = language => {
3636
const langCode = languageIdToCode(currentLanguage);
3737
const additionalCodes = contentLanguageCodes[langCode] || [];
3838
if (language.id.toLowerCase() === currentLanguage.toLowerCase()) {
39-
// Best possible match, return a 2 to have it still be truthy, but distinguishable
40-
// from a 1 which is a lang_code match
39+
// Best possible match, return a 3 to sort first
40+
// Exact match between content and the language the user is using
41+
return 3;
42+
}
43+
if (language.id === langCode || additionalCodes.includes(language.id)) {
44+
// Here the language for the content has no region code, and we have an exact
45+
// match with the language we are using.
4146
return 2;
4247
}
4348
if (language.lang_code === langCode || additionalCodes.includes(language.lang_code)) {
49+
// Here the language for the content has a region code, but the language itself
50+
// matches, even if the region is different.
4451
return 1;
4552
}
4653
return 0;

0 commit comments

Comments
 (0)