Instead of showing the form for offline entries, we're showing them as normal entries and the form is only used for creating new entries. Additionally, the form won't be shown as a split view item any longer, it will always open a new page.
307 lines
9.5 KiB
TypeScript
307 lines
9.5 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> {
|
|
|
|
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) => Promise<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 entry objects.
|
|
*
|
|
* @param entry Item to check.
|
|
* @returns 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.
|
|
* @returns Whether the item is an offline entry.
|
|
*/
|
|
isOfflineEntry(entry: AddonModGlossaryEntryItem): entry is AddonModGlossaryOfflineEntry {
|
|
return !this.isOnlineEntry(entry);
|
|
}
|
|
|
|
/**
|
|
* @inheritdoc
|
|
*/
|
|
getItemPath(entry: AddonModGlossaryEntryItem): string {
|
|
if (this.isOfflineEntry(entry)) {
|
|
return `${this.GLOSSARY_PATH_PREFIX}entry/new-${entry.timecreated}`;
|
|
}
|
|
|
|
return `${this.GLOSSARY_PATH_PREFIX}entry/${entry.id}`;
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
|
|
const glossaryId = this.glossary.id;
|
|
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesBySearch(glossaryId, query, true, options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesBySearch(glossaryId, query, true);
|
|
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.
|
|
*
|
|
* @param invalidateGlossary Whether to invalidate the entire glossary or not
|
|
*/
|
|
async invalidateCache(invalidateGlossary: boolean = true): Promise<void> {
|
|
await Promise.all<unknown>([
|
|
this.fetchInvalidate && this.fetchInvalidate(),
|
|
invalidateGlossary && AddonModGlossary.invalidateCourseGlossaries(this.COURSE_ID),
|
|
invalidateGlossary && 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!');
|
|
}
|
|
|
|
const glossaryId = this.glossary.id;
|
|
this.fetchMode = mode;
|
|
this.isSearch = false;
|
|
this.setDirty(true);
|
|
|
|
switch (mode) {
|
|
case 'author_all':
|
|
// Browse by author.
|
|
this.viewMode = 'author';
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesByAuthor(glossaryId, options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesByAuthor(glossaryId);
|
|
break;
|
|
|
|
case 'cat_all':
|
|
// Browse by category.
|
|
this.viewMode = 'cat';
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesByCategory(glossaryId, options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesByCategory(glossaryId);
|
|
break;
|
|
|
|
case 'newest_first':
|
|
// Newest first.
|
|
this.viewMode = 'date';
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesByDate(glossaryId, 'CREATION', options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesByDate(glossaryId, 'CREATION');
|
|
break;
|
|
|
|
case 'recently_updated':
|
|
// Recently updated.
|
|
this.viewMode = 'date';
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesByDate(glossaryId, 'UPDATE', options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesByDate(glossaryId, 'UPDATE');
|
|
break;
|
|
|
|
case 'letter_all':
|
|
default:
|
|
// Consider it is 'letter_all'.
|
|
this.viewMode = 'letter';
|
|
this.fetchMode = 'letter_all';
|
|
this.fetchFunction = (options) => AddonModGlossary.getEntriesByLetter(glossaryId, options);
|
|
this.fetchInvalidate = () => AddonModGlossary.invalidateEntriesByLetter(glossaryId);
|
|
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.getGlossaryOfflineEntries(glossary.id);
|
|
|
|
offlineEntries.sort((a, b) => a.concept.localeCompare(b.concept));
|
|
|
|
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;
|
|
|
|
/**
|
|
* Fetch mode to sort entries.
|
|
*/
|
|
export type AddonModGlossaryFetchMode = 'author_all' | 'cat_all' | 'newest_first' | 'recently_updated' | 'letter_all';
|