MOBILE-4081 notifications: Improve source

main
Noel De Martin 2022-06-02 17:07:01 +02:00
parent 11c1b2a7da
commit a2f6e0139e
5 changed files with 160 additions and 26 deletions

View File

@ -0,0 +1,62 @@
// (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 { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
import { AddonNotificationsGetReadType } from '@addons/notifications/services/notifications';
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
/**
* Provides a list of notifications using legacy web services.
*/
export class AddonLegacyNotificationsNotificationsSource extends AddonNotificationsNotificationsSource {
/**
* @inheritdoc
*/
protected async loadPageItems(page: number): Promise<{
items: AddonNotificationsNotificationToRender[];
hasMoreItems: boolean;
}> {
let items: AddonNotificationsNotificationToRender[] = [];
let hasMoreItems = false;
let pageUnreadCount = 0;
const pageLength = this.getPageLength();
const totalUnread = () => this.totals[AddonNotificationsGetReadType.UNREAD] ?? Number.MAX_VALUE;
// Load unread notifications first.
if (totalUnread() > page * pageLength) {
const pageResults = await this.loadNotifications(AddonNotificationsGetReadType.UNREAD, page * pageLength);
items = items.concat(pageResults.notifications);
hasMoreItems = pageResults.hasMoreNotifications;
pageUnreadCount = pageResults.notifications.length;
}
// If all unread notifications have been fetched, load read notifications first.
if (totalUnread() < (page + 1) * pageLength) {
const offset = Math.max(0, page * pageLength - totalUnread());
const pageResults = await this.loadNotifications(
AddonNotificationsGetReadType.READ,
offset,
pageLength - pageUnreadCount,
);
items = items.concat(pageResults.notifications);
hasMoreItems = pageResults.hasMoreNotifications;
}
return { items, hasMoreItems };
}
}

View File

@ -12,30 +12,20 @@
// 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 {
AddonNotifications,
AddonNotificationsGetReadType,
AddonNotificationsProvider,
} from '@addons/notifications/services/notifications';
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source'; import { CoreRoutedItemsManagerSource } from '@classes/items-management/routed-items-manager-source';
import { AddonNotifications } from '../services/notifications';
import { AddonNotificationsHelper, AddonNotificationsNotificationToRender } from '../services/notifications-helper';
/** /**
* Provides a list of notifications * Provides a list of notifications.
*/ */
export class AddonsNotificationsNotificationsSource extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationToRender> { export class AddonNotificationsNotificationsSource extends CoreRoutedItemsManagerSource<AddonNotificationsNotificationToRender> {
/** protected totals: Record<string, number> = {};
* @inheritdoc
*/
protected async loadPageItems(page: number): Promise<{
items: AddonNotificationsNotificationToRender[];
hasMoreItems: boolean;
}> {
// TODO this should be refactored to avoid using the existing items.
const { notifications, canLoadMore } = await AddonNotifications.getNotifications(page === 0 ? [] : this.getItems() ?? []);
return {
items: notifications.map(notification => AddonNotificationsHelper.formatNotificationText(notification)),
hasMoreItems: canLoadMore,
};
}
/** /**
* @inheritdoc * @inheritdoc
@ -44,4 +34,79 @@ export class AddonsNotificationsNotificationsSource extends CoreRoutedItemsManag
return notification.id.toString(); return notification.id.toString();
} }
/**
* @inheritdoc
*/
reset(): void {
this.totals = {};
super.reset();
}
/**
* @inheritdoc
*/
protected async loadPageItems(page: number): Promise<{
items: AddonNotificationsNotificationToRender[];
hasMoreItems: boolean;
}> {
const results = await this.loadNotifications(AddonNotificationsGetReadType.BOTH, page * this.getPageLength());
return {
items: results.notifications,
hasMoreItems: results.hasMoreNotifications,
};
}
/**
* Load notifications of the given type.
*
* @param type Type.
* @param offset Offset.
* @param limit Limit.
* @returns Notifications and whether there are any more.
*/
protected async loadNotifications(type: AddonNotificationsGetReadType, offset: number, limit?: number): Promise<{
notifications: AddonNotificationsNotificationToRender[];
hasMoreNotifications: boolean;
}> {
limit = limit ?? this.getPageLength();
if (type in this.totals && this.totals[type] <= offset) {
return {
notifications: [],
hasMoreNotifications: false,
};
}
const notifications = await AddonNotifications.getNotificationsWithStatus(type, { offset, limit });
if (notifications.length < limit) {
this.totals[type] = offset + notifications.length;
}
return {
notifications,
hasMoreNotifications: (this.totals[type] ?? Number.MAX_VALUE) > offset + limit,
};
}
/**
* @inheritdoc
*/
protected setItems(notifications: AddonNotificationsNotificationToRender[], hasMoreItems: boolean): void {
const sortedNotifications = notifications.slice(0);
sortedNotifications.sort((a, b) => a.timecreated < b.timecreated ? 1 : -1);
super.setItems(sortedNotifications, hasMoreItems);
}
/**
* @inheritdoc
*/
protected getPageLength(): number {
return AddonNotificationsProvider.LIST_LIMIT;
}
} }

