MOBILE-3449 assign: Add synchronization to more pages
parent
d2db79ebea
commit
d2ae7505e1
|
@ -408,6 +408,7 @@
|
||||||
"addon.mod_assign.submitassignment_help": "assign",
|
"addon.mod_assign.submitassignment_help": "assign",
|
||||||
"addon.mod_assign.submittedearly": "assign",
|
"addon.mod_assign.submittedearly": "assign",
|
||||||
"addon.mod_assign.submittedlate": "assign",
|
"addon.mod_assign.submittedlate": "assign",
|
||||||
|
"addon.mod_assign.syncblockedusercomponent": "local_moodlemobileapp",
|
||||||
"addon.mod_assign.timemodified": "assign",
|
"addon.mod_assign.timemodified": "assign",
|
||||||
"addon.mod_assign.timeremaining": "assign",
|
"addon.mod_assign.timeremaining": "assign",
|
||||||
"addon.mod_assign.ungroupedusers": "assign",
|
"addon.mod_assign.ungroupedusers": "assign",
|
||||||
|
|
|
@ -299,7 +299,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
*/
|
*/
|
||||||
protected hasSyncSucceed(result: any): boolean {
|
protected hasSyncSucceed(result: any): boolean {
|
||||||
if (result.updated) {
|
if (result.updated) {
|
||||||
this.submissionComponent && this.submissionComponent.invalidateAndRefresh();
|
this.submissionComponent && this.submissionComponent.invalidateAndRefresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.updated;
|
return result.updated;
|
||||||
|
@ -324,7 +324,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises).finally(() => {
|
return Promise.all(promises).finally(() => {
|
||||||
this.submissionComponent && this.submissionComponent.invalidateAndRefresh();
|
this.submissionComponent && this.submissionComponent.invalidateAndRefresh(true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Component, Input, OnInit, OnDestroy, ViewChild, Optional, ViewChildren,
|
||||||
import { NavController } from 'ionic-angular';
|
import { NavController } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider, CoreEventObserver } from '@providers/events';
|
||||||
import { CoreGroupsProvider } from '@providers/groups';
|
import { CoreGroupsProvider } from '@providers/groups';
|
||||||
import { CoreLangProvider } from '@providers/lang';
|
import { CoreLangProvider } from '@providers/lang';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
@ -35,7 +35,7 @@ import {
|
||||||
} from '../../providers/assign';
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
import { AddonModAssignHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
import { AddonModAssignSync } from '../../providers/assign-sync';
|
import { AddonModAssignSync, AddonModAssignSyncProvider } from '../../providers/assign-sync';
|
||||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||||
import { CoreTabComponent } from '@components/tabs/tab';
|
import { CoreTabComponent } from '@components/tabs/tab';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
@ -109,6 +109,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
protected submissionStatusAvailable: boolean; // Whether we were able to retrieve the submission status.
|
protected submissionStatusAvailable: boolean; // Whether we were able to retrieve the submission status.
|
||||||
protected originalGrades: any = {}; // Object with the original grade data, to check for changes.
|
protected originalGrades: any = {}; // Object with the original grade data, to check for changes.
|
||||||
protected isDestroyed: boolean; // Whether the component has been destroyed.
|
protected isDestroyed: boolean; // Whether the component has been destroyed.
|
||||||
|
protected syncObserver: CoreEventObserver;
|
||||||
|
|
||||||
constructor(protected navCtrl: NavController, protected appProvider: CoreAppProvider, protected domUtils: CoreDomUtilsProvider,
|
constructor(protected navCtrl: NavController, protected appProvider: CoreAppProvider, protected domUtils: CoreDomUtilsProvider,
|
||||||
sitesProvider: CoreSitesProvider, protected syncProvider: CoreSyncProvider, protected timeUtils: CoreTimeUtilsProvider,
|
sitesProvider: CoreSitesProvider, protected syncProvider: CoreSyncProvider, protected timeUtils: CoreTimeUtilsProvider,
|
||||||
|
@ -131,7 +132,29 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
this.selectedTab = this.showGrade && this.showGrade !== 'false' ? 1 : 0;
|
this.selectedTab = this.showGrade && this.showGrade !== 'false' ? 1 : 0;
|
||||||
this.isSubmittedForGrading = !!this.submitId;
|
this.isSubmittedForGrading = !!this.submitId;
|
||||||
|
|
||||||
this.loadData();
|
this.loadData(true);
|
||||||
|
|
||||||
|
// Refresh data if this assign is synchronized and it's grading.
|
||||||
|
const events = [AddonModAssignSyncProvider.AUTO_SYNCED, AddonModAssignSyncProvider.MANUAL_SYNCED];
|
||||||
|
|
||||||
|
this.syncObserver = this.eventsProvider.onMultiple(events, async (data) => {
|
||||||
|
// Check that user is grading and this grade wasn't blocked when sync was performed.
|
||||||
|
if (!this.loaded || !this.isGrading || data.gradesBlocked.indexOf(this.submitId) != -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.context == 'submission' && data.submitId == this.submitId) {
|
||||||
|
// Manual sync triggered by this same submission, ignore it.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't refresh if the user has modified some data.
|
||||||
|
const hasDataToSave = await this.hasDataToSave();
|
||||||
|
|
||||||
|
if (!hasDataToSave) {
|
||||||
|
this.invalidateAndRefresh(false);
|
||||||
|
}
|
||||||
|
}, this.siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,7 +266,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
}, this.siteId);
|
}, this.siteId);
|
||||||
} else {
|
} else {
|
||||||
// Invalidate and refresh data to update this view.
|
// Invalidate and refresh data to update this view.
|
||||||
this.invalidateAndRefresh();
|
this.invalidateAndRefresh(true);
|
||||||
}
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
this.domUtils.showErrorModalDefault(error, 'core.error', true);
|
||||||
|
@ -336,9 +359,10 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Invalidate and refresh data.
|
* Invalidate and refresh data.
|
||||||
*
|
*
|
||||||
|
* @param sync Whether to try to synchronize data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
invalidateAndRefresh(): Promise<any> {
|
invalidateAndRefresh(sync?: boolean): Promise<any> {
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
@ -363,16 +387,17 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
return Promise.all(promises).catch(() => {
|
return Promise.all(promises).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return this.loadData();
|
return this.loadData(sync);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the data to render the submission.
|
* Load the data to render the submission.
|
||||||
*
|
*
|
||||||
|
* @param sync Whether to try to synchronize data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async loadData(): Promise<any> {
|
protected async loadData(sync?: boolean): Promise<any> {
|
||||||
let isBlind = !!this.blindId;
|
let isBlind = !!this.blindId;
|
||||||
|
|
||||||
this.previousAttempt = undefined;
|
this.previousAttempt = undefined;
|
||||||
|
@ -387,6 +412,25 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
// Get the assignment.
|
// Get the assignment.
|
||||||
this.assign = await this.assignProvider.getAssignment(this.courseId, this.moduleId);
|
this.assign = await this.assignProvider.getAssignment(this.courseId, this.moduleId);
|
||||||
|
|
||||||
|
if (this.submitId != this.currentUserId && sync) {
|
||||||
|
// Teacher viewing a student submission. Try to sync the assign, there could be offline grades stored.
|
||||||
|
try {
|
||||||
|
const result = await AddonModAssignSync.instance.syncAssign(this.assign.id);
|
||||||
|
|
||||||
|
if (result && result.updated) {
|
||||||
|
this.eventsProvider.trigger(AddonModAssignSyncProvider.MANUAL_SYNCED, {
|
||||||
|
assignId: this.assign.id,
|
||||||
|
warnings: result.warnings,
|
||||||
|
gradesBlocked: result.gradesBlocked,
|
||||||
|
context: 'submission',
|
||||||
|
submitId: this.submitId,
|
||||||
|
}, this.siteId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore errors, probably user is offline or sync is blocked.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const time = this.timeUtils.timestamp();
|
const time = this.timeUtils.timestamp();
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
|
@ -785,7 +829,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
return this.discardDrafts();
|
return this.discardDrafts();
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
// Invalidate and refresh data.
|
// Invalidate and refresh data.
|
||||||
this.invalidateAndRefresh();
|
this.invalidateAndRefresh(true);
|
||||||
|
|
||||||
this.eventsProvider.trigger(AddonModAssignProvider.GRADED_EVENT, {
|
this.eventsProvider.trigger(AddonModAssignProvider.GRADED_EVENT, {
|
||||||
assignmentId: this.assign.id,
|
assignmentId: this.assign.id,
|
||||||
|
@ -1008,6 +1052,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.setGradeSyncBlocked(false);
|
this.setGradeSyncBlocked(false);
|
||||||
this.isDestroyed = true;
|
this.isDestroyed = true;
|
||||||
|
this.syncObserver && this.syncObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider, CoreEventObserver } from '@providers/events';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
import { CoreGroupsProvider, CoreGroupInfo } from '@providers/groups';
|
||||||
|
@ -23,6 +23,7 @@ import {
|
||||||
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignGrade, AddonModAssignSubmission
|
AddonModAssignProvider, AddonModAssignAssign, AddonModAssignGrade, AddonModAssignSubmission
|
||||||
} from '../../providers/assign';
|
} from '../../providers/assign';
|
||||||
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
import { AddonModAssignOfflineProvider } from '../../providers/assign-offline';
|
||||||
|
import { AddonModAssignSyncProvider, AddonModAssignSync } from '../../providers/assign-sync';
|
||||||
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from '../../providers/helper';
|
import { AddonModAssignHelperProvider, AddonModAssignSubmissionFormatted } from '../../providers/helper';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
|
||||||
|
@ -54,10 +55,11 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
protected moduleId: number; // Module ID the submission belongs to.
|
protected moduleId: number; // Module ID the submission belongs to.
|
||||||
protected courseId: number; // Course ID the assignment belongs to.
|
protected courseId: number; // Course ID the assignment belongs to.
|
||||||
protected selectedStatus: string; // The status to see.
|
protected selectedStatus: string; // The status to see.
|
||||||
protected gradedObserver; // Observer to refresh data when a grade changes.
|
protected gradedObserver: CoreEventObserver; // Observer to refresh data when a grade changes.
|
||||||
|
protected syncObserver: CoreEventObserver; // OObserver to refresh data when the async is synchronized.
|
||||||
protected submissionsData: {canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]};
|
protected submissionsData: {canviewsubmissions: boolean, submissions?: AddonModAssignSubmission[]};
|
||||||
|
|
||||||
constructor(navParams: NavParams, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
constructor(navParams: NavParams, protected sitesProvider: CoreSitesProvider, protected eventsProvider: CoreEventsProvider,
|
||||||
protected domUtils: CoreDomUtilsProvider, protected translate: TranslateService,
|
protected domUtils: CoreDomUtilsProvider, protected translate: TranslateService,
|
||||||
protected assignProvider: AddonModAssignProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider,
|
protected assignProvider: AddonModAssignProvider, protected assignOfflineProvider: AddonModAssignOfflineProvider,
|
||||||
protected assignHelper: AddonModAssignHelperProvider, protected groupsProvider: CoreGroupsProvider) {
|
protected assignHelper: AddonModAssignHelperProvider, protected groupsProvider: CoreGroupsProvider) {
|
||||||
|
@ -79,22 +81,37 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Update data if some grade changes.
|
// Update data if some grade changes.
|
||||||
this.gradedObserver = eventsProvider.on(AddonModAssignProvider.GRADED_EVENT, (data) => {
|
this.gradedObserver = eventsProvider.on(AddonModAssignProvider.GRADED_EVENT, (data) => {
|
||||||
if (this.assign && data.assignmentId == this.assign.id && data.userId == sitesProvider.getCurrentSiteUserId()) {
|
if (this.loaded && this.assign && data.assignmentId == this.assign.id &&
|
||||||
|
data.userId == sitesProvider.getCurrentSiteUserId()) {
|
||||||
// Grade changed, refresh the data.
|
// Grade changed, refresh the data.
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
|
||||||
this.refreshAllData().finally(() => {
|
this.refreshAllData(true).finally(() => {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, sitesProvider.getCurrentSiteId());
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
// Refresh data if this assign is synchronized.
|
||||||
|
const events = [AddonModAssignSyncProvider.AUTO_SYNCED, AddonModAssignSyncProvider.MANUAL_SYNCED];
|
||||||
|
this.syncObserver = eventsProvider.onMultiple(events, (data) => {
|
||||||
|
if (!this.loaded || data.context == 'submission-list') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loaded = false;
|
||||||
|
|
||||||
|
this.refreshAllData(false).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.fetchAssignment().finally(() => {
|
this.fetchAssignment(true).finally(() => {
|
||||||
if (!this.selectedSubmissionId && this.splitviewCtrl.isOn() && this.submissions.length > 0) {
|
if (!this.selectedSubmissionId && this.splitviewCtrl.isOn() && this.submissions.length > 0) {
|
||||||
// Take first and load it.
|
// Take first and load it.
|
||||||
this.loadSubmission(this.submissions[0]);
|
this.loadSubmission(this.submissions[0]);
|
||||||
|
@ -107,34 +124,49 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Fetch assignment data.
|
* Fetch assignment data.
|
||||||
*
|
*
|
||||||
|
* @param sync Whether to try to synchronize data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchAssignment(): Promise<any> {
|
protected async fetchAssignment(sync?: boolean): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Get assignment data.
|
||||||
|
this.assign = await this.assignProvider.getAssignment(this.courseId, this.moduleId);
|
||||||
|
|
||||||
// Get assignment data.
|
this.title = this.assign.name || this.title;
|
||||||
return this.assignProvider.getAssignment(this.courseId, this.moduleId).then((assign) => {
|
|
||||||
this.title = assign.name || this.title;
|
|
||||||
this.assign = assign;
|
|
||||||
|
|
||||||
// Get assignment submissions.
|
if (sync) {
|
||||||
return this.assignProvider.getSubmissions(assign.id);
|
try {
|
||||||
}).then((data) => {
|
// Try to synchronize data.
|
||||||
if (!data.canviewsubmissions) {
|
const result = await AddonModAssignSync.instance.syncAssign(this.assign.id);
|
||||||
// User shouldn't be able to reach here.
|
|
||||||
return Promise.reject(null);
|
if (result && result.updated) {
|
||||||
|
this.eventsProvider.trigger(AddonModAssignSyncProvider.MANUAL_SYNCED, {
|
||||||
|
assignId: this.assign.id,
|
||||||
|
warnings: result.warnings,
|
||||||
|
gradesBlocked: result.gradesBlocked,
|
||||||
|
context: 'submission-list',
|
||||||
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore errors, probably user is offline or sync is blocked.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.submissionsData = data;
|
// Get assignment submissions.
|
||||||
|
this.submissionsData = await this.assignProvider.getSubmissions(this.assign.id);
|
||||||
|
|
||||||
|
if (!this.submissionsData.canviewsubmissions) {
|
||||||
|
// User shouldn't be able to reach here.
|
||||||
|
throw new Error('Cannot view submissions.');
|
||||||
|
}
|
||||||
|
|
||||||
// Check if groupmode is enabled to avoid showing wrong numbers.
|
// Check if groupmode is enabled to avoid showing wrong numbers.
|
||||||
return this.groupsProvider.getActivityGroupInfo(this.assign.cmid, false).then((groupInfo) => {
|
this.groupInfo = await this.groupsProvider.getActivityGroupInfo(this.assign.cmid, false);
|
||||||
this.groupInfo = groupInfo;
|
|
||||||
|
|
||||||
return this.setGroup(this.groupsProvider.validateGroupId(this.groupId, groupInfo));
|
await this.setGroup(this.groupsProvider.validateGroupId(this.groupId, this.groupInfo));
|
||||||
});
|
} catch (error) {
|
||||||
}).catch((error) => {
|
|
||||||
this.domUtils.showErrorModalDefault(error, 'Error getting assigment data.');
|
this.domUtils.showErrorModalDefault(error, 'Error getting assigment data.');
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -265,9 +297,10 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Refresh all the data.
|
* Refresh all the data.
|
||||||
*
|
*
|
||||||
|
* @param sync Whether to try to synchronize data.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected refreshAllData(): Promise<any> {
|
protected refreshAllData(sync?: boolean): Promise<any> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
promises.push(this.assignProvider.invalidateAssignmentData(this.courseId));
|
promises.push(this.assignProvider.invalidateAssignmentData(this.courseId));
|
||||||
|
@ -279,7 +312,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises).finally(() => {
|
return Promise.all(promises).finally(() => {
|
||||||
return this.fetchAssignment();
|
return this.fetchAssignment(sync);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +322,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
* @param refresher Refresher.
|
* @param refresher Refresher.
|
||||||
*/
|
*/
|
||||||
refreshList(refresher: any): void {
|
refreshList(refresher: any): void {
|
||||||
this.refreshAllData().finally(() => {
|
this.refreshAllData(true).finally(() => {
|
||||||
refresher.complete();
|
refresher.complete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -299,6 +332,7 @@ export class AddonModAssignSubmissionListPage implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.gradedObserver && this.gradedObserver.off();
|
this.gradedObserver && this.gradedObserver.off();
|
||||||
|
this.syncObserver && this.syncObserver.off();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(promises).finally(() => {
|
return Promise.all(promises).finally(() => {
|
||||||
this.submissionComponent && this.submissionComponent.invalidateAndRefresh();
|
this.submissionComponent && this.submissionComponent.invalidateAndRefresh(true);
|
||||||
|
|
||||||
return this.fetchSubmission();
|
return this.fetchSubmission();
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
import { CoreGradesHelperProvider } from '@core/grades/providers/helper';
|
||||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
import { CoreSyncBaseProvider, CoreSyncBlockedError } from '@classes/base-sync';
|
||||||
import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission } from './assign';
|
import { AddonModAssignProvider, AddonModAssignAssign, AddonModAssignSubmission } from './assign';
|
||||||
import { AddonModAssignOfflineProvider } from './assign-offline';
|
import { AddonModAssignOfflineProvider } from './assign-offline';
|
||||||
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
import { AddonModAssignSubmissionDelegate } from './submission-delegate';
|
||||||
|
@ -46,6 +46,11 @@ export interface AddonModAssignSyncResult {
|
||||||
* Whether data was updated in the site.
|
* Whether data was updated in the site.
|
||||||
*/
|
*/
|
||||||
updated: boolean;
|
updated: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether some grade couldn't be synced because it was blocked.
|
||||||
|
*/
|
||||||
|
gradesBlocked: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,6 +60,7 @@ export interface AddonModAssignSyncResult {
|
||||||
export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
|
|
||||||
static AUTO_SYNCED = 'addon_mod_assign_autom_synced';
|
static AUTO_SYNCED = 'addon_mod_assign_autom_synced';
|
||||||
|
static MANUAL_SYNCED = 'addon_mod_assign_manual_synced';
|
||||||
|
|
||||||
protected componentTranslate: string;
|
protected componentTranslate: string;
|
||||||
|
|
||||||
|
@ -161,6 +167,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
this.eventsProvider.trigger(AddonModAssignSyncProvider.AUTO_SYNCED, {
|
this.eventsProvider.trigger(AddonModAssignSyncProvider.AUTO_SYNCED, {
|
||||||
assignId: assignId,
|
assignId: assignId,
|
||||||
warnings: data.warnings,
|
warnings: data.warnings,
|
||||||
|
gradesBlocked: data.gradesBlocked,
|
||||||
}, siteId);
|
}, siteId);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -199,7 +206,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, assignId, siteId)) {
|
if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, assignId, siteId)) {
|
||||||
this.logger.error('Cannot sync assign ' + assignId + ' because it is blocked.');
|
this.logger.error('Cannot sync assign ' + assignId + ' because it is blocked.');
|
||||||
|
|
||||||
throw new Error(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate}));
|
throw new CoreSyncBlockedError(this.translate.instant('core.errorsyncblocked', {$a: this.componentTranslate}));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.addOngoingSync(assignId, this.performSyncAssign(assignId, siteId), siteId);
|
return this.addOngoingSync(assignId, this.performSyncAssign(assignId, siteId), siteId);
|
||||||
|
@ -219,6 +226,7 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
const result: AddonModAssignSyncResult = {
|
const result: AddonModAssignSyncResult = {
|
||||||
warnings: [],
|
warnings: [],
|
||||||
updated: false,
|
updated: false,
|
||||||
|
gradesBlocked: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load offline data and sync offline logs.
|
// Load offline data and sync offline logs.
|
||||||
|
@ -254,9 +262,18 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
promises = promises.concat(grades.map(async (grade) => {
|
promises = promises.concat(grades.map(async (grade) => {
|
||||||
await this.syncSubmissionGrade(assign, grade, result.warnings, courseId, siteId);
|
try {
|
||||||
|
await this.syncSubmissionGrade(assign, grade, result.warnings, courseId, siteId);
|
||||||
|
|
||||||
result.updated = true;
|
result.updated = true;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof CoreSyncBlockedError) {
|
||||||
|
// Grade blocked, but allow finish the sync.
|
||||||
|
result.gradesBlocked.push(grade.userid);
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
@ -408,9 +425,9 @@ export class AddonModAssignSyncProvider extends CoreSyncBaseProvider {
|
||||||
|
|
||||||
// Check if this grade sync is blocked.
|
// Check if this grade sync is blocked.
|
||||||
if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, syncId, siteId)) {
|
if (this.syncProvider.isBlocked(AddonModAssignProvider.COMPONENT, syncId, siteId)) {
|
||||||
this.logger.error(`Cannot sync grade for assign ${assign.id} and user ${userId} because it is blocked.`);
|
this.logger.error(`Cannot sync grade for assign ${assign.id} and user ${userId} because it is blocked.!!!!`);
|
||||||
|
|
||||||
throw new Error(this.translate.instant('core.errorsyncblocked',
|
throw new CoreSyncBlockedError(this.translate.instant('core.errorsyncblocked',
|
||||||
{$a: this.translate.instant('addon.mod_assign.syncblockedusercomponent')}));
|
{$a: this.translate.instant('addon.mod_assign.syncblockedusercomponent')}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,18 @@ import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocked sync error.
|
||||||
|
*/
|
||||||
|
export class CoreSyncBlockedError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
// Set the prototype explicitly, otherwise instanceof won't work as expected.
|
||||||
|
Object.setPrototypeOf(this, CoreSyncBlockedError.prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class to create sync providers. It provides some common functions.
|
* Base class to create sync providers. It provides some common functions.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -124,6 +124,33 @@ export class CoreEventsProvider {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen for several events. To stop listening to the events:
|
||||||
|
* let observer = eventsProvider.onMultiple(['something', 'another'], myCallBack);
|
||||||
|
* ...
|
||||||
|
* observer.off();
|
||||||
|
*
|
||||||
|
* @param eventNames Names of the events to listen to.
|
||||||
|
* @param callBack Function to call when any of the events is triggered.
|
||||||
|
* @param siteId Site where to trigger the event. Undefined won't check the site.
|
||||||
|
* @return Observer to stop listening.
|
||||||
|
*/
|
||||||
|
onMultiple(eventNames: string[], callBack: (value: any) => void, siteId?: string): CoreEventObserver {
|
||||||
|
|
||||||
|
const observers = eventNames.map((name) => {
|
||||||
|
return this.on(name, callBack, siteId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create and return a CoreEventObserver.
|
||||||
|
return {
|
||||||
|
off: (): void => {
|
||||||
|
observers.forEach((observer) => {
|
||||||
|
observer.off();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers an event, notifying all the observers.
|
* Triggers an event, notifying all the observers.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue