commit
6ad4d8744d
|
@ -183,6 +183,9 @@ export class AddonModAssignFeedbackCommentsHandler implements AddonModAssignFeed
|
||||||
* @return True or promise resolved with true if enabled.
|
* @return True or promise resolved with true if enabled.
|
||||||
*/
|
*/
|
||||||
isEnabled(): boolean | Promise<boolean> {
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
// In here we should check if comments is not disabled in site.
|
||||||
|
// But due to this is not a common comments place and it can be disabled separately into Moodle (disabling the plugin).
|
||||||
|
// We are leaving it always enabled. It's also a teacher's feature.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,6 +282,7 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
||||||
return this.getDatabaseInfoHelper(module, courseId, false, false, true, siteId).then((info) => {
|
return this.getDatabaseInfoHelper(module, courseId, false, false, true, siteId).then((info) => {
|
||||||
// Prefetch the database data.
|
// Prefetch the database data.
|
||||||
const database = info.database,
|
const database = info.database,
|
||||||
|
commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(),
|
||||||
promises = [];
|
promises = [];
|
||||||
|
|
||||||
promises.push(this.dataProvider.getFields(database.id, false, true, siteId));
|
promises.push(this.dataProvider.getFields(database.id, false, true, siteId));
|
||||||
|
@ -295,7 +296,7 @@ export class AddonModDataPrefetchHandler extends CoreCourseActivityPrefetchHandl
|
||||||
info.entries.forEach((entry) => {
|
info.entries.forEach((entry) => {
|
||||||
promises.push(this.dataProvider.getEntry(database.id, entry.id, true, siteId));
|
promises.push(this.dataProvider.getEntry(database.id, entry.id, true, siteId));
|
||||||
|
|
||||||
if (database.comments) {
|
if (commentsEnabled && database.comments) {
|
||||||
promises.push(this.commentsProvider.getComments('module', database.coursemodule, 'mod_data', entry.id,
|
promises.push(this.commentsProvider.getComments('module', database.coursemodule, 'mod_data', entry.id,
|
||||||
'database_entry', 0, siteId));
|
'database_entry', 0, siteId));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,9 @@
|
||||||
<ion-item text-wrap *ngIf="entry.approved != 1">
|
<ion-item text-wrap *ngIf="entry.approved != 1">
|
||||||
<p><em>{{ 'addon.mod_glossary.entrypendingapproval' | translate }}</em></p>
|
<p><em>{{ 'addon.mod_glossary.entrypendingapproval' | translate }}</em></p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
<ion-item *ngIf="glossary && glossary.allowcomments && entry && entry.id > 0 && commentsEnabled">
|
||||||
|
<core-comments contextLevel="module" [instanceId]="glossary.coursemodule" component="mod_glossary" [itemId]="entry.id" area="glossary_entry" [courseId]="glossary.courseid"></core-comments>
|
||||||
|
</ion-item>
|
||||||
<core-rating-rate *ngIf="glossary && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="glossary.coursemodule" [itemId]="entry.id" [itemSetId]="0" [courseId]="glossary.courseid" [aggregateMethod]="glossary.assessed" [scaleId]="glossary.scale" [userId]="entry.userid" (onUpdate)="ratingUpdated()"></core-rating-rate>
|
<core-rating-rate *ngIf="glossary && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="glossary.coursemodule" [itemId]="entry.id" [itemSetId]="0" [courseId]="glossary.courseid" [aggregateMethod]="glossary.assessed" [scaleId]="glossary.scale" [userId]="entry.userid" (onUpdate)="ratingUpdated()"></core-rating-rate>
|
||||||
<core-rating-aggregate *ngIf="glossary && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="glossary.coursemodule" [itemId]="entry.id" [courseId]="glossary.courseid" [aggregateMethod]="glossary.assessed" [scaleId]="glossary.scale"></core-rating-aggregate>
|
<core-rating-aggregate *ngIf="glossary && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="glossary.coursemodule" [itemId]="entry.id" [courseId]="glossary.courseid" [aggregateMethod]="glossary.assessed" [scaleId]="glossary.scale"></core-rating-aggregate>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
||||||
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
||||||
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
import { AddonModGlossaryEntryPage } from './entry';
|
import { AddonModGlossaryEntryPage } from './entry';
|
||||||
|
@ -32,6 +33,7 @@ import { AddonModGlossaryEntryPage } from './entry';
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
IonicPageModule.forChild(AddonModGlossaryEntryPage),
|
IonicPageModule.forChild(AddonModGlossaryEntryPage),
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
|
CoreCommentsComponentsModule,
|
||||||
CoreRatingComponentsModule,
|
CoreRatingComponentsModule,
|
||||||
CoreTagComponentsModule
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
|
|
|
@ -12,11 +12,13 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
import { Component, ViewChild } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||||
import { CoreTagProvider } from '@core/tag/providers/tag';
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||||
|
import { CoreCommentsCommentsComponent } from '@core/comments/components/comments/comments';
|
||||||
import { AddonModGlossaryProvider } from '../../providers/glossary';
|
import { AddonModGlossaryProvider } from '../../providers/glossary';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +30,8 @@ import { AddonModGlossaryProvider } from '../../providers/glossary';
|
||||||
templateUrl: 'entry.html',
|
templateUrl: 'entry.html',
|
||||||
})
|
})
|
||||||
export class AddonModGlossaryEntryPage {
|
export class AddonModGlossaryEntryPage {
|
||||||
|
@ViewChild(CoreCommentsCommentsComponent) comments: CoreCommentsCommentsComponent;
|
||||||
|
|
||||||
component = AddonModGlossaryProvider.COMPONENT;
|
component = AddonModGlossaryProvider.COMPONENT;
|
||||||
componentId: number;
|
componentId: number;
|
||||||
entry: any;
|
entry: any;
|
||||||
|
@ -37,23 +41,27 @@ export class AddonModGlossaryEntryPage {
|
||||||
showDate = false;
|
showDate = false;
|
||||||
ratingInfo: CoreRatingInfo;
|
ratingInfo: CoreRatingInfo;
|
||||||
tagsEnabled: boolean;
|
tagsEnabled: boolean;
|
||||||
|
commentsEnabled: boolean;
|
||||||
|
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected entryId: number;
|
protected entryId: number;
|
||||||
|
|
||||||
constructor(navParams: NavParams,
|
constructor(navParams: NavParams,
|
||||||
private domUtils: CoreDomUtilsProvider,
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
private glossaryProvider: AddonModGlossaryProvider,
|
protected glossaryProvider: AddonModGlossaryProvider,
|
||||||
private tagProvider: CoreTagProvider) {
|
protected tagProvider: CoreTagProvider,
|
||||||
|
protected commentsProvider: CoreCommentsProvider) {
|
||||||
this.courseId = navParams.get('courseId');
|
this.courseId = navParams.get('courseId');
|
||||||
this.entryId = navParams.get('entryId');
|
this.entryId = navParams.get('entryId');
|
||||||
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View loaded.
|
* View loaded.
|
||||||
*/
|
*/
|
||||||
ionViewDidLoad(): void {
|
ionViewDidLoad(): void {
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
|
this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite();
|
||||||
|
|
||||||
this.fetchEntry().then(() => {
|
this.fetchEntry().then(() => {
|
||||||
this.glossaryProvider.logEntryView(this.entry.id, this.componentId, this.glossary.name).catch(() => {
|
this.glossaryProvider.logEntryView(this.entry.id, this.componentId, this.glossary.name).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
@ -70,6 +78,14 @@ export class AddonModGlossaryEntryPage {
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
doRefresh(refresher?: any): Promise<any> {
|
doRefresh(refresher?: any): Promise<any> {
|
||||||
|
if (this.glossary && this.glossary.allowcomments && this.entry && this.entry.id > 0 && this.commentsEnabled &&
|
||||||
|
this.comments) {
|
||||||
|
// Refresh comments. Don't add it to promises because we don't want the comments fetch to block the entry fetch.
|
||||||
|
this.comments.doRefresh().catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this.glossaryProvider.invalidateEntry(this.entry.id).catch(() => {
|
return this.glossaryProvider.invalidateEntry(this.entry.id).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
|
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||||
import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
import { CoreCourseActivityPrefetchHandlerBase } from '@core/course/classes/activity-prefetch-handler';
|
||||||
import { AddonModGlossaryProvider } from './glossary';
|
import { AddonModGlossaryProvider } from './glossary';
|
||||||
import { AddonModGlossarySyncProvider } from './sync';
|
import { AddonModGlossarySyncProvider } from './sync';
|
||||||
|
@ -43,8 +44,9 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
|
||||||
sitesProvider: CoreSitesProvider,
|
sitesProvider: CoreSitesProvider,
|
||||||
domUtils: CoreDomUtilsProvider,
|
domUtils: CoreDomUtilsProvider,
|
||||||
filterHelper: CoreFilterHelperProvider,
|
filterHelper: CoreFilterHelperProvider,
|
||||||
private glossaryProvider: AddonModGlossaryProvider,
|
protected glossaryProvider: AddonModGlossaryProvider,
|
||||||
private syncProvider: AddonModGlossarySyncProvider) {
|
protected commentsProvider: CoreCommentsProvider,
|
||||||
|
protected syncProvider: AddonModGlossarySyncProvider) {
|
||||||
|
|
||||||
super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper);
|
super(translate, appProvider, utils, courseProvider, filepoolProvider, sitesProvider, domUtils, filterHelper);
|
||||||
}
|
}
|
||||||
|
@ -160,8 +162,9 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
|
||||||
// Fetch all entries to get information from.
|
// Fetch all entries to get information from.
|
||||||
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter,
|
promises.push(this.glossaryProvider.fetchAllEntries(this.glossaryProvider.getEntriesByLetter,
|
||||||
[glossary.id, 'ALL'], false, false, siteId).then((entries) => {
|
[glossary.id, 'ALL'], false, false, siteId).then((entries) => {
|
||||||
const promises = [];
|
const promises = [],
|
||||||
const avatars = {}; // List of user avatars, preventing duplicates.
|
commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite(),
|
||||||
|
avatars = {}; // List of user avatars, preventing duplicates.
|
||||||
|
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
// Don't fetch individual entries, it's too many WS calls.
|
// Don't fetch individual entries, it's too many WS calls.
|
||||||
|
@ -169,6 +172,11 @@ export class AddonModGlossaryPrefetchHandler extends CoreCourseActivityPrefetchH
|
||||||
if (entry.userpictureurl) {
|
if (entry.userpictureurl) {
|
||||||
avatars[entry.userpictureurl] = true;
|
avatars[entry.userpictureurl] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (glossary.allowcomments && commentsEnabled) {
|
||||||
|
promises.push(this.commentsProvider.getComments('module', glossary.coursemodule, 'mod_glossary', entry.id,
|
||||||
|
'glossary_entry', 0, siteId));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prefetch intro files, entries files and user avatars.
|
// Prefetch intro files, entries files and user avatars.
|
||||||
|
|
|
@ -44,9 +44,12 @@ export class CoreCommentsCommentsComponent implements OnChanges, OnDestroy {
|
||||||
|
|
||||||
protected updateSiteObserver;
|
protected updateSiteObserver;
|
||||||
protected refreshCommentsObserver;
|
protected refreshCommentsObserver;
|
||||||
|
protected commentsCountObserver;
|
||||||
|
|
||||||
constructor(private navCtrl: NavController, private commentsProvider: CoreCommentsProvider,
|
constructor(private navCtrl: NavController,
|
||||||
sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
private commentsProvider: CoreCommentsProvider,
|
||||||
|
sitesProvider: CoreSitesProvider,
|
||||||
|
eventsProvider: CoreEventsProvider,
|
||||||
@Optional() private svComponent: CoreSplitViewComponent) {
|
@Optional() private svComponent: CoreSplitViewComponent) {
|
||||||
|
|
||||||
this.onLoading = new EventEmitter<boolean>();
|
this.onLoading = new EventEmitter<boolean>();
|
||||||
|
@ -76,6 +79,17 @@ export class CoreCommentsCommentsComponent implements OnChanges, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, sitesProvider.getCurrentSiteId());
|
}, sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
// Refresh comments count if event received.
|
||||||
|
this.commentsCountObserver = eventsProvider.on(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, (data) => {
|
||||||
|
// Verify these comments need to be updated.
|
||||||
|
if (!this.commentsCount.endsWith('+') && this.undefinedOrEqual(data, 'contextLevel') &&
|
||||||
|
this.undefinedOrEqual(data, 'instanceId') && this.undefinedOrEqual(data, 'component') &&
|
||||||
|
this.undefinedOrEqual(data, 'itemId') && this.undefinedOrEqual(data, 'area') && !this.countError) {
|
||||||
|
// Parse and unparse string.
|
||||||
|
this.commentsCount = parseInt(this.commentsCount, 10) + data.countChange + '';
|
||||||
|
}
|
||||||
|
}, sitesProvider.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,6 +181,7 @@ export class CoreCommentsCommentsComponent implements OnChanges, OnDestroy {
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.updateSiteObserver && this.updateSiteObserver.off();
|
this.updateSiteObserver && this.updateSiteObserver.off();
|
||||||
this.refreshCommentsObserver && this.refreshCommentsObserver.off();
|
this.refreshCommentsObserver && this.refreshCommentsObserver.off();
|
||||||
|
this.commentsCountObserver && this.commentsCountObserver.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<core-loading *ngIf="!disabled" [hideUntil]="commentsLoaded || !displaySpinner">
|
<core-loading *ngIf="!disabled" [hideUntil]="commentsLoaded || !displaySpinner">
|
||||||
<div (click)="openComments($event)" *ngIf="!countError" [class.core-comments-clickable]="!disabled">
|
<div *ngIf="!countError" (click)="openComments($event)" [class.core-comments-clickable]="!disabled">
|
||||||
{{ 'core.comments.commentscount' | translate : {'$a': commentsCount} }}
|
{{ 'core.comments.commentscount' | translate : {'$a': commentsCount} }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="countError">
|
<div *ngIf="countError">
|
||||||
|
|
|
@ -63,11 +63,18 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
protected syncObserver: any;
|
protected syncObserver: any;
|
||||||
protected currentUser: any;
|
protected currentUser: any;
|
||||||
|
|
||||||
constructor(navParams: NavParams, private sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider,
|
constructor(navParams: NavParams,
|
||||||
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private modalCtrl: ModalController,
|
protected sitesProvider: CoreSitesProvider,
|
||||||
private commentsProvider: CoreCommentsProvider, private offlineComments: CoreCommentsOfflineProvider,
|
protected userProvider: CoreUserProvider,
|
||||||
eventsProvider: CoreEventsProvider, private commentsSync: CoreCommentsSyncProvider,
|
protected domUtils: CoreDomUtilsProvider,
|
||||||
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) {
|
protected translate: TranslateService,
|
||||||
|
protected modalCtrl: ModalController,
|
||||||
|
protected commentsProvider: CoreCommentsProvider,
|
||||||
|
protected offlineComments: CoreCommentsOfflineProvider,
|
||||||
|
protected eventsProvider: CoreEventsProvider,
|
||||||
|
protected commentsSync: CoreCommentsSyncProvider,
|
||||||
|
protected textUtils: CoreTextUtilsProvider,
|
||||||
|
protected timeUtils: CoreTimeUtilsProvider) {
|
||||||
|
|
||||||
this.contextLevel = navParams.get('contextLevel');
|
this.contextLevel = navParams.get('contextLevel');
|
||||||
this.instanceId = navParams.get('instanceId');
|
this.instanceId = navParams.get('instanceId');
|
||||||
|
@ -127,31 +134,8 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
return promise.catch(() => {
|
return promise.catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
return this.offlineComments.getComment(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
return this.loadOfflineData();
|
||||||
this.area).then((offlineComment) => {
|
}).then(() => {
|
||||||
this.offlineComment = offlineComment;
|
|
||||||
|
|
||||||
if (offlineComment && !this.currentUser) {
|
|
||||||
return this.userProvider.getProfile(this.currentUserId, undefined, true).then((user) => {
|
|
||||||
this.currentUser = user;
|
|
||||||
this.offlineComment.profileimageurl = user.profileimageurl;
|
|
||||||
this.offlineComment.fullname = user.fullname;
|
|
||||||
this.offlineComment.userid = user.id;
|
|
||||||
}).catch(() => {
|
|
||||||
// Ignore errors.
|
|
||||||
});
|
|
||||||
} else if (offlineComment) {
|
|
||||||
this.offlineComment.profileimageurl = this.currentUser.profileimageurl;
|
|
||||||
this.offlineComment.fullname = this.currentUser.fullname;
|
|
||||||
this.offlineComment.userid = this.currentUser.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.offlineComments.getDeletedComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
|
||||||
this.area);
|
|
||||||
});
|
|
||||||
}).then((deletedComments) => {
|
|
||||||
this.hasOffline = !!this.offlineComment || deletedComments.length > 0;
|
|
||||||
|
|
||||||
// Get comments data.
|
// Get comments data.
|
||||||
return this.commentsProvider.getComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
return this.commentsProvider.getComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
this.area, this.page).then((response) => {
|
this.area, this.page).then((response) => {
|
||||||
|
@ -165,30 +149,10 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
this.canLoadMore = response.comments.length > 0 && response.comments.length >= CoreCommentsProvider.pageSize;
|
this.canLoadMore = response.comments.length > 0 && response.comments.length >= CoreCommentsProvider.pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.all(comments.map((comment) => {
|
return Promise.all(comments.map((comment) => this.loadCommentProfile(comment)));
|
||||||
// Get the user profile image.
|
|
||||||
return this.userProvider.getProfile(comment.userid, undefined, true).then((user) => {
|
|
||||||
comment.profileimageurl = user.profileimageurl;
|
|
||||||
|
|
||||||
return comment;
|
|
||||||
}).catch(() => {
|
|
||||||
// Ignore errors.
|
|
||||||
return comment;
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}).then((comments) => {
|
}).then((comments) => {
|
||||||
this.comments = this.comments.concat(comments);
|
this.comments = this.comments.concat(comments);
|
||||||
|
|
||||||
deletedComments && deletedComments.forEach((deletedComment) => {
|
|
||||||
const comment = this.comments.find((comment) => {
|
|
||||||
return comment.id == deletedComment.commentid;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (comment) {
|
|
||||||
comment.deleted = deletedComment.deleted;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.canDeleteComments = this.addDeleteCommentsAvailable && (this.hasOffline || this.comments.some((comment) => {
|
this.canDeleteComments = this.addDeleteCommentsAvailable && (this.hasOffline || this.comments.some((comment) => {
|
||||||
return !!comment.delete;
|
return !!comment.delete;
|
||||||
}));
|
}));
|
||||||
|
@ -231,11 +195,11 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
refreshComments(showErrors: boolean, refresher?: any): Promise<any> {
|
refreshComments(showErrors: boolean, refresher?: any): Promise<any> {
|
||||||
|
this.commentsLoaded = false;
|
||||||
this.refreshIcon = 'spinner';
|
this.refreshIcon = 'spinner';
|
||||||
this.syncIcon = 'spinner';
|
this.syncIcon = 'spinner';
|
||||||
|
|
||||||
return this.commentsProvider.invalidateCommentsData(this.contextLevel, this.instanceId, this.componentName,
|
return this.invalidateComments().finally(() => {
|
||||||
this.itemId, this.area).finally(() => {
|
|
||||||
this.page = 0;
|
this.page = 0;
|
||||||
this.comments = [];
|
this.comments = [];
|
||||||
|
|
||||||
|
@ -297,10 +261,25 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
const modal = this.modalCtrl.create('CoreCommentsAddPage', params);
|
const modal = this.modalCtrl.create('CoreCommentsAddPage', params);
|
||||||
modal.onDidDismiss((data) => {
|
modal.onDidDismiss((data) => {
|
||||||
if (data && data.comments) {
|
if (data && data.comments) {
|
||||||
this.comments = data.comments.concat(this.comments);
|
this.invalidateComments();
|
||||||
this.canDeleteComments = this.addDeleteCommentsAvailable;
|
|
||||||
|
return Promise.all(data.comments.map((comment) => this.loadCommentProfile(comment))).then((addedComments) => {
|
||||||
|
// Add the comment to the top.
|
||||||
|
this.comments = addedComments.concat(this.comments);
|
||||||
|
this.canDeleteComments = this.addDeleteCommentsAvailable;
|
||||||
|
|
||||||
|
this.eventsProvider.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, {
|
||||||
|
contextLevel: this.contextLevel,
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
component: this.componentName,
|
||||||
|
itemId: this.itemId,
|
||||||
|
area: this.area,
|
||||||
|
countChange: addedComments.length,
|
||||||
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
|
});
|
||||||
} else if (data && !data.comments) {
|
} else if (data && !data.comments) {
|
||||||
this.fetchComments(false);
|
// Comments added in offline mode.
|
||||||
|
return this.loadOfflineData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
modal.present();
|
modal.present();
|
||||||
|
@ -310,26 +289,45 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
* Delete a comment.
|
* Delete a comment.
|
||||||
*
|
*
|
||||||
* @param e Click event.
|
* @param e Click event.
|
||||||
* @param comment Comment to delete.
|
* @param deleteComment Comment to delete.
|
||||||
*/
|
*/
|
||||||
deleteComment(e: Event, comment: any): void {
|
deleteComment(e: Event, deleteComment: any): void {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
const time = this.timeUtils.userDate((comment.lastmodified || comment.timecreated) * 1000, 'core.strftimerecentfull');
|
const time = this.timeUtils.userDate((deleteComment.lastmodified || deleteComment.timecreated) * 1000,
|
||||||
|
'core.strftimerecentfull');
|
||||||
|
|
||||||
comment.contextlevel = this.contextLevel;
|
deleteComment.contextlevel = this.contextLevel;
|
||||||
comment.instanceid = this.instanceId;
|
deleteComment.instanceid = this.instanceId;
|
||||||
comment.component = this.componentName;
|
deleteComment.component = this.componentName;
|
||||||
comment.itemid = this.itemId;
|
deleteComment.itemid = this.itemId;
|
||||||
comment.area = this.area;
|
deleteComment.area = this.area;
|
||||||
|
|
||||||
this.domUtils.showDeleteConfirm('core.comments.deletecommentbyon', {$a:
|
this.domUtils.showDeleteConfirm('core.comments.deletecommentbyon', {$a:
|
||||||
{ user: comment.fullname || '', time: time } }).then(() => {
|
{ user: deleteComment.fullname || '', time: time } }).then(() => {
|
||||||
this.commentsProvider.deleteComment(comment).then(() => {
|
this.commentsProvider.deleteComment(deleteComment).then((deletedOnline) => {
|
||||||
this.showDelete = false;
|
this.showDelete = false;
|
||||||
|
|
||||||
this.refreshComments(true);
|
if (deletedOnline) {
|
||||||
|
const index = this.comments.findIndex((comment) => comment.id == deleteComment.id);
|
||||||
|
if (index >= 0) {
|
||||||
|
this.comments.splice(index, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.loadOfflineData();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventsProvider.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, {
|
||||||
|
contextLevel: this.contextLevel,
|
||||||
|
instanceId: this.instanceId,
|
||||||
|
component: this.componentName,
|
||||||
|
itemId: this.itemId,
|
||||||
|
area: this.area,
|
||||||
|
countChange: -1,
|
||||||
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
|
this.invalidateComments();
|
||||||
|
|
||||||
this.domUtils.showToast('core.comments.eventcommentdeleted', true, 3000);
|
this.domUtils.showToast('core.comments.eventcommentdeleted', true, 3000);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -340,6 +338,91 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate comments.
|
||||||
|
*
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected invalidateComments(): Promise<void> {
|
||||||
|
return this.commentsProvider.invalidateCommentsData(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
|
this.area);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the profile info onto the comment object.
|
||||||
|
*
|
||||||
|
* @param comment Comment object.
|
||||||
|
* @return Promise resolved with modified comment when done.
|
||||||
|
*/
|
||||||
|
protected loadCommentProfile(comment: any): Promise<any> {
|
||||||
|
// Get the user profile image.
|
||||||
|
return this.userProvider.getProfile(comment.userid, undefined, true).then((user) => {
|
||||||
|
comment.profileimageurl = user.profileimageurl;
|
||||||
|
comment.fullname = user.fullname;
|
||||||
|
comment.userid = user.id;
|
||||||
|
|
||||||
|
return comment;
|
||||||
|
}).catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
|
return comment;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load offline comments.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected loadOfflineData(): Promise<void> {
|
||||||
|
const promises = [];
|
||||||
|
let hasDeletedComments = false;
|
||||||
|
|
||||||
|
// Load the only offline comment allowed if any.
|
||||||
|
promises.push(this.offlineComments.getComment(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
|
this.area).then((offlineComment) => {
|
||||||
|
|
||||||
|
if (offlineComment && !this.currentUser) {
|
||||||
|
offlineComment.userid = this.currentUserId;
|
||||||
|
|
||||||
|
this.loadCommentProfile(offlineComment).then((comment) => {
|
||||||
|
// Save this fields for further requests.
|
||||||
|
if (comment.fullname) {
|
||||||
|
this.currentUser = {};
|
||||||
|
this.currentUser.profileimageurl = comment.profileimageurl;
|
||||||
|
this.currentUser.fullname = comment.fullname;
|
||||||
|
this.currentUser.userid = comment.userid;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (offlineComment) {
|
||||||
|
offlineComment.profileimageurl = this.currentUser.profileimageurl;
|
||||||
|
offlineComment.fullname = this.currentUser.fullname;
|
||||||
|
offlineComment.userid = this.currentUser.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.offlineComment = offlineComment;
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Load deleted comments offline.
|
||||||
|
promises.push(this.offlineComments.getDeletedComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
|
this.area).then((deletedComments) => {
|
||||||
|
hasDeletedComments = deletedComments && deletedComments.length > 0;
|
||||||
|
|
||||||
|
hasDeletedComments && deletedComments.forEach((deletedComment) => {
|
||||||
|
const comment = this.comments.find((comment) => {
|
||||||
|
return comment.id == deletedComment.commentid;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
comment.deleted = deletedComment.deleted;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
return Promise.all(promises).then(() => {
|
||||||
|
this.hasOffline = !!this.offlineComment || hasDeletedComments;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore a comment.
|
* Restore a comment.
|
||||||
*
|
*
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { CoreCommentsOfflineProvider } from './offline';
|
||||||
export class CoreCommentsProvider {
|
export class CoreCommentsProvider {
|
||||||
|
|
||||||
static REFRESH_COMMENTS_EVENT = 'core_comments_refresh_comments';
|
static REFRESH_COMMENTS_EVENT = 'core_comments_refresh_comments';
|
||||||
|
static COMMENTS_COUNT_CHANGED_EVENT = 'core_comments_count_changed';
|
||||||
|
|
||||||
protected ROOT_CACHE_KEY = 'mmComments:';
|
protected ROOT_CACHE_KEY = 'mmComments:';
|
||||||
static pageSize = 1; // At least it will be one.
|
static pageSize = 1; // At least it will be one.
|
||||||
|
@ -162,15 +163,18 @@ export class CoreCommentsProvider {
|
||||||
*
|
*
|
||||||
* @param comment Comment object to delete.
|
* @param comment Comment object to delete.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @return Promise resolved when deleted, rejected otherwise. Promise resolved doesn't mean that comments
|
* @return Promise resolved when deleted (with true if deleted in online, false otherwise), rejected otherwise. Promise resolved
|
||||||
* have been deleted, the resolve param can contain errors for comments not deleted.
|
* doesn't mean that comments have been deleted, the resolve param can contain errors for comments not deleted.
|
||||||
*/
|
*/
|
||||||
deleteComment(comment: any, siteId?: string): Promise<void> {
|
deleteComment(comment: any, siteId?: string): Promise<boolean> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
// Offline comment, just delete it.
|
||||||
if (!comment.id) {
|
if (!comment.id) {
|
||||||
return this.commentsOffline.removeComment(comment.contextlevel, comment.instanceid, comment.component, comment.itemid,
|
return this.commentsOffline.removeComment(comment.contextlevel, comment.instanceid, comment.component, comment.itemid,
|
||||||
comment.area, siteId);
|
comment.area, siteId).then(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience function to store the action to be synchronized later.
|
// Convenience function to store the action to be synchronized later.
|
||||||
|
|
|
@ -159,6 +159,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
const errors = [],
|
const errors = [],
|
||||||
promises = [],
|
promises = [],
|
||||||
deleteCommentIds = [];
|
deleteCommentIds = [];
|
||||||
|
let countChange = 0;
|
||||||
|
|
||||||
comments.forEach((comment) => {
|
comments.forEach((comment) => {
|
||||||
if (comment.commentid) {
|
if (comment.commentid) {
|
||||||
|
@ -166,6 +167,8 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
} else {
|
} else {
|
||||||
promises.push(this.commentsProvider.addCommentOnline(comment.content, contextLevel, instanceId, component,
|
promises.push(this.commentsProvider.addCommentOnline(comment.content, contextLevel, instanceId, component,
|
||||||
itemId, area, siteId).then((response) => {
|
itemId, area, siteId).then((response) => {
|
||||||
|
countChange++;
|
||||||
|
|
||||||
return this.commentsOffline.removeComment(contextLevel, instanceId, component, itemId, area, siteId);
|
return this.commentsOffline.removeComment(contextLevel, instanceId, component, itemId, area, siteId);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -174,6 +177,8 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
if (deleteCommentIds.length > 0) {
|
if (deleteCommentIds.length > 0) {
|
||||||
promises.push(this.commentsProvider.deleteCommentsOnline(deleteCommentIds, contextLevel, instanceId, component,
|
promises.push(this.commentsProvider.deleteCommentsOnline(deleteCommentIds, contextLevel, instanceId, component,
|
||||||
itemId, area, siteId).then((response) => {
|
itemId, area, siteId).then((response) => {
|
||||||
|
countChange--;
|
||||||
|
|
||||||
return this.commentsOffline.removeDeletedComments(contextLevel, instanceId, component, itemId, area,
|
return this.commentsOffline.removeDeletedComments(contextLevel, instanceId, component, itemId, area,
|
||||||
siteId);
|
siteId);
|
||||||
}));
|
}));
|
||||||
|
@ -181,6 +186,15 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
|
|
||||||
// Send the comments.
|
// Send the comments.
|
||||||
return Promise.all(promises).then(() => {
|
return Promise.all(promises).then(() => {
|
||||||
|
this.eventsProvider.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, {
|
||||||
|
contextLevel: contextLevel,
|
||||||
|
instanceId: instanceId,
|
||||||
|
component: component,
|
||||||
|
itemId: itemId,
|
||||||
|
area: area,
|
||||||
|
countChange: countChange,
|
||||||
|
}, this.sitesProvider.getCurrentSiteId());
|
||||||
|
|
||||||
// Fetch the comments from server to be sure they're up to date.
|
// Fetch the comments from server to be sure they're up to date.
|
||||||
return this.commentsProvider.invalidateCommentsData(contextLevel, instanceId, component, itemId, area, siteId)
|
return this.commentsProvider.invalidateCommentsData(contextLevel, instanceId, component, itemId, area, siteId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
Loading…
Reference in New Issue