386 lines
12 KiB
TypeScript
386 lines
12 KiB
TypeScript
// (C) Copyright 2015 Moodle Pty Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import { Params } from '@angular/router';
|
|
import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
|
|
import {
|
|
AddonModGlossary,
|
|
AddonModGlossaryEntry,
|
|
AddonModGlossaryGetEntriesOptions,
|
|
AddonModGlossaryGetEntriesWSResponse,
|
|
AddonModGlossaryGlossary,
|
|
AddonModGlossaryProvider,
|
|
} from '../services/glossary';
|
|
import { AddonModGlossaryOffline, AddonModGlossaryOfflineEntry } from '../services/glossary-offline';
|
|
|
|
/**
|
|
* Provides a collection of glossary entries.
|
|
*/
|
|
export class AddonModGlossaryEntriesSource extends CoreRoutedItemsManagerSource<AddonModGlossaryEntryItem> {
|
|
|
|
static readonly NEW_ENTRY: AddonModGlossaryNewEntryForm = { newEntry: true };
|
|
|
|
readonly COURSE_ID: number;
|
|
readonly CM_ID: number;
|
|
readonly GLOSSARY_PATH_PREFIX: string;
|
|
|
|
isSearch = false;
|
|
hasSearched = false;
|
|
fetchMode?: AddonModGlossaryFetchMode;
|
|
viewMode?: string;
|
|
glossary?: AddonModGlossaryGlossary;
|
|
onlineEntries: AddonModGlossaryEntry[] = [];
|
|
offlineEntries: AddonModGlossaryOfflineEntry[] = [];
|
|
|
|
protected fetchFunction?: (options?: AddonModGlossaryGetEntriesOptions) => AddonModGlossaryGetEntriesWSResponse;
|
|
protected fetchInvalidate?: () => Promise<void>;
|
|
|
|
constructor(courseId: number, cmId: number, glossaryPathPrefix: string) {
|
|
super();
|
|
|
|
this.COURSE_ID = courseId;
|
|
this.CM_ID = cmId;
|
|
this.GLOSSARY_PATH_PREFIX = glossaryPathPrefix;
|
|
}
|
|
|
|
/**
|
|
* Type guard to infer NewEntryForm objects.
|
|
*
|
|
* @param entry Item to check.
|
|
* @return Whether the item is a new entry form.
|
|
*/
|
|
isNewEntryForm(entry: AddonModGlossaryEntryItem): entry is AddonModGlossaryNewEntryForm {
|
|
return 'newEntry' in entry;
|
|
}
|
|
|
|
/**
|
|
* Type guard to infer entry objects.
|
|
*
|
|
* @param entry Item to check.
|
|
* @return Whether the item is an offline entry.
|
|
*/
|
|
isOnlineEntry(entry: AddonModGlossaryEntryItem): entry is AddonModGlossaryEntry {
|
|
return 'id' in entry;
|
|
}
|
|
|
|
/**
|
|
* Type guard to infer entry objects.
|
|
*
|
|
* @param entry Item to check.
|
|
* @return Whether the item is an offline entry.
|
|
*/
|
|
isOfflineEntry(entry: AddonModGlossaryEntryItem): entry is AddonModGlossaryOfflineEntry {
|
|
return !this.isNewEntryForm(entry) && !this.isOnlineEntry(entry);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
getItemPath(entry: AddonModGlossaryEntryItem): string {
|
|
if (this.isOnlineEntry(entry)) {
|
|
return `${this.GLOSSARY_PATH_PREFIX}entry/${entry.id}`;
|
|
}
|
|
|
|
if (this.isOfflineEntry(entry)) {
|
|
return `${this.GLOSSARY_PATH_PREFIX}edit/${entry.timecreated}`;
|
|
}
|
|
|
|
return `${this.GLOSSARY_PATH_PREFIX}edit/0`;
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
getItemQueryParams(entry: AddonModGlossaryEntryItem): Params {
|
|
const params: Params = {
|
|
cmId: this.CM_ID,
|
|
courseId: this.COURSE_ID,
|
|
};
|
|
|
|
if (this.isOfflineEntry(entry)) {
|
|
params.concept = entry.concept;
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
getPagesLoaded(): number {
|
|
if (this.items === null) {
|
|
return 0;
|
|
}
|
|
|
|
return Math.ceil(this.onlineEntries.length / this.getPageLength());
|
|
}
|
|
|
|
/**
|
|
* Start searching.
|
|
*/
|
|
startSearch(): void {
|
|
this.isSearch = true;
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Stop searching and restore unfiltered collection.
|
|
*
|
|
* @param cachedOnlineEntries Cached online entries.
|
|
* @param hasMoreOnlineEntries Whether there were more online entries.
|
|
*/
|
|
stopSearch(cachedOnlineEntries: AddonModGlossaryEntry[], hasMoreOnlineEntries: boolean): void {
|
|
if (!this.fetchMode) {
|
|
return;
|
|
}
|
|
|
|
this.isSearch = false;
|
|
this.hasSearched = false;
|
|
this.onlineEntries = cachedOnlineEntries;
|
|
this.hasMoreItems = hasMoreOnlineEntries;
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Set search query.
|
|
*
|
|
* @param query Search query.
|
|
*/
|
|
search(query: string): void {
|
|
if (!this.glossary) {
|
|
return;
|
|
}
|
|
|
|
this.fetchFunction = AddonModGlossary.getEntriesBySearch.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
query,
|
|
true,
|
|
'CONCEPT',
|
|
'ASC',
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesBySearch.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
query,
|
|
true,
|
|
'CONCEPT',
|
|
'ASC',
|
|
);
|
|
this.hasSearched = true;
|
|
this.setDirty(true);
|
|
}
|
|
|
|
/**
|
|
* Load glossary.
|
|
*/
|
|
async loadGlossary(): Promise<void> {
|
|
this.glossary = await AddonModGlossary.getGlossary(this.COURSE_ID, this.CM_ID);
|
|
}
|
|
|
|
/**
|
|
* Invalidate glossary cache.
|
|
*/
|
|
async invalidateCache(): Promise<void> {
|
|
await Promise.all([
|
|
AddonModGlossary.invalidateCourseGlossaries(this.COURSE_ID),
|
|
this.fetchInvalidate && this.fetchInvalidate(),
|
|
this.glossary && AddonModGlossary.invalidateCategories(this.glossary.id),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Change fetch mode.
|
|
*
|
|
* @param mode New mode.
|
|
*/
|
|
switchMode(mode: AddonModGlossaryFetchMode): void {
|
|
if (!this.glossary) {
|
|
throw new Error('Can\'t switch entries mode without a glossary!');
|
|
}
|
|
|
|
this.fetchMode = mode;
|
|
this.isSearch = false;
|
|
this.setDirty(true);
|
|
|
|
switch (mode) {
|
|
case 'author_all':
|
|
// Browse by author.
|
|
this.viewMode = 'author';
|
|
this.fetchFunction = AddonModGlossary.getEntriesByAuthor.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'ALL',
|
|
'LASTNAME',
|
|
'ASC',
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesByAuthor.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'ALL',
|
|
'LASTNAME',
|
|
'ASC',
|
|
);
|
|
break;
|
|
|
|
case 'cat_all':
|
|
// Browse by category.
|
|
this.viewMode = 'cat';
|
|
this.fetchFunction = AddonModGlossary.getEntriesByCategory.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
AddonModGlossaryProvider.SHOW_ALL_CATEGORIES,
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesByCategory.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
AddonModGlossaryProvider.SHOW_ALL_CATEGORIES,
|
|
);
|
|
break;
|
|
|
|
case 'newest_first':
|
|
// Newest first.
|
|
this.viewMode = 'date';
|
|
this.fetchFunction = AddonModGlossary.getEntriesByDate.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'CREATION',
|
|
'DESC',
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesByDate.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'CREATION',
|
|
'DESC',
|
|
);
|
|
break;
|
|
|
|
case 'recently_updated':
|
|
// Recently updated.
|
|
this.viewMode = 'date';
|
|
this.fetchFunction = AddonModGlossary.getEntriesByDate.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'UPDATE',
|
|
'DESC',
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesByDate.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'UPDATE',
|
|
'DESC',
|
|
);
|
|
break;
|
|
|
|
case 'letter_all':
|
|
default:
|
|
// Consider it is 'letter_all'.
|
|
this.viewMode = 'letter';
|
|
this.fetchMode = 'letter_all';
|
|
this.fetchFunction = AddonModGlossary.getEntriesByLetter.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'ALL',
|
|
);
|
|
this.fetchInvalidate = AddonModGlossary.invalidateEntriesByLetter.bind(
|
|
AddonModGlossary.instance,
|
|
this.glossary.id,
|
|
'ALL',
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
protected async loadPageItems(page: number): Promise<{ items: AddonModGlossaryEntryItem[]; hasMoreItems: boolean }> {
|
|
const glossary = this.glossary;
|
|
const fetchFunction = this.fetchFunction;
|
|
|
|
if (!glossary || !fetchFunction) {
|
|
throw new Error('Can\'t load entries without glossary or fetch function');
|
|
}
|
|
|
|
const entries: AddonModGlossaryEntryItem[] = [];
|
|
|
|
if (page === 0) {
|
|
const offlineEntries = await AddonModGlossaryOffline.getGlossaryNewEntries(glossary.id);
|
|
|
|
offlineEntries.sort((a, b) => a.concept.localeCompare(b.concept));
|
|
|
|
entries.push(AddonModGlossaryEntriesSource.NEW_ENTRY);
|
|
entries.push(...offlineEntries);
|
|
}
|
|
|
|
const from = page * this.getPageLength();
|
|
const pageEntries = await fetchFunction({ from, cmId: this.CM_ID });
|
|
|
|
entries.push(...pageEntries.entries);
|
|
|
|
return {
|
|
items: entries,
|
|
hasMoreItems: from + pageEntries.entries.length < pageEntries.count,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
protected getPageLength(): number {
|
|
return AddonModGlossaryProvider.LIMIT_ENTRIES;
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
protected setItems(entries: AddonModGlossaryEntryItem[], hasMoreItems: boolean): void {
|
|
this.onlineEntries = [];
|
|
this.offlineEntries = [];
|
|
|
|
entries.forEach(entry => {
|
|
this.isOnlineEntry(entry) && this.onlineEntries.push(entry);
|
|
this.isOfflineEntry(entry) && this.offlineEntries.push(entry);
|
|
});
|
|
|
|
super.setItems(entries, hasMoreItems);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
reset(): void {
|
|
this.onlineEntries = [];
|
|
this.offlineEntries = [];
|
|
|
|
super.reset();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Type of items that can be held by the entries manager.
|
|
*/
|
|
export type AddonModGlossaryEntryItem = AddonModGlossaryEntry | AddonModGlossaryOfflineEntry | AddonModGlossaryNewEntryForm;
|
|
|
|
/**
|
|
* Type to select the new entry form.
|
|
*/
|
|
export type AddonModGlossaryNewEntryForm = { newEntry: true };
|
|
|
|
/**
|
|
* Fetch mode to sort entries.
|
|
*/
|
|
export type AddonModGlossaryFetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'letter_all';
|