MOBILE-3052 glossary: Don't prefetch get_by_id

main
Dani Palou 2019-07-22 13:05:36 +02:00
parent fa3a2d3140
commit ecd918a0d7
4 changed files with 227 additions and 46 deletions

View File

@ -269,7 +269,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
this.viewMode = 'cat'; this.viewMode = 'cat';
this.fetchFunction = this.glossaryProvider.getEntriesByCategory; this.fetchFunction = this.glossaryProvider.getEntriesByCategory;
this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByCategory; this.fetchInvalidate = this.glossaryProvider.invalidateEntriesByCategory;
this.fetchArguments = [this.glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATERGORIES]; this.fetchArguments = [this.glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES];
this.getDivider = (entry: any): string => entry.categoryname; this.getDivider = (entry: any): string => entry.categoryname;
this.showDivider = (entry?: any, previous?: any): boolean => { this.showDivider = (entry?: any, previous?: any): boolean => {
return !previous || this.getDivider(entry) != this.getDivider(previous); return !previous || this.getDivider(entry) != this.getDivider(previous);

View File

@ -17,7 +17,7 @@ import { TranslateService } from '@ngx-translate/core';
import { CoreSite } from '@classes/site'; import { CoreSite } from '@classes/site';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CoreFilepoolProvider } from '@providers/filepool'; import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
import { CoreTextUtilsProvider } from '@providers/utils/text'; import { CoreTextUtilsProvider } from '@providers/utils/text';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
@ -32,13 +32,40 @@ export class AddonModGlossaryProvider {
static COMPONENT = 'mmaModGlossary'; static COMPONENT = 'mmaModGlossary';
static LIMIT_ENTRIES = 25; static LIMIT_ENTRIES = 25;
static LIMIT_CATEGORIES = 10; static LIMIT_CATEGORIES = 10;
static SHOW_ALL_CATERGORIES = 0; static SHOW_ALL_CATEGORIES = 0;
static SHOW_NOT_CATEGORISED = -1; static SHOW_NOT_CATEGORISED = -1;
static ADD_ENTRY_EVENT = 'addon_mod_glossary_add_entry'; static ADD_ENTRY_EVENT = 'addon_mod_glossary_add_entry';
protected ROOT_CACHE_KEY = 'mmaModGlossary:'; protected ROOT_CACHE_KEY = 'mmaModGlossary:';
// Variables for database.
static ENTRIES_TABLE = 'addon_mod_glossary_entry_glossaryid';
protected siteSchema: CoreSiteSchema = {
name: 'AddonModGlossaryProvider',
version: 1,
tables: [
{
name: AddonModGlossaryProvider.ENTRIES_TABLE,
columns: [
{
name: 'entryid',
type: 'INTEGER',
primaryKey: true
},
{
name: 'glossaryid',
type: 'INTEGER',
},
{
name: 'pagefrom',
type: 'INTEGER',
}
]
}
]
};
constructor(private appProvider: CoreAppProvider, constructor(private appProvider: CoreAppProvider,
private sitesProvider: CoreSitesProvider, private sitesProvider: CoreSitesProvider,
private filepoolProvider: CoreFilepoolProvider, private filepoolProvider: CoreFilepoolProvider,
@ -46,7 +73,10 @@ export class AddonModGlossaryProvider {
private textUtils: CoreTextUtilsProvider, private textUtils: CoreTextUtilsProvider,
private utils: CoreUtilsProvider, private utils: CoreUtilsProvider,
private glossaryOffline: AddonModGlossaryOfflineProvider, private glossaryOffline: AddonModGlossaryOfflineProvider,
private logHelper: CoreCourseLogHelperProvider) {} private logHelper: CoreCourseLogHelperProvider) {
this.sitesProvider.registerSiteSchema(this.siteSchema);
}
/** /**
* Get the course glossary cache key. * Get the course glossary cache key.
@ -118,12 +148,13 @@ export class AddonModGlossaryProvider {
* @param {string} sort The direction of the order: ASC or DESC * @param {string} sort The direction of the order: ASC or DESC
* @param {number} from Start returning records from here. * @param {number} from Start returning records from here.
* @param {number} limit Number of records to return. * @param {number} limit Number of records to return.
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false. * @param {boolean} omitExpires True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Resolved with the entries. * @return {Promise<any[]>} Resolved with the entries.
*/ */
getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, from: number, limit: number, getEntriesByAuthor(glossaryId: number, letter: string, field: string, sort: string, from: number, limit: number,
forceCache: boolean, siteId?: string): Promise<any[]> { omitExpires: boolean, forceOffline: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: glossaryId, id: glossaryId,
@ -135,7 +166,8 @@ export class AddonModGlossaryProvider {
}; };
const preSets = { const preSets = {
cacheKey: this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort), cacheKey: this.getEntriesByAuthorCacheKey(glossaryId, letter, field, sort),
omitExpires: forceCache, omitExpires: omitExpires,
forceOffline: forceOffline,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };
@ -165,16 +197,18 @@ export class AddonModGlossaryProvider {
* Get entries by category. * Get entries by category.
* *
* @param {number} glossaryId Glossary Id. * @param {number} glossaryId Glossary Id.
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or * @param {string} categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or
* constant SHOW_NOT_CATEGORISED for uncategorised entries. * constant SHOW_NOT_CATEGORISED for uncategorised entries.
* @param {number} from Start returning records from here. * @param {number} from Start returning records from here.
* @param {number} limit Number of records to return. * @param {number} limit Number of records to return.
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false. * @param {boolean} omitExpires True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Resolved with the entries. * @return {Promise<any[]>} Resolved with the entries.
*/ */
getEntriesByCategory(glossaryId: number, categoryId: number, from: number, limit: number, forceCache: boolean, getEntriesByCategory(glossaryId: number, categoryId: number, from: number, limit: number, omitExpires: boolean,
siteId?: string): Promise<any[]> { forceOffline: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: glossaryId, id: glossaryId,
@ -184,7 +218,8 @@ export class AddonModGlossaryProvider {
}; };
const preSets = { const preSets = {
cacheKey: this.getEntriesByCategoryCacheKey(glossaryId, categoryId), cacheKey: this.getEntriesByCategoryCacheKey(glossaryId, categoryId),
omitExpires: forceCache, omitExpires: omitExpires,
forceOffline: forceOffline,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };
@ -196,7 +231,7 @@ export class AddonModGlossaryProvider {
* Invalidate cache of entries by category. * Invalidate cache of entries by category.
* *
* @param {number} glossaryId Glossary Id. * @param {number} glossaryId Glossary Id.
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or * @param {string} categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or
* constant SHOW_NOT_CATEGORISED for uncategorised entries. * constant SHOW_NOT_CATEGORISED for uncategorised entries.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Resolved when data is invalidated. * @return {Promise<any>} Resolved when data is invalidated.
@ -213,7 +248,7 @@ export class AddonModGlossaryProvider {
* Get the entries by category cache key. * Get the entries by category cache key.
* *
* @param {number} glossaryId Glossary Id. * @param {number} glossaryId Glossary Id.
* @param {string} categoryId The category ID. Use constant SHOW_ALL_CATERGORIES for all categories, or * @param {string} categoryId The category ID. Use constant SHOW_ALL_CATEGORIES for all categories, or
* constant SHOW_NOT_CATEGORISED for uncategorised entries. * constant SHOW_NOT_CATEGORISED for uncategorised entries.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
@ -241,12 +276,14 @@ export class AddonModGlossaryProvider {
* @param {string} sort The direction of the order. * @param {string} sort The direction of the order.
* @param {number} from Start returning records from here. * @param {number} from Start returning records from here.
* @param {number} limit Number of records to return. * @param {number} limit Number of records to return.
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false. * @param {boolean} omitExpires True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Resolved with the entries. * @return {Promise<any[]>} Resolved with the entries.
*/ */
getEntriesByDate(glossaryId: number, order: string, sort: string, from: number, limit: number, forceCache: boolean, getEntriesByDate(glossaryId: number, order: string, sort: string, from: number, limit: number, omitExpires: boolean,
siteId?: string): Promise<any[]> { forceOffline: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: glossaryId, id: glossaryId,
@ -257,7 +294,8 @@ export class AddonModGlossaryProvider {
}; };
const preSets = { const preSets = {
cacheKey: this.getEntriesByDateCacheKey(glossaryId, order, sort), cacheKey: this.getEntriesByDateCacheKey(glossaryId, order, sort),
omitExpires: forceCache, omitExpires: omitExpires,
forceOffline: forceOffline,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };
@ -300,12 +338,14 @@ export class AddonModGlossaryProvider {
* @param {string} letter A letter, or a special keyword. * @param {string} letter A letter, or a special keyword.
* @param {number} from Start returning records from here. * @param {number} from Start returning records from here.
* @param {number} limit Number of records to return. * @param {number} limit Number of records to return.
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false. * @param {boolean} omitExpires True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Resolved with the entries. * @return {Promise<any>} Resolved with the entries.
*/ */
getEntriesByLetter(glossaryId: number, letter: string, from: number, limit: number, forceCache: boolean, siteId?: string): getEntriesByLetter(glossaryId: number, letter: string, from: number, limit: number, omitExpires: boolean, forceOffline: boolean,
Promise<any[]> { siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: glossaryId, id: glossaryId,
@ -315,11 +355,22 @@ export class AddonModGlossaryProvider {
}; };
const preSets = { const preSets = {
cacheKey: this.getEntriesByLetterCacheKey(glossaryId, letter), cacheKey: this.getEntriesByLetterCacheKey(glossaryId, letter),
omitExpires: forceCache, omitExpires: omitExpires,
forceOffline: forceOffline,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };
return site.read('mod_glossary_get_entries_by_letter', params, preSets); return site.read('mod_glossary_get_entries_by_letter', params, preSets).then((result) => {
if (limit == AddonModGlossaryProvider.LIMIT_ENTRIES) {
// Store entries in background, don't block the user for this.
this.storeEntries(glossaryId, result.entries, from, site.getId()).catch(() => {
// Ignore errors.
});
}
return result;
});
}); });
} }
@ -364,12 +415,13 @@ export class AddonModGlossaryProvider {
* @param {string} sort The direction of the order. * @param {string} sort The direction of the order.
* @param {number} from Start returning records from here. * @param {number} from Start returning records from here.
* @param {number} limit Number of records to return. * @param {number} limit Number of records to return.
* @param {boolean} forceCache True to always get the value from cache, false otherwise. Default false. * @param {boolean} omitExpires True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} forceOffline True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Resolved with the entries. * @return {Promise<any[]>} Resolved with the entries.
*/ */
getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, from: number, getEntriesBySearch(glossaryId: number, query: string, fullSearch: boolean, order: string, sort: string, from: number,
limit: number, forceCache: boolean, siteId?: string): Promise<any[]> { limit: number, omitExpires: boolean, forceOffline: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: glossaryId, id: glossaryId,
@ -382,7 +434,8 @@ export class AddonModGlossaryProvider {
}; };
const preSets = { const preSets = {
cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort), cacheKey: this.getEntriesBySearchCacheKey(glossaryId, query, fullSearch, order, sort),
omitExpires: forceCache, omitExpires: omitExpires,
forceOffline: forceOffline,
updateFrequency: CoreSite.FREQUENCY_SOMETIMES updateFrequency: CoreSite.FREQUENCY_SOMETIMES
}; };
@ -497,7 +550,7 @@ export class AddonModGlossaryProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the entry. * @return {Promise<any>} Promise resolved with the entry.
*/ */
getEntry(entryId: number, siteId?: string): Promise<{entry: any, ratinginfo: CoreRatingInfo}> { getEntry(entryId: number, siteId?: string): Promise<{entry: any, ratinginfo: CoreRatingInfo, from?: number}> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
id: entryId id: entryId
@ -513,6 +566,74 @@ export class AddonModGlossaryProvider {
} else { } else {
return Promise.reject(null); return Promise.reject(null);
} }
}).catch((error) => {
// Entry not found. Search it in the list of entries.
let glossaryId;
const searchEntry = (from: number, loadNext: boolean): Promise<any> => {
// Get the entries from this "page" and check if the entry we're looking for is in it.
return this.getEntriesByLetter(glossaryId, 'ALL', from, AddonModGlossaryProvider.LIMIT_ENTRIES, false, true,
siteId).then((result) => {
for (let i = 0; i < result.entries.length; i++) {
const entry = result.entries[i];
if (entry.id == entryId) {
// Entry found, return it.
return {
entry: entry,
from: from
};
}
}
const nextFrom = from + result.entries.length;
if (nextFrom < result.count && loadNext) {
// Get the next "page".
return searchEntry(nextFrom, true);
}
// No more pages and the entry wasn't found. Reject.
return Promise.reject(null);
});
};
return this.getStoredDataForEntry(entryId, site.getId()).then((data) => {
glossaryId = data.glossaryId;
if (typeof data.from != 'undefined') {
return searchEntry(data.from, false).catch(() => {
// Entry not found in that page. Search all pages.
return searchEntry(0, true);
});
}
// Page not specified, search all pages.
return searchEntry(0, true);
}).catch(() => {
return Promise.reject(error);
});
});
});
}
/**
* Get a glossary ID and the "from" of a given entry.
*
* @param {number} entryId Entry ID.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the glossary ID and the "from".
*/
getStoredDataForEntry(entryId: number, siteId?: string): Promise<{glossaryId: number, from: number}> {
return this.sitesProvider.getSite(siteId).then((site) => {
const conditions = {
entryid: entryId
};
return site.getDb().getRecord(AddonModGlossaryProvider.ENTRIES_TABLE, conditions).then((record) => {
return {
glossaryId: record.glossaryid,
from: record.pagefrom
};
}); });
}); });
} }
@ -524,19 +645,21 @@ export class AddonModGlossaryProvider {
* @param {any[]} fetchArguments Arguments to call the fetching. * @param {any[]} fetchArguments Arguments to call the fetching.
* @param {number} [limitFrom=0] Number of entries already fetched, so fetch will be done from this number. * @param {number} [limitFrom=0] Number of entries already fetched, so fetch will be done from this number.
* @param {number} [limitNum] Number of records to return. Defaults to LIMIT_ENTRIES. * @param {number} [limitNum] Number of records to return. Defaults to LIMIT_ENTRIES.
* @param {boolean} [forceCache=false] True to always get the value from cache, false otherwise. Default false. * @param {boolean} [omitExpires=false] True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} [forceOffline=false] True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the response. * @return {Promise<any>} Promise resolved with the response.
*/ */
fetchEntries(fetchFunction: Function, fetchArguments: any[], limitFrom: number = 0, limitNum?: number, fetchEntries(fetchFunction: Function, fetchArguments: any[], limitFrom: number = 0, limitNum?: number,
forceCache: boolean = false, siteId?: string): Promise<any> { omitExpires: boolean = false, forceOffline: boolean = false, siteId?: string): Promise<any> {
limitNum = limitNum || AddonModGlossaryProvider.LIMIT_ENTRIES; limitNum = limitNum || AddonModGlossaryProvider.LIMIT_ENTRIES;
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const args = fetchArguments.slice(); const args = fetchArguments.slice();
args.push(limitFrom); args.push(limitFrom);
args.push(limitNum); args.push(limitNum);
args.push(forceCache); args.push(omitExpires);
args.push(forceOffline);
args.push(siteId); args.push(siteId);
return fetchFunction.apply(this, args); return fetchFunction.apply(this, args);
@ -547,18 +670,21 @@ export class AddonModGlossaryProvider {
* *
* @param {Function} fetchFunction Function to fetch. * @param {Function} fetchFunction Function to fetch.
* @param {any[]} fetchArguments Arguments to call the fetching. * @param {any[]} fetchArguments Arguments to call the fetching.
* @param {boolean} [forceCache=false] True to always get the value from cache, false otherwise. Default false. * @param {boolean} [omitExpires=false] True to always get the value from cache. If data isn't cached, it will call the WS.
* @param {boolean} [forceOffline=false] True to always get the value from cache. If data isn't cached, it won't call the WS.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any[]>} Promise resolved with all entrries. * @return {Promise<any[]>} Promise resolved with all entrries.
*/ */
fetchAllEntries(fetchFunction: Function, fetchArguments: any[], forceCache: boolean = false, siteId?: string): Promise<any[]> { fetchAllEntries(fetchFunction: Function, fetchArguments: any[], omitExpires: boolean = false, forceOffline: boolean = false,
siteId?: string): Promise<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const entries = []; const entries = [];
const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES; const limitNum = AddonModGlossaryProvider.LIMIT_ENTRIES;
const fetchMoreEntries = (): Promise<any[]> => { const fetchMoreEntries = (): Promise<any[]> => {
return this.fetchEntries(fetchFunction, fetchArguments, entries.length, limitNum, forceCache, siteId).then((result) => { return this.fetchEntries(fetchFunction, fetchArguments, entries.length, limitNum, omitExpires, forceOffline, siteId)
.then((result) => {
Array.prototype.push.apply(entries, result.entries); Array.prototype.push.apply(entries, result.entries);
return entries.length < result.count ? fetchMoreEntries() : entries; return entries.length < result.count ? fetchMoreEntries() : entries;
@ -633,7 +759,8 @@ export class AddonModGlossaryProvider {
const promises = []; const promises = [];
if (!onlyEntriesList) { if (!onlyEntriesList) {
promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], true, siteId).then((entries) => { promises.push(this.fetchAllEntries(this.getEntriesByLetter, [glossary.id, 'ALL'], true, false, siteId)
.then((entries) => {
return this.invalidateEntries(entries, siteId); return this.invalidateEntries(entries, siteId);
})); }));
} }
@ -644,7 +771,7 @@ export class AddonModGlossaryProvider {
promises.push(this.invalidateEntriesByLetter(glossary.id, 'ALL', siteId)); promises.push(this.invalidateEntriesByLetter(glossary.id, 'ALL', siteId));
break; break;
case 'cat': case 'cat':
promises.push(this.invalidateEntriesByCategory(glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATERGORIES, promises.push(this.invalidateEntriesByCategory(glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES,
siteId)); siteId));
break; break;
case 'date': case 'date':
@ -850,7 +977,7 @@ export class AddonModGlossaryProvider {
// If we get here, there's no offline entry with this name, check online. // If we get here, there's no offline entry with this name, check online.
// Get entries from the cache. // Get entries from the cache.
return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], true, siteId).then((entries) => { return this.fetchAllEntries(this.getEntriesByLetter, [glossaryId, 'ALL'], true, false, siteId).then((entries) => {
// Check if there's any entry with the same concept. // Check if there's any entry with the same concept.
return entries.some((entry) => entry.concept == concept); return entries.some((entry) => entry.concept == concept);
}); });
@ -906,4 +1033,44 @@ export class AddonModGlossaryProvider {
return this.logHelper.logSingle('mod_glossary_view_entry', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name, return this.logHelper.logSingle('mod_glossary_view_entry', params, AddonModGlossaryProvider.COMPONENT, glossaryId, name,
'glossary', {entryid: entryId}, siteId); 'glossary', {entryid: entryId}, siteId);
} }
/**
* Store several entries so we can determine their glossaryId in offline.
*
* @param {number} glossaryId Glossary ID the entries belongs to.
* @param {any[]} entries Entries.
* @param {number} from The "page" the entries belong to.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when done.
*/
protected storeEntries(glossaryId: number, entries: any[], from: number, siteId?: string): Promise<any> {
const promises = [];
entries.forEach((entry) => {
promises.push(this.storeEntryId(glossaryId, entry.id, from, siteId));
});
return Promise.all(promises);
}
/**
* Store an entry so we can determine its glossaryId in offline.
*
* @param {number} glossaryId Glossary ID the entry belongs to.
* @param {number} entryId Entry ID.
* @param {number} from The "page" the entry belongs to.
* @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when done.
*/
protected storeEntryId(glossaryId: number, entryId: number, from: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
const entry = {
entryid: entryId,
glossaryid: glossaryId,
pagefrom: from
};
return site.getDb().insertRecord(AddonModGlossaryProvider.ENTRIES_TABLE, entry);
});
}
} }

View File

@ -139,17 +139,17 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
break; break;
case 'cat': // Not implemented. case 'cat': // Not implemented.
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByCategory, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByCategory,
[glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATERGORIES], false, siteId)); [glossary.id, AddonModGlossaryProvider.SHOW_ALL_CATEGORIES], false, false, siteId));
break; break;
case 'date': case 'date':
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate,
[glossary.id, 'CREATION', 'DESC'], false, siteId)); [glossary.id, 'CREATION', 'DESC'], false, false, siteId));
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByDate,
[glossary.id, 'UPDATE', 'DESC'], false, siteId)); [glossary.id, 'UPDATE', 'DESC'], false, false, siteId));
break; break;
case 'author': case 'author':
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByAuthor, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByAuthor,
[glossary.id, 'ALL', 'LASTNAME', 'ASC'], false, siteId)); [glossary.id, 'ALL', 'LASTNAME', 'ASC'], false, false, siteId));
break; break;
default: default:
} }
@ -157,13 +157,12 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
// Fetch all entries to get information from. // Fetch all entries to get information from.
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter, promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter,
[glossary.id, 'ALL'], false, siteId).then((entries) => { [glossary.id, 'ALL'], false, false, siteId).then((entries) => {
const promises = []; const promises = [];
const avatars = {}; // List of user avatars, preventing duplicates. const avatars = {}; // List of user avatars, preventing duplicates.
entries.forEach((entry) => { entries.forEach((entry) => {
// Fetch individual entries. // Don't fetch individual entries, it's too many WS calls.
promises.push(this.glossaryProvider.getEntry(entry.id, siteId));
if (entry.userpictureurl) { if (entry.userpictureurl) {
avatars[entry.userpictureurl] = true; avatars[entry.userpictureurl] = true;
@ -180,6 +179,10 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
return Promise.all(promises); return Promise.all(promises);
})); }));
// Prefetch data for link handlers.
promises.push(this.courseProvider.getModuleBasicInfo(module.id, siteId));
promises.push(this.courseProvider.getModuleBasicInfoByInstance(glossary.id, 'glossary', siteId));
return Promise.all(promises); return Promise.all(promises);
}); });
} }

