forked from EVOgeek/Vmeda.Online
347 lines
13 KiB
TypeScript
347 lines
13 KiB
TypeScript
// (C) Copyright 2015 Martin Dougiamas
|
|
//
|
|
// 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 { Component, OnDestroy, Optional, NgZone } from '@angular/core';
|
|
import { IonicPage, NavParams, NavController, Content } from 'ionic-angular';
|
|
import { Network } from '@ionic-native/network';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { AddonModFeedbackProvider } from '../../providers/feedback';
|
|
import { AddonModFeedbackHelperProvider } from '../../providers/helper';
|
|
import { AddonModFeedbackSyncProvider } from '../../providers/sync';
|
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
|
import { CoreAppProvider } from '@providers/app';
|
|
import { CoreEventsProvider } from '@providers/events';
|
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
|
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
|
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
|
import { CoreSitesProvider } from '@providers/sites';
|
|
|
|
/**
|
|
* Page that displays feedback form.
|
|
*/
|
|
@IonicPage({ segment: 'addon-mod-feedback-form' })
|
|
@Component({
|
|
selector: 'page-addon-mod-feedback-form',
|
|
templateUrl: 'form.html',
|
|
})
|
|
export class AddonModFeedbackFormPage implements OnDestroy {
|
|
|
|
protected module: any;
|
|
protected currentPage: number;
|
|
protected submitted: any;
|
|
protected feedback;
|
|
protected siteAfterSubmit;
|
|
protected onlineObserver;
|
|
protected originalData;
|
|
protected currentSite;
|
|
protected forceLeave = false;
|
|
|
|
title: string;
|
|
preview = false;
|
|
courseId: number;
|
|
componentId: number;
|
|
completionPageContents: string;
|
|
component = AddonModFeedbackProvider.COMPONENT;
|
|
offline = false;
|
|
feedbackLoaded = false;
|
|
access: any;
|
|
items = [];
|
|
hasPrevPage = false;
|
|
hasNextPage = false;
|
|
completed = false;
|
|
completedOffline = false;
|
|
|
|
constructor(navParams: NavParams, protected feedbackProvider: AddonModFeedbackProvider, protected appProvider: CoreAppProvider,
|
|
protected utils: CoreUtilsProvider, protected domUtils: CoreDomUtilsProvider, protected navCtrl: NavController,
|
|
protected feedbackHelper: AddonModFeedbackHelperProvider, protected courseProvider: CoreCourseProvider,
|
|
protected eventsProvider: CoreEventsProvider, protected feedbackSync: AddonModFeedbackSyncProvider, network: Network,
|
|
protected translate: TranslateService, protected loginHelper: CoreLoginHelperProvider,
|
|
protected linkHelper: CoreContentLinksHelperProvider, sitesProvider: CoreSitesProvider,
|
|
@Optional() private content: Content, zone: NgZone) {
|
|
|
|
this.module = navParams.get('module');
|
|
this.courseId = navParams.get('courseId');
|
|
this.currentPage = navParams.get('page');
|
|
this.title = navParams.get('title');
|
|
this.preview = !!navParams.get('preview');
|
|
this.componentId = navParams.get('moduleId') || this.module.id;
|
|
|
|
this.currentSite = sitesProvider.getCurrentSite();
|
|
|
|
// Refresh online status when changes.
|
|
this.onlineObserver = network.onchange().subscribe((online) => {
|
|
// Execute the callback in the Angular zone, so change detection doesn't stop working.
|
|
zone.run(() => {
|
|
this.offline = !online;
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* View loaded.
|
|
*/
|
|
ionViewDidLoad(): void {
|
|
this.fetchData().then(() => {
|
|
this.feedbackProvider.logView(this.feedback.id, true).then(() => {
|
|
this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus);
|
|
}).catch(() => {
|
|
// Ignore errors.
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* View entered.
|
|
*/
|
|
ionViewDidEnter(): void {
|
|
this.forceLeave = false;
|
|
}
|
|
|
|
/**
|
|
* Check if we can leave the page or not.
|
|
*
|
|
* @return {boolean | Promise<void>} Resolved if we can leave it, rejected if not.
|
|
*/
|
|
ionViewCanLeave(): boolean | Promise<void> {
|
|
if (this.forceLeave) {
|
|
return true;
|
|
}
|
|
|
|
if (!this.preview) {
|
|
const responses = this.feedbackHelper.getPageItemsResponses(this.items);
|
|
|
|
if (this.items && !this.completed && this.originalData) {
|
|
// Form submitted. Check if there is any change.
|
|
if (!this.utils.basicLeftCompare(responses, this.originalData, 3)) {
|
|
return this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
|
|
}
|
|
}
|
|
}
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
/**
|
|
* Fetch all the data required for the view.
|
|
*
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
*/
|
|
protected fetchData(): Promise<any> {
|
|
this.offline = !this.appProvider.isOnline();
|
|
|
|
return this.feedbackProvider.getFeedback(this.courseId, this.module.id).then((feedbackData) => {
|
|
this.feedback = feedbackData;
|
|
|
|
this.title = this.feedback.name || this.title;
|
|
|
|
return this.fetchAccessData();
|
|
}).then((accessData) => {
|
|
if (!this.preview && accessData.cansubmit && !accessData.isempty) {
|
|
return typeof this.currentPage == 'undefined' ?
|
|
this.feedbackProvider.getResumePage(this.feedback.id, this.offline, true) :
|
|
Promise.resolve(this.currentPage);
|
|
} else {
|
|
this.preview = true;
|
|
|
|
return Promise.resolve(0);
|
|
}
|
|
}).catch((error) => {
|
|
if (!this.offline && !this.utils.isWebServiceError(error)) {
|
|
// If it fails, go offline.
|
|
this.offline = true;
|
|
|
|
return this.feedbackProvider.getResumePage(this.feedback.id, true);
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}).then((page) => {
|
|
return this.fetchFeedbackPageData(page || 0);
|
|
}).catch((message) => {
|
|
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
|
this.forceLeave = true;
|
|
this.navCtrl.pop();
|
|
|
|
return Promise.reject(null);
|
|
}).finally(() => {
|
|
this.feedbackLoaded = true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Fetch access information.
|
|
*
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
*/
|
|
protected fetchAccessData(): Promise<any> {
|
|
return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, this.offline, true).catch((error) => {
|
|
if (!this.offline && !this.utils.isWebServiceError(error)) {
|
|
// If it fails, go offline.
|
|
this.offline = true;
|
|
|
|
return this.feedbackProvider.getFeedbackAccessInformation(this.feedback.id, true);
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}).then((accessData) => {
|
|
this.access = accessData;
|
|
|
|
return accessData;
|
|
});
|
|
}
|
|
|
|
protected fetchFeedbackPageData(page: number = 0): Promise<void> {
|
|
let promise;
|
|
this.items = [];
|
|
|
|
if (this.preview) {
|
|
promise = this.feedbackProvider.getItems(this.feedback.id);
|
|
} else {
|
|
this.currentPage = page;
|
|
|
|
promise = this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, this.offline, true).catch((error) => {
|
|
if (!this.offline && !this.utils.isWebServiceError(error)) {
|
|
// If it fails, go offline.
|
|
this.offline = true;
|
|
|
|
return this.feedbackProvider.getPageItemsWithValues(this.feedback.id, page, true);
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}).then((response) => {
|
|
this.hasPrevPage = !!response.hasprevpage;
|
|
this.hasNextPage = !!response.hasnextpage;
|
|
|
|
return response;
|
|
});
|
|
}
|
|
|
|
return promise.then((response) => {
|
|
this.items = response.items.map((itemData) => {
|
|
return this.feedbackHelper.getItemForm(itemData, this.preview);
|
|
}).filter((itemData) => {
|
|
// Filter items with errors.
|
|
return itemData;
|
|
});
|
|
|
|
if (!this.preview) {
|
|
const itemsCopy = this.utils.clone(this.items); // Copy the array to avoid modifications.
|
|
this.originalData = this.feedbackHelper.getPageItemsResponses(itemsCopy);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Function to allow page navigation through the questions form.
|
|
*
|
|
* @param {boolean} goPrevious If true it will go back to the previous page, if false, it will go forward.
|
|
* @return {Promise<void>} Resolved when done.
|
|
*/
|
|
gotoPage(goPrevious: boolean): Promise<void> {
|
|
this.domUtils.scrollToTop(this.content);
|
|
this.feedbackLoaded = false;
|
|
|
|
const responses = this.feedbackHelper.getPageItemsResponses(this.items),
|
|
formHasErrors = this.items.some((item) => {
|
|
return item.isEmpty || item.hasError;
|
|
});
|
|
|
|
// Sync other pages first.
|
|
return this.feedbackSync.syncFeedback(this.feedback.id).catch(() => {
|
|
// Ignore errors.
|
|
}).then(() => {
|
|
return this.feedbackProvider.processPage(this.feedback.id, this.currentPage, responses, goPrevious, formHasErrors,
|
|
this.courseId).then((response) => {
|
|
const jumpTo = parseInt(response.jumpto, 10);
|
|
|
|
if (response.completed) {
|
|
// Form is completed, show completion message and buttons.
|
|
this.items = [];
|
|
this.completed = true;
|
|
this.completedOffline = !!response.offline;
|
|
this.completionPageContents = response.completionpagecontents;
|
|
this.siteAfterSubmit = response.siteaftersubmit;
|
|
this.submitted = true;
|
|
|
|
// Invalidate access information so user will see home page updated (continue form or completion messages).
|
|
const promises = [];
|
|
promises.push(this.feedbackProvider.invalidateFeedbackAccessInformationData(this.feedback.id));
|
|
promises.push(this.feedbackProvider.invalidateResumePageData(this.feedback.id));
|
|
|
|
return Promise.all(promises).then(() => {
|
|
return this.fetchAccessData();
|
|
});
|
|
} else if (isNaN(jumpTo) || jumpTo == this.currentPage) {
|
|
// Errors on questions, stay in page.
|
|
return Promise.resolve();
|
|
} else {
|
|
this.submitted = true;
|
|
// Invalidate access information so user will see home page updated (continue form).
|
|
this.feedbackProvider.invalidateResumePageData(this.feedback.id);
|
|
|
|
// Fetch the new page.
|
|
return this.fetchFeedbackPageData(jumpTo);
|
|
}
|
|
});
|
|
}).catch((message) => {
|
|
this.domUtils.showErrorModalDefault(message, 'core.course.errorgetmodule', true);
|
|
|
|
return Promise.reject(null);
|
|
}).finally(() => {
|
|
this.feedbackLoaded = true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Function to link implemented features.
|
|
*/
|
|
showAnalysis(): void {
|
|
this.submitted = 'analysis';
|
|
this.feedbackHelper.openFeature('analysis', this.navCtrl, this.module, this.courseId);
|
|
}
|
|
|
|
/**
|
|
* Function to go to the page after submit.
|
|
*/
|
|
continue(): void {
|
|
if (this.siteAfterSubmit) {
|
|
const modal = this.domUtils.showModalLoading();
|
|
this.linkHelper.handleLink(this.siteAfterSubmit).then((treated) => {
|
|
if (!treated) {
|
|
return this.currentSite.openInBrowserWithAutoLoginIfSameSite(this.siteAfterSubmit);
|
|
}
|
|
}).finally(() => {
|
|
modal.dismiss();
|
|
});
|
|
} else {
|
|
// Use redirect to make the course the new history root (to avoid "loops" in history).
|
|
this.loginHelper.redirect('CoreCourseSectionPage', {
|
|
course: { id: this.courseId }
|
|
}, this.currentSite.getId());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Component being destroyed.
|
|
*/
|
|
ngOnDestroy(): void {
|
|
if (this.submitted) {
|
|
const tab = this.submitted == 'analysis' ? 'analysis' : 'overview';
|
|
// If form has been submitted, the info has been already invalidated but we should update index view.
|
|
this.eventsProvider.trigger(AddonModFeedbackProvider.FORM_SUBMITTED, {feedbackId: this.feedback.id, tab: tab});
|
|
}
|
|
this.onlineObserver && this.onlineObserver.unsubscribe();
|
|
}
|
|
}
|