Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bookmarks feature #8272

Merged
merged 12 commits into from
Sep 14, 2021
4 changes: 4 additions & 0 deletions kolibri/core/assets/src/api-resources/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export const DynamicNetworkLocationResource = new Resource({
name: 'dynamicnetworklocation',
});

export const BookmarksResource = new Resource({
name: 'bookmarks',
});

export { default as ClassroomResource } from './classroom';
export { default as ContentNodeResource } from './contentNode';
export { default as ContentNodeGranularResource } from './contentNodeGranular';
Expand Down
1 change: 1 addition & 0 deletions kolibri/core/assets/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const ContentNodeKinds = {
CLASSROOM: 'CLASSROOM',
ACTIVITY: 'ACTIVITY',
SLIDESHOW: 'slideshow',
BOOKMARK: 'bookmark',
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
};

export const LearningActivities = {
Expand Down
2 changes: 2 additions & 0 deletions kolibri/plugins/coach/assets/src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const PageNames = {
EXAM_CREATION_SELECT_CHANNEL_QUIZ_TOPIC: 'EXAM_CREATION_SELECT_CHANNEL_QUIZ_TOPIC',
EXAM_CREATION_CHANNEL_QUIZ_PREVIEW: 'EXAM_CREATION_CHANNEL_QUIZ_PREVIEW',
EXAM_CREATION_TOPIC: 'EXAM_CREATION_TOPIC',
EXAM_CREATION_BOOKMARKS: 'EXAM_CREATION_BOOKMARKS',
EXAM_CREATION_BOOKMARKS_MAIN: 'EXAM_CREATION_BOOKMARKS_MAIN',
EXAM_CREATION_PREVIEW: 'EXAM_CREATION_PREVIEW',
EXAM_CREATION_SEARCH: 'EXAM_CREATION_SEARCH',
EXAM_CREATION_QUESTION_SELECTION: 'EXAM_CREATION_QUESTION_SELECTION',
Expand Down
60 changes: 58 additions & 2 deletions kolibri/plugins/coach/assets/src/modules/examCreation/handlers.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import pickBy from 'lodash/pickBy';
import uniq from 'lodash/uniq';
import { ContentNodeKinds } from 'kolibri.coreVue.vuex.constants';
import { ContentNodeResource, ContentNodeSearchResource, ChannelResource } from 'kolibri.resources';
import {
ContentNodeResource,
BookmarksResource,
ContentNodeSearchResource,
ChannelResource,
} from 'kolibri.resources';
import { assessmentMetaDataState } from 'kolibri.coreVue.vuex.mappers';
import router from 'kolibri.coreVue.router';
import { PageNames } from '../../constants';
import { filterAndAnnotateContentList, fetchChannelQuizzes } from './actions';

function showExamCreationPage(store, params) {
const { contentList, pageName, ancestors = [], searchResults = null } = params;
const { contentList, bookmarksList, pageName, ancestors = [], searchResults = null } = params;
return store.dispatch('loading').then(() => {
store.commit('examCreation/SET_ANCESTORS', ancestors);
store.commit('examCreation/SET_CONTENT_LIST', contentList);
store.commit('examCreation/SET_BOOKMARKS_LIST', bookmarksList);
if (searchResults) {
store.commit('examCreation/SET_SEARCH_RESULTS', searchResults);
}
Expand Down Expand Up @@ -118,6 +124,56 @@ export function showExamCreationTopicPage(store, params) {
});
});
}
export function showExamCreationBookamrksPage(store, params) {
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
return store.dispatch('loading').then(() => {
const { topicId } = params;
const topicNodePromise = ContentNodeResource.fetchModel({ id: topicId });
const childNodesPromise = ContentNodeResource.fetchCollection({
getParams: {
parent: topicId,
kind_in: [ContentNodeKinds.TOPIC, ContentNodeKinds.VIDEO, ContentNodeKinds.EXERCISE],
},
});
const loadRequirements = [topicNodePromise, childNodesPromise];

return Promise.all(loadRequirements).then(([topicNode, childNodes]) => {
return filterAndAnnotateContentList(childNodes).then(() => {
store.commit('SET_TOOLBAR_ROUTE', {
name: PageNames.EXAMS,
});
return showExamCreationPage(store, {
classId: params.classId,
bookmarksList: childNodes,
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
pageName: PageNames.EXAM_CREATION_BOOKMARKS,
ancestors: [...topicNode.ancestors, topicNode],
});
});
});
});
}
export function showExamCreationAllBookmarks(store) {
return store.dispatch('loading').then(() => {
let dataArr = [];
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
getBookmarks()
.then(bookmarks => {
bookmarks.forEach(bookmark => {
ContentNodeResource.fetchModel({ id: bookmark.contentnode_id }).then(data => {
dataArr.push(data);
});
});
})
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
.then(() => {
return showExamCreationPage(store, {
bookmarksList: dataArr,
});
});
});
}
function getBookmarks() {
return BookmarksResource.fetchCollection().then(bookmarks => {
return bookmarks;
});
}

export function showExamCreationPreviewPage(store, params, query = {}) {
const { classId, contentId } = params;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function defaultState() {
numberOfQuestions: 10,
seed: getRandomInt(), // consistent seed is used for question selection
contentList: [],
bookmarksList: [],
selectedExercises: {},
availableQuestions: 0,
searchResults: {
Expand Down Expand Up @@ -70,6 +71,9 @@ export default {
SET_CONTENT_LIST(state, contentList) {
state.contentList = contentList;
},
SET_BOOKMARKS_LIST(state, bookmarksList) {
state.bookmarksList = bookmarksList;
},
ADD_TO_SELECTED_EXERCISES(state, exercises) {
state.selectedExercises = Object.assign(
{},
Expand Down
18 changes: 18 additions & 0 deletions kolibri/plugins/coach/assets/src/routes/planExamRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { PageNames } from '../constants';
import {
showExamCreationRootPage,
showExamCreationTopicPage,
showExamCreationBookamrksPage,
showExamCreationAllBookmarks,
showExamCreationSearchPage,
showExamCreationQuestionSelectionPage,
showExamCreationPreviewPage,
Expand Down Expand Up @@ -64,6 +66,22 @@ export default [
showExamCreationTopicPage(store, toRoute.params);
},
},
{
name: PageNames.EXAM_CREATION_BOOKMARKS,
path: '/:classId/plan/quizzes/new/bookmark/:topicId',
component: CreateExamPage,
handler: toRoute => {
showExamCreationBookamrksPage(store, toRoute.params);
},
},
{
name: PageNames.EXAM_CREATION_BOOKMARKS_MAIN,
path: '/:classId/plan/quizzes/new/bookmarks',
component: CreateExamPage,
handler: toRoute => {
showExamCreationAllBookmarks(store, toRoute.params);
},
},
{
name: PageNames.EXAM_CREATION_SEARCH,
path: '/:classId/plan/quizzes/new/search/:searchTerm',
Expand Down
157 changes: 120 additions & 37 deletions kolibri/plugins/coach/assets/src/views/plan/CreateExamPage/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,44 +81,68 @@
</KGrid>

<h2>{{ $tr('chooseExercises') }}</h2>
<div v-if="!showChannels">
<ContentCardList
:contentList="bookmarksContentList"
:contentHasCheckbox="contentHasCheckbox"
:contentCardMessage="() => ''"
:contentCardLink="bookmarksLink"
:contentIsChecked="() => false"
:viewMoreButtonState="viewMoreButtonState"
:showSelectAll="selectAllIsVisible"
:contentIsIndeterminate="() => false"
@changeselectall="toggleTopicInWorkingResources"
@change_content_card="toggleSelected"
@moreresults="handleMoreResults"
/>
</div>
<div v-if="showChannels">
<p>Select from bookmarks</p>
<div @click="lessonCardClicked">
<LessonContentCard
Evgeni998 marked this conversation as resolved.
Show resolved Hide resolved
:title="$tr('bookmarks')"
:link="getBookmarksLink()"
:kind="$tr('bookmark')"
:description="this.bookmarksCount + $tr('resources')"
:isLeaf="false"
/>
</div>
<LessonsSearchBox
class="search-box"
@searchterm="handleSearchTerm"
/>

<LessonsSearchBox
class="search-box"
@searchterm="handleSearchTerm"
/>

<LessonsSearchFilters
v-if="inSearchMode"
v-model="filters"
:searchTerm="searchTerm"
:searchResults="searchResults"
/>
<ResourceSelectionBreadcrumbs
v-else
:ancestors="ancestors"
:channelsLink="channelsLink"
:topicsLink="topicsLink"
/>

<h2>{{ topicTitle }}</h2>
<p>{{ topicDescription }}</p>

<ContentCardList
:contentList="filteredContentList"
:showSelectAll="selectAllIsVisible"
:viewMoreButtonState="viewMoreButtonState"
:selectAllChecked="selectAllChecked"
:selectAllIndeterminate="selectAllIndeterminate"
:contentIsChecked="contentIsSelected"
:contentIsIndeterminate="contentIsIndeterminate"
:contentHasCheckbox="contentHasCheckbox"
:contentCardMessage="selectionMetadata"
:contentCardLink="contentLink"
@changeselectall="toggleTopicInWorkingResources"
@change_content_card="toggleSelected"
@moreresults="handleMoreResults"
/>

<LessonsSearchFilters
v-if="inSearchMode"
v-model="filters"
:searchTerm="searchTerm"
:searchResults="searchResults"
/>
<ResourceSelectionBreadcrumbs
v-else
:ancestors="ancestors"
:channelsLink="channelsLink"
:topicsLink="topicsLink"
/>
<h2>{{ topicTitle }}</h2>
<p>{{ topicDescription }}</p>
<p>Select from channels</p>
<ContentCardList
:contentList="filteredContentList"
:showSelectAll="selectAllIsVisible"
:viewMoreButtonState="viewMoreButtonState"
:selectAllChecked="selectAllChecked"
:selectAllIndeterminate="selectAllIndeterminate"
:contentIsChecked="contentIsSelected"
:contentIsIndeterminate="contentIsIndeterminate"
:contentHasCheckbox="contentHasCheckbox"
:contentCardMessage="selectionMetadata"
:contentCardLink="contentLink"
@changeselectall="toggleTopicInWorkingResources"
@change_content_card="toggleSelected"
@moreresults="handleMoreResults"
/>
</div>
<BottomAppBar v-if="inSearchMode">
<KRouterLink
appearance="raised-button"
Expand Down Expand Up @@ -159,13 +183,15 @@
import pickBy from 'lodash/pickBy';
import BottomAppBar from 'kolibri.coreVue.components.BottomAppBar';
import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
import { BookmarksResource } from 'kolibri.resources';
import { PageNames } from '../../../constants/';
import { MAX_QUESTIONS } from '../../../constants/examConstants';
import LessonsSearchBox from '../../plan/LessonResourceSelectionPage/SearchTools/LessonsSearchBox';
import LessonsSearchFilters from '../../plan/LessonResourceSelectionPage/SearchTools/LessonsSearchFilters';
import ResourceSelectionBreadcrumbs from '../../plan/LessonResourceSelectionPage/SearchTools/ResourceSelectionBreadcrumbs';
import ContentCardList from '../../plan/LessonResourceSelectionPage/ContentCardList';
import commonCoach from '../../common';
import LessonContentCard from '../LessonResourceSelectionPage/LessonContentCard/index';

export default {
// TODO: Rename this to 'ExamCreationPage'
Expand All @@ -177,6 +203,7 @@
ResourceSelectionBreadcrumbs,
ContentCardList,
BottomAppBar,
LessonContentCard,
},
mixins: [commonCoreStrings, commonCoach, responsiveWindowMixin],
data() {
Expand All @@ -190,6 +217,8 @@
role: this.$route.query.role || null,
},
numQuestionsBlurred: false,
showChannels: true,
bookmarksCount: 0
};
},
computed: {
Expand All @@ -198,6 +227,7 @@
...mapState('examCreation', [
'numberOfQuestions',
'contentList',
'bookmarksList',
'selectedExercises',
'availableQuestions',
'searchResults',
Expand Down Expand Up @@ -249,6 +279,9 @@
}
},
},
bookmarksContentList() {
return this.bookmarksList ? this.bookmarksList : [];
},
filteredContentList() {
const { role } = this.filters || {};
if (!this.inSearchMode) {
Expand Down Expand Up @@ -365,12 +398,50 @@
});
},
},
created() {
BookmarksResource.fetchCollection().then(bookmarks => {
this.bookmarksCount = bookmarks.length
});
},
methods: {
...mapActions('examCreation', [
'addToSelectedExercises',
'removeFromSelectedExercises',
'fetchAdditionalSearchResults',
]),
lessonCardClicked() {
this.showChannels = false;
},
getBookmarksLink() {
return {
name: PageNames.EXAM_CREATION_BOOKMARKS_MAIN,
};
},
bookmarksLink(content) {
if (!content.is_leaf) {
return {
name: PageNames.EXAM_CREATION_BOOKMARKS,
params: {
classId: this.classId,
topicId: content.id,
},
};
}
const { query } = this.$route;
return {
name: PageNames.EXAM_CREATION_PREVIEW,
params: {
classId: this.classId,
contentId: content.id,
},
query: {
...query,
...pickBy({
searchTerm: this.$route.params.searchTerm,
}),
},
};
},
contentLink(content) {
if (!content.is_leaf) {
return {
Expand Down Expand Up @@ -526,6 +597,9 @@
},
},
$trs: {
resources: ' resources',
bookmark: 'bookmark',
bookmarks: 'Bookmarks',
createNewExamLabel: {
message: 'Create new quiz',
context: "Title of the screen launched from the 'New quiz' button on the 'Plan' tab.",
Expand Down Expand Up @@ -578,4 +652,13 @@
vertical-align: middle;
}

.bookmarks-container {
display: flex;
align-items: center;
}

.lesson-content-card {
width: 100%;
}

</style>
Loading