View File

@ -29,9 +29,10 @@ import { CorePushNotificationsDelegate } from '@features/pushnotifications/servi
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager'; import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
import { CoreTimeUtils } from '@services/utils/time'; import { CoreTimeUtils } from '@services/utils/time';
import { AddonsNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
import { CoreListItemsManager } from '@classes/items-management/list-items-manager'; import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper'; import { AddonNotificationsNotificationToRender } from '@addons/notifications/services/notifications-helper';
import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
/** /**
* Page that displays the list of notifications. * Page that displays the list of notifications.
@ -44,7 +45,7 @@ import { AddonNotificationsNotificationToRender } from '@addons/notifications/se
export class AddonNotificationsListPage implements AfterViewInit, OnDestroy { export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
@ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent; @ViewChild(CoreSplitViewComponent) splitView!: CoreSplitViewComponent;
notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonsNotificationsNotificationsSource>; notifications!: CoreListItemsManager<AddonNotificationsNotificationToRender, AddonNotificationsNotificationsSource>;
fetchMoreNotificationsFailed = false; fetchMoreNotificationsFailed = false;
canMarkAllNotificationsAsRead = false; canMarkAllNotificationsAsRead = false;
loadingMarkAllNotificationsAsRead = false; loadingMarkAllNotificationsAsRead = false;
@ -58,7 +59,9 @@ export class AddonNotificationsListPage implements AfterViewInit, OnDestroy {
constructor() { constructor() {
try { try {
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
AddonsNotificationsNotificationsSource, CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.0')
? AddonNotificationsNotificationsSource
: AddonLegacyNotificationsNotificationsSource,
[], [],
); );

View File

@ -12,7 +12,8 @@
// 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 { AddonsNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source'; import { AddonLegacyNotificationsNotificationsSource } from '@addons/notifications/classes/legacy-notifications-source';
import { AddonNotificationsNotificationsSource } from '@addons/notifications/classes/notifications-source';
import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click'; import { AddonNotificationsNotificationData } from '@addons/notifications/services/handlers/push-click';
import { import {
AddonNotificationsHelper, AddonNotificationsHelper,
@ -118,7 +119,9 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
*/ */
getNotificationById(notificationId: number): AddonNotificationsNotification | undefined { getNotificationById(notificationId: number): AddonNotificationsNotification | undefined {
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource( const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
AddonsNotificationsNotificationsSource, CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.0')
? AddonNotificationsNotificationsSource
: AddonLegacyNotificationsNotificationsSource,
[], [],
); );
const notification = source.getItems()?.find(({ id }) => id === notificationId); const notification = source.getItems()?.find(({ id }) => id === notificationId);
@ -137,7 +140,7 @@ export class AddonNotificationsNotificationPage implements OnInit, OnDestroy {
* *
* @param source Notifications source * @param source Notifications source
*/ */
async loadNotifications(source: AddonsNotificationsNotificationsSource): Promise<void> { async loadNotifications(source: AddonNotificationsNotificationsSource): Promise<void> {
this.notifications = new AddonNotificationSwipeItemsManager(source); this.notifications = new AddonNotificationSwipeItemsManager(source);
await this.notifications.start(); await this.notifications.start();

View File

@ -165,6 +165,7 @@ export class AddonNotificationsProvider {
* @param notifications Current list of loaded notifications. It's used to calculate the offset. * @param notifications Current list of loaded notifications. It's used to calculate the offset.
* @param options Other options. * @param options Other options.
* @return Promise resolved with notifications and if can load more. * @return Promise resolved with notifications and if can load more.
* @deprecated since 4.1. Use getNotificationsWithStatus instead.
*/ */
async getNotifications( async getNotifications(
notifications: AddonNotificationsNotificationMessageFormatted[], notifications: AddonNotificationsNotificationMessageFormatted[],
@ -231,7 +232,7 @@ export class AddonNotificationsProvider {
* @param options Other options. * @param options Other options.
* @return Promise resolved with notifications. * @return Promise resolved with notifications.
*/ */
protected async getNotificationsWithStatus( async getNotificationsWithStatus(
read: AddonNotificationsGetReadType, read: AddonNotificationsGetReadType,
options: AddonNotificationsGetNotificationsOptions = {}, options: AddonNotificationsGetNotificationsOptions = {},
): Promise<AddonNotificationsNotificationMessageFormatted[]> { ): Promise<AddonNotificationsNotificationMessageFormatted[]> {