View File

@ -60,6 +60,12 @@ export interface CoreSiteWSPreSets {
*/ */
emergencyCache?: boolean; emergencyCache?: boolean;
/**
* If true, the app won't call the WS. If the data isn't cached, the call will fail.
* @type {boolean}
*/
forceOffline?: boolean;
/** /**
* Extra key to add to the cache when storing this call, to identify the entry. * Extra key to add to the cache when storing this call, to identify the entry.
* @type {string} * @type {string}
@ -668,7 +674,12 @@ export class CoreSite {
} }
const promise = this.getFromCache(method, data, preSets, false, originalData).catch(() => { const promise = this.getFromCache(method, data, preSets, false, originalData).catch(() => {
// Do not pass those options to the core WS factory. if (preSets.forceOffline) {
// Don't call the WS, just fail.
return Promise.reject(this.wsProvider.createFakeWSError('core.cannotconnect', true));
}
// Call the WS.
return this.callOrEnqueueRequest(method, data, preSets, wsPreSets).then((response) => { return this.callOrEnqueueRequest(method, data, preSets, wsPreSets).then((response) => {
if (preSets.saveToCache) { if (preSets.saveToCache) {
this.saveToCache(method, data, response, preSets); this.saveToCache(method, data, response, preSets);
@ -1043,7 +1054,7 @@ export class CoreSite {
const now = Date.now(); const now = Date.now();
let expirationTime; let expirationTime;
preSets.omitExpires = preSets.omitExpires || !this.appProvider.isOnline(); preSets.omitExpires = preSets.omitExpires || preSets.forceOffline || !this.appProvider.isOnline();
if (!preSets.omitExpires) { if (!preSets.omitExpires) {
let expirationDelay = this.UPDATE_FREQUENCIES[preSets.updateFrequency] || let expirationDelay = this.UPDATE_FREQUENCIES[preSets.updateFrequency] ||