MOBILE-2877 comments: Delete comments
parent
79bdd4ed02
commit
8a56dc84b8
|
@ -169,11 +169,13 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
* @param {any} refresher Refresher instance.
|
* @param {any} refresher Refresher instance.
|
||||||
*/
|
*/
|
||||||
refresh(refresher?: any): void {
|
refresh(refresher?: any): void {
|
||||||
this.entries.forEach((entry) => {
|
const promises = this.entries.map((entry) => {
|
||||||
this.commentsProvider.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog');
|
return this.commentsProvider.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.blogProvider.invalidateEntries(this.filter).finally(() => {
|
promises.push(this.blogProvider.invalidateEntries(this.filter));
|
||||||
|
|
||||||
|
Promise.all(promises).finally(() => {
|
||||||
this.fetchEntries(true).finally(() => {
|
this.fetchEntries(true).finally(() => {
|
||||||
if (refresher) {
|
if (refresher) {
|
||||||
refresher.complete();
|
refresher.complete();
|
||||||
|
|
|
@ -1269,9 +1269,12 @@
|
||||||
"core.comments.comments": "Comments",
|
"core.comments.comments": "Comments",
|
||||||
"core.comments.commentscount": "Comments ({{$a}})",
|
"core.comments.commentscount": "Comments ({{$a}})",
|
||||||
"core.comments.commentsnotworking": "Comments cannot be retrieved",
|
"core.comments.commentsnotworking": "Comments cannot be retrieved",
|
||||||
|
"core.comments.deletecommentbyon": "Delete comment posted by {{$a.user}} on {{$a.time}}",
|
||||||
"core.comments.eventcommentcreated": "Comment created",
|
"core.comments.eventcommentcreated": "Comment created",
|
||||||
|
"core.comments.eventcommentdeleted": "Comment deleted",
|
||||||
"core.comments.nocomments": "No comments",
|
"core.comments.nocomments": "No comments",
|
||||||
"core.comments.savecomment": "Save comment",
|
"core.comments.savecomment": "Save comment",
|
||||||
|
"core.comments.warningcommentsnotsent": "Couldn't sync comments. {{error}}",
|
||||||
"core.completion-alt-auto-fail": "Completed: {{$a}} (did not achieve pass grade)",
|
"core.completion-alt-auto-fail": "Completed: {{$a}} (did not achieve pass grade)",
|
||||||
"core.completion-alt-auto-n": "Not completed: {{$a}}",
|
"core.completion-alt-auto-n": "Not completed: {{$a}}",
|
||||||
"core.completion-alt-auto-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}})",
|
"core.completion-alt-auto-n-override": "Not completed: {{$a.modname}} (set by {{$a.overrideuser}})",
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
"comments": "Comments",
|
"comments": "Comments",
|
||||||
"commentscount": "Comments ({{$a}})",
|
"commentscount": "Comments ({{$a}})",
|
||||||
"commentsnotworking": "Comments cannot be retrieved",
|
"commentsnotworking": "Comments cannot be retrieved",
|
||||||
|
"deletecommentbyon": "Delete comment posted by {{$a.user}} on {{$a.time}}",
|
||||||
"eventcommentcreated": "Comment created",
|
"eventcommentcreated": "Comment created",
|
||||||
|
"eventcommentdeleted": "Comment deleted",
|
||||||
"nocomments": "No comments",
|
"nocomments": "No comments",
|
||||||
"savecomment": "Save comment"
|
"savecomment": "Save comment",
|
||||||
|
"warningcommentsnotsent": "Couldn't sync comments. {{error}}"
|
||||||
}
|
}
|
|
@ -57,7 +57,7 @@ export class CoreCommentsAddPage {
|
||||||
|
|
||||||
this.appProvider.closeKeyboard();
|
this.appProvider.closeKeyboard();
|
||||||
const loadingModal = this.domUtils.showModalLoading('core.sending', true);
|
const loadingModal = this.domUtils.showModalLoading('core.sending', true);
|
||||||
// Freeze the add note button.
|
// Freeze the add comment button.
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this.commentsProvider.addComment(this.content, this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
this.commentsProvider.addComment(this.content, this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
this.area).then((commentsResponse) => {
|
this.area).then((commentsResponse) => {
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
<ion-navbar core-back-button>
|
<ion-navbar core-back-button>
|
||||||
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
<ion-title><core-format-text [text]="title"></core-format-text></ion-title>
|
||||||
<ion-buttons end>
|
<ion-buttons end>
|
||||||
|
<button *ngIf="canDeleteComments" item-end ion-button icon-only clear (click)="toggleDelete($event)" [attr.aria-label]="'core.delete' | translate">
|
||||||
|
<ion-icon name="create" ios="md-create"></ion-icon>
|
||||||
|
</button>
|
||||||
<core-context-menu>
|
<core-context-menu>
|
||||||
<core-context-menu-item [hidden]="!(commentsLoaded && !hasOffline)" [priority]="100" [content]="'core.refresh' | translate" (action)="refreshComments(false)" [iconAction]="refreshIcon" [closeOnClick]="true"></core-context-menu-item>
|
<core-context-menu-item [hidden]="!(commentsLoaded && !hasOffline)" [priority]="100" [content]="'core.refresh' | translate" (action)="refreshComments(false)" [iconAction]="refreshIcon" [closeOnClick]="true"></core-context-menu-item>
|
||||||
<core-context-menu-item [hidden]="!(commentsLoaded && hasOffline)" [priority]="100" [content]="'core.settings.synchronizenow' | translate" (action)="refreshComments(true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
<core-context-menu-item [hidden]="!(commentsLoaded && hasOffline)" [priority]="100" [content]="'core.settings.synchronizenow' | translate" (action)="refreshComments(true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
|
@ -21,13 +24,16 @@
|
||||||
{{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }}
|
{{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-card *ngIf="hasOffline" (click)="addComment($event)">
|
<ion-card *ngIf="offlineComment" (click)="addComment($event)">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-avatar core-user-avatar [user]="offlineComment" item-start></ion-avatar>
|
<ion-avatar core-user-avatar [user]="offlineComment" item-start></ion-avatar>
|
||||||
<h2>{{ offlineComment.fullname }}</h2>
|
<h2>{{ offlineComment.fullname }}</h2>
|
||||||
<p>
|
<p>
|
||||||
<ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}
|
<ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}
|
||||||
</p>
|
</p>
|
||||||
|
<button *ngIf="showDelete" item-end ion-button icon-only clear [@coreSlideInOut]="'fromRight'" color="danger" (click)="deleteComment($event, offlineComment)" [attr.aria-label]="'core.delete' | translate">
|
||||||
|
<ion-icon name="trash"></ion-icon>
|
||||||
|
</button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<core-format-text clean="true" [text]="offlineComment.content"></core-format-text>
|
<core-format-text clean="true" [text]="offlineComment.content"></core-format-text>
|
||||||
|
@ -38,7 +44,16 @@
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<ion-avatar core-user-avatar [user]="comment" item-start></ion-avatar>
|
<ion-avatar core-user-avatar [user]="comment" item-start></ion-avatar>
|
||||||
<h2>{{ comment.fullname }}</h2>
|
<h2>{{ comment.fullname }}</h2>
|
||||||
<p>{{ comment.timecreated * 1000 | coreFormatDate: 'strftimerecentfull' }}</p>
|
<p *ngIf="!comment.deleted">{{ comment.timecreated * 1000 | coreFormatDate: 'strftimerecentfull' }}</p>
|
||||||
|
<p *ngIf="comment.deleted">
|
||||||
|
<ion-icon name="trash"></ion-icon> <span text-wrap>{{ 'core.deletedoffline' | translate }}</span>
|
||||||
|
</p>
|
||||||
|
<button *ngIf="showDelete && !comment.deleted && comment.delete" item-end ion-button icon-only clear [@coreSlideInOut]="'fromRight'" color="danger" (click)="deleteComment($event, comment)" [attr.aria-label]="'core.delete' | translate">
|
||||||
|
<ion-icon name="trash"></ion-icon>
|
||||||
|
</button>
|
||||||
|
<button *ngIf="showDelete && comment.deleted" item-end ion-button icon-only clear color="danger" (click)="undoDeleteComment($event, comment)" [attr.aria-label]="'core.restore' | translate">
|
||||||
|
<ion-icon name="undo"></ion-icon>
|
||||||
|
</button>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
<core-format-text clean="true" [text]="comment.content"></core-format-text>
|
<core-format-text clean="true" [text]="comment.content"></core-format-text>
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
import { Component, ViewChild, OnDestroy } from '@angular/core';
|
import { Component, ViewChild, OnDestroy } from '@angular/core';
|
||||||
import { IonicPage, Content, NavParams, ModalController } from 'ionic-angular';
|
import { IonicPage, Content, NavParams, ModalController } from 'ionic-angular';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { coreSlideInOut } from '@classes/animations';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { CoreCommentsProvider } from '../../providers/comments';
|
import { CoreCommentsProvider } from '../../providers/comments';
|
||||||
|
@ -31,6 +33,7 @@ import { CoreCommentsSyncProvider } from '../../providers/sync';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'page-core-comments-viewer',
|
selector: 'page-core-comments-viewer',
|
||||||
templateUrl: 'viewer.html',
|
templateUrl: 'viewer.html',
|
||||||
|
animations: [coreSlideInOut]
|
||||||
})
|
})
|
||||||
export class CoreCommentsViewerPage implements OnDestroy {
|
export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
@ViewChild(Content) content: Content;
|
@ViewChild(Content) content: Content;
|
||||||
|
@ -47,12 +50,15 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
canAddComments = false;
|
canAddComments = false;
|
||||||
|
canDeleteComments = false;
|
||||||
|
showDelete = false;
|
||||||
hasOffline = false;
|
hasOffline = false;
|
||||||
refreshIcon = 'spinner';
|
refreshIcon = 'spinner';
|
||||||
syncIcon = 'spinner';
|
syncIcon = 'spinner';
|
||||||
offlineComment: any;
|
offlineComment: any;
|
||||||
|
currentUserId: number;
|
||||||
|
|
||||||
protected addCommentsAvailable = false;
|
protected addDeleteCommentsAvailable = false;
|
||||||
protected syncObserver: any;
|
protected syncObserver: any;
|
||||||
protected currentUser: any;
|
protected currentUser: any;
|
||||||
|
|
||||||
|
@ -60,7 +66,7 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private modalCtrl: ModalController,
|
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private modalCtrl: ModalController,
|
||||||
private commentsProvider: CoreCommentsProvider, private offlineComments: CoreCommentsOfflineProvider,
|
private commentsProvider: CoreCommentsProvider, private offlineComments: CoreCommentsOfflineProvider,
|
||||||
eventsProvider: CoreEventsProvider, private commentsSync: CoreCommentsSyncProvider,
|
eventsProvider: CoreEventsProvider, private commentsSync: CoreCommentsSyncProvider,
|
||||||
private textUtils: CoreTextUtilsProvider) {
|
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider) {
|
||||||
|
|
||||||
this.contextLevel = navParams.get('contextLevel');
|
this.contextLevel = navParams.get('contextLevel');
|
||||||
this.instanceId = navParams.get('instanceId');
|
this.instanceId = navParams.get('instanceId');
|
||||||
|
@ -96,34 +102,35 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
*/
|
*/
|
||||||
ionViewDidLoad(): void {
|
ionViewDidLoad(): void {
|
||||||
this.commentsProvider.isAddCommentsAvailable().then((enabled) => {
|
this.commentsProvider.isAddCommentsAvailable().then((enabled) => {
|
||||||
this.addCommentsAvailable = enabled;
|
// Is implicit the user can delete if he can add.
|
||||||
|
this.addDeleteCommentsAvailable = enabled;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.currentUserId = this.sitesProvider.getCurrentSiteUserId();
|
||||||
this.fetchComments(true);
|
this.fetchComments(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the comments.
|
* Fetches the comments.
|
||||||
*
|
*
|
||||||
* @param {boolean} sync When to resync notes.
|
* @param {boolean} sync When to resync comments.
|
||||||
* @param {boolean} [showErrors] When to display errors or not.
|
* @param {boolean} [showErrors] When to display errors or not.
|
||||||
* @return {Promise<any>} Resolved when done.
|
* @return {Promise<any>} Resolved when done.
|
||||||
*/
|
*/
|
||||||
protected fetchComments(sync: boolean, showErrors?: boolean): Promise<any> {
|
protected fetchComments(sync: boolean, showErrors?: boolean): Promise<any> {
|
||||||
this.loadMoreError = false;
|
this.loadMoreError = false;
|
||||||
|
|
||||||
const promise = sync ? this.syncComment(showErrors) : Promise.resolve();
|
const promise = sync ? this.syncComments(showErrors) : Promise.resolve();
|
||||||
|
|
||||||
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.offlineComments.getComment(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
this.area).then((offlineComment) => {
|
this.area).then((offlineComment) => {
|
||||||
this.hasOffline = !!offlineComment;
|
|
||||||
this.offlineComment = offlineComment;
|
this.offlineComment = offlineComment;
|
||||||
|
|
||||||
if (this.hasOffline && !this.currentUser) {
|
if (offlineComment && !this.currentUser) {
|
||||||
return this.userProvider.getProfile(this.sitesProvider.getCurrentSiteUserId(), undefined, true).then((user) => {
|
return this.userProvider.getProfile(this.currentUserId, undefined, true).then((user) => {
|
||||||
this.currentUser = user;
|
this.currentUser = user;
|
||||||
this.offlineComment.profileimageurl = user.profileimageurl;
|
this.offlineComment.profileimageurl = user.profileimageurl;
|
||||||
this.offlineComment.fullname = user.fullname;
|
this.offlineComment.fullname = user.fullname;
|
||||||
|
@ -131,32 +138,53 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
});
|
});
|
||||||
} else if (this.hasOffline) {
|
} else if (offlineComment) {
|
||||||
this.offlineComment.profileimageurl = this.currentUser.profileimageurl;
|
this.offlineComment.profileimageurl = this.currentUser.profileimageurl;
|
||||||
this.offlineComment.fullname = this.currentUser.fullname;
|
this.offlineComment.fullname = this.currentUser.fullname;
|
||||||
this.offlineComment.userid = this.currentUser.id;
|
this.offlineComment.userid = this.currentUser.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.offlineComments.getDeletedComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
|
this.area);
|
||||||
});
|
});
|
||||||
}).then(() => {
|
}).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) => {
|
||||||
this.canAddComments = this.addCommentsAvailable && response.canpost;
|
this.canAddComments = this.addDeleteCommentsAvailable && response.canpost;
|
||||||
|
|
||||||
const comments = response.comments.sort((a, b) => b.timecreated - a.timecreated);
|
const comments = response.comments.sort((a, b) => b.timecreated - a.timecreated);
|
||||||
this.canLoadMore = comments.length >= CoreCommentsProvider.pageSize;
|
this.canLoadMore = comments.length >= CoreCommentsProvider.pageSize;
|
||||||
|
|
||||||
this.comments.forEach((comment) => {
|
return Promise.all(comments.map((comment) => {
|
||||||
// Get the user profile image.
|
// Get the user profile image.
|
||||||
this.userProvider.getProfile(comment.userid, undefined, true).then((user) => {
|
return this.userProvider.getProfile(comment.userid, undefined, true).then((user) => {
|
||||||
comment.profileimageurl = user.profileimageurl;
|
comment.profileimageurl = user.profileimageurl;
|
||||||
|
|
||||||
|
return comment;
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
|
return comment;
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
|
}).then((comments) => {
|
||||||
|
this.comments = this.comments.concat(comments);
|
||||||
|
|
||||||
|
deletedComments && deletedComments.forEach((deletedComment) => {
|
||||||
|
const comment = this.comments.find((comment) => {
|
||||||
|
return comment.id == deletedComment.commentid;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.comments = this.comments.concat(comments);
|
if (comment) {
|
||||||
|
comment.deleted = deletedComment.deleted;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.canDeleteComments = this.addDeleteCommentsAvailable && (this.hasOffline || this.comments.some((comment) => {
|
||||||
|
return !!comment.delete;
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
|
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
|
||||||
|
@ -174,7 +202,7 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to load more cp,,emts.
|
* Function to load more commemts.
|
||||||
*
|
*
|
||||||
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
|
* @param {any} [infiniteComplete] Infinite scroll complete function. Only used from core-infinite-loading.
|
||||||
* @return {Promise<any>} Resolved when done.
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
@ -228,8 +256,8 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
* @param {boolean} showErrors Whether to display errors or not.
|
* @param {boolean} showErrors Whether to display errors or not.
|
||||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
private syncComment(showErrors: boolean): Promise<any> {
|
private syncComments(showErrors: boolean): Promise<any> {
|
||||||
return this.commentsSync.syncComment(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
return this.commentsSync.syncComments(this.contextLevel, this.instanceId, this.componentName, this.itemId,
|
||||||
this.area).then((warnings) => {
|
this.area).then((warnings) => {
|
||||||
this.showSyncWarnings(warnings);
|
this.showSyncWarnings(warnings);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -263,6 +291,7 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
modal.onDidDismiss((data) => {
|
modal.onDidDismiss((data) => {
|
||||||
if (data && data.comments) {
|
if (data && data.comments) {
|
||||||
this.comments = data.comments.concat(this.comments);
|
this.comments = data.comments.concat(this.comments);
|
||||||
|
this.canDeleteComments = this.addDeleteCommentsAvailable;
|
||||||
} else if (data && !data.comments) {
|
} else if (data && !data.comments) {
|
||||||
this.fetchComments(false);
|
this.fetchComments(false);
|
||||||
}
|
}
|
||||||
|
@ -270,6 +299,63 @@ export class CoreCommentsViewerPage implements OnDestroy {
|
||||||
modal.present();
|
modal.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a comment.
|
||||||
|
*
|
||||||
|
* @param {Event} e Click event.
|
||||||
|
* @param {any} comment Comment to delete.
|
||||||
|
*/
|
||||||
|
deleteComment(e: Event, comment: any): void {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const time = this.timeUtils.userDate((comment.lastmodified || comment.timecreated) * 1000, 'core.strftimerecentfull');
|
||||||
|
|
||||||
|
comment.contextlevel = this.contextLevel;
|
||||||
|
comment.instanceid = this.instanceId;
|
||||||
|
comment.component = this.componentName;
|
||||||
|
comment.itemid = this.itemId;
|
||||||
|
comment.area = this.area;
|
||||||
|
|
||||||
|
this.domUtils.showConfirm(this.translate.instant('core.comments.deletecommentbyon', {$a:
|
||||||
|
{ user: comment.fullname || '', time: time } })).then(() => {
|
||||||
|
this.commentsProvider.deleteComment(comment).then(() => {
|
||||||
|
this.showDelete = false;
|
||||||
|
|
||||||
|
this.refreshComments(true);
|
||||||
|
|
||||||
|
this.domUtils.showToast('core.comments.eventcommentdeleted', true, 3000);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Delete comment failed.');
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
// User cancelled, nothing to do.
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore a comment.
|
||||||
|
*
|
||||||
|
* @param {Event} e Click event.
|
||||||
|
* @param {any} comment Comment to delete.
|
||||||
|
*/
|
||||||
|
undoDeleteComment(e: Event, comment: any): void {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.offlineComments.undoDeleteComment(comment.id).then(() => {
|
||||||
|
comment.deleted = false;
|
||||||
|
this.showDelete = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle delete.
|
||||||
|
*/
|
||||||
|
toggleDelete(): void {
|
||||||
|
this.showDelete = !this.showDelete;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page destroyed.
|
* Page destroyed.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -48,7 +48,7 @@ export class CoreCommentsProvider {
|
||||||
siteId?: string): Promise<boolean> {
|
siteId?: string): Promise<boolean> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
// Convenience function to store a note to be synchronized later.
|
// Convenience function to store a comment to be synchronized later.
|
||||||
const storeOffline = (): Promise<any> => {
|
const storeOffline = (): Promise<any> => {
|
||||||
return this.commentsOffline.saveComment(content, contextLevel, instanceId, component, itemId, area, siteId).then(() => {
|
return this.commentsOffline.saveComment(content, contextLevel, instanceId, component, itemId, area, siteId).then(() => {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
|
@ -56,11 +56,11 @@ export class CoreCommentsProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!this.appProvider.isOnline()) {
|
if (!this.appProvider.isOnline()) {
|
||||||
// App is offline, store the note.
|
// App is offline, store the comment.
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send note to server.
|
// Send comment to server.
|
||||||
return this.addCommentOnline(content, contextLevel, instanceId, component, itemId, area, siteId).then((comments) => {
|
return this.addCommentOnline(content, contextLevel, instanceId, component, itemId, area, siteId).then((comments) => {
|
||||||
return comments;
|
return comments;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
@ -69,7 +69,7 @@ export class CoreCommentsProvider {
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error sending note, store it to retry later.
|
// Error sending comment, store it to retry later.
|
||||||
return storeOffline();
|
return storeOffline();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ export class CoreCommentsProvider {
|
||||||
* @param {any[]} comments Comments to save.
|
* @param {any[]} comments Comments to save.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @return {Promise<any>} Promise resolved when added, rejected otherwise. Promise resolved doesn't mean that comments
|
* @return {Promise<any>} Promise resolved when added, rejected otherwise. Promise resolved doesn't mean that comments
|
||||||
* have been added, the resolve param can contain errors for notes not sent.
|
* have been added, the resolve param can contain errors for comments not sent.
|
||||||
*/
|
*/
|
||||||
addCommentsOnline(comments: any[], siteId?: string): Promise<any> {
|
addCommentsOnline(comments: any[], siteId?: string): Promise<any> {
|
||||||
if (!comments || !comments.length) {
|
if (!comments || !comments.length) {
|
||||||
|
@ -155,6 +155,79 @@ export class CoreCommentsProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a comment.
|
||||||
|
*
|
||||||
|
* @param {any} comment Comment object to delete.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<void>} Promise resolved when deleted, rejected otherwise. Promise resolved 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> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
if (!comment.id) {
|
||||||
|
return this.commentsOffline.removeComment(comment.contextlevel, comment.instanceid, comment.component, comment.itemid,
|
||||||
|
comment.area, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience function to store the action to be synchronized later.
|
||||||
|
const storeOffline = (): Promise<any> => {
|
||||||
|
return this.commentsOffline.deleteComment(comment.id, comment.contextlevel, comment.instanceid, comment.component,
|
||||||
|
comment.itemid, comment.area, siteId).then(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.appProvider.isOnline()) {
|
||||||
|
// App is offline, store the comment.
|
||||||
|
return storeOffline();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send comment to server.
|
||||||
|
return this.deleteCommentsOnline([comment.id], comment.contextlevel, comment.instanceid, comment.component, comment.itemid,
|
||||||
|
comment.area, siteId).then(() => {
|
||||||
|
return true;
|
||||||
|
}).catch((error) => {
|
||||||
|
if (this.utils.isWebServiceError(error)) {
|
||||||
|
// It's a WebService error, the user cannot send the comment so don't store it.
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error sending comment, store it to retry later.
|
||||||
|
return storeOffline();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a comment. It will fail if offline or cannot connect.
|
||||||
|
*
|
||||||
|
* @param {number[]} commentIds Comment IDs to delete.
|
||||||
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {number} itemId Associated id.
|
||||||
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<void>} Promise resolved when deleted, rejected otherwise. Promise resolved doesn't mean that comments
|
||||||
|
* have been deleted, the resolve param can contain errors for comments not deleted.
|
||||||
|
*/
|
||||||
|
deleteCommentsOnline(commentIds: number[], contextLevel: string, instanceId: number, component: string, itemId: number,
|
||||||
|
area: string = '', siteId?: string): Promise<void> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = {
|
||||||
|
comments: commentIds
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.write('core_comment_delete_comments', data).then((response) => {
|
||||||
|
// A comment was deleted, invalidate comments.
|
||||||
|
return this.invalidateCommentsData(contextLevel, instanceId, component, itemId, area, siteId).catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether WS to add/delete comments are available in site.
|
* Returns whether WS to add/delete comments are available in site.
|
||||||
*
|
*
|
||||||
|
@ -239,7 +312,7 @@ export class CoreCommentsProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comments count number to show ont he comments component.
|
* Get comments count number to show on the comments component.
|
||||||
*
|
*
|
||||||
* @param {string} contextLevel Contextlevel system, course, user...
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
* @param {number} instanceId The Instance id of item associated with the context level.
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
@ -284,7 +357,6 @@ export class CoreCommentsProvider {
|
||||||
return getCommentsPageCount(1).then((countMore) => {
|
return getCommentsPageCount(1).then((countMore) => {
|
||||||
// Page limit was reached on the previous call.
|
// Page limit was reached on the previous call.
|
||||||
if (countMore > 0) {
|
if (countMore > 0) {
|
||||||
CoreCommentsProvider.pageSizeOK = true;
|
|
||||||
|
|
||||||
return (CoreCommentsProvider.pageSize - 1) + '+';
|
return (CoreCommentsProvider.pageSize - 1) + '+';
|
||||||
}
|
}
|
||||||
|
@ -308,11 +380,14 @@ export class CoreCommentsProvider {
|
||||||
invalidateCommentsData(contextLevel: string, instanceId: number, component: string, itemId: number,
|
invalidateCommentsData(contextLevel: string, instanceId: number, component: string, itemId: number,
|
||||||
area: string = '', siteId?: string): Promise<any> {
|
area: string = '', siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
|
||||||
|
return this.utils.allPromises([
|
||||||
// This is done with starting with to avoid conflicts with previous keys that were including page.
|
// This is done with starting with to avoid conflicts with previous keys that were including page.
|
||||||
site.invalidateWsCacheForKeyStartingWith(this.getCommentsCacheKey(contextLevel, instanceId, component, itemId,
|
site.invalidateWsCacheForKeyStartingWith(this.getCommentsCacheKey(contextLevel, instanceId, component, itemId,
|
||||||
area) + ':');
|
area) + ':'),
|
||||||
|
|
||||||
return site.invalidateWsCacheForKey(this.getCommentsCacheKey(contextLevel, instanceId, component, itemId, area));
|
site.invalidateWsCacheForKey(this.getCommentsCacheKey(contextLevel, instanceId, component, itemId, area))
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ export class CoreCommentsOfflineProvider {
|
||||||
|
|
||||||
// Variables for database.
|
// Variables for database.
|
||||||
static COMMENTS_TABLE = 'core_comments_offline_comments';
|
static COMMENTS_TABLE = 'core_comments_offline_comments';
|
||||||
|
static COMMENTS_DELETED_TABLE = 'core_comments_deleted_offline_comments';
|
||||||
protected siteSchema: CoreSiteSchema = {
|
protected siteSchema: CoreSiteSchema = {
|
||||||
name: 'CoreCommentsOfflineProvider',
|
name: 'CoreCommentsOfflineProvider',
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -55,16 +56,46 @@ export class CoreCommentsOfflineProvider {
|
||||||
name: 'content',
|
name: 'content',
|
||||||
type: 'TEXT'
|
type: 'TEXT'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'action',
|
|
||||||
type: 'TEXT'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'lastmodified',
|
name: 'lastmodified',
|
||||||
type: 'INTEGER'
|
type: 'INTEGER'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
primaryKeys: ['contextlevel', 'instanceid', 'component', 'itemid', 'area']
|
primaryKeys: ['contextlevel', 'instanceid', 'component', 'itemid', 'area']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'commentid',
|
||||||
|
type: 'INTEGER',
|
||||||
|
primaryKey: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'contextlevel',
|
||||||
|
type: 'TEXT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'instanceid',
|
||||||
|
type: 'INTEGER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'component',
|
||||||
|
type: 'TEXT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'itemid',
|
||||||
|
type: 'INTEGER'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'area',
|
||||||
|
type: 'TEXT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'deleted',
|
||||||
|
type: 'INTEGER'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
@ -73,30 +104,6 @@ export class CoreCommentsOfflineProvider {
|
||||||
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a comment.
|
|
||||||
*
|
|
||||||
* @param {string} contextLevel Contextlevel system, course, user...
|
|
||||||
* @param {number} instanceId The Instance id of item associated with the context level.
|
|
||||||
* @param {string} component Component name.
|
|
||||||
* @param {number} itemId Associated id.
|
|
||||||
* @param {string} [area=''] String comment area. Default empty.
|
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
||||||
* @return {Promise<any>} Promise resolved if deleted, rejected if failure.
|
|
||||||
*/
|
|
||||||
removeComment(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
|
||||||
siteId?: string): Promise<any> {
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
|
||||||
return site.getDb().deleteRecords(CoreCommentsOfflineProvider.COMMENTS_TABLE, {
|
|
||||||
contextlevel: contextLevel,
|
|
||||||
instanceid: instanceId,
|
|
||||||
component: component,
|
|
||||||
itemid: itemId,
|
|
||||||
area: area
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all offline comments.
|
* Get all offline comments.
|
||||||
*
|
*
|
||||||
|
@ -105,7 +112,10 @@ export class CoreCommentsOfflineProvider {
|
||||||
*/
|
*/
|
||||||
getAllComments(siteId?: string): Promise<any> {
|
getAllComments(siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
return site.getDb().getRecords(CoreCommentsOfflineProvider.COMMENTS_TABLE);
|
return Promise.all([site.getDb().getRecords(CoreCommentsOfflineProvider.COMMENTS_TABLE),
|
||||||
|
site.getDb().getRecords(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE)]).then((results) => {
|
||||||
|
return [].concat.apply([], results);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,19 +146,116 @@ export class CoreCommentsOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if there are offline comments.
|
* Get all offline comments added or deleted of a special area.
|
||||||
*
|
*
|
||||||
* @param {string} contextLevel Contextlevel system, course, user...
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
* @param {number} instanceId The Instance id of item associated with the context level.
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
* @param {string} component Component name.
|
* @param {string} component Component name.
|
||||||
* @param {number} itemId Associated id.
|
* @param {number} itemId Associated id.
|
||||||
* @param {string} [area=''] String comment area. Default empty.
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
* @return {Promise<boolean>} Promise resolved with boolean: true if has offline comments, false otherwise.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved with the comments.
|
||||||
*/
|
*/
|
||||||
hasComments(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
getComments(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
siteId?: string): Promise<boolean> {
|
siteId?: string): Promise<any> {
|
||||||
return this.getComment(contextLevel, instanceId, component, itemId, area, siteId).then((comments) => {
|
let comments = [];
|
||||||
return !!comments.length;
|
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.getComment(contextLevel, instanceId, component, itemId, area, siteId).then((comment) => {
|
||||||
|
comments = comment ? [comment] : [];
|
||||||
|
|
||||||
|
return this.getDeletedComments(contextLevel, instanceId, component, itemId, area, siteId);
|
||||||
|
}).then((deletedComments) => {
|
||||||
|
comments = comments.concat(deletedComments);
|
||||||
|
|
||||||
|
return comments;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all offline deleted comments.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved with comments.
|
||||||
|
*/
|
||||||
|
getAllDeletedComments(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().getRecords(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an offline comment.
|
||||||
|
*
|
||||||
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {number} itemId Associated id.
|
||||||
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved with the comments.
|
||||||
|
*/
|
||||||
|
getDeletedComments(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().getRecords(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE, {
|
||||||
|
contextlevel: contextLevel,
|
||||||
|
instanceid: instanceId,
|
||||||
|
component: component,
|
||||||
|
itemid: itemId,
|
||||||
|
area: area
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an offline comment.
|
||||||
|
*
|
||||||
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {number} itemId Associated id.
|
||||||
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved if deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
removeComment(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().deleteRecords(CoreCommentsOfflineProvider.COMMENTS_TABLE, {
|
||||||
|
contextlevel: contextLevel,
|
||||||
|
instanceid: instanceId,
|
||||||
|
component: component,
|
||||||
|
itemid: itemId,
|
||||||
|
area: area
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an offline deleted comment.
|
||||||
|
*
|
||||||
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {number} itemId Associated id.
|
||||||
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved if deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
removeDeletedComments(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().deleteRecords(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE, {
|
||||||
|
contextlevel: contextLevel,
|
||||||
|
instanceid: instanceId,
|
||||||
|
component: component,
|
||||||
|
itemid: itemId,
|
||||||
|
area: area
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +282,6 @@ export class CoreCommentsOfflineProvider {
|
||||||
itemid: itemId,
|
itemid: itemId,
|
||||||
area: area,
|
area: area,
|
||||||
content: content,
|
content: content,
|
||||||
action: 'add',
|
|
||||||
lastmodified: now
|
lastmodified: now
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -184,4 +290,49 @@ export class CoreCommentsOfflineProvider {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a comment offline to be sent later.
|
||||||
|
*
|
||||||
|
* @param {number} commentId Comment ID.
|
||||||
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {number} itemId Associated id.
|
||||||
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved if stored, rejected if failure.
|
||||||
|
*/
|
||||||
|
deleteComment(commentId: number, contextLevel: string, instanceId: number, component: string, itemId: number,
|
||||||
|
area: string = '', siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const now = this.timeUtils.timestamp();
|
||||||
|
const data = {
|
||||||
|
contextlevel: contextLevel,
|
||||||
|
instanceid: instanceId,
|
||||||
|
component: component,
|
||||||
|
itemid: itemId,
|
||||||
|
area: area,
|
||||||
|
commentid: commentId,
|
||||||
|
deleted: now
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.getDb().insertRecord(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE, data).then(() => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo delete a comment.
|
||||||
|
*
|
||||||
|
* @param {number} commentId Comment ID.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved if deleted, rejected if failure.
|
||||||
|
*/
|
||||||
|
undoDeleteComment(commentId: number, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().deleteRecords(CoreCommentsOfflineProvider.COMMENTS_DELETED_TABLE, { commentid: commentId });
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreCommentsOfflineProvider } from './offline';
|
import { CoreCommentsOfflineProvider } from './offline';
|
||||||
import { CoreCommentsProvider } from './comments';
|
import { CoreCommentsProvider } from './comments';
|
||||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
|
||||||
import { CoreEventsProvider } from '@providers/events';
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
|
@ -39,7 +38,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider, translate: TranslateService,
|
||||||
private commentsOffline: CoreCommentsOfflineProvider, private utils: CoreUtilsProvider,
|
private commentsOffline: CoreCommentsOfflineProvider, private utils: CoreUtilsProvider,
|
||||||
private eventsProvider: CoreEventsProvider, private commentsProvider: CoreCommentsProvider,
|
private eventsProvider: CoreEventsProvider, private commentsProvider: CoreCommentsProvider,
|
||||||
private coursesProvider: CoreCoursesProvider, timeUtils: CoreTimeUtilsProvider) {
|
timeUtils: CoreTimeUtilsProvider) {
|
||||||
|
|
||||||
super('CoreCommentsSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils);
|
super('CoreCommentsSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils);
|
||||||
}
|
}
|
||||||
|
@ -64,10 +63,19 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
*/
|
*/
|
||||||
private syncAllCommentsFunc(siteId: string, force: boolean): Promise<any> {
|
private syncAllCommentsFunc(siteId: string, force: boolean): Promise<any> {
|
||||||
return this.commentsOffline.getAllComments(siteId).then((comments) => {
|
return this.commentsOffline.getAllComments(siteId).then((comments) => {
|
||||||
|
|
||||||
|
// Get Unique array.
|
||||||
|
comments.forEach((comment) => {
|
||||||
|
comment.syncId = this.getSyncId(comment.contextlevel, comment.instanceid, comment.component, comment.itemid,
|
||||||
|
comment.area);
|
||||||
|
});
|
||||||
|
|
||||||
|
comments = this.utils.uniqueArray(comments, 'syncId');
|
||||||
|
|
||||||
// Sync all courses.
|
// Sync all courses.
|
||||||
const promises = comments.map((comment) => {
|
const promises = comments.map((comment) => {
|
||||||
const promise = force ? this.syncComment(comment.contextlevel, comment.instanceid, comment.component,
|
const promise = force ? this.syncComments(comment.contextlevel, comment.instanceid, comment.component,
|
||||||
comment.itemid, comment.area, siteId) : this.syncCommentIfNeeded(comment.contextlevel, comment.instanceid,
|
comment.itemid, comment.area, siteId) : this.syncCommentsIfNeeded(comment.contextlevel, comment.instanceid,
|
||||||
comment.component, comment.itemid, comment.area, siteId);
|
comment.component, comment.itemid, comment.area, siteId);
|
||||||
|
|
||||||
return promise.then((warnings) => {
|
return promise.then((warnings) => {
|
||||||
|
@ -90,7 +98,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync course notes only if a certain time has passed since the last time.
|
* Sync course comments only if a certain time has passed since the last time.
|
||||||
*
|
*
|
||||||
* @param {string} contextLevel Contextlevel system, course, user...
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
* @param {number} instanceId The Instance id of item associated with the context level.
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
@ -98,21 +106,21 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
* @param {number} itemId Associated id.
|
* @param {number} itemId Associated id.
|
||||||
* @param {string} [area=''] String comment area. Default empty.
|
* @param {string} [area=''] String comment area. Default empty.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @return {Promise<any>} Promise resolved when the notes are synced or if they don't need to be synced.
|
* @return {Promise<any>} Promise resolved when the comments are synced or if they don't need to be synced.
|
||||||
*/
|
*/
|
||||||
private syncCommentIfNeeded(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
private syncCommentsIfNeeded(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
siteId?: string): Promise<void> {
|
siteId?: string): Promise<void> {
|
||||||
const syncId = this.getSyncId(contextLevel, instanceId, component, itemId, area);
|
const syncId = this.getSyncId(contextLevel, instanceId, component, itemId, area);
|
||||||
|
|
||||||
return this.isSyncNeeded(syncId, siteId).then((needed) => {
|
return this.isSyncNeeded(syncId, siteId).then((needed) => {
|
||||||
if (needed) {
|
if (needed) {
|
||||||
return this.syncComment(contextLevel, instanceId, component, itemId, area, siteId);
|
return this.syncComments(contextLevel, instanceId, component, itemId, area, siteId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize notes of a course.
|
* Synchronize comments in a particular area.
|
||||||
*
|
*
|
||||||
* @param {string} contextLevel Contextlevel system, course, user...
|
* @param {string} contextLevel Contextlevel system, course, user...
|
||||||
* @param {number} instanceId The Instance id of item associated with the context level.
|
* @param {number} instanceId The Instance id of item associated with the context level.
|
||||||
|
@ -122,14 +130,14 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
||||||
*/
|
*/
|
||||||
syncComment(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
syncComments(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = '',
|
||||||
siteId?: string): Promise<any> {
|
siteId?: string): Promise<any> {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
const syncId = this.getSyncId(contextLevel, instanceId, component, itemId, area);
|
const syncId = this.getSyncId(contextLevel, instanceId, component, itemId, area);
|
||||||
|
|
||||||
if (this.isSyncing(syncId, siteId)) {
|
if (this.isSyncing(syncId, siteId)) {
|
||||||
// There's already a sync ongoing for notes, return the promise.
|
// There's already a sync ongoing for comments, return the promise.
|
||||||
return this.getOngoingSync(syncId, siteId);
|
return this.getOngoingSync(syncId, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +146,9 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
const warnings = [];
|
const warnings = [];
|
||||||
|
|
||||||
// Get offline comments to be sent.
|
// Get offline comments to be sent.
|
||||||
const syncPromise = this.commentsOffline.getComment(contextLevel, instanceId, component, itemId, area, siteId)
|
const syncPromise = this.commentsOffline.getComments(contextLevel, instanceId, component, itemId, area, siteId)
|
||||||
.then((comment) => {
|
.then((comments) => {
|
||||||
if (!comment) {
|
if (!comments.length) {
|
||||||
// Nothing to sync.
|
// Nothing to sync.
|
||||||
return;
|
return;
|
||||||
} else if (!this.appProvider.isOnline()) {
|
} else if (!this.appProvider.isOnline()) {
|
||||||
|
@ -148,19 +156,31 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
return Promise.reject(this.translate.instant('core.networkerrormsg'));
|
return Promise.reject(this.translate.instant('core.networkerrormsg'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const errors = [];
|
const errors = [],
|
||||||
let commentsResponse = [];
|
promises = [],
|
||||||
let promise;
|
deleteCommentIds = [];
|
||||||
|
|
||||||
if (comment.action == 'add') {
|
comments.forEach((comment) => {
|
||||||
promise = this.commentsProvider.addCommentOnline(comment.content, contextLevel, instanceId, component, itemId, area,
|
if (comment.commentid) {
|
||||||
|
deleteCommentIds.push(comment.commentid);
|
||||||
|
} else {
|
||||||
|
promises.push(this.commentsProvider.addCommentOnline(comment.content, contextLevel, instanceId, component,
|
||||||
|
itemId, area, siteId).then((response) => {
|
||||||
|
return this.commentsOffline.removeComment(contextLevel, instanceId, component, itemId, area, siteId);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (deleteCommentIds.length > 0) {
|
||||||
|
promises.push(this.commentsProvider.deleteCommentsOnline(deleteCommentIds, contextLevel, instanceId, component,
|
||||||
|
itemId, area, siteId).then((response) => {
|
||||||
|
return this.commentsOffline.removeDeletedComments(contextLevel, instanceId, component, itemId, area,
|
||||||
siteId);
|
siteId);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the comments.
|
// Send the comments.
|
||||||
return promise.then((response) => {
|
return Promise.all(promises).then(() => {
|
||||||
commentsResponse = response;
|
|
||||||
|
|
||||||
// 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(() => {
|
||||||
|
@ -171,27 +191,15 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider {
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
if (this.utils.isWebServiceError(error)) {
|
if (this.utils.isWebServiceError(error)) {
|
||||||
// It's a WebService error, this means the user cannot send comments.
|
// It's a WebService error, this means the user cannot send comments.
|
||||||
errors.push(error);
|
errors.push(error.message);
|
||||||
} else {
|
} else {
|
||||||
// Not a WebService error, reject the synchronization to try again.
|
// Not a WebService error, reject the synchronization to try again.
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
}).then(() => {
|
|
||||||
// Notes were sent, delete them from local DB.
|
|
||||||
const promises = commentsResponse.map((comment) => {
|
|
||||||
return this.commentsOffline.removeComment(contextLevel, instanceId, component, itemId, area, siteId);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (errors && errors.length) {
|
if (errors && errors.length) {
|
||||||
errors.forEach((error) => {
|
errors.forEach((error) => {
|
||||||
warnings.push(this.translate.instant('addon.notes.warningnotenotsent', {
|
warnings.push(this.translate.instant('core.comments.warningcommentsnotsent', {
|
||||||
contextLevel: contextLevel,
|
|
||||||
instanceId: instanceId,
|
|
||||||
componentName: component,
|
|
||||||
itemId: itemId,
|
|
||||||
area: area,
|
|
||||||
error: error
|
error: error
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue