commit
514bc2bcdd
|
@ -87,7 +87,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
protected isInTransition = false; // Weather Slides is in transition.
|
protected isInTransition = false; // Weather Slides is in transition.
|
||||||
protected slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
protected slidesSwiper: any; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||||
protected slidesSwiperLoaded = false;
|
protected slidesSwiperLoaded = false;
|
||||||
protected scrollListenersSet: Record<string | number, boolean> = {}; // Prevent setting listeners twice.
|
protected scrollElements: Record<string | number, HTMLElement> = {}; // Scroll elements for each loaded tab.
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected element: ElementRef,
|
protected element: ElementRef,
|
||||||
|
@ -444,11 +444,11 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
/**
|
/**
|
||||||
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
||||||
*
|
*
|
||||||
* @param scrollEvent Scroll event to check scroll position.
|
* @param scrollTop Scroll top.
|
||||||
* @param content Content element to check measures.
|
* @param scrollElement Content scroll element to check measures.
|
||||||
*/
|
*/
|
||||||
showHideTabs(scrollEvent: CustomEvent, content: HTMLElement): void {
|
showHideTabs(scrollTop: number, scrollElement: HTMLElement): void {
|
||||||
if (!this.tabBarElement || !this.tabsElement || !content) {
|
if (!this.tabBarElement || !this.tabsElement || !scrollElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,8 +467,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scroll = parseInt(scrollEvent.detail.scrollTop, 10);
|
if (scrollTop <= 0) {
|
||||||
if (scroll <= 0) {
|
|
||||||
// Ensure tabbar is shown.
|
// Ensure tabbar is shown.
|
||||||
this.tabsElement.style.top = '0';
|
this.tabsElement.style.top = '0';
|
||||||
this.tabsElement.style.height = '';
|
this.tabsElement.style.height = '';
|
||||||
|
@ -479,30 +478,30 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scroll == this.lastScroll) {
|
if (scrollTop == this.lastScroll) {
|
||||||
// Ensure scroll has been modified to avoid flicks.
|
// Ensure scroll has been modified to avoid flicks.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tabsShown && scroll > this.tabBarHeight) {
|
if (this.tabsShown && scrollTop > this.tabBarHeight) {
|
||||||
this.tabsShown = false;
|
this.tabsShown = false;
|
||||||
|
|
||||||
// Hide tabs.
|
// Hide tabs.
|
||||||
this.tabBarElement.classList.add('tabs-hidden');
|
this.tabBarElement.classList.add('tabs-hidden');
|
||||||
this.tabsElement.style.top = '0';
|
this.tabsElement.style.top = '0';
|
||||||
this.tabsElement.style.height = '';
|
this.tabsElement.style.height = '';
|
||||||
} else if (!this.tabsShown && scroll <= this.tabBarHeight) {
|
} else if (!this.tabsShown && scrollTop <= this.tabBarHeight) {
|
||||||
this.tabsShown = true;
|
this.tabsShown = true;
|
||||||
this.tabBarElement.classList.remove('tabs-hidden');
|
this.tabBarElement.classList.remove('tabs-hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.tabsShown && content.scrollHeight > content.clientHeight + (this.tabBarHeight - scroll)) {
|
if (this.tabsShown && scrollElement.scrollHeight > scrollElement.clientHeight + (this.tabBarHeight - scrollTop)) {
|
||||||
// Smooth translation.
|
// Smooth translation.
|
||||||
this.tabsElement.style.top = - scroll + 'px';
|
this.tabsElement.style.top = - scrollTop + 'px';
|
||||||
this.tabsElement.style.height = 'calc(100% + ' + scroll + 'px';
|
this.tabsElement.style.height = 'calc(100% + ' + scrollTop + 'px';
|
||||||
}
|
}
|
||||||
// Use lastScroll after moving the tabs to avoid flickering.
|
// Use lastScroll after moving the tabs to avoid flickering.
|
||||||
this.lastScroll = parseInt(scrollEvent.detail.scrollTop, 10);
|
this.lastScroll = scrollTop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -546,7 +545,12 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
await this.slides!.slideTo(index);
|
// Check if we need to slide to the tab because it's not visible.
|
||||||
|
const firstVisibleTab = await this.slides!.getActiveIndex();
|
||||||
|
const lastVisibleTab = firstVisibleTab + this.slidesOpts.slidesPerView - 1;
|
||||||
|
if (index < firstVisibleTab || index > lastVisibleTab) {
|
||||||
|
await this.slides!.slideTo(index, 0, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ok = await this.loadTab(tabToSelect);
|
const ok = await this.loadTab(tabToSelect);
|
||||||
|
@ -580,17 +584,28 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements OnInit, Aft
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async listenContentScroll(element: HTMLElement, id: number | string): Promise<void> {
|
async listenContentScroll(element: HTMLElement, id: number | string): Promise<void> {
|
||||||
const content = element.querySelector('ion-content');
|
if (this.scrollElements[id]) {
|
||||||
|
return; // Already set.
|
||||||
|
}
|
||||||
|
|
||||||
if (!content || this.scrollListenersSet[id]) {
|
let content = element.querySelector('ion-content');
|
||||||
|
if (!content) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search the inner ion-content if there's more than one.
|
||||||
|
let childContent = content.querySelector('ion-content') || null;
|
||||||
|
while (childContent != null) {
|
||||||
|
content = childContent;
|
||||||
|
childContent = content.querySelector('ion-content') || null;
|
||||||
|
}
|
||||||
|
|
||||||
const scroll = await content.getScrollElement();
|
const scroll = await content.getScrollElement();
|
||||||
|
|
||||||
content.scrollEvents = true;
|
content.scrollEvents = true;
|
||||||
this.scrollListenersSet[id] = true;
|
this.scrollElements[id] = scroll;
|
||||||
content.addEventListener('ionScroll', (e: CustomEvent): void => {
|
content.addEventListener('ionScroll', (e: CustomEvent): void => {
|
||||||
this.showHideTabs(e, scroll);
|
this.showHideTabs(parseInt(e.detail.scrollTop, 10), scroll);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,15 @@ export class CoreTabsOutletComponent extends CoreTabsBaseComponent<CoreTabsOutle
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listenContentScroll(stackEvent.enteringView.element, stackEvent.enteringView.id);
|
|
||||||
this.showHideNavBarButtons(stackEvent.enteringView.element.tagName);
|
this.showHideNavBarButtons(stackEvent.enteringView.element.tagName);
|
||||||
|
|
||||||
|
await this.listenContentScroll(stackEvent.enteringView.element, stackEvent.enteringView.id);
|
||||||
|
|
||||||
|
const scrollElement = this.scrollElements[stackEvent.enteringView.id];
|
||||||
|
if (scrollElement) {
|
||||||
|
// Show or hide tabs based on the new page scroll.
|
||||||
|
this.showHideTabs(scrollElement.scrollTop, scrollElement);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ function buildRoutes(injector: Injector): Routes {
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: CoreCourseIndexPage,
|
component: CoreCourseIndexPage,
|
||||||
|
data: {
|
||||||
|
isCourseIndex: true,
|
||||||
|
},
|
||||||
children: routes.children,
|
children: routes.children,
|
||||||
},
|
},
|
||||||
...routes.siblings,
|
...routes.siblings,
|
||||||
|
|
|
@ -71,7 +71,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
const index = this.tabs.findIndex((tab) => tab.name == data.name);
|
const index = this.tabs.findIndex((tab) => tab.name == data.name);
|
||||||
|
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
this.tabsComponent?.selectByIndex(index + 1);
|
this.tabsComponent?.selectByIndex(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -129,11 +129,9 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
// Load the course handlers.
|
// Load the course handlers.
|
||||||
const handlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(this.course!, false, false);
|
const handlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(this.course!, false, false);
|
||||||
|
|
||||||
this.tabs = [...this.tabs, ...handlers.map(handler => handler.data)];
|
|
||||||
|
|
||||||
let tabToLoad: number | undefined;
|
let tabToLoad: number | undefined;
|
||||||
|
|
||||||
// Add the courseId to the handler component data.
|
// Create the full path.
|
||||||
handlers.forEach((handler, index) => {
|
handlers.forEach((handler, index) => {
|
||||||
handler.data.page = CoreTextUtils.concatenatePaths(this.currentPagePath, handler.data.page);
|
handler.data.page = CoreTextUtils.concatenatePaths(this.currentPagePath, handler.data.page);
|
||||||
handler.data.pageParams = handler.data.pageParams || {};
|
handler.data.pageParams = handler.data.pageParams || {};
|
||||||
|
@ -144,6 +142,11 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.tabs = [...this.tabs, ...handlers.map(handler => ({
|
||||||
|
...handler.data,
|
||||||
|
name: handler.name,
|
||||||
|
}))];
|
||||||
|
|
||||||
// Select the tab if needed.
|
// Select the tab if needed.
|
||||||
this.firstTabName = undefined;
|
this.firstTabName = undefined;
|
||||||
if (tabToLoad) {
|
if (tabToLoad) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ import { CoreCourseLogCronHandler } from './handlers/log-cron';
|
||||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
|
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
|
||||||
import { CoreCourseAutoSyncData, CoreCourseSyncProvider } from './sync';
|
import { CoreCourseAutoSyncData, CoreCourseSyncProvider } from './sync';
|
||||||
import { CoreTagItem } from '@features/tag/services/tag';
|
import { CoreTagItem } from '@features/tag/services/tag';
|
||||||
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'mmCourse:';
|
const ROOT_CACHE_KEY = 'mmCourse:';
|
||||||
|
|
||||||
|
@ -176,10 +177,14 @@ export class CoreCourseProvider {
|
||||||
* @param courseId Course ID.
|
* @param courseId Course ID.
|
||||||
* @return Whether the current view is a certain course.
|
* @return Whether the current view is a certain course.
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
currentViewIsCourse(courseId: number): boolean {
|
currentViewIsCourse(courseId: number): boolean {
|
||||||
// @todo implement
|
const route = CoreNavigator.getCurrentRoute({ routeData: { isCourseIndex: true } });
|
||||||
return false;
|
|
||||||
|
if (!route) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Number(route.snapshot.params.courseId) == courseId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { CoreError } from '@classes/errors/error';
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
import { CoreCourseHelper } from '@features/course/services/course-helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features regarding grades information.
|
* Service that provides some features regarding grades information.
|
||||||
|
@ -458,14 +459,13 @@ export class CoreGradesHelperProvider {
|
||||||
siteId?: string,
|
siteId?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const modal = await CoreDomUtils.showModalLoading();
|
const modal = await CoreDomUtils.showModalLoading();
|
||||||
let currentUserId: number;
|
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
siteId = site.id;
|
||||||
|
const currentUserId = site.getUserId();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const site = await CoreSites.getSite(siteId);
|
|
||||||
|
|
||||||
siteId = site.id;
|
|
||||||
currentUserId = site.getUserId();
|
|
||||||
|
|
||||||
if (!moduleId) {
|
if (!moduleId) {
|
||||||
throw new CoreError('Invalid moduleId');
|
throw new CoreError('Invalid moduleId');
|
||||||
}
|
}
|
||||||
|
@ -477,27 +477,27 @@ export class CoreGradesHelperProvider {
|
||||||
throw new CoreError('No grades found.');
|
throw new CoreError('No grades found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can get grades. Do it.
|
||||||
|
const items = await CoreGrades.getGradeItems(courseId, userId, undefined, siteId);
|
||||||
|
|
||||||
|
// Find the item of the module.
|
||||||
|
const item = Array.isArray(items) && items.find((item) => moduleId == item.cmid);
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
throw new CoreError('Grade item not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open the item directly.
|
||||||
|
const gradeId = item.id;
|
||||||
|
|
||||||
|
await CoreUtils.ignoreErrors(
|
||||||
|
CoreNavigator.navigateToSitePath(`/grades/${courseId}/${gradeId}`, {
|
||||||
|
siteId,
|
||||||
|
params: { userId },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
try {
|
try {
|
||||||
// Can get grades. Do it.
|
|
||||||
const items = await CoreGrades.getGradeItems(courseId, userId, undefined, siteId);
|
|
||||||
|
|
||||||
// Find the item of the module.
|
|
||||||
const item = Array.isArray(items) && items.find((item) => moduleId == item.cmid);
|
|
||||||
|
|
||||||
if (!item) {
|
|
||||||
throw new CoreError('Grade item not found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open the item directly.
|
|
||||||
const gradeId = item.id;
|
|
||||||
|
|
||||||
await CoreUtils.ignoreErrors(
|
|
||||||
CoreNavigator.navigateToSitePath(`/grades/${courseId}/${gradeId}`, {
|
|
||||||
siteId,
|
|
||||||
params: { userId },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
// Cannot get grade items or there's no need to.
|
// Cannot get grade items or there's no need to.
|
||||||
if (userId && userId != currentUserId) {
|
if (userId && userId != currentUserId) {
|
||||||
// View another user grades. Open the grades page directly.
|
// View another user grades. Open the grades page directly.
|
||||||
|
@ -517,24 +517,12 @@ export class CoreGradesHelperProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo
|
|
||||||
// Open the course with the grades tab selected.
|
// Open the course with the grades tab selected.
|
||||||
// await CoreCourseHelper.getCourse(courseId, siteId).then(async (result) => {
|
await CoreCourseHelper.getAndOpenCourse(courseId, { selectedTab: 'CoreGrades' }, siteId);
|
||||||
// const pageParams = {
|
} catch (error) {
|
||||||
// course: result.course,
|
// Cannot get course for some reason, just open the grades page.
|
||||||
// selectedTab: 'CoreGrades',
|
await CoreNavigator.navigateToSitePath(`/grades/${courseId}`, { siteId });
|
||||||
// };
|
|
||||||
|
|
||||||
// // CoreContentLinksHelper.goInSite(navCtrl, 'CoreCourseSectionPage', pageParams, siteId)
|
|
||||||
// return await CoreUtils.ignoreErrors(CoreNavigator.navigateToSitePath('/course', {
|
|
||||||
// siteId,
|
|
||||||
// params: pageParams,
|
|
||||||
// }));
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
// Cannot get course for some reason, just open the grades page.
|
|
||||||
await CoreNavigator.navigateToSitePath(`/grades/${courseId}`, { siteId });
|
|
||||||
} finally {
|
} finally {
|
||||||
modal.dismiss();
|
modal.dismiss();
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ export type CoreNavigatorCurrentRouteOptions = Partial<{
|
||||||
params: Params; // Params to get the value from.
|
params: Params; // Params to get the value from.
|
||||||
route: ActivatedRoute; // Current Route.
|
route: ActivatedRoute; // Current Route.
|
||||||
pageComponent: unknown;
|
pageComponent: unknown;
|
||||||
|
routeData: Record<string, unknown>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,18 +402,22 @@ export class CoreNavigatorService {
|
||||||
*/
|
*/
|
||||||
getCurrentRoute(): ActivatedRoute;
|
getCurrentRoute(): ActivatedRoute;
|
||||||
getCurrentRoute(options: CoreNavigatorCurrentRouteOptions): ActivatedRoute | null;
|
getCurrentRoute(options: CoreNavigatorCurrentRouteOptions): ActivatedRoute | null;
|
||||||
getCurrentRoute({ route, pageComponent }: CoreNavigatorCurrentRouteOptions = {}): ActivatedRoute | null {
|
getCurrentRoute({ route, pageComponent, routeData }: CoreNavigatorCurrentRouteOptions = {}): ActivatedRoute | null {
|
||||||
route = route ?? Router.routerState.root;
|
route = route ?? Router.routerState.root;
|
||||||
|
|
||||||
if (pageComponent && route.component === pageComponent) {
|
if (pageComponent && route.component === pageComponent) {
|
||||||
return route;
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (route.firstChild) {
|
if (routeData && CoreUtils.basicLeftCompare(routeData, route.snapshot.data, 3)) {
|
||||||
return this.getCurrentRoute({ route: route.firstChild, pageComponent });
|
return route;
|
||||||
}
|
}
|
||||||
|
|
||||||
return pageComponent ? null : route;
|
if (route.firstChild) {
|
||||||
|
return this.getCurrentRoute({ route: route.firstChild, pageComponent, routeData });
|
||||||
|
}
|
||||||
|
|
||||||
|
return pageComponent || routeData ? null : route;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -176,7 +176,7 @@ export class CoreUtilsProvider {
|
||||||
maxLevels: number = 0,
|
maxLevels: number = 0,
|
||||||
level: number = 0,
|
level: number = 0,
|
||||||
undefinedIsNull: boolean = true,
|
undefinedIsNull: boolean = true,
|
||||||
): boolean | undefined {
|
): boolean {
|
||||||
if (typeof itemA == 'function' || typeof itemB == 'function') {
|
if (typeof itemA == 'function' || typeof itemB == 'function') {
|
||||||
return true; // Don't compare functions.
|
return true; // Don't compare functions.
|
||||||
} else if (typeof itemA == 'object' && typeof itemB == 'object') {
|
} else if (typeof itemA == 'object' && typeof itemB == 'object') {
|
||||||
|
@ -189,7 +189,7 @@ export class CoreUtilsProvider {
|
||||||
const value = itemA[name];
|
const value = itemA[name];
|
||||||
if (name == '$$hashKey') {
|
if (name == '$$hashKey') {
|
||||||
// Ignore $$hashKey property since it's a "calculated" property.
|
// Ignore $$hashKey property since it's a "calculated" property.
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.basicLeftCompare(value, itemB[name], maxLevels, level + 1)) {
|
if (!this.basicLeftCompare(value, itemB[name], maxLevels, level + 1)) {
|
||||||
|
|
Loading…
Reference in New Issue