MOBILE-3873 bbb: Support BBB activity
parent
fd8d433f05
commit
30fdefd1db
|
@ -433,6 +433,19 @@
|
|||
"addon.mod_assign_submission_file.pluginname": "assignsubmission_file",
|
||||
"addon.mod_assign_submission_onlinetext.pluginname": "assignsubmission_onlinetext",
|
||||
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
||||
"addon.mod_bigbluebuttonbn.mod_form_field_closingtime": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.mod_form_field_openingtime": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.userlimitreached": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_conference_action_join": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_error_unable_join_student": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_groups_selection_warning": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_conference_in_progress": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_conference_room_ready": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_moderator": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_moderators": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_session_started_at": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_viewer": "bigbluebuttonbn",
|
||||
"addon.mod_bigbluebuttonbn.view_message_viewers": "bigbluebuttonbn",
|
||||
"addon.mod_book.errorchapter": "book",
|
||||
"addon.mod_book.modulenameplural": "book",
|
||||
"addon.mod_book.navnexttitle": "book",
|
||||
|
|
|
@ -55,6 +55,7 @@ export class AddonModAssignSubmissionsSource extends CoreItemsManagerSource<Addo
|
|||
separateGroups: false,
|
||||
visibleGroups: false,
|
||||
defaultGroupId: 0,
|
||||
canAccessAllGroups: false,
|
||||
};
|
||||
|
||||
protected submissionsData: { canviewsubmissions: boolean; submissions?: AddonModAssignSubmission[] } = {
|
||||
|
|
|
@ -71,6 +71,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
|||
separateGroups: false,
|
||||
visibleGroups: false,
|
||||
defaultGroupId: 0,
|
||||
canAccessAllGroups: false,
|
||||
};
|
||||
|
||||
// Status.
|
||||
|
|
|
@ -35,6 +35,96 @@
|
|||
contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
|
||||
</core-course-module-description>
|
||||
|
||||
<!-- TODO -->
|
||||
<p>TODO: BBB native content</p>
|
||||
<ng-container *ngIf="groupInfo && (groupInfo.separateGroups || groupInfo.visibleGroups)">
|
||||
<ion-card class="core-info-card" *ngIf="groupInfo.groups && groupInfo.groups.length > 1">
|
||||
<ion-item>
|
||||
<ion-icon name="fas-question-circle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ 'addon.mod_bigbluebuttonbn.view_groups_selection_warning' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<ion-item class="ion-text-wrap" >
|
||||
<ion-label id="addon-bigbluebuttonbn-groupslabel">
|
||||
<ng-container *ngIf="groupInfo.separateGroups">{{'core.groupsseparate' | translate }}</ng-container>
|
||||
<ng-container *ngIf="groupInfo.visibleGroups">{{'core.groupsvisible' | translate }}</ng-container>
|
||||
</ion-label>
|
||||
<ion-select [(ngModel)]="groupId" (ionChange)="groupChanged()" aria-labelledby="addon-bigbluebuttonbn-groupslabel"
|
||||
interface="action-sheet" [interfaceOptions]="{header: 'core.group' | translate}">
|
||||
<ion-select-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">
|
||||
{{groupOpt.name}}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="meetingInfo">
|
||||
<ion-item class="ion-text-wrap" *ngIf="meetingInfo.openingtime">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_bigbluebuttonbn.mod_form_field_openingtime' | translate }}</h3>
|
||||
</ion-label>
|
||||
<p slot="end">{{ meetingInfo.openingtime * 1000 | coreFormatDate }}</p>
|
||||
</ion-item>
|
||||
<ion-item class="ion-text-wrap" *ngIf="meetingInfo.closingtime">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_bigbluebuttonbn.mod_form_field_closingtime' | translate }}</h3>
|
||||
</ion-label>
|
||||
<p slot="end">{{ meetingInfo.closingtime * 1000 | coreFormatDate }}</p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="meetingInfo.canjoin && !meetingInfo.statusrunning">
|
||||
<ion-label>
|
||||
<p>{{ 'addon.mod_bigbluebuttonbn.view_message_conference_room_ready' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ng-container *ngIf="meetingInfo.statusrunning">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<p>{{ 'addon.mod_bigbluebuttonbn.view_message_conference_in_progress' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-text-wrap" *ngIf="meetingInfo.startedat">
|
||||
<ion-label>
|
||||
<h3>{{ 'addon.mod_bigbluebuttonbn.view_message_session_started_at' | translate }}</h3>
|
||||
</ion-label>
|
||||
<p slot="end">{{ meetingInfo.startedat * 1000 | coreFormatDate: "strftimetime" }}</p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h3 *ngIf="meetingInfo.moderatorplural">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_message_moderators' | translate }}
|
||||
</h3>
|
||||
<h3 *ngIf="!meetingInfo.moderatorplural">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_message_moderator' | translate }}
|
||||
</h3>
|
||||
</ion-label>
|
||||
<p slot="end">{{ meetingInfo.moderatorcount }}</p>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h3 *ngIf="meetingInfo.participantplural">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_message_viewers' | translate }}
|
||||
</h3>
|
||||
<h3 *ngIf="!meetingInfo.participantplural">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_message_viewer' | translate }}
|
||||
</h3>
|
||||
</ion-label>
|
||||
<p slot="end">{{ meetingInfo.participantcount }}</p>
|
||||
</ion-item>
|
||||
</ng-container>
|
||||
|
||||
<ion-button *ngIf="meetingInfo.canjoin" class="ion-margin" expand="block" (click)="joinRoom()">
|
||||
{{ 'addon.mod_bigbluebuttonbn.view_conference_action_join' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-card *ngIf="!meetingInfo.canjoin" class="core-warning-card">
|
||||
<ion-item class="ion-text-wrap">
|
||||
<ion-icon name="fas-exclamation-triangle" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ meetingInfo.statusmessage }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
</core-loading>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
@import "~theme/globals";
|
||||
|
||||
:host {
|
||||
ion-item > p[slot="end"] {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
|
@ -15,8 +15,13 @@
|
|||
import { Component, OnInit, Optional } from '@angular/core';
|
||||
import { CoreCourseModuleMainActivityComponent } from '@features/course/classes/main-activity-component';
|
||||
import { CoreCourseContentsPage } from '@features/course/pages/contents/contents';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
import { AddonModBBBData, AddonModBBBService } from '../../services/bigbluebuttonbn';
|
||||
import { CoreGroupInfo, CoreGroups } from '@services/groups';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
import { AddonModBBB, AddonModBBBData, AddonModBBBMeetingInfoWSResponse, AddonModBBBService } from '../../services/bigbluebuttonbn';
|
||||
|
||||
/**
|
||||
* Component that displays a Big Blue Button activity.
|
||||
|
@ -24,12 +29,16 @@ import { AddonModBBBData, AddonModBBBService } from '../../services/bigbluebutto
|
|||
@Component({
|
||||
selector: 'addon-mod-bbb-index',
|
||||
templateUrl: 'index.html',
|
||||
styleUrls: ['index.scss'],
|
||||
})
|
||||
export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit {
|
||||
|
||||
component = AddonModBBBService.COMPONENT;
|
||||
moduleName = 'bigbluebuttonbn';
|
||||
bbb?: AddonModBBBData;
|
||||
groupInfo?: CoreGroupInfo;
|
||||
groupId = 0;
|
||||
meetingInfo?: AddonModBBBMeetingInfoWSResponse;
|
||||
|
||||
constructor(
|
||||
protected content?: IonContent,
|
||||
|
@ -46,7 +55,17 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo
|
|||
|
||||
await this.loadContent();
|
||||
|
||||
// @todo
|
||||
if (!this.bbb) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await AddonModBBB.logView(this.bbb.id, this.bbb.name);
|
||||
|
||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,10 +73,108 @@ export class AddonModBBBIndexComponent extends CoreCourseModuleMainActivityCompo
|
|||
*/
|
||||
protected async fetchContent(refresh: boolean = false): Promise<void> {
|
||||
try {
|
||||
// @todo
|
||||
this.bbb = await AddonModBBB.getBBB(this.courseId, this.module.id);
|
||||
|
||||
this.description = this.bbb.intro;
|
||||
this.dataRetrieved.emit(this.bbb);
|
||||
|
||||
this.groupInfo = await CoreGroups.getActivityGroupInfo(this.module.id, false);
|
||||
|
||||
this.groupId = CoreGroups.validateGroupId(this.groupId, this.groupInfo);
|
||||
|
||||
await this.fetchMeetingInfo();
|
||||
} finally {
|
||||
this.fillContextMenu(refresh);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get meeting info.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async fetchMeetingInfo(): Promise<void> {
|
||||
if (!this.bbb) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.meetingInfo = await AddonModBBB.getMeetingInfo(this.bbb.id, this.groupId);
|
||||
|
||||
if (this.meetingInfo.statusrunning && this.meetingInfo.userlimit > 0) {
|
||||
const count = (this.meetingInfo.participantcount || 0) + (this.meetingInfo.moderatorcount || 0);
|
||||
if (count === this.meetingInfo.userlimit) {
|
||||
this.meetingInfo.statusmessage = Translate.instant('addon.mod_bigbluebuttonbn.userlimitreached');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update meeting info.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async updateMeetingInfo(): Promise<void> {
|
||||
if (!this.bbb) {
|
||||
return;
|
||||
}
|
||||
|
||||
await AddonModBBB.invalidateAllGroupsMeetingInfo(this.bbb.id);
|
||||
|
||||
await this.fetchMeetingInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
protected async invalidateContent(): Promise<void> {
|
||||
const promises: Promise<void>[] = [];
|
||||
|
||||
promises.push(AddonModBBB.invalidateBBBs(this.courseId));
|
||||
promises.push(CoreGroups.invalidateActivityGroupInfo(this.module.id));
|
||||
|
||||
if (this.bbb) {
|
||||
promises.push(AddonModBBB.invalidateAllGroupsMeetingInfo(this.bbb.id));
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* Group changed, reload some data.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async groupChanged(): Promise<void> {
|
||||
this.loaded = false;
|
||||
|
||||
try {
|
||||
await this.fetchMeetingInfo();
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModal(error);
|
||||
} finally {
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join the room.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async joinRoom(): Promise<void> {
|
||||
const modal = await CoreDomUtils.showModalLoading();
|
||||
|
||||
try {
|
||||
const joinUrl = await AddonModBBB.getJoinUrl(this.module.id, this.groupId);
|
||||
|
||||
CoreUtils.openInBrowser(joinUrl);
|
||||
|
||||
this.updateMeetingInfo();
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModal(error);
|
||||
} finally {
|
||||
modal.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
{
|
||||
"view_error_unable_join_student": "BeeUnable to connect to the BigBlueButton server. Please contact your Teacher or the Administrator.p"
|
||||
"mod_form_field_closingtime": "Join closed",
|
||||
"mod_form_field_openingtime": "Join open",
|
||||
"userlimitreached": "The number of users allowed in a meeting has been reached.",
|
||||
"view_conference_action_join": "Join session",
|
||||
"view_error_unable_join_student": "Unable to connect to the BigBlueButton server. Please contact your Teacher or the Administrator.",
|
||||
"view_groups_selection_warning": "There is a conference room for each group and you have access to more than one. Be sure to select the correct one.",
|
||||
"view_message_conference_in_progress": "This conference is in progress.",
|
||||
"view_message_conference_room_ready": "This conference room is ready. You can join the session now.",
|
||||
"view_message_moderator": "moderator",
|
||||
"view_message_moderators": "moderators",
|
||||
"view_message_session_started_at": "This session started at",
|
||||
"view_message_viewer": "viewer",
|
||||
"view_message_viewers": "viewers"
|
||||
}
|
||||
|
|
|
@ -304,8 +304,8 @@ export type AddonModBBBMeetingInfoWSResponse = {
|
|||
userlimit: number; // User limit.
|
||||
bigbluebuttonbnid: string; // Bigbluebuttonbn instance id.
|
||||
meetingid: string; // Meeting id.
|
||||
openingtime?: string; // Opening time.
|
||||
closingtime?: string; // Closing time.
|
||||
openingtime?: number; // Opening time.
|
||||
closingtime?: number; // Closing time.
|
||||
statusrunning?: boolean; // Status running.
|
||||
statusclosed?: boolean; // Status closed.
|
||||
statusopen?: boolean; // Status open.
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import { CoreConstants } from '@/core/constants';
|
||||
import { Injectable, Type } from '@angular/core';
|
||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||
import { CoreCourseAnyModuleData } from '@features/course/services/course';
|
||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||
import { CoreSitePluginsModuleHandler } from '@features/siteplugins/classes/handlers/module-handler';
|
||||
import { CoreSitePlugins } from '@features/siteplugins/services/siteplugins';
|
||||
|
@ -71,17 +71,17 @@ export class AddonModBBBModuleHandlerService extends CoreModuleHandlerBase imple
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
getData(
|
||||
module: CoreCourseAnyModuleData,
|
||||
async getData(
|
||||
module: CoreCourseModule,
|
||||
courseId: number,
|
||||
sectionId?: number,
|
||||
forCoursePage?: boolean,
|
||||
): CoreCourseModuleHandlerData {
|
||||
): Promise<CoreCourseModuleHandlerData> {
|
||||
if (this.sitePluginHandler) {
|
||||
return this.sitePluginHandler.getData(module, courseId, sectionId, forCoursePage);
|
||||
}
|
||||
|
||||
const data = super.getData(module, courseId, sectionId, forCoursePage);
|
||||
const data = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||
|
||||
data.showDownloadButton = false;
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ export class AddonModWorkshopIndexComponent extends CoreCourseModuleMainActivity
|
|||
separateGroups: false,
|
||||
visibleGroups: false,
|
||||
defaultGroupId: 0,
|
||||
canAccessAllGroups: false,
|
||||
};
|
||||
|
||||
canSubmit = false;
|
||||
|
|
|
@ -151,6 +151,7 @@ export class CoreGroupsProvider {
|
|||
const groupInfo: CoreGroupInfo = {
|
||||
groups: [],
|
||||
defaultGroupId: 0,
|
||||
canAccessAllGroups: false,
|
||||
};
|
||||
|
||||
const groupMode = await this.getActivityGroupMode(cmId, siteId, ignoreCache);
|
||||
|
@ -161,6 +162,8 @@ export class CoreGroupsProvider {
|
|||
let result: CoreGroupGetActivityAllowedGroupsWSResponse;
|
||||
if (groupInfo.separateGroups || groupInfo.visibleGroups) {
|
||||
result = await this.getActivityAllowedGroups(cmId, userId, siteId, ignoreCache);
|
||||
|
||||
groupInfo.canAccessAllGroups = !!result.canaccessallgroups;
|
||||
} else {
|
||||
result = {
|
||||
groups: [],
|
||||
|
@ -471,6 +474,11 @@ export type CoreGroupInfo = {
|
|||
* The group ID to use by default. If all participants is visible, 0 will be used. First group ID otherwise.
|
||||
*/
|
||||
defaultGroupId: number;
|
||||
|
||||
/**
|
||||
* Whether the user has the capability to access all groups in the context.
|
||||
*/
|
||||
canAccessAllGroups: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue