Merge pull request #4212 from crazyserver/MOBILE-4616

MOBILE-4616 feedback: Fix tab selection after submit form
main
Alfonso Salces 2024-10-16 08:24:29 +02:00 committed by GitHub
commit cd848c5879
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 130 additions and 88 deletions

View File

@ -20,20 +20,13 @@
[courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()" /> [courseId]="courseId" [hasDataToSync]="hasOffline" (completionChanged)="onCompletionChange()" />
<core-tabs [hideUntil]="tabsReady" [selectedIndex]="firstSelectedTab"> <core-tabs [hideUntil]="tabsReady" [selectedIndex]="firstSelectedTab">
<core-tab [title]="'addon.mod_feedback.overview' | translate" id="overview" (ionSelect)="tabChanged('overview')"> <core-tab [title]="tabs.overview.label | translate" [id]="tabs.overview.name" (ionSelect)="tabChanged(tabs.overview.name)">
<ng-template> <ng-template>
<ng-container *ngTemplateOutlet="tabOverview" /> <ng-container *ngTemplateOutlet="tabOverview" />
</ng-template> </ng-template>
</core-tab> </core-tab>
<core-tab *ngIf="showAnalysis && access && access.canviewreports" id="analysis" [title]="'addon.mod_feedback.analysis' | translate" <core-tab *ngIf="showAnalysis && access" [id]="tabs.analysis.name" [title]="tabs.analysis.label | translate"
(ionSelect)="tabChanged('analysis')"> (ionSelect)="tabChanged(tabs.analysis.name)">
<ng-template>
<ng-container *ngTemplateOutlet="tabAnalysis" />
</ng-template>
</core-tab>
<core-tab *ngIf="showAnalysis && access && !access.canviewreports" id="analysis"
[title]="'addon.mod_feedback.completed_feedbacks' | translate" (ionSelect)="tabChanged('analysis')">
<ng-template> <ng-template>
<ng-container *ngTemplateOutlet="tabAnalysis" /> <ng-container *ngTemplateOutlet="tabAnalysis" />
</ng-template> </ng-template>
@ -101,7 +94,7 @@
<!-- Template to render the overview. --> <!-- Template to render the overview. -->
<ng-template #tabOverview> <ng-template #tabOverview>
<core-loading [hideUntil]="tabsLoaded.overview"> <core-loading [hideUntil]="tabs.overview.loaded">
<ng-container *ngTemplateOutlet="basicInfo" /> <ng-container *ngTemplateOutlet="basicInfo" />
<ion-card class="core-info-card" *ngIf="access && access.cancomplete && !access.isopen"> <ion-card class="core-info-card" *ngIf="access && access.cancomplete && !access.isopen">
@ -153,7 +146,7 @@
<!-- Template to render the analysis. --> <!-- Template to render the analysis. -->
<ng-template #tabAnalysis> <ng-template #tabAnalysis>
<core-loading [hideUntil]="tabsLoaded.analysis"> <core-loading [hideUntil]="tabs.analysis.loaded">
<ng-container *ngTemplateOutlet="basicInfo" /> <ng-container *ngTemplateOutlet="basicInfo" />
<ng-container *ngIf="access && (access.canedititems || !access.isempty)"> <ng-container *ngIf="access && (access.canedititems || !access.isempty)">

View File

@ -38,7 +38,12 @@ import {
AddonModFeedbackSyncResult, AddonModFeedbackSyncResult,
} from '../../services/feedback-sync'; } from '../../services/feedback-sync';
import { AddonModFeedbackPrefetchHandler } from '../../services/handlers/prefetch'; import { AddonModFeedbackPrefetchHandler } from '../../services/handlers/prefetch';
import { ADDON_MOD_FEEDBACK_COMPONENT, ADDON_MOD_FEEDBACK_FORM_SUBMITTED, ADDON_MOD_FEEDBACK_PAGE_NAME } from '../../constants'; import {
ADDON_MOD_FEEDBACK_COMPONENT,
ADDON_MOD_FEEDBACK_FORM_SUBMITTED,
ADDON_MOD_FEEDBACK_PAGE_NAME,
AddonModFeedbackIndexTabName,
} from '../../constants';
/** /**
* Component that displays a feedback index page. * Component that displays a feedback index page.
@ -51,7 +56,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
@ViewChild(CoreTabsComponent) tabsComponent?: CoreTabsComponent; @ViewChild(CoreTabsComponent) tabsComponent?: CoreTabsComponent;
@Input() tab = 'overview'; @Input() selectedTab: AddonModFeedbackIndexTabName = AddonModFeedbackIndexTabName.OVERVIEW;
@Input() group = 0; @Input() group = 0;
component = ADDON_MOD_FEEDBACK_COMPONENT; component = ADDON_MOD_FEEDBACK_COMPONENT;
@ -75,9 +80,9 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
closeTimeReadable: '', closeTimeReadable: '',
}; };
tabsLoaded = { tabs = {
overview: false, overview: { name: AddonModFeedbackIndexTabName.OVERVIEW, label: 'addon.mod_feedback.overview', loaded: false },
analysis: false, analysis: { name: AddonModFeedbackIndexTabName.ANALYSIS, label: 'addon.mod_feedback.analysis', loaded: false },
}; };
protected submitObserver: CoreEventObserver; protected submitObserver: CoreEventObserver;
@ -92,12 +97,12 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
// Listen for form submit events. // Listen for form submit events.
this.submitObserver = CoreEvents.on(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, async (data) => { this.submitObserver = CoreEvents.on(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, async (data) => {
if (!this.feedback || data.feedbackId != this.feedback.id) { if (!this.feedback || data.feedbackId !== this.feedback.id) {
return; return;
} }
this.tabsLoaded.analysis = false; this.tabs.analysis.loaded = false;
this.tabsLoaded.overview = false; this.tabs.overview.loaded = false;
this.showLoading = true; this.showLoading = true;
// Prefetch data if needed. // Prefetch data if needed.
@ -110,7 +115,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
} }
// Load the right tab. // Load the right tab.
if (data.tab != this.tab) { if (data.tab !== this.selectedTab) {
this.tabChanged(data.tab); this.tabChanged(data.tab);
} else { } else {
this.loadContent(true); this.loadContent(true);
@ -149,7 +154,9 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
*/ */
protected callAnalyticsLogEvent(): void { protected callAnalyticsLogEvent(): void {
this.analyticsLogEvent('mod_feedback_view_feedback', { this.analyticsLogEvent('mod_feedback_view_feedback', {
url: this.tab === 'analysis' ? `/mod/feedback/analysis.php?id=${this.module.id}` : undefined, url: this.selectedTab === AddonModFeedbackIndexTabName.ANALYSIS
? `/mod/feedback/analysis.php?id=${this.module.id}`
: undefined,
}); });
} }
@ -168,8 +175,8 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
promises.push(AddonModFeedback.invalidateResumePageData(this.feedback.id)); promises.push(AddonModFeedback.invalidateResumePageData(this.feedback.id));
} }
this.tabsLoaded.analysis = false; this.tabs.analysis.loaded = false;
this.tabsLoaded.overview = false; this.tabs.overview.loaded = false;
await Promise.all(promises); await Promise.all(promises);
} }
@ -178,7 +185,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
* @inheritdoc * @inheritdoc
*/ */
protected isRefreshSyncNeeded(syncEventData: AddonModFeedbackAutoSyncData): boolean { protected isRefreshSyncNeeded(syncEventData: AddonModFeedbackAutoSyncData): boolean {
if (this.feedback && syncEventData.feedbackId == this.feedback.id) { if (syncEventData.feedbackId === this.feedback?.id) {
// Refresh the data. // Refresh the data.
this.content?.scrollToTop(); this.content?.scrollToTop();
@ -207,12 +214,17 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback.id, { cmId: this.module.id }); this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback.id, { cmId: this.module.id });
this.showAnalysis = (this.access.canviewreports || this.access.canviewanalysis) && !this.access.isempty; this.showAnalysis = (this.access.canviewreports || this.access.canviewanalysis) && !this.access.isempty;
this.tabs.analysis.label = this.access.canviewreports
? 'addon.mod_feedback.analysis'
: 'addon.mod_feedback.completed_feedbacks';
this.firstSelectedTab = 0; this.firstSelectedTab = 0;
if (!this.showAnalysis) { if (!this.showAnalysis) {
this.tab = 'overview'; this.selectedTab = AddonModFeedbackIndexTabName.OVERVIEW;
} }
if (this.tab == 'analysis') { if (this.selectedTab === AddonModFeedbackIndexTabName.ANALYSIS) {
this.firstSelectedTab = 1; this.firstSelectedTab = 1;
return await this.fetchFeedbackAnalysisData(); return await this.fetchFeedbackAnalysisData();
@ -227,34 +239,36 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
if (this.tabsReady) { if (this.tabsReady) {
// Make sure the right tab is selected. // Make sure the right tab is selected.
this.tabsComponent?.selectTab(this.tab || 'overview'); this.tabsComponent?.selectTab(this.selectedTab ?? AddonModFeedbackIndexTabName.OVERVIEW);
} }
} }
} }
/** /**
* Convenience function to get feedback overview data. * Convenience function to get feedback overview data.
*
* @returns Resolved when done.
*/ */
protected async fetchFeedbackOverviewData(): Promise<void> { protected async fetchFeedbackOverviewData(): Promise<void> {
if (!this.access || !this.feedback) {
return;
}
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
if (this.access!.cancomplete && this.access!.cansubmit && this.access!.isopen) { if (this.access.cancomplete && this.access.cansubmit && this.access.isopen) {
promises.push(AddonModFeedback.getResumePage(this.feedback!.id, { cmId: this.module.id }).then((goPage) => { promises.push(AddonModFeedback.getResumePage(this.feedback.id, { cmId: this.module.id }).then((goPage) => {
this.goPage = goPage > 0 ? goPage : undefined; this.goPage = goPage > 0 ? goPage : undefined;
return; return;
})); }));
} }
if (this.access!.canedititems) { if (this.access.canedititems) {
this.overview.timeopen = (this.feedback!.timeopen || 0) * 1000; this.overview.timeopen = (this.feedback.timeopen || 0) * 1000;
this.overview.openTimeReadable = this.overview.timeopen ? CoreTimeUtils.userDate(this.overview.timeopen) : ''; this.overview.openTimeReadable = this.overview.timeopen ? CoreTimeUtils.userDate(this.overview.timeopen) : '';
this.overview.timeclose = (this.feedback!.timeclose || 0) * 1000; this.overview.timeclose = (this.feedback.timeclose || 0) * 1000;
this.overview.closeTimeReadable = this.overview.timeclose ? CoreTimeUtils.userDate(this.overview.timeclose) : ''; this.overview.closeTimeReadable = this.overview.timeclose ? CoreTimeUtils.userDate(this.overview.timeclose) : '';
} }
if (this.access!.canviewanalysis) { if (this.access.canviewanalysis) {
// Get groups (only for teachers). // Get groups (only for teachers).
promises.push(this.fetchGroupInfo(this.module.id)); promises.push(this.fetchGroupInfo(this.module.id));
} }
@ -262,7 +276,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
try { try {
await Promise.all(promises); await Promise.all(promises);
} finally { } finally {
this.tabsLoaded.overview = true; this.tabs.overview.loaded = true;
} }
} }
@ -273,15 +287,14 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
*/ */
protected async fetchFeedbackAnalysisData(): Promise<void> { protected async fetchFeedbackAnalysisData(): Promise<void> {
try { try {
if (this.access!.canviewanalysis) { if (this.access?.canviewanalysis) {
// Get groups (only for teachers). // Get groups (only for teachers).
await this.fetchGroupInfo(this.module.id); await this.fetchGroupInfo(this.module.id);
} else { } else {
this.tabChanged('overview'); this.tabChanged(AddonModFeedbackIndexTabName.OVERVIEW);
} }
} finally { } finally {
this.tabsLoaded.analysis = true; this.tabs.analysis.loaded = true;
} }
} }
@ -419,7 +432,7 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
* Open attempts page. * Open attempts page.
*/ */
openAttempts(): void { openAttempts(): void {
if (!this.access!.canviewreports || this.completedCount <= 0) { if (!this.access || !this.access.canviewreports || this.completedCount <= 0) {
return; return;
} }
@ -438,11 +451,11 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
* *
* @param tabName New tab name. * @param tabName New tab name.
*/ */
tabChanged(tabName: string): void { tabChanged(tabName: AddonModFeedbackIndexTabName): void {
const tabHasChanged = this.tab !== undefined && this.tab !== tabName; const tabHasChanged = this.selectedTab !== undefined && this.selectedTab !== tabName;
this.tab = tabName; this.selectedTab = tabName;
if (!this.tabsLoaded[this.tab]) { if (!this.tabs[this.selectedTab].loaded) {
this.loadContent(false, false, true); this.loadContent(false, false, true);
} }
@ -455,17 +468,20 @@ export class AddonModFeedbackIndexComponent extends CoreCourseModuleMainActivity
* Set group to see the analysis. * Set group to see the analysis.
* *
* @param groupId Group ID. * @param groupId Group ID.
* @returns Resolved when done.
*/ */
async setGroup(groupId: number): Promise<void> { async setGroup(groupId: number): Promise<void> {
if (!this.feedback) {
return;
}
this.group = groupId; this.group = groupId;
const analysis = await AddonModFeedback.getAnalysis(this.feedback!.id, { groupId, cmId: this.module.id }); const analysis = await AddonModFeedback.getAnalysis(this.feedback.id, { groupId, cmId: this.module.id });
this.completedCount = analysis.completedcount; this.completedCount = analysis.completedcount;
this.itemsCount = analysis.itemscount; this.itemsCount = analysis.itemscount;
if (this.tab == 'analysis') { if (this.selectedTab === AddonModFeedbackIndexTabName.ANALYSIS) {
let num = 1; let num = 1;
this.items = <AddonModFeedbackItem[]> analysis.itemsdata.map((itemData) => { this.items = <AddonModFeedbackItem[]> analysis.itemsdata.map((itemData) => {

View File

@ -24,3 +24,11 @@ export const ADDON_MOD_FEEDBACK_MULTICHOICE_HIDENOSELECT = 'h';
export const ADDON_MOD_FEEDBACK_MULTICHOICERATED_VALUE_SEP = '####'; export const ADDON_MOD_FEEDBACK_MULTICHOICERATED_VALUE_SEP = '####';
export const ADDON_MOD_FEEDBACK_PER_PAGE = 20; export const ADDON_MOD_FEEDBACK_PER_PAGE = 20;
/**
* Index Tabs.
*/
export enum AddonModFeedbackIndexTabName {
OVERVIEW = 'overview',
ANALYSIS = 'analysis',
}

View File

@ -36,8 +36,14 @@ import {
import { AddonModFeedbackFormItem, AddonModFeedbackHelper } from '../../services/feedback-helper'; import { AddonModFeedbackFormItem, AddonModFeedbackHelper } from '../../services/feedback-helper';
import { AddonModFeedbackSync } from '../../services/feedback-sync'; import { AddonModFeedbackSync } from '../../services/feedback-sync';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics'; import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { ADDON_MOD_FEEDBACK_COMPONENT, ADDON_MOD_FEEDBACK_FORM_SUBMITTED, ADDON_MOD_FEEDBACK_PAGE_NAME } from '../../constants'; import {
ADDON_MOD_FEEDBACK_COMPONENT,
ADDON_MOD_FEEDBACK_FORM_SUBMITTED,
ADDON_MOD_FEEDBACK_PAGE_NAME,
AddonModFeedbackIndexTabName,
} from '../../constants';
import { CoreLoadings } from '@services/loadings'; import { CoreLoadings } from '@services/loadings';
import { CoreError } from '@classes/errors/error';
/** /**
* Page that displays feedback form. * Page that displays feedback form.
@ -117,14 +123,14 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
return; return;
} }
if (!this.feedback) { if (!this.feedback || !this.module) {
return; return;
} }
try { try {
await AddonModFeedback.logView(this.feedback.id, true); await AddonModFeedback.logView(this.feedback.id, true);
CoreCourse.checkModuleCompletion(this.courseId, this.module!.completiondata); CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
} catch { } catch {
// Ignore errors. // Ignore errors.
} }
@ -183,7 +189,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
let page = 0; let page = 0;
if (!this.preview && this.access!.cansubmit && !this.access!.isempty) { if (!this.preview && this.access?.cansubmit && !this.access?.isempty) {
page = this.currentPage ?? await this.fetchResumePage(options); page = this.currentPage ?? await this.fetchResumePage(options);
} else { } else {
this.preview = true; this.preview = true;
@ -203,11 +209,14 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* Fetch access information. * Fetch access information.
* *
* @param options Options. * @param options Options.
* @returns Promise resolved when done.
*/ */
protected async fetchAccessData(options: CoreCourseCommonModWSOptions): Promise<void> { protected async fetchAccessData(options: CoreCourseCommonModWSOptions): Promise<void> {
if (!this.feedback) {
return;
}
try { try {
this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback!.id, options); this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback.id, options);
} catch (error) { } catch (error) {
if (this.offline || CoreUtils.isWebServiceError(error)) { if (this.offline || CoreUtils.isWebServiceError(error)) {
// Already offline or shouldn't go offline, fail. // Already offline or shouldn't go offline, fail.
@ -218,7 +227,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
this.offline = true; this.offline = true;
options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE; options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE;
this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback!.id, options); this.access = await AddonModFeedback.getFeedbackAccessInformation(this.feedback.id, options);
} }
} }
@ -229,8 +238,12 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* @returns Promise resolved with the page to resume. * @returns Promise resolved with the page to resume.
*/ */
protected async fetchResumePage(options: CoreCourseCommonModWSOptions): Promise<number> { protected async fetchResumePage(options: CoreCourseCommonModWSOptions): Promise<number> {
if (!this.feedback) {
throw new CoreError('Cannot fetch resume page: missing feedback');
}
try { try {
return await AddonModFeedback.getResumePage(this.feedback!.id, options); return await AddonModFeedback.getResumePage(this.feedback.id, options);
} catch (error) { } catch (error) {
if (this.offline || CoreUtils.isWebServiceError(error)) { if (this.offline || CoreUtils.isWebServiceError(error)) {
// Already offline or shouldn't go offline, fail. // Already offline or shouldn't go offline, fail.
@ -241,7 +254,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
this.offline = true; this.offline = true;
options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE; options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE;
return AddonModFeedback.getResumePage(this.feedback!.id, options); return AddonModFeedback.getResumePage(this.feedback.id, options);
} }
} }
@ -249,7 +262,6 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* Fetch page data. * Fetch page data.
* *
* @param page Page to load. * @param page Page to load.
* @returns Promise resolved when done.
*/ */
protected async fetchFeedbackPageData(page: number = 0): Promise<void> { protected async fetchFeedbackPageData(page: number = 0): Promise<void> {
this.items = []; this.items = [];
@ -274,6 +286,10 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* @returns Promise resolved with WS response. * @returns Promise resolved with WS response.
*/ */
protected async fetchPageItems(page: number): Promise<AddonModFeedbackPageItems> { protected async fetchPageItems(page: number): Promise<AddonModFeedbackPageItems> {
if (!this.feedback) {
throw new CoreError('Cannot fetch page items: missing feedback');
}
const options = { const options = {
cmId: this.cmId, cmId: this.cmId,
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK, readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
@ -281,7 +297,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
}; };
if (this.preview) { if (this.preview) {
const response = await AddonModFeedback.getItems(this.feedback!.id, options); const response = await AddonModFeedback.getItems(this.feedback.id, options);
return { return {
items: response.items, items: response.items,
@ -295,7 +311,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
let response: AddonModFeedbackPageItems; let response: AddonModFeedbackPageItems;
try { try {
response = await AddonModFeedback.getPageItemsWithValues(this.feedback!.id, page, options); response = await AddonModFeedback.getPageItemsWithValues(this.feedback.id, page, options);
} catch (error) { } catch (error) {
if (this.offline || CoreUtils.isWebServiceError(error)) { if (this.offline || CoreUtils.isWebServiceError(error)) {
// Already offline or shouldn't go offline, fail. // Already offline or shouldn't go offline, fail.
@ -306,7 +322,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
this.offline = true; this.offline = true;
options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE; options.readingStrategy = CoreSitesReadingStrategy.PREFER_CACHE;
response = await AddonModFeedback.getPageItemsWithValues(this.feedback!.id, page, options); response = await AddonModFeedback.getPageItemsWithValues(this.feedback.id, page, options);
} }
this.hasPrevPage = !!response.hasprevpage; this.hasPrevPage = !!response.hasprevpage;
@ -319,9 +335,12 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* Function to allow page navigation through the questions form. * Function to allow page navigation through the questions form.
* *
* @param goPrevious If true it will go back to the previous page, if false, it will go forward. * @param goPrevious If true it will go back to the previous page, if false, it will go forward.
* @returns Resolved when done.
*/ */
async gotoPage(goPrevious: boolean): Promise<void> { async gotoPage(goPrevious: boolean): Promise<void> {
if (!this.feedback || this.currentPage === undefined) {
return;
}
this.content?.scrollToTop(); this.content?.scrollToTop();
this.feedbackLoaded = false; this.feedbackLoaded = false;
@ -330,9 +349,9 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
try { try {
// Sync other pages first. // Sync other pages first.
await CoreUtils.ignoreErrors(AddonModFeedbackSync.syncFeedback(this.feedback!.id)); await CoreUtils.ignoreErrors(AddonModFeedbackSync.syncFeedback(this.feedback.id));
const response = await AddonModFeedback.processPage(this.feedback!.id, this.currentPage!, responses, { const response = await AddonModFeedback.processPage(this.feedback.id, this.currentPage, responses, {
goPrevious, goPrevious,
formHasErrors, formHasErrors,
courseId: this.courseId, courseId: this.courseId,
@ -351,14 +370,14 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
// Invalidate access information so user will see home page updated (continue form or completion messages). // Invalidate access information so user will see home page updated (continue form or completion messages).
await Promise.all([ await Promise.all([
AddonModFeedback.invalidateFeedbackAccessInformationData(this.feedback!.id), AddonModFeedback.invalidateFeedbackAccessInformationData(this.feedback.id),
AddonModFeedback.invalidateResumePageData(this.feedback!.id), AddonModFeedback.invalidateResumePageData(this.feedback.id),
]); ]);
// If form has been submitted, the info has been already invalidated but we should update index view. // If form has been submitted, the info has been already invalidated but we should update index view.
CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, { CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, {
feedbackId: this.feedback!.id, feedbackId: this.feedback.id,
tab: 'overview', tab: AddonModFeedbackIndexTabName.OVERVIEW,
offline: this.completedOffline, offline: this.completedOffline,
}); });
@ -371,11 +390,11 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
// Errors on questions, stay in page. // Errors on questions, stay in page.
} else { } else {
// Invalidate access information so user will see home page updated (continue form). // Invalidate access information so user will see home page updated (continue form).
await AddonModFeedback.invalidateResumePageData(this.feedback!.id); await AddonModFeedback.invalidateResumePageData(this.feedback.id);
CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, { CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, {
feedbackId: this.feedback!.id, feedbackId: this.feedback.id,
tab: 'overview', tab: AddonModFeedbackIndexTabName.OVERVIEW,
offline: this.completedOffline, offline: this.completedOffline,
}); });
@ -393,11 +412,15 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
* Function to link implemented features. * Function to link implemented features.
*/ */
showAnalysis(): void { showAnalysis(): void {
if (!this.feedback) {
return;
}
if (this.fromIndex) { if (this.fromIndex) {
// Previous page is the index page, go back. // Previous page is the index page, go back.
CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, { CoreEvents.trigger(ADDON_MOD_FEEDBACK_FORM_SUBMITTED, {
feedbackId: this.feedback!.id, feedbackId: this.feedback.id,
tab: 'analysis', tab: AddonModFeedbackIndexTabName.ANALYSIS,
offline: this.completedOffline, offline: this.completedOffline,
}); });
@ -409,7 +432,7 @@ export class AddonModFeedbackFormPage implements OnInit, OnDestroy, CanLeave {
CoreNavigator.navigateToSitePath(ADDON_MOD_FEEDBACK_PAGE_NAME + `/${this.courseId}/${this.cmId}`, { CoreNavigator.navigateToSitePath(ADDON_MOD_FEEDBACK_PAGE_NAME + `/${this.courseId}/${this.cmId}`, {
params: { params: {
module: this.module, module: this.module,
tab: 'analysis', tab: AddonModFeedbackIndexTabName.ANALYSIS,
}, },
}); });
} }

View File

@ -16,6 +16,6 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" /> <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}" />
</ion-refresher> </ion-refresher>
<addon-mod-feedback-index [module]="module" [courseId]="courseId" [group]="selectedGroup" [tab]="selectedTab" <addon-mod-feedback-index [module]="module" [courseId]="courseId" [group]="selectedGroup" [selectedTab]="selectedTab"
(dataRetrieved)="updateData($event)" /> (dataRetrieved)="updateData($event)" />
</ion-content> </ion-content>

View File

@ -16,6 +16,7 @@ import { Component, OnInit, ViewChild } from '@angular/core';
import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page'; import { CoreCourseModuleMainActivityPage } from '@features/course/classes/main-activity-page';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { AddonModFeedbackIndexComponent } from '../../components/index/index'; import { AddonModFeedbackIndexComponent } from '../../components/index/index';
import { AddonModFeedbackIndexTabName } from '../../constants';
/** /**
* Page that displays a feedback. * Page that displays a feedback.
@ -28,7 +29,7 @@ export class AddonModFeedbackIndexPage extends CoreCourseModuleMainActivityPage<
@ViewChild(AddonModFeedbackIndexComponent) activityComponent?: AddonModFeedbackIndexComponent; @ViewChild(AddonModFeedbackIndexComponent) activityComponent?: AddonModFeedbackIndexComponent;
selectedTab?: string; selectedTab?: AddonModFeedbackIndexTabName;
selectedGroup?: number; selectedGroup?: number;
/** /**

View File

@ -34,6 +34,7 @@ import {
ADDON_MOD_FEEDBACK_MULTICHOICE_TYPE_SEP, ADDON_MOD_FEEDBACK_MULTICHOICE_TYPE_SEP,
ADDON_MOD_FEEDBACK_MULTICHOICERATED_VALUE_SEP, ADDON_MOD_FEEDBACK_MULTICHOICERATED_VALUE_SEP,
ADDON_MOD_FEEDBACK_PER_PAGE, ADDON_MOD_FEEDBACK_PER_PAGE,
AddonModFeedbackIndexTabName,
} from '../constants'; } from '../constants';
/** /**
@ -1263,7 +1264,7 @@ declare module '@singletons/events' {
*/ */
export type AddonModFeedbackFormSubmittedData = { export type AddonModFeedbackFormSubmittedData = {
feedbackId: number; feedbackId: number;
tab: string; tab: AddonModFeedbackIndexTabName;
offline: boolean; offline: boolean;
}; };

View File

@ -19,7 +19,7 @@ import { CoreCourse } from '@features/course/services/course';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { ADDON_MOD_FEEDBACK_PAGE_NAME } from '../../constants'; import { ADDON_MOD_FEEDBACK_PAGE_NAME, AddonModFeedbackIndexTabName } from '../../constants';
import { CoreLoadings } from '@services/loadings'; import { CoreLoadings } from '@services/loadings';
/** /**
@ -59,7 +59,7 @@ export class AddonModFeedbackAnalysisLinkHandlerService extends CoreContentLinks
{ {
params: { params: {
module, module,
tab: 'analysis', tab: AddonModFeedbackIndexTabName.ANALYSIS,
}, },
siteId, siteId,
}, },

View File

@ -221,7 +221,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
this.swiper.update(); this.swiper.update();
await CoreWait.nextTick(); await CoreWait.nextTick();
if (!this.hasSliddenToInitial && this.selectedIndex && this.selectedIndex >= this.swiper.slidesPerViewDynamic()) { if (!this.hasSliddenToInitial && this.selectedIndex >= this.swiper.slidesPerViewDynamic()) {
this.hasSliddenToInitial = true; this.hasSliddenToInitial = true;
this.shouldSlideToInitial = true; this.shouldSlideToInitial = true;
@ -233,7 +233,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
}, 400); }, 400);
return; return;
} else if (this.selectedIndex) { } else {
this.hasSliddenToInitial = true; this.hasSliddenToInitial = true;
} }
@ -301,21 +301,21 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
* @returns Initial tab, undefined if no valid tab found. * @returns Initial tab, undefined if no valid tab found.
*/ */
protected calculateInitialTab(): T | undefined { protected calculateInitialTab(): T | undefined {
const selectedTab: T | undefined = this.tabs[this.selectedIndex || 0] || undefined; const selectedTab: T | undefined = this.tabs[this.selectedIndex] || undefined;
if (selectedTab && selectedTab.enabled) { if (selectedTab?.enabled) {
return selectedTab; return selectedTab;
} }
// The tab is not enabled or not shown. Get the first tab that is enabled. // The tab is not enabled or not shown. Get the first tab that is enabled.
return this.tabs.find((tab) => tab.enabled) || undefined; return this.tabs.find((tab) => tab.enabled);
} }
/** /**
* Method executed when the slides are changed. * Method executed when the slides are changed.
*/ */
slideChanged(): void { slideChanged(): void {
if (!this.swiper) { if (!this.swiper || this.swiper.destroyed) {
return; return;
} }
@ -340,7 +340,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
* Calculate the number of slides that can fit on the screen. * Calculate the number of slides that can fit on the screen.
*/ */
protected async calculateMaxSlides(): Promise<void> { protected async calculateMaxSlides(): Promise<void> {
if (!this.swiper) { if (!this.swiper || this.swiper.destroyed) {
return; return;
} }
@ -456,7 +456,7 @@ export class CoreTabsBaseComponent<T extends CoreTabBase> implements AfterViewIn
return; return;
} }
if (this.selected && this.swiper) { if (this.selected && this.swiper && !this.swiper.destroyed) {
// Check if we need to slide to the tab because it's not visible. // Check if we need to slide to the tab because it's not visible.
const firstVisibleTab = this.swiper.activeIndex; const firstVisibleTab = this.swiper.activeIndex;
const lastVisibleTab = firstVisibleTab + this.swiper.slidesPerViewDynamic() - 1; const lastVisibleTab = firstVisibleTab + this.swiper.slidesPerViewDynamic() - 1;