forked from CIT/Vmeda.Online
		
	MOBILE-3663 forum: Add ratings to forum
This commit is contained in:
		
							parent
							
								
									44a03b61e7
								
							
						
					
					
						commit
						44e3c42d76
					
				@ -18,6 +18,7 @@ import { CoreCourseComponentsModule } from '@features/course/components/componen
 | 
			
		||||
import { CoreEditorComponentsModule } from '@features/editor/components/components.module';
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { CoreTagComponentsModule } from '@features/tag/components/components.module';
 | 
			
		||||
import { CoreRatingComponentsModule } from '@features/rating/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { AddonModForumDiscussionOptionsMenuComponent } from './discussion-options-menu/discussion-options-menu';
 | 
			
		||||
import { AddonModForumEditPostComponent } from './edit-post/edit-post';
 | 
			
		||||
@ -40,6 +41,7 @@ import { AddonModForumSortOrderSelectorComponent } from './sort-order-selector/s
 | 
			
		||||
        CoreCourseComponentsModule,
 | 
			
		||||
        CoreTagComponentsModule,
 | 
			
		||||
        CoreEditorComponentsModule,
 | 
			
		||||
        CoreRatingComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [
 | 
			
		||||
        AddonModForumIndexComponent,
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,10 @@ import { CoreScreen } from '@services/screen';
 | 
			
		||||
import { CoreArray } from '@singletons/array';
 | 
			
		||||
import { AddonModForumPrefetchHandler } from '../../services/handlers/prefetch';
 | 
			
		||||
import { AddonModForumModuleHandlerService } from '../../services/handlers/module';
 | 
			
		||||
import { CoreRatingProvider } from '@features/rating/services/rating';
 | 
			
		||||
import { CoreRatingSyncProvider } from '@features/rating/services/rating-sync';
 | 
			
		||||
import { CoreRatingOffline } from '@features/rating/services/rating-offline';
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Component that displays a forum entry page.
 | 
			
		||||
@ -88,9 +92,9 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
    protected viewDiscObserver?: CoreEventObserver;
 | 
			
		||||
    protected changeDiscObserver?: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    hasOfflineRatings?: boolean;
 | 
			
		||||
    protected ratingOfflineObserver: any;
 | 
			
		||||
    protected ratingSyncObserver: any;
 | 
			
		||||
    hasOfflineRatings = false;
 | 
			
		||||
    protected ratingOfflineObserver?: CoreEventObserver;
 | 
			
		||||
    protected ratingSyncObserver?: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        route: ActivatedRoute,
 | 
			
		||||
@ -166,7 +170,21 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // @todo Listen for offline ratings saved and synced.
 | 
			
		||||
        // Listen for offline ratings saved and synced.
 | 
			
		||||
        this.ratingOfflineObserver = CoreEvents.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => {
 | 
			
		||||
            if (this.forum && data.component == 'mod_forum' && data.ratingArea == 'post' &&
 | 
			
		||||
                    data.contextLevel == ContextLevel.MODULE && data.instanceId == this.forum.cmid) {
 | 
			
		||||
                this.hasOfflineRatings = true;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.ratingSyncObserver = CoreEvents.on(CoreRatingSyncProvider.SYNCED_EVENT, async (data) => {
 | 
			
		||||
            if (this.forum && data.component == 'mod_forum' && data.ratingArea == 'post' &&
 | 
			
		||||
                    data.contextLevel == ContextLevel.MODULE && data.instanceId == this.forum.cmid) {
 | 
			
		||||
                this.hasOfflineRatings =
 | 
			
		||||
                    await CoreRatingOffline.hasRatings('mod_forum', 'post', ContextLevel.MODULE, this.forum.cmid);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async ngAfterViewInit(): Promise<void> {
 | 
			
		||||
@ -224,7 +242,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
 | 
			
		||||
            await Promise.all([
 | 
			
		||||
                this.fetchOfflineDiscussions(),
 | 
			
		||||
                this.fetchDiscussions(refresh),
 | 
			
		||||
                // @todo fetch hasOfflineRatings.
 | 
			
		||||
                CoreRatingOffline.hasRatings('mod_forum', 'post', ContextLevel.MODULE, this.forum!.cmid).then((hasRatings) => {
 | 
			
		||||
                    this.hasOfflineRatings = hasRatings;
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }),
 | 
			
		||||
            ]);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (refresh) {
 | 
			
		||||
 | 
			
		||||
@ -75,8 +75,7 @@
 | 
			
		||||
                <core-tag-list [tags]="post.tags"></core-tag-list>
 | 
			
		||||
            </ion-label>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <!-- @todo -->
 | 
			
		||||
        <!-- <core-rating-rate *ngIf="forum && ratingInfo"
 | 
			
		||||
        <core-rating-rate *ngIf="forum && ratingInfo"
 | 
			
		||||
            [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id"
 | 
			
		||||
            [itemSetId]="discussionId" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale"
 | 
			
		||||
            [userId]="post.author.id" (onUpdate)="ratingUpdated()">
 | 
			
		||||
@ -84,7 +83,7 @@
 | 
			
		||||
        <core-rating-aggregate *ngIf="forum && ratingInfo"
 | 
			
		||||
            [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id"
 | 
			
		||||
            [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale">
 | 
			
		||||
        </core-rating-aggregate> -->
 | 
			
		||||
        </core-rating-aggregate>
 | 
			
		||||
 | 
			
		||||
        <ion-item *ngIf="post.id > 0 && post.capabilities.reply && !post.isprivatereply"
 | 
			
		||||
            class="ion-no-padding ion-text-end addon-forum-reply-button">
 | 
			
		||||
 | 
			
		||||
@ -52,6 +52,7 @@ import { AddonModForumOffline, AddonModForumReplyOptions } from '../../services/
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { AddonModForumPostOptionsMenuComponent } from '../post-options-menu/post-options-menu';
 | 
			
		||||
import { AddonModForumEditPostComponent } from '../edit-post/edit-post';
 | 
			
		||||
import { CoreRatingInfo } from '@features/rating/services/rating';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
 | 
			
		||||
@ -75,7 +76,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy, OnChanges
 | 
			
		||||
    @Input() forum!: AddonModForumData; // The forum the post belongs to. Required for attachments and offline posts.
 | 
			
		||||
    @Input() accessInfo!: AddonModForumAccessInformation; // Forum access information.
 | 
			
		||||
    @Input() parentSubject?: string; // Subject of parent post.
 | 
			
		||||
    @Input() ratingInfo?: any; // TODO CoreRatingInfo; // Rating info item.
 | 
			
		||||
    @Input() ratingInfo?: CoreRatingInfo; // Rating info item.
 | 
			
		||||
    @Input() leavingPage?: boolean; // Whether the page that contains this post is being left and will be destroyed.
 | 
			
		||||
    @Input() highlight = false;
 | 
			
		||||
    @Output() onPostChange: EventEmitter<void> = new EventEmitter<void>(); // Event emitted when a reply is posted or modified.
 | 
			
		||||
 | 
			
		||||
@ -12,9 +12,13 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Component, OnDestroy, ViewChild, OnInit, AfterViewInit, ElementRef, Optional } from '@angular/core';
 | 
			
		||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
 | 
			
		||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreRatingInfo, CoreRatingProvider } from '@features/rating/services/rating';
 | 
			
		||||
import { CoreRatingOffline } from '@features/rating/services/rating-offline';
 | 
			
		||||
import { CoreRatingSyncProvider } from '@features/rating/services/rating-sync';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CanLeave } from '@guards/can-leave';
 | 
			
		||||
import { IonContent } from '@ionic/angular';
 | 
			
		||||
@ -34,7 +38,6 @@ import {
 | 
			
		||||
    AddonModForumDiscussion,
 | 
			
		||||
    AddonModForumPost,
 | 
			
		||||
    AddonModForumProvider,
 | 
			
		||||
    AddonModForumRatingInfo,
 | 
			
		||||
} from '../../services/forum';
 | 
			
		||||
import { AddonModForumHelper } from '../../services/helper';
 | 
			
		||||
import { AddonModForumOffline } from '../../services/offline';
 | 
			
		||||
@ -101,8 +104,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
    protected syncObserver?: CoreEventObserver;
 | 
			
		||||
    protected syncManualObserver?: CoreEventObserver;
 | 
			
		||||
 | 
			
		||||
    ratingInfo?: AddonModForumRatingInfo;
 | 
			
		||||
    hasOfflineRatings!: boolean;
 | 
			
		||||
    ratingInfo?: CoreRatingInfo;
 | 
			
		||||
    hasOfflineRatings = false;
 | 
			
		||||
    protected ratingOfflineObserver?: CoreEventObserver;
 | 
			
		||||
    protected ratingSyncObserver?: CoreEventObserver;
 | 
			
		||||
    protected changeDiscObserver?: CoreEventObserver;
 | 
			
		||||
@ -203,7 +206,20 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
            AddonModForum.invalidateDiscussionsList(this.forumId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // @todo Listen for offline ratings saved and synced.
 | 
			
		||||
        // Listen for offline ratings saved and synced.
 | 
			
		||||
        this.ratingOfflineObserver = CoreEvents.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => {
 | 
			
		||||
            if (data.component == 'mod_forum' && data.ratingArea == 'post' && data.contextLevel == ContextLevel.MODULE &&
 | 
			
		||||
                    data.instanceId == this.cmId && data.itemSetId == this.discussionId) {
 | 
			
		||||
                this.hasOfflineRatings = true;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.ratingSyncObserver = CoreEvents.on(CoreRatingSyncProvider.SYNCED_EVENT, async (data) => {
 | 
			
		||||
            if (data.component == 'mod_forum' && data.ratingArea == 'post' && data.contextLevel == ContextLevel.MODULE &&
 | 
			
		||||
                    data.instanceId == this.cmId && data.itemSetId == this.discussionId) {
 | 
			
		||||
                this.hasOfflineRatings = false;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.changeDiscObserver = CoreEvents.on(AddonModForumProvider.CHANGE_DISCUSSION_EVENT, data => {
 | 
			
		||||
            if ((this.forumId && this.forumId === data.forumId) || data.cmId === this.cmId) {
 | 
			
		||||
@ -345,7 +361,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
 | 
			
		||||
            const response = await AddonModForum.getDiscussionPosts(this.discussionId, { cmId: this.cmId });
 | 
			
		||||
            const replies = await AddonModForumOffline.getDiscussionReplies(this.discussionId);
 | 
			
		||||
            const ratingInfo = response.ratinginfo;
 | 
			
		||||
            this.ratingInfo = response.ratinginfo;
 | 
			
		||||
 | 
			
		||||
            onlinePosts = response.posts;
 | 
			
		||||
            this.courseId = response.courseid || this.courseId;
 | 
			
		||||
            this.forumId = response.forumid || this.forumId;
 | 
			
		||||
@ -462,7 +479,6 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this.posts = posts;
 | 
			
		||||
            this.ratingInfo = ratingInfo;
 | 
			
		||||
            this.postSubjects = this.getAllPosts().reduce(
 | 
			
		||||
                (postSubjects, post) => {
 | 
			
		||||
                    postSubjects[post.id] = post.subject;
 | 
			
		||||
@ -487,7 +503,8 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
 | 
			
		||||
                this.canPin = false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // @todo fetch hasOfflineRatings.
 | 
			
		||||
            this.hasOfflineRatings =
 | 
			
		||||
                await CoreRatingOffline.hasRatings('mod_forum', 'post', ContextLevel.MODULE, this.cmId, this.discussionId);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModal(error);
 | 
			
		||||
        } finally {
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { CoreCourseCommonModWSOptions } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreFileEntry } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreRatingInfo } from '@features/rating/services/rating';
 | 
			
		||||
import { CoreUser } from '@features/user/services/user';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
@ -553,7 +554,7 @@ export class AddonModForumProvider {
 | 
			
		||||
        posts: AddonModForumPost[];
 | 
			
		||||
        courseid?: number;
 | 
			
		||||
        forumid?: number;
 | 
			
		||||
        ratinginfo?: AddonModForumRatingInfo;
 | 
			
		||||
        ratinginfo?: CoreRatingInfo;
 | 
			
		||||
    }> {
 | 
			
		||||
        // Convenience function to translate legacy data to new format.
 | 
			
		||||
        const translateLegacyPostsFormat = (posts: AddonModForumLegacyPost[]): AddonModForumPost[] => posts.map((post) => {
 | 
			
		||||
@ -1526,40 +1527,6 @@ export type AddonModForumLegacyPost = {
 | 
			
		||||
    }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Forum rating info.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModForumRatingInfo = {
 | 
			
		||||
    contextid: number; // Context id.
 | 
			
		||||
    component: string; // Context name.
 | 
			
		||||
    ratingarea: string; // Rating area name.
 | 
			
		||||
    canviewall?: boolean; // Whether the user can view all the individual ratings.
 | 
			
		||||
    canviewany?: boolean; // Whether the user can view aggregate of ratings of others.
 | 
			
		||||
    scales?: { // Different scales used information.
 | 
			
		||||
        id: number; // Scale id.
 | 
			
		||||
        courseid?: number; // Course id.
 | 
			
		||||
        name?: string; // Scale name (when a real scale is used).
 | 
			
		||||
        max: number; // Max value for the scale.
 | 
			
		||||
        isnumeric: boolean; // Whether is a numeric scale.
 | 
			
		||||
        items?: { // Scale items. Only returned for not numerical scales.
 | 
			
		||||
            value: number; // Scale value/option id.
 | 
			
		||||
            name: string; // Scale name.
 | 
			
		||||
        }[];
 | 
			
		||||
    }[];
 | 
			
		||||
    ratings?: { // The ratings.
 | 
			
		||||
        itemid: number; // Item id.
 | 
			
		||||
        scaleid?: number; // Scale id.
 | 
			
		||||
        userid?: number; // User who rated id.
 | 
			
		||||
        aggregate?: number; // Aggregated ratings grade.
 | 
			
		||||
        aggregatestr?: string; // Aggregated ratings as string.
 | 
			
		||||
        aggregatelabel?: string; // The aggregation label.
 | 
			
		||||
        count?: number; // Ratings count (used when aggregating).
 | 
			
		||||
        rating?: number; // The rating the user gave.
 | 
			
		||||
        canrate?: boolean; // Whether the user can rate the item.
 | 
			
		||||
        canviewaggregate?: boolean; // Whether the user can view the aggregated grade.
 | 
			
		||||
    }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Options to pass to get discussions.
 | 
			
		||||
 */
 | 
			
		||||
@ -1994,7 +1961,7 @@ export type AddonModForumGetDiscussionPostsWSResponse = {
 | 
			
		||||
    posts: AddonModForumWSPost[];
 | 
			
		||||
    forumid: number; // The forum id.
 | 
			
		||||
    courseid: number; // The forum course id.
 | 
			
		||||
    ratinginfo?: AddonModForumRatingInfo; // Rating information.
 | 
			
		||||
    ratinginfo?: CoreRatingInfo; // Rating information.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2012,7 +1979,7 @@ export type AddonModForumGetForumDiscussionPostsWSParams = {
 | 
			
		||||
 */
 | 
			
		||||
export type AddonModForumGetForumDiscussionPostsWSResponse = {
 | 
			
		||||
    posts: AddonModForumLegacyPost[];
 | 
			
		||||
    ratinginfo?: AddonModForumRatingInfo; // Rating information.
 | 
			
		||||
    ratinginfo?: CoreRatingInfo; // Rating information.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,11 +12,13 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { CoreSyncBaseProvider } from '@classes/base-sync';
 | 
			
		||||
import { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseLogHelper } from '@features/course/services/log-helper';
 | 
			
		||||
import { CoreFileUploader } from '@features/fileuploader/services/fileuploader';
 | 
			
		||||
import { CoreRatingSync } from '@features/rating/services/rating-sync';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreGroups } from '@services/groups';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
@ -327,14 +329,44 @@ export class AddonModForumSyncProvider extends CoreSyncBaseProvider<AddonModForu
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved if sync is successful, rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
    async syncRatings(cmId?: number, discussionId?: number, force?: boolean, siteId?: string): Promise<{
 | 
			
		||||
        updated: boolean;
 | 
			
		||||
        warnings: string[];
 | 
			
		||||
    }> {
 | 
			
		||||
        // @todo
 | 
			
		||||
    async syncRatings(cmId?: number, discussionId?: number, force?: boolean, siteId?: string): Promise<AddonModForumSyncResult> {
 | 
			
		||||
        siteId = siteId || CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        return { updated: true, warnings: [] };
 | 
			
		||||
        const results =
 | 
			
		||||
            await CoreRatingSync.syncRatings('mod_forum', 'post', ContextLevel.MODULE, cmId, discussionId, force, siteId);
 | 
			
		||||
 | 
			
		||||
        let updated = false;
 | 
			
		||||
        const warnings: string[] = [];
 | 
			
		||||
        const promises: Promise<void>[] = [];
 | 
			
		||||
 | 
			
		||||
        results.forEach((result) => {
 | 
			
		||||
            if (result.updated.length) {
 | 
			
		||||
                updated = true;
 | 
			
		||||
 | 
			
		||||
                // Invalidate discussions of updated ratings.
 | 
			
		||||
                promises.push(AddonModForum.invalidateDiscussionPosts(result.itemSet!.itemSetId, undefined, siteId));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (result.warnings.length) {
 | 
			
		||||
                // Fetch forum to construct the warning message.
 | 
			
		||||
                promises.push(AddonModForum.getForum(result.itemSet!.courseId!, result.itemSet!.instanceId, { siteId })
 | 
			
		||||
                    .then((forum) => {
 | 
			
		||||
                        result.warnings.forEach((warning) => {
 | 
			
		||||
                            warnings.push(Translate.instant('core.warningofflinedatadeleted', {
 | 
			
		||||
                                component: this.componentTranslate,
 | 
			
		||||
                                name: forum.name,
 | 
			
		||||
                                error: warning,
 | 
			
		||||
                            }));
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                        return;
 | 
			
		||||
                    }));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await CoreUtils.allPromises(promises);
 | 
			
		||||
 | 
			
		||||
        return { updated, warnings };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user