2021-03-10 16:23:17 +01:00
|
|
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
import { CoreConstants } from '@/core/constants';
|
2021-05-04 15:26:06 +02:00
|
|
|
import { AddonNotesAddComponent, AddonNotesAddModalReturn } from '@addons/notes/components/add/add-modal';
|
|
|
|
import { AddonNotes, AddonNotesNoteFormatted, AddonNotesPublishState } from '@addons/notes/services/notes';
|
2021-03-10 16:23:17 +01:00
|
|
|
import { AddonNotesOffline } from '@addons/notes/services/notes-offline';
|
|
|
|
import { AddonNotesSync, AddonNotesSyncProvider } from '@addons/notes/services/notes-sync';
|
|
|
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
|
|
import { CoreAnimations } from '@components/animations';
|
|
|
|
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
|
|
|
|
import { IonContent, IonRefresher } from '@ionic/angular';
|
2023-06-22 11:09:33 +02:00
|
|
|
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
2021-03-10 16:23:17 +01:00
|
|
|
import { CoreNavigator } from '@services/navigator';
|
|
|
|
import { CoreSites } from '@services/sites';
|
2022-05-30 15:18:01 +02:00
|
|
|
import { CoreDomUtils, ToastDuration } from '@services/utils/dom';
|
2021-03-10 16:23:17 +01:00
|
|
|
import { CoreTextUtils } from '@services/utils/text';
|
2023-06-22 11:09:33 +02:00
|
|
|
import { CoreUrlUtils } from '@services/utils/url';
|
2021-03-10 16:23:17 +01:00
|
|
|
import { CoreUtils } from '@services/utils/utils';
|
2023-06-22 11:09:33 +02:00
|
|
|
import { Translate } from '@singletons';
|
2021-03-10 16:23:17 +01:00
|
|
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
2023-06-22 11:09:33 +02:00
|
|
|
import { CoreTime } from '@singletons/time';
|
2021-03-10 16:23:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Page that displays a list of notes.
|
|
|
|
*/
|
|
|
|
@Component({
|
|
|
|
selector: 'page-addon-notes-list-page',
|
|
|
|
templateUrl: 'list.html',
|
|
|
|
animations: [CoreAnimations.SLIDE_IN_OUT],
|
|
|
|
})
|
|
|
|
export class AddonNotesListPage implements OnInit, OnDestroy {
|
|
|
|
|
2023-11-07 15:52:08 +01:00
|
|
|
@ViewChild(IonContent) content?: IonContent;
|
2021-03-10 16:23:17 +01:00
|
|
|
|
2021-09-09 16:38:38 +02:00
|
|
|
courseId!: number;
|
2021-03-10 16:23:17 +01:00
|
|
|
userId?: number;
|
2021-05-04 15:26:06 +02:00
|
|
|
type: AddonNotesPublishState = 'course';
|
2021-03-10 16:23:17 +01:00
|
|
|
refreshIcon = CoreConstants.ICON_LOADING;
|
|
|
|
syncIcon = CoreConstants.ICON_LOADING;
|
|
|
|
notes: AddonNotesNoteFormatted[] = [];
|
|
|
|
hasOffline = false;
|
|
|
|
notesLoaded = false;
|
|
|
|
user?: CoreUserProfile;
|
|
|
|
showDelete = false;
|
|
|
|
canDeleteNotes = false;
|
2021-09-09 16:38:38 +02:00
|
|
|
currentUserId!: number;
|
2021-03-10 16:23:17 +01:00
|
|
|
|
2021-09-09 16:38:38 +02:00
|
|
|
protected syncObserver!: CoreEventObserver;
|
2023-06-22 11:09:33 +02:00
|
|
|
protected logView: () => void;
|
2021-03-10 16:23:17 +01:00
|
|
|
|
|
|
|
constructor() {
|
2023-06-22 11:09:33 +02:00
|
|
|
this.logView = CoreTime.once(() => this.performLogView());
|
|
|
|
|
2021-09-09 16:38:38 +02:00
|
|
|
try {
|
|
|
|
this.courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
|
|
|
this.userId = CoreNavigator.getRouteNumberParam('userId');
|
|
|
|
} catch (error) {
|
|
|
|
CoreDomUtils.showErrorModal(error);
|
|
|
|
|
|
|
|
CoreNavigator.back();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2021-03-10 16:23:17 +01:00
|
|
|
|
|
|
|
// Refresh data if notes are synchronized automatically.
|
|
|
|
this.syncObserver = CoreEvents.on(AddonNotesSyncProvider.AUTO_SYNCED, (data) => {
|
|
|
|
if (data.courseId == this.courseId) {
|
|
|
|
// Show the sync warnings.
|
|
|
|
this.showSyncWarnings(data.warnings);
|
|
|
|
|
|
|
|
// Refresh the data.
|
|
|
|
this.notesLoaded = false;
|
|
|
|
this.refreshIcon = CoreConstants.ICON_LOADING;
|
|
|
|
this.syncIcon = CoreConstants.ICON_LOADING;
|
|
|
|
|
|
|
|
this.content?.scrollToTop();
|
|
|
|
this.fetchNotes(false);
|
|
|
|
}
|
|
|
|
}, CoreSites.getCurrentSiteId());
|
|
|
|
|
|
|
|
this.currentUserId = CoreSites.getCurrentSiteUserId();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2022-12-01 12:31:00 +01:00
|
|
|
* @inheritdoc
|
2021-03-10 16:23:17 +01:00
|
|
|
*/
|
|
|
|
async ngOnInit(): Promise<void> {
|
|
|
|
await this.fetchNotes(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch notes.
|
|
|
|
*
|
|
|
|
* @param sync When to resync notes.
|
2023-05-09 09:00:54 +02:00
|
|
|
* @param showSyncErrors When to display sync errors or not.
|
2022-12-01 12:31:00 +01:00
|
|
|
* @returns Promise with the notes.
|
2021-03-10 16:23:17 +01:00
|
|
|
*/
|
2023-05-09 09:00:54 +02:00
|
|
|
protected async fetchNotes(sync = false, showSyncErrors = false): Promise<void> {
|
2021-03-10 16:23:17 +01:00
|
|
|
if (sync) {
|
2023-05-09 09:00:54 +02:00
|
|
|
await this.syncNotes(showSyncErrors);
|
2021-03-10 16:23:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
const allNotes = await AddonNotes.getNotes(this.courseId, this.userId);
|
|
|
|
|
|
|
|
const notesList: AddonNotesNoteFormatted[] = allNotes[this.type + 'notes'] || [];
|
|
|
|
|
|
|
|
notesList.forEach((note) => {
|
|
|
|
note.content = CoreTextUtils.decodeHTML(note.content);
|
|
|
|
});
|
|
|
|
|
|
|
|
await AddonNotes.setOfflineDeletedNotes(notesList, this.courseId);
|
|
|
|
|
|
|
|
this.hasOffline = notesList.some((note) => note.offline || note.deleted);
|
|
|
|
|
|
|
|
if (this.userId) {
|
|
|
|
this.notes = notesList;
|
|
|
|
|
|
|
|
// Get the user profile to retrieve the user image.
|
|
|
|
this.user = await CoreUser.getProfile(this.userId, this.courseId, true);
|
|
|
|
} else {
|
|
|
|
this.notes = await AddonNotes.getNotesUserData(notesList);
|
|
|
|
}
|
2022-03-08 07:42:09 +01:00
|
|
|
|
2023-06-22 11:09:33 +02:00
|
|
|
this.logView();
|
2021-03-10 16:23:17 +01:00
|
|
|
} catch (error) {
|
|
|
|
CoreDomUtils.showErrorModal(error);
|
|
|
|
} finally {
|
|
|
|
let canDelete = this.notes && this.notes.length > 0;
|
|
|
|
if (canDelete && this.type == 'personal') {
|
2021-05-13 14:12:42 +02:00
|
|
|
canDelete = !!this.notes.find((note) => note.usermodified == this.currentUserId);
|
2021-03-10 16:23:17 +01:00
|
|
|
}
|
|
|
|
this.canDeleteNotes = canDelete;
|
|
|
|
|
|
|
|
this.notesLoaded = true;
|
|
|
|
this.refreshIcon = CoreConstants.ICON_REFRESH;
|
|
|
|
this.syncIcon = CoreConstants.ICON_SYNC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh notes on PTR.
|
|
|
|
*
|
2023-05-09 09:00:54 +02:00
|
|
|
* @param showSyncErrors Whether to display sync errors or not.
|
2021-03-10 16:23:17 +01:00
|
|
|
* @param refresher Refresher instance.
|
|
|
|
*/
|
2023-05-09 09:00:54 +02:00
|
|
|
refreshNotes(showSyncErrors: boolean, refresher?: IonRefresher): void {
|
2021-03-10 16:23:17 +01:00
|
|
|
this.refreshIcon = CoreConstants.ICON_LOADING;
|
|
|
|
this.syncIcon = CoreConstants.ICON_LOADING;
|
|
|
|
|
|
|
|
AddonNotes.invalidateNotes(this.courseId, this.userId).finally(() => {
|
2023-05-09 09:00:54 +02:00
|
|
|
this.fetchNotes(true, showSyncErrors).finally(() => {
|
2021-03-10 16:23:17 +01:00
|
|
|
if (refresher) {
|
|
|
|
refresher?.complete();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called when the type has changed.
|
2021-05-06 11:06:17 +02:00
|
|
|
*
|
|
|
|
* @param type New type.
|
2021-03-10 16:23:17 +01:00
|
|
|
*/
|
2021-05-04 15:26:06 +02:00
|
|
|
async typeChanged(type: AddonNotesPublishState): Promise<void> {
|
2021-05-06 11:06:17 +02:00
|
|
|
this.type = type;
|
2021-03-10 16:23:17 +01:00
|
|
|
this.notesLoaded = false;
|
|
|
|
this.refreshIcon = CoreConstants.ICON_LOADING;
|
|
|
|
this.syncIcon = CoreConstants.ICON_LOADING;
|
|
|
|
|
|
|
|
await this.fetchNotes(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a new Note to user and course.
|
|
|
|
*
|
|
|
|
* @param e Event.
|
|
|
|
*/
|
|
|
|
async addNote(e: Event): Promise<void> {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
2023-06-22 11:09:33 +02:00
|
|
|
this.logViewAdd();
|
|
|
|
|
2021-05-04 15:26:06 +02:00
|
|
|
const modalData = await CoreDomUtils.openModal<AddonNotesAddModalReturn>({
|
2021-03-10 16:23:17 +01:00
|
|
|
component: AddonNotesAddComponent,
|
|
|
|
componentProps: {
|
|
|
|
userId: this.userId,
|
|
|
|
courseId: this.courseId,
|
|
|
|
type: this.type,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2021-12-16 10:46:40 +01:00
|
|
|
if (modalData !== undefined) {
|
2021-03-10 16:23:17 +01:00
|
|
|
|
2021-05-04 15:26:06 +02:00
|
|
|
if (modalData.sent && modalData.type) {
|
|
|
|
if (modalData.type != this.type) {
|
|
|
|
this.type = modalData.type;
|
2021-03-10 16:23:17 +01:00
|
|
|
this.notesLoaded = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.refreshNotes(false);
|
2021-05-04 15:26:06 +02:00
|
|
|
} else if (modalData.type && modalData.type != this.type) {
|
|
|
|
this.typeChanged(modalData.type);
|
2021-03-10 16:23:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete a note.
|
|
|
|
*
|
|
|
|
* @param e Click event.
|
|
|
|
* @param note Note to delete.
|
|
|
|
*/
|
|
|
|
async deleteNote(e: Event, note: AddonNotesNoteFormatted): Promise<void> {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
try {
|
2023-06-22 11:09:33 +02:00
|
|
|
this.logViewDelete(note);
|
|
|
|
|
2021-03-10 16:23:17 +01:00
|
|
|
await CoreDomUtils.showDeleteConfirm('addon.notes.deleteconfirm');
|
|
|
|
try {
|
|
|
|
await AddonNotes.deleteNote(note, this.courseId);
|
|
|
|
this.showDelete = false;
|
|
|
|
|
|
|
|
this.refreshNotes(false);
|
|
|
|
|
2022-05-30 15:18:01 +02:00
|
|
|
CoreDomUtils.showToast('addon.notes.eventnotedeleted', true, ToastDuration.LONG);
|
2021-03-10 16:23:17 +01:00
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
CoreDomUtils.showErrorModalDefault(error, 'Delete note failed.');
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
// User cancelled, nothing to do.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Restore a note.
|
|
|
|
*
|
|
|
|
* @param e Click event.
|
|
|
|
* @param note Note to delete.
|
|
|
|
*/
|
|
|
|
async undoDeleteNote(e: Event, note: AddonNotesNoteFormatted): Promise<void> {
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
await AddonNotesOffline.undoDeleteNote(note.id);
|
2023-05-09 09:00:54 +02:00
|
|
|
this.refreshNotes(false);
|
2021-03-10 16:23:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle delete.
|
|
|
|
*/
|
|
|
|
toggleDelete(): void {
|
|
|
|
this.showDelete = !this.showDelete;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tries to synchronize course notes.
|
|
|
|
*
|
2023-05-09 09:00:54 +02:00
|
|
|
* @param showSyncErrors Whether to display sync errors or not.
|
2022-12-01 12:31:00 +01:00
|
|
|
* @returns Promise resolved when done.
|
2021-03-10 16:23:17 +01:00
|
|
|
*/
|
2023-05-09 09:00:54 +02:00
|
|
|
protected async syncNotes(showSyncErrors: boolean): Promise<void> {
|
2021-03-10 16:23:17 +01:00
|
|
|
try {
|
|
|
|
const result = await AddonNotesSync.syncNotes(this.courseId);
|
|
|
|
|
|
|
|
this.showSyncWarnings(result.warnings);
|
|
|
|
} catch (error) {
|
2023-05-09 09:00:54 +02:00
|
|
|
if (showSyncErrors) {
|
2021-03-10 16:23:17 +01:00
|
|
|
CoreDomUtils.showErrorModalDefault(error, 'core.errorsync', true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show sync warnings if any.
|
|
|
|
*
|
|
|
|
* @param warnings the warnings
|
|
|
|
*/
|
|
|
|
protected showSyncWarnings(warnings: string[]): void {
|
|
|
|
const message = CoreTextUtils.buildMessage(warnings);
|
|
|
|
|
|
|
|
if (message) {
|
2023-04-05 10:20:16 +02:00
|
|
|
CoreDomUtils.showAlert(undefined, message);
|
2021-03-10 16:23:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-22 11:09:33 +02:00
|
|
|
/**
|
|
|
|
* Log view.
|
|
|
|
*/
|
|
|
|
protected async performLogView(): Promise<void> {
|
|
|
|
await CoreUtils.ignoreErrors(AddonNotes.logView(this.courseId, this.userId));
|
|
|
|
|
|
|
|
CoreAnalytics.logEvent({
|
|
|
|
type: CoreAnalyticsEventType.VIEW_ITEM_LIST,
|
|
|
|
ws: 'core_notes_view_notes',
|
|
|
|
name: Translate.instant('addon.notes.notes'),
|
|
|
|
data: { courseid: this.courseId, userid: this.userId || 0, category: 'notes' },
|
|
|
|
url: CoreUrlUtils.addParamsToUrl('/notes/index.php', {
|
|
|
|
user: this.userId,
|
|
|
|
course: this.courseId !== CoreSites.getCurrentSiteHomeId() ? this.courseId : undefined,
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log view.
|
|
|
|
*/
|
|
|
|
protected async logViewAdd(): Promise<void> {
|
|
|
|
CoreAnalytics.logEvent({
|
|
|
|
type: CoreAnalyticsEventType.VIEW_ITEM,
|
|
|
|
ws: 'core_notes_create_notes',
|
|
|
|
name: Translate.instant('addon.notes.notes'),
|
|
|
|
data: { courseid: this.courseId, userid: this.userId || 0, category: 'notes' },
|
|
|
|
url: CoreUrlUtils.addParamsToUrl('/notes/edit.php', {
|
|
|
|
courseid: this.courseId,
|
|
|
|
userid: this.userId,
|
|
|
|
publishstate: this.type === 'personal' ? 'draft' : (this.type === 'course' ? 'public' : 'site'),
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log view.
|
|
|
|
*/
|
|
|
|
protected async logViewDelete(note: AddonNotesNoteFormatted): Promise<void> {
|
|
|
|
if (!note.id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CoreAnalytics.logEvent({
|
|
|
|
type: CoreAnalyticsEventType.VIEW_ITEM,
|
|
|
|
ws: 'core_notes_delete_notes',
|
|
|
|
name: Translate.instant('addon.notes.notes'),
|
|
|
|
data: { id: note.id, category: 'notes' },
|
|
|
|
url: `/notes/delete.php?id=${note.id}`,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-03-10 16:23:17 +01:00
|
|
|
/**
|
|
|
|
* Page destroyed.
|
|
|
|
*/
|
|
|
|
ngOnDestroy(): void {
|
|
|
|
this.syncObserver && this.syncObserver.off();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|