From 338b3a87a0d4030c68d9378ccfbe190f0400d227 Mon Sep 17 00:00:00 2001 From: Marcella Maki Date: Thu, 24 Aug 2023 14:42:19 -0400 Subject: [PATCH 01/26] Download page displays in sidenav only when download permissions permitted --- .../src/my_downloads/views/MyDownloadsSideNavEntry.js | 5 ++++- kolibri/plugins/learn/kolibri_plugin.py | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloadsSideNavEntry.js b/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloadsSideNavEntry.js index 0f2b3bd97ab..8c4e2cc9420 100644 --- a/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloadsSideNavEntry.js +++ b/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloadsSideNavEntry.js @@ -2,6 +2,7 @@ import { UserKinds, NavComponentSections } from 'kolibri.coreVue.vuex.constants' import navComponents from 'kolibri.utils.navComponents'; import coreStrings from 'kolibri.utils.coreStrings'; import urls from 'kolibri.urls'; +import plugin_data from 'plugin_data'; const component = { name: 'MyDownloadsSideNavEntry', @@ -17,6 +18,8 @@ const component = { section: NavComponentSections.ACCOUNT, }; -navComponents.register(component); +if (plugin_data.allowLearnerDownloads) { + navComponents.register(component); +} export default component; diff --git a/kolibri/plugins/learn/kolibri_plugin.py b/kolibri/plugins/learn/kolibri_plugin.py index 38f2227bfd3..041da845b05 100644 --- a/kolibri/plugins/learn/kolibri_plugin.py +++ b/kolibri/plugins/learn/kolibri_plugin.py @@ -172,3 +172,11 @@ def on_renew(self, instance, network_locations): @register_hook class MyDownloadsNavAction(NavigationHook): bundle_id = "my_downloads_side_nav" + + @property + def plugin_data(self): + return { + "allowLearnerDownloads": get_device_setting( + "allow_learner_download_resources" + ), + } From c14586a828f8c3e107ea7be32c0b695f8895ee3a Mon Sep 17 00:00:00 2001 From: Marcella Maki Date: Tue, 29 Aug 2023 09:48:49 -0400 Subject: [PATCH 02/26] Add download progress polling, remove download before complete, disable download button after download initiated cleanup --- .../views/DownloadsList/index.vue | 44 ++++++++++--------- .../src/my_downloads/views/MyDownloads.vue | 40 ++++++++++++++++- .../HybridLearningFooter.vue | 10 ++++- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/kolibri/plugins/learn/assets/src/my_downloads/views/DownloadsList/index.vue b/kolibri/plugins/learn/assets/src/my_downloads/views/DownloadsList/index.vue index 1211e6274a8..2e607ee889c 100644 --- a/kolibri/plugins/learn/assets/src/my_downloads/views/DownloadsList/index.vue +++ b/kolibri/plugins/learn/assets/src/my_downloads/views/DownloadsList/index.vue @@ -75,7 +75,6 @@ @@ -111,8 +110,8 @@ import CoreTable from 'kolibri.coreVue.components.CoreTable'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; import PaginatedListContainerWithBackend from 'kolibri-common/components/PaginatedListContainerWithBackend'; - import { computed, getCurrentInstance, watch, ref } from 'kolibri.lib.vueCompositionApi'; - import { get, set } from '@vueuse/core'; + import { computed, getCurrentInstance } from 'kolibri.lib.vueCompositionApi'; + import { get } from '@vueuse/core'; import useContentLink from '../../../composables/useContentLink'; import useLearningActivities from '../../../composables/useLearningActivities'; import useDownloadRequests from '../../../composables/useDownloadRequests'; @@ -132,14 +131,12 @@ const { genExternalContentURLBackLinkCurrentPage } = useContentLink(); const { getLearningActivityIcon } = useLearningActivities(); const { downloadRequestMap, availableSpace } = useDownloadRequests(); - const totalPageNumber = ref(0); const store = getCurrentInstance().proxy.$store; const query = computed(() => get(route).query); const route = computed(() => store.state.route); const sort = computed(() => query.value.sort); const pageSizeNumber = computed(() => Number(query.value.page_size || 25)); const activityType = computed(() => query.value.activity || 'all'); - const downloads = ref([]); const sortedFilteredDownloads = () => { let downloadsToDisplay = []; if (downloadRequestMap) { @@ -148,7 +145,7 @@ } if (activityType) { if (activityType.value !== 'all') { - downloadsToDisplay = downloadsToDisplay.filter(download => + downloadsToDisplay = this.downloadsToDisplay.filter(download => download.metadata.learning_activities.includes(activityType.value) ); } @@ -177,19 +174,13 @@ } } } - set(totalPageNumber, Math.ceil(downloadsToDisplay.length / pageSizeNumber.value)); - set(downloads, downloadsToDisplay); }; sortedFilteredDownloads(); - watch(route, () => { - sortedFilteredDownloads(); - }); return { downloadRequestMap, - downloads, + pageSizeNumber, getLearningActivityIcon, sortedFilteredDownloads, - totalPageNumber, availableSpace, genExternalContentURLBackLinkCurrentPage, }; @@ -239,18 +230,28 @@ }, }, paginatedDownloads() { - const startIndex = (this.currentPage - 1) * this.itemsPerPage; - const endIndex = startIndex + this.itemsPerPage; - return this.downloads.slice(startIndex, endIndex); + if (this.downloads && this.downloads.length > 0) { + const startIndex = (this.currentPage - 1) * this.itemsPerPage; + const endIndex = startIndex + this.itemsPerPage; + return this.downloads.slice(startIndex, endIndex); + } else { + return []; + } + }, + downloads() { + return this.sortedFilteredDownloads; + }, + totalPageNumber() { + if (this.downloadsToDisplay && this.downloadsToDisplay.length) { + return Math.ceil(this.downloadsToDisplay.length / this.pageSizeNumber.value); + } + return 1; }, areAllSelected() { return Object.keys(this.downloads).every(id => this.selectedDownloads.includes(id)); }, areAnyAvailable() { - if (this.downloads && this.downloads.length > 0) { - return this.downloads.filter(download => download.status === 'COMPLETED').length > 0; - } - return false; + return this.downloads && this.downloads.length > 0; }, }, watch: { @@ -332,7 +333,10 @@ message = this.$formatRelative(download.requested_at, { now: this.now }); break; case 'FAILED': + // if (check source id is on the network) { message = this.coreString('downloadFailedWillRetry'); + // } + message = this.coreString('downloadedFailedCanNotRetry'); break; default: // If no valid sort option provided, return unsorted array diff --git a/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloads.vue b/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloads.vue index 7309f228022..241598e5f8d 100644 --- a/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloads.vue +++ b/kolibri/plugins/learn/assets/src/my_downloads/views/MyDownloads.vue @@ -100,13 +100,13 @@ activityType: activityType.value, }); }; - fetchDownloads(); fetchAvailableFreespace(); watch(route, fetchDownloads); return { downloadRequestMap, loading, + fetchDownloads, availableSpace, totalPageNumber, fetchAvailableFreespace, @@ -122,6 +122,12 @@ ); }, }, + created() { + this.startPolling(); + }, + beforeDestroy() { + clearInterval(this.pollingInterval); + }, methods: { formattedSize(size) { if (size > 0) { @@ -139,6 +145,38 @@ }); } }, + startPolling() { + this.pollingInterval = setInterval(async () => { + await this.fetchDownloads(); + this.fetchDevices().then(devices => (this.networkDevices = devices)); + this.calculatePollingInterval(); + }, 1000); // Initial interval of 1 second + }, + calculatePollingInterval() { + let pollingInterval = 30000; // Default interval of 30 seconds + + for (const download in this.downloadRequestMap) { + const status = this.downloadRequestMap[download].status; + + if (status === 'PENDING' || status === 'FAILED') { + pollingInterval = 5000; // Poll every 5 seconds + break; + } else if (status === 'IN_PROGRESS') { + pollingInterval = 1000; // Poll every 1 second + break; + } + } + + if (pollingInterval !== this.pollingInterval) { + clearInterval(this.pollingInterval); + this.pollingInterval = setInterval(async () => { + this.fetchDownloads(); + this.fetchDevices().then(devices => (this.networkDevices = devices)); + console.log(this.networkDevices); + this.calculatePollingInterval(); + }, pollingInterval); + } + }, }, }; diff --git a/kolibri/plugins/learn/assets/src/views/HybridLearningContentCard/HybridLearningFooter.vue b/kolibri/plugins/learn/assets/src/views/HybridLearningContentCard/HybridLearningFooter.vue index c27bb89db76..d9a13fe0411 100644 --- a/kolibri/plugins/learn/assets/src/views/HybridLearningContentCard/HybridLearningFooter.vue +++ b/kolibri/plugins/learn/assets/src/views/HybridLearningContentCard/HybridLearningFooter.vue @@ -149,7 +149,15 @@ return !this.isTopic && this.allowDownloads; }, downloadedByLearner() { - return this.downloadEnabled && Boolean(this.downloadRequestMap[this.contentNode.id]); + let downloaded = null; + for (const key in this.downloadRequestMap) { + const item = this.downloadRequestMap[key]; + if (item.contentnode_id === this.contentNode.id) { + downloaded = item; + break; + } + } + return this.downloadEnabled && Boolean(downloaded); }, downloadableByLearner() { return ( From 7164551201379afe63e843a1cae5d13be36910c3 Mon Sep 17 00:00:00 2001 From: Marcella Maki Date: Tue, 29 Aug 2023 09:50:55 -0400 Subject: [PATCH 03/26] Fixing prop warning topics page --- .../learn/assets/src/views/TopicsPage/ToggleHeaderTabs.vue | 3 +++ .../learn/assets/src/views/TopicsPage/TopicsPanelModal.vue | 4 ++-- kolibri/plugins/learn/assets/src/views/TopicsPage/index.vue | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/kolibri/plugins/learn/assets/src/views/TopicsPage/ToggleHeaderTabs.vue b/kolibri/plugins/learn/assets/src/views/TopicsPage/ToggleHeaderTabs.vue index c97e1b89934..226f5dccb00 100644 --- a/kolibri/plugins/learn/assets/src/views/TopicsPage/ToggleHeaderTabs.vue +++ b/kolibri/plugins/learn/assets/src/views/TopicsPage/ToggleHeaderTabs.vue @@ -60,6 +60,9 @@ props: { topic: { type: Object, + default() { + return {}; + }, required: true, }, topics: { diff --git a/kolibri/plugins/learn/assets/src/views/TopicsPage/TopicsPanelModal.vue b/kolibri/plugins/learn/assets/src/views/TopicsPage/TopicsPanelModal.vue index d14699e9175..f70e7e490d7 100644 --- a/kolibri/plugins/learn/assets/src/views/TopicsPage/TopicsPanelModal.vue +++ b/kolibri/plugins/learn/assets/src/views/TopicsPage/TopicsPanelModal.vue @@ -64,8 +64,8 @@ }, props: { topicMore: { - type: Object, - default: null, + type: Boolean, + default: false, }, topics: { type: Array, diff --git a/kolibri/plugins/learn/assets/src/views/TopicsPage/index.vue b/kolibri/plugins/learn/assets/src/views/TopicsPage/index.vue index f3d5c9a048b..23d33b13b98 100644 --- a/kolibri/plugins/learn/assets/src/views/TopicsPage/index.vue +++ b/kolibri/plugins/learn/assets/src/views/TopicsPage/index.vue @@ -38,7 +38,7 @@ >