MOBILE-3643 forum: Infer event payloads
parent
205fb9e7cb
commit
a695f9cb66
|
@ -22,6 +22,8 @@ import {
|
|||
AddonModForumProvider,
|
||||
AddonModForumSortOrder,
|
||||
AddonModForumDiscussion,
|
||||
AddonModForumNewDiscussionData,
|
||||
AddonModForumReplyDiscussionData,
|
||||
} from '@addons/mod/forum/services/forum.service';
|
||||
import { AddonModForumOffline, AddonModForumOfflineDiscussion } from '@addons/mod/forum/services/offline.service';
|
||||
import { ModalController, PopoverController, Translate } from '@singletons';
|
||||
|
@ -127,7 +129,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
AddonModForumProvider.REPLY_DISCUSSION_EVENT,
|
||||
this.eventReceived.bind(this, false),
|
||||
);
|
||||
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data: any) => {
|
||||
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data => {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module!.id) {
|
||||
AddonModForum.instance.invalidateDiscussionsList(this.forum!.id).finally(() => {
|
||||
if (data.discussionId) {
|
||||
|
@ -152,7 +154,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
}
|
||||
|
||||
if (typeof data.deleted != 'undefined' && data.deleted) {
|
||||
if (data.post.parentid == 0 && CoreScreen.instance.isTablet && !this.discussions.empty) {
|
||||
if (data.post?.parentid == 0 && CoreScreen.instance.isTablet && !this.discussions.empty) {
|
||||
// Discussion deleted, clear details page.
|
||||
this.discussions.select(this.discussions[0]);
|
||||
}
|
||||
|
@ -275,7 +277,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
|
||||
if (updated) {
|
||||
// Sync successful, send event.
|
||||
CoreEvents.trigger<AddonModForumManualSyncData>(AddonModForumSyncProvider.MANUAL_SYNCED, {
|
||||
CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
|
||||
forumId: forum.id,
|
||||
userId: CoreSites.instance.getCurrentSiteUserId(),
|
||||
source: 'index',
|
||||
|
@ -553,18 +555,22 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
* @param isNewDiscussion Whether it's a new discussion event.
|
||||
* @param data Event data.
|
||||
*/
|
||||
protected eventReceived(isNewDiscussion: boolean, data: any): void {
|
||||
protected eventReceived(
|
||||
isNewDiscussion: boolean,
|
||||
data: AddonModForumNewDiscussionData | AddonModForumReplyDiscussionData,
|
||||
): void {
|
||||
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module?.id) {
|
||||
this.showLoadingAndRefresh(false).finally(() => {
|
||||
// If it's a new discussion in tablet mode, try to open it.
|
||||
if (isNewDiscussion && CoreScreen.instance.isTablet) {
|
||||
const newDiscussionData = data as AddonModForumNewDiscussionData;
|
||||
const discussion = this.discussions.items.find(disc => {
|
||||
if (this.discussions.isOfflineDiscussion(disc)) {
|
||||
return disc.timecreated === data.discTimecreated;
|
||||
return disc.timecreated === newDiscussionData.discTimecreated;
|
||||
}
|
||||
|
||||
if (this.discussions.isOnlineDiscussion(disc)) {
|
||||
return CoreArray.contains(data.discussionIds, disc.discussion);
|
||||
return CoreArray.contains(newDiscussionData.discussionIds ?? [], disc.discussion);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -37,7 +37,7 @@ import {
|
|||
} from '../../services/forum.service';
|
||||
import { AddonModForumHelper } from '../../services/helper.service';
|
||||
import { AddonModForumOffline } from '../../services/offline.service';
|
||||
import { AddonModForumManualSyncData, AddonModForumSync, AddonModForumSyncProvider } from '../../services/sync.service';
|
||||
import { AddonModForumSync, AddonModForumSyncProvider } from '../../services/sync.service';
|
||||
|
||||
type SortType = 'flat-newest' | 'flat-oldest' | 'nested';
|
||||
|
||||
|
@ -170,7 +170,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
}
|
||||
|
||||
// Refresh data if this discussion is synchronized automatically.
|
||||
this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, (data: any) => {
|
||||
this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, data => {
|
||||
if (data.forumId == this.forumId && this.discussionId == data.discussionId
|
||||
&& data.userId == CoreSites.instance.getCurrentSiteUserId()) {
|
||||
// Refresh the data.
|
||||
|
@ -180,7 +180,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
}, CoreSites.instance.getCurrentSiteId());
|
||||
|
||||
// Refresh data if this forum discussion is synchronized from discussions list.
|
||||
this.syncManualObserver = CoreEvents.on(AddonModForumSyncProvider.MANUAL_SYNCED, (data: AddonModForumManualSyncData) => {
|
||||
this.syncManualObserver = CoreEvents.on(AddonModForumSyncProvider.MANUAL_SYNCED, data => {
|
||||
if (data.source != 'discussion' && data.forumId == this.forumId &&
|
||||
data.userId == CoreSites.instance.getCurrentSiteUserId()) {
|
||||
// Refresh the data.
|
||||
|
@ -196,7 +196,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
|
||||
// @todo Listen for offline ratings saved and synced.
|
||||
|
||||
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data: any) => {
|
||||
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data => {
|
||||
if ((this.forumId && this.forumId === data.forumId) || data.cmId === this.cmId) {
|
||||
AddonModForum.instance.invalidateDiscussionsList(this.forumId).finally(() => {
|
||||
if (typeof data.locked != 'undefined') {
|
||||
|
@ -210,7 +210,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
}
|
||||
|
||||
if (typeof data.deleted != 'undefined' && data.deleted) {
|
||||
if (!data.post.parentid) {
|
||||
if (!data.post?.parentid) {
|
||||
// @todo
|
||||
// if (this.svComponent && this.svComponent.isOn()) {
|
||||
// this.svComponent.emptyDetails();
|
||||
|
@ -545,7 +545,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
|
|||
|
||||
if (result && result.updated) {
|
||||
// Sync successful, send event.
|
||||
CoreEvents.trigger<AddonModForumManualSyncData>(AddonModForumSyncProvider.MANUAL_SYNCED, {
|
||||
CoreEvents.trigger(AddonModForumSyncProvider.MANUAL_SYNCED, {
|
||||
forumId: this.forumId,
|
||||
userId: CoreSites.instance.getCurrentSiteUserId(),
|
||||
source: 'discussion',
|
||||
|
|
|
@ -120,7 +120,7 @@ export class AddonModForumNewDiscussionPage implements OnInit, OnDestroy, CanLea
|
|||
}
|
||||
|
||||
// Refresh data if this discussion is synchronized automatically.
|
||||
this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, (data: any) => {
|
||||
this.syncObserver = CoreEvents.on(AddonModForumSyncProvider.AUTO_SYNCED, data => {
|
||||
if (data.forumId == this.forumId && data.userId == CoreSites.instance.getCurrentSiteUserId()) {
|
||||
CoreDomUtils.instance.showAlertTranslated('core.notice', 'core.contenteditingsynced');
|
||||
this.returnToDiscussions();
|
||||
|
|
|
@ -30,6 +30,22 @@ import { AddonModForumOffline, AddonModForumOfflineDiscussion, AddonModForumRepl
|
|||
|
||||
const ROOT_CACHE_KEY = 'mmaModForum:';
|
||||
|
||||
declare module '@singletons/events' {
|
||||
|
||||
/**
|
||||
* Augment CoreEventsData interface with events specific to this service.
|
||||
*
|
||||
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||
*/
|
||||
export interface CoreEventsData {
|
||||
[AddonModForumProvider.NEW_DISCUSSION_EVENT]: AddonModForumNewDiscussionData;
|
||||
[AddonModForumProvider.REPLY_DISCUSSION_EVENT]: AddonModForumReplyDiscussionData;
|
||||
[AddonModForumProvider.CHANGE_DISCUSSION_EVENT]: AddonModForumChangeDiscussionData;
|
||||
[AddonModForumProvider.MARK_READ_EVENT]: AddonModForumMarkReadData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Service that provides some features for forums.
|
||||
*
|
||||
|
@ -2151,3 +2167,44 @@ export type AddonModForumUpdateDiscussionPostWSParams = {
|
|||
* Data returned by mod_forum_update_discussion_post WS.
|
||||
*/
|
||||
export type AddonModForumUpdateDiscussionPostWSResponse = CoreStatusWithWarningsWSResponse;
|
||||
|
||||
/**
|
||||
* Data passed to NEW_DISCUSSION_EVENT event.
|
||||
*/
|
||||
export type AddonModForumNewDiscussionData = {
|
||||
forumId: number;
|
||||
cmId: number;
|
||||
discussionIds?: number[] | null;
|
||||
discTimecreated?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to REPLY_DISCUSSION_EVENT event.
|
||||
*/
|
||||
export type AddonModForumReplyDiscussionData = {
|
||||
forumId: number;
|
||||
discussionId: number;
|
||||
cmId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to CHANGE_DISCUSSION_EVENT event.
|
||||
*/
|
||||
export type AddonModForumChangeDiscussionData = {
|
||||
forumId: number;
|
||||
discussionId: number;
|
||||
cmId: number;
|
||||
deleted?: boolean;
|
||||
post?: AddonModForumPost;
|
||||
locked?: boolean;
|
||||
pinned?: boolean;
|
||||
starred?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to MARK_READ_EVENT event.
|
||||
*/
|
||||
export type AddonModForumMarkReadData = {
|
||||
courseId: number;
|
||||
moduleId: number;
|
||||
};
|
||||
|
|
|
@ -95,7 +95,7 @@ export class AddonModForumModuleHandlerService implements CoreCourseModuleHandle
|
|||
|
||||
const event = CoreEvents.on(
|
||||
AddonModForumProvider.MARK_READ_EVENT,
|
||||
(eventData: { courseId?: number; moduleId?: number; siteId?: string }) => {
|
||||
eventData => {
|
||||
if (eventData.courseId !== courseId || eventData.moduleId !== module.id) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,20 @@ import {
|
|||
import { AddonModForumHelper } from './helper.service';
|
||||
import { AddonModForumOffline, AddonModForumOfflineDiscussion, AddonModForumOfflineReply } from './offline.service';
|
||||
|
||||
declare module '@singletons/events' {
|
||||
|
||||
/**
|
||||
* Augment CoreEventsData interface with events specific to this service.
|
||||
*
|
||||
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
|
||||
*/
|
||||
export interface CoreEventsData {
|
||||
[AddonModForumSyncProvider.AUTO_SYNCED]: AddonModForumAutoSyncData;
|
||||
[AddonModForumSyncProvider.MANUAL_SYNCED]: AddonModForumManualSyncData;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to sync forums.
|
||||
*/
|
||||
|
@ -95,7 +109,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider<AddonModForu
|
|||
|
||||
if (result && result.updated) {
|
||||
// Sync successful, send event.
|
||||
CoreEvents.trigger<AddonModForumAutoSyncData>(AddonModForumSyncProvider.AUTO_SYNCED, {
|
||||
CoreEvents.trigger(AddonModForumSyncProvider.AUTO_SYNCED, {
|
||||
forumId: discussion.forumid,
|
||||
userId: discussion.userid,
|
||||
warnings: result.warnings,
|
||||
|
@ -127,7 +141,7 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider<AddonModForu
|
|||
|
||||
if (result && result.updated) {
|
||||
// Sync successful, send event.
|
||||
CoreEvents.trigger<AddonModForumAutoSyncData>(AddonModForumSyncProvider.AUTO_SYNCED, {
|
||||
CoreEvents.trigger(AddonModForumSyncProvider.AUTO_SYNCED, {
|
||||
forumId: reply.forumid,
|
||||
discussionId: reply.discussionid,
|
||||
userId: reply.userid,
|
||||
|
|
|
@ -28,6 +28,25 @@ export interface CoreEventObserver {
|
|||
off: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event payloads.
|
||||
*/
|
||||
export interface CoreEventsData {
|
||||
[CoreEvents.SITE_UPDATED]: CoreEventSiteUpdatedData;
|
||||
[CoreEvents.SITE_ADDED]: CoreEventSiteAddedData;
|
||||
[CoreEvents.SESSION_EXPIRED]: CoreEventSessionExpiredData;
|
||||
[CoreEvents.CORE_LOADING_CHANGED]: CoreEventLoadingChangedData;
|
||||
[CoreEvents.COURSE_STATUS_CHANGED]: CoreEventCourseStatusChanged;
|
||||
[CoreEvents.PACKAGE_STATUS_CHANGED]: CoreEventPackageStatusChanged;
|
||||
[CoreEvents.USER_DELETED]: CoreEventUserDeletedData;
|
||||
[CoreEvents.FORM_ACTION]: CoreEventFormActionData;
|
||||
[CoreEvents.NOTIFICATION_SOUND_CHANGED]: CoreEventNotificationSoundChangedData;
|
||||
[CoreEvents.SELECT_COURSE_TAB]: CoreEventSelectCourseTabData;
|
||||
[CoreEvents.COMPLETION_MODULE_VIEWED]: CoreEventCompletionModuleViewedData;
|
||||
[CoreEvents.SECTION_STATUS_CHANGED]: CoreEventSectionStatusChangedData;
|
||||
[CoreEvents.ACTIVITY_DATA_SENT]: CoreEventActivityDataSentData;
|
||||
};
|
||||
|
||||
/*
|
||||
* Service to send and listen to events.
|
||||
*/
|
||||
|
@ -84,15 +103,15 @@ export class CoreEvents {
|
|||
* @param siteId Site where to trigger the event. Undefined won't check the site.
|
||||
* @return Observer to stop listening.
|
||||
*/
|
||||
static on<T = unknown>(
|
||||
eventName: string,
|
||||
callBack: (value: T & { siteId?: string }) => void,
|
||||
static on<Fallback = unknown, Event extends string = string>(
|
||||
eventName: Event,
|
||||
callBack: (value: CoreEventData<Event, Fallback> & { siteId?: string }) => void,
|
||||
siteId?: string,
|
||||
): CoreEventObserver {
|
||||
// If it's a unique event and has been triggered already, call the callBack.
|
||||
// We don't need to create an observer because the event won't be triggered again.
|
||||
if (this.uniqueEvents[eventName]) {
|
||||
callBack(<T> this.uniqueEvents[eventName].data);
|
||||
callBack(this.uniqueEvents[eventName].data as CoreEventData<Event, Fallback> & { siteId?: string });
|
||||
|
||||
// Return a fake observer to prevent errors.
|
||||
return {
|
||||
|
@ -106,14 +125,16 @@ export class CoreEvents {
|
|||
|
||||
if (typeof this.observables[eventName] == 'undefined') {
|
||||
// No observable for this event, create a new one.
|
||||
this.observables[eventName] = new Subject<T>();
|
||||
this.observables[eventName] = new Subject();
|
||||
}
|
||||
|
||||
const subscription = this.observables[eventName].subscribe((value: T & {siteId?: string}) => {
|
||||
if (!siteId || value.siteId == siteId) {
|
||||
callBack(value);
|
||||
}
|
||||
});
|
||||
const subscription = this.observables[eventName].subscribe(
|
||||
(value: CoreEventData<Event, Fallback> & { siteId?: string }) => {
|
||||
if (!siteId || value.siteId == siteId) {
|
||||
callBack(value);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Create and return a CoreEventObserver.
|
||||
return {
|
||||
|
@ -155,7 +176,11 @@ export class CoreEvents {
|
|||
* @param data Data to pass to the observers.
|
||||
* @param siteId Site where to trigger the event. Undefined means no Site.
|
||||
*/
|
||||
static trigger<T = unknown>(eventName: string, data?: T, siteId?: string): void {
|
||||
static trigger<Fallback = unknown, Event extends string = string>(
|
||||
eventName: Event,
|
||||
data?: CoreEventData<Event, Fallback>,
|
||||
siteId?: string,
|
||||
): void {
|
||||
this.logger.debug(`Event '${eventName}' triggered.`);
|
||||
if (this.observables[eventName]) {
|
||||
if (siteId) {
|
||||
|
@ -172,7 +197,11 @@ export class CoreEvents {
|
|||
* @param data Data to pass to the observers.
|
||||
* @param siteId Site where to trigger the event. Undefined means no Site.
|
||||
*/
|
||||
static triggerUnique<T = unknown>(eventName: string, data: T, siteId?: string): void {
|
||||
static triggerUnique<Fallback = unknown, Event extends string = string>(
|
||||
eventName: Event,
|
||||
data: CoreEventData<Event, Fallback>,
|
||||
siteId?: string,
|
||||
): void {
|
||||
if (this.uniqueEvents[eventName]) {
|
||||
this.logger.debug(`Unique event '${eventName}' ignored because it was already triggered.`);
|
||||
} else {
|
||||
|
@ -196,6 +225,11 @@ export class CoreEvents {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve payload type for a given event.
|
||||
*/
|
||||
export type CoreEventData<Event, Fallback> = Event extends keyof CoreEventsData ? CoreEventsData[Event] : Fallback;
|
||||
|
||||
/**
|
||||
* Some events contains siteId added by the trigger function. This type is intended to be combined with others.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue