MOBILE-3643 forum: Migrate offline discussions
parent
26f18ac5f4
commit
d41523d4bb
|
@ -1,3 +1,17 @@
|
||||||
|
<!-- Buttons to add to the header. -->
|
||||||
|
<core-navbar-buttons slot="end">
|
||||||
|
<core-context-menu>
|
||||||
|
<core-context-menu-item *ngIf="externalUrl" [priority]="900" [content]="'core.openinbrowser' | translate" [href]="externalUrl" iconAction="open"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="description" [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="blog" [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog($event)"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline" [priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" (action)="doRefresh(null, $event)" [iconAction]="refreshIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="discussions.loaded && (hasOffline || hasOfflineRatings) && isOnline" [priority]="600" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch($event)" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="size" [priority]="400" [content]="'core.clearstoreddata' | translate:{$a: size}" iconDescription="cube" (action)="removeFiles($event)" iconAction="trash" [closeOnClick]="false"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="sortingAvailable" [priority]="300" [content]="'core.sort' | translate" (action)="showSortOrderSelector()" iconAction="fa-sort"></core-context-menu-item>
|
||||||
|
</core-context-menu>
|
||||||
|
</core-navbar-buttons>
|
||||||
|
|
||||||
<!-- Content. -->
|
<!-- Content. -->
|
||||||
<core-split-view>
|
<core-split-view>
|
||||||
<ion-refresher slot="fixed" [disabled]="!discussions.loaded" (ionRefresh)="doRefresh($event)">
|
<ion-refresher slot="fixed" [disabled]="!discussions.loaded" (ionRefresh)="doRefresh($event)">
|
||||||
|
@ -27,7 +41,7 @@
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<ng-container *ngIf="forum">
|
<ng-container *ngIf="forum">
|
||||||
<core-empty-box *ngIf="discussions.empty && offlineDiscussions.length == 0" icon="chatbubbles" [message]="'addon.mod_forum.forumnodiscussionsyet' | translate">
|
<core-empty-box *ngIf="discussions.empty" icon="chatbubbles" [message]="'addon.mod_forum.forumnodiscussionsyet' | translate">
|
||||||
</core-empty-box>
|
</core-empty-box>
|
||||||
|
|
||||||
<div *ngIf="!discussions.empty && sortingAvailable && selectedSortOrder" class="ion-text-wrap addon-forum-sorting-select">
|
<div *ngIf="!discussions.empty && sortingAvailable && selectedSortOrder" class="ion-text-wrap addon-forum-sorting-select">
|
||||||
|
@ -41,39 +55,15 @@
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ion-item *ngFor="let discussion of offlineDiscussions"
|
|
||||||
class="ion-text-wrap addon-mod-forum-discussion" detail="true"
|
|
||||||
[attr.lines="none"]="discussion.groupname" [class.core-selected-item]="discussion.timecreated == -selectedDiscussion"
|
|
||||||
(click)="openNewDiscussion(discussion.timecreated)">
|
|
||||||
<ion-label>
|
|
||||||
<div class="addon-mod-forum-discussion-title">
|
|
||||||
<h2>
|
|
||||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id" [courseId]="courseId"></core-format-text>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div class="addon-mod-forum-discussion-info">
|
|
||||||
<core-user-avatar [user]="discussion" slot="start" [courseId]="courseId" *ngIf="discussion.userfullname">
|
|
||||||
</core-user-avatar>
|
|
||||||
<div class="addon-mod-forum-discussion-author">
|
|
||||||
<h3 *ngIf="discussion.userfullname">{{discussion.userfullname}}</h3>
|
|
||||||
<p *ngIf="discussion.groupname"><ion-icon name="people"></ion-icon> {{ discussion.groupname }}</p>
|
|
||||||
<p><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
|
|
||||||
<ion-item *ngFor="let discussion of discussions.items"
|
<ion-item *ngFor="let discussion of discussions.items"
|
||||||
class="addon-mod-forum-discussion" detail="true"
|
class="addon-mod-forum-discussion" detail="true"
|
||||||
[class.core-selected-item]="discussions.isSelected(discussion)"
|
[lines]="discussion.groupname && 'none'" [class.core-selected-item]="discussions.isSelected(discussion)"
|
||||||
(click)="discussions.select(discussion)">
|
(click)="discussions.select(discussion)">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<div class="addon-mod-forum-discussion-title">
|
<div class="addon-mod-forum-discussion-title">
|
||||||
<h2 class="ion-text-wrap">
|
<h2 class="ion-text-wrap">
|
||||||
<ion-icon name="fa-map-pin" *ngIf="discussion.pinned">
|
<ion-icon name="fa-map-pin" *ngIf="discussion.pinned"></ion-icon>
|
||||||
</ion-icon>
|
<ion-icon name="fa-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred"></ion-icon>
|
||||||
<ion-icon name="fa-star" class="addon-forum-star" *ngIf="!discussion.pinned && discussion.starred">
|
|
||||||
</ion-icon>
|
|
||||||
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id" [courseId]="courseId"></core-format-text>
|
<core-format-text [text]="discussion.subject" contextLevel="module" [contextInstanceId]="module && module.id" [courseId]="courseId"></core-format-text>
|
||||||
</h2>
|
</h2>
|
||||||
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite"
|
<ion-button *ngIf="canPin || discussion.canlock || discussion.canfavourite"
|
||||||
|
@ -85,15 +75,15 @@
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="addon-mod-forum-discussion-info">
|
<div class="addon-mod-forum-discussion-info">
|
||||||
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId">
|
<core-user-avatar *ngIf="discussion.userfullname" [user]="discussion" slot="start" [courseId]="courseId"></core-user-avatar>
|
||||||
</core-user-avatar>
|
|
||||||
<div class="addon-mod-forum-discussion-author">
|
<div class="addon-mod-forum-discussion-author">
|
||||||
<h3 *ngIf="discussion.userfullname">{{discussion.userfullname}}</h3>
|
<h3 *ngIf="discussion.userfullname">{{discussion.userfullname}}</h3>
|
||||||
<p *ngIf="discussion.groupname"><ion-icon name="people"></ion-icon> {{ discussion.groupname }}</p>
|
<p *ngIf="discussion.groupname"><ion-icon name="people"></ion-icon> {{ discussion.groupname }}</p>
|
||||||
<p>{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}</p>
|
<p *ngIf="discussions.isOnlineDiscussion(discussion)">{{discussion.created * 1000 | coreFormatDate: "strftimerecentfull"}}</p>
|
||||||
|
<p *ngIf="discussions.isOfflineDiscussion(discussion)"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ion-row class="ion-text-center addon-mod-forum-discussion-more-info">
|
<ion-row *ngIf="discussions.isOnlineDiscussion(discussion)" class="ion-text-center addon-mod-forum-discussion-more-info">
|
||||||
<ion-col class="ion-text-start">
|
<ion-col class="ion-text-start">
|
||||||
<ion-note>
|
<ion-note>
|
||||||
<ion-icon name="time"></ion-icon> {{ 'addon.mod_forum.lastpost' | translate }}
|
<ion-icon name="time"></ion-icon> {{ 'addon.mod_forum.lastpost' | translate }}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { ModalController, PopoverController, Translate } from '@singletons';
|
||||||
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
||||||
import { AddonModForumHelper } from '@addons/mod/forum/services/helper.service';
|
import { AddonModForumHelper } from '@addons/mod/forum/services/helper.service';
|
||||||
import { CoreGroups, CoreGroupsProvider } from '@services/groups';
|
import { CoreGroups, CoreGroupsProvider } from '@services/groups';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents, CoreEventObserver } from '@singletons/events';
|
||||||
import { AddonModForumSyncProvider } from '@addons/mod/forum/services/sync.service';
|
import { AddonModForumSyncProvider } from '@addons/mod/forum/services/sync.service';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreUser } from '@features/user/services/user';
|
import { CoreUser } from '@features/user/services/user';
|
||||||
|
@ -40,24 +40,7 @@ import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu';
|
import { AddonModForumDiscussionOptionsMenuComponent } from '../discussion-options-menu/discussion-options-menu';
|
||||||
import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
|
import { AddonModForumSortOrderSelectorComponent } from '../sort-order-selector/sort-order-selector';
|
||||||
import { CoreScreen } from '@services/screen';
|
import { CoreScreen } from '@services/screen';
|
||||||
|
import { CoreArray } from '@singletons/array';
|
||||||
/**
|
|
||||||
* Type to use for selecting new discussion form in the discussions manager.
|
|
||||||
*/
|
|
||||||
type NewDiscussionForm = {
|
|
||||||
newDiscussion: true;
|
|
||||||
timeCreated: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type guard to infer NewDiscussionForm objects.
|
|
||||||
*
|
|
||||||
* @param discussion Object to check.
|
|
||||||
* @return Whether the object is a new discussion form.
|
|
||||||
*/
|
|
||||||
function isNewDiscussionForm(discussion: Record<string, unknown>): discussion is NewDiscussionForm {
|
|
||||||
return 'newDiscussion' in discussion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a forum entry page.
|
* Component that displays a forum entry page.
|
||||||
|
@ -78,8 +61,6 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
canLoadMore = false;
|
canLoadMore = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
discussions: AddonModForumDiscussionsManager;
|
discussions: AddonModForumDiscussionsManager;
|
||||||
offlineDiscussions: AddonModForumOfflineDiscussion[] = [];
|
|
||||||
selectedDiscussion = 0; // Disucssion ID or negative timecreated if it's an offline discussion.
|
|
||||||
canAddDiscussion = false;
|
canAddDiscussion = false;
|
||||||
addDiscussionText!: string;
|
addDiscussionText!: string;
|
||||||
availabilityMessage: string | null = null;
|
availabilityMessage: string | null = null;
|
||||||
|
@ -93,11 +74,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
protected page = 0;
|
protected page = 0;
|
||||||
trackPosts = false;
|
trackPosts = false;
|
||||||
protected usesGroups = false;
|
protected usesGroups = false;
|
||||||
protected syncManualObserver: any; // It will observe the sync manual event.
|
protected syncManualObserver?: CoreEventObserver; // It will observe the sync manual event.
|
||||||
protected replyObserver: any;
|
protected replyObserver?: CoreEventObserver;
|
||||||
protected newDiscObserver: any;
|
protected newDiscObserver?: CoreEventObserver;
|
||||||
protected viewDiscObserver: any;
|
protected viewDiscObserver?: CoreEventObserver;
|
||||||
protected changeDiscObserver: any;
|
protected changeDiscObserver?: CoreEventObserver;
|
||||||
|
|
||||||
hasOfflineRatings?: boolean;
|
hasOfflineRatings?: boolean;
|
||||||
protected ratingOfflineObserver: any;
|
protected ratingOfflineObserver: any;
|
||||||
|
@ -141,6 +122,41 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
AddonModForumProvider.REPLY_DISCUSSION_EVENT,
|
AddonModForumProvider.REPLY_DISCUSSION_EVENT,
|
||||||
this.eventReceived.bind(this, false),
|
this.eventReceived.bind(this, false),
|
||||||
);
|
);
|
||||||
|
this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, (data: any) => {
|
||||||
|
if ((this.forum && this.forum.id === data.forumId) || data.cmId === this.module!.id) {
|
||||||
|
AddonModForum.instance.invalidateDiscussionsList(this.forum!.id).finally(() => {
|
||||||
|
if (data.discussionId) {
|
||||||
|
// Discussion changed, search it in the list of discussions.
|
||||||
|
const discussion = this.discussions.items.find(
|
||||||
|
(disc) => this.discussions.isOnlineDiscussion(disc) && data.discussionId == disc.discussion,
|
||||||
|
) as AddonModForumDiscussion;
|
||||||
|
|
||||||
|
if (discussion) {
|
||||||
|
if (typeof data.locked != 'undefined') {
|
||||||
|
discussion.locked = data.locked;
|
||||||
|
}
|
||||||
|
if (typeof data.pinned != 'undefined') {
|
||||||
|
discussion.pinned = data.pinned;
|
||||||
|
}
|
||||||
|
if (typeof data.starred != 'undefined') {
|
||||||
|
discussion.starred = data.starred;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showLoadingAndRefresh(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof data.deleted != 'undefined' && data.deleted) {
|
||||||
|
if (data.post.parentid == 0 && CoreScreen.instance.isTablet && !this.discussions.empty) {
|
||||||
|
// Discussion deleted, clear details page.
|
||||||
|
this.discussions.select(this.discussions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showLoadingAndRefresh(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngAfterViewInit(): Promise<void> {
|
async ngAfterViewInit(): Promise<void> {
|
||||||
|
@ -356,7 +372,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
this.hasOffline = !!offlineDiscussions.length;
|
this.hasOffline = !!offlineDiscussions.length;
|
||||||
|
|
||||||
if (!this.hasOffline) {
|
if (!this.hasOffline) {
|
||||||
this.offlineDiscussions = [];
|
this.discussions.setOfflineDiscussions([]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -387,7 +403,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
// Sort discussion by time (newer first).
|
// Sort discussion by time (newer first).
|
||||||
offlineDiscussions.sort((a, b) => b.timecreated - a.timecreated);
|
offlineDiscussions.sort((a, b) => b.timecreated - a.timecreated);
|
||||||
|
|
||||||
this.offlineDiscussions = offlineDiscussions;
|
this.discussions.setOfflineDiscussions(offlineDiscussions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -435,7 +451,12 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.discussions.setItems(this.page === 0 ? discussions : this.discussions.items.concat(discussions));
|
if (this.page === 0) {
|
||||||
|
this.discussions.setOnlineDiscussions(discussions);
|
||||||
|
} else {
|
||||||
|
this.discussions.setItems(this.discussions.items.concat(discussions));
|
||||||
|
}
|
||||||
|
|
||||||
this.canLoadMore = response.canLoadMore;
|
this.canLoadMore = response.canLoadMore;
|
||||||
this.page++;
|
this.page++;
|
||||||
|
|
||||||
|
@ -540,18 +561,20 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
this.showLoadingAndRefresh(false).finally(() => {
|
this.showLoadingAndRefresh(false).finally(() => {
|
||||||
// If it's a new discussion in tablet mode, try to open it.
|
// If it's a new discussion in tablet mode, try to open it.
|
||||||
if (isNewDiscussion && CoreScreen.instance.isTablet) {
|
if (isNewDiscussion && CoreScreen.instance.isTablet) {
|
||||||
if (data.discussionIds) {
|
const discussion = this.discussions.items.find(disc => {
|
||||||
// Discussion sent to server, search it in the list of discussions.
|
if (this.discussions.isOfflineDiscussion(disc)) {
|
||||||
const discussion = this.discussions.items.find(
|
return disc.timecreated === data.discTimecreated;
|
||||||
(disc) =>
|
}
|
||||||
!isNewDiscussionForm(disc) &&
|
|
||||||
data.discussionIds.indexOf(disc.discussion) >= 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
|
if (this.discussions.isOnlineDiscussion(disc)) {
|
||||||
|
return CoreArray.contains(data.discussionIds, disc.discussion);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (discussion || !this.discussions.empty) {
|
||||||
this.discussions.select(discussion ?? this.discussions.items[0]);
|
this.discussions.select(discussion ?? this.discussions.items[0]);
|
||||||
} else if (data.discTimecreated) {
|
|
||||||
// It's an offline discussion, open it.
|
|
||||||
this.openNewDiscussion(data.discTimecreated);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -566,11 +589,8 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
*
|
*
|
||||||
* @param timeCreated Creation time of the offline discussion.
|
* @param timeCreated Creation time of the offline discussion.
|
||||||
*/
|
*/
|
||||||
openNewDiscussion(timeCreated: number = 0): void {
|
openNewDiscussion(): void {
|
||||||
this.discussions.select({
|
this.discussions.select({ newDiscussion: true });
|
||||||
newDiscussion: true,
|
|
||||||
timeCreated,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -650,7 +670,20 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModForumDiscussion | NewDiscussionForm> {
|
/**
|
||||||
|
* Type to select the new discussion form.
|
||||||
|
*/
|
||||||
|
type NewDiscussionForm = { newDiscussion: true };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of items that can be held by the discussions manager.
|
||||||
|
*/
|
||||||
|
type DiscussionItem = AddonModForumDiscussion | AddonModForumOfflineDiscussion | NewDiscussionForm;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discussions manager.
|
||||||
|
*/
|
||||||
|
class AddonModForumDiscussionsManager extends CorePageItemsListManager<DiscussionItem> {
|
||||||
|
|
||||||
private discussionsPathPrefix: string;
|
private discussionsPathPrefix: string;
|
||||||
private component: AddonModForumIndexComponent;
|
private component: AddonModForumIndexComponent;
|
||||||
|
@ -662,29 +695,107 @@ class AddonModForumDiscussionsManager extends CorePageItemsListManager<AddonModF
|
||||||
this.discussionsPathPrefix = discussionsPathPrefix;
|
this.discussionsPathPrefix = discussionsPathPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemQueryParams(discussion: AddonModForumDiscussion | NewDiscussionForm): Params {
|
get onlineDiscussions(): AddonModForumDiscussion[] {
|
||||||
|
return this.items.filter(discussion => this.isOnlineDiscussion(discussion)) as AddonModForumDiscussion[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
getItemQueryParams(discussion: DiscussionItem): Params {
|
||||||
return {
|
return {
|
||||||
courseId: this.component.courseId,
|
courseId: this.component.courseId,
|
||||||
cmId: this.component.module!.id,
|
cmId: this.component.module!.id,
|
||||||
forumId: this.component.forum!.id,
|
forumId: this.component.forum!.id,
|
||||||
...(
|
...(this.isOnlineDiscussion(discussion) ? { discussion, trackPosts: this.component.trackPosts } : {}),
|
||||||
isNewDiscussionForm(discussion)
|
|
||||||
? { timeCreated: discussion.timeCreated }
|
|
||||||
: { discussion, trackPosts: this.component.trackPosts }
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getItemPath(discussion: AddonModForumDiscussion | NewDiscussionForm): string {
|
/**
|
||||||
const discussionId = isNewDiscussionForm(discussion) ? 'new' : discussion.id;
|
* Type guard to infer NewDiscussionForm objects.
|
||||||
|
*
|
||||||
return this.discussionsPathPrefix + discussionId;
|
* @param discussion Item to check.
|
||||||
|
* @return Whether the item is a new discussion form.
|
||||||
|
*/
|
||||||
|
isNewDiscussionForm(discussion: DiscussionItem): discussion is NewDiscussionForm {
|
||||||
|
return 'newDiscussion' in discussion;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
/**
|
||||||
const discussionId = route.params.discussionId;
|
* Type guard to infer AddonModForumDiscussion objects.
|
||||||
|
*
|
||||||
|
* @param discussion Item to check.
|
||||||
|
* @return Whether the item is an online discussion.
|
||||||
|
*/
|
||||||
|
isOfflineDiscussion(discussion: DiscussionItem): discussion is AddonModForumOfflineDiscussion {
|
||||||
|
return !this.isNewDiscussionForm(discussion)
|
||||||
|
&& !this.isOnlineDiscussion(discussion);
|
||||||
|
}
|
||||||
|
|
||||||
return discussionId ? this.discussionsPathPrefix + discussionId : null;
|
/**
|
||||||
|
* Type guard to infer AddonModForumDiscussion objects.
|
||||||
|
*
|
||||||
|
* @param discussion Item to check.
|
||||||
|
* @return Whether the item is an online discussion.
|
||||||
|
*/
|
||||||
|
isOnlineDiscussion(discussion: DiscussionItem): discussion is AddonModForumDiscussion {
|
||||||
|
return 'id' in discussion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update online discussion items.
|
||||||
|
*
|
||||||
|
* @param onlineDiscussions Online discussions
|
||||||
|
*/
|
||||||
|
setOnlineDiscussions(onlineDiscussions: AddonModForumDiscussion[]): void {
|
||||||
|
const otherDiscussions = this.items.filter(discussion => !this.isOnlineDiscussion(discussion));
|
||||||
|
|
||||||
|
this.setItems(otherDiscussions.concat(onlineDiscussions));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update offline discussion items.
|
||||||
|
*
|
||||||
|
* @param offlineDiscussions Offline discussions
|
||||||
|
*/
|
||||||
|
setOfflineDiscussions(offlineDiscussions: AddonModForumOfflineDiscussion[]): void {
|
||||||
|
const otherDiscussions = this.items.filter(discussion => !this.isOfflineDiscussion(discussion));
|
||||||
|
|
||||||
|
this.setItems((offlineDiscussions as DiscussionItem[]).concat(otherDiscussions));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getItemPath(discussion: DiscussionItem): string {
|
||||||
|
const getRelativePath = () => {
|
||||||
|
if (this.isOnlineDiscussion(discussion)) {
|
||||||
|
return discussion.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isOfflineDiscussion(discussion)) {
|
||||||
|
return `new/${discussion.timecreated}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'new/0';
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.discussionsPathPrefix + getRelativePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected getSelectedItemPath(route: ActivatedRouteSnapshot): string | null {
|
||||||
|
if (route.params.discussionId) {
|
||||||
|
return this.discussionsPathPrefix + route.params.discussionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.params.timeCreated) {
|
||||||
|
return this.discussionsPathPrefix + `new/${route.params.timeCreated}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ const mobileRoutes: Routes = [
|
||||||
component: AddonModForumIndexPage,
|
component: AddonModForumIndexPage,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ':courseId/:cmId/new',
|
path: ':courseId/:cmId/new/:timeCreated',
|
||||||
loadChildren: () => import('./pages/new-discussion/new-discussion.module').then(m => m.AddonForumNewDiscussionPageModule),
|
loadChildren: () => import('./pages/new-discussion/new-discussion.module').then(m => m.AddonForumNewDiscussionPageModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -43,7 +43,7 @@ const tabletRoutes: Routes = [
|
||||||
component: AddonModForumIndexPage,
|
component: AddonModForumIndexPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'new',
|
path: 'new/:timeCreated',
|
||||||
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
||||||
.then(m => m.AddonForumNewDiscussionPageModule),
|
.then(m => m.AddonForumNewDiscussionPageModule),
|
||||||
},
|
},
|
||||||
|
|
|
@ -34,7 +34,7 @@ const mainMenuRoutes: Routes = [
|
||||||
...conditionalRoutes(
|
...conditionalRoutes(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
path: 'course/index/contents/mod_forum/new',
|
path: 'course/index/contents/mod_forum/new/:timeCreated',
|
||||||
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
||||||
.then(m => m.AddonForumNewDiscussionPageModule),
|
.then(m => m.AddonForumNewDiscussionPageModule),
|
||||||
},
|
},
|
||||||
|
@ -50,7 +50,7 @@ const mainMenuRoutes: Routes = [
|
||||||
const courseContentsRoutes: Routes = conditionalRoutes(
|
const courseContentsRoutes: Routes = conditionalRoutes(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
path: 'mod_forum/new',
|
path: 'mod_forum/new/:timeCreated',
|
||||||
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
loadChildren: () => import('./pages/new-discussion/new-discussion.module')
|
||||||
.then(m => m.AddonForumNewDiscussionPageModule),
|
.then(m => m.AddonForumNewDiscussionPageModule),
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// 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 { ActivatedRouteSnapshot, Params } from '@angular/router';
|
import { ActivatedRoute, ActivatedRouteSnapshot, Params } from '@angular/router';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
@ -135,17 +135,19 @@ export abstract class CorePageItemsListManager<Item> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this item is already selected, do nothing.
|
// If this item is already selected, do nothing.
|
||||||
|
const itemRoute = this.getItemRoute(route);
|
||||||
const itemPath = this.getItemPath(item);
|
const itemPath = this.getItemPath(item);
|
||||||
|
const selectedItemPath = itemRoute ? this.getSelectedItemPath(itemRoute.snapshot) : null;
|
||||||
|
|
||||||
if (route.firstChild?.routeConfig?.path === itemPath) {
|
if (selectedItemPath === itemPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigate to item.
|
// Navigate to item.
|
||||||
const params = this.getItemQueryParams(item);
|
const params = this.getItemQueryParams(item);
|
||||||
const pathPrefix = route.firstChild ? itemPath.split('/').fill('../').join('') : '';
|
const pathPrefix = selectedItemPath ? selectedItemPath.split('/').fill('../').join('') : '';
|
||||||
|
|
||||||
await CoreNavigator.instance.navigate(pathPrefix + itemPath, { params });
|
await CoreNavigator.instance.navigate(pathPrefix + itemPath, { params, reset: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -220,4 +222,20 @@ export abstract class CorePageItemsListManager<Item> {
|
||||||
*/
|
*/
|
||||||
protected abstract getSelectedItemPath(route: ActivatedRouteSnapshot): string | null;
|
protected abstract getSelectedItemPath(route: ActivatedRouteSnapshot): string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the active item route, if any.
|
||||||
|
*
|
||||||
|
* @param pageRoute Page route.
|
||||||
|
* @return Item route.
|
||||||
|
*/
|
||||||
|
private getItemRoute(pageRoute: ActivatedRoute): ActivatedRoute | null {
|
||||||
|
let itemRoute = pageRoute.firstChild;
|
||||||
|
|
||||||
|
while (itemRoute && !itemRoute.component) {
|
||||||
|
itemRoute = itemRoute.firstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemRoute;